From 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Tue, 17 Mar 2015 19:50:07 -0700 Subject: Initial publish --- README.md | 38 +- avstream/avshws/ReadMe.md | 10 + avstream/avshws/avshws.h | 232 + avstream/avshws/avshws.htm | 458 + avstream/avshws/avshws.inf | 172 + avstream/avshws/avshws.rc | 22 + avstream/avshws/avshws.sln | 28 + avstream/avshws/avshws.vcxproj | 201 + avstream/avshws/avshws.vcxproj.Filters | 46 + avstream/avshws/capture.cpp | 1704 ++ avstream/avshws/capture.h | 313 + avstream/avshws/device.cpp | 875 + avstream/avshws/device.h | 294 + avstream/avshws/filter.cpp | 203 + avstream/avshws/filter.h | 88 + avstream/avshws/hwsim.cpp | 868 + avstream/avshws/hwsim.h | 290 + avstream/avshws/image.cpp | 605 + avstream/avshws/image.h | 478 + avstream/avshws/purecall.c | 50 + avstream/samplemft0/Mft0.cpp | 975 ++ avstream/samplemft0/Mft0.h | 224 + avstream/samplemft0/Mft0.rgs | 15 + avstream/samplemft0/ReadMe.md | 28 + avstream/samplemft0/SampleHelpers.h | 143 + avstream/samplemft0/SampleMft0.cpp | 83 + avstream/samplemft0/SampleMft0.def | 16 + avstream/samplemft0/SampleMft0.idl | 48 + avstream/samplemft0/SampleMft0.rc | Bin 0 -> 6644 bytes avstream/samplemft0/SampleMft0.rgs | 3 + avstream/samplemft0/SampleMft0.sln | 28 + avstream/samplemft0/SampleMft0.vcxproj | 221 + avstream/samplemft0/SampleMft0.vcxproj.Filters | 45 + avstream/samplemft0/dllmain.cpp | 19 + avstream/samplemft0/dllmain.h | 10 + avstream/samplemft0/resource.h | Bin 0 -> 1710 bytes avstream/samplemft0/stdafx.cpp | 13 + avstream/samplemft0/stdafx.h | 42 + avstream/samplemft0/stdafxsrc.cpp | 1 + avstream/samplemft0/targetver.h | 15 + bluetooth/serialhcibus/Fdo.c | 2165 +++ bluetooth/serialhcibus/Io.h | 251 + bluetooth/serialhcibus/ReadMe.md | 59 + bluetooth/serialhcibus/WDK/Debugdef.h | 49 + bluetooth/serialhcibus/WDK/SerialBusWdk.inx | 88 + bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj | 210 + .../serialhcibus/WDK/SerialBusWdk.vcxproj.Filters | 51 + bluetooth/serialhcibus/WDK/device.c | 257 + bluetooth/serialhcibus/WDK/device.h | 56 + bluetooth/serialhcibus/WDK/driver.rc | 14 + bluetooth/serialhcibus/driver.c | 378 + bluetooth/serialhcibus/driver.h | 530 + bluetooth/serialhcibus/io.c | 1592 ++ bluetooth/serialhcibus/pdo.c | 972 ++ bluetooth/serialhcibus/public.h | 47 + bluetooth/serialhcibus/serialhcibus.sln | 28 + filesys/cdfs/ReadMe.md | 10 + filesys/cdfs/allocsup.c | 933 ++ filesys/cdfs/cachesup.c | 671 + filesys/cdfs/cd.h | 534 + filesys/cdfs/cddata.c | 1209 ++ filesys/cdfs/cddata.h | 273 + filesys/cdfs/cdfs.rc | 14 + filesys/cdfs/cdfs.sln | 28 + filesys/cdfs/cdfs.vcxproj | 330 + filesys/cdfs/cdfs.vcxproj.Filters | 115 + filesys/cdfs/cdinit.c | 387 + filesys/cdfs/cdprocs.h | 2263 +++ filesys/cdfs/cdprocssrc.c | 1 + filesys/cdfs/cdstruc.h | 1898 +++ filesys/cdfs/cleanup.c | 364 + filesys/cdfs/close.c | 1019 ++ filesys/cdfs/create.c | 2962 ++++ filesys/cdfs/devctrl.c | 197 + filesys/cdfs/deviosup.c | 4172 +++++ filesys/cdfs/dirctrl.c | 1544 ++ filesys/cdfs/dirsup.c | 1870 +++ filesys/cdfs/fieldoff.c | 476 + filesys/cdfs/fileinfo.c | 1528 ++ filesys/cdfs/filobsup.c | 245 + filesys/cdfs/fsctrl.c | 3440 ++++ filesys/cdfs/fspdisp.c | 280 + filesys/cdfs/lockctrl.c | 683 + filesys/cdfs/namesup.c | 1127 ++ filesys/cdfs/nodetype.h | 106 + filesys/cdfs/pathsup.c | 1001 ++ filesys/cdfs/pnp.c | 833 + filesys/cdfs/prefxsup.c | 715 + filesys/cdfs/read.c | 566 + filesys/cdfs/resrcsup.c | 389 + filesys/cdfs/shutdown.c | 175 + filesys/cdfs/strucsup.c | 2828 ++++ filesys/cdfs/verfysup.c | 984 ++ filesys/cdfs/volinfo.c | 598 + filesys/cdfs/workque.c | 416 + filesys/cdfs/write.c | 381 + filesys/miniFilter/MetadataManager/DataStore.c | 1082 ++ .../miniFilter/MetadataManager/MetadataManager.rc | 10 + .../miniFilter/MetadataManager/MetadataManager.sln | 28 + .../MetadataManager/MetadataManagerInit.c | 865 + .../MetadataManager/MetadataManagerProc.h | 255 + .../MetadataManager/MetadataManagerStruc.h | 197 + filesys/miniFilter/MetadataManager/ReadMe.md | 21 + filesys/miniFilter/MetadataManager/fmm.inf | 96 + filesys/miniFilter/MetadataManager/fmm.vcxproj | 183 + .../miniFilter/MetadataManager/fmm.vcxproj.Filters | 40 + filesys/miniFilter/MetadataManager/operations.c | 1356 ++ filesys/miniFilter/MetadataManager/pch.h | 47 + filesys/miniFilter/MetadataManager/support.c | 244 + filesys/miniFilter/avscan/ReadMe.md | 8 + filesys/miniFilter/avscan/avscan.inf | 106 + filesys/miniFilter/avscan/avscan.sln | 46 + filesys/miniFilter/avscan/filter/avscan.c | 3152 ++++ filesys/miniFilter/avscan/filter/avscan.h | 271 + filesys/miniFilter/avscan/filter/avscan.rc | 10 + filesys/miniFilter/avscan/filter/avscan.vcxproj | 185 + .../avscan/filter/avscan.vcxproj.Filters | 46 + filesys/miniFilter/avscan/filter/communication.c | 1407 ++ filesys/miniFilter/avscan/filter/context.c | 943 ++ filesys/miniFilter/avscan/filter/context.h | 357 + filesys/miniFilter/avscan/filter/csvfs.c | 1039 ++ filesys/miniFilter/avscan/filter/csvfs.h | 62 + filesys/miniFilter/avscan/filter/scan.c | 571 + filesys/miniFilter/avscan/filter/scan.h | 65 + filesys/miniFilter/avscan/filter/utility.c | 370 + filesys/miniFilter/avscan/filter/utility.h | 252 + filesys/miniFilter/avscan/inc/avlib.h | 201 + filesys/miniFilter/avscan/user/avscan.c | 101 + filesys/miniFilter/avscan/user/avscan.rc | 10 + filesys/miniFilter/avscan/user/avscan.vcxproj | 194 + .../miniFilter/avscan/user/avscan.vcxproj.Filters | 33 + filesys/miniFilter/avscan/user/userscan.c | 1250 ++ filesys/miniFilter/avscan/user/userscan.h | 109 + filesys/miniFilter/avscan/user/utility.c | 126 + filesys/miniFilter/avscan/user/utility.h | 30 + filesys/miniFilter/cancelSafe/ReadMe.md | 14 + filesys/miniFilter/cancelSafe/cancelSafe.c | 1932 +++ filesys/miniFilter/cancelSafe/cancelSafe.inf | 99 + filesys/miniFilter/cancelSafe/cancelSafe.rc | 10 + filesys/miniFilter/cancelSafe/cancelSafe.sln | 28 + filesys/miniFilter/cancelSafe/cancelSafe.vcxproj | 184 + .../cancelSafe/cancelSafe.vcxproj.Filters | 31 + filesys/miniFilter/cdo/Cdo.rc | 10 + filesys/miniFilter/cdo/CdoInit.c | 374 + filesys/miniFilter/cdo/CdoOperations.c | 2226 +++ filesys/miniFilter/cdo/CdoProc.h | 328 + filesys/miniFilter/cdo/CdoStruct.h | 137 + filesys/miniFilter/cdo/ReadMe.md | 16 + filesys/miniFilter/cdo/cdo.inf | 96 + filesys/miniFilter/cdo/cdo.sln | 28 + filesys/miniFilter/cdo/cdo.vcxproj | 153 + filesys/miniFilter/cdo/cdo.vcxproj.Filters | 34 + filesys/miniFilter/cdo/pch.h | 48 + filesys/miniFilter/change/ReadMe.md | 17 + filesys/miniFilter/change/change.c | 1393 ++ filesys/miniFilter/change/change.h | 50 + filesys/miniFilter/change/change.inf | 95 + filesys/miniFilter/change/change.rc | 10 + filesys/miniFilter/change/change.sln | 28 + filesys/miniFilter/change/change.vcxproj | 181 + filesys/miniFilter/change/change.vcxproj.Filters | 34 + filesys/miniFilter/change/context.c | 609 + filesys/miniFilter/change/context.h | 151 + filesys/miniFilter/change/utility.h | 58 + filesys/miniFilter/ctx/CtxInit.c | 908 + filesys/miniFilter/ctx/CtxProc.h | 230 + filesys/miniFilter/ctx/CtxStruc.h | 213 + filesys/miniFilter/ctx/ReadMe.md | 14 + filesys/miniFilter/ctx/context.c | 885 + filesys/miniFilter/ctx/ctx.inf | 96 + filesys/miniFilter/ctx/ctx.rc | 10 + filesys/miniFilter/ctx/ctx.sln | 28 + filesys/miniFilter/ctx/ctx.vcxproj | 183 + filesys/miniFilter/ctx/ctx.vcxproj.Filters | 40 + filesys/miniFilter/ctx/operations.c | 1080 ++ filesys/miniFilter/ctx/pch.h | 46 + filesys/miniFilter/ctx/support.c | 117 + filesys/miniFilter/delete/ReadMe.md | 16 + filesys/miniFilter/delete/delete.c | 3253 ++++ filesys/miniFilter/delete/delete.inf | 96 + filesys/miniFilter/delete/delete.rc | 10 + filesys/miniFilter/delete/delete.sln | 28 + filesys/miniFilter/delete/delete.vcxproj | 180 + filesys/miniFilter/delete/delete.vcxproj.Filters | 31 + filesys/miniFilter/minispy/ReadMe.md | 17 + .../miniFilter/minispy/filter/RegistrationData.c | 302 + filesys/miniFilter/minispy/filter/minispy.c | 1380 ++ filesys/miniFilter/minispy/filter/minispy.rc | 10 + filesys/miniFilter/minispy/filter/minispy.vcxproj | 198 + .../minispy/filter/minispy.vcxproj.Filters | 37 + filesys/miniFilter/minispy/filter/mspyKern.h | 412 + filesys/miniFilter/minispy/filter/mspyLib.c | 1541 ++ filesys/miniFilter/minispy/inc/minispy.h | 251 + filesys/miniFilter/minispy/minispy.inf | 111 + filesys/miniFilter/minispy/minispy.sln | 46 + filesys/miniFilter/minispy/user/minispy.vcxproj | 193 + .../minispy/user/minispy.vcxproj.Filters | 30 + filesys/miniFilter/minispy/user/mspyLog.c | 1193 ++ filesys/miniFilter/minispy/user/mspyLog.h | 425 + filesys/miniFilter/minispy/user/mspyUser.c | 979 ++ filesys/miniFilter/minispy/user/mspyUser.rc | 10 + filesys/miniFilter/nullFilter/ReadMe.md | 15 + filesys/miniFilter/nullFilter/nullFilter.c | 240 + filesys/miniFilter/nullFilter/nullFilter.inf | 95 + filesys/miniFilter/nullFilter/nullFilter.rc | 10 + filesys/miniFilter/nullFilter/nullFilter.sln | 28 + filesys/miniFilter/nullFilter/nullFilter.vcxproj | 152 + .../nullFilter/nullFilter.vcxproj.Filters | 31 + filesys/miniFilter/passThrough/ReadMe.md | 15 + filesys/miniFilter/passThrough/passThrough.c | 887 + filesys/miniFilter/passThrough/passThrough.inf | 96 + filesys/miniFilter/passThrough/passThrough.rc | 10 + filesys/miniFilter/passThrough/passThrough.sln | 28 + filesys/miniFilter/passThrough/passThrough.vcxproj | 152 + .../passThrough/passThrough.vcxproj.Filters | 31 + filesys/miniFilter/scanner/ReadMe.md | 17 + filesys/miniFilter/scanner/filter/scanner.c | 1685 ++ filesys/miniFilter/scanner/filter/scanner.h | 160 + filesys/miniFilter/scanner/filter/scanner.rc | 10 + filesys/miniFilter/scanner/filter/scanner.vcxproj | 192 + .../scanner/filter/scanner.vcxproj.Filters | 31 + filesys/miniFilter/scanner/inc/scanuk.h | 49 + filesys/miniFilter/scanner/scanner.inf | 104 + filesys/miniFilter/scanner/scanner.sln | 46 + filesys/miniFilter/scanner/user/scanUser.c | 416 + filesys/miniFilter/scanner/user/scanUser.rc | 10 + filesys/miniFilter/scanner/user/scanuser.h | 67 + filesys/miniFilter/scanner/user/scanuser.vcxproj | 192 + .../scanner/user/scanuser.vcxproj.Filters | 27 + filesys/miniFilter/simrep/ReadMe.md | 19 + filesys/miniFilter/simrep/simrep.c | 3006 ++++ filesys/miniFilter/simrep/simrep.inf | 110 + filesys/miniFilter/simrep/simrep.rc | 10 + filesys/miniFilter/simrep/simrep.sln | 28 + filesys/miniFilter/simrep/simrep.vcxproj | 180 + filesys/miniFilter/simrep/simrep.vcxproj.Filters | 31 + filesys/miniFilter/swapBuffers/ReadMe.md | 15 + filesys/miniFilter/swapBuffers/swapBuffers.c | 2334 +++ filesys/miniFilter/swapBuffers/swapBuffers.inf | 95 + filesys/miniFilter/swapBuffers/swapBuffers.rc | 10 + filesys/miniFilter/swapBuffers/swapBuffers.sln | 28 + filesys/miniFilter/swapBuffers/swapBuffers.vcxproj | 180 + .../swapBuffers/swapBuffers.vcxproj.Filters | 31 + general/PLX9x5x/PLX9x5x.sln | 46 + general/PLX9x5x/ReadMe.md | 23 + general/PLX9x5x/sys/Init.c | 777 + general/PLX9x5x/sys/IsrDpc.c | 441 + general/PLX9x5x/sys/Pci9656.c | 623 + general/PLX9x5x/sys/Pci9656.h | 14 + general/PLX9x5x/sys/Pci9656.rc | 14 + general/PLX9x5x/sys/Pci9x5x.vcxproj | 221 + general/PLX9x5x/sys/Pci9x5x.vcxproj.Filters | 54 + general/PLX9x5x/sys/Precomp.h | 17 + general/PLX9x5x/sys/Private.h | 211 + general/PLX9x5x/sys/Public.h | 35 + general/PLX9x5x/sys/Read.c | 453 + general/PLX9x5x/sys/Reg9656.h | 259 + general/PLX9x5x/sys/Write.c | 525 + general/PLX9x5x/sys/pci9x5x.inx | 97 + general/PLX9x5x/sys/precompsrc.c | 1 + general/PLX9x5x/sys/trace.h | 66 + general/PLX9x5x/test/plx.cpp | 1104 ++ general/PLX9x5x/test/plx.hpp | 146 + general/PLX9x5x/test/plx.vcxproj | 184 + general/PLX9x5x/test/plx.vcxproj.Filters | 22 + general/PLX9x5x/test/test.cmd | 2 + general/SystemDma/wdm/ReadMe.md | 14 + general/SystemDma/wdm/SystemDma.sln | 46 + general/SystemDma/wdm/exe/SystemDmaApp.vcxproj | 196 + .../SystemDma/wdm/exe/SystemDmaApp.vcxproj.Filters | 25 + general/SystemDma/wdm/exe/install.c | 537 + general/SystemDma/wdm/exe/testapp.c | 180 + general/SystemDma/wdm/sys/SDma.vcxproj | 140 + general/SystemDma/wdm/sys/SDma.vcxproj.Filters | 31 + general/SystemDma/wdm/sys/sdma.c | 1364 ++ general/SystemDma/wdm/sys/sdma.h | 38 + general/SystemDma/wdm/sys/sdma.rc | 10 + general/cancel/ReadMe.md | 27 + general/cancel/cancel.sln | 59 + general/cancel/exe/canclapp.vcxproj | 180 + general/cancel/exe/canclapp.vcxproj.Filters | 25 + general/cancel/exe/install.c | 480 + general/cancel/exe/testapp.c | 333 + general/cancel/exe/testapp.h | 14 + general/cancel/startio/cancel.c | 965 ++ general/cancel/startio/cancel.h | 166 + general/cancel/startio/cancel.rc | 10 + general/cancel/startio/cancel.vcxproj | 152 + general/cancel/startio/cancel.vcxproj.Filters | 31 + general/cancel/sys/cancel.c | 945 ++ general/cancel/sys/cancel.h | 181 + general/cancel/sys/cancel.rc | 10 + general/cancel/sys/cancel.vcxproj | 152 + general/cancel/sys/cancel.vcxproj.Filters | 31 + general/echo/kmdf/ReadMe.md | 84 + general/echo/kmdf/driver/AutoSync/device.c | 210 + general/echo/kmdf/driver/AutoSync/device.h | 48 + general/echo/kmdf/driver/AutoSync/driver.c | 202 + general/echo/kmdf/driver/AutoSync/driver.h | 34 + general/echo/kmdf/driver/AutoSync/echo.inx | 104 + general/echo/kmdf/driver/AutoSync/echo.vcxproj | 168 + .../echo/kmdf/driver/AutoSync/echo.vcxproj.Filters | 40 + general/echo/kmdf/driver/AutoSync/queue.c | 532 + general/echo/kmdf/driver/AutoSync/queue.h | 64 + general/echo/kmdf/driver/DriverSync/device.c | 223 + general/echo/kmdf/driver/DriverSync/device.h | 48 + general/echo/kmdf/driver/DriverSync/driver.c | 201 + general/echo/kmdf/driver/DriverSync/driver.h | 48 + general/echo/kmdf/driver/DriverSync/echo_2.inx | 105 + general/echo/kmdf/driver/DriverSync/echo_2.vcxproj | 180 + .../kmdf/driver/DriverSync/echo_2.vcxproj.Filters | 40 + general/echo/kmdf/driver/DriverSync/queue.c | 816 + general/echo/kmdf/driver/DriverSync/queue.h | 67 + general/echo/kmdf/exe/echoapp.cpp | 700 + general/echo/kmdf/exe/echoapp.vcxproj | 171 + general/echo/kmdf/exe/echoapp.vcxproj.Filters | 22 + general/echo/kmdf/exe/public.h | 30 + general/echo/kmdf/kmdfecho.sln | 63 + general/echo/umdf/Comsup.cpp | 344 + general/echo/umdf/Comsup.h | 215 + general/echo/umdf/Device.cpp | 415 + general/echo/umdf/Device.h | 217 + general/echo/umdf/Driver.cpp | 220 + general/echo/umdf/Driver.h | 149 + general/echo/umdf/Echo.rc | 21 + general/echo/umdf/Queue.cpp | 545 + general/echo/umdf/Queue.h | 213 + general/echo/umdf/ReadMe.md | 124 + general/echo/umdf/WUDFEchoDriver.inx | 87 + general/echo/umdf/WUDFEchoDriver.vcxproj | 256 + general/echo/umdf/WUDFEchoDriver.vcxproj.Filters | 54 + general/echo/umdf/dllsup.cpp | 176 + general/echo/umdf/echo.sln | 28 + general/echo/umdf/echodriver.ctl | 1 + general/echo/umdf/exports.def | 10 + general/echo/umdf/internal.h | 114 + general/echo/umdf2/ReadMe.md | 77 + general/echo/umdf2/driver/AutoSync/device.c | 202 + general/echo/umdf2/driver/AutoSync/device.h | 48 + general/echo/umdf2/driver/AutoSync/driver.c | 192 + general/echo/umdf2/driver/AutoSync/driver.h | 46 + general/echo/umdf2/driver/AutoSync/echo.vcxproj | 180 + .../umdf2/driver/AutoSync/echo.vcxproj.Filters | 40 + general/echo/umdf2/driver/AutoSync/echoum.inx | 89 + general/echo/umdf2/driver/AutoSync/queue.c | 541 + general/echo/umdf2/driver/AutoSync/queue.h | 62 + general/echo/umdf2/exe/echoapp.cpp | 700 + general/echo/umdf2/exe/echoapp.vcxproj | 171 + general/echo/umdf2/exe/echoapp.vcxproj.Filters | 22 + general/echo/umdf2/exe/public.h | 30 + general/echo/umdf2/umdf2echo.sln | 49 + general/echo/umdfSocketEcho/Driver/Connection.cpp | 263 + general/echo/umdfSocketEcho/Driver/FileContext.h | 30 + general/echo/umdfSocketEcho/Driver/Queue.cpp | 580 + general/echo/umdfSocketEcho/Driver/Queue.h | 83 + general/echo/umdfSocketEcho/Driver/SocketEcho.inx | 89 + general/echo/umdfSocketEcho/Driver/SocketEcho.rc | 21 + .../echo/umdfSocketEcho/Driver/SocketEcho.vcxproj | 245 + .../Driver/SocketEcho.vcxproj.Filters | 54 + general/echo/umdfSocketEcho/Driver/connection.h | 32 + general/echo/umdfSocketEcho/Driver/device.cpp | 469 + general/echo/umdfSocketEcho/Driver/device.h | 70 + general/echo/umdfSocketEcho/Driver/devicecontext.h | 32 + general/echo/umdfSocketEcho/Driver/dllsup.cpp | 111 + general/echo/umdfSocketEcho/Driver/driver.cpp | 174 + general/echo/umdfSocketEcho/Driver/driver.h | 53 + general/echo/umdfSocketEcho/Driver/exports.def | 6 + general/echo/umdfSocketEcho/Driver/internal.h | 117 + general/echo/umdfSocketEcho/Exe/internal.h | 18 + .../echo/umdfSocketEcho/Exe/socketechoserver.cpp | 512 + general/echo/umdfSocketEcho/Exe/socketechoserver.h | 48 + .../umdfSocketEcho/Exe/socketechoserver.vcxproj | 179 + .../Exe/socketechoserver.vcxproj.Filters | 22 + general/echo/umdfSocketEcho/ReadMe.md | 184 + general/echo/umdfSocketEcho/umdfsocketecho.sln | 46 + general/event/ReadMe.md | 25 + general/event/eventsample.sln | 46 + general/event/exe/event.vcxproj | 180 + general/event/exe/event.vcxproj.Filters | 25 + general/event/exe/eventtest.c | 266 + general/event/exe/install.c | 558 + general/event/wdm/event.c | 1070 ++ general/event/wdm/event.h | 99 + general/event/wdm/event.rc | 11 + general/event/wdm/event.vcxproj | 156 + general/event/wdm/event.vcxproj.Filters | 31 + general/event/wdm/public.h | 48 + general/filehistory/ReadMe.md | 23 + general/filehistory/exe/fhsetup.cpp | 271 + general/filehistory/exe/fhsetup.h | 27 + general/filehistory/exe/fhsetup.vcxproj | 191 + general/filehistory/exe/fhsetup.vcxproj.Filters | 22 + general/filehistory/filehistory.sln | 28 + general/installwdf/Install.cpp | 509 + general/installwdf/InstallWdf.vcxproj | 187 + general/installwdf/InstallWdf.vcxproj.Filters | 22 + general/installwdf/ReadMe.md | 9 + general/installwdf/installwdf.sln | 28 + general/ioctl/wdm/ReadMe.md | 17 + general/ioctl/wdm/exe/install.c | 550 + general/ioctl/wdm/exe/ioctlapp.vcxproj | 196 + general/ioctl/wdm/exe/ioctlapp.vcxproj.Filters | 25 + general/ioctl/wdm/exe/testapp.c | 261 + general/ioctl/wdm/ioctl.sln | 46 + general/ioctl/wdm/sys/sioctl.c | 744 + general/ioctl/wdm/sys/sioctl.h | 47 + general/ioctl/wdm/sys/sioctl.rc | 10 + general/ioctl/wdm/sys/sioctl.vcxproj | 140 + general/ioctl/wdm/sys/sioctl.vcxproj.Filters | 31 + general/obcallback/ReadMe.md | 42 + .../obcallback/control/ObCallbackTestCtrl.vcxproj | 236 + .../control/ObCallbackTestCtrl.vcxproj.Filters | 28 + general/obcallback/control/common.h | 86 + general/obcallback/control/main.cpp | 320 + general/obcallback/control/pch.h | 18 + general/obcallback/control/pchsrc.cpp | 1 + general/obcallback/control/readme.txt | 57 + general/obcallback/control/utils.cpp | 727 + general/obcallback/driver/ObCallbackTest.vcxproj | 210 + .../driver/ObCallbackTest.vcxproj.Filters | 35 + general/obcallback/driver/callback.c | 479 + general/obcallback/driver/pch.h | 13 + general/obcallback/driver/pchsrc.c | 1 + general/obcallback/driver/shared.h | 95 + general/obcallback/driver/tdriver.c | 532 + general/obcallback/driver/tdriver.h | 125 + general/obcallback/driver/util.c | 73 + general/obcallback/obcallback.sln | 46 + general/pcidrv/ReadMe.md | 186 + general/pcidrv/kmdf/HW/PCIDRV.vcxproj | 360 + general/pcidrv/kmdf/HW/PCIDRV.vcxproj.Filters | 75 + general/pcidrv/kmdf/HW/e100_557.h | 789 + general/pcidrv/kmdf/HW/e100_equ.h | 187 + general/pcidrv/kmdf/HW/eeprom.c | 306 + general/pcidrv/kmdf/HW/isrdpc.c | 904 + general/pcidrv/kmdf/HW/localwpp.ini | 17 + general/pcidrv/kmdf/HW/macros.h | 312 + general/pcidrv/kmdf/HW/nic_def.h | 800 + general/pcidrv/kmdf/HW/nic_init.c | 2378 +++ general/pcidrv/kmdf/HW/nic_pm.c | 1710 ++ general/pcidrv/kmdf/HW/nic_recv.c | 630 + general/pcidrv/kmdf/HW/nic_req.c | 1453 ++ general/pcidrv/kmdf/HW/nic_send.c | 838 + general/pcidrv/kmdf/HW/nuiouser.h | 105 + general/pcidrv/kmdf/HW/physet.c | 1001 ++ general/pcidrv/kmdf/HW/precomp.h | 45 + general/pcidrv/kmdf/HW/precompsrc.c | 1 + general/pcidrv/kmdf/HW/routines.c | 479 + general/pcidrv/kmdf/PCIDRV.C | 1792 ++ general/pcidrv/kmdf/PCIDRV.H | 401 + general/pcidrv/kmdf/PCIDRV.RC | 33 + general/pcidrv/kmdf/PCIDRV.mof | 19 + general/pcidrv/kmdf/genpci.inx | 127 + general/pcidrv/kmdf/public.h | 48 + general/pcidrv/kmdf/trace.h | 140 + general/pcidrv/kmdf/wmi.c | 144 + general/pcidrv/pcidrv.sln | 49 + general/pcidrv/test/myping.c | 1054 ++ general/pcidrv/test/myping.vcxproj | 201 + general/pcidrv/test/myping.vcxproj.Filters | 30 + general/pcidrv/test/resource.h | 17 + general/pcidrv/test/testapp.c | 1224 ++ general/pcidrv/test/testapp.h | 260 + general/pcidrv/test/testapp.rc | 37 + general/perfcounters/kcs/ReadMe.md | 15 + general/perfcounters/kcs/kcs.c | 407 + general/perfcounters/kcs/kcs.h | 34 + general/perfcounters/kcs/kcs.man | 101 + general/perfcounters/kcs/kcs.rc | 1 + general/perfcounters/kcs/kcs.sln | 28 + general/perfcounters/kcs/kcs.vcxproj | 174 + general/perfcounters/kcs/kcs.vcxproj.Filters | 31 + general/registry/regfltr/ReadMe.md | 23 + general/registry/regfltr/exe/capture.c | 151 + general/registry/regfltr/exe/common.h | 168 + general/registry/regfltr/exe/post.c | 366 + general/registry/regfltr/exe/pre.c | 428 + general/registry/regfltr/exe/regctrl.c | 288 + general/registry/regfltr/exe/regctrl.h | 108 + general/registry/regfltr/exe/regctrl.rc | 11 + general/registry/regfltr/exe/regctrl.vcxproj | 204 + .../registry/regfltr/exe/regctrl.vcxproj.Filters | 39 + general/registry/regfltr/exe/util.c | 707 + general/registry/regfltr/regfltr.sln | 46 + general/registry/regfltr/sys/capture.c | 620 + general/registry/regfltr/sys/context.c | 582 + general/registry/regfltr/sys/driver.c | 468 + general/registry/regfltr/sys/multialt.c | 776 + general/registry/regfltr/sys/post.c | 768 + general/registry/regfltr/sys/pre.c | 799 + general/registry/regfltr/sys/regfltr.c | 871 + general/registry/regfltr/sys/regfltr.h | 488 + general/registry/regfltr/sys/regfltr.rc | 11 + general/registry/regfltr/sys/regfltr.vcxproj | 214 + .../registry/regfltr/sys/regfltr.vcxproj.Filters | 61 + general/registry/regfltr/sys/txr.c | 784 + general/registry/regfltr/sys/txrutil.c | 283 + general/registry/regfltr/sys/util.c | 304 + general/registry/regfltr/sys/version.c | 611 + general/toaster/toastpkg/ReadMe.md | 21 + general/toaster/toastpkg/inf/autorun.inf | 15 + general/toaster/toastpkg/inf/toastpkg.inf | 137 + general/toaster/toastpkg/toastapp/precomp.h | 21 + general/toaster/toastpkg/toastapp/precompsrc.c | 1 + general/toaster/toastpkg/toastapp/rc_ids.h | 22 + general/toaster/toastpkg/toastapp/toastapp.c | 813 + general/toaster/toastpkg/toastapp/toastapp.dlg | 12 + general/toaster/toastpkg/toastapp/toastapp.h | 29 + general/toaster/toastpkg/toastapp/toastapp.ico | Bin 0 -> 766 bytes general/toaster/toastpkg/toastapp/toastapp.rc | 69 + general/toaster/toastpkg/toastapp/toastapp.vcxproj | 209 + .../toastpkg/toastapp/toastapp.vcxproj.Filters | 30 + .../toaster/toastpkg/toastcd/ToastApp/setup.ini | Bin 0 -> 758 bytes .../toaster/toastpkg/toastcd/ToastApp/toastapp.msi | Bin 0 -> 181760 bytes general/toaster/toastpkg/toastcd/amd64/toaster.sys | Bin 0 -> 41472 bytes general/toaster/toastpkg/toastcd/autorun.inf | 15 + general/toaster/toastpkg/toastcd/i386/toaster.sys | Bin 0 -> 24064 bytes general/toaster/toastpkg/toastcd/toastpkg.inf | 137 + general/toaster/toastpkg/toastcd/toastpkg.tag | 0 general/toaster/toastpkg/toastcd/tostx86.cat | Bin 0 -> 10350 bytes general/toaster/toastpkg/toastcd/tstamd64.cat | Bin 0 -> 10252 bytes general/toaster/toastpkg/toastco/precomp.h | 21 + general/toaster/toastpkg/toastco/precompsrc.c | 1 + general/toaster/toastpkg/toastco/toastco.c | 1312 ++ general/toaster/toastpkg/toastco/toastco.h | 33 + general/toaster/toastpkg/toastco/toastco.rc | 85 + general/toaster/toastpkg/toastco/tostrco2.def | 24 + general/toaster/toastpkg/toastco/tostrco2.vcxproj | 243 + .../toastpkg/toastco/tostrco2.vcxproj.Filters | 33 + general/toaster/toastpkg/toastpkg.sln | 62 + general/toaster/toastpkg/toastva/header.bmp | Bin 0 -> 5686 bytes general/toaster/toastpkg/toastva/precomp.h | 21 + general/toaster/toastpkg/toastva/precompsrc.c | 1 + general/toaster/toastpkg/toastva/resource.h | 61 + general/toaster/toastpkg/toastva/search.avi | Bin 0 -> 20796 bytes general/toaster/toastpkg/toastva/toastapp.ico | Bin 0 -> 766 bytes general/toaster/toastpkg/toastva/toastva.c | 209 + general/toaster/toastpkg/toastva/toastva.h | 79 + general/toaster/toastpkg/toastva/toastva.rc | 221 + general/toaster/toastpkg/toastva/toastva.vcxproj | 221 + .../toastpkg/toastva/toastva.vcxproj.Filters | 36 + general/toaster/toastpkg/toastva/util.c | 517 + general/toaster/toastpkg/toastva/watermrk.bmp | Bin 0 -> 52574 bytes general/toaster/toastpkg/toastva/wizard.c | 883 + general/toaster/umdf2/Package/package.VcxProj | 91 + .../toaster/umdf2/Package/package.VcxProj.Filters | 21 + general/toaster/umdf2/ReadMe.md | 58 + general/toaster/umdf2/exe/enum/Enum.vcxproj | 171 + .../toaster/umdf2/exe/enum/Enum.vcxproj.Filters | 22 + general/toaster/umdf2/exe/enum/enum.c | 317 + general/toaster/umdf2/exe/notify/notify.c | 1244 ++ general/toaster/umdf2/exe/notify/notify.h | 182 + general/toaster/umdf2/exe/notify/notify.rc | 77 + general/toaster/umdf2/exe/notify/notify.vcxproj | 172 + .../umdf2/exe/notify/notify.vcxproj.Filters | 27 + general/toaster/umdf2/exe/notify/toaster.ico | Bin 0 -> 4534 bytes general/toaster/umdf2/exe/toast/toast.c | 343 + general/toaster/umdf2/exe/toast/toast.vcxproj | 171 + .../toaster/umdf2/exe/toast/toast.vcxproj.Filters | 22 + general/toaster/umdf2/filter/generic/filter.c | 387 + general/toaster/umdf2/filter/generic/filter.h | 85 + general/toaster/umdf2/filter/generic/filter.rc | 12 + general/toaster/umdf2/filter/generic/filterum.inx | 98 + .../toaster/umdf2/filter/generic/filterum.vcxproj | 188 + .../umdf2/filter/generic/filterum.vcxproj.Filters | 34 + general/toaster/umdf2/func/featured/power.c | 385 + general/toaster/umdf2/func/featured/toaster.c | 862 + general/toaster/umdf2/func/featured/toaster.rc | 12 + .../toaster/umdf2/func/featured/wdffeaturedum.inx | 98 + .../umdf2/func/featured/wdffeaturedum.vcxproj | 180 + .../func/featured/wdffeaturedum.vcxproj.Filters | 42 + general/toaster/umdf2/func/shared/toaster.h | 118 + general/toaster/umdf2/func/simple/toaster.c | 418 + general/toaster/umdf2/func/simple/wdfsimpleum.inx | 97 + .../toaster/umdf2/func/simple/wdfsimpleum.vcxproj | 180 + .../umdf2/func/simple/wdfsimpleum.vcxproj.Filters | 34 + general/toaster/umdf2/inc/driver.h | 69 + general/toaster/umdf2/inc/public.h | 167 + general/toaster/umdf2/umdf2toaster.sln | 123 + general/tracing/SystemTraceControl/ReadMe.md | 11 + general/tracing/SystemTraceControl/ReadMe.txt | 36 + .../SystemTraceControl/SystemTraceControl.cpp | 221 + .../SystemTraceControl/SystemTraceControl.sln | 28 + .../SystemTraceControl/SystemTraceControl.vcxproj | 179 + .../SystemTraceControl.vcxproj.Filters | 22 + general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj | 195 + .../evntdrv/Eventdrv/Eventdrv.vcxproj.Filters | 34 + general/tracing/evntdrv/Eventdrv/drvioctl.h | 34 + general/tracing/evntdrv/Eventdrv/evntdrv.c | 345 + general/tracing/evntdrv/Eventdrv/evntdrv.xml | 99 + general/tracing/evntdrv/Eventdrv/evntdrvevents.rc | 3 + general/tracing/evntdrv/ReadMe.md | 62 + general/tracing/evntdrv/eventdrv.sln | 46 + general/tracing/evntdrv/evntctrl/evntctrl.vcxproj | 152 + .../evntdrv/evntctrl/evntctrl.vcxproj.Filters | 25 + general/tracing/evntdrv/evntctrl/install.c | 499 + general/tracing/evntdrv/evntctrl/install.h | 37 + general/tracing/evntdrv/evntctrl/tracectl.c | 243 + general/tracing/tracedriver/ReadMe.md | 73 + general/tracing/tracedriver/tracectl/install.c | 499 + general/tracing/tracedriver/tracectl/install.h | 14 + general/tracing/tracedriver/tracectl/tracectl.c | 244 + .../tracing/tracedriver/tracectl/tracectl.vcxproj | 152 + .../tracedriver/tracectl/tracectl.vcxproj.Filters | 25 + general/tracing/tracedriver/tracedrv.sln | 46 + general/tracing/tracedriver/tracedrv/drvioctl.h | 34 + general/tracing/tracedriver/tracedrv/tracedrv.c | 379 + general/tracing/tracedriver/tracedrv/tracedrv.ctl | 1 + general/tracing/tracedriver/tracedrv/tracedrv.h | 113 + general/tracing/tracedriver/tracedrv/tracedrv.rc | 28 + .../tracing/tracedriver/tracedrv/tracedrv.vcxproj | 164 + .../tracedriver/tracedrv/tracedrv.vcxproj.Filters | 31 + general/umdfSkeleton/ReadMe.md | 7 + general/umdfSkeleton/Skeleton.rc | 21 + general/umdfSkeleton/UMDFSkeleton.vcxproj | 264 + general/umdfSkeleton/UMDFSkeleton.vcxproj.Filters | 57 + general/umdfSkeleton/UMDFSkeleton_OSR.inx | 96 + general/umdfSkeleton/UMDFSkeleton_Root.inx | 77 + general/umdfSkeleton/comsup.cpp | 344 + general/umdfSkeleton/comsup.h | 215 + general/umdfSkeleton/device.cpp | 238 + general/umdfSkeleton/device.h | 115 + general/umdfSkeleton/dllsup.cpp | 177 + general/umdfSkeleton/driver.cpp | 220 + general/umdfSkeleton/driver.h | 149 + general/umdfSkeleton/exports.def | 10 + general/umdfSkeleton/internal.h | 90 + general/umdfSkeleton/umdfSkeleton.sln | 28 + gpio/samples/ReadMe.md | 28 + gpio/samples/sim.sln | 72 + gpio/samples/simdevice/GpioSample.asl | 124 + gpio/samples/simdevice/common.h | 84 + gpio/samples/simdevice/simdevice.c | 819 + gpio/samples/simdevice/simdevice.inx | 76 + gpio/samples/simdevice/simdevice.rc | 12 + gpio/samples/simdevice/simdevice.vcxproj | 189 + gpio/samples/simdevice/simdevice.vcxproj.Filters | 39 + gpio/samples/simdeviceumdf/GpioSampleUMDF.asl | 122 + gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj | 282 + .../simdeviceumdf/SimdeviceUMDF.vcxproj.Filters | 54 + gpio/samples/simdeviceumdf/comsup.cpp | 344 + gpio/samples/simdeviceumdf/comsup.h | 217 + gpio/samples/simdeviceumdf/device.cpp | 1106 ++ gpio/samples/simdeviceumdf/device.h | 365 + gpio/samples/simdeviceumdf/dllsup.cpp | 151 + gpio/samples/simdeviceumdf/driver.cpp | 222 + gpio/samples/simdeviceumdf/driver.h | 151 + gpio/samples/simdeviceumdf/exports.def | 10 + gpio/samples/simdeviceumdf/internal.h | 96 + gpio/samples/simdeviceumdf/queue.cpp | 529 + gpio/samples/simdeviceumdf/queue.h | 211 + gpio/samples/simdeviceumdf/simdeviceumdf.rc | 21 + gpio/samples/simdeviceumdf/simdeviceumdfdriver.ctl | 1 + gpio/samples/simdeviceumdf/simdeviceumdfdriver.inx | 78 + gpio/samples/simgpio/simgpio.c | 2540 +++ gpio/samples/simgpio/simgpio.inx | Bin 0 -> 3616 bytes gpio/samples/simgpio/simgpio.rc | 12 + gpio/samples/simgpio/simgpio.vcxproj | 161 + gpio/samples/simgpio/simgpio.vcxproj.Filters | 39 + gpio/samples/simgpio_i2c/simgpio_i2c.c | 2692 +++ gpio/samples/simgpio_i2c/simgpio_i2c.h | 176 + gpio/samples/simgpio_i2c/simgpio_i2c.inx | Bin 0 -> 3688 bytes gpio/samples/simgpio_i2c/simgpio_i2c.rc | 12 + gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj | 200 + .../simgpio_i2c/simgpio_i2c.vcxproj.Filters | 42 + gpio/samples/simgpio_i2c/spb_i2c.c | 666 + gpio/samples/simgpio_i2c/trace.h | 75 + network/config/bindview/BINDING.CPP | 827 + network/config/bindview/BINDVIEW.CPP | 2336 +++ network/config/bindview/BINDVIEW.H | 250 + network/config/bindview/BINDVIEW.ICO | Bin 0 -> 766 bytes network/config/bindview/BindView.rc | 194 + network/config/bindview/Component.cpp | 897 + network/config/bindview/NetCfgAPI.cpp | 878 + network/config/bindview/NetCfgAPI.h | 92 + network/config/bindview/RESOURCE.H | 43 + network/config/bindview/ReadMe.md | 7 + network/config/bindview/bindview.htm | 345 + network/config/bindview/bindview.sln | 28 + network/config/bindview/bindview.vcxproj | 211 + network/config/bindview/bindview.vcxproj.Filters | 36 + network/modem/fakemodem/ReadMe.md | 8 + network/modem/fakemodem/driver.c | 474 + network/modem/fakemodem/fakemodem.h | 156 + network/modem/fakemodem/fakemodem.sln | 28 + network/modem/fakemodem/fakemodem.vcxproj | 168 + network/modem/fakemodem/fakemodem.vcxproj.Filters | 40 + network/modem/fakemodem/ioctl.c | 790 + network/modem/fakemodem/mdmfake.inx | 267 + network/modem/fakemodem/readwrit.c | 465 + network/ndis/extension/ReadMe.md | 21 + network/ndis/extension/base/SxApi.h | 1196 ++ network/ndis/extension/base/SxBase.c | 1395 ++ network/ndis/extension/base/SxBase.h | 168 + network/ndis/extension/base/SxLibrary.c | 839 + network/ndis/extension/base/SxLibrary.h | 464 + network/ndis/extension/base/precomp.h | 11 + network/ndis/extension/base/precompsrc.c | 1 + network/ndis/extension/base/sxbase.vcxproj | 130 + network/ndis/extension/base/sxbase.vcxproj.Filters | 32 + network/ndis/extension/extensions.sln | 55 + .../samples/forward/MSForwardExtPolicy.mof | 32 + .../samples/forward/MSForwardExtPolicyStatus.mof | 31 + .../ndis/extension/samples/forward/MsForwardExt.c | 2584 +++ .../ndis/extension/samples/forward/MsForwardExt.h | 219 + .../ndis/extension/samples/forward/MsForwardExt.rc | 24 + network/ndis/extension/samples/forward/install.cmd | 6 + .../extension/samples/forward/msforwardext.inf | 85 + .../extension/samples/forward/msforwardext.vcxproj | 127 + .../samples/forward/msforwardext.vcxproj.Filters | 34 + network/ndis/extension/samples/forward/precomp.h | 11 + .../ndis/extension/samples/forward/precompsrc.c | 1 + .../ndis/extension/samples/forward/removeRoute.ps1 | 12 + .../ndis/extension/samples/forward/setRoute.ps1 | 54 + .../ndis/extension/samples/forward/uninstall.cmd | 3 + .../samples/passthrough/MsPassthroughExt.c | 597 + .../samples/passthrough/MsPassthroughExt.rc | 24 + .../ndis/extension/samples/passthrough/install.cmd | 1 + .../samples/passthrough/mspassthroughext.inf | 89 + .../samples/passthrough/mspassthroughext.vcxproj | 127 + .../passthrough/mspassthroughext.vcxproj.Filters | 34 + .../ndis/extension/samples/passthrough/precomp.h | 11 + .../extension/samples/passthrough/precompsrc.c | 1 + .../extension/samples/passthrough/uninstall.cmd | 1 + network/ndis/filter/ReadMe.md | 117 + network/ndis/filter/device.c | 289 + network/ndis/filter/filter.c | 1903 +++ network/ndis/filter/filter.h | 456 + network/ndis/filter/filter.rc | 40 + network/ndis/filter/filter.sln | 28 + network/ndis/filter/filteruser.h | 89 + network/ndis/filter/flt_dbg.c | 409 + network/ndis/filter/flt_dbg.h | 195 + network/ndis/filter/ndislwf.vcxproj | 283 + network/ndis/filter/ndislwf.vcxproj.Filters | 40 + network/ndis/filter/netlwf.inf | 202 + network/ndis/filter/precomp.h | 6 + network/ndis/filter/precompsrc.c | 1 + network/ndis/mux/ReadMe.md | 174 + network/ndis/mux/driver/60/miniport.c | 2953 ++++ network/ndis/mux/driver/60/mux.c | 516 + network/ndis/mux/driver/60/mux.h | 963 ++ network/ndis/mux/driver/60/mux.htm | 617 + network/ndis/mux/driver/60/mux.rc | 50 + network/ndis/mux/driver/60/mux_mp.inf | 69 + network/ndis/mux/driver/60/muxp.inf | 100 + network/ndis/mux/driver/60/novlan/mux.vcxproj | 287 + .../ndis/mux/driver/60/novlan/mux.vcxproj.Filters | 40 + network/ndis/mux/driver/60/novlan/precompsrc.c | 1 + network/ndis/mux/driver/60/precomp.h | 14 + network/ndis/mux/driver/60/protocol.c | 3353 ++++ network/ndis/mux/driver/60/public.h | 46 + network/ndis/mux/driver/60/vlan/muxvlan.vcxproj | 299 + .../mux/driver/60/vlan/muxvlan.vcxproj.Filters | 40 + network/ndis/mux/driver/60/vlan/precompsrc.c | 1 + network/ndis/mux/mux.sln | 66 + network/ndis/mux/notifyob/adapter.cpp | 965 ++ network/ndis/mux/notifyob/adapter.h | 99 + network/ndis/mux/notifyob/common.cpp | 624 + network/ndis/mux/notifyob/common.h | 103 + network/ndis/mux/notifyob/dllmain.cpp | 117 + network/ndis/mux/notifyob/implinc.cpp | 31 + network/ndis/mux/notifyob/list.h | 241 + network/ndis/mux/notifyob/mux.def | 6 + network/ndis/mux/notifyob/mux.vcxproj | 274 + network/ndis/mux/notifyob/mux.vcxproj.Filters | 48 + network/ndis/mux/notifyob/notify.RC | 47 + network/ndis/mux/notifyob/notify.RGS | 14 + network/ndis/mux/notifyob/notify.cpp | 2139 +++ network/ndis/mux/notifyob/notify.h | 282 + network/ndis/mux/notifyob/notify.htm | 552 + network/ndis/mux/notifyob/notifyn.idl | 34 + network/ndis/mux/notifyob/public.h | 47 + network/ndis/mux/notifyob/resource.h | 12 + network/ndis/mux/notifyob/virtual.cpp | 512 + network/ndis/mux/notifyob/virtual.h | 68 + network/ndis/ndisprot/6x/ReadMe.md | 84 + network/ndis/ndisprot/6x/ndisprot60.sln | 63 + network/ndis/ndisprot/6x/sys/60/ndisprot60.inf | 90 + network/ndis/ndisprot/6x/sys/60/ndisprot60.rc | 49 + network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj | 265 + .../ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters | 49 + network/ndis/ndisprot/6x/sys/60/precompsrc.c | 1 + network/ndis/ndisprot/6x/sys/630/ndisprot630.inf | 109 + network/ndis/ndisprot/6x/sys/630/ndisprot630.rc | 49 + .../ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj | 265 + .../6x/sys/630/ndisprot630.vcxproj.Filters | 49 + network/ndis/ndisprot/6x/sys/630/precompsrc.c | 1 + network/ndis/ndisprot/6x/sys/debug.c | 425 + network/ndis/ndisprot/6x/sys/debug.h | 220 + network/ndis/ndisprot/6x/sys/excallbk.c | 195 + network/ndis/ndisprot/6x/sys/macros.h | 234 + network/ndis/ndisprot/6x/sys/ndisbind.c | 2061 +++ network/ndis/ndisprot/6x/sys/ndisprot.h | 567 + network/ndis/ndisprot/6x/sys/ntdisp.c | 916 ++ network/ndis/ndisprot/6x/sys/precomp.h | 18 + network/ndis/ndisprot/6x/sys/protuser.h | 92 + network/ndis/ndisprot/6x/sys/recv.c | 1119 ++ network/ndis/ndisprot/6x/sys/send.c | 463 + network/ndis/ndisprot/6x/test/prottest.c | 665 + network/ndis/ndisprot/6x/test/prottest.vcxproj | 243 + .../ndis/ndisprot/6x/test/prottest.vcxproj.Filters | 22 + network/ndis/netvmini/6x/60/netvmini60.inf | Bin 0 -> 8200 bytes network/ndis/netvmini/6x/60/netvmini60.rc | 51 + network/ndis/netvmini/6x/60/netvmini60.vcxproj | 218 + .../ndis/netvmini/6x/60/netvmini60.vcxproj.Filters | 46 + network/ndis/netvmini/6x/620/netvmini620.inf | Bin 0 -> 12946 bytes network/ndis/netvmini/6x/620/netvmini620.rc | 51 + network/ndis/netvmini/6x/620/netvmini620.vcxproj | 218 + .../netvmini/6x/620/netvmini620.vcxproj.Filters | 49 + network/ndis/netvmini/6x/630/netvmini630.inf | Bin 0 -> 15616 bytes network/ndis/netvmini/6x/630/netvmini630.rc | 50 + network/ndis/netvmini/6x/630/netvmini630.vcxproj | 218 + .../netvmini/6x/630/netvmini630.vcxproj.Filters | 52 + network/ndis/netvmini/6x/ReadMe.md | 15 + network/ndis/netvmini/6x/adapter.c | 2318 +++ network/ndis/netvmini/6x/adapter.h | 376 + network/ndis/netvmini/6x/ctrlpath.c | 1755 ++ network/ndis/netvmini/6x/ctrlpath.h | 28 + network/ndis/netvmini/6x/datapath.c | 1740 ++ network/ndis/netvmini/6x/datapath.h | 77 + network/ndis/netvmini/6x/hardware.h | 321 + network/ndis/netvmini/6x/miniport.c | 622 + network/ndis/netvmini/6x/miniport.h | 255 + network/ndis/netvmini/6x/mphal.c | 1002 ++ network/ndis/netvmini/6x/mphal.h | 126 + network/ndis/netvmini/6x/netvmin6.h | 45 + network/ndis/netvmini/6x/netvmini.sln | 59 + network/ndis/netvmini/6x/qos.c | 653 + network/ndis/netvmini/6x/qos.h | 70 + network/ndis/netvmini/6x/tcbrcb.c | 221 + network/ndis/netvmini/6x/tcbrcb.h | 81 + network/ndis/netvmini/6x/trace.h | 38 + network/ndis/netvmini/6x/vmq.c | 3123 ++++ network/ndis/netvmini/6x/vmq.h | 393 + network/trans/ddproxy/ReadMe.md | 66 + network/trans/ddproxy/ddproxy.sln | 28 + network/trans/ddproxy/sys/DD_drv.c | 1059 ++ network/trans/ddproxy/sys/DD_proxy.c | 1039 ++ network/trans/ddproxy/sys/DD_proxy.h | 251 + network/trans/ddproxy/sys/ddproxy.inf | 63 + network/trans/ddproxy/sys/ddproxy.vcxproj | 185 + network/trans/ddproxy/sys/ddproxy.vcxproj.Filters | 29 + network/trans/inspect/ReadMe.md | 55 + network/trans/inspect/inspect.sln | 28 + network/trans/inspect/sys/TL_drv.c | 927 ++ network/trans/inspect/sys/inspect.c | 1418 ++ network/trans/inspect/sys/inspect.h | 214 + network/trans/inspect/sys/inspect.inf | 63 + network/trans/inspect/sys/inspect.vcxproj | 186 + network/trans/inspect/sys/inspect.vcxproj.Filters | 32 + network/trans/inspect/sys/utils.c | 448 + network/trans/inspect/sys/utils.h | 298 + network/trans/msnmntr/ReadMe.md | 79 + network/trans/msnmntr/exe/monitor.cpp | 715 + network/trans/msnmntr/exe/monitor.vcxproj | 251 + network/trans/msnmntr/exe/monitor.vcxproj.Filters | 22 + network/trans/msnmntr/exe/mtrace.cmd | 288 + network/trans/msnmntr/inc/ioctl.h | 36 + network/trans/msnmntr/inc/mntrguid.h | 44 + network/trans/msnmntr/msnmntr.sln | 46 + network/trans/msnmntr/sys/ctl.c | 149 + network/trans/msnmntr/sys/ctl.h | 19 + network/trans/msnmntr/sys/init.c | 244 + network/trans/msnmntr/sys/msnmntr.c | 883 + network/trans/msnmntr/sys/msnmntr.h | 55 + network/trans/msnmntr/sys/msnmntr.inf | 54 + network/trans/msnmntr/sys/msnmntr.vcxproj | 194 + network/trans/msnmntr/sys/msnmntr.vcxproj.Filters | 35 + network/trans/msnmntr/sys/notify.c | 294 + network/trans/msnmntr/sys/notify.h | 32 + network/trans/stmedit/ReadMe.md | 60 + network/trans/stmedit/stmedit.sln | 28 + network/trans/stmedit/sys/inline_edit.c | 546 + network/trans/stmedit/sys/inline_edit.h | 65 + network/trans/stmedit/sys/oob_edit.c | 1363 ++ network/trans/stmedit/sys/oob_edit.h | 83 + network/trans/stmedit/sys/stmedit.inf | 66 + network/trans/stmedit/sys/stmedit.vcxproj | 186 + network/trans/stmedit/sys/stmedit.vcxproj.Filters | 32 + network/trans/stmedit/sys/stream_callout.c | 772 + network/trans/stmedit/sys/stream_callout.h | 79 + network/wlan/ReadMe.md | 91 + network/wlan/ihvsample/adapters.cpp | 439 + network/wlan/ihvsample/adapters.h | 125 + network/wlan/ihvsample/ihv1xext.xml | 52 + network/wlan/ihvsample/ihvconn.xml | 48 + network/wlan/ihvsample/ihvmachine.xml | 62 + network/wlan/ihvsample/ihvonexext.cpp | 1113 ++ network/wlan/ihvsample/ihvonexext.h | 108 + network/wlan/ihvsample/ihvplapmachineoruser.xml | 64 + network/wlan/ihvsample/ihvplapuser.xml | 64 + network/wlan/ihvsample/ihvsample.cpp | 1554 ++ network/wlan/ihvsample/ihvsample.def | 8 + network/wlan/ihvsample/ihvsample.h | 261 + network/wlan/ihvsample/ihvsample.vcxproj | 254 + network/wlan/ihvsample/ihvsample.vcxproj.Filters | 43 + network/wlan/ihvsample/ihvtimelymachineoruser.xml | 64 + network/wlan/ihvsample/ihvtimelyuser.xml | 63 + network/wlan/ihvsample/ihvwep.cpp | 837 + network/wlan/ihvsample/ihvwep.h | 62 + network/wlan/ihvsample/ihvwep1.xml | 54 + network/wlan/ihvsample/ihvwep2.xml | 54 + network/wlan/ihvsample/precomp.h | 36 + network/wlan/ihvsample/precompsrc.c | 1 + network/wlan/ihvsample/profile.cpp | 1222 ++ network/wlan/ihvsample/profile.h | 108 + network/wlan/ihvsample/rc4utils.h | 67 + network/wlan/ihvsample/utils.cpp | 662 + network/wlan/ihvsample/utils.h | 318 + network/wlan/ihvsampleui/IHVClassFactory.cpp | 166 + network/wlan/ihvsampleui/IHVClassFactory.h | 28 + network/wlan/ihvsampleui/IHVRegistryHelper.cpp | 284 + network/wlan/ihvsampleui/IHVRegistryHelper.h | 40 + network/wlan/ihvsampleui/IHVSample.idl | 71 + network/wlan/ihvsampleui/IHVSampleExtUI.cpp | 971 ++ network/wlan/ihvsampleui/IHVSampleExtUI.h | 238 + network/wlan/ihvsampleui/IHVSampleExtUICon.cpp | 345 + network/wlan/ihvsampleui/IHVSampleExtUICon.h | 103 + network/wlan/ihvsampleui/IHVSampleExtUIKey.cpp | 462 + network/wlan/ihvsampleui/IHVSampleExtUIKey.h | 104 + network/wlan/ihvsampleui/IHVSampleExtUISec.cpp | 434 + network/wlan/ihvsampleui/IHVSampleExtUISec.h | 105 + network/wlan/ihvsampleui/IHVSampleProfile.cpp | 918 ++ network/wlan/ihvsampleui/IHVSampleProfile.h | 296 + network/wlan/ihvsampleui/IHVSampleUI.cpp | 122 + network/wlan/ihvsampleui/IHVSampleUI.def | 7 + network/wlan/ihvsampleui/IHVSampleUI.rc | 129 + network/wlan/ihvsampleui/IHVSampleUI.vcxproj | 258 + .../wlan/ihvsampleui/IHVSampleUI.vcxproj.Filters | 60 + network/wlan/ihvsampleui/IHVUIInc.idl | 1 + network/wlan/ihvsampleui/iunk.h | 54 + network/wlan/ihvsampleui/precomp.h | 43 + network/wlan/ihvsampleui/resource.h | 31 + network/wlan/ihvsampleui/utils.cpp | 408 + network/wlan/ihvsampleui/utils.h | 98 + network/wlan/wlan.sln | 46 + network/wsk/echosrv/ReadMe.md | 58 + network/wsk/echosrv/echosrv.ctl | 1 + network/wsk/echosrv/echosrv.sln | 28 + network/wsk/echosrv/echosrv.vcxproj | 188 + network/wsk/echosrv/echosrv.vcxproj.Filters | 31 + network/wsk/echosrv/wsksmple.c | 1361 ++ network/wsk/echosrv/wsksmple.rc | 11 + nfp/net/ReadMe.md | 11 + nfp/net/driver/Connection.cpp | 296 + nfp/net/driver/FileContext.cpp | 713 + nfp/net/driver/FileContext.h | 340 + nfp/net/driver/NetNfpProvider.vcxproj | 265 + nfp/net/driver/NetNfpProvider.vcxproj.Filters | 60 + nfp/net/driver/Queue.cpp | 824 + nfp/net/driver/Queue.h | 175 + nfp/net/driver/connection.h | 125 + nfp/net/driver/device.cpp | 261 + nfp/net/driver/device.h | 73 + nfp/net/driver/dllsup.cpp | 112 + nfp/net/driver/driver.cpp | 150 + nfp/net/driver/driver.h | 53 + nfp/net/driver/exports.def | 6 + nfp/net/driver/internal.h | 152 + nfp/net/driver/list.h | 119 + nfp/net/driver/netnfpprovider.inx | 83 + nfp/net/driver/netnfpprovider.rc | 18 + nfp/net/driver/socketlistener.cpp | 222 + nfp/net/driver/socketlistener.h | 84 + nfp/net/driver/wppdefs.h | 452 + nfp/net/exe/NetNfpControl.cpp | 579 + nfp/net/exe/NetNfpControl.vcxproj | 262 + nfp/net/exe/NetNfpControl.vcxproj.Filters | 25 + nfp/net/exe/precomp.h | 37 + nfp/net/exe/precompsrc.cpp | 1 + nfp/net/inc/NetNfp.h | 32 + nfp/net/netnfp.sln | 46 + pofx/UMDF2/App/PowerFxApp.cpp | 592 + pofx/UMDF2/App/PowerFxApp.vcxproj | 192 + pofx/UMDF2/App/PowerFxApp.vcxproj.Filters | 25 + pofx/UMDF2/App/UserInput.cpp | 201 + pofx/UMDF2/App/include.h | 95 + pofx/UMDF2/Driver/SingleComp/HwSim.c | 197 + pofx/UMDF2/Driver/SingleComp/HwSim.h | 63 + .../SingleComp/SingleComponentSingleStateUm.inx | 90 + .../SingleComponentSingleStateUm.vcxproj | 213 + .../SingleComponentSingleStateUm.vcxproj.Filters | 37 + pofx/UMDF2/Driver/SingleComp/driver.c | 540 + pofx/UMDF2/Driver/SingleComp/driver.h | 85 + pofx/UMDF2/Driver/SingleComp/include.h | 7 + pofx/UMDF2/ReadMe.md | 53 + pofx/UMDF2/inc/AppInterface.h | 69 + pofx/UMDF2/pofx.sln | 49 + pofx/WDF/App/PowerFxApp.cpp | 592 + pofx/WDF/App/PowerFxApp.vcxproj | 192 + pofx/WDF/App/PowerFxApp.vcxproj.Filters | 25 + pofx/WDF/App/UserInput.cpp | 201 + pofx/WDF/App/include.h | 95 + pofx/WDF/Driver/MultiComp/WdfMultiComp.mht | 2807 ++++ pofx/WDF/Driver/MultiComp/driver/HwSim.c | 251 + pofx/WDF/Driver/MultiComp/driver/HwSim.h | 83 + pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.c | 1036 ++ pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.h | 114 + pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx | 79 + .../Driver/MultiComp/driver/WdfMultiComp.vcxproj | 188 + .../MultiComp/driver/WdfMultiComp.vcxproj.Filters | 37 + pofx/WDF/Driver/MultiComp/inc/WdfPoFx.h | 731 + pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj | 183 + .../Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters | 35 + pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h | 382 + pofx/WDF/Driver/MultiComp/lib/component.c | 562 + pofx/WDF/Driver/MultiComp/lib/device.c | 868 + pofx/WDF/Driver/MultiComp/lib/init.c | 1336 ++ pofx/WDF/Driver/MultiComp/lib/s0idle.c | 471 + pofx/WDF/Driver/MultiComp/lib/s0idle.h | 113 + pofx/WDF/Driver/SingleComp/HwSim.c | 265 + pofx/WDF/Driver/SingleComp/HwSim.h | 74 + .../SingleComp/SingleComponentFStateDriver.vcxproj | 176 + .../SingleComponentFStateDriver.vcxproj.Filters | 37 + .../SingleComp/SingleComponentFStateSample.inx | 105 + pofx/WDF/Driver/SingleComp/driver.c | 1243 ++ pofx/WDF/Driver/SingleComp/driver.h | 150 + pofx/WDF/Driver/SingleComp/include.h | 3 + pofx/WDF/ReadMe.md | 114 + pofx/WDF/inc/AppInterface.h | 69 + pofx/WDF/pofx.sln | 83 + .../BlankProjSrc/BlankProject.vcxproj | 141 + .../BlankProjSrc/BlankProject.vcxproj.Filters | 17 + .../MarkupCompatibility/M1.1a.oxps | Bin 0 -> 110463 bytes .../MarkupCompatibility/M1.2a.oxps | Bin 0 -> 110704 bytes .../MarkupCompatibility/M2.14a.oxps | Bin 0 -> 113173 bytes .../MarkupCompatibility/M2.17a.oxps | Bin 0 -> 114090 bytes .../MarkupCompatibility/M2.20a.oxps | Bin 0 -> 110933 bytes .../MarkupCompatibility/M2.24a.oxps | Bin 0 -> 114270 bytes .../MarkupCompatibility/M2.27a.oxps | Bin 0 -> 113251 bytes .../MarkupCompatibility/M2.27b.oxps | Bin 0 -> 114316 bytes .../MarkupCompatibility/M2.30a.oxps | Bin 0 -> 109949 bytes .../MarkupCompatibility/M2.31a.oxps | Bin 0 -> 109239 bytes .../MarkupCompatibility/M2.32a.oxps | Bin 0 -> 108094 bytes .../MarkupCompatibility/M2.33a.oxps | Bin 0 -> 106754 bytes .../MarkupCompatibility/M2.35a.oxps | Bin 0 -> 110403 bytes .../MarkupCompatibility/M2.36a.oxps | Bin 0 -> 117918 bytes .../MarkupCompatibility/M2.36b.oxps | Bin 0 -> 114139 bytes .../MarkupCompatibility/M2.41a.oxps | Bin 0 -> 109093 bytes .../MarkupCompatibility/M2.41b.oxps | Bin 0 -> 108518 bytes .../MarkupCompatibility/M2.42a.oxps | Bin 0 -> 110434 bytes .../MarkupCompatibility/M2.42b.oxps | Bin 0 -> 109580 bytes .../MarkupCompatibility/M2.43a.oxps | Bin 0 -> 109032 bytes .../MarkupCompatibility/M2.43b.oxps | Bin 0 -> 108300 bytes .../MarkupCompatibility/M2.44a.oxps | Bin 0 -> 107717 bytes .../MarkupCompatibility/M2.48a.oxps | Bin 0 -> 112803 bytes .../MarkupCompatibility/M2.49a.oxps | Bin 0 -> 111223 bytes .../MarkupCompatibility/M2.49b.oxps | Bin 0 -> 110444 bytes .../MarkupCompatibility/M2.50a.oxps | Bin 0 -> 113588 bytes .../MarkupCompatibility/M2.52a.oxps | Bin 0 -> 110381 bytes .../MarkupCompatibility/M2.55a.oxps | Bin 0 -> 109084 bytes .../MarkupCompatibility/M2.56a.oxps | Bin 0 -> 109737 bytes .../ConformanceViolations_oxps/OXPS/M1.2a.oxps | Bin 0 -> 726 bytes .../ConformanceViolations_oxps/OXPS/M10.23a.oxps | Bin 0 -> 103254 bytes .../ConformanceViolations_oxps/OXPS/M10.6a.oxps | Bin 0 -> 102575 bytes .../ConformanceViolations_oxps/OXPS/M10.6b.oxps | Bin 0 -> 102650 bytes .../ConformanceViolations_oxps/OXPS/M12.2a.oxps | Bin 0 -> 104751 bytes .../ConformanceViolations_oxps/OXPS/M12.3a.oxps | Bin 0 -> 104685 bytes .../ConformanceViolations_oxps/OXPS/M12.3b.oxps | Bin 0 -> 110319 bytes .../ConformanceViolations_oxps/OXPS/M12.5a.oxps | Bin 0 -> 107624 bytes .../ConformanceViolations_oxps/OXPS/M12.5b.oxps | Bin 0 -> 107283 bytes .../ConformanceViolations_oxps/OXPS/M12.5c.oxps | Bin 0 -> 107632 bytes .../ConformanceViolations_oxps/OXPS/M12.7a.oxps | Bin 0 -> 104362 bytes .../ConformanceViolations_oxps/OXPS/M2.10a.oxps | Bin 0 -> 106123 bytes .../ConformanceViolations_oxps/OXPS/M2.10b.oxps | Bin 0 -> 106268 bytes .../ConformanceViolations_oxps/OXPS/M2.13a.oxps | Bin 0 -> 101636 bytes .../ConformanceViolations_oxps/OXPS/M2.13b.oxps | Bin 0 -> 101686 bytes .../ConformanceViolations_oxps/OXPS/M2.14a.oxps | Bin 0 -> 102956 bytes .../ConformanceViolations_oxps/OXPS/M2.14b.oxps | Bin 0 -> 102633 bytes .../ConformanceViolations_oxps/OXPS/M2.17a.oxps | Bin 0 -> 177448 bytes .../ConformanceViolations_oxps/OXPS/M2.18a.oxps | Bin 0 -> 64522 bytes .../ConformanceViolations_oxps/OXPS/M2.25a.oxps | Bin 0 -> 120979 bytes .../ConformanceViolations_oxps/OXPS/M2.2a.oxps | Bin 0 -> 100662 bytes .../ConformanceViolations_oxps/OXPS/M2.2b.oxps | Bin 0 -> 101217 bytes .../ConformanceViolations_oxps/OXPS/M2.2c.oxps | Bin 0 -> 101321 bytes .../ConformanceViolations_oxps/OXPS/M2.35a.oxps | Bin 0 -> 107168 bytes .../ConformanceViolations_oxps/OXPS/M2.36a.oxps | Bin 0 -> 102480 bytes .../ConformanceViolations_oxps/OXPS/M2.37a.oxps | Bin 0 -> 1422218 bytes .../ConformanceViolations_oxps/OXPS/M2.39a.oxps | Bin 0 -> 145780 bytes .../ConformanceViolations_oxps/OXPS/M2.39b.oxps | Bin 0 -> 145568 bytes .../ConformanceViolations_oxps/OXPS/M2.39c.oxps | Bin 0 -> 477300 bytes .../ConformanceViolations_oxps/OXPS/M2.3a.oxps | Bin 0 -> 102696 bytes .../ConformanceViolations_oxps/OXPS/M2.3b.oxps | Bin 0 -> 102754 bytes .../ConformanceViolations_oxps/OXPS/M2.3c.oxps | Bin 0 -> 102373 bytes .../ConformanceViolations_oxps/OXPS/M2.4a.oxps | Bin 0 -> 101906 bytes .../ConformanceViolations_oxps/OXPS/M2.4b.oxps | Bin 0 -> 102381 bytes .../ConformanceViolations_oxps/OXPS/M2.54a.oxps | Bin 0 -> 102759 bytes .../ConformanceViolations_oxps/OXPS/M2.59a.oxps | Bin 0 -> 111139 bytes .../ConformanceViolations_oxps/OXPS/M2.59b.oxps | Bin 0 -> 110266 bytes .../ConformanceViolations_oxps/OXPS/M2.59c.oxps | Bin 0 -> 110253 bytes .../ConformanceViolations_oxps/OXPS/M2.5a.oxps | Bin 0 -> 100836 bytes .../ConformanceViolations_oxps/OXPS/M2.5b.oxps | Bin 0 -> 102393 bytes .../ConformanceViolations_oxps/OXPS/M2.6a.oxps | Bin 0 -> 52761 bytes .../ConformanceViolations_oxps/OXPS/M2.6b.oxps | Bin 0 -> 103567 bytes .../ConformanceViolations_oxps/OXPS/M2.6c.oxps | Bin 0 -> 103670 bytes .../ConformanceViolations_oxps/OXPS/M2.70a.oxps | Bin 0 -> 106392 bytes .../ConformanceViolations_oxps/OXPS/M2.71a.oxps | Bin 0 -> 103343 bytes .../ConformanceViolations_oxps/OXPS/M2.71b.oxps | Bin 0 -> 104231 bytes .../ConformanceViolations_oxps/OXPS/M2.71c.oxps | Bin 0 -> 103611 bytes .../ConformanceViolations_oxps/OXPS/M2.72a.oxps | Bin 0 -> 106618 bytes .../ConformanceViolations_oxps/OXPS/M2.72b.oxps | Bin 0 -> 108011 bytes .../ConformanceViolations_oxps/OXPS/M2.72c.oxps | Bin 0 -> 108008 bytes .../ConformanceViolations_oxps/OXPS/M2.73a.oxps | Bin 0 -> 106480 bytes .../ConformanceViolations_oxps/OXPS/M2.73b.oxps | Bin 0 -> 106625 bytes .../ConformanceViolations_oxps/OXPS/M2.74a.oxps | Bin 0 -> 104771 bytes .../ConformanceViolations_oxps/OXPS/M2.74b.oxps | Bin 0 -> 105454 bytes .../ConformanceViolations_oxps/OXPS/M2.74c.oxps | Bin 0 -> 105675 bytes .../ConformanceViolations_oxps/OXPS/M2.75a.oxps | Bin 0 -> 146656 bytes .../ConformanceViolations_oxps/OXPS/M2.75b.oxps | Bin 0 -> 146976 bytes .../ConformanceViolations_oxps/OXPS/M2.75c.oxps | Bin 0 -> 146399 bytes .../ConformanceViolations_oxps/OXPS/M2.75d.oxps | Bin 0 -> 145829 bytes .../ConformanceViolations_oxps/OXPS/M2.76a.oxps | Bin 0 -> 108053 bytes .../ConformanceViolations_oxps/OXPS/M2.77a.oxps | Bin 0 -> 106368 bytes .../ConformanceViolations_oxps/OXPS/M2.77b.oxps | Bin 0 -> 107928 bytes .../ConformanceViolations_oxps/OXPS/M2.7a.oxps | Bin 0 -> 60527 bytes .../ConformanceViolations_oxps/OXPS/M2.7b.oxps | Bin 0 -> 102923 bytes .../ConformanceViolations_oxps/OXPS/M2.7c.oxps | Bin 0 -> 102929 bytes .../ConformanceViolations_oxps/OXPS/M2.80.oxps | Bin 0 -> 477645 bytes .../ConformanceViolations_oxps/OXPS/M2.81.oxps | Bin 0 -> 477491 bytes .../ConformanceViolations_oxps/OXPS/M2.82.oxps | Bin 0 -> 469824 bytes .../ConformanceViolations_oxps/OXPS/M2.8a.oxps | Bin 0 -> 103512 bytes .../ConformanceViolations_oxps/OXPS/M2.8b.oxps | Bin 0 -> 104190 bytes .../ConformanceViolations_oxps/OXPS/M2.8c.oxps | Bin 0 -> 104205 bytes .../ConformanceViolations_oxps/OXPS/M3.11a.oxps | Bin 0 -> 104939 bytes .../ConformanceViolations_oxps/OXPS/M3.11b.oxps | Bin 0 -> 105129 bytes .../ConformanceViolations_oxps/OXPS/M3.12a.oxps | Bin 0 -> 104209 bytes .../ConformanceViolations_oxps/OXPS/M3.12b.oxps | Bin 0 -> 105077 bytes .../ConformanceViolations_oxps/OXPS/M3.13a.oxps | Bin 0 -> 105068 bytes .../ConformanceViolations_oxps/OXPS/M3.14a.oxps | Bin 0 -> 104964 bytes .../ConformanceViolations_oxps/OXPS/M3.20a.oxps | Bin 0 -> 106622 bytes .../ConformanceViolations_oxps/OXPS/M3.20b.oxps | Bin 0 -> 106582 bytes .../ConformanceViolations_oxps/OXPS/M3.2a.oxps | Bin 0 -> 105997 bytes .../ConformanceViolations_oxps/OXPS/M3.2b.oxps | Bin 0 -> 106469 bytes .../ConformanceViolations_oxps/OXPS/M3.3a.oxps | Bin 0 -> 105076 bytes .../ConformanceViolations_oxps/OXPS/M3.5a.oxps | Bin 0 -> 105282 bytes .../ConformanceViolations_oxps/OXPS/M3.5b.oxps | Bin 0 -> 105795 bytes .../ConformanceViolations_oxps/OXPS/M3.6a.oxps | Bin 0 -> 107755 bytes .../ConformanceViolations_oxps/OXPS/M3.6b.oxps | Bin 0 -> 113676 bytes .../ConformanceViolations_oxps/OXPS/M4.1a.oxps | Bin 0 -> 104661 bytes .../ConformanceViolations_oxps/OXPS/M4.1b.oxps | Bin 0 -> 105685 bytes .../ConformanceViolations_oxps/OXPS/M4.1c.oxps | Bin 0 -> 104661 bytes .../ConformanceViolations_oxps/OXPS/M4.2a.oxps | Bin 0 -> 106211 bytes .../ConformanceViolations_oxps/OXPS/M4.2b.oxps | Bin 0 -> 106722 bytes .../ConformanceViolations_oxps/OXPS/M4.3a.oxps | Bin 0 -> 105684 bytes .../ConformanceViolations_oxps/OXPS/M5.15a.oxps | Bin 0 -> 106255 bytes .../ConformanceViolations_oxps/OXPS/M5.2a.oxps | Bin 0 -> 104472 bytes .../ConformanceViolations_oxps/OXPS/M5.2b.oxps | Bin 0 -> 104678 bytes .../ConformanceViolations_oxps/OXPS/M5.2c.oxps | Bin 0 -> 104708 bytes .../ConformanceViolations_oxps/OXPS/M5.3a.oxps | Bin 0 -> 105241 bytes .../ConformanceViolations_oxps/OXPS/M5.3b.oxps | Bin 0 -> 105232 bytes .../ConformanceViolations_oxps/OXPS/M5.3c.oxps | Bin 0 -> 105241 bytes .../ConformanceViolations_oxps/OXPS/M5.4a.oxps | Bin 0 -> 109836 bytes .../ConformanceViolations_oxps/OXPS/M5.4b.oxps | Bin 0 -> 110089 bytes .../ConformanceViolations_oxps/OXPS/M5.7a.oxps | Bin 0 -> 107491 bytes .../ConformanceViolations_oxps/OXPS/M6.1a.oxps | Bin 0 -> 106380 bytes .../ConformanceViolations_oxps/OXPS/M6.1b.oxps | Bin 0 -> 105638 bytes .../ConformanceViolations_oxps/OXPS/M6.2a.oxps | Bin 0 -> 106352 bytes .../ConformanceViolations_oxps/OXPS/M6.2b.oxps | Bin 0 -> 105670 bytes .../ConformanceViolations_oxps/OXPS/M6.3a.oxps | Bin 0 -> 106922 bytes .../ConformanceViolations_oxps/OXPS/M6.3b.oxps | Bin 0 -> 107336 bytes .../ConformanceViolations_oxps/OXPS/M6.4a.oxps | Bin 0 -> 106409 bytes .../ConformanceViolations_oxps/OXPS/M6.4b.oxps | Bin 0 -> 106030 bytes .../ConformanceViolations_oxps/OXPS/M6.5a.oxps | Bin 0 -> 106729 bytes .../ConformanceViolations_oxps/OXPS/M6.5b.oxps | Bin 0 -> 106425 bytes .../ConformanceViolations_oxps/OXPS/M6.6a.oxps | Bin 0 -> 106359 bytes .../ConformanceViolations_oxps/OXPS/M6.6b.oxps | Bin 0 -> 106363 bytes .../ConformanceViolations_oxps/OXPS/M7.11a.oxps | Bin 0 -> 143685 bytes .../ConformanceViolations_oxps/OXPS/M7.11b.oxps | Bin 0 -> 142905 bytes .../ConformanceViolations_oxps/OXPS/M7.2a.oxps | Bin 0 -> 103509 bytes .../ConformanceViolations_oxps/OXPS/M7.2b.oxps | Bin 0 -> 104065 bytes .../ConformanceViolations_oxps/OXPS/M7.3a.oxps | Bin 0 -> 145744 bytes .../ConformanceViolations_oxps/OXPS/M7.5a.oxps | Bin 0 -> 103137 bytes .../ConformanceViolations_oxps/OXPS/M7.6a.oxps | Bin 0 -> 143109 bytes .../ConformanceViolations_oxps/OXPS/M8.14a.oxps | Bin 0 -> 524425 bytes .../ConformanceViolations_oxps/OXPS/M9.10a.oxps | Bin 0 -> 141326 bytes .../ConformanceViolations_oxps/OXPS/M9.10b.oxps | Bin 0 -> 141476 bytes .../OpenPackagingConventions/M1.10a.oxps | Bin 0 -> 146594 bytes .../OpenPackagingConventions/M1.10b.oxps | Bin 0 -> 101375 bytes .../OpenPackagingConventions/M1.11a.oxps | Bin 0 -> 90001 bytes .../OpenPackagingConventions/M1.12a.oxps | Bin 0 -> 125552 bytes .../OpenPackagingConventions/M1.13a.oxps | Bin 0 -> 120137 bytes .../OpenPackagingConventions/M1.13b.oxps | Bin 0 -> 120018 bytes .../OpenPackagingConventions/M1.14a.oxps | Bin 0 -> 120617 bytes .../OpenPackagingConventions/M1.14b.oxps | Bin 0 -> 120571 bytes .../OpenPackagingConventions/M1.14c.oxps | Bin 0 -> 120515 bytes .../OpenPackagingConventions/M1.15a.oxps | Bin 0 -> 116371 bytes .../OpenPackagingConventions/M1.17a.oxps | Bin 0 -> 87089 bytes .../OpenPackagingConventions/M1.17b.oxps | Bin 0 -> 86179 bytes .../OpenPackagingConventions/M1.18a.oxps | Bin 0 -> 83532 bytes .../OpenPackagingConventions/M1.18b.oxps | Bin 0 -> 82355 bytes .../OpenPackagingConventions/M1.1a.oxps | Bin 0 -> 120451 bytes .../OpenPackagingConventions/M1.20a.oxps | Bin 0 -> 122057 bytes .../OpenPackagingConventions/M1.20b.oxps | Bin 0 -> 122193 bytes .../OpenPackagingConventions/M1.21a.oxps | Bin 0 -> 119692 bytes .../OpenPackagingConventions/M1.21b.oxps | Bin 0 -> 120277 bytes .../OpenPackagingConventions/M1.22a.oxps | Bin 0 -> 82619 bytes .../OpenPackagingConventions/M1.25a.oxps | Bin 0 -> 115110 bytes .../OpenPackagingConventions/M1.26a.oxps | Bin 0 -> 87726 bytes .../OpenPackagingConventions/M1.26b.oxps | Bin 0 -> 88950 bytes .../OpenPackagingConventions/M1.27a.oxps | Bin 0 -> 80693 bytes .../OpenPackagingConventions/M1.27b.oxps | Bin 0 -> 82653 bytes .../OpenPackagingConventions/M1.28a.oxps | Bin 0 -> 80925 bytes .../OpenPackagingConventions/M1.28b.oxps | Bin 0 -> 82088 bytes .../OpenPackagingConventions/M1.2a.oxps | Bin 0 -> 137277 bytes .../OpenPackagingConventions/M1.2b.oxps | Bin 0 -> 136477 bytes .../OpenPackagingConventions/M1.2c.oxps | Bin 0 -> 137609 bytes .../OpenPackagingConventions/M1.30a.oxps | Bin 0 -> 83363 bytes .../OpenPackagingConventions/M1.30b.oxps | Bin 0 -> 82894 bytes .../OpenPackagingConventions/M1.3a.oxps | Bin 0 -> 102125 bytes .../OpenPackagingConventions/M1.5a.oxps | Bin 0 -> 123720 bytes .../OpenPackagingConventions/M1.6a.oxps | Bin 0 -> 107037 bytes .../OpenPackagingConventions/M1.6b.oxps | Bin 0 -> 107452 bytes .../OpenPackagingConventions/M1.6c.oxps | Bin 0 -> 106664 bytes .../OpenPackagingConventions/M1.6d.oxps | Bin 0 -> 106686 bytes .../OpenPackagingConventions/M1.7a.oxps | Bin 0 -> 106737 bytes .../OpenPackagingConventions/M1.7b.oxps | Bin 0 -> 107048 bytes .../OpenPackagingConventions/M1.8a.oxps | Bin 0 -> 106099 bytes .../OpenPackagingConventions/M1.8b.oxps | Bin 0 -> 105504 bytes .../OpenPackagingConventions/M1.8c.oxps | Bin 0 -> 105505 bytes .../OpenPackagingConventions/M1.9a.oxps | Bin 0 -> 125830 bytes .../OpenPackagingConventions/M2.10a.oxps | Bin 0 -> 79045 bytes .../OpenPackagingConventions/M2.18a.oxps | Bin 0 -> 127465 bytes .../OpenPackagingConventions/M2.4a.oxps | Bin 0 -> 85086 bytes .../OpenPackagingConventions/M2.5a.oxps | Bin 0 -> 82164 bytes .../OpenPackagingConventions/M2.5b.oxps | Bin 0 -> 82171 bytes .../OpenPackagingConventions/M2.6a.oxps | Bin 0 -> 81169 bytes .../OpenPackagingConventions/M2.6b.oxps | Bin 0 -> 81792 bytes .../OpenPackagingConventions/M2.7a.oxps | Bin 0 -> 81348 bytes .../OpenPackagingConventions/M2.7b.oxps | Bin 0 -> 81608 bytes .../OpenPackagingConventions/M3.10a.oxps | Bin 0 -> 84347 bytes .../OpenPackagingConventions/M3.14a.oxps | Bin 0 -> 133028 bytes .../OpenPackagingConventions/M3.9a.oxps | Bin 0 -> 106930 bytes .../OpenXPSTestFiles_EndUserLicenseAgreement.txt | 7 + .../QualityLogicMinBar_oxps/mb01.oxps | Bin 0 -> 813337 bytes .../QualityLogicMinBar_oxps/mb02.oxps | Bin 0 -> 2243951 bytes .../QualityLogicMinBar_oxps/mb03.oxps | Bin 0 -> 14423522 bytes .../QualityLogicMinBar_oxps/mb04.oxps | Bin 0 -> 2116093 bytes .../QualityLogicMinBar_oxps/mb05.oxps | Bin 0 -> 4577317 bytes .../QualityLogicMinBar_oxps/mb06.oxps | Bin 0 -> 233280 bytes .../QualityLogicMinBar_oxps/mb07.oxps | Bin 0 -> 477710 bytes .../QualityLogicMinBar_oxps/mb08.oxps | Bin 0 -> 160252 bytes .../QualityLogicMinBar_oxps/mb09.oxps | Bin 0 -> 241162 bytes print/SampleOpenXPS/ReadMe.md | 7 + print/SampleOpenXPS/SampleOpenXPS.sln | 28 + print/SampleOpenXPS/readme.txt | 147 + print/SampleXPS/BlankProjSrc/BlankProject.vcxproj | 141 + .../BlankProjSrc/BlankProject.vcxproj.Filters | 17 + .../MarkupCompatibility/M1.1a.xps | Bin 0 -> 281044 bytes .../MarkupCompatibility/M1.2a.xps | Bin 0 -> 281046 bytes .../MarkupCompatibility/M2.13a.xps | Bin 0 -> 281089 bytes .../MarkupCompatibility/M2.14a.xps | Bin 0 -> 281079 bytes .../MarkupCompatibility/M2.17a.xps | Bin 0 -> 281082 bytes .../MarkupCompatibility/M2.20a.xps | Bin 0 -> 281104 bytes .../MarkupCompatibility/M2.24a.xps | Bin 0 -> 281107 bytes .../MarkupCompatibility/M2.27a.xps | Bin 0 -> 281063 bytes .../MarkupCompatibility/M2.27b.xps | Bin 0 -> 281053 bytes .../MarkupCompatibility/M2.30a.xps | Bin 0 -> 281063 bytes .../MarkupCompatibility/M2.31a.xps | Bin 0 -> 281196 bytes .../MarkupCompatibility/M2.32a.xps | Bin 0 -> 281189 bytes .../MarkupCompatibility/M2.33a.xps | Bin 0 -> 281201 bytes .../MarkupCompatibility/M2.35a.xps | Bin 0 -> 281212 bytes .../MarkupCompatibility/M2.35b.xps | Bin 0 -> 281206 bytes .../MarkupCompatibility/M2.36a.xps | Bin 0 -> 281203 bytes .../MarkupCompatibility/M2.41a.xps | Bin 0 -> 281231 bytes .../MarkupCompatibility/M2.42a.xps | Bin 0 -> 281232 bytes .../MarkupCompatibility/M2.43a.xps | Bin 0 -> 281257 bytes .../MarkupCompatibility/M2.44a.xps | Bin 0 -> 281178 bytes .../MarkupCompatibility/M2.48a.xps | Bin 0 -> 281260 bytes .../MarkupCompatibility/M2.49a.xps | Bin 0 -> 281256 bytes .../MarkupCompatibility/M2.50a.xps | Bin 0 -> 281252 bytes .../MarkupCompatibility/M2.52a.xps | Bin 0 -> 281252 bytes .../MarkupCompatibility/M2.55a.xps | Bin 0 -> 281260 bytes .../MarkupCompatibility/M2.56a.xps | Bin 0 -> 281256 bytes .../OpenPackagingConventions/M1.10a.xps | Bin 0 -> 51959 bytes .../OpenPackagingConventions/M1.11a.xps | Bin 0 -> 52024 bytes .../OpenPackagingConventions/M1.17a.xps | Bin 0 -> 51970 bytes .../OpenPackagingConventions/M1.18a.xps | Bin 0 -> 51972 bytes .../OpenPackagingConventions/M1.18b.xps | Bin 0 -> 51972 bytes .../OpenPackagingConventions/M1.1a.xps | Bin 0 -> 51921 bytes .../OpenPackagingConventions/M1.1b.xps | Bin 0 -> 51927 bytes .../OpenPackagingConventions/M1.20a.xps | Bin 0 -> 51975 bytes .../OpenPackagingConventions/M1.26a.xps | Bin 0 -> 51968 bytes .../OpenPackagingConventions/M1.26b.xps | Bin 0 -> 51971 bytes .../OpenPackagingConventions/M1.26c.xps | Bin 0 -> 51969 bytes .../OpenPackagingConventions/M1.27a.xps | Bin 0 -> 51958 bytes .../OpenPackagingConventions/M1.28a.xps | Bin 0 -> 51962 bytes .../OpenPackagingConventions/M1.2a.xps | Bin 0 -> 51961 bytes .../OpenPackagingConventions/M1.2b.xps | Bin 0 -> 51957 bytes .../OpenPackagingConventions/M1.34a.xps | Bin 0 -> 51979 bytes .../OpenPackagingConventions/M1.3a.xps | Bin 0 -> 51932 bytes .../OpenPackagingConventions/M1.5a.xps | Bin 0 -> 51933 bytes .../OpenPackagingConventions/M1.6a.xps | Bin 0 -> 51934 bytes .../OpenPackagingConventions/M1.6b.xps | Bin 0 -> 51936 bytes .../OpenPackagingConventions/M1.7a.xps | Bin 0 -> 51980 bytes .../OpenPackagingConventions/M1.7c.xps | Bin 0 -> 51968 bytes .../OpenPackagingConventions/M1.8a.xps | Bin 0 -> 51971 bytes .../OpenPackagingConventions/M2.18a.xps | Bin 0 -> 52359 bytes .../OpenPackagingConventions/M2.24a.xps | Bin 0 -> 51924 bytes .../OpenPackagingConventions/M2.5a.xps | Bin 0 -> 51975 bytes .../OpenPackagingConventions/M2.5b.xps | Bin 0 -> 51974 bytes .../OpenPackagingConventions/M2.6a.xps | Bin 0 -> 51967 bytes .../OpenPackagingConventions/M2.6b.xps | Bin 0 -> 51965 bytes .../OpenPackagingConventions/M2.7a.xps | Bin 0 -> 51956 bytes .../OpenPackagingConventions/M2.7b.xps | Bin 0 -> 51950 bytes .../OpenPackagingConventions/M3.10a.xps | Bin 0 -> 57170 bytes .../OpenPackagingConventions/M3.10b.xps | Bin 0 -> 57171 bytes .../OpenPackagingConventions/M3.10c.xps | Bin 0 -> 57170 bytes .../OpenPackagingConventions/M3.11a.xps | Bin 0 -> 57167 bytes .../OpenPackagingConventions/M3.11b.xps | Bin 0 -> 57164 bytes .../OpenPackagingConventions/M3.12a.xps | Bin 0 -> 57167 bytes .../OpenPackagingConventions/M3.12b.xps | Bin 0 -> 57164 bytes .../OpenPackagingConventions/M3.13a.xps | Bin 0 -> 57169 bytes .../OpenPackagingConventions/M3.14a.xps | Bin 0 -> 57163 bytes .../OpenPackagingConventions/M3.15a.xps | Bin 0 -> 57089 bytes .../OpenPackagingConventions/M3.16a.xps | Bin 0 -> 56390 bytes .../OpenPackagingConventions/M3.17a.xps | Bin 0 -> 57117 bytes .../OpenPackagingConventions/M3.17b.xps | Bin 0 -> 57167 bytes .../OpenPackagingConventions/M3.18a.xps | Bin 0 -> 57168 bytes .../OpenPackagingConventions/M3.1a.xps | Bin 0 -> 51964 bytes .../OpenPackagingConventions/M3.1b.xps | Bin 0 -> 52382 bytes .../OpenPackagingConventions/M3.20a.xps | Bin 0 -> 57169 bytes .../OpenPackagingConventions/M3.21a.xps | Bin 0 -> 57166 bytes .../OpenPackagingConventions/M3.21b.xps | Bin 0 -> 57161 bytes .../OpenPackagingConventions/M3.25a.xps | Bin 0 -> 57164 bytes .../OpenPackagingConventions/M3.26a.xps | Bin 0 -> 57165 bytes .../OpenPackagingConventions/M3.28a.xps | Bin 0 -> 57167 bytes .../OpenPackagingConventions/M3.29a.xps | Bin 0 -> 57166 bytes .../OpenPackagingConventions/M3.2a.xps | Bin 0 -> 52018 bytes .../OpenPackagingConventions/M3.31a.xps | Bin 0 -> 57165 bytes .../OpenPackagingConventions/M3.31b.xps | Bin 0 -> 57166 bytes .../OpenPackagingConventions/M3.32a.xps | Bin 0 -> 57166 bytes .../OpenPackagingConventions/M3.32b.xps | Bin 0 -> 57165 bytes .../OpenPackagingConventions/M3.4a.xps | Bin 0 -> 57304 bytes .../OpenPackagingConventions/M3.4b.xps | Bin 0 -> 57069 bytes .../OpenPackagingConventions/M3.5a.xps | Bin 0 -> 57016 bytes .../OpenPackagingConventions/M3.6a.xps | Bin 0 -> 57023 bytes .../OpenPackagingConventions/M3.7a.xps | Bin 0 -> 57167 bytes .../OpenPackagingConventions/M3.8a.xps | Bin 0 -> 57224 bytes .../OpenPackagingConventions/M3.9a.xps | Bin 0 -> 57175 bytes .../OpenPackagingConventions/M3.9b.xps | Bin 0 -> 57181 bytes .../OpenPackagingConventions/M3.9c.xps | Bin 0 -> 57169 bytes .../OpenPackagingConventions/M4.1a.xps | Bin 0 -> 51970 bytes .../OpenPackagingConventions/M4.2a.xps | Bin 0 -> 51971 bytes .../OpenPackagingConventions/M4.3a.xps | Bin 0 -> 51975 bytes .../OpenPackagingConventions/M6.1a.xps | Bin 0 -> 55529 bytes .../OpenPackagingConventions/M6.2a.xps | Bin 0 -> 55529 bytes .../OpenPackagingConventions/M6.4a.xps | Bin 0 -> 55529 bytes .../OpenPackagingConventions/M6.6a.xps | Bin 0 -> 55529 bytes .../OpenPackagingConventions/M6.7a.xps | Bin 0 -> 55529 bytes .../OpenPackagingConventions/M6.8a.xps | Bin 0 -> 55529 bytes .../OpenPackagingConventions/M7.2a.xps | Bin 0 -> 51976 bytes .../SampleXPS/ConformanceViolations/XPS/M1.2a.xps | Bin 0 -> 306 bytes .../SampleXPS/ConformanceViolations/XPS/M12.2a.xps | Bin 0 -> 79569 bytes .../SampleXPS/ConformanceViolations/XPS/M12.3b.xps | Bin 0 -> 157979 bytes .../SampleXPS/ConformanceViolations/XPS/M12.5a.xps | Bin 0 -> 7172 bytes .../SampleXPS/ConformanceViolations/XPS/M12.7a.xps | Bin 0 -> 79569 bytes .../SampleXPS/ConformanceViolations/XPS/M2.10a.xps | Bin 0 -> 52433 bytes .../SampleXPS/ConformanceViolations/XPS/M2.13a.xps | Bin 0 -> 52452 bytes .../SampleXPS/ConformanceViolations/XPS/M2.13b.xps | Bin 0 -> 55919 bytes .../SampleXPS/ConformanceViolations/XPS/M2.14a.xps | Bin 0 -> 52502 bytes .../SampleXPS/ConformanceViolations/XPS/M2.18a.xps | Bin 0 -> 1621196 bytes .../SampleXPS/ConformanceViolations/XPS/M2.25a.xps | Bin 0 -> 9501586 bytes .../SampleXPS/ConformanceViolations/XPS/M2.36a.xps | Bin 0 -> 81864 bytes .../SampleXPS/ConformanceViolations/XPS/M2.37a.xps | Bin 0 -> 84470 bytes .../SampleXPS/ConformanceViolations/XPS/M2.3a.xps | Bin 0 -> 52234 bytes .../SampleXPS/ConformanceViolations/XPS/M2.3b.xps | Bin 0 -> 52452 bytes .../SampleXPS/ConformanceViolations/XPS/M2.3c.xps | Bin 0 -> 55919 bytes .../SampleXPS/ConformanceViolations/XPS/M2.4a.xps | Bin 0 -> 52180 bytes .../SampleXPS/ConformanceViolations/XPS/M2.4b.xps | Bin 0 -> 52419 bytes .../SampleXPS/ConformanceViolations/XPS/M2.59a.xps | Bin 0 -> 5266 bytes .../SampleXPS/ConformanceViolations/XPS/M2.5a.xps | Bin 0 -> 51800 bytes .../SampleXPS/ConformanceViolations/XPS/M2.5b.xps | Bin 0 -> 52373 bytes .../SampleXPS/ConformanceViolations/XPS/M2.6a.xps | Bin 0 -> 5839 bytes .../SampleXPS/ConformanceViolations/XPS/M2.6b.xps | Bin 0 -> 52382 bytes .../SampleXPS/ConformanceViolations/XPS/M2.71a.xps | Bin 0 -> 79658 bytes .../SampleXPS/ConformanceViolations/XPS/M2.71b.xps | Bin 0 -> 79650 bytes .../SampleXPS/ConformanceViolations/XPS/M2.71c.xps | Bin 0 -> 79761 bytes .../SampleXPS/ConformanceViolations/XPS/M2.73a.xps | Bin 0 -> 79606 bytes .../SampleXPS/ConformanceViolations/XPS/M2.73b.xps | Bin 0 -> 79624 bytes .../SampleXPS/ConformanceViolations/XPS/M2.74a.xps | Bin 0 -> 13699 bytes .../SampleXPS/ConformanceViolations/XPS/M2.75a.xps | Bin 0 -> 79578 bytes .../SampleXPS/ConformanceViolations/XPS/M2.75b.xps | Bin 0 -> 79583 bytes .../SampleXPS/ConformanceViolations/XPS/M2.76a.xps | Bin 0 -> 79563 bytes .../SampleXPS/ConformanceViolations/XPS/M3.2a.xps | Bin 0 -> 52499 bytes .../SampleXPS/ConformanceViolations/XPS/M3.3a.xps | Bin 0 -> 52464 bytes .../SampleXPS/ConformanceViolations/XPS/M3.5a.xps | Bin 0 -> 52457 bytes .../SampleXPS/ConformanceViolations/XPS/M3.6a.xps | Bin 0 -> 55591 bytes .../SampleXPS/ConformanceViolations/XPS/M3.6b.xps | Bin 0 -> 52463 bytes .../SampleXPS/ConformanceViolations/XPS/M4.3a.xps | Bin 0 -> 2396 bytes .../SampleXPS/ConformanceViolations/XPS/M5.15a.xps | Bin 0 -> 37130 bytes .../SampleXPS/ConformanceViolations/XPS/M5.2a.xps | Bin 0 -> 26289 bytes .../SampleXPS/ConformanceViolations/XPS/M5.2b.xps | Bin 0 -> 26292 bytes .../SampleXPS/ConformanceViolations/XPS/M5.4a.xps | Bin 0 -> 26348 bytes .../SampleXPS/ConformanceViolations/XPS/M5.4b.xps | Bin 0 -> 26340 bytes .../SampleXPS/ConformanceViolations/XPS/M5.7a.xps | Bin 0 -> 13158 bytes .../SampleXPS/ConformanceViolations/XPS/M6.2a.xps | Bin 0 -> 79597 bytes .../SampleXPS/ConformanceViolations/XPS/M6.3a.xps | Bin 0 -> 13735 bytes .../SampleXPS/ConformanceViolations/XPS/M7.5a.xps | Bin 0 -> 19114 bytes print/SampleXPS/QualityLogicMinBar/mb01.xps | Bin 0 -> 813339 bytes print/SampleXPS/QualityLogicMinBar/mb02.xps | Bin 0 -> 2243925 bytes print/SampleXPS/QualityLogicMinBar/mb03.xps | Bin 0 -> 14423371 bytes print/SampleXPS/QualityLogicMinBar/mb04.xps | Bin 0 -> 2115955 bytes print/SampleXPS/QualityLogicMinBar/mb05.xps | Bin 0 -> 4577346 bytes print/SampleXPS/QualityLogicMinBar/mb06.xps | Bin 0 -> 231855 bytes print/SampleXPS/QualityLogicMinBar/mb07.xps | Bin 0 -> 468479 bytes print/SampleXPS/QualityLogicMinBar/mb08.xps | Bin 0 -> 160268 bytes print/SampleXPS/QualityLogicMinBar/mb09.xps | Bin 0 -> 236946 bytes print/SampleXPS/ReadMe.md | 7 + print/SampleXPS/SampleXPS.sln | 28 + .../XPSTestFiles_EndUserLicenseAgreement.txt | 7 + print/SampleXPS/readme.txt | 190 + print/SimplePipelineFilter/FilterSample.def | 8 + print/SimplePipelineFilter/FilterSample.rc | 10 + print/SimplePipelineFilter/ReadMe.md | 9 + .../SimplePipelineFilter/SimplePipelineFilter.sln | 28 + print/SimplePipelineFilter/StreamFilter.cxx | 263 + print/SimplePipelineFilter/StreamFilter.hxx | 99 + .../SimplePipelineFilter/WdkPipelineFilter.vcxproj | 294 + .../WdkPipelineFilter.vcxproj.Filters | 39 + print/SimplePipelineFilter/XpsFilter.cxx | 785 + print/SimplePipelineFilter/XpsFilter.hxx | 126 + print/SimplePipelineFilter/main.cxx | 172 + print/SimplePipelineFilter/main.hxx | 51 + print/SimplePipelineFilter/precomp.hxx | 26 + print/SimplePipelineFilter/precompsrc.cpp | 1 + print/SimplePipelineFilter/smartptr.hxx | 233 + print/SimplePipelineFilter/trace.hxx | 51 + print/XpsRasFilter/ReadMe.md | 38 + print/XpsRasFilter/XpsRasFilter.sln | 28 + .../install/xpsrassmpl-PipelineConfig.xml | 32 + print/XpsRasFilter/install/xpsrassmpl.gpd | 342 + print/XpsRasFilter/install/xpsrassmpl.inf | 87 + print/XpsRasFilter/src/BitmapHandler.cpp | 361 + print/XpsRasFilter/src/BitmapHandler.h | 64 + print/XpsRasFilter/src/Exception.cpp | 43 + print/XpsRasFilter/src/Exception.h | 86 + print/XpsRasFilter/src/OMConvertor.cpp | 774 + print/XpsRasFilter/src/OMConvertor.h | 90 + print/XpsRasFilter/src/PThandler.cpp | 781 + print/XpsRasFilter/src/PThandler.h | 112 + print/XpsRasFilter/src/UnknownBase.h | 158 + print/XpsRasFilter/src/WppTrace.cpp | 41 + print/XpsRasFilter/src/WppTrace.h | 58 + print/XpsRasFilter/src/dllentry.cpp | 326 + print/XpsRasFilter/src/filtertypes.h | 497 + print/XpsRasFilter/src/precomp.h | 86 + print/XpsRasFilter/src/precompsrc.cpp | 1 + print/XpsRasFilter/src/rasinterface.cpp | 412 + print/XpsRasFilter/src/rasinterface.h | 107 + print/XpsRasFilter/src/xpsrasfilter.cpp | 396 + print/XpsRasFilter/src/xpsrasfilter.def | 22 + print/XpsRasFilter/src/xpsrasfilter.h | 141 + print/XpsRasFilter/src/xpsrasfilter.rc | 28 + print/XpsRasFilter/src/xpsrasfilter.vcxproj | 349 + .../XpsRasFilter/src/xpsrasfilter.vcxproj.Filters | 54 + print/cpsuisam/CPSUIDAT.C | 1331 ++ print/cpsuisam/CPSUIDAT.H | 73 + print/cpsuisam/CPSUISAM.DEF | 6 + print/cpsuisam/CPSUISAM.DLG | 35 + print/cpsuisam/CPSUISAM.H | 37 + print/cpsuisam/DEBUG.C | 114 + print/cpsuisam/DEBUG.H | 66 + print/cpsuisam/PRECOMP.H | 17 + print/cpsuisam/ReadMe.md | 13 + print/cpsuisam/cpsuisam.c | 665 + print/cpsuisam/cpsuisam.sln | 28 + print/cpsuisam/cpsuisam.vcxproj | 255 + print/cpsuisam/cpsuisam.vcxproj.Filters | 36 + print/cpsuisam/icons/apple.ico | Bin 0 -> 1078 bytes print/cpsuisam/icons/cpsuisam.ico | Bin 0 -> 1078 bytes print/cpsuisam/precompsrc.c | 1 + print/cpsuisam/res.rc | 51 + .../ExtensionSample/App.xaml | 7 + .../ExtensionSample/App.xaml.cs | 146 + .../ExtensionSample/BidiHelper.cs | 154 + .../ExtensionSample/Fabrikam_Logo.png | Bin 0 -> 2589 bytes .../ExtensionSample/PrintPreferenceWindow.xaml | 147 + .../ExtensionSample/PrintPreferenceWindow.xaml.cs | 467 + .../ExtensionSample/PrintSchemaHelper.cs | 190 + .../ExtensionSample/PrinterExtensionSample.csproj | 219 + .../ExtensionSample/Properties/AssemblyInfo.cs | 62 + .../Properties/Resources.Designer.cs | 63 + .../ExtensionSample/Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 26 + .../ExtensionSample/Properties/Settings.settings | 7 + .../ExtensionSample/Strings.Designer.cs | 82 + .../ExtensionSample/Strings.resx | 129 + .../ExtensionSample/ValidationModalDialog.xaml | 19 + .../ExtensionSample/ValidationModalDialog.xaml.cs | 108 + .../ExtensionSample/WindowHelper.cs | 83 + .../ExtensionSample/app.config | 9 + .../ExtensionSample/bidi_Ink_mock.xml | 74 + .../PrinterExtensionAdapters.cs | 2051 +++ .../PrinterExtensionLibrary.csproj | 83 + .../PrinterExtensionManager.cs | 121 + .../PrinterExtensionTypes.cs | 921 ++ .../Properties/AssemblyInfo.cs | 33 + .../PrinterExtensionSample.sln | 86 + .../PrinterExtensionSample/ReadMe.md | 14 + .../ConstraintScript.js | 679 + .../ConstraintScript.sln | 28 + .../ConstraintScript.vcxproj | 150 + .../ConstraintScript.vcxproj.Filters | 21 + .../v4PrintDriver-ConstraintScript/ReadMe.md | 23 + .../v4PrintDriver-Intellisense-Windows8.1.js | 126 + .../v4PrintDriver-Intellisense.js | 513 + .../v4PrintDriver-USBMon-Bidi-Extension/ReadMe.md | 44 + .../USBMon-Bidi-Extension.js | Bin 0 -> 34662 bytes .../USBMon-Bidi-Extension.sln | 28 + .../USBMon-Bidi-Extension.vcxproj | 151 + .../USBMon-Bidi-Extension.vcxproj.Filters | 26 + .../USBMon-Bidi-Extension.xml | 24 + .../v4PrintDriver-Intellisense-Windows8.1.js | 126 + .../v4PrintDriver-Intellisense.js | 513 + .../v4PrintDriver-WSDMon-Bidi-Extension/ReadMe.md | 47 + .../WSDMon-Bidi-Extension.sln | 28 + .../WSDMon-Bidi-Extension.vcxproj | 150 + .../WSDMon-Bidi-Extension.vcxproj.Filters | 26 + .../WSDMon-Bidi-Extension.xml | 79 + sd/sdiomars/ReadMe.md | 24 + sd/sdiomars/mars.c | 1124 ++ sd/sdiomars/mars.h | 200 + sd/sdiomars/mars.inx | Bin 0 -> 4408 bytes sd/sdiomars/mars.vcxproj | 180 + sd/sdiomars/mars.vcxproj.Filters | 34 + sd/sdiomars/ntddmars.h | 113 + sd/sdiomars/sdiomars.sln | 28 + security/elam/ReadMe.md | 111 + security/elam/elam.sln | 28 + security/elam/elamsample.c | 473 + security/elam/elamsample.h | 174 + security/elam/elamsample.rc | 10 + security/elam/elamsample.vcxproj | 141 + security/elam/elamsample.vcxproj.Filters | 31 + sensors/ADXL345Acc/ADXL345Acc.asl | 57 + sensors/ADXL345Acc/ADXL345Acc.def | 6 + sensors/ADXL345Acc/ADXL345Acc.inx | 95 + sensors/ADXL345Acc/ADXL345Acc.sln | 28 + sensors/ADXL345Acc/ADXL345Acc.vcxproj | 205 + sensors/ADXL345Acc/ADXL345Acc.vcxproj.Filters | 43 + sensors/ADXL345Acc/Device.h | 173 + sensors/ADXL345Acc/Driver.h | 19 + sensors/ADXL345Acc/ReadMe.md | 7 + sensors/ADXL345Acc/SensorsTrace.h | 102 + sensors/ADXL345Acc/adxl345.h | 123 + sensors/ADXL345Acc/client.cpp | 1036 ++ sensors/ADXL345Acc/device.cpp | 474 + sensors/ADXL345Acc/driver.cpp | 66 + sensors/Activity/Activity.def | 6 + sensors/Activity/Activity.inx | 95 + sensors/Activity/Activity.sln | 28 + sensors/Activity/Activity.vcxproj | 205 + sensors/Activity/Activity.vcxproj.Filters | 46 + sensors/Activity/Device.h | 199 + sensors/Activity/Driver.h | 16 + sensors/Activity/HardwareSimulator.h | 49 + sensors/Activity/ReadMe.md | 8 + sensors/Activity/SensorsTrace.h | 93 + sensors/Activity/client.cpp | 1021 ++ sensors/Activity/device.cpp | 831 + sensors/Activity/driver.cpp | 55 + sensors/Activity/hardwaresimulator.cpp | 276 + sensors/CustomSensors/CustomSensors.def | 6 + sensors/CustomSensors/CustomSensors.inx | 95 + sensors/CustomSensors/CustomSensors.sln | 28 + sensors/CustomSensors/CustomSensors.vcxproj | 205 + .../CustomSensors/CustomSensors.vcxproj.Filters | 46 + sensors/CustomSensors/Device.h | 136 + sensors/CustomSensors/Driver.h | 19 + sensors/CustomSensors/HardwareSimulator.h | 57 + sensors/CustomSensors/ReadMe.md | 7 + sensors/CustomSensors/SensorsTrace.h | 144 + sensors/CustomSensors/client.cpp | 593 + sensors/CustomSensors/device.cpp | 595 + sensors/CustomSensors/driver.cpp | 80 + sensors/CustomSensors/hardwaresimulator.cpp | 249 + sensors/Pedometer/Device.h | 209 + sensors/Pedometer/Driver.h | 19 + sensors/Pedometer/HardwareSimulator.h | 102 + sensors/Pedometer/Pedometer.def | 6 + sensors/Pedometer/Pedometer.inx | 95 + sensors/Pedometer/Pedometer.sln | 28 + sensors/Pedometer/Pedometer.vcxproj | 205 + sensors/Pedometer/Pedometer.vcxproj.Filters | 46 + sensors/Pedometer/ReadMe.md | 7 + sensors/Pedometer/SensorsTrace.h | 93 + sensors/Pedometer/client.cpp | 1224 ++ sensors/Pedometer/device.cpp | 738 + sensors/Pedometer/driver.cpp | 76 + sensors/Pedometer/hardwaresimulator.cpp | 677 + sensors/SimpleDeviceOrientationSensor/Device.h | 134 + sensors/SimpleDeviceOrientationSensor/Driver.h | 19 + .../HardwareSimulator.h | 58 + sensors/SimpleDeviceOrientationSensor/ReadMe.md | 7 + .../SimpleDeviceOrientationSensor/SensorsTrace.h | 90 + .../SimpleDeviceOrientationSensor.def | 6 + .../SimpleDeviceOrientationSensor.inx | 96 + .../SimpleDeviceOrientationSensor.sln | 28 + .../SimpleDeviceOrientationSensor.vcxproj | 205 + .../SimpleDeviceOrientationSensor.vcxproj.Filters | 46 + sensors/SimpleDeviceOrientationSensor/client.cpp | 577 + sensors/SimpleDeviceOrientationSensor/device.cpp | 567 + sensors/SimpleDeviceOrientationSensor/driver.cpp | 75 + .../hardwaresimulator.cpp | 248 + serial/VirtualSerial/ComPort/exports.def | 6 + serial/VirtualSerial/ComPort/virtualserial.inx | 78 + serial/VirtualSerial/ComPort/virtualserial.rc | 17 + serial/VirtualSerial/ComPort/virtualserial.vcxproj | 256 + .../ComPort/virtualserial.vcxproj.Filters | 57 + serial/VirtualSerial/FakeModem/exports.def | 6 + serial/VirtualSerial/FakeModem/fakemodem.inx | 202 + serial/VirtualSerial/FakeModem/fakemodem.rc | 17 + serial/VirtualSerial/FakeModem/fakemodem.vcxproj | 256 + .../FakeModem/fakemodem.vcxproj.Filters | 57 + serial/VirtualSerial/ReadMe.md | 59 + serial/VirtualSerial/VirtualSerial.sln | 46 + serial/VirtualSerial/comsup.cpp | 344 + serial/VirtualSerial/comsup.h | 215 + serial/VirtualSerial/device.cpp | 674 + serial/VirtualSerial/device.h | 261 + serial/VirtualSerial/dllsup.cpp | 177 + serial/VirtualSerial/driver.cpp | 220 + serial/VirtualSerial/driver.h | 149 + serial/VirtualSerial/internal.h | 133 + serial/VirtualSerial/queue.cpp | 1188 ++ serial/VirtualSerial/queue.h | 206 + serial/VirtualSerial/ringbuffer.cpp | 258 + serial/VirtualSerial/ringbuffer.h | 223 + serial/VirtualSerial/serial.h | 48 + serial/serenum/ReadMe.md | 39 + serial/serenum/SerEnum_sample.vcxproj | 219 + serial/serenum/SerEnum_sample.vcxproj.Filters | 49 + serial/serenum/enum.c | 1834 +++ serial/serenum/log.c | 185 + serial/serenum/log.h | 57 + serial/serenum/pch.h | 15 + serial/serenum/pchsrc.c | 1 + serial/serenum/pnp.c | 1505 ++ serial/serenum/power.c | 299 + serial/serenum/serenum.c | 766 + serial/serenum/serenum.h | 626 + serial/serenum/serenum.rc | 16 + serial/serenum/serenum.sln | 28 + serial/serenum/string.c | 1029 ++ serial/serial/ReadMe.md | 35 + serial/serial/error.c | 67 + serial/serial/flush.c | 86 + serial/serial/immediat.c | 458 + serial/serial/initunlo.c | 197 + serial/serial/ioctl.c | 2195 +++ serial/serial/isr.c | 1517 ++ serial/serial/log.c | 97 + serial/serial/log.h | 37 + serial/serial/modmflow.c | 1714 ++ serial/serial/openclos.c | 850 + serial/serial/pnp.c | 2800 ++++ serial/serial/power.c | 331 + serial/serial/precomp.h | 18 + serial/serial/precompsrc.c | 1 + serial/serial/purge.c | 175 + serial/serial/qsfile.c | 180 + serial/serial/read.c | 1748 ++ serial/serial/registry.c | 443 + serial/serial/serial.h | 1757 ++ serial/serial/serial.inx | 111 + serial/serial/serial.rc | 14 + serial/serial/serial.sln | 28 + serial/serial/serialp.h | 596 + serial/serial/serlog.mc | 290 + serial/serial/trace.h | 118 + serial/serial/utils.c | 1946 +++ serial/serial/waitmask.c | 574 + serial/serial/wdfserial.vcxproj | 389 + serial/serial/wdfserial.vcxproj.Filters | 99 + serial/serial/wmi.c | 295 + serial/serial/write.c | 1195 ++ setup/DIFxAPI/AppDrv/AppDrv.vcxproj | 187 + setup/DIFxAPI/AppDrv/AppDrv.vcxproj.Filters | 22 + setup/DIFxAPI/AppDrv/AppDrvDepend.c | 147 + setup/DIFxAPI/AppDrv/readme.htm | 109 + setup/DIFxAPI/DIFxAPI.sln | 46 + setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj | 187 + setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj.Filters | 22 + setup/DIFxAPI/DIFxCmd/difxcmd.c | 218 + setup/DIFxAPI/DIFxCmd/readme.htm | 59 + setup/DIFxAPI/ReadMe.md | 5 + setup/devcon/ReadMe.md | 111 + setup/devcon/cmds.cpp | 2408 +++ setup/devcon/devcon.cpp | 1162 ++ setup/devcon/devcon.h | 133 + setup/devcon/devcon.rc | 37 + setup/devcon/devcon.sln | 28 + setup/devcon/devcon.vcxproj | 183 + setup/devcon/devcon.vcxproj.Filters | 33 + setup/devcon/dump.cpp | 1328 ++ setup/devcon/msg.mc | 987 ++ setup/devcon/rc_ids.h | 28 + spb/SkeletonI2C/ReadMe.md | 117 + spb/SkeletonI2C/SkeletonI2C.sln | 28 + spb/SkeletonI2C/controller.cpp | 851 + spb/SkeletonI2C/controller.h | 76 + spb/SkeletonI2C/device.cpp | 2268 +++ spb/SkeletonI2C/device.h | 368 + spb/SkeletonI2C/driver.cpp | 454 + spb/SkeletonI2C/driver.h | 36 + spb/SkeletonI2C/hw.cpp | 83 + spb/SkeletonI2C/hw.h | 88 + spb/SkeletonI2C/i2ctrace.h | 57 + spb/SkeletonI2C/internal.h | 293 + spb/SkeletonI2C/resource.rc | 11 + spb/SkeletonI2C/skeletoni2c.asl | 14 + spb/SkeletonI2C/skeletoni2c.h | 96 + spb/SkeletonI2C/skeletoni2c.inx | Bin 0 -> 3856 bytes spb/SkeletonI2C/skeletoni2c.vcxproj | 198 + spb/SkeletonI2C/skeletoni2c.vcxproj.Filters | 48 + spb/SpbTestTool/ReadMe.md | 203 + spb/SpbTestTool/SpbTestTool.sln | 46 + spb/SpbTestTool/exe/SpbTestTool.vcxproj | 205 + spb/SpbTestTool/exe/SpbTestTool.vcxproj.Filters | 28 + spb/SpbTestTool/exe/command.cpp | 660 + spb/SpbTestTool/exe/command.h | 638 + spb/SpbTestTool/exe/internal.h | 99 + spb/SpbTestTool/exe/main.cpp | 633 + spb/SpbTestTool/exe/util.cpp | 250 + spb/SpbTestTool/sys/SpbTestTool.vcxproj | 198 + spb/SpbTestTool/sys/SpbTestTool.vcxproj.Filters | 45 + spb/SpbTestTool/sys/device.cpp | 848 + spb/SpbTestTool/sys/device.h | 43 + spb/SpbTestTool/sys/driver.cpp | 414 + spb/SpbTestTool/sys/driver.h | 36 + spb/SpbTestTool/sys/internal.h | 144 + spb/SpbTestTool/sys/peripheral.cpp | 1683 ++ spb/SpbTestTool/sys/peripheral.h | 104 + spb/SpbTestTool/sys/resource.rc | 11 + spb/SpbTestTool/sys/spbtestioctl.h | 50 + spb/SpbTestTool/sys/spbtesttool.asl | 26 + spb/SpbTestTool/sys/spbtesttool.inx | Bin 0 -> 4682 bytes spb/SpbTestTool/sys/trace.h | 54 + storage/class/classpnp/ReadMe.md | 15 + storage/class/classpnp/classpnp.sln | 28 + storage/class/classpnp/src/autorun.c | 4388 +++++ storage/class/classpnp/src/class.c | 16415 +++++++++++++++++++ storage/class/classpnp/src/class.def | 119 + storage/class/classpnp/src/class.rc | 22 + storage/class/classpnp/src/classlog.mof | 175 + storage/class/classpnp/src/classp.h | 2619 +++ storage/class/classpnp/src/classpnp.htm | 273 + storage/class/classpnp/src/classpnp.vcxproj | 322 + .../class/classpnp/src/classpnp.vcxproj.Filters | 87 + storage/class/classpnp/src/classwmi.c | 1224 ++ storage/class/classpnp/src/clntirp.c | 783 + storage/class/classpnp/src/create.c | 1018 ++ storage/class/classpnp/src/data.c | 222 + storage/class/classpnp/src/debug.c | 966 ++ storage/class/classpnp/src/debug.h | 132 + storage/class/classpnp/src/dictlib.c | 218 + storage/class/classpnp/src/dispatch.c | 131 + storage/class/classpnp/src/history.c | 146 + storage/class/classpnp/src/lock.c | 550 + storage/class/classpnp/src/obsolete.c | 1125 ++ storage/class/classpnp/src/power.c | 2650 +++ storage/class/classpnp/src/retry.c | 758 + storage/class/classpnp/src/srblib.c | 374 + storage/class/classpnp/src/utils.c | 8906 ++++++++++ storage/class/classpnp/src/xferpkt.c | 2047 +++ storage/filters/addfilter/ReadMe.md | 26 + storage/filters/addfilter/addfilter.sln | 28 + storage/filters/addfilter/src/addfilter.c | 1129 ++ storage/filters/addfilter/src/addfilter.rc | 21 + storage/filters/addfilter/src/addfilter.vcxproj | 192 + .../addfilter/src/addfilter.vcxproj.Filters | 27 + storage/filters/addfilter/src/addfiltr.htm | 245 + storage/iscsi/ReadMe.md | 10 + storage/iscsi/iscsi.sln | 28 + storage/iscsi/src/EmptyProject.vcxproj | 141 + storage/iscsi/src/EmptyProject.vcxproj.Filters | 17 + storage/iscsi/src/blockdir | 2 + storage/iscsi/src/client/wmisample.c | 7990 +++++++++ storage/iscsi/src/common.mof | 848 + storage/iscsi/src/config.mof | 487 + storage/iscsi/src/def.mof | 34 + storage/iscsi/src/iscsihba.mof | 13094 +++++++++++++++ storage/iscsi/src/iscsiprf.mof | Bin 0 -> 46042 bytes storage/iscsi/src/mgmt.mof | 1413 ++ storage/iscsi/src/readme.txt | 49 + storage/miniports/lsi_u3/ReadMe.md | 18 + storage/miniports/lsi_u3/lsi_u3.sln | 28 + storage/miniports/lsi_u3/src/lsi_u3.c | 8624 ++++++++++ storage/miniports/lsi_u3/src/lsi_u3.h | 160 + storage/miniports/lsi_u3/src/lsi_u3.inf | Bin 0 -> 5998 bytes storage/miniports/lsi_u3/src/lsi_u3.rc | 60 + storage/miniports/lsi_u3/src/lsi_u3.vcxproj | 184 + .../miniports/lsi_u3/src/lsi_u3.vcxproj.Filters | 31 + storage/miniports/lsi_u3/src/lsidmi.h | 80 + storage/miniports/lsi_u3/src/lsinvm.h | 429 + storage/miniports/lsi_u3/src/lsisiop.h | 415 + storage/miniports/lsi_u3/src/lsisvdt.h | 274 + storage/miniports/lsi_u3/src/lsiver.h | 55 + storage/miniports/lsi_u3/src/scr_u3m.h | 514 + storage/miniports/storahci/ReadMe.md | 8 + storage/miniports/storahci/src/ahci.h | 921 ++ storage/miniports/storahci/src/common.c | 7890 +++++++++ storage/miniports/storahci/src/common.h | 973 ++ storage/miniports/storahci/src/entrypts.c | 2531 +++ storage/miniports/storahci/src/entrypts.h | 673 + storage/miniports/storahci/src/generic.h | 171 + storage/miniports/storahci/src/hbastat.c | 2018 +++ storage/miniports/storahci/src/hbastat.h | 110 + .../miniports/storahci/src/inbox/storahci.vcxproj | 191 + .../storahci/src/inbox/storahci.vcxproj.Filters | 46 + storage/miniports/storahci/src/io.c | 1535 ++ storage/miniports/storahci/src/io.h | 125 + storage/miniports/storahci/src/pnppower.c | 2640 +++ storage/miniports/storahci/src/pnppower.h | 181 + storage/miniports/storahci/src/storahci.rc | 21 + storage/miniports/storahci/src/util.c | 1235 ++ storage/miniports/storahci/src/util.h | 1572 ++ storage/miniports/storahci/storahci.sln | 28 + storage/sfloppy/ReadMe.md | 15 + storage/sfloppy/sfloppy.sln | 28 + storage/sfloppy/src/floppy.c | 3423 ++++ storage/sfloppy/src/sfloppy.htm | 187 + storage/sfloppy/src/sfloppy.inf | 94 + storage/sfloppy/src/sfloppy.rc | 23 + storage/sfloppy/src/sfloppy.vcxproj | 181 + storage/sfloppy/src/sfloppy.vcxproj.Filters | 31 + storage/tools/spti/ReadMe.md | 12 + storage/tools/spti/spti.sln | 28 + storage/tools/spti/src/spti.c | 1204 ++ storage/tools/spti/src/spti.h | 97 + storage/tools/spti/src/spti.htm | Bin 0 -> 109034 bytes storage/tools/spti/src/spti.vcxproj | 167 + storage/tools/spti/src/spti.vcxproj.Filters | 22 + thermal/simsensor/ReadMe.md | 11 + thermal/simsensor/simsensor.c | 1549 ++ thermal/simsensor/simsensor.h | 102 + thermal/simsensor/simsensor.inf | 106 + thermal/simsensor/simsensor.rc | 11 + thermal/simsensor/simsensor.sln | 28 + thermal/simsensor/simsensor.vcxproj | 181 + thermal/simsensor/simsensor.vcxproj.Filters | 31 + thermal/simsensor/thermalsample.asl | 20 + thermal/thermalclient/ReadMe.md | 9 + thermal/thermalclient/simtc.c | 332 + thermal/thermalclient/simtc.h | 77 + thermal/thermalclient/simtc.inf | 87 + thermal/thermalclient/simtc.rc | 11 + thermal/thermalclient/simtc.vcxproj | 181 + thermal/thermalclient/simtc.vcxproj.Filters | 31 + thermal/thermalclient/thermalclient.sln | 28 + thermal/thermalclient/thermalsample.asl | 20 + tools/sdv/samples/SDV-FailDriver-KMDF/ReadMe.md | 46 + .../SDV-FailDriver-KMDF/SDV-FailDriver-KMDF.sln | 49 + .../SDV-FailDriver-KMDF/driver/fail_driver1.c | 209 + .../SDV-FailDriver-KMDF/driver/fail_driver1.h | 39 + .../driver/fail_driver1.vcxproj | 181 + .../driver/fail_driver1.vcxproj.Filters | 26 + .../SDV-FailDriver-KMDF/driver/sdv-user.sdv | 8 + .../SDV-FailDriver-KMDF/library/fail_library1.c | 98 + .../SDV-FailDriver-KMDF/library/fail_library1.h | 61 + .../library/fail_library1.vcxproj | 141 + .../library/fail_library1.vcxproj.Filters | 26 + tools/sdv/samples/SDV-FailDriver-NDIS/ReadMe.md | 39 + .../SDV-FailDriver-NDIS/SDV-FailDriver-NDIS.sln | 28 + .../sdv/samples/SDV-FailDriver-NDIS/driver/main.c | 812 + tools/sdv/samples/SDV-FailDriver-NDIS/driver/mp.h | 122 + .../SDV-FailDriver-NDIS/driver/sdv-user.sdv | 4 + .../samples/SDV-FailDriver-NDIS/driver/sdvmp.rc | 29 + .../SDV-FailDriver-NDIS/driver/sdvmp.vcxproj | 185 + .../driver/sdvmp.vcxproj.Filters | 31 + .../sdv/samples/SDV-FailDriver-STORPORT/ReadMe.md | 41 + .../SDV-FailDriver-STORPORT.sln | 28 + .../SDV-FailDriver-STORPORT/driver/MAKEFILE.ddk | 7 + .../SDV-FailDriver-STORPORT/driver/SCR_U3M.H | 514 + .../SDV-FailDriver-STORPORT/driver/lsi_u3.c | 8858 ++++++++++ .../SDV-FailDriver-STORPORT/driver/lsi_u3.h | 194 + .../SDV-FailDriver-STORPORT/driver/lsi_u3.rc | 60 + .../SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj | 181 + .../driver/lsi_u3.vcxproj.Filters | 31 + .../SDV-FailDriver-STORPORT/driver/lsi_u3_inf.txt | Bin 0 -> 5968 bytes .../SDV-FailDriver-STORPORT/driver/lsidmi.h | 80 + .../SDV-FailDriver-STORPORT/driver/lsinvm.h | 429 + .../SDV-FailDriver-STORPORT/driver/lsisiop.h | 415 + .../SDV-FailDriver-STORPORT/driver/lsisvdt.h | 274 + .../SDV-FailDriver-STORPORT/driver/lsiver.h | 55 + .../SDV-FailDriver-STORPORT/driver/sdv-user.sdv | 7 + tools/sdv/samples/SDV-FailDriver-WDM/ReadMe.md | 39 + .../SDV-FailDriver-WDM/SDV-FailDriver-WDM.sln | 28 + .../SDV-FailDriver-WDM/driver/fail_driver1.c | 285 + .../SDV-FailDriver-WDM/driver/fail_driver1.h | 75 + .../SDV-FailDriver-WDM/driver/fail_driver1.vcxproj | 156 + .../driver/fail_driver1.vcxproj.Filters | 26 + .../samples/SDV-FailDriver-WDM/driver/sdv-user.sdv | 5 + usb/kmdf_enumswitches/ReadMe.md | 47 + usb/kmdf_enumswitches/inc/prototypes.h | 14 + usb/kmdf_enumswitches/inc/public.h | 173 + usb/kmdf_enumswitches/kmdf_enumswitches.sln | 28 + usb/kmdf_enumswitches/sys/Device.c | 384 + usb/kmdf_enumswitches/sys/driver.c | 188 + usb/kmdf_enumswitches/sys/interrupt.c | 195 + usb/kmdf_enumswitches/sys/kmdf_enumswitches.inx | 112 + .../sys/kmdf_enumswitches.vcxproj | 229 + .../sys/kmdf_enumswitches.vcxproj.Filters | 43 + usb/kmdf_enumswitches/sys/osrusbfx2.h | 227 + usb/kmdf_enumswitches/sys/rawpdo.c | 330 + usb/kmdf_enumswitches/sys/rawpdo.h | 60 + usb/kmdf_enumswitches/sys/trace.h | 115 + usb/umdf2_fx2/ReadMe.md | 326 + ...5-1C56-4807-ACCD-B28C09D37FF0.devicemetadata-ms | Bin 0 -> 105893 bytes usb/umdf2_fx2/driver/Device.c | 911 + usb/umdf2_fx2/driver/bulkrwr.c | 430 + usb/umdf2_fx2/driver/driver.c | 277 + usb/umdf2_fx2/driver/interrupt.c | 184 + usb/umdf2_fx2/driver/ioctl.c | 1056 ++ usb/umdf2_fx2/driver/osrusbfx2.h | 312 + usb/umdf2_fx2/driver/osrusbfx2.man | 309 + usb/umdf2_fx2/driver/osrusbfx2.rc | 13 + usb/umdf2_fx2/driver/osrusbfx2um.inx | 103 + usb/umdf2_fx2/driver/osrusbfx2um.vcxproj | 216 + usb/umdf2_fx2/driver/osrusbfx2um.vcxproj.Filters | 51 + usb/umdf2_fx2/driver/trace.h | 115 + usb/umdf2_fx2/exe/dump.c | 444 + usb/umdf2_fx2/exe/osrusbfx2.vcxproj | 181 + usb/umdf2_fx2/exe/osrusbfx2.vcxproj.Filters | 30 + usb/umdf2_fx2/exe/test.cmd | 6 + usb/umdf2_fx2/exe/testapp.c | 1262 ++ usb/umdf2_fx2/exe/testapp.rc | 12 + usb/umdf2_fx2/inc/prototypes.h | 14 + usb/umdf2_fx2/inc/public.h | 173 + usb/umdf2_fx2/umdf2_fx2.sln | 46 + usb/umdf_filter_kmdf/Package/package.VcxProj | 91 + .../Package/package.VcxProj.Filters | 21 + usb/umdf_filter_kmdf/ReadMe.md | 38 + usb/umdf_filter_kmdf/inc/WUDFOsrUsbPublic.h | 32 + usb/umdf_filter_kmdf/inc/list.h | 77 + usb/umdf_filter_kmdf/inc/public.h | 217 + usb/umdf_filter_kmdf/inc/usb_hw.h | 233 + usb/umdf_filter_kmdf/kmdf_driver/Device.c | 1051 ++ usb/umdf_filter_kmdf/kmdf_driver/bulkrwr.c | 436 + usb/umdf_filter_kmdf/kmdf_driver/driver.c | 299 + usb/umdf_filter_kmdf/kmdf_driver/interrupt.c | 184 + usb/umdf_filter_kmdf/kmdf_driver/ioctl.c | 1057 ++ usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.h | 335 + usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.man | 309 + usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.rc | 18 + usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.vcxproj | 245 + .../kmdf_driver/osrusbfx2.vcxproj.Filters | 46 + usb/umdf_filter_kmdf/kmdf_driver/trace.h | 115 + usb/umdf_filter_kmdf/umdf_filter/OsrUsbFilter.rc | 17 + .../umdf_filter/WUDFOsrUsbFilter.vcxproj | 287 + .../umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters | 51 + .../umdf_filter/WUDFOsrUsbFilterOnKmDriver.inx | 123 + usb/umdf_filter_kmdf/umdf_filter/comsup.cpp | 351 + usb/umdf_filter_kmdf/umdf_filter/comsup.h | 215 + usb/umdf_filter_kmdf/umdf_filter/device.cpp | 243 + usb/umdf_filter_kmdf/umdf_filter/device.h | 114 + usb/umdf_filter_kmdf/umdf_filter/dllsup.cpp | 183 + usb/umdf_filter_kmdf/umdf_filter/driver.cpp | 207 + usb/umdf_filter_kmdf/umdf_filter/driver.h | 145 + usb/umdf_filter_kmdf/umdf_filter/exports.def | 4 + usb/umdf_filter_kmdf/umdf_filter/internal.h | 111 + usb/umdf_filter_kmdf/umdf_filter/queue.cpp | 538 + usb/umdf_filter_kmdf/umdf_filter/queue.h | 253 + usb/umdf_filter_kmdf/umdf_filter_kmdf.sln | 59 + usb/umdf_filter_umdf/Package/package.VcxProj | 91 + .../Package/package.VcxProj.Filters | 21 + usb/umdf_filter_umdf/ReadMe.md | 57 + usb/umdf_filter_umdf/inc/WUDFOsrUsbPublic.h | 32 + usb/umdf_filter_umdf/inc/list.h | 77 + usb/umdf_filter_umdf/inc/public.h | 217 + usb/umdf_filter_umdf/inc/usb_hw.h | 233 + usb/umdf_filter_umdf/umdf_driver/ControlQueue.cpp | 564 + usb/umdf_filter_umdf/umdf_driver/ControlQueue.h | 101 + usb/umdf_filter_umdf/umdf_driver/Device.cpp | 2087 +++ usb/umdf_filter_umdf/umdf_driver/Device.h | 604 + usb/umdf_filter_umdf/umdf_driver/Driver.cpp | 220 + usb/umdf_filter_umdf/umdf_driver/Driver.h | 149 + usb/umdf_filter_umdf/umdf_driver/OsrUsbFx2.ctl | 1 + usb/umdf_filter_umdf/umdf_driver/OsrUsbFx2.rc | 21 + .../umdf_driver/ReadWriteQueue.cpp | 425 + usb/umdf_filter_umdf/umdf_driver/ReadWriteQueue.h | 163 + .../umdf_driver/WUDFOsrUsbFx2.vcxproj | 270 + .../umdf_driver/WUDFOsrUsbFx2.vcxproj.Filters | 52 + usb/umdf_filter_umdf/umdf_driver/comsup.cpp | 344 + usb/umdf_filter_umdf/umdf_driver/comsup.h | 215 + usb/umdf_filter_umdf/umdf_driver/dllsup.cpp | 202 + usb/umdf_filter_umdf/umdf_driver/exports.def | 4 + usb/umdf_filter_umdf/umdf_driver/internal.h | 170 + usb/umdf_filter_umdf/umdf_driver/queue.cpp | 147 + usb/umdf_filter_umdf/umdf_driver/queue.h | 93 + usb/umdf_filter_umdf/umdf_filter/OsrUsbFilter.rc | 17 + .../umdf_filter/WUDFOsrUsbFilter.vcxproj | 287 + .../umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters | 51 + .../umdf_filter/WUDFOsrUsbFilterOnUmFx2Driver.inx | 114 + usb/umdf_filter_umdf/umdf_filter/comsup.cpp | 351 + usb/umdf_filter_umdf/umdf_filter/comsup.h | 215 + usb/umdf_filter_umdf/umdf_filter/device.cpp | 243 + usb/umdf_filter_umdf/umdf_filter/device.h | 114 + usb/umdf_filter_umdf/umdf_filter/dllsup.cpp | 183 + usb/umdf_filter_umdf/umdf_filter/driver.cpp | 207 + usb/umdf_filter_umdf/umdf_filter/driver.h | 145 + usb/umdf_filter_umdf/umdf_filter/exports.def | 4 + usb/umdf_filter_umdf/umdf_filter/internal.h | 111 + usb/umdf_filter_umdf/umdf_filter/queue.cpp | 538 + usb/umdf_filter_umdf/umdf_filter/queue.h | 253 + usb/umdf_filter_umdf/umdf_filter_umdf.sln | 59 + usb/usbview/ReadMe.md | 89 + usb/usbview/app.config | 12 + usb/usbview/bang.ico | Bin 0 -> 1846 bytes usb/usbview/codeanalysis.h | 133 + usb/usbview/debug.c | 209 + usb/usbview/devnode.c | 336 + usb/usbview/dispaud.c | 1164 ++ usb/usbview/display.c | 4814 ++++++ usb/usbview/dispvid.c | 5649 +++++++ usb/usbview/enum.c | 3366 ++++ usb/usbview/h264.c | 750 + usb/usbview/h264.h | 164 + usb/usbview/hub.ico | Bin 0 -> 766 bytes usb/usbview/langidlist.h | 206 + usb/usbview/monitor.ico | Bin 0 -> 10134 bytes usb/usbview/port.ico | Bin 0 -> 766 bytes usb/usbview/resource.h | 51 + usb/usbview/split.cur | Bin 0 -> 326 bytes usb/usbview/ssport.ico | Bin 0 -> 766 bytes usb/usbview/ssusb.ico | Bin 0 -> 766 bytes usb/usbview/usb.ico | Bin 0 -> 766 bytes usb/usbview/usbdesc.h | 388 + usb/usbview/usbschema.hpp | 5815 +++++++ usb/usbview/usbviddesc.h | 743 + usb/usbview/usbview.sln | 28 + usb/usbview/usbview.vcxproj | 182 + usb/usbview/usbview.vcxproj.Filters | 51 + usb/usbview/uvcdesc.h | 1106 ++ usb/usbview/uvcview.c | 2153 +++ usb/usbview/uvcview.h | 675 + usb/usbview/uvcview.rc | 152 + usb/usbview/vndrlist.h | 8930 ++++++++++ usb/usbview/xmlhelper.cpp | 3058 ++++ usb/usbview/xmlhelper.h | 41 + video/KMDOD/KMDOD.sln | 28 + video/KMDOD/ReadMe.md | 71 + video/KMDOD/Sample/SampleDisplay.vcxproj | 198 + video/KMDOD/Sample/SampleDisplay.vcxproj.Filters | 49 + video/KMDOD/Sample/sampledisplay.inf | Bin 0 -> 5754 bytes video/KMDOD/bdd.cxx | 856 + video/KMDOD/bdd.hxx | 649 + video/KMDOD/bdd_ddi.cxx | 579 + video/KMDOD/bdd_dmm.cxx | 1083 ++ video/KMDOD/bdd_errorlog.hxx | 84 + video/KMDOD/bdd_util.cxx | 115 + video/KMDOD/bltfuncs.cxx | 346 + video/KMDOD/blthw.cxx | 556 + video/KMDOD/memory.cxx | 78 + video/KMDOD/sampledisplay.rc | 30 + video/pixlib/PixLib.vcxproj | 151 + video/pixlib/PixLib.vcxproj.Filters | 22 + video/pixlib/ReadMe.md | 7 + video/pixlib/pixel.cpp | 695 + video/pixlib/pixel.hpp | 365 + video/pixlib/pixlib.cpp | 8 + video/pixlib/pixlib.sln | 28 + wmi/wmiacpi/ReadMe.md | 54 + wmi/wmiacpi/wmiacpi.sln | 28 + wmi/wmiacpi/wmiacpi/acpimof.def | 2 + wmi/wmiacpi/wmiacpi/acpimof.mof | 240 + wmi/wmiacpi/wmiacpi/acpimof.rc | 21 + wmi/wmiacpi/wmiacpi/acpimof.vcxproj | 189 + wmi/wmiacpi/wmiacpi/acpimof.vcxproj.Filters | 27 + wmi/wmiacpi/wmiacpi/device.asl | 770 + wmi/wmisamp/ReadMe.md | 42 + wmi/wmisamp/WmiHandler.c | 959 ++ wmi/wmisamp/WmiSamp.rc | 34 + wmi/wmisamp/WmiSamp.vcxproj | 152 + wmi/wmisamp/WmiSamp.vcxproj.Filters | 45 + wmi/wmisamp/wmisamp.c | 533 + wmi/wmisamp/wmisamp.h | 139 + wmi/wmisamp/wmisamp.inx | 119 + wmi/wmisamp/wmisamp.mof | 337 + wmi/wmisamp/wmisamp.sln | 28 + wpd/WpdBasicHardwareDriver/Device.cpp | 415 + wpd/WpdBasicHardwareDriver/Device.h | 89 + wpd/WpdBasicHardwareDriver/Driver.cpp | 197 + wpd/WpdBasicHardwareDriver/Driver.h | 44 + wpd/WpdBasicHardwareDriver/Queue.cpp | 340 + wpd/WpdBasicHardwareDriver/Queue.h | 93 + wpd/WpdBasicHardwareDriver/RS232Connection.cpp | 316 + wpd/WpdBasicHardwareDriver/RS232Connection.h | 153 + wpd/WpdBasicHardwareDriver/RS232Target.cpp | 433 + wpd/WpdBasicHardwareDriver/RS232Target.h | 53 + wpd/WpdBasicHardwareDriver/ReadMe.md | 43 + wpd/WpdBasicHardwareDriver/Stdafxsrc.cpp | 1 + wpd/WpdBasicHardwareDriver/WpdBaseDriver.cpp | 506 + wpd/WpdBasicHardwareDriver/WpdBaseDriver.h | 69 + .../WpdBasicHardwareDriver.cpp | 60 + .../WpdBasicHardwareDriver.def | 9 + .../WpdBasicHardwareDriver.idl | 24 + .../WpdBasicHardwareDriver.inx | 81 + .../WpdBasicHardwareDriver.rc | 15 + .../WpdBasicHardwareDriver.rgs | 26 + .../WpdBasicHardwareDriver.sln | 28 + .../WpdBasicHardwareDriver.vcxproj | 341 + .../WpdBasicHardwareDriver.vcxproj.Filters | 75 + wpd/WpdBasicHardwareDriver/WpdCapabilities.cpp | 505 + wpd/WpdBasicHardwareDriver/WpdCapabilities.h | 41 + wpd/WpdBasicHardwareDriver/WpdObjectEnum.cpp | 431 + wpd/WpdBasicHardwareDriver/WpdObjectEnum.h | 107 + wpd/WpdBasicHardwareDriver/WpdObjectProperties.cpp | 1314 ++ wpd/WpdBasicHardwareDriver/WpdObjectProperties.h | 111 + .../firmware/compass_wpd_enabled.bs2 | 94 + .../firmware/flex_force_wpd_enabled.bs2 | 60 + .../firmware/h48c_3-axis_wpd_enabled.bs2 | 175 + .../firmware/memsic2125_wpd_enabled.bs2 | 107 + .../firmware/piezo_wpd_enabled.bs2 | 55 + .../firmware/ping_wpd_enabled.bs2 | 57 + .../firmware/pir_wpd_enabled.bs2 | 50 + .../firmware/qti_wpd_enabled.bs2 | 63 + .../firmware/temp_humidity_wpd_enabled.bs2 | 225 + wpd/WpdBasicHardwareDriver/resource.h | 3 + wpd/WpdBasicHardwareDriver/stdafx.h | 320 + wpd/WpdHelloWorldDriver/Device.cpp | 443 + wpd/WpdHelloWorldDriver/Device.h | 91 + wpd/WpdHelloWorldDriver/Driver.cpp | 192 + wpd/WpdHelloWorldDriver/Driver.h | 47 + wpd/WpdHelloWorldDriver/Queue.cpp | 335 + wpd/WpdHelloWorldDriver/Queue.h | 92 + wpd/WpdHelloWorldDriver/ReadMe.md | 130 + wpd/WpdHelloWorldDriver/Stdafxsrc.cpp | 1 + wpd/WpdHelloWorldDriver/WpdBaseDriver.cpp | 249 + wpd/WpdHelloWorldDriver/WpdBaseDriver.h | 37 + wpd/WpdHelloWorldDriver/WpdCapabilities.cpp | 906 + wpd/WpdHelloWorldDriver/WpdCapabilities.h | 64 + wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.cpp | 62 + wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.def | 9 + wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.idl | 24 + wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.inx | 82 + wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.rc | 15 + wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.rgs | 26 + wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.sln | 28 + .../WpdHelloWorldDriver.vcxproj | 344 + .../WpdHelloWorldDriver.vcxproj.Filters | 77 + wpd/WpdHelloWorldDriver/WpdObjectEnum.cpp | 418 + wpd/WpdHelloWorldDriver/WpdObjectEnum.h | 103 + wpd/WpdHelloWorldDriver/WpdObjectProperties.cpp | 1421 ++ wpd/WpdHelloWorldDriver/WpdObjectProperties.h | 80 + wpd/WpdHelloWorldDriver/WpdObjectResources.cpp | 676 + wpd/WpdHelloWorldDriver/WpdObjectResources.h | 111 + wpd/WpdHelloWorldDriver/resource.h | 3 + wpd/WpdHelloWorldDriver/stdafx.h | 266 + wpd/WpdMultiTransportDriver/Device.cpp | 552 + wpd/WpdMultiTransportDriver/Device.h | 97 + wpd/WpdMultiTransportDriver/Driver.cpp | 215 + wpd/WpdMultiTransportDriver/Driver.h | 47 + wpd/WpdMultiTransportDriver/Queue.cpp | 417 + wpd/WpdMultiTransportDriver/Queue.h | 149 + wpd/WpdMultiTransportDriver/ReadMe.md | 17 + wpd/WpdMultiTransportDriver/Stdafxsrc.cpp | 1 + wpd/WpdMultiTransportDriver/WpdBaseDriver.cpp | 250 + wpd/WpdMultiTransportDriver/WpdBaseDriver.h | 39 + wpd/WpdMultiTransportDriver/WpdCapabilities.cpp | 906 + wpd/WpdMultiTransportDriver/WpdCapabilities.h | 64 + .../WpdMultiTransportDriver.cpp | 61 + .../WpdMultiTransportDriver.def | 9 + .../WpdMultiTransportDriver.idl | 24 + .../WpdMultiTransportDriver.inx | 83 + .../WpdMultiTransportDriver.rc | 15 + .../WpdMultiTransportDriver.rgs | 26 + .../WpdMultiTransportDriver.sln | 28 + .../WpdMultiTransportDriver.vcxproj | 344 + .../WpdMultiTransportDriver.vcxproj.Filters | 77 + wpd/WpdMultiTransportDriver/WpdObjectEnum.cpp | 416 + wpd/WpdMultiTransportDriver/WpdObjectEnum.h | 103 + .../WpdObjectProperties.cpp | 1421 ++ wpd/WpdMultiTransportDriver/WpdObjectProperties.h | 79 + wpd/WpdMultiTransportDriver/WpdObjectResources.cpp | 674 + wpd/WpdMultiTransportDriver/WpdObjectResources.h | 111 + wpd/WpdMultiTransportDriver/resource.h | 3 + wpd/WpdMultiTransportDriver/stdafx.h | 266 + wpd/WpdServiceSampleDriver/Device.cpp | 367 + wpd/WpdServiceSampleDriver/Device.h | 91 + wpd/WpdServiceSampleDriver/Driver.cpp | 200 + wpd/WpdServiceSampleDriver/Driver.h | 47 + wpd/WpdServiceSampleDriver/FakeContactContent.cpp | 268 + wpd/WpdServiceSampleDriver/FakeContactContent.h | 77 + wpd/WpdServiceSampleDriver/FakeContactsService.cpp | 753 + wpd/WpdServiceSampleDriver/FakeContactsService.h | 101 + .../FakeContactsServiceContent.cpp | 555 + .../FakeContactsServiceContent.h | 92 + wpd/WpdServiceSampleDriver/FakeContent.cpp | 635 + wpd/WpdServiceSampleDriver/FakeContent.h | 151 + wpd/WpdServiceSampleDriver/FakeDevice.cpp | 1122 ++ wpd/WpdServiceSampleDriver/FakeDevice.h | 182 + wpd/WpdServiceSampleDriver/FakeDeviceContent.cpp | 428 + wpd/WpdServiceSampleDriver/FakeDeviceContent.h | 95 + wpd/WpdServiceSampleDriver/FakeStorage.cpp | 217 + wpd/WpdServiceSampleDriver/FakeStorage.h | 76 + wpd/WpdServiceSampleDriver/Queue.cpp | 425 + wpd/WpdServiceSampleDriver/Queue.h | 98 + wpd/WpdServiceSampleDriver/ReadMe.md | 20 + .../SampleContactsServiceIcon.ico | Bin 0 -> 22384 bytes wpd/WpdServiceSampleDriver/SampleDeviceIcon.ico | Bin 0 -> 47076 bytes wpd/WpdServiceSampleDriver/Stdafxsrc.cpp | 1 + wpd/WpdServiceSampleDriver/WpdBaseDriver.cpp | 393 + wpd/WpdServiceSampleDriver/WpdBaseDriver.h | 45 + wpd/WpdServiceSampleDriver/WpdCapabilities.cpp | 674 + wpd/WpdServiceSampleDriver/WpdCapabilities.h | 59 + wpd/WpdServiceSampleDriver/WpdObjectEnum.cpp | 311 + wpd/WpdServiceSampleDriver/WpdObjectEnum.h | 105 + wpd/WpdServiceSampleDriver/WpdObjectManagement.cpp | 297 + wpd/WpdServiceSampleDriver/WpdObjectManagement.h | 27 + wpd/WpdServiceSampleDriver/WpdObjectProperties.cpp | 543 + wpd/WpdServiceSampleDriver/WpdObjectProperties.h | 42 + .../WpdObjectPropertiesBulk.cpp | 1068 ++ .../WpdObjectPropertiesBulk.h | 148 + wpd/WpdServiceSampleDriver/WpdObjectResources.cpp | 465 + wpd/WpdServiceSampleDriver/WpdObjectResources.h | 112 + wpd/WpdServiceSampleDriver/WpdService.cpp | 132 + wpd/WpdServiceSampleDriver/WpdService.h | 30 + .../WpdServiceCapabilities.cpp | 832 + .../WpdServiceCapabilities.h | 76 + wpd/WpdServiceSampleDriver/WpdServiceMethods.cpp | 526 + wpd/WpdServiceSampleDriver/WpdServiceMethods.h | 151 + .../WpdServiceSampleDriver.cpp | 62 + .../WpdServiceSampleDriver.def | 9 + .../WpdServiceSampleDriver.idl | 24 + .../WpdServiceSampleDriver.inx | 79 + .../WpdServiceSampleDriver.rc | 18 + .../WpdServiceSampleDriver.rgs | 26 + .../WpdServiceSampleDriver.sln | 28 + .../WpdServiceSampleDriver.vcxproj | 449 + .../WpdServiceSampleDriver.vcxproj.Filters | 116 + wpd/WpdServiceSampleDriver/helpers.cpp | 700 + wpd/WpdServiceSampleDriver/helpers.h | 459 + wpd/WpdServiceSampleDriver/resource.h | 5 + wpd/WpdServiceSampleDriver/stdafx.h | 123 + wpd/WpdWudfSampleDriver/ContextMap.h | 135 + wpd/WpdWudfSampleDriver/Device.cpp | 365 + wpd/WpdWudfSampleDriver/Device.h | 119 + wpd/WpdWudfSampleDriver/DeviceObjectFakeContent.h | 410 + wpd/WpdWudfSampleDriver/Driver.cpp | 199 + wpd/WpdWudfSampleDriver/Driver.h | 48 + wpd/WpdWudfSampleDriver/FakeContactContent.h | 442 + wpd/WpdWudfSampleDriver/FakeContent.h | 576 + wpd/WpdWudfSampleDriver/FakeDevice.h | 2415 +++ wpd/WpdWudfSampleDriver/FakeFolderContent.h | 234 + wpd/WpdWudfSampleDriver/FakeImageContent.h | 402 + wpd/WpdWudfSampleDriver/FakeMemoContent.h | 347 + wpd/WpdWudfSampleDriver/FakeMusicContent.h | 354 + wpd/WpdWudfSampleDriver/FakeVideoContent.h | 368 + wpd/WpdWudfSampleDriver/NetworkConfigFakeContent.h | 378 + wpd/WpdWudfSampleDriver/Queue.cpp | 334 + wpd/WpdWudfSampleDriver/Queue.h | 93 + wpd/WpdWudfSampleDriver/ReadMe.md | 20 + .../RenderingInformationFakeContent.h | 138 + wpd/WpdWudfSampleDriver/SampleAudioAnnotation.wav | Bin 0 -> 77240 bytes wpd/WpdWudfSampleDriver/SampleContactPhoto.png | Bin 0 -> 16494 bytes wpd/WpdWudfSampleDriver/SampleDeviceIcon.ico | Bin 0 -> 47076 bytes .../SampleExternalStorageIcon.ico | Bin 0 -> 34732 bytes wpd/WpdWudfSampleDriver/SampleImage.jpg | Bin 0 -> 182165 bytes wpd/WpdWudfSampleDriver/SampleImageThumbnail.jpg | Bin 0 -> 2539 bytes .../SampleInternalStorageIcon.ico | Bin 0 -> 34132 bytes wpd/WpdWudfSampleDriver/SampleMemo.txt | 1 + wpd/WpdWudfSampleDriver/SampleMemoFolderIcon.ico | Bin 0 -> 74683 bytes wpd/WpdWudfSampleDriver/SampleMemoIcon.ico | Bin 0 -> 38634 bytes wpd/WpdWudfSampleDriver/SampleMusic.wma | Bin 0 -> 2624518 bytes wpd/WpdWudfSampleDriver/SampleVideo.wmv | Bin 0 -> 63836 bytes wpd/WpdWudfSampleDriver/Stdafxsrc.cpp | 1 + wpd/WpdWudfSampleDriver/StorageObjectFakeContent.h | 327 + wpd/WpdWudfSampleDriver/WpdBaseDriver.cpp | 456 + wpd/WpdWudfSampleDriver/WpdBaseDriver.h | 113 + wpd/WpdWudfSampleDriver/WpdCapabilities.cpp | 899 + wpd/WpdWudfSampleDriver/WpdCapabilities.h | 60 + wpd/WpdWudfSampleDriver/WpdNetworkConfig.cpp | 122 + wpd/WpdWudfSampleDriver/WpdNetworkConfig.h | 21 + wpd/WpdWudfSampleDriver/WpdObjectEnum.cpp | 386 + wpd/WpdWudfSampleDriver/WpdObjectEnum.h | 105 + wpd/WpdWudfSampleDriver/WpdObjectManagement.cpp | 1160 ++ wpd/WpdWudfSampleDriver/WpdObjectManagement.h | 131 + wpd/WpdWudfSampleDriver/WpdObjectProperties.cpp | 445 + wpd/WpdWudfSampleDriver/WpdObjectProperties.h | 36 + .../WpdObjectPropertiesBulk.cpp | 1027 ++ wpd/WpdWudfSampleDriver/WpdObjectPropertiesBulk.h | 134 + wpd/WpdWudfSampleDriver/WpdObjectResources.cpp | 944 ++ wpd/WpdWudfSampleDriver/WpdObjectResources.h | 129 + wpd/WpdWudfSampleDriver/WpdStorage.cpp | 102 + wpd/WpdWudfSampleDriver/WpdStorage.h | 24 + wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.cpp | 68 + wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.def | 9 + wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.idl | 24 + wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.inx | 82 + wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rc | 31 + wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rgs | 26 + wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.sln | 28 + .../WpdWudfSampleDriver.vcxproj | 407 + .../WpdWudfSampleDriver.vcxproj.Filters | 92 + wpd/WpdWudfSampleDriver/helpers.cpp | 1600 ++ wpd/WpdWudfSampleDriver/helpers.h | 188 + wpd/WpdWudfSampleDriver/resource.h | 16 + wpd/WpdWudfSampleDriver/stdafx.h | 118 + 2300 files changed, 644953 insertions(+), 2 deletions(-) create mode 100644 avstream/avshws/ReadMe.md create mode 100644 avstream/avshws/avshws.h create mode 100644 avstream/avshws/avshws.htm create mode 100644 avstream/avshws/avshws.inf create mode 100644 avstream/avshws/avshws.rc create mode 100644 avstream/avshws/avshws.sln create mode 100644 avstream/avshws/avshws.vcxproj create mode 100644 avstream/avshws/avshws.vcxproj.Filters create mode 100644 avstream/avshws/capture.cpp create mode 100644 avstream/avshws/capture.h create mode 100644 avstream/avshws/device.cpp create mode 100644 avstream/avshws/device.h create mode 100644 avstream/avshws/filter.cpp create mode 100644 avstream/avshws/filter.h create mode 100644 avstream/avshws/hwsim.cpp create mode 100644 avstream/avshws/hwsim.h create mode 100644 avstream/avshws/image.cpp create mode 100644 avstream/avshws/image.h create mode 100644 avstream/avshws/purecall.c create mode 100644 avstream/samplemft0/Mft0.cpp create mode 100644 avstream/samplemft0/Mft0.h create mode 100644 avstream/samplemft0/Mft0.rgs create mode 100644 avstream/samplemft0/ReadMe.md create mode 100644 avstream/samplemft0/SampleHelpers.h create mode 100644 avstream/samplemft0/SampleMft0.cpp create mode 100644 avstream/samplemft0/SampleMft0.def create mode 100644 avstream/samplemft0/SampleMft0.idl create mode 100644 avstream/samplemft0/SampleMft0.rc create mode 100644 avstream/samplemft0/SampleMft0.rgs create mode 100644 avstream/samplemft0/SampleMft0.sln create mode 100644 avstream/samplemft0/SampleMft0.vcxproj create mode 100644 avstream/samplemft0/SampleMft0.vcxproj.Filters create mode 100644 avstream/samplemft0/dllmain.cpp create mode 100644 avstream/samplemft0/dllmain.h create mode 100644 avstream/samplemft0/resource.h create mode 100644 avstream/samplemft0/stdafx.cpp create mode 100644 avstream/samplemft0/stdafx.h create mode 100644 avstream/samplemft0/stdafxsrc.cpp create mode 100644 avstream/samplemft0/targetver.h create mode 100644 bluetooth/serialhcibus/Fdo.c create mode 100644 bluetooth/serialhcibus/Io.h create mode 100644 bluetooth/serialhcibus/ReadMe.md create mode 100644 bluetooth/serialhcibus/WDK/Debugdef.h create mode 100644 bluetooth/serialhcibus/WDK/SerialBusWdk.inx create mode 100644 bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj create mode 100644 bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters create mode 100644 bluetooth/serialhcibus/WDK/device.c create mode 100644 bluetooth/serialhcibus/WDK/device.h create mode 100644 bluetooth/serialhcibus/WDK/driver.rc create mode 100644 bluetooth/serialhcibus/driver.c create mode 100644 bluetooth/serialhcibus/driver.h create mode 100644 bluetooth/serialhcibus/io.c create mode 100644 bluetooth/serialhcibus/pdo.c create mode 100644 bluetooth/serialhcibus/public.h create mode 100644 bluetooth/serialhcibus/serialhcibus.sln create mode 100644 filesys/cdfs/ReadMe.md create mode 100644 filesys/cdfs/allocsup.c create mode 100644 filesys/cdfs/cachesup.c create mode 100644 filesys/cdfs/cd.h create mode 100644 filesys/cdfs/cddata.c create mode 100644 filesys/cdfs/cddata.h create mode 100644 filesys/cdfs/cdfs.rc create mode 100644 filesys/cdfs/cdfs.sln create mode 100644 filesys/cdfs/cdfs.vcxproj create mode 100644 filesys/cdfs/cdfs.vcxproj.Filters create mode 100644 filesys/cdfs/cdinit.c create mode 100644 filesys/cdfs/cdprocs.h create mode 100644 filesys/cdfs/cdprocssrc.c create mode 100644 filesys/cdfs/cdstruc.h create mode 100644 filesys/cdfs/cleanup.c create mode 100644 filesys/cdfs/close.c create mode 100644 filesys/cdfs/create.c create mode 100644 filesys/cdfs/devctrl.c create mode 100644 filesys/cdfs/deviosup.c create mode 100644 filesys/cdfs/dirctrl.c create mode 100644 filesys/cdfs/dirsup.c create mode 100644 filesys/cdfs/fieldoff.c create mode 100644 filesys/cdfs/fileinfo.c create mode 100644 filesys/cdfs/filobsup.c create mode 100644 filesys/cdfs/fsctrl.c create mode 100644 filesys/cdfs/fspdisp.c create mode 100644 filesys/cdfs/lockctrl.c create mode 100644 filesys/cdfs/namesup.c create mode 100644 filesys/cdfs/nodetype.h create mode 100644 filesys/cdfs/pathsup.c create mode 100644 filesys/cdfs/pnp.c create mode 100644 filesys/cdfs/prefxsup.c create mode 100644 filesys/cdfs/read.c create mode 100644 filesys/cdfs/resrcsup.c create mode 100644 filesys/cdfs/shutdown.c create mode 100644 filesys/cdfs/strucsup.c create mode 100644 filesys/cdfs/verfysup.c create mode 100644 filesys/cdfs/volinfo.c create mode 100644 filesys/cdfs/workque.c create mode 100644 filesys/cdfs/write.c create mode 100644 filesys/miniFilter/MetadataManager/DataStore.c create mode 100644 filesys/miniFilter/MetadataManager/MetadataManager.rc create mode 100644 filesys/miniFilter/MetadataManager/MetadataManager.sln create mode 100644 filesys/miniFilter/MetadataManager/MetadataManagerInit.c create mode 100644 filesys/miniFilter/MetadataManager/MetadataManagerProc.h create mode 100644 filesys/miniFilter/MetadataManager/MetadataManagerStruc.h create mode 100644 filesys/miniFilter/MetadataManager/ReadMe.md create mode 100644 filesys/miniFilter/MetadataManager/fmm.inf create mode 100644 filesys/miniFilter/MetadataManager/fmm.vcxproj create mode 100644 filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters create mode 100644 filesys/miniFilter/MetadataManager/operations.c create mode 100644 filesys/miniFilter/MetadataManager/pch.h create mode 100644 filesys/miniFilter/MetadataManager/support.c create mode 100644 filesys/miniFilter/avscan/ReadMe.md create mode 100644 filesys/miniFilter/avscan/avscan.inf create mode 100644 filesys/miniFilter/avscan/avscan.sln create mode 100644 filesys/miniFilter/avscan/filter/avscan.c create mode 100644 filesys/miniFilter/avscan/filter/avscan.h create mode 100644 filesys/miniFilter/avscan/filter/avscan.rc create mode 100644 filesys/miniFilter/avscan/filter/avscan.vcxproj create mode 100644 filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters create mode 100644 filesys/miniFilter/avscan/filter/communication.c create mode 100644 filesys/miniFilter/avscan/filter/context.c create mode 100644 filesys/miniFilter/avscan/filter/context.h create mode 100644 filesys/miniFilter/avscan/filter/csvfs.c create mode 100644 filesys/miniFilter/avscan/filter/csvfs.h create mode 100644 filesys/miniFilter/avscan/filter/scan.c create mode 100644 filesys/miniFilter/avscan/filter/scan.h create mode 100644 filesys/miniFilter/avscan/filter/utility.c create mode 100644 filesys/miniFilter/avscan/filter/utility.h create mode 100644 filesys/miniFilter/avscan/inc/avlib.h create mode 100644 filesys/miniFilter/avscan/user/avscan.c create mode 100644 filesys/miniFilter/avscan/user/avscan.rc create mode 100644 filesys/miniFilter/avscan/user/avscan.vcxproj create mode 100644 filesys/miniFilter/avscan/user/avscan.vcxproj.Filters create mode 100644 filesys/miniFilter/avscan/user/userscan.c create mode 100644 filesys/miniFilter/avscan/user/userscan.h create mode 100644 filesys/miniFilter/avscan/user/utility.c create mode 100644 filesys/miniFilter/avscan/user/utility.h create mode 100644 filesys/miniFilter/cancelSafe/ReadMe.md create mode 100644 filesys/miniFilter/cancelSafe/cancelSafe.c create mode 100644 filesys/miniFilter/cancelSafe/cancelSafe.inf create mode 100644 filesys/miniFilter/cancelSafe/cancelSafe.rc create mode 100644 filesys/miniFilter/cancelSafe/cancelSafe.sln create mode 100644 filesys/miniFilter/cancelSafe/cancelSafe.vcxproj create mode 100644 filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters create mode 100644 filesys/miniFilter/cdo/Cdo.rc create mode 100644 filesys/miniFilter/cdo/CdoInit.c create mode 100644 filesys/miniFilter/cdo/CdoOperations.c create mode 100644 filesys/miniFilter/cdo/CdoProc.h create mode 100644 filesys/miniFilter/cdo/CdoStruct.h create mode 100644 filesys/miniFilter/cdo/ReadMe.md create mode 100644 filesys/miniFilter/cdo/cdo.inf create mode 100644 filesys/miniFilter/cdo/cdo.sln create mode 100644 filesys/miniFilter/cdo/cdo.vcxproj create mode 100644 filesys/miniFilter/cdo/cdo.vcxproj.Filters create mode 100644 filesys/miniFilter/cdo/pch.h create mode 100644 filesys/miniFilter/change/ReadMe.md create mode 100644 filesys/miniFilter/change/change.c create mode 100644 filesys/miniFilter/change/change.h create mode 100644 filesys/miniFilter/change/change.inf create mode 100644 filesys/miniFilter/change/change.rc create mode 100644 filesys/miniFilter/change/change.sln create mode 100644 filesys/miniFilter/change/change.vcxproj create mode 100644 filesys/miniFilter/change/change.vcxproj.Filters create mode 100644 filesys/miniFilter/change/context.c create mode 100644 filesys/miniFilter/change/context.h create mode 100644 filesys/miniFilter/change/utility.h create mode 100644 filesys/miniFilter/ctx/CtxInit.c create mode 100644 filesys/miniFilter/ctx/CtxProc.h create mode 100644 filesys/miniFilter/ctx/CtxStruc.h create mode 100644 filesys/miniFilter/ctx/ReadMe.md create mode 100644 filesys/miniFilter/ctx/context.c create mode 100644 filesys/miniFilter/ctx/ctx.inf create mode 100644 filesys/miniFilter/ctx/ctx.rc create mode 100644 filesys/miniFilter/ctx/ctx.sln create mode 100644 filesys/miniFilter/ctx/ctx.vcxproj create mode 100644 filesys/miniFilter/ctx/ctx.vcxproj.Filters create mode 100644 filesys/miniFilter/ctx/operations.c create mode 100644 filesys/miniFilter/ctx/pch.h create mode 100644 filesys/miniFilter/ctx/support.c create mode 100644 filesys/miniFilter/delete/ReadMe.md create mode 100644 filesys/miniFilter/delete/delete.c create mode 100644 filesys/miniFilter/delete/delete.inf create mode 100644 filesys/miniFilter/delete/delete.rc create mode 100644 filesys/miniFilter/delete/delete.sln create mode 100644 filesys/miniFilter/delete/delete.vcxproj create mode 100644 filesys/miniFilter/delete/delete.vcxproj.Filters create mode 100644 filesys/miniFilter/minispy/ReadMe.md create mode 100644 filesys/miniFilter/minispy/filter/RegistrationData.c create mode 100644 filesys/miniFilter/minispy/filter/minispy.c create mode 100644 filesys/miniFilter/minispy/filter/minispy.rc create mode 100644 filesys/miniFilter/minispy/filter/minispy.vcxproj create mode 100644 filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters create mode 100644 filesys/miniFilter/minispy/filter/mspyKern.h create mode 100644 filesys/miniFilter/minispy/filter/mspyLib.c create mode 100644 filesys/miniFilter/minispy/inc/minispy.h create mode 100644 filesys/miniFilter/minispy/minispy.inf create mode 100644 filesys/miniFilter/minispy/minispy.sln create mode 100644 filesys/miniFilter/minispy/user/minispy.vcxproj create mode 100644 filesys/miniFilter/minispy/user/minispy.vcxproj.Filters create mode 100644 filesys/miniFilter/minispy/user/mspyLog.c create mode 100644 filesys/miniFilter/minispy/user/mspyLog.h create mode 100644 filesys/miniFilter/minispy/user/mspyUser.c create mode 100644 filesys/miniFilter/minispy/user/mspyUser.rc create mode 100644 filesys/miniFilter/nullFilter/ReadMe.md create mode 100644 filesys/miniFilter/nullFilter/nullFilter.c create mode 100644 filesys/miniFilter/nullFilter/nullFilter.inf create mode 100644 filesys/miniFilter/nullFilter/nullFilter.rc create mode 100644 filesys/miniFilter/nullFilter/nullFilter.sln create mode 100644 filesys/miniFilter/nullFilter/nullFilter.vcxproj create mode 100644 filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters create mode 100644 filesys/miniFilter/passThrough/ReadMe.md create mode 100644 filesys/miniFilter/passThrough/passThrough.c create mode 100644 filesys/miniFilter/passThrough/passThrough.inf create mode 100644 filesys/miniFilter/passThrough/passThrough.rc create mode 100644 filesys/miniFilter/passThrough/passThrough.sln create mode 100644 filesys/miniFilter/passThrough/passThrough.vcxproj create mode 100644 filesys/miniFilter/passThrough/passThrough.vcxproj.Filters create mode 100644 filesys/miniFilter/scanner/ReadMe.md create mode 100644 filesys/miniFilter/scanner/filter/scanner.c create mode 100644 filesys/miniFilter/scanner/filter/scanner.h create mode 100644 filesys/miniFilter/scanner/filter/scanner.rc create mode 100644 filesys/miniFilter/scanner/filter/scanner.vcxproj create mode 100644 filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters create mode 100644 filesys/miniFilter/scanner/inc/scanuk.h create mode 100644 filesys/miniFilter/scanner/scanner.inf create mode 100644 filesys/miniFilter/scanner/scanner.sln create mode 100644 filesys/miniFilter/scanner/user/scanUser.c create mode 100644 filesys/miniFilter/scanner/user/scanUser.rc create mode 100644 filesys/miniFilter/scanner/user/scanuser.h create mode 100644 filesys/miniFilter/scanner/user/scanuser.vcxproj create mode 100644 filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters create mode 100644 filesys/miniFilter/simrep/ReadMe.md create mode 100644 filesys/miniFilter/simrep/simrep.c create mode 100644 filesys/miniFilter/simrep/simrep.inf create mode 100644 filesys/miniFilter/simrep/simrep.rc create mode 100644 filesys/miniFilter/simrep/simrep.sln create mode 100644 filesys/miniFilter/simrep/simrep.vcxproj create mode 100644 filesys/miniFilter/simrep/simrep.vcxproj.Filters create mode 100644 filesys/miniFilter/swapBuffers/ReadMe.md create mode 100644 filesys/miniFilter/swapBuffers/swapBuffers.c create mode 100644 filesys/miniFilter/swapBuffers/swapBuffers.inf create mode 100644 filesys/miniFilter/swapBuffers/swapBuffers.rc create mode 100644 filesys/miniFilter/swapBuffers/swapBuffers.sln create mode 100644 filesys/miniFilter/swapBuffers/swapBuffers.vcxproj create mode 100644 filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters create mode 100644 general/PLX9x5x/PLX9x5x.sln create mode 100644 general/PLX9x5x/ReadMe.md create mode 100644 general/PLX9x5x/sys/Init.c create mode 100644 general/PLX9x5x/sys/IsrDpc.c create mode 100644 general/PLX9x5x/sys/Pci9656.c create mode 100644 general/PLX9x5x/sys/Pci9656.h create mode 100644 general/PLX9x5x/sys/Pci9656.rc create mode 100644 general/PLX9x5x/sys/Pci9x5x.vcxproj create mode 100644 general/PLX9x5x/sys/Pci9x5x.vcxproj.Filters create mode 100644 general/PLX9x5x/sys/Precomp.h create mode 100644 general/PLX9x5x/sys/Private.h create mode 100644 general/PLX9x5x/sys/Public.h create mode 100644 general/PLX9x5x/sys/Read.c create mode 100644 general/PLX9x5x/sys/Reg9656.h create mode 100644 general/PLX9x5x/sys/Write.c create mode 100644 general/PLX9x5x/sys/pci9x5x.inx create mode 100644 general/PLX9x5x/sys/precompsrc.c create mode 100644 general/PLX9x5x/sys/trace.h create mode 100644 general/PLX9x5x/test/plx.cpp create mode 100644 general/PLX9x5x/test/plx.hpp create mode 100644 general/PLX9x5x/test/plx.vcxproj create mode 100644 general/PLX9x5x/test/plx.vcxproj.Filters create mode 100644 general/PLX9x5x/test/test.cmd create mode 100644 general/SystemDma/wdm/ReadMe.md create mode 100644 general/SystemDma/wdm/SystemDma.sln create mode 100644 general/SystemDma/wdm/exe/SystemDmaApp.vcxproj create mode 100644 general/SystemDma/wdm/exe/SystemDmaApp.vcxproj.Filters create mode 100644 general/SystemDma/wdm/exe/install.c create mode 100644 general/SystemDma/wdm/exe/testapp.c create mode 100644 general/SystemDma/wdm/sys/SDma.vcxproj create mode 100644 general/SystemDma/wdm/sys/SDma.vcxproj.Filters create mode 100644 general/SystemDma/wdm/sys/sdma.c create mode 100644 general/SystemDma/wdm/sys/sdma.h create mode 100644 general/SystemDma/wdm/sys/sdma.rc create mode 100644 general/cancel/ReadMe.md create mode 100644 general/cancel/cancel.sln create mode 100644 general/cancel/exe/canclapp.vcxproj create mode 100644 general/cancel/exe/canclapp.vcxproj.Filters create mode 100644 general/cancel/exe/install.c create mode 100644 general/cancel/exe/testapp.c create mode 100644 general/cancel/exe/testapp.h create mode 100644 general/cancel/startio/cancel.c create mode 100644 general/cancel/startio/cancel.h create mode 100644 general/cancel/startio/cancel.rc create mode 100644 general/cancel/startio/cancel.vcxproj create mode 100644 general/cancel/startio/cancel.vcxproj.Filters create mode 100644 general/cancel/sys/cancel.c create mode 100644 general/cancel/sys/cancel.h create mode 100644 general/cancel/sys/cancel.rc create mode 100644 general/cancel/sys/cancel.vcxproj create mode 100644 general/cancel/sys/cancel.vcxproj.Filters create mode 100644 general/echo/kmdf/ReadMe.md create mode 100644 general/echo/kmdf/driver/AutoSync/device.c create mode 100644 general/echo/kmdf/driver/AutoSync/device.h create mode 100644 general/echo/kmdf/driver/AutoSync/driver.c create mode 100644 general/echo/kmdf/driver/AutoSync/driver.h create mode 100644 general/echo/kmdf/driver/AutoSync/echo.inx create mode 100644 general/echo/kmdf/driver/AutoSync/echo.vcxproj create mode 100644 general/echo/kmdf/driver/AutoSync/echo.vcxproj.Filters create mode 100644 general/echo/kmdf/driver/AutoSync/queue.c create mode 100644 general/echo/kmdf/driver/AutoSync/queue.h create mode 100644 general/echo/kmdf/driver/DriverSync/device.c create mode 100644 general/echo/kmdf/driver/DriverSync/device.h create mode 100644 general/echo/kmdf/driver/DriverSync/driver.c create mode 100644 general/echo/kmdf/driver/DriverSync/driver.h create mode 100644 general/echo/kmdf/driver/DriverSync/echo_2.inx create mode 100644 general/echo/kmdf/driver/DriverSync/echo_2.vcxproj create mode 100644 general/echo/kmdf/driver/DriverSync/echo_2.vcxproj.Filters create mode 100644 general/echo/kmdf/driver/DriverSync/queue.c create mode 100644 general/echo/kmdf/driver/DriverSync/queue.h create mode 100644 general/echo/kmdf/exe/echoapp.cpp create mode 100644 general/echo/kmdf/exe/echoapp.vcxproj create mode 100644 general/echo/kmdf/exe/echoapp.vcxproj.Filters create mode 100644 general/echo/kmdf/exe/public.h create mode 100644 general/echo/kmdf/kmdfecho.sln create mode 100644 general/echo/umdf/Comsup.cpp create mode 100644 general/echo/umdf/Comsup.h create mode 100644 general/echo/umdf/Device.cpp create mode 100644 general/echo/umdf/Device.h create mode 100644 general/echo/umdf/Driver.cpp create mode 100644 general/echo/umdf/Driver.h create mode 100644 general/echo/umdf/Echo.rc create mode 100644 general/echo/umdf/Queue.cpp create mode 100644 general/echo/umdf/Queue.h create mode 100644 general/echo/umdf/ReadMe.md create mode 100644 general/echo/umdf/WUDFEchoDriver.inx create mode 100644 general/echo/umdf/WUDFEchoDriver.vcxproj create mode 100644 general/echo/umdf/WUDFEchoDriver.vcxproj.Filters create mode 100644 general/echo/umdf/dllsup.cpp create mode 100644 general/echo/umdf/echo.sln create mode 100644 general/echo/umdf/echodriver.ctl create mode 100644 general/echo/umdf/exports.def create mode 100644 general/echo/umdf/internal.h create mode 100644 general/echo/umdf2/ReadMe.md create mode 100644 general/echo/umdf2/driver/AutoSync/device.c create mode 100644 general/echo/umdf2/driver/AutoSync/device.h create mode 100644 general/echo/umdf2/driver/AutoSync/driver.c create mode 100644 general/echo/umdf2/driver/AutoSync/driver.h create mode 100644 general/echo/umdf2/driver/AutoSync/echo.vcxproj create mode 100644 general/echo/umdf2/driver/AutoSync/echo.vcxproj.Filters create mode 100644 general/echo/umdf2/driver/AutoSync/echoum.inx create mode 100644 general/echo/umdf2/driver/AutoSync/queue.c create mode 100644 general/echo/umdf2/driver/AutoSync/queue.h create mode 100644 general/echo/umdf2/exe/echoapp.cpp create mode 100644 general/echo/umdf2/exe/echoapp.vcxproj create mode 100644 general/echo/umdf2/exe/echoapp.vcxproj.Filters create mode 100644 general/echo/umdf2/exe/public.h create mode 100644 general/echo/umdf2/umdf2echo.sln create mode 100644 general/echo/umdfSocketEcho/Driver/Connection.cpp create mode 100644 general/echo/umdfSocketEcho/Driver/FileContext.h create mode 100644 general/echo/umdfSocketEcho/Driver/Queue.cpp create mode 100644 general/echo/umdfSocketEcho/Driver/Queue.h create mode 100644 general/echo/umdfSocketEcho/Driver/SocketEcho.inx create mode 100644 general/echo/umdfSocketEcho/Driver/SocketEcho.rc create mode 100644 general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj create mode 100644 general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj.Filters create mode 100644 general/echo/umdfSocketEcho/Driver/connection.h create mode 100644 general/echo/umdfSocketEcho/Driver/device.cpp create mode 100644 general/echo/umdfSocketEcho/Driver/device.h create mode 100644 general/echo/umdfSocketEcho/Driver/devicecontext.h create mode 100644 general/echo/umdfSocketEcho/Driver/dllsup.cpp create mode 100644 general/echo/umdfSocketEcho/Driver/driver.cpp create mode 100644 general/echo/umdfSocketEcho/Driver/driver.h create mode 100644 general/echo/umdfSocketEcho/Driver/exports.def create mode 100644 general/echo/umdfSocketEcho/Driver/internal.h create mode 100644 general/echo/umdfSocketEcho/Exe/internal.h create mode 100644 general/echo/umdfSocketEcho/Exe/socketechoserver.cpp create mode 100644 general/echo/umdfSocketEcho/Exe/socketechoserver.h create mode 100644 general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj create mode 100644 general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj.Filters create mode 100644 general/echo/umdfSocketEcho/ReadMe.md create mode 100644 general/echo/umdfSocketEcho/umdfsocketecho.sln create mode 100644 general/event/ReadMe.md create mode 100644 general/event/eventsample.sln create mode 100644 general/event/exe/event.vcxproj create mode 100644 general/event/exe/event.vcxproj.Filters create mode 100644 general/event/exe/eventtest.c create mode 100644 general/event/exe/install.c create mode 100644 general/event/wdm/event.c create mode 100644 general/event/wdm/event.h create mode 100644 general/event/wdm/event.rc create mode 100644 general/event/wdm/event.vcxproj create mode 100644 general/event/wdm/event.vcxproj.Filters create mode 100644 general/event/wdm/public.h create mode 100644 general/filehistory/ReadMe.md create mode 100644 general/filehistory/exe/fhsetup.cpp create mode 100644 general/filehistory/exe/fhsetup.h create mode 100644 general/filehistory/exe/fhsetup.vcxproj create mode 100644 general/filehistory/exe/fhsetup.vcxproj.Filters create mode 100644 general/filehistory/filehistory.sln create mode 100644 general/installwdf/Install.cpp create mode 100644 general/installwdf/InstallWdf.vcxproj create mode 100644 general/installwdf/InstallWdf.vcxproj.Filters create mode 100644 general/installwdf/ReadMe.md create mode 100644 general/installwdf/installwdf.sln create mode 100644 general/ioctl/wdm/ReadMe.md create mode 100644 general/ioctl/wdm/exe/install.c create mode 100644 general/ioctl/wdm/exe/ioctlapp.vcxproj create mode 100644 general/ioctl/wdm/exe/ioctlapp.vcxproj.Filters create mode 100644 general/ioctl/wdm/exe/testapp.c create mode 100644 general/ioctl/wdm/ioctl.sln create mode 100644 general/ioctl/wdm/sys/sioctl.c create mode 100644 general/ioctl/wdm/sys/sioctl.h create mode 100644 general/ioctl/wdm/sys/sioctl.rc create mode 100644 general/ioctl/wdm/sys/sioctl.vcxproj create mode 100644 general/ioctl/wdm/sys/sioctl.vcxproj.Filters create mode 100644 general/obcallback/ReadMe.md create mode 100644 general/obcallback/control/ObCallbackTestCtrl.vcxproj create mode 100644 general/obcallback/control/ObCallbackTestCtrl.vcxproj.Filters create mode 100644 general/obcallback/control/common.h create mode 100644 general/obcallback/control/main.cpp create mode 100644 general/obcallback/control/pch.h create mode 100644 general/obcallback/control/pchsrc.cpp create mode 100644 general/obcallback/control/readme.txt create mode 100644 general/obcallback/control/utils.cpp create mode 100644 general/obcallback/driver/ObCallbackTest.vcxproj create mode 100644 general/obcallback/driver/ObCallbackTest.vcxproj.Filters create mode 100644 general/obcallback/driver/callback.c create mode 100644 general/obcallback/driver/pch.h create mode 100644 general/obcallback/driver/pchsrc.c create mode 100644 general/obcallback/driver/shared.h create mode 100644 general/obcallback/driver/tdriver.c create mode 100644 general/obcallback/driver/tdriver.h create mode 100644 general/obcallback/driver/util.c create mode 100644 general/obcallback/obcallback.sln create mode 100644 general/pcidrv/ReadMe.md create mode 100644 general/pcidrv/kmdf/HW/PCIDRV.vcxproj create mode 100644 general/pcidrv/kmdf/HW/PCIDRV.vcxproj.Filters create mode 100644 general/pcidrv/kmdf/HW/e100_557.h create mode 100644 general/pcidrv/kmdf/HW/e100_equ.h create mode 100644 general/pcidrv/kmdf/HW/eeprom.c create mode 100644 general/pcidrv/kmdf/HW/isrdpc.c create mode 100644 general/pcidrv/kmdf/HW/localwpp.ini create mode 100644 general/pcidrv/kmdf/HW/macros.h create mode 100644 general/pcidrv/kmdf/HW/nic_def.h create mode 100644 general/pcidrv/kmdf/HW/nic_init.c create mode 100644 general/pcidrv/kmdf/HW/nic_pm.c create mode 100644 general/pcidrv/kmdf/HW/nic_recv.c create mode 100644 general/pcidrv/kmdf/HW/nic_req.c create mode 100644 general/pcidrv/kmdf/HW/nic_send.c create mode 100644 general/pcidrv/kmdf/HW/nuiouser.h create mode 100644 general/pcidrv/kmdf/HW/physet.c create mode 100644 general/pcidrv/kmdf/HW/precomp.h create mode 100644 general/pcidrv/kmdf/HW/precompsrc.c create mode 100644 general/pcidrv/kmdf/HW/routines.c create mode 100644 general/pcidrv/kmdf/PCIDRV.C create mode 100644 general/pcidrv/kmdf/PCIDRV.H create mode 100644 general/pcidrv/kmdf/PCIDRV.RC create mode 100644 general/pcidrv/kmdf/PCIDRV.mof create mode 100644 general/pcidrv/kmdf/genpci.inx create mode 100644 general/pcidrv/kmdf/public.h create mode 100644 general/pcidrv/kmdf/trace.h create mode 100644 general/pcidrv/kmdf/wmi.c create mode 100644 general/pcidrv/pcidrv.sln create mode 100644 general/pcidrv/test/myping.c create mode 100644 general/pcidrv/test/myping.vcxproj create mode 100644 general/pcidrv/test/myping.vcxproj.Filters create mode 100644 general/pcidrv/test/resource.h create mode 100644 general/pcidrv/test/testapp.c create mode 100644 general/pcidrv/test/testapp.h create mode 100644 general/pcidrv/test/testapp.rc create mode 100644 general/perfcounters/kcs/ReadMe.md create mode 100644 general/perfcounters/kcs/kcs.c create mode 100644 general/perfcounters/kcs/kcs.h create mode 100644 general/perfcounters/kcs/kcs.man create mode 100644 general/perfcounters/kcs/kcs.rc create mode 100644 general/perfcounters/kcs/kcs.sln create mode 100644 general/perfcounters/kcs/kcs.vcxproj create mode 100644 general/perfcounters/kcs/kcs.vcxproj.Filters create mode 100644 general/registry/regfltr/ReadMe.md create mode 100644 general/registry/regfltr/exe/capture.c create mode 100644 general/registry/regfltr/exe/common.h create mode 100644 general/registry/regfltr/exe/post.c create mode 100644 general/registry/regfltr/exe/pre.c create mode 100644 general/registry/regfltr/exe/regctrl.c create mode 100644 general/registry/regfltr/exe/regctrl.h create mode 100644 general/registry/regfltr/exe/regctrl.rc create mode 100644 general/registry/regfltr/exe/regctrl.vcxproj create mode 100644 general/registry/regfltr/exe/regctrl.vcxproj.Filters create mode 100644 general/registry/regfltr/exe/util.c create mode 100644 general/registry/regfltr/regfltr.sln create mode 100644 general/registry/regfltr/sys/capture.c create mode 100644 general/registry/regfltr/sys/context.c create mode 100644 general/registry/regfltr/sys/driver.c create mode 100644 general/registry/regfltr/sys/multialt.c create mode 100644 general/registry/regfltr/sys/post.c create mode 100644 general/registry/regfltr/sys/pre.c create mode 100644 general/registry/regfltr/sys/regfltr.c create mode 100644 general/registry/regfltr/sys/regfltr.h create mode 100644 general/registry/regfltr/sys/regfltr.rc create mode 100644 general/registry/regfltr/sys/regfltr.vcxproj create mode 100644 general/registry/regfltr/sys/regfltr.vcxproj.Filters create mode 100644 general/registry/regfltr/sys/txr.c create mode 100644 general/registry/regfltr/sys/txrutil.c create mode 100644 general/registry/regfltr/sys/util.c create mode 100644 general/registry/regfltr/sys/version.c create mode 100644 general/toaster/toastpkg/ReadMe.md create mode 100644 general/toaster/toastpkg/inf/autorun.inf create mode 100644 general/toaster/toastpkg/inf/toastpkg.inf create mode 100644 general/toaster/toastpkg/toastapp/precomp.h create mode 100644 general/toaster/toastpkg/toastapp/precompsrc.c create mode 100644 general/toaster/toastpkg/toastapp/rc_ids.h create mode 100644 general/toaster/toastpkg/toastapp/toastapp.c create mode 100644 general/toaster/toastpkg/toastapp/toastapp.dlg create mode 100644 general/toaster/toastpkg/toastapp/toastapp.h create mode 100644 general/toaster/toastpkg/toastapp/toastapp.ico create mode 100644 general/toaster/toastpkg/toastapp/toastapp.rc create mode 100644 general/toaster/toastpkg/toastapp/toastapp.vcxproj create mode 100644 general/toaster/toastpkg/toastapp/toastapp.vcxproj.Filters create mode 100644 general/toaster/toastpkg/toastcd/ToastApp/setup.ini create mode 100644 general/toaster/toastpkg/toastcd/ToastApp/toastapp.msi create mode 100644 general/toaster/toastpkg/toastcd/amd64/toaster.sys create mode 100644 general/toaster/toastpkg/toastcd/autorun.inf create mode 100644 general/toaster/toastpkg/toastcd/i386/toaster.sys create mode 100644 general/toaster/toastpkg/toastcd/toastpkg.inf create mode 100644 general/toaster/toastpkg/toastcd/toastpkg.tag create mode 100644 general/toaster/toastpkg/toastcd/tostx86.cat create mode 100644 general/toaster/toastpkg/toastcd/tstamd64.cat create mode 100644 general/toaster/toastpkg/toastco/precomp.h create mode 100644 general/toaster/toastpkg/toastco/precompsrc.c create mode 100644 general/toaster/toastpkg/toastco/toastco.c create mode 100644 general/toaster/toastpkg/toastco/toastco.h create mode 100644 general/toaster/toastpkg/toastco/toastco.rc create mode 100644 general/toaster/toastpkg/toastco/tostrco2.def create mode 100644 general/toaster/toastpkg/toastco/tostrco2.vcxproj create mode 100644 general/toaster/toastpkg/toastco/tostrco2.vcxproj.Filters create mode 100644 general/toaster/toastpkg/toastpkg.sln create mode 100644 general/toaster/toastpkg/toastva/header.bmp create mode 100644 general/toaster/toastpkg/toastva/precomp.h create mode 100644 general/toaster/toastpkg/toastva/precompsrc.c create mode 100644 general/toaster/toastpkg/toastva/resource.h create mode 100644 general/toaster/toastpkg/toastva/search.avi create mode 100644 general/toaster/toastpkg/toastva/toastapp.ico create mode 100644 general/toaster/toastpkg/toastva/toastva.c create mode 100644 general/toaster/toastpkg/toastva/toastva.h create mode 100644 general/toaster/toastpkg/toastva/toastva.rc create mode 100644 general/toaster/toastpkg/toastva/toastva.vcxproj create mode 100644 general/toaster/toastpkg/toastva/toastva.vcxproj.Filters create mode 100644 general/toaster/toastpkg/toastva/util.c create mode 100644 general/toaster/toastpkg/toastva/watermrk.bmp create mode 100644 general/toaster/toastpkg/toastva/wizard.c create mode 100644 general/toaster/umdf2/Package/package.VcxProj create mode 100644 general/toaster/umdf2/Package/package.VcxProj.Filters create mode 100644 general/toaster/umdf2/ReadMe.md create mode 100644 general/toaster/umdf2/exe/enum/Enum.vcxproj create mode 100644 general/toaster/umdf2/exe/enum/Enum.vcxproj.Filters create mode 100644 general/toaster/umdf2/exe/enum/enum.c create mode 100644 general/toaster/umdf2/exe/notify/notify.c create mode 100644 general/toaster/umdf2/exe/notify/notify.h create mode 100644 general/toaster/umdf2/exe/notify/notify.rc create mode 100644 general/toaster/umdf2/exe/notify/notify.vcxproj create mode 100644 general/toaster/umdf2/exe/notify/notify.vcxproj.Filters create mode 100644 general/toaster/umdf2/exe/notify/toaster.ico create mode 100644 general/toaster/umdf2/exe/toast/toast.c create mode 100644 general/toaster/umdf2/exe/toast/toast.vcxproj create mode 100644 general/toaster/umdf2/exe/toast/toast.vcxproj.Filters create mode 100644 general/toaster/umdf2/filter/generic/filter.c create mode 100644 general/toaster/umdf2/filter/generic/filter.h create mode 100644 general/toaster/umdf2/filter/generic/filter.rc create mode 100644 general/toaster/umdf2/filter/generic/filterum.inx create mode 100644 general/toaster/umdf2/filter/generic/filterum.vcxproj create mode 100644 general/toaster/umdf2/filter/generic/filterum.vcxproj.Filters create mode 100644 general/toaster/umdf2/func/featured/power.c create mode 100644 general/toaster/umdf2/func/featured/toaster.c create mode 100644 general/toaster/umdf2/func/featured/toaster.rc create mode 100644 general/toaster/umdf2/func/featured/wdffeaturedum.inx create mode 100644 general/toaster/umdf2/func/featured/wdffeaturedum.vcxproj create mode 100644 general/toaster/umdf2/func/featured/wdffeaturedum.vcxproj.Filters create mode 100644 general/toaster/umdf2/func/shared/toaster.h create mode 100644 general/toaster/umdf2/func/simple/toaster.c create mode 100644 general/toaster/umdf2/func/simple/wdfsimpleum.inx create mode 100644 general/toaster/umdf2/func/simple/wdfsimpleum.vcxproj create mode 100644 general/toaster/umdf2/func/simple/wdfsimpleum.vcxproj.Filters create mode 100644 general/toaster/umdf2/inc/driver.h create mode 100644 general/toaster/umdf2/inc/public.h create mode 100644 general/toaster/umdf2/umdf2toaster.sln create mode 100644 general/tracing/SystemTraceControl/ReadMe.md create mode 100644 general/tracing/SystemTraceControl/ReadMe.txt create mode 100644 general/tracing/SystemTraceControl/SystemTraceControl.cpp create mode 100644 general/tracing/SystemTraceControl/SystemTraceControl.sln create mode 100644 general/tracing/SystemTraceControl/SystemTraceControl.vcxproj create mode 100644 general/tracing/SystemTraceControl/SystemTraceControl.vcxproj.Filters create mode 100644 general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj create mode 100644 general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj.Filters create mode 100644 general/tracing/evntdrv/Eventdrv/drvioctl.h create mode 100644 general/tracing/evntdrv/Eventdrv/evntdrv.c create mode 100644 general/tracing/evntdrv/Eventdrv/evntdrv.xml create mode 100644 general/tracing/evntdrv/Eventdrv/evntdrvevents.rc create mode 100644 general/tracing/evntdrv/ReadMe.md create mode 100644 general/tracing/evntdrv/eventdrv.sln create mode 100644 general/tracing/evntdrv/evntctrl/evntctrl.vcxproj create mode 100644 general/tracing/evntdrv/evntctrl/evntctrl.vcxproj.Filters create mode 100644 general/tracing/evntdrv/evntctrl/install.c create mode 100644 general/tracing/evntdrv/evntctrl/install.h create mode 100644 general/tracing/evntdrv/evntctrl/tracectl.c create mode 100644 general/tracing/tracedriver/ReadMe.md create mode 100644 general/tracing/tracedriver/tracectl/install.c create mode 100644 general/tracing/tracedriver/tracectl/install.h create mode 100644 general/tracing/tracedriver/tracectl/tracectl.c create mode 100644 general/tracing/tracedriver/tracectl/tracectl.vcxproj create mode 100644 general/tracing/tracedriver/tracectl/tracectl.vcxproj.Filters create mode 100644 general/tracing/tracedriver/tracedrv.sln create mode 100644 general/tracing/tracedriver/tracedrv/drvioctl.h create mode 100644 general/tracing/tracedriver/tracedrv/tracedrv.c create mode 100644 general/tracing/tracedriver/tracedrv/tracedrv.ctl create mode 100644 general/tracing/tracedriver/tracedrv/tracedrv.h create mode 100644 general/tracing/tracedriver/tracedrv/tracedrv.rc create mode 100644 general/tracing/tracedriver/tracedrv/tracedrv.vcxproj create mode 100644 general/tracing/tracedriver/tracedrv/tracedrv.vcxproj.Filters create mode 100644 general/umdfSkeleton/ReadMe.md create mode 100644 general/umdfSkeleton/Skeleton.rc create mode 100644 general/umdfSkeleton/UMDFSkeleton.vcxproj create mode 100644 general/umdfSkeleton/UMDFSkeleton.vcxproj.Filters create mode 100644 general/umdfSkeleton/UMDFSkeleton_OSR.inx create mode 100644 general/umdfSkeleton/UMDFSkeleton_Root.inx create mode 100644 general/umdfSkeleton/comsup.cpp create mode 100644 general/umdfSkeleton/comsup.h create mode 100644 general/umdfSkeleton/device.cpp create mode 100644 general/umdfSkeleton/device.h create mode 100644 general/umdfSkeleton/dllsup.cpp create mode 100644 general/umdfSkeleton/driver.cpp create mode 100644 general/umdfSkeleton/driver.h create mode 100644 general/umdfSkeleton/exports.def create mode 100644 general/umdfSkeleton/internal.h create mode 100644 general/umdfSkeleton/umdfSkeleton.sln create mode 100644 gpio/samples/ReadMe.md create mode 100644 gpio/samples/sim.sln create mode 100644 gpio/samples/simdevice/GpioSample.asl create mode 100644 gpio/samples/simdevice/common.h create mode 100644 gpio/samples/simdevice/simdevice.c create mode 100644 gpio/samples/simdevice/simdevice.inx create mode 100644 gpio/samples/simdevice/simdevice.rc create mode 100644 gpio/samples/simdevice/simdevice.vcxproj create mode 100644 gpio/samples/simdevice/simdevice.vcxproj.Filters create mode 100644 gpio/samples/simdeviceumdf/GpioSampleUMDF.asl create mode 100644 gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj create mode 100644 gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj.Filters create mode 100644 gpio/samples/simdeviceumdf/comsup.cpp create mode 100644 gpio/samples/simdeviceumdf/comsup.h create mode 100644 gpio/samples/simdeviceumdf/device.cpp create mode 100644 gpio/samples/simdeviceumdf/device.h create mode 100644 gpio/samples/simdeviceumdf/dllsup.cpp create mode 100644 gpio/samples/simdeviceumdf/driver.cpp create mode 100644 gpio/samples/simdeviceumdf/driver.h create mode 100644 gpio/samples/simdeviceumdf/exports.def create mode 100644 gpio/samples/simdeviceumdf/internal.h create mode 100644 gpio/samples/simdeviceumdf/queue.cpp create mode 100644 gpio/samples/simdeviceumdf/queue.h create mode 100644 gpio/samples/simdeviceumdf/simdeviceumdf.rc create mode 100644 gpio/samples/simdeviceumdf/simdeviceumdfdriver.ctl create mode 100644 gpio/samples/simdeviceumdf/simdeviceumdfdriver.inx create mode 100644 gpio/samples/simgpio/simgpio.c create mode 100644 gpio/samples/simgpio/simgpio.inx create mode 100644 gpio/samples/simgpio/simgpio.rc create mode 100644 gpio/samples/simgpio/simgpio.vcxproj create mode 100644 gpio/samples/simgpio/simgpio.vcxproj.Filters create mode 100644 gpio/samples/simgpio_i2c/simgpio_i2c.c create mode 100644 gpio/samples/simgpio_i2c/simgpio_i2c.h create mode 100644 gpio/samples/simgpio_i2c/simgpio_i2c.inx create mode 100644 gpio/samples/simgpio_i2c/simgpio_i2c.rc create mode 100644 gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj create mode 100644 gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters create mode 100644 gpio/samples/simgpio_i2c/spb_i2c.c create mode 100644 gpio/samples/simgpio_i2c/trace.h create mode 100644 network/config/bindview/BINDING.CPP create mode 100644 network/config/bindview/BINDVIEW.CPP create mode 100644 network/config/bindview/BINDVIEW.H create mode 100644 network/config/bindview/BINDVIEW.ICO create mode 100644 network/config/bindview/BindView.rc create mode 100644 network/config/bindview/Component.cpp create mode 100644 network/config/bindview/NetCfgAPI.cpp create mode 100644 network/config/bindview/NetCfgAPI.h create mode 100644 network/config/bindview/RESOURCE.H create mode 100644 network/config/bindview/ReadMe.md create mode 100644 network/config/bindview/bindview.htm create mode 100644 network/config/bindview/bindview.sln create mode 100644 network/config/bindview/bindview.vcxproj create mode 100644 network/config/bindview/bindview.vcxproj.Filters create mode 100644 network/modem/fakemodem/ReadMe.md create mode 100644 network/modem/fakemodem/driver.c create mode 100644 network/modem/fakemodem/fakemodem.h create mode 100644 network/modem/fakemodem/fakemodem.sln create mode 100644 network/modem/fakemodem/fakemodem.vcxproj create mode 100644 network/modem/fakemodem/fakemodem.vcxproj.Filters create mode 100644 network/modem/fakemodem/ioctl.c create mode 100644 network/modem/fakemodem/mdmfake.inx create mode 100644 network/modem/fakemodem/readwrit.c create mode 100644 network/ndis/extension/ReadMe.md create mode 100644 network/ndis/extension/base/SxApi.h create mode 100644 network/ndis/extension/base/SxBase.c create mode 100644 network/ndis/extension/base/SxBase.h create mode 100644 network/ndis/extension/base/SxLibrary.c create mode 100644 network/ndis/extension/base/SxLibrary.h create mode 100644 network/ndis/extension/base/precomp.h create mode 100644 network/ndis/extension/base/precompsrc.c create mode 100644 network/ndis/extension/base/sxbase.vcxproj create mode 100644 network/ndis/extension/base/sxbase.vcxproj.Filters create mode 100644 network/ndis/extension/extensions.sln create mode 100644 network/ndis/extension/samples/forward/MSForwardExtPolicy.mof create mode 100644 network/ndis/extension/samples/forward/MSForwardExtPolicyStatus.mof create mode 100644 network/ndis/extension/samples/forward/MsForwardExt.c create mode 100644 network/ndis/extension/samples/forward/MsForwardExt.h create mode 100644 network/ndis/extension/samples/forward/MsForwardExt.rc create mode 100644 network/ndis/extension/samples/forward/install.cmd create mode 100644 network/ndis/extension/samples/forward/msforwardext.inf create mode 100644 network/ndis/extension/samples/forward/msforwardext.vcxproj create mode 100644 network/ndis/extension/samples/forward/msforwardext.vcxproj.Filters create mode 100644 network/ndis/extension/samples/forward/precomp.h create mode 100644 network/ndis/extension/samples/forward/precompsrc.c create mode 100644 network/ndis/extension/samples/forward/removeRoute.ps1 create mode 100644 network/ndis/extension/samples/forward/setRoute.ps1 create mode 100644 network/ndis/extension/samples/forward/uninstall.cmd create mode 100644 network/ndis/extension/samples/passthrough/MsPassthroughExt.c create mode 100644 network/ndis/extension/samples/passthrough/MsPassthroughExt.rc create mode 100644 network/ndis/extension/samples/passthrough/install.cmd create mode 100644 network/ndis/extension/samples/passthrough/mspassthroughext.inf create mode 100644 network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj create mode 100644 network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj.Filters create mode 100644 network/ndis/extension/samples/passthrough/precomp.h create mode 100644 network/ndis/extension/samples/passthrough/precompsrc.c create mode 100644 network/ndis/extension/samples/passthrough/uninstall.cmd create mode 100644 network/ndis/filter/ReadMe.md create mode 100644 network/ndis/filter/device.c create mode 100644 network/ndis/filter/filter.c create mode 100644 network/ndis/filter/filter.h create mode 100644 network/ndis/filter/filter.rc create mode 100644 network/ndis/filter/filter.sln create mode 100644 network/ndis/filter/filteruser.h create mode 100644 network/ndis/filter/flt_dbg.c create mode 100644 network/ndis/filter/flt_dbg.h create mode 100644 network/ndis/filter/ndislwf.vcxproj create mode 100644 network/ndis/filter/ndislwf.vcxproj.Filters create mode 100644 network/ndis/filter/netlwf.inf create mode 100644 network/ndis/filter/precomp.h create mode 100644 network/ndis/filter/precompsrc.c create mode 100644 network/ndis/mux/ReadMe.md create mode 100644 network/ndis/mux/driver/60/miniport.c create mode 100644 network/ndis/mux/driver/60/mux.c create mode 100644 network/ndis/mux/driver/60/mux.h create mode 100644 network/ndis/mux/driver/60/mux.htm create mode 100644 network/ndis/mux/driver/60/mux.rc create mode 100644 network/ndis/mux/driver/60/mux_mp.inf create mode 100644 network/ndis/mux/driver/60/muxp.inf create mode 100644 network/ndis/mux/driver/60/novlan/mux.vcxproj create mode 100644 network/ndis/mux/driver/60/novlan/mux.vcxproj.Filters create mode 100644 network/ndis/mux/driver/60/novlan/precompsrc.c create mode 100644 network/ndis/mux/driver/60/precomp.h create mode 100644 network/ndis/mux/driver/60/protocol.c create mode 100644 network/ndis/mux/driver/60/public.h create mode 100644 network/ndis/mux/driver/60/vlan/muxvlan.vcxproj create mode 100644 network/ndis/mux/driver/60/vlan/muxvlan.vcxproj.Filters create mode 100644 network/ndis/mux/driver/60/vlan/precompsrc.c create mode 100644 network/ndis/mux/mux.sln create mode 100644 network/ndis/mux/notifyob/adapter.cpp create mode 100644 network/ndis/mux/notifyob/adapter.h create mode 100644 network/ndis/mux/notifyob/common.cpp create mode 100644 network/ndis/mux/notifyob/common.h create mode 100644 network/ndis/mux/notifyob/dllmain.cpp create mode 100644 network/ndis/mux/notifyob/implinc.cpp create mode 100644 network/ndis/mux/notifyob/list.h create mode 100644 network/ndis/mux/notifyob/mux.def create mode 100644 network/ndis/mux/notifyob/mux.vcxproj create mode 100644 network/ndis/mux/notifyob/mux.vcxproj.Filters create mode 100644 network/ndis/mux/notifyob/notify.RC create mode 100644 network/ndis/mux/notifyob/notify.RGS create mode 100644 network/ndis/mux/notifyob/notify.cpp create mode 100644 network/ndis/mux/notifyob/notify.h create mode 100644 network/ndis/mux/notifyob/notify.htm create mode 100644 network/ndis/mux/notifyob/notifyn.idl create mode 100644 network/ndis/mux/notifyob/public.h create mode 100644 network/ndis/mux/notifyob/resource.h create mode 100644 network/ndis/mux/notifyob/virtual.cpp create mode 100644 network/ndis/mux/notifyob/virtual.h create mode 100644 network/ndis/ndisprot/6x/ReadMe.md create mode 100644 network/ndis/ndisprot/6x/ndisprot60.sln create mode 100644 network/ndis/ndisprot/6x/sys/60/ndisprot60.inf create mode 100644 network/ndis/ndisprot/6x/sys/60/ndisprot60.rc create mode 100644 network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj create mode 100644 network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters create mode 100644 network/ndis/ndisprot/6x/sys/60/precompsrc.c create mode 100644 network/ndis/ndisprot/6x/sys/630/ndisprot630.inf create mode 100644 network/ndis/ndisprot/6x/sys/630/ndisprot630.rc create mode 100644 network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj create mode 100644 network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj.Filters create mode 100644 network/ndis/ndisprot/6x/sys/630/precompsrc.c create mode 100644 network/ndis/ndisprot/6x/sys/debug.c create mode 100644 network/ndis/ndisprot/6x/sys/debug.h create mode 100644 network/ndis/ndisprot/6x/sys/excallbk.c create mode 100644 network/ndis/ndisprot/6x/sys/macros.h create mode 100644 network/ndis/ndisprot/6x/sys/ndisbind.c create mode 100644 network/ndis/ndisprot/6x/sys/ndisprot.h create mode 100644 network/ndis/ndisprot/6x/sys/ntdisp.c create mode 100644 network/ndis/ndisprot/6x/sys/precomp.h create mode 100644 network/ndis/ndisprot/6x/sys/protuser.h create mode 100644 network/ndis/ndisprot/6x/sys/recv.c create mode 100644 network/ndis/ndisprot/6x/sys/send.c create mode 100644 network/ndis/ndisprot/6x/test/prottest.c create mode 100644 network/ndis/ndisprot/6x/test/prottest.vcxproj create mode 100644 network/ndis/ndisprot/6x/test/prottest.vcxproj.Filters create mode 100644 network/ndis/netvmini/6x/60/netvmini60.inf create mode 100644 network/ndis/netvmini/6x/60/netvmini60.rc create mode 100644 network/ndis/netvmini/6x/60/netvmini60.vcxproj create mode 100644 network/ndis/netvmini/6x/60/netvmini60.vcxproj.Filters create mode 100644 network/ndis/netvmini/6x/620/netvmini620.inf create mode 100644 network/ndis/netvmini/6x/620/netvmini620.rc create mode 100644 network/ndis/netvmini/6x/620/netvmini620.vcxproj create mode 100644 network/ndis/netvmini/6x/620/netvmini620.vcxproj.Filters create mode 100644 network/ndis/netvmini/6x/630/netvmini630.inf create mode 100644 network/ndis/netvmini/6x/630/netvmini630.rc create mode 100644 network/ndis/netvmini/6x/630/netvmini630.vcxproj create mode 100644 network/ndis/netvmini/6x/630/netvmini630.vcxproj.Filters create mode 100644 network/ndis/netvmini/6x/ReadMe.md create mode 100644 network/ndis/netvmini/6x/adapter.c create mode 100644 network/ndis/netvmini/6x/adapter.h create mode 100644 network/ndis/netvmini/6x/ctrlpath.c create mode 100644 network/ndis/netvmini/6x/ctrlpath.h create mode 100644 network/ndis/netvmini/6x/datapath.c create mode 100644 network/ndis/netvmini/6x/datapath.h create mode 100644 network/ndis/netvmini/6x/hardware.h create mode 100644 network/ndis/netvmini/6x/miniport.c create mode 100644 network/ndis/netvmini/6x/miniport.h create mode 100644 network/ndis/netvmini/6x/mphal.c create mode 100644 network/ndis/netvmini/6x/mphal.h create mode 100644 network/ndis/netvmini/6x/netvmin6.h create mode 100644 network/ndis/netvmini/6x/netvmini.sln create mode 100644 network/ndis/netvmini/6x/qos.c create mode 100644 network/ndis/netvmini/6x/qos.h create mode 100644 network/ndis/netvmini/6x/tcbrcb.c create mode 100644 network/ndis/netvmini/6x/tcbrcb.h create mode 100644 network/ndis/netvmini/6x/trace.h create mode 100644 network/ndis/netvmini/6x/vmq.c create mode 100644 network/ndis/netvmini/6x/vmq.h create mode 100644 network/trans/ddproxy/ReadMe.md create mode 100644 network/trans/ddproxy/ddproxy.sln create mode 100644 network/trans/ddproxy/sys/DD_drv.c create mode 100644 network/trans/ddproxy/sys/DD_proxy.c create mode 100644 network/trans/ddproxy/sys/DD_proxy.h create mode 100644 network/trans/ddproxy/sys/ddproxy.inf create mode 100644 network/trans/ddproxy/sys/ddproxy.vcxproj create mode 100644 network/trans/ddproxy/sys/ddproxy.vcxproj.Filters create mode 100644 network/trans/inspect/ReadMe.md create mode 100644 network/trans/inspect/inspect.sln create mode 100644 network/trans/inspect/sys/TL_drv.c create mode 100644 network/trans/inspect/sys/inspect.c create mode 100644 network/trans/inspect/sys/inspect.h create mode 100644 network/trans/inspect/sys/inspect.inf create mode 100644 network/trans/inspect/sys/inspect.vcxproj create mode 100644 network/trans/inspect/sys/inspect.vcxproj.Filters create mode 100644 network/trans/inspect/sys/utils.c create mode 100644 network/trans/inspect/sys/utils.h create mode 100644 network/trans/msnmntr/ReadMe.md create mode 100644 network/trans/msnmntr/exe/monitor.cpp create mode 100644 network/trans/msnmntr/exe/monitor.vcxproj create mode 100644 network/trans/msnmntr/exe/monitor.vcxproj.Filters create mode 100644 network/trans/msnmntr/exe/mtrace.cmd create mode 100644 network/trans/msnmntr/inc/ioctl.h create mode 100644 network/trans/msnmntr/inc/mntrguid.h create mode 100644 network/trans/msnmntr/msnmntr.sln create mode 100644 network/trans/msnmntr/sys/ctl.c create mode 100644 network/trans/msnmntr/sys/ctl.h create mode 100644 network/trans/msnmntr/sys/init.c create mode 100644 network/trans/msnmntr/sys/msnmntr.c create mode 100644 network/trans/msnmntr/sys/msnmntr.h create mode 100644 network/trans/msnmntr/sys/msnmntr.inf create mode 100644 network/trans/msnmntr/sys/msnmntr.vcxproj create mode 100644 network/trans/msnmntr/sys/msnmntr.vcxproj.Filters create mode 100644 network/trans/msnmntr/sys/notify.c create mode 100644 network/trans/msnmntr/sys/notify.h create mode 100644 network/trans/stmedit/ReadMe.md create mode 100644 network/trans/stmedit/stmedit.sln create mode 100644 network/trans/stmedit/sys/inline_edit.c create mode 100644 network/trans/stmedit/sys/inline_edit.h create mode 100644 network/trans/stmedit/sys/oob_edit.c create mode 100644 network/trans/stmedit/sys/oob_edit.h create mode 100644 network/trans/stmedit/sys/stmedit.inf create mode 100644 network/trans/stmedit/sys/stmedit.vcxproj create mode 100644 network/trans/stmedit/sys/stmedit.vcxproj.Filters create mode 100644 network/trans/stmedit/sys/stream_callout.c create mode 100644 network/trans/stmedit/sys/stream_callout.h create mode 100644 network/wlan/ReadMe.md create mode 100644 network/wlan/ihvsample/adapters.cpp create mode 100644 network/wlan/ihvsample/adapters.h create mode 100644 network/wlan/ihvsample/ihv1xext.xml create mode 100644 network/wlan/ihvsample/ihvconn.xml create mode 100644 network/wlan/ihvsample/ihvmachine.xml create mode 100644 network/wlan/ihvsample/ihvonexext.cpp create mode 100644 network/wlan/ihvsample/ihvonexext.h create mode 100644 network/wlan/ihvsample/ihvplapmachineoruser.xml create mode 100644 network/wlan/ihvsample/ihvplapuser.xml create mode 100644 network/wlan/ihvsample/ihvsample.cpp create mode 100644 network/wlan/ihvsample/ihvsample.def create mode 100644 network/wlan/ihvsample/ihvsample.h create mode 100644 network/wlan/ihvsample/ihvsample.vcxproj create mode 100644 network/wlan/ihvsample/ihvsample.vcxproj.Filters create mode 100644 network/wlan/ihvsample/ihvtimelymachineoruser.xml create mode 100644 network/wlan/ihvsample/ihvtimelyuser.xml create mode 100644 network/wlan/ihvsample/ihvwep.cpp create mode 100644 network/wlan/ihvsample/ihvwep.h create mode 100644 network/wlan/ihvsample/ihvwep1.xml create mode 100644 network/wlan/ihvsample/ihvwep2.xml create mode 100644 network/wlan/ihvsample/precomp.h create mode 100644 network/wlan/ihvsample/precompsrc.c create mode 100644 network/wlan/ihvsample/profile.cpp create mode 100644 network/wlan/ihvsample/profile.h create mode 100644 network/wlan/ihvsample/rc4utils.h create mode 100644 network/wlan/ihvsample/utils.cpp create mode 100644 network/wlan/ihvsample/utils.h create mode 100644 network/wlan/ihvsampleui/IHVClassFactory.cpp create mode 100644 network/wlan/ihvsampleui/IHVClassFactory.h create mode 100644 network/wlan/ihvsampleui/IHVRegistryHelper.cpp create mode 100644 network/wlan/ihvsampleui/IHVRegistryHelper.h create mode 100644 network/wlan/ihvsampleui/IHVSample.idl create mode 100644 network/wlan/ihvsampleui/IHVSampleExtUI.cpp create mode 100644 network/wlan/ihvsampleui/IHVSampleExtUI.h create mode 100644 network/wlan/ihvsampleui/IHVSampleExtUICon.cpp create mode 100644 network/wlan/ihvsampleui/IHVSampleExtUICon.h create mode 100644 network/wlan/ihvsampleui/IHVSampleExtUIKey.cpp create mode 100644 network/wlan/ihvsampleui/IHVSampleExtUIKey.h create mode 100644 network/wlan/ihvsampleui/IHVSampleExtUISec.cpp create mode 100644 network/wlan/ihvsampleui/IHVSampleExtUISec.h create mode 100644 network/wlan/ihvsampleui/IHVSampleProfile.cpp create mode 100644 network/wlan/ihvsampleui/IHVSampleProfile.h create mode 100644 network/wlan/ihvsampleui/IHVSampleUI.cpp create mode 100644 network/wlan/ihvsampleui/IHVSampleUI.def create mode 100644 network/wlan/ihvsampleui/IHVSampleUI.rc create mode 100644 network/wlan/ihvsampleui/IHVSampleUI.vcxproj create mode 100644 network/wlan/ihvsampleui/IHVSampleUI.vcxproj.Filters create mode 100644 network/wlan/ihvsampleui/IHVUIInc.idl create mode 100644 network/wlan/ihvsampleui/iunk.h create mode 100644 network/wlan/ihvsampleui/precomp.h create mode 100644 network/wlan/ihvsampleui/resource.h create mode 100644 network/wlan/ihvsampleui/utils.cpp create mode 100644 network/wlan/ihvsampleui/utils.h create mode 100644 network/wlan/wlan.sln create mode 100644 network/wsk/echosrv/ReadMe.md create mode 100644 network/wsk/echosrv/echosrv.ctl create mode 100644 network/wsk/echosrv/echosrv.sln create mode 100644 network/wsk/echosrv/echosrv.vcxproj create mode 100644 network/wsk/echosrv/echosrv.vcxproj.Filters create mode 100644 network/wsk/echosrv/wsksmple.c create mode 100644 network/wsk/echosrv/wsksmple.rc create mode 100644 nfp/net/ReadMe.md create mode 100644 nfp/net/driver/Connection.cpp create mode 100644 nfp/net/driver/FileContext.cpp create mode 100644 nfp/net/driver/FileContext.h create mode 100644 nfp/net/driver/NetNfpProvider.vcxproj create mode 100644 nfp/net/driver/NetNfpProvider.vcxproj.Filters create mode 100644 nfp/net/driver/Queue.cpp create mode 100644 nfp/net/driver/Queue.h create mode 100644 nfp/net/driver/connection.h create mode 100644 nfp/net/driver/device.cpp create mode 100644 nfp/net/driver/device.h create mode 100644 nfp/net/driver/dllsup.cpp create mode 100644 nfp/net/driver/driver.cpp create mode 100644 nfp/net/driver/driver.h create mode 100644 nfp/net/driver/exports.def create mode 100644 nfp/net/driver/internal.h create mode 100644 nfp/net/driver/list.h create mode 100644 nfp/net/driver/netnfpprovider.inx create mode 100644 nfp/net/driver/netnfpprovider.rc create mode 100644 nfp/net/driver/socketlistener.cpp create mode 100644 nfp/net/driver/socketlistener.h create mode 100644 nfp/net/driver/wppdefs.h create mode 100644 nfp/net/exe/NetNfpControl.cpp create mode 100644 nfp/net/exe/NetNfpControl.vcxproj create mode 100644 nfp/net/exe/NetNfpControl.vcxproj.Filters create mode 100644 nfp/net/exe/precomp.h create mode 100644 nfp/net/exe/precompsrc.cpp create mode 100644 nfp/net/inc/NetNfp.h create mode 100644 nfp/net/netnfp.sln create mode 100644 pofx/UMDF2/App/PowerFxApp.cpp create mode 100644 pofx/UMDF2/App/PowerFxApp.vcxproj create mode 100644 pofx/UMDF2/App/PowerFxApp.vcxproj.Filters create mode 100644 pofx/UMDF2/App/UserInput.cpp create mode 100644 pofx/UMDF2/App/include.h create mode 100644 pofx/UMDF2/Driver/SingleComp/HwSim.c create mode 100644 pofx/UMDF2/Driver/SingleComp/HwSim.h create mode 100644 pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.inx create mode 100644 pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj create mode 100644 pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj.Filters create mode 100644 pofx/UMDF2/Driver/SingleComp/driver.c create mode 100644 pofx/UMDF2/Driver/SingleComp/driver.h create mode 100644 pofx/UMDF2/Driver/SingleComp/include.h create mode 100644 pofx/UMDF2/ReadMe.md create mode 100644 pofx/UMDF2/inc/AppInterface.h create mode 100644 pofx/UMDF2/pofx.sln create mode 100644 pofx/WDF/App/PowerFxApp.cpp create mode 100644 pofx/WDF/App/PowerFxApp.vcxproj create mode 100644 pofx/WDF/App/PowerFxApp.vcxproj.Filters create mode 100644 pofx/WDF/App/UserInput.cpp create mode 100644 pofx/WDF/App/include.h create mode 100644 pofx/WDF/Driver/MultiComp/WdfMultiComp.mht create mode 100644 pofx/WDF/Driver/MultiComp/driver/HwSim.c create mode 100644 pofx/WDF/Driver/MultiComp/driver/HwSim.h create mode 100644 pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.c create mode 100644 pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.h create mode 100644 pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx create mode 100644 pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj create mode 100644 pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters create mode 100644 pofx/WDF/Driver/MultiComp/inc/WdfPoFx.h create mode 100644 pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj create mode 100644 pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters create mode 100644 pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h create mode 100644 pofx/WDF/Driver/MultiComp/lib/component.c create mode 100644 pofx/WDF/Driver/MultiComp/lib/device.c create mode 100644 pofx/WDF/Driver/MultiComp/lib/init.c create mode 100644 pofx/WDF/Driver/MultiComp/lib/s0idle.c create mode 100644 pofx/WDF/Driver/MultiComp/lib/s0idle.h create mode 100644 pofx/WDF/Driver/SingleComp/HwSim.c create mode 100644 pofx/WDF/Driver/SingleComp/HwSim.h create mode 100644 pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj create mode 100644 pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj.Filters create mode 100644 pofx/WDF/Driver/SingleComp/SingleComponentFStateSample.inx create mode 100644 pofx/WDF/Driver/SingleComp/driver.c create mode 100644 pofx/WDF/Driver/SingleComp/driver.h create mode 100644 pofx/WDF/Driver/SingleComp/include.h create mode 100644 pofx/WDF/ReadMe.md create mode 100644 pofx/WDF/inc/AppInterface.h create mode 100644 pofx/WDF/pofx.sln create mode 100644 print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj create mode 100644 print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj.Filters create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M1.1a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M1.2a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.14a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.17a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.20a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.24a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.27a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.27b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.30a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.31a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.32a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.33a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.35a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.36a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.36b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.41a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.41b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.42a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.42b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.43a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.43b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.44a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.48a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.49a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.49b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.50a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.52a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.55a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.56a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M1.2a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.23a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.6a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.6b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.2a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.3a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.3b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.7a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.10a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.10b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.13a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.13b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.14a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.14b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.17a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.18a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.25a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.35a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.36a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.37a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.4a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.4b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.54a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.5a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.5b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.70a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.73a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.73b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75d.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.76a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.77a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.77b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.80.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.81.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.82.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.11a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.11b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.12a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.12b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.13a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.14a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.20a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.20b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.2a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.2b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.3a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.5a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.5b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.6a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.6b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.2a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.2b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.3a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.15a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.4a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.4b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.7a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.1a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.1b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.2a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.2b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.3a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.3b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.4a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.4b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.5a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.5b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.6a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.6b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.11a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.11b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.2a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.2b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.3a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.5a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.6a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M8.14a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M9.10a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M9.10b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.10a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.10b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.11a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.12a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.13a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.13b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.15a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.17a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.17b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.18a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.18b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.1a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.20a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.20b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.21a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.21b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.22a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.25a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.26a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.26b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.27a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.27b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.28a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.28b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.30a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.30b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.3a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.5a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6d.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.7a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.7b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8c.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.9a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.10a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.18a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.4a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.5a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.5b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.6a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.6b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.7a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.7b.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.10a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.14a.oxps create mode 100644 print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.9a.oxps create mode 100644 print/SampleOpenXPS/OpenXPSTestFiles_EndUserLicenseAgreement.txt create mode 100644 print/SampleOpenXPS/QualityLogicMinBar_oxps/mb01.oxps create mode 100644 print/SampleOpenXPS/QualityLogicMinBar_oxps/mb02.oxps create mode 100644 print/SampleOpenXPS/QualityLogicMinBar_oxps/mb03.oxps create mode 100644 print/SampleOpenXPS/QualityLogicMinBar_oxps/mb04.oxps create mode 100644 print/SampleOpenXPS/QualityLogicMinBar_oxps/mb05.oxps create mode 100644 print/SampleOpenXPS/QualityLogicMinBar_oxps/mb06.oxps create mode 100644 print/SampleOpenXPS/QualityLogicMinBar_oxps/mb07.oxps create mode 100644 print/SampleOpenXPS/QualityLogicMinBar_oxps/mb08.oxps create mode 100644 print/SampleOpenXPS/QualityLogicMinBar_oxps/mb09.oxps create mode 100644 print/SampleOpenXPS/ReadMe.md create mode 100644 print/SampleOpenXPS/SampleOpenXPS.sln create mode 100644 print/SampleOpenXPS/readme.txt create mode 100644 print/SampleXPS/BlankProjSrc/BlankProject.vcxproj create mode 100644 print/SampleXPS/BlankProjSrc/BlankProject.vcxproj.Filters create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M1.1a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M1.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.13a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.14a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.17a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.20a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.24a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.27a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.27b.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.30a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.31a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.32a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.33a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.35a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.35b.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.36a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.41a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.42a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.43a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.44a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.48a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.49a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.50a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.52a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.55a.xps create mode 100644 print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.56a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.10a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.11a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.17a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.18a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.18b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.1a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.1b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.20a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26c.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.27a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.28a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.2b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.34a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.3a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.5a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.6a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.6b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.7a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.7c.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.8a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.18a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.24a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.5a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.5b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.6a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.6b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.7a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.7b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10c.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.11a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.11b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.12a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.12b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.13a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.14a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.15a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.16a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.17a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.17b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.18a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.1a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.1b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.20a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.21a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.21b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.25a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.26a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.28a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.29a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.31a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.31b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.32a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.32b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.4a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.4b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.5a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.6a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.7a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.8a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9b.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9c.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.1a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.3a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.1a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.4a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.6a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.7a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.8a.xps create mode 100644 print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M7.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M1.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M12.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M12.3b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M12.5a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M12.7a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.10a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.13a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.13b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.14a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.18a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.25a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.36a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.37a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.3a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.3b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.3c.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.4a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.4b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.59a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.5a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.5b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.6a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.6b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.71a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.71b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.71c.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.73a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.73b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.74a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.75a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.75b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M2.76a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M3.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M3.3a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M3.5a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M3.6a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M3.6b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M4.3a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M5.15a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M5.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M5.2b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M5.4a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M5.4b.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M5.7a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M6.2a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M6.3a.xps create mode 100644 print/SampleXPS/ConformanceViolations/XPS/M7.5a.xps create mode 100644 print/SampleXPS/QualityLogicMinBar/mb01.xps create mode 100644 print/SampleXPS/QualityLogicMinBar/mb02.xps create mode 100644 print/SampleXPS/QualityLogicMinBar/mb03.xps create mode 100644 print/SampleXPS/QualityLogicMinBar/mb04.xps create mode 100644 print/SampleXPS/QualityLogicMinBar/mb05.xps create mode 100644 print/SampleXPS/QualityLogicMinBar/mb06.xps create mode 100644 print/SampleXPS/QualityLogicMinBar/mb07.xps create mode 100644 print/SampleXPS/QualityLogicMinBar/mb08.xps create mode 100644 print/SampleXPS/QualityLogicMinBar/mb09.xps create mode 100644 print/SampleXPS/ReadMe.md create mode 100644 print/SampleXPS/SampleXPS.sln create mode 100644 print/SampleXPS/XPSTestFiles_EndUserLicenseAgreement.txt create mode 100644 print/SampleXPS/readme.txt create mode 100644 print/SimplePipelineFilter/FilterSample.def create mode 100644 print/SimplePipelineFilter/FilterSample.rc create mode 100644 print/SimplePipelineFilter/ReadMe.md create mode 100644 print/SimplePipelineFilter/SimplePipelineFilter.sln create mode 100644 print/SimplePipelineFilter/StreamFilter.cxx create mode 100644 print/SimplePipelineFilter/StreamFilter.hxx create mode 100644 print/SimplePipelineFilter/WdkPipelineFilter.vcxproj create mode 100644 print/SimplePipelineFilter/WdkPipelineFilter.vcxproj.Filters create mode 100644 print/SimplePipelineFilter/XpsFilter.cxx create mode 100644 print/SimplePipelineFilter/XpsFilter.hxx create mode 100644 print/SimplePipelineFilter/main.cxx create mode 100644 print/SimplePipelineFilter/main.hxx create mode 100644 print/SimplePipelineFilter/precomp.hxx create mode 100644 print/SimplePipelineFilter/precompsrc.cpp create mode 100644 print/SimplePipelineFilter/smartptr.hxx create mode 100644 print/SimplePipelineFilter/trace.hxx create mode 100644 print/XpsRasFilter/ReadMe.md create mode 100644 print/XpsRasFilter/XpsRasFilter.sln create mode 100644 print/XpsRasFilter/install/xpsrassmpl-PipelineConfig.xml create mode 100644 print/XpsRasFilter/install/xpsrassmpl.gpd create mode 100644 print/XpsRasFilter/install/xpsrassmpl.inf create mode 100644 print/XpsRasFilter/src/BitmapHandler.cpp create mode 100644 print/XpsRasFilter/src/BitmapHandler.h create mode 100644 print/XpsRasFilter/src/Exception.cpp create mode 100644 print/XpsRasFilter/src/Exception.h create mode 100644 print/XpsRasFilter/src/OMConvertor.cpp create mode 100644 print/XpsRasFilter/src/OMConvertor.h create mode 100644 print/XpsRasFilter/src/PThandler.cpp create mode 100644 print/XpsRasFilter/src/PThandler.h create mode 100644 print/XpsRasFilter/src/UnknownBase.h create mode 100644 print/XpsRasFilter/src/WppTrace.cpp create mode 100644 print/XpsRasFilter/src/WppTrace.h create mode 100644 print/XpsRasFilter/src/dllentry.cpp create mode 100644 print/XpsRasFilter/src/filtertypes.h create mode 100644 print/XpsRasFilter/src/precomp.h create mode 100644 print/XpsRasFilter/src/precompsrc.cpp create mode 100644 print/XpsRasFilter/src/rasinterface.cpp create mode 100644 print/XpsRasFilter/src/rasinterface.h create mode 100644 print/XpsRasFilter/src/xpsrasfilter.cpp create mode 100644 print/XpsRasFilter/src/xpsrasfilter.def create mode 100644 print/XpsRasFilter/src/xpsrasfilter.h create mode 100644 print/XpsRasFilter/src/xpsrasfilter.rc create mode 100644 print/XpsRasFilter/src/xpsrasfilter.vcxproj create mode 100644 print/XpsRasFilter/src/xpsrasfilter.vcxproj.Filters create mode 100644 print/cpsuisam/CPSUIDAT.C create mode 100644 print/cpsuisam/CPSUIDAT.H create mode 100644 print/cpsuisam/CPSUISAM.DEF create mode 100644 print/cpsuisam/CPSUISAM.DLG create mode 100644 print/cpsuisam/CPSUISAM.H create mode 100644 print/cpsuisam/DEBUG.C create mode 100644 print/cpsuisam/DEBUG.H create mode 100644 print/cpsuisam/PRECOMP.H create mode 100644 print/cpsuisam/ReadMe.md create mode 100644 print/cpsuisam/cpsuisam.c create mode 100644 print/cpsuisam/cpsuisam.sln create mode 100644 print/cpsuisam/cpsuisam.vcxproj create mode 100644 print/cpsuisam/cpsuisam.vcxproj.Filters create mode 100644 print/cpsuisam/icons/apple.ico create mode 100644 print/cpsuisam/icons/cpsuisam.ico create mode 100644 print/cpsuisam/precompsrc.c create mode 100644 print/cpsuisam/res.rc create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/App.xaml create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/App.xaml.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/BidiHelper.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Fabrikam_Logo.png create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/PrintPreferenceWindow.xaml create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/PrintPreferenceWindow.xaml.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/PrintSchemaHelper.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/PrinterExtensionSample.csproj create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Properties/AssemblyInfo.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Properties/Resources.Designer.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Properties/Resources.resx create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Properties/Settings.Designer.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Properties/Settings.settings create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Strings.Designer.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Strings.resx create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/ValidationModalDialog.xaml create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/ValidationModalDialog.xaml.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/WindowHelper.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/app.config create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/bidi_Ink_mock.xml create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/PrinterExtensionLibrary/PrinterExtensionAdapters.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/PrinterExtensionLibrary/PrinterExtensionLibrary.csproj create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/PrinterExtensionLibrary/PrinterExtensionManager.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/PrinterExtensionLibrary/PrinterExtensionTypes.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/PrinterExtensionLibrary/Properties/AssemblyInfo.cs create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/PrinterExtensionSample.sln create mode 100644 print/v4PrintDriverSamples/PrinterExtensionSample/ReadMe.md create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.js create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.sln create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.vcxproj create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.vcxproj.Filters create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ReadMe.md create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/v4PrintDriver-Intellisense-Windows8.1.js create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/v4PrintDriver-Intellisense.js create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/ReadMe.md create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.js create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.sln create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.vcxproj create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.vcxproj.Filters create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.xml create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/v4PrintDriver-Intellisense-Windows8.1.js create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/v4PrintDriver-Intellisense.js create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/ReadMe.md create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.sln create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.vcxproj create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.vcxproj.Filters create mode 100644 print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.xml create mode 100644 sd/sdiomars/ReadMe.md create mode 100644 sd/sdiomars/mars.c create mode 100644 sd/sdiomars/mars.h create mode 100644 sd/sdiomars/mars.inx create mode 100644 sd/sdiomars/mars.vcxproj create mode 100644 sd/sdiomars/mars.vcxproj.Filters create mode 100644 sd/sdiomars/ntddmars.h create mode 100644 sd/sdiomars/sdiomars.sln create mode 100644 security/elam/ReadMe.md create mode 100644 security/elam/elam.sln create mode 100644 security/elam/elamsample.c create mode 100644 security/elam/elamsample.h create mode 100644 security/elam/elamsample.rc create mode 100644 security/elam/elamsample.vcxproj create mode 100644 security/elam/elamsample.vcxproj.Filters create mode 100644 sensors/ADXL345Acc/ADXL345Acc.asl create mode 100644 sensors/ADXL345Acc/ADXL345Acc.def create mode 100644 sensors/ADXL345Acc/ADXL345Acc.inx create mode 100644 sensors/ADXL345Acc/ADXL345Acc.sln create mode 100644 sensors/ADXL345Acc/ADXL345Acc.vcxproj create mode 100644 sensors/ADXL345Acc/ADXL345Acc.vcxproj.Filters create mode 100644 sensors/ADXL345Acc/Device.h create mode 100644 sensors/ADXL345Acc/Driver.h create mode 100644 sensors/ADXL345Acc/ReadMe.md create mode 100644 sensors/ADXL345Acc/SensorsTrace.h create mode 100644 sensors/ADXL345Acc/adxl345.h create mode 100644 sensors/ADXL345Acc/client.cpp create mode 100644 sensors/ADXL345Acc/device.cpp create mode 100644 sensors/ADXL345Acc/driver.cpp create mode 100644 sensors/Activity/Activity.def create mode 100644 sensors/Activity/Activity.inx create mode 100644 sensors/Activity/Activity.sln create mode 100644 sensors/Activity/Activity.vcxproj create mode 100644 sensors/Activity/Activity.vcxproj.Filters create mode 100644 sensors/Activity/Device.h create mode 100644 sensors/Activity/Driver.h create mode 100644 sensors/Activity/HardwareSimulator.h create mode 100644 sensors/Activity/ReadMe.md create mode 100644 sensors/Activity/SensorsTrace.h create mode 100644 sensors/Activity/client.cpp create mode 100644 sensors/Activity/device.cpp create mode 100644 sensors/Activity/driver.cpp create mode 100644 sensors/Activity/hardwaresimulator.cpp create mode 100644 sensors/CustomSensors/CustomSensors.def create mode 100644 sensors/CustomSensors/CustomSensors.inx create mode 100644 sensors/CustomSensors/CustomSensors.sln create mode 100644 sensors/CustomSensors/CustomSensors.vcxproj create mode 100644 sensors/CustomSensors/CustomSensors.vcxproj.Filters create mode 100644 sensors/CustomSensors/Device.h create mode 100644 sensors/CustomSensors/Driver.h create mode 100644 sensors/CustomSensors/HardwareSimulator.h create mode 100644 sensors/CustomSensors/ReadMe.md create mode 100644 sensors/CustomSensors/SensorsTrace.h create mode 100644 sensors/CustomSensors/client.cpp create mode 100644 sensors/CustomSensors/device.cpp create mode 100644 sensors/CustomSensors/driver.cpp create mode 100644 sensors/CustomSensors/hardwaresimulator.cpp create mode 100644 sensors/Pedometer/Device.h create mode 100644 sensors/Pedometer/Driver.h create mode 100644 sensors/Pedometer/HardwareSimulator.h create mode 100644 sensors/Pedometer/Pedometer.def create mode 100644 sensors/Pedometer/Pedometer.inx create mode 100644 sensors/Pedometer/Pedometer.sln create mode 100644 sensors/Pedometer/Pedometer.vcxproj create mode 100644 sensors/Pedometer/Pedometer.vcxproj.Filters create mode 100644 sensors/Pedometer/ReadMe.md create mode 100644 sensors/Pedometer/SensorsTrace.h create mode 100644 sensors/Pedometer/client.cpp create mode 100644 sensors/Pedometer/device.cpp create mode 100644 sensors/Pedometer/driver.cpp create mode 100644 sensors/Pedometer/hardwaresimulator.cpp create mode 100644 sensors/SimpleDeviceOrientationSensor/Device.h create mode 100644 sensors/SimpleDeviceOrientationSensor/Driver.h create mode 100644 sensors/SimpleDeviceOrientationSensor/HardwareSimulator.h create mode 100644 sensors/SimpleDeviceOrientationSensor/ReadMe.md create mode 100644 sensors/SimpleDeviceOrientationSensor/SensorsTrace.h create mode 100644 sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.def create mode 100644 sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.inx create mode 100644 sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.sln create mode 100644 sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj create mode 100644 sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj.Filters create mode 100644 sensors/SimpleDeviceOrientationSensor/client.cpp create mode 100644 sensors/SimpleDeviceOrientationSensor/device.cpp create mode 100644 sensors/SimpleDeviceOrientationSensor/driver.cpp create mode 100644 sensors/SimpleDeviceOrientationSensor/hardwaresimulator.cpp create mode 100644 serial/VirtualSerial/ComPort/exports.def create mode 100644 serial/VirtualSerial/ComPort/virtualserial.inx create mode 100644 serial/VirtualSerial/ComPort/virtualserial.rc create mode 100644 serial/VirtualSerial/ComPort/virtualserial.vcxproj create mode 100644 serial/VirtualSerial/ComPort/virtualserial.vcxproj.Filters create mode 100644 serial/VirtualSerial/FakeModem/exports.def create mode 100644 serial/VirtualSerial/FakeModem/fakemodem.inx create mode 100644 serial/VirtualSerial/FakeModem/fakemodem.rc create mode 100644 serial/VirtualSerial/FakeModem/fakemodem.vcxproj create mode 100644 serial/VirtualSerial/FakeModem/fakemodem.vcxproj.Filters create mode 100644 serial/VirtualSerial/ReadMe.md create mode 100644 serial/VirtualSerial/VirtualSerial.sln create mode 100644 serial/VirtualSerial/comsup.cpp create mode 100644 serial/VirtualSerial/comsup.h create mode 100644 serial/VirtualSerial/device.cpp create mode 100644 serial/VirtualSerial/device.h create mode 100644 serial/VirtualSerial/dllsup.cpp create mode 100644 serial/VirtualSerial/driver.cpp create mode 100644 serial/VirtualSerial/driver.h create mode 100644 serial/VirtualSerial/internal.h create mode 100644 serial/VirtualSerial/queue.cpp create mode 100644 serial/VirtualSerial/queue.h create mode 100644 serial/VirtualSerial/ringbuffer.cpp create mode 100644 serial/VirtualSerial/ringbuffer.h create mode 100644 serial/VirtualSerial/serial.h create mode 100644 serial/serenum/ReadMe.md create mode 100644 serial/serenum/SerEnum_sample.vcxproj create mode 100644 serial/serenum/SerEnum_sample.vcxproj.Filters create mode 100644 serial/serenum/enum.c create mode 100644 serial/serenum/log.c create mode 100644 serial/serenum/log.h create mode 100644 serial/serenum/pch.h create mode 100644 serial/serenum/pchsrc.c create mode 100644 serial/serenum/pnp.c create mode 100644 serial/serenum/power.c create mode 100644 serial/serenum/serenum.c create mode 100644 serial/serenum/serenum.h create mode 100644 serial/serenum/serenum.rc create mode 100644 serial/serenum/serenum.sln create mode 100644 serial/serenum/string.c create mode 100644 serial/serial/ReadMe.md create mode 100644 serial/serial/error.c create mode 100644 serial/serial/flush.c create mode 100644 serial/serial/immediat.c create mode 100644 serial/serial/initunlo.c create mode 100644 serial/serial/ioctl.c create mode 100644 serial/serial/isr.c create mode 100644 serial/serial/log.c create mode 100644 serial/serial/log.h create mode 100644 serial/serial/modmflow.c create mode 100644 serial/serial/openclos.c create mode 100644 serial/serial/pnp.c create mode 100644 serial/serial/power.c create mode 100644 serial/serial/precomp.h create mode 100644 serial/serial/precompsrc.c create mode 100644 serial/serial/purge.c create mode 100644 serial/serial/qsfile.c create mode 100644 serial/serial/read.c create mode 100644 serial/serial/registry.c create mode 100644 serial/serial/serial.h create mode 100644 serial/serial/serial.inx create mode 100644 serial/serial/serial.rc create mode 100644 serial/serial/serial.sln create mode 100644 serial/serial/serialp.h create mode 100644 serial/serial/serlog.mc create mode 100644 serial/serial/trace.h create mode 100644 serial/serial/utils.c create mode 100644 serial/serial/waitmask.c create mode 100644 serial/serial/wdfserial.vcxproj create mode 100644 serial/serial/wdfserial.vcxproj.Filters create mode 100644 serial/serial/wmi.c create mode 100644 serial/serial/write.c create mode 100644 setup/DIFxAPI/AppDrv/AppDrv.vcxproj create mode 100644 setup/DIFxAPI/AppDrv/AppDrv.vcxproj.Filters create mode 100644 setup/DIFxAPI/AppDrv/AppDrvDepend.c create mode 100644 setup/DIFxAPI/AppDrv/readme.htm create mode 100644 setup/DIFxAPI/DIFxAPI.sln create mode 100644 setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj create mode 100644 setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj.Filters create mode 100644 setup/DIFxAPI/DIFxCmd/difxcmd.c create mode 100644 setup/DIFxAPI/DIFxCmd/readme.htm create mode 100644 setup/DIFxAPI/ReadMe.md create mode 100644 setup/devcon/ReadMe.md create mode 100644 setup/devcon/cmds.cpp create mode 100644 setup/devcon/devcon.cpp create mode 100644 setup/devcon/devcon.h create mode 100644 setup/devcon/devcon.rc create mode 100644 setup/devcon/devcon.sln create mode 100644 setup/devcon/devcon.vcxproj create mode 100644 setup/devcon/devcon.vcxproj.Filters create mode 100644 setup/devcon/dump.cpp create mode 100644 setup/devcon/msg.mc create mode 100644 setup/devcon/rc_ids.h create mode 100644 spb/SkeletonI2C/ReadMe.md create mode 100644 spb/SkeletonI2C/SkeletonI2C.sln create mode 100644 spb/SkeletonI2C/controller.cpp create mode 100644 spb/SkeletonI2C/controller.h create mode 100644 spb/SkeletonI2C/device.cpp create mode 100644 spb/SkeletonI2C/device.h create mode 100644 spb/SkeletonI2C/driver.cpp create mode 100644 spb/SkeletonI2C/driver.h create mode 100644 spb/SkeletonI2C/hw.cpp create mode 100644 spb/SkeletonI2C/hw.h create mode 100644 spb/SkeletonI2C/i2ctrace.h create mode 100644 spb/SkeletonI2C/internal.h create mode 100644 spb/SkeletonI2C/resource.rc create mode 100644 spb/SkeletonI2C/skeletoni2c.asl create mode 100644 spb/SkeletonI2C/skeletoni2c.h create mode 100644 spb/SkeletonI2C/skeletoni2c.inx create mode 100644 spb/SkeletonI2C/skeletoni2c.vcxproj create mode 100644 spb/SkeletonI2C/skeletoni2c.vcxproj.Filters create mode 100644 spb/SpbTestTool/ReadMe.md create mode 100644 spb/SpbTestTool/SpbTestTool.sln create mode 100644 spb/SpbTestTool/exe/SpbTestTool.vcxproj create mode 100644 spb/SpbTestTool/exe/SpbTestTool.vcxproj.Filters create mode 100644 spb/SpbTestTool/exe/command.cpp create mode 100644 spb/SpbTestTool/exe/command.h create mode 100644 spb/SpbTestTool/exe/internal.h create mode 100644 spb/SpbTestTool/exe/main.cpp create mode 100644 spb/SpbTestTool/exe/util.cpp create mode 100644 spb/SpbTestTool/sys/SpbTestTool.vcxproj create mode 100644 spb/SpbTestTool/sys/SpbTestTool.vcxproj.Filters create mode 100644 spb/SpbTestTool/sys/device.cpp create mode 100644 spb/SpbTestTool/sys/device.h create mode 100644 spb/SpbTestTool/sys/driver.cpp create mode 100644 spb/SpbTestTool/sys/driver.h create mode 100644 spb/SpbTestTool/sys/internal.h create mode 100644 spb/SpbTestTool/sys/peripheral.cpp create mode 100644 spb/SpbTestTool/sys/peripheral.h create mode 100644 spb/SpbTestTool/sys/resource.rc create mode 100644 spb/SpbTestTool/sys/spbtestioctl.h create mode 100644 spb/SpbTestTool/sys/spbtesttool.asl create mode 100644 spb/SpbTestTool/sys/spbtesttool.inx create mode 100644 spb/SpbTestTool/sys/trace.h create mode 100644 storage/class/classpnp/ReadMe.md create mode 100644 storage/class/classpnp/classpnp.sln create mode 100644 storage/class/classpnp/src/autorun.c create mode 100644 storage/class/classpnp/src/class.c create mode 100644 storage/class/classpnp/src/class.def create mode 100644 storage/class/classpnp/src/class.rc create mode 100644 storage/class/classpnp/src/classlog.mof create mode 100644 storage/class/classpnp/src/classp.h create mode 100644 storage/class/classpnp/src/classpnp.htm create mode 100644 storage/class/classpnp/src/classpnp.vcxproj create mode 100644 storage/class/classpnp/src/classpnp.vcxproj.Filters create mode 100644 storage/class/classpnp/src/classwmi.c create mode 100644 storage/class/classpnp/src/clntirp.c create mode 100644 storage/class/classpnp/src/create.c create mode 100644 storage/class/classpnp/src/data.c create mode 100644 storage/class/classpnp/src/debug.c create mode 100644 storage/class/classpnp/src/debug.h create mode 100644 storage/class/classpnp/src/dictlib.c create mode 100644 storage/class/classpnp/src/dispatch.c create mode 100644 storage/class/classpnp/src/history.c create mode 100644 storage/class/classpnp/src/lock.c create mode 100644 storage/class/classpnp/src/obsolete.c create mode 100644 storage/class/classpnp/src/power.c create mode 100644 storage/class/classpnp/src/retry.c create mode 100644 storage/class/classpnp/src/srblib.c create mode 100644 storage/class/classpnp/src/utils.c create mode 100644 storage/class/classpnp/src/xferpkt.c create mode 100644 storage/filters/addfilter/ReadMe.md create mode 100644 storage/filters/addfilter/addfilter.sln create mode 100644 storage/filters/addfilter/src/addfilter.c create mode 100644 storage/filters/addfilter/src/addfilter.rc create mode 100644 storage/filters/addfilter/src/addfilter.vcxproj create mode 100644 storage/filters/addfilter/src/addfilter.vcxproj.Filters create mode 100644 storage/filters/addfilter/src/addfiltr.htm create mode 100644 storage/iscsi/ReadMe.md create mode 100644 storage/iscsi/iscsi.sln create mode 100644 storage/iscsi/src/EmptyProject.vcxproj create mode 100644 storage/iscsi/src/EmptyProject.vcxproj.Filters create mode 100644 storage/iscsi/src/blockdir create mode 100644 storage/iscsi/src/client/wmisample.c create mode 100644 storage/iscsi/src/common.mof create mode 100644 storage/iscsi/src/config.mof create mode 100644 storage/iscsi/src/def.mof create mode 100644 storage/iscsi/src/iscsihba.mof create mode 100644 storage/iscsi/src/iscsiprf.mof create mode 100644 storage/iscsi/src/mgmt.mof create mode 100644 storage/iscsi/src/readme.txt create mode 100644 storage/miniports/lsi_u3/ReadMe.md create mode 100644 storage/miniports/lsi_u3/lsi_u3.sln create mode 100644 storage/miniports/lsi_u3/src/lsi_u3.c create mode 100644 storage/miniports/lsi_u3/src/lsi_u3.h create mode 100644 storage/miniports/lsi_u3/src/lsi_u3.inf create mode 100644 storage/miniports/lsi_u3/src/lsi_u3.rc create mode 100644 storage/miniports/lsi_u3/src/lsi_u3.vcxproj create mode 100644 storage/miniports/lsi_u3/src/lsi_u3.vcxproj.Filters create mode 100644 storage/miniports/lsi_u3/src/lsidmi.h create mode 100644 storage/miniports/lsi_u3/src/lsinvm.h create mode 100644 storage/miniports/lsi_u3/src/lsisiop.h create mode 100644 storage/miniports/lsi_u3/src/lsisvdt.h create mode 100644 storage/miniports/lsi_u3/src/lsiver.h create mode 100644 storage/miniports/lsi_u3/src/scr_u3m.h create mode 100644 storage/miniports/storahci/ReadMe.md create mode 100644 storage/miniports/storahci/src/ahci.h create mode 100644 storage/miniports/storahci/src/common.c create mode 100644 storage/miniports/storahci/src/common.h create mode 100644 storage/miniports/storahci/src/entrypts.c create mode 100644 storage/miniports/storahci/src/entrypts.h create mode 100644 storage/miniports/storahci/src/generic.h create mode 100644 storage/miniports/storahci/src/hbastat.c create mode 100644 storage/miniports/storahci/src/hbastat.h create mode 100644 storage/miniports/storahci/src/inbox/storahci.vcxproj create mode 100644 storage/miniports/storahci/src/inbox/storahci.vcxproj.Filters create mode 100644 storage/miniports/storahci/src/io.c create mode 100644 storage/miniports/storahci/src/io.h create mode 100644 storage/miniports/storahci/src/pnppower.c create mode 100644 storage/miniports/storahci/src/pnppower.h create mode 100644 storage/miniports/storahci/src/storahci.rc create mode 100644 storage/miniports/storahci/src/util.c create mode 100644 storage/miniports/storahci/src/util.h create mode 100644 storage/miniports/storahci/storahci.sln create mode 100644 storage/sfloppy/ReadMe.md create mode 100644 storage/sfloppy/sfloppy.sln create mode 100644 storage/sfloppy/src/floppy.c create mode 100644 storage/sfloppy/src/sfloppy.htm create mode 100644 storage/sfloppy/src/sfloppy.inf create mode 100644 storage/sfloppy/src/sfloppy.rc create mode 100644 storage/sfloppy/src/sfloppy.vcxproj create mode 100644 storage/sfloppy/src/sfloppy.vcxproj.Filters create mode 100644 storage/tools/spti/ReadMe.md create mode 100644 storage/tools/spti/spti.sln create mode 100644 storage/tools/spti/src/spti.c create mode 100644 storage/tools/spti/src/spti.h create mode 100644 storage/tools/spti/src/spti.htm create mode 100644 storage/tools/spti/src/spti.vcxproj create mode 100644 storage/tools/spti/src/spti.vcxproj.Filters create mode 100644 thermal/simsensor/ReadMe.md create mode 100644 thermal/simsensor/simsensor.c create mode 100644 thermal/simsensor/simsensor.h create mode 100644 thermal/simsensor/simsensor.inf create mode 100644 thermal/simsensor/simsensor.rc create mode 100644 thermal/simsensor/simsensor.sln create mode 100644 thermal/simsensor/simsensor.vcxproj create mode 100644 thermal/simsensor/simsensor.vcxproj.Filters create mode 100644 thermal/simsensor/thermalsample.asl create mode 100644 thermal/thermalclient/ReadMe.md create mode 100644 thermal/thermalclient/simtc.c create mode 100644 thermal/thermalclient/simtc.h create mode 100644 thermal/thermalclient/simtc.inf create mode 100644 thermal/thermalclient/simtc.rc create mode 100644 thermal/thermalclient/simtc.vcxproj create mode 100644 thermal/thermalclient/simtc.vcxproj.Filters create mode 100644 thermal/thermalclient/thermalclient.sln create mode 100644 thermal/thermalclient/thermalsample.asl create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/ReadMe.md create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/SDV-FailDriver-KMDF.sln create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.c create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.h create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.vcxproj create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.vcxproj.Filters create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/driver/sdv-user.sdv create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.c create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.h create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.vcxproj create mode 100644 tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.vcxproj.Filters create mode 100644 tools/sdv/samples/SDV-FailDriver-NDIS/ReadMe.md create mode 100644 tools/sdv/samples/SDV-FailDriver-NDIS/SDV-FailDriver-NDIS.sln create mode 100644 tools/sdv/samples/SDV-FailDriver-NDIS/driver/main.c create mode 100644 tools/sdv/samples/SDV-FailDriver-NDIS/driver/mp.h create mode 100644 tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdv-user.sdv create mode 100644 tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.rc create mode 100644 tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.vcxproj create mode 100644 tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.vcxproj.Filters create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/ReadMe.md create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/SDV-FailDriver-STORPORT.sln create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/MAKEFILE.ddk create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/SCR_U3M.H create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.c create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.h create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.rc create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj.Filters create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3_inf.txt create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsidmi.h create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsinvm.h create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsisiop.h create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsisvdt.h create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsiver.h create mode 100644 tools/sdv/samples/SDV-FailDriver-STORPORT/driver/sdv-user.sdv create mode 100644 tools/sdv/samples/SDV-FailDriver-WDM/ReadMe.md create mode 100644 tools/sdv/samples/SDV-FailDriver-WDM/SDV-FailDriver-WDM.sln create mode 100644 tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.c create mode 100644 tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.h create mode 100644 tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.vcxproj create mode 100644 tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.vcxproj.Filters create mode 100644 tools/sdv/samples/SDV-FailDriver-WDM/driver/sdv-user.sdv create mode 100644 usb/kmdf_enumswitches/ReadMe.md create mode 100644 usb/kmdf_enumswitches/inc/prototypes.h create mode 100644 usb/kmdf_enumswitches/inc/public.h create mode 100644 usb/kmdf_enumswitches/kmdf_enumswitches.sln create mode 100644 usb/kmdf_enumswitches/sys/Device.c create mode 100644 usb/kmdf_enumswitches/sys/driver.c create mode 100644 usb/kmdf_enumswitches/sys/interrupt.c create mode 100644 usb/kmdf_enumswitches/sys/kmdf_enumswitches.inx create mode 100644 usb/kmdf_enumswitches/sys/kmdf_enumswitches.vcxproj create mode 100644 usb/kmdf_enumswitches/sys/kmdf_enumswitches.vcxproj.Filters create mode 100644 usb/kmdf_enumswitches/sys/osrusbfx2.h create mode 100644 usb/kmdf_enumswitches/sys/rawpdo.c create mode 100644 usb/kmdf_enumswitches/sys/rawpdo.h create mode 100644 usb/kmdf_enumswitches/sys/trace.h create mode 100644 usb/umdf2_fx2/ReadMe.md create mode 100644 usb/umdf2_fx2/deviceMetadata/B4D697F5-1C56-4807-ACCD-B28C09D37FF0.devicemetadata-ms create mode 100644 usb/umdf2_fx2/driver/Device.c create mode 100644 usb/umdf2_fx2/driver/bulkrwr.c create mode 100644 usb/umdf2_fx2/driver/driver.c create mode 100644 usb/umdf2_fx2/driver/interrupt.c create mode 100644 usb/umdf2_fx2/driver/ioctl.c create mode 100644 usb/umdf2_fx2/driver/osrusbfx2.h create mode 100644 usb/umdf2_fx2/driver/osrusbfx2.man create mode 100644 usb/umdf2_fx2/driver/osrusbfx2.rc create mode 100644 usb/umdf2_fx2/driver/osrusbfx2um.inx create mode 100644 usb/umdf2_fx2/driver/osrusbfx2um.vcxproj create mode 100644 usb/umdf2_fx2/driver/osrusbfx2um.vcxproj.Filters create mode 100644 usb/umdf2_fx2/driver/trace.h create mode 100644 usb/umdf2_fx2/exe/dump.c create mode 100644 usb/umdf2_fx2/exe/osrusbfx2.vcxproj create mode 100644 usb/umdf2_fx2/exe/osrusbfx2.vcxproj.Filters create mode 100644 usb/umdf2_fx2/exe/test.cmd create mode 100644 usb/umdf2_fx2/exe/testapp.c create mode 100644 usb/umdf2_fx2/exe/testapp.rc create mode 100644 usb/umdf2_fx2/inc/prototypes.h create mode 100644 usb/umdf2_fx2/inc/public.h create mode 100644 usb/umdf2_fx2/umdf2_fx2.sln create mode 100644 usb/umdf_filter_kmdf/Package/package.VcxProj create mode 100644 usb/umdf_filter_kmdf/Package/package.VcxProj.Filters create mode 100644 usb/umdf_filter_kmdf/ReadMe.md create mode 100644 usb/umdf_filter_kmdf/inc/WUDFOsrUsbPublic.h create mode 100644 usb/umdf_filter_kmdf/inc/list.h create mode 100644 usb/umdf_filter_kmdf/inc/public.h create mode 100644 usb/umdf_filter_kmdf/inc/usb_hw.h create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/Device.c create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/bulkrwr.c create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/driver.c create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/interrupt.c create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/ioctl.c create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.h create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.man create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.rc create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.vcxproj create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.vcxproj.Filters create mode 100644 usb/umdf_filter_kmdf/kmdf_driver/trace.h create mode 100644 usb/umdf_filter_kmdf/umdf_filter/OsrUsbFilter.rc create mode 100644 usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilter.vcxproj create mode 100644 usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters create mode 100644 usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilterOnKmDriver.inx create mode 100644 usb/umdf_filter_kmdf/umdf_filter/comsup.cpp create mode 100644 usb/umdf_filter_kmdf/umdf_filter/comsup.h create mode 100644 usb/umdf_filter_kmdf/umdf_filter/device.cpp create mode 100644 usb/umdf_filter_kmdf/umdf_filter/device.h create mode 100644 usb/umdf_filter_kmdf/umdf_filter/dllsup.cpp create mode 100644 usb/umdf_filter_kmdf/umdf_filter/driver.cpp create mode 100644 usb/umdf_filter_kmdf/umdf_filter/driver.h create mode 100644 usb/umdf_filter_kmdf/umdf_filter/exports.def create mode 100644 usb/umdf_filter_kmdf/umdf_filter/internal.h create mode 100644 usb/umdf_filter_kmdf/umdf_filter/queue.cpp create mode 100644 usb/umdf_filter_kmdf/umdf_filter/queue.h create mode 100644 usb/umdf_filter_kmdf/umdf_filter_kmdf.sln create mode 100644 usb/umdf_filter_umdf/Package/package.VcxProj create mode 100644 usb/umdf_filter_umdf/Package/package.VcxProj.Filters create mode 100644 usb/umdf_filter_umdf/ReadMe.md create mode 100644 usb/umdf_filter_umdf/inc/WUDFOsrUsbPublic.h create mode 100644 usb/umdf_filter_umdf/inc/list.h create mode 100644 usb/umdf_filter_umdf/inc/public.h create mode 100644 usb/umdf_filter_umdf/inc/usb_hw.h create mode 100644 usb/umdf_filter_umdf/umdf_driver/ControlQueue.cpp create mode 100644 usb/umdf_filter_umdf/umdf_driver/ControlQueue.h create mode 100644 usb/umdf_filter_umdf/umdf_driver/Device.cpp create mode 100644 usb/umdf_filter_umdf/umdf_driver/Device.h create mode 100644 usb/umdf_filter_umdf/umdf_driver/Driver.cpp create mode 100644 usb/umdf_filter_umdf/umdf_driver/Driver.h create mode 100644 usb/umdf_filter_umdf/umdf_driver/OsrUsbFx2.ctl create mode 100644 usb/umdf_filter_umdf/umdf_driver/OsrUsbFx2.rc create mode 100644 usb/umdf_filter_umdf/umdf_driver/ReadWriteQueue.cpp create mode 100644 usb/umdf_filter_umdf/umdf_driver/ReadWriteQueue.h create mode 100644 usb/umdf_filter_umdf/umdf_driver/WUDFOsrUsbFx2.vcxproj create mode 100644 usb/umdf_filter_umdf/umdf_driver/WUDFOsrUsbFx2.vcxproj.Filters create mode 100644 usb/umdf_filter_umdf/umdf_driver/comsup.cpp create mode 100644 usb/umdf_filter_umdf/umdf_driver/comsup.h create mode 100644 usb/umdf_filter_umdf/umdf_driver/dllsup.cpp create mode 100644 usb/umdf_filter_umdf/umdf_driver/exports.def create mode 100644 usb/umdf_filter_umdf/umdf_driver/internal.h create mode 100644 usb/umdf_filter_umdf/umdf_driver/queue.cpp create mode 100644 usb/umdf_filter_umdf/umdf_driver/queue.h create mode 100644 usb/umdf_filter_umdf/umdf_filter/OsrUsbFilter.rc create mode 100644 usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilter.vcxproj create mode 100644 usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters create mode 100644 usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilterOnUmFx2Driver.inx create mode 100644 usb/umdf_filter_umdf/umdf_filter/comsup.cpp create mode 100644 usb/umdf_filter_umdf/umdf_filter/comsup.h create mode 100644 usb/umdf_filter_umdf/umdf_filter/device.cpp create mode 100644 usb/umdf_filter_umdf/umdf_filter/device.h create mode 100644 usb/umdf_filter_umdf/umdf_filter/dllsup.cpp create mode 100644 usb/umdf_filter_umdf/umdf_filter/driver.cpp create mode 100644 usb/umdf_filter_umdf/umdf_filter/driver.h create mode 100644 usb/umdf_filter_umdf/umdf_filter/exports.def create mode 100644 usb/umdf_filter_umdf/umdf_filter/internal.h create mode 100644 usb/umdf_filter_umdf/umdf_filter/queue.cpp create mode 100644 usb/umdf_filter_umdf/umdf_filter/queue.h create mode 100644 usb/umdf_filter_umdf/umdf_filter_umdf.sln create mode 100644 usb/usbview/ReadMe.md create mode 100644 usb/usbview/app.config create mode 100644 usb/usbview/bang.ico create mode 100644 usb/usbview/codeanalysis.h create mode 100644 usb/usbview/debug.c create mode 100644 usb/usbview/devnode.c create mode 100644 usb/usbview/dispaud.c create mode 100644 usb/usbview/display.c create mode 100644 usb/usbview/dispvid.c create mode 100644 usb/usbview/enum.c create mode 100644 usb/usbview/h264.c create mode 100644 usb/usbview/h264.h create mode 100644 usb/usbview/hub.ico create mode 100644 usb/usbview/langidlist.h create mode 100644 usb/usbview/monitor.ico create mode 100644 usb/usbview/port.ico create mode 100644 usb/usbview/resource.h create mode 100644 usb/usbview/split.cur create mode 100644 usb/usbview/ssport.ico create mode 100644 usb/usbview/ssusb.ico create mode 100644 usb/usbview/usb.ico create mode 100644 usb/usbview/usbdesc.h create mode 100644 usb/usbview/usbschema.hpp create mode 100644 usb/usbview/usbviddesc.h create mode 100644 usb/usbview/usbview.sln create mode 100644 usb/usbview/usbview.vcxproj create mode 100644 usb/usbview/usbview.vcxproj.Filters create mode 100644 usb/usbview/uvcdesc.h create mode 100644 usb/usbview/uvcview.c create mode 100644 usb/usbview/uvcview.h create mode 100644 usb/usbview/uvcview.rc create mode 100644 usb/usbview/vndrlist.h create mode 100644 usb/usbview/xmlhelper.cpp create mode 100644 usb/usbview/xmlhelper.h create mode 100644 video/KMDOD/KMDOD.sln create mode 100644 video/KMDOD/ReadMe.md create mode 100644 video/KMDOD/Sample/SampleDisplay.vcxproj create mode 100644 video/KMDOD/Sample/SampleDisplay.vcxproj.Filters create mode 100644 video/KMDOD/Sample/sampledisplay.inf create mode 100644 video/KMDOD/bdd.cxx create mode 100644 video/KMDOD/bdd.hxx create mode 100644 video/KMDOD/bdd_ddi.cxx create mode 100644 video/KMDOD/bdd_dmm.cxx create mode 100644 video/KMDOD/bdd_errorlog.hxx create mode 100644 video/KMDOD/bdd_util.cxx create mode 100644 video/KMDOD/bltfuncs.cxx create mode 100644 video/KMDOD/blthw.cxx create mode 100644 video/KMDOD/memory.cxx create mode 100644 video/KMDOD/sampledisplay.rc create mode 100644 video/pixlib/PixLib.vcxproj create mode 100644 video/pixlib/PixLib.vcxproj.Filters create mode 100644 video/pixlib/ReadMe.md create mode 100644 video/pixlib/pixel.cpp create mode 100644 video/pixlib/pixel.hpp create mode 100644 video/pixlib/pixlib.cpp create mode 100644 video/pixlib/pixlib.sln create mode 100644 wmi/wmiacpi/ReadMe.md create mode 100644 wmi/wmiacpi/wmiacpi.sln create mode 100644 wmi/wmiacpi/wmiacpi/acpimof.def create mode 100644 wmi/wmiacpi/wmiacpi/acpimof.mof create mode 100644 wmi/wmiacpi/wmiacpi/acpimof.rc create mode 100644 wmi/wmiacpi/wmiacpi/acpimof.vcxproj create mode 100644 wmi/wmiacpi/wmiacpi/acpimof.vcxproj.Filters create mode 100644 wmi/wmiacpi/wmiacpi/device.asl create mode 100644 wmi/wmisamp/ReadMe.md create mode 100644 wmi/wmisamp/WmiHandler.c create mode 100644 wmi/wmisamp/WmiSamp.rc create mode 100644 wmi/wmisamp/WmiSamp.vcxproj create mode 100644 wmi/wmisamp/WmiSamp.vcxproj.Filters create mode 100644 wmi/wmisamp/wmisamp.c create mode 100644 wmi/wmisamp/wmisamp.h create mode 100644 wmi/wmisamp/wmisamp.inx create mode 100644 wmi/wmisamp/wmisamp.mof create mode 100644 wmi/wmisamp/wmisamp.sln create mode 100644 wpd/WpdBasicHardwareDriver/Device.cpp create mode 100644 wpd/WpdBasicHardwareDriver/Device.h create mode 100644 wpd/WpdBasicHardwareDriver/Driver.cpp create mode 100644 wpd/WpdBasicHardwareDriver/Driver.h create mode 100644 wpd/WpdBasicHardwareDriver/Queue.cpp create mode 100644 wpd/WpdBasicHardwareDriver/Queue.h create mode 100644 wpd/WpdBasicHardwareDriver/RS232Connection.cpp create mode 100644 wpd/WpdBasicHardwareDriver/RS232Connection.h create mode 100644 wpd/WpdBasicHardwareDriver/RS232Target.cpp create mode 100644 wpd/WpdBasicHardwareDriver/RS232Target.h create mode 100644 wpd/WpdBasicHardwareDriver/ReadMe.md create mode 100644 wpd/WpdBasicHardwareDriver/Stdafxsrc.cpp create mode 100644 wpd/WpdBasicHardwareDriver/WpdBaseDriver.cpp create mode 100644 wpd/WpdBasicHardwareDriver/WpdBaseDriver.h create mode 100644 wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.cpp create mode 100644 wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.def create mode 100644 wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.idl create mode 100644 wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.inx create mode 100644 wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.rc create mode 100644 wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.rgs create mode 100644 wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.sln create mode 100644 wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.vcxproj create mode 100644 wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.vcxproj.Filters create mode 100644 wpd/WpdBasicHardwareDriver/WpdCapabilities.cpp create mode 100644 wpd/WpdBasicHardwareDriver/WpdCapabilities.h create mode 100644 wpd/WpdBasicHardwareDriver/WpdObjectEnum.cpp create mode 100644 wpd/WpdBasicHardwareDriver/WpdObjectEnum.h create mode 100644 wpd/WpdBasicHardwareDriver/WpdObjectProperties.cpp create mode 100644 wpd/WpdBasicHardwareDriver/WpdObjectProperties.h create mode 100644 wpd/WpdBasicHardwareDriver/firmware/compass_wpd_enabled.bs2 create mode 100644 wpd/WpdBasicHardwareDriver/firmware/flex_force_wpd_enabled.bs2 create mode 100644 wpd/WpdBasicHardwareDriver/firmware/h48c_3-axis_wpd_enabled.bs2 create mode 100644 wpd/WpdBasicHardwareDriver/firmware/memsic2125_wpd_enabled.bs2 create mode 100644 wpd/WpdBasicHardwareDriver/firmware/piezo_wpd_enabled.bs2 create mode 100644 wpd/WpdBasicHardwareDriver/firmware/ping_wpd_enabled.bs2 create mode 100644 wpd/WpdBasicHardwareDriver/firmware/pir_wpd_enabled.bs2 create mode 100644 wpd/WpdBasicHardwareDriver/firmware/qti_wpd_enabled.bs2 create mode 100644 wpd/WpdBasicHardwareDriver/firmware/temp_humidity_wpd_enabled.bs2 create mode 100644 wpd/WpdBasicHardwareDriver/resource.h create mode 100644 wpd/WpdBasicHardwareDriver/stdafx.h create mode 100644 wpd/WpdHelloWorldDriver/Device.cpp create mode 100644 wpd/WpdHelloWorldDriver/Device.h create mode 100644 wpd/WpdHelloWorldDriver/Driver.cpp create mode 100644 wpd/WpdHelloWorldDriver/Driver.h create mode 100644 wpd/WpdHelloWorldDriver/Queue.cpp create mode 100644 wpd/WpdHelloWorldDriver/Queue.h create mode 100644 wpd/WpdHelloWorldDriver/ReadMe.md create mode 100644 wpd/WpdHelloWorldDriver/Stdafxsrc.cpp create mode 100644 wpd/WpdHelloWorldDriver/WpdBaseDriver.cpp create mode 100644 wpd/WpdHelloWorldDriver/WpdBaseDriver.h create mode 100644 wpd/WpdHelloWorldDriver/WpdCapabilities.cpp create mode 100644 wpd/WpdHelloWorldDriver/WpdCapabilities.h create mode 100644 wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.cpp create mode 100644 wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.def create mode 100644 wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.idl create mode 100644 wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.inx create mode 100644 wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.rc create mode 100644 wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.rgs create mode 100644 wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.sln create mode 100644 wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.vcxproj create mode 100644 wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.vcxproj.Filters create mode 100644 wpd/WpdHelloWorldDriver/WpdObjectEnum.cpp create mode 100644 wpd/WpdHelloWorldDriver/WpdObjectEnum.h create mode 100644 wpd/WpdHelloWorldDriver/WpdObjectProperties.cpp create mode 100644 wpd/WpdHelloWorldDriver/WpdObjectProperties.h create mode 100644 wpd/WpdHelloWorldDriver/WpdObjectResources.cpp create mode 100644 wpd/WpdHelloWorldDriver/WpdObjectResources.h create mode 100644 wpd/WpdHelloWorldDriver/resource.h create mode 100644 wpd/WpdHelloWorldDriver/stdafx.h create mode 100644 wpd/WpdMultiTransportDriver/Device.cpp create mode 100644 wpd/WpdMultiTransportDriver/Device.h create mode 100644 wpd/WpdMultiTransportDriver/Driver.cpp create mode 100644 wpd/WpdMultiTransportDriver/Driver.h create mode 100644 wpd/WpdMultiTransportDriver/Queue.cpp create mode 100644 wpd/WpdMultiTransportDriver/Queue.h create mode 100644 wpd/WpdMultiTransportDriver/ReadMe.md create mode 100644 wpd/WpdMultiTransportDriver/Stdafxsrc.cpp create mode 100644 wpd/WpdMultiTransportDriver/WpdBaseDriver.cpp create mode 100644 wpd/WpdMultiTransportDriver/WpdBaseDriver.h create mode 100644 wpd/WpdMultiTransportDriver/WpdCapabilities.cpp create mode 100644 wpd/WpdMultiTransportDriver/WpdCapabilities.h create mode 100644 wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.cpp create mode 100644 wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.def create mode 100644 wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.idl create mode 100644 wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.inx create mode 100644 wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.rc create mode 100644 wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.rgs create mode 100644 wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.sln create mode 100644 wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj create mode 100644 wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj.Filters create mode 100644 wpd/WpdMultiTransportDriver/WpdObjectEnum.cpp create mode 100644 wpd/WpdMultiTransportDriver/WpdObjectEnum.h create mode 100644 wpd/WpdMultiTransportDriver/WpdObjectProperties.cpp create mode 100644 wpd/WpdMultiTransportDriver/WpdObjectProperties.h create mode 100644 wpd/WpdMultiTransportDriver/WpdObjectResources.cpp create mode 100644 wpd/WpdMultiTransportDriver/WpdObjectResources.h create mode 100644 wpd/WpdMultiTransportDriver/resource.h create mode 100644 wpd/WpdMultiTransportDriver/stdafx.h create mode 100644 wpd/WpdServiceSampleDriver/Device.cpp create mode 100644 wpd/WpdServiceSampleDriver/Device.h create mode 100644 wpd/WpdServiceSampleDriver/Driver.cpp create mode 100644 wpd/WpdServiceSampleDriver/Driver.h create mode 100644 wpd/WpdServiceSampleDriver/FakeContactContent.cpp create mode 100644 wpd/WpdServiceSampleDriver/FakeContactContent.h create mode 100644 wpd/WpdServiceSampleDriver/FakeContactsService.cpp create mode 100644 wpd/WpdServiceSampleDriver/FakeContactsService.h create mode 100644 wpd/WpdServiceSampleDriver/FakeContactsServiceContent.cpp create mode 100644 wpd/WpdServiceSampleDriver/FakeContactsServiceContent.h create mode 100644 wpd/WpdServiceSampleDriver/FakeContent.cpp create mode 100644 wpd/WpdServiceSampleDriver/FakeContent.h create mode 100644 wpd/WpdServiceSampleDriver/FakeDevice.cpp create mode 100644 wpd/WpdServiceSampleDriver/FakeDevice.h create mode 100644 wpd/WpdServiceSampleDriver/FakeDeviceContent.cpp create mode 100644 wpd/WpdServiceSampleDriver/FakeDeviceContent.h create mode 100644 wpd/WpdServiceSampleDriver/FakeStorage.cpp create mode 100644 wpd/WpdServiceSampleDriver/FakeStorage.h create mode 100644 wpd/WpdServiceSampleDriver/Queue.cpp create mode 100644 wpd/WpdServiceSampleDriver/Queue.h create mode 100644 wpd/WpdServiceSampleDriver/ReadMe.md create mode 100644 wpd/WpdServiceSampleDriver/SampleContactsServiceIcon.ico create mode 100644 wpd/WpdServiceSampleDriver/SampleDeviceIcon.ico create mode 100644 wpd/WpdServiceSampleDriver/Stdafxsrc.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdBaseDriver.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdBaseDriver.h create mode 100644 wpd/WpdServiceSampleDriver/WpdCapabilities.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdCapabilities.h create mode 100644 wpd/WpdServiceSampleDriver/WpdObjectEnum.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdObjectEnum.h create mode 100644 wpd/WpdServiceSampleDriver/WpdObjectManagement.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdObjectManagement.h create mode 100644 wpd/WpdServiceSampleDriver/WpdObjectProperties.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdObjectProperties.h create mode 100644 wpd/WpdServiceSampleDriver/WpdObjectPropertiesBulk.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdObjectPropertiesBulk.h create mode 100644 wpd/WpdServiceSampleDriver/WpdObjectResources.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdObjectResources.h create mode 100644 wpd/WpdServiceSampleDriver/WpdService.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdService.h create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceCapabilities.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceCapabilities.h create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceMethods.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceMethods.h create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.cpp create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.def create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.idl create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.inx create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.rc create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.rgs create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.sln create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj create mode 100644 wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj.Filters create mode 100644 wpd/WpdServiceSampleDriver/helpers.cpp create mode 100644 wpd/WpdServiceSampleDriver/helpers.h create mode 100644 wpd/WpdServiceSampleDriver/resource.h create mode 100644 wpd/WpdServiceSampleDriver/stdafx.h create mode 100644 wpd/WpdWudfSampleDriver/ContextMap.h create mode 100644 wpd/WpdWudfSampleDriver/Device.cpp create mode 100644 wpd/WpdWudfSampleDriver/Device.h create mode 100644 wpd/WpdWudfSampleDriver/DeviceObjectFakeContent.h create mode 100644 wpd/WpdWudfSampleDriver/Driver.cpp create mode 100644 wpd/WpdWudfSampleDriver/Driver.h create mode 100644 wpd/WpdWudfSampleDriver/FakeContactContent.h create mode 100644 wpd/WpdWudfSampleDriver/FakeContent.h create mode 100644 wpd/WpdWudfSampleDriver/FakeDevice.h create mode 100644 wpd/WpdWudfSampleDriver/FakeFolderContent.h create mode 100644 wpd/WpdWudfSampleDriver/FakeImageContent.h create mode 100644 wpd/WpdWudfSampleDriver/FakeMemoContent.h create mode 100644 wpd/WpdWudfSampleDriver/FakeMusicContent.h create mode 100644 wpd/WpdWudfSampleDriver/FakeVideoContent.h create mode 100644 wpd/WpdWudfSampleDriver/NetworkConfigFakeContent.h create mode 100644 wpd/WpdWudfSampleDriver/Queue.cpp create mode 100644 wpd/WpdWudfSampleDriver/Queue.h create mode 100644 wpd/WpdWudfSampleDriver/ReadMe.md create mode 100644 wpd/WpdWudfSampleDriver/RenderingInformationFakeContent.h create mode 100644 wpd/WpdWudfSampleDriver/SampleAudioAnnotation.wav create mode 100644 wpd/WpdWudfSampleDriver/SampleContactPhoto.png create mode 100644 wpd/WpdWudfSampleDriver/SampleDeviceIcon.ico create mode 100644 wpd/WpdWudfSampleDriver/SampleExternalStorageIcon.ico create mode 100644 wpd/WpdWudfSampleDriver/SampleImage.jpg create mode 100644 wpd/WpdWudfSampleDriver/SampleImageThumbnail.jpg create mode 100644 wpd/WpdWudfSampleDriver/SampleInternalStorageIcon.ico create mode 100644 wpd/WpdWudfSampleDriver/SampleMemo.txt create mode 100644 wpd/WpdWudfSampleDriver/SampleMemoFolderIcon.ico create mode 100644 wpd/WpdWudfSampleDriver/SampleMemoIcon.ico create mode 100644 wpd/WpdWudfSampleDriver/SampleMusic.wma create mode 100644 wpd/WpdWudfSampleDriver/SampleVideo.wmv create mode 100644 wpd/WpdWudfSampleDriver/Stdafxsrc.cpp create mode 100644 wpd/WpdWudfSampleDriver/StorageObjectFakeContent.h create mode 100644 wpd/WpdWudfSampleDriver/WpdBaseDriver.cpp create mode 100644 wpd/WpdWudfSampleDriver/WpdBaseDriver.h create mode 100644 wpd/WpdWudfSampleDriver/WpdCapabilities.cpp create mode 100644 wpd/WpdWudfSampleDriver/WpdCapabilities.h create mode 100644 wpd/WpdWudfSampleDriver/WpdNetworkConfig.cpp create mode 100644 wpd/WpdWudfSampleDriver/WpdNetworkConfig.h create mode 100644 wpd/WpdWudfSampleDriver/WpdObjectEnum.cpp create mode 100644 wpd/WpdWudfSampleDriver/WpdObjectEnum.h create mode 100644 wpd/WpdWudfSampleDriver/WpdObjectManagement.cpp create mode 100644 wpd/WpdWudfSampleDriver/WpdObjectManagement.h create mode 100644 wpd/WpdWudfSampleDriver/WpdObjectProperties.cpp create mode 100644 wpd/WpdWudfSampleDriver/WpdObjectProperties.h create mode 100644 wpd/WpdWudfSampleDriver/WpdObjectPropertiesBulk.cpp create mode 100644 wpd/WpdWudfSampleDriver/WpdObjectPropertiesBulk.h create mode 100644 wpd/WpdWudfSampleDriver/WpdObjectResources.cpp create mode 100644 wpd/WpdWudfSampleDriver/WpdObjectResources.h create mode 100644 wpd/WpdWudfSampleDriver/WpdStorage.cpp create mode 100644 wpd/WpdWudfSampleDriver/WpdStorage.h create mode 100644 wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.cpp create mode 100644 wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.def create mode 100644 wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.idl create mode 100644 wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.inx create mode 100644 wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rc create mode 100644 wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rgs create mode 100644 wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.sln create mode 100644 wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj create mode 100644 wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj.Filters create mode 100644 wpd/WpdWudfSampleDriver/helpers.cpp create mode 100644 wpd/WpdWudfSampleDriver/helpers.h create mode 100644 wpd/WpdWudfSampleDriver/resource.h create mode 100644 wpd/WpdWudfSampleDriver/stdafx.h diff --git a/README.md b/README.md index c0aa44cb..fc7cdbe6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,36 @@ -# Windows-driver-samples -This repo contains driver samples prepared for use with Microsoft Visual Studio and the Windows Driver Kit (WDK). It contains both universal driver and desktop-only driver samples. +# Driver samples for Windows 10 # +These are the official Microsoft Windows Driver Kit (WDK) team driver code samples for Windows 10. They provide a foundation for universal driver support of all hardware form factors, from phones to desktop PCs. Use these samples with Microsoft Visual Studio 2015 CTP 6, Microsoft Visual Studio Tools for Windows 10 Technical Preview, Windows SDK for Windows 10, and WDK 10. + +## Windows 10 driver development ## +Use Visual Studio 2015 CTP 6 and the the Windows 10 Driver Kit (WDK) to build, test, and deploy your drivers. With Windows 10, the driver development environment is integrated into Visual Studio. To get Windows 10 Technical Preview and the driver development kits and tools, join the Windows Insider Program. + +[Become a Windows Insider](https://insider.windows.com/ "Become a Windows Insider") + +### Windows 10 Driver Kit (WDK) ### +Take a look at the compilation of the new and changed driver-related content for Windows 10. Areas of improvement include camera, print, display, Near Field Communication (NFC), WLAN, Bluetooth, and more. + +[Find out what’s new in the WDK](http://go.microsoft.com/fwlink/?LinkId=528349 "Find out what’s new in the WDK") + +### Universal drivers in Windows 10 ### +Write one driver that runs on both Windows 10 for desktop editions and Windows 10 for mobile devices, as well as other Windows 10 editions that share a common set of interfaces. + +[Build your universal drivers](http://go.microsoft.com/fwlink/p/?LinkId=524488 "Build your universal drivers") + +### Windows Driver Frameworks ### +The Windows Driver Frameworks (WDF) are a set of libraries that make it simple to write high-quality device drivers. + +[WDF driver development guide](http://go.microsoft.com/fwlink/p/?LinkId=524489 "WDF driver development guide") + +### Samples ### +Use the samples in this repo to guide your Windows driver development. Whether you’re just getting started or porting an older driver to the newest version of Windows, code samples are valuable guides on how to write drivers. + +### Build your first driver ### +If you're writing your first driver, use these exercises to get started. Each exercise is independent of the others, so you can do them in any order. + +[Write a UMDF driver based on a template](http://go.microsoft.com/fwlink/p/?LinkId=524492 "Write a UMDF driver based on a template") + +[Write a KMDF Hello World driver](http://go.microsoft.com/fwlink/p/?LinkId=524493 "Write a KMDF Hello World driver") + +[Write a KMDF driver based on a template](http://go.microsoft.com/fwlink/p/?LinkId=524494 "Write a KMDF driver based on a template") + + diff --git a/avstream/avshws/ReadMe.md b/avstream/avshws/ReadMe.md new file mode 100644 index 00000000..be81c949 --- /dev/null +++ b/avstream/avshws/ReadMe.md @@ -0,0 +1,10 @@ +AVStream simulated hardware sample driver (Avshws) +================================================== + +The AVStream simulated hardware sample driver (Avshws) provides a pin-centric [AVStream](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554240) capture driver for a simulated piece of hardware. This streaming media driver performs video captures at 320 x 240 pixels in either RGB24 or YUV422 format using direct memory access (DMA) into capture buffers. The purpose of the sample is to demonstrate how to write a pin-centric AVStream minidriver. The sample also shows how to implement DMA by using the related functionality provided by the AVStream class driver. + +This sample features enhanced parameter validation and overflow detection. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + diff --git a/avstream/avshws/avshws.h b/avstream/avshws/avshws.h new file mode 100644 index 00000000..3647c1ac --- /dev/null +++ b/avstream/avshws/avshws.h @@ -0,0 +1,232 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + avshws.h + + Abstract: + + AVStream Simulated Hardware Sample header file. This is the + main header. + + History: + + created 3/12/2001 + +**************************************************************************/ + +/************************************************* + + Standard Includes + +*************************************************/ + +#ifndef _avshws_h_ +#define _avshws_h_ + +extern "C" { +#include +} + +#include +#include +#include +#include +#include +#define NOBITMAP +#include +#undef NOBITMAP +#include +#include +#include +#include + +/************************************************* + + Misc Definitions + +*************************************************/ +#pragma warning (disable : 4100 4127 4131 4189 4701 4706) +#define STR_MODULENAME "avshws: " +#define DEBUGLVL_VERBOSE 2 +#define DEBUGLVL_TERSE 1 +#define DEBUGLVL_ERROR 0 + +const DebugLevel = DEBUGLVL_TERSE; + +#if (DBG) +#define _DbgPrintF(lvl, strings) \ +{ \ + if (lvl <= DebugLevel) {\ + DbgPrint(STR_MODULENAME);\ + DbgPrint##strings;\ + DbgPrint("\n");\ + if ((lvl) == DEBUGLVL_ERROR) {\ + NT_ASSERT(0);\ + } \ + }\ +} +#else // !DBG + #define _DbgPrintF(lvl, strings) +#endif // !DBG + +#define ABS(x) ((x) < 0 ? (-(x)) : (x)) + +#ifndef mmioFOURCC +#define mmioFOURCC( ch0, ch1, ch2, ch3 ) \ + ( (DWORD)(BYTE)(ch0) | ( (DWORD)(BYTE)(ch1) << 8 ) | \ + ( (DWORD)(BYTE)(ch2) << 16 ) | ( (DWORD)(BYTE)(ch3) << 24 ) ) +#endif + +#define FOURCC_YUY2 mmioFOURCC('Y', 'U', 'Y', '2') +// +// CAPTURE_PIN_DATA_RANGE_COUNT: +// +// The number of ranges supported on the capture pin. +// +#define CAPTURE_PIN_DATA_RANGE_COUNT 2 + +// +// CAPTURE_FILTER_PIN_COUNT: +// +// The number of pins on the capture filter. +// +#define CAPTURE_FILTER_PIN_COUNT 1 + +// +// CAPTURE_FILTER_CATEGORIES_COUNT: +// +// The number of categories for the capture filter. +// +#define CAPTURE_FILTER_CATEGORIES_COUNT 3 + +#define AVSHWS_POOLTAG 'hSVA' + +/************************************************* + + Externed information + +*************************************************/ +// +// filter.cpp externs: +// +extern +const +KSFILTER_DISPATCH +CaptureFilterDispatch; + +extern +const +KSFILTER_DESCRIPTOR +CaptureFilterDescriptor; + +extern +const +KSPIN_DESCRIPTOR_EX +CaptureFilterPinDescriptors [CAPTURE_FILTER_PIN_COUNT]; + +extern +const +GUID +CaptureFilterCategories [CAPTURE_FILTER_CATEGORIES_COUNT]; + +// +// capture.cpp externs: +// +extern +const +KSALLOCATOR_FRAMING_EX +CapturePinAllocatorFraming; + +extern +const +KSPIN_DISPATCH +CapturePinDispatch; + +extern +const +PKSDATARANGE +CapturePinDataRanges [CAPTURE_PIN_DATA_RANGE_COUNT]; + +/************************************************* + + Enums / Typedefs + +*************************************************/ + +typedef enum _HARDWARE_STATE { + + HardwareStopped = 0, + HardwarePaused, + HardwareRunning + +} HARDWARE_STATE, *PHARDWARE_STATE; + +/************************************************* + + Class Definitions + +*************************************************/ + +// +// IHardwareSink: +// +// This interface is used by the hardware simulation to fake interrupt +// service routines. The Interrupt method is called at DPC as a fake +// interrupt. +// +class IHardwareSink { + +public: + + virtual + void + Interrupt ( + ) = 0; + +}; + +// +// ICaptureSink: +// +// This is a capture sink interface. The device level calls back the +// CompleteMappings method passing the number of completed mappings for +// the capture pin. This method is called during the device DPC. +// +class ICaptureSink { + +public: + + virtual + void + CompleteMappings ( + IN ULONG NumMappings + ) = 0; + +}; + + + +/************************************************* + + Global Functions + +*************************************************/ + +/************************************************* + + Internal Includes + +*************************************************/ + +#include "image.h" +#include "hwsim.h" +#include "device.h" +#include "filter.h" +#include "capture.h" +#include +#endif //_avshws_h_ \ No newline at end of file diff --git a/avstream/avshws/avshws.htm b/avstream/avshws/avshws.htm new file mode 100644 index 00000000..bd715e2c --- /dev/null +++ b/avstream/avshws/avshws.htm @@ -0,0 +1,458 @@ + + + + + + + + +AVSHwS + + + + + + + +
+ +

AVSHwS: AVStream Simulated +Hardware Sample Driver

+ +

SUMMARY

+ +

The AVSHwS sample provides a pin-centric AVStream capture driver for a simulated piece of hardware. The +driver performs captures at 320x240 in either an RGB24 or YUV422 format via +direct DMA into capture buffers. The purpose of the sample is to demonstrate +how to write a pin-centric AVStream minidriver. The sample also shows how to implement DMA by +using the related functionality provided by AVStream. 

+ +

This sample features enhanced +parameter validation and overflow detection.

+ + + +

BUILDING THE SAMPLE

+ +

Build the sample by typing build -cez in either the standard +checked or free WDK build environment. A successful build produces AVSHwS.sys.

+ +

The sample works on +32-bit x86 and 64-bit amd64 platforms running Vista Beta 2 or higher, Windows XP +SP2, Windows Server 2003 SP1 and Windows 2000 (If DirectX 8 or higher is +installed on the system)

+ +

Installation +instructions:

+ +
    +
  • Copy AVSHwS.sys and AVSHwS.INF to a directory
  • +
  • Open “Add Hardware” wizard from Control Panel
  • +
  • Select "Install the hardware that I manually + select from a list (Advanced)"
  • +
  • When you see "What type of hardware do you want + to install", select Sound, Video, and Game Controllers
  • +
  • Select "Have Disk..."
  • +
  • Browse to " AVSHwS.INF "
  • +
  • Follow the on screen instructions
  • +
+ +

PROGRAMMING TOUR

+ +

DriverEntry in device.cpp +is the initial point of entry. This routine passes control to AVStream through a call to KsInitializeDriver. +In this call, the minidriver passes the device +descriptor, an AVStream structure that recursively +defines the AVStream object hierarchy for a driver. +This is common behavior for an AVStream minidriver.

+ +

At device start time (see +the CcaptureDevice::PnpStart method in device.cpp), a simulated piece of capture hardware +is created (the ChardwareSimulation class), a DMA adapter is acquired from the operating system and is +registered with AVStream through a call to KsDeviceRegisterAdapterObject. This call is required +for a sample that performs DMA directly into the capture buffers, instead of +using DMA to write to a common buffer. In PnPStart +the driver creates the KS Filter for this device dynamically by calling KsCreateFilterFactory.

+ +

Filter.cpp is where the sample lays out the +KSPIN_DESCRIPTOR_EX structure for the single capture pin. In addition, a +KSFILTER_DISPATCH structure and a KSFILTER_DESCRIPTOR structure are provided in +this source file. The filter dispatch provides only a create dispatch, a +routine that is included in Filter.cpp. The +process dispatch is provided on the pin, since this is a pin-centric sample.

+ +

Capture.cpp contains source for the video +capture pin on the capture filter. This is where the KSPIN_DISPATCH structure +for the unique pin is provided. This dispatch structure specifies a Process +callback routine, also defined in this source file. This routine is where +stream pointer manipulation and cloning occurs.

+ +

The process callback is +one of two routines of interest in Capture.cpp +that demonstrate how to do DMA transfers using AVStream +functionality. The other is CCapturePin::CompleteMappings. +These two methods show how to use the queue, obtain clone pointers, use +scatter/gather lists, and perform other DMA-related tasks.

+ +

Hwsim.cpp contains the hardware simulation +code and also code that fills the scatter/gather mappings. This source file +includes the Start, Pause and Stop methods for the hardware simulation class (CHardwaresimulation). Image synthesis and overlay code is +also here. The supplied objects provide image synthesis (pixel, color-bar, +etc...) to RGB24 and UYVY buffers as well as software string overlay into these +buffers. The Image.cpp file, including data, +must exist in locked segments.

+ +

See comments in all .cpp files. Also see complete AVStream +documentation in the WDK documentation.

+ +

RUNNING THE SAMPLE

+ +

Once +installation is complete, access the driver through the graphedt +tool. Graphedt.exe +is available in the Tools directory of the WDK. In the Graphedt application, click the Graph menu and select +Insert Filters. The sample appears under "WDM Streaming Capture +Devices" as "avshws Source." Click +Insert Filter and the sample appears in the graph as a single filter labelled as ”avshws +Source”. There is one output pin which is the video capture pin. It sends out +video in YUY2 format. Attach this filter to either a DirectShow Video Renderer or the VMR default video renderer +and click Play.

+ +

The output produced by +the sample is a 320x240 image of standard EIA-189-A color bars. In the middle +of the image near the bottom, a clock appears over the image. This clock +displays the elapsed time since the graph was introduced into the run state +following the last stop. The clock shows MINUTES:SECONDS.HUNDREDTHS. +

+ +

In the top left corner of +the image, a counter counts the number of frames that have been dropped since +the graph was introduced into the run state after the last stop.

+ +

COMMENTS

+ +

For more +information on AVStream, see the WDK documentation.

+ +

CODE TOUR

+ +

File +Manifest

+ +
File           Description
 
AVSHwS.htm     The Sample Tour documentation for this sample (this file).
Sources        The generic file for building the code sample.
AVSHwS.inf     A sample installation file.
AVSHwS.h       The main header file for the sample.
device.cpp     DriverEntry, Plug and Play handling, initialization, device level code.
device.h       Header file for above.
filter.cpp     Filter level code for the capture filter.
filter.h       Header file for above.
capture.cpp    Pin level code for the capture pin, DMA handling.
capture.h      Header file for above.
hwsim.cpp      Hardware simulation code, filling scatter/gather mappings, etc.
hwsim.h        Header file for above.
image.cpp      RGB24 and UYVY image synthesis and overlay code.
image.h        Header file for above.
purecall.c     Purecall stub for virtual function usage
AVSHwS.rc      Resource file mainly for version.
 
 
+ + + + + +
+

 

+
+ +
 
+ +

© +2004- 2006 Microsoft Corporation +

+ +
 
+ +

 

+ +
+ + + + diff --git a/avstream/avshws/avshws.inf b/avstream/avshws/avshws.inf new file mode 100644 index 00000000..5bf66468 --- /dev/null +++ b/avstream/avshws/avshws.inf @@ -0,0 +1,172 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +; KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR +; PURPOSE. +; +;Module Name: +; +; AVSHWS.INF +; +;Abstract: +; +; +;AVStream pin centric sample mini driver (AVSHWS.sys) installation file. +;Supports x86, amd64, arm and arm64 platforms +; +;--*/ + +[Version] +signature="$WINDOWS NT$" +Class=Media +ClassGUID={4d36e96c-e325-11ce-bfc1-08002be10318} +Provider=%Msft% +Catalogfile=avshws.cat +DriverVer=08/31/2006,6.0.5600.0 + +[SourceDisksNames] +1000=%cdname%,,, + +[SourceDisksFiles] +avshws.sys=1000 + +[DestinationDirs] +DefaultDestDir=12 +avshws.CopyFiles=12 + +[avshws.CopyFiles] +avshws.sys + +[Manufacturer] +%Msft%=Microsoft,NTx86,NTamd64,ntarm,ntarm64 + +[Microsoft.NTx86] +%avshws.DeviceDesc%=avshws.NTx86,AVSHWS + +[Microsoft.NTamd64] +%avshws.DeviceDesc%=avshws.NTamd64,AVSHWS + +[Microsoft.NTarm] +%avshws.DeviceDesc%=avshws.NTarm,AVSHWS + +[Microsoft.NTarm64] +%avshws.DeviceDesc%=avshws.NTarm64,AVSHWS + +;--------------------------------------------------------------- +; x 86 D D I n s t a l l +;--------------------------------------------------------------- + +[avshws.NTx86] +Include=ks.inf, KSCAPTUR.inf +Needs=KS.Registration,KSCAPTUR.Registration.NT +CopyFiles=avshws.CopyFiles + +;--------------------------------------------------------------- +; A M D 64 D D I n s t a l l +;--------------------------------------------------------------- + +[avshws.NTamd64] +Include=ks.inf,KSCAPTUR.inf +Needs=KS.Registration,KSCAPTUR.Registration.NT +CopyFiles=avshws.CopyFiles + +;--------------------------------------------------------------- +; A R M D D I n s t a l l +;--------------------------------------------------------------- + +[avshws.NTarm] +Include=ks.inf,KSCAPTUR.inf +Needs=KS.Registration,KSCAPTUR.Registration.NT +CopyFiles=avshws.CopyFiles + +;--------------------------------------------------------------- +; A R M 64 D D I n s t a l l +;--------------------------------------------------------------- + +[avshws.NTarm64] +Include=ks.inf,KSCAPTUR.inf +Needs=KS.Registration,KSCAPTUR.Registration.NT +CopyFiles=avshws.CopyFiles + +;--------------------------------------------------------------- +; I n t e r f a c e s +;--------------------------------------------------------------- + +[avshws.NTx86.Interfaces] +AddInterface=%KSCATEGORY_CAPTURE%,"GLOBAL",CaptureInterface.NT,0 +AddInterface=%KSCATEGORY_VIDEO%,"GLOBAL",CaptureInterface.NT,0 +AddInterface=%KSCATEGORY_VIDEO_CAMERA%,"GLOBAL",CaptureInterface.NT,0 + +[avshws.NTamd64.Interfaces] +AddInterface=%KSCATEGORY_CAPTURE%,"GLOBAL",CaptureInterface.NT,0 +AddInterface=%KSCATEGORY_VIDEO%,"GLOBAL",CaptureInterface.NT,0 +AddInterface=%KSCATEGORY_VIDEO_CAMERA%,"GLOBAL",CaptureInterface.NT,0 + +[avshws.NTarm.Interfaces] +AddInterface=%KSCATEGORY_CAPTURE%,"GLOBAL",CaptureInterface.NT,0 +AddInterface=%KSCATEGORY_VIDEO%,"GLOBAL",CaptureInterface.NT,0 +AddInterface=%KSCATEGORY_VIDEO_CAMERA%,"GLOBAL",CaptureInterface.NT,0 + +[avshws.NTarm64.Interfaces] +AddInterface=%KSCATEGORY_CAPTURE%,"GLOBAL",CaptureInterface.NT,0 +AddInterface=%KSCATEGORY_VIDEO%,"GLOBAL",CaptureInterface.NT,0 +AddInterface=%KSCATEGORY_VIDEO_CAMERA%,"GLOBAL",CaptureInterface.NT,0 + +[CaptureInterface.NT] +AddReg=avshws.Reader.AddReg + +;--------------------------------------------------------------- +; A d d R e g +;--------------------------------------------------------------- + +[avshws.Reader.AddReg] +HKR,,CLSID,,%Proxy.CLSID% +HKR,,FriendlyName,,%avshws.Reader.FriendlyName% + +;--------------------------------------------------------------- +; S e r v i c e s +;--------------------------------------------------------------- + +[avshws.NTx86.Services] +AddService=avshws, 0x00000002, avshws.ServiceInstall + +[avshws.NTamd64.Services] +AddService=avshws, 0x00000002, avshws.ServiceInstall + +[avshws.NTarm.Services] +AddService=avshws, 0x00000002, avshws.ServiceInstall + +[avshws.NTarm64.Services] +AddService=avshws, 0x00000002, avshws.ServiceInstall + +[avshws.ServiceInstall] +DisplayName=%avshws.DeviceDesc% +ServiceType=%SERVICE_KERNEL_DRIVER% +StartType=%SERVICE_DEMAND_START% +ErrorControl=%SERVICE_ERROR_NORMAL% +ServiceBinary=%12%\avshws.sys + +;--------------------------------------------------------------- +; S t r i n g s +;--------------------------------------------------------------- + +[Strings] +; non-localizable +Proxy.CLSID="{17CCA71B-ECD7-11D0-B908-00A0C9223196}" +KSCATEGORY_CAPTURE="{65E8773D-8F56-11D0-A3B9-00A0C9223196}" +KSCATEGORY_VIDEO="{6994AD05-93EF-11D0-A3CC-00A0C9223196}" +KSCATEGORY_VIDEO_CAMERA="{E5323777-F976-4f5b-9B55-B94699C46E44}" + +SERVICE_KERNEL_DRIVER=1 +SERVICE_DEMAND_START=3 +SERVICE_ERROR_NORMAL=1 +REG_DWORD=0x00010001 + +;localizable +Msft="Microsoft" +avshws.DeviceDesc="AVStream Simulated Hardware Sample" +avshws.Reader.FriendlyName="avshws Source" +cdname="AVSHWS Installation Disk" \ No newline at end of file diff --git a/avstream/avshws/avshws.rc b/avstream/avshws/avshws.rc new file mode 100644 index 00000000..3e8a3e6a --- /dev/null +++ b/avstream/avshws/avshws.rc @@ -0,0 +1,22 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 1999 - 1999 +// +// File: captst.rc +// +//-------------------------------------------------------------------------- + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "AVStream Simulated Hardware Sample" +#define VER_INTERNALNAME_STR "avshws.sys" +#define VER_ORIGINALFILENAME_STR "avshws.sys" + +#include "common.ver" + diff --git a/avstream/avshws/avshws.sln b/avstream/avshws/avshws.sln new file mode 100644 index 00000000..08896741 --- /dev/null +++ b/avstream/avshws/avshws.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avshws", "avshws.vcxproj", "{A186D470-31E9-4CAF-9919-6279777D8ABD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A186D470-31E9-4CAF-9919-6279777D8ABD}.Debug|Win32.ActiveCfg = Debug|Win32 + {A186D470-31E9-4CAF-9919-6279777D8ABD}.Debug|Win32.Build.0 = Debug|Win32 + {A186D470-31E9-4CAF-9919-6279777D8ABD}.Release|Win32.ActiveCfg = Release|Win32 + {A186D470-31E9-4CAF-9919-6279777D8ABD}.Release|Win32.Build.0 = Release|Win32 + {A186D470-31E9-4CAF-9919-6279777D8ABD}.Debug|x64.ActiveCfg = Debug|x64 + {A186D470-31E9-4CAF-9919-6279777D8ABD}.Debug|x64.Build.0 = Debug|x64 + {A186D470-31E9-4CAF-9919-6279777D8ABD}.Release|x64.ActiveCfg = Release|x64 + {A186D470-31E9-4CAF-9919-6279777D8ABD}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/avstream/avshws/avshws.vcxproj b/avstream/avshws/avshws.vcxproj new file mode 100644 index 00000000..af274dcc --- /dev/null +++ b/avstream/avshws/avshws.vcxproj @@ -0,0 +1,201 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A186D470-31E9-4CAF-9919-6279777D8ABD} + $(MSBuildProjectName) + Debug + Win32 + {FBEB03DA-BEFF-446C-AF78-E7ED9881996F} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + avshws + + + avshws + + + avshws + + + avshws + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib + %(AdditionalOptions) -merge:PAGECONST=PAGE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib + %(AdditionalOptions) -merge:PAGECONST=PAGE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib + %(AdditionalOptions) -merge:PAGECONST=PAGE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib + %(AdditionalOptions) -merge:PAGECONST=PAGE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE;DEBUG_LEVEL=DEBUGLVL_BLAB;_WIN2K_COMPAT_SLIST_USAGE;_NO_SYS_GUID_OPERATOR_EQ_ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/avstream/avshws/avshws.vcxproj.Filters b/avstream/avshws/avshws.vcxproj.Filters new file mode 100644 index 00000000..318e9d42 --- /dev/null +++ b/avstream/avshws/avshws.vcxproj.Filters @@ -0,0 +1,46 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C2894B1A-8188-468F-AF01-0EAE14CD2040} + + + h;hpp;hxx;hm;inl;inc;xsd + {73B3B8CC-EA08-420A-AD92-489BAEEF0DE5} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A91E7CB8-3994-4C84-8CBA-0B18FA3FDD11} + + + inf;inv;inx;mof;mc; + {8C5EEC35-3838-457C-A60E-507C6503A48D} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/avstream/avshws/capture.cpp b/avstream/avshws/capture.cpp new file mode 100644 index 00000000..5632a4a1 --- /dev/null +++ b/avstream/avshws/capture.cpp @@ -0,0 +1,1704 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + capture.cpp + + Abstract: + + This file contains source for the video capture pin on the capture + filter. The capture sample performs "fake" DMA directly into + the capture buffers. Common buffer DMA will work slightly differently. + + For common buffer DMA, the general technique would be DPC schedules + processing with KsPinAttemptProcessing. The processing routine grabs + the leading edge, copies data out of the common buffer and advances. + Cloning would not be necessary with this technique. It would be + similiar to the way "AVSSamp" works, but it would be pin-centric. + + History: + + created 3/8/2001 + +**************************************************************************/ + +#include "avshws.h" +#include +#include "ntintsafe.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +#define DMAX_X 320 +#define DMAX_Y 240 +#define D_X 320 +#define D_Y 240 + +CCapturePin:: +CCapturePin ( + IN PKSPIN Pin + ) : + m_Pin (Pin) + ,m_PresentationTime (0) + +/*++ + +Routine Description: + + Construct a new capture pin. + +Arguments: + + Pin - + The AVStream pin object corresponding to the capture pin + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + PKSDEVICE Device = KsPinGetDevice (Pin); + + // + // Set up our device pointer. This gives us access to "hardware I/O" + // during the capture routines. + // + m_Device = reinterpret_cast (Device -> Context); +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +DispatchCreate ( + IN PKSPIN Pin, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Create a new capture pin. This is the creation dispatch for + the video capture pin. + +Arguments: + + Pin - + The pin being created + + Irp - + The creation Irp + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + CCapturePin *CapPin = new (NonPagedPool) CCapturePin (Pin); + + if (!CapPin) { + // + // Return failure if we couldn't create the pin. + // + Status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + // + // Add the item to the object bag if we we were successful. + // Whenever the pin closes, the bag is cleaned up and we will be + // freed. + // + Status = KsAddItemToObjectBag ( + Pin -> Bag, + reinterpret_cast (CapPin), + reinterpret_cast (CCapturePin::Cleanup) + ); + + if (!NT_SUCCESS (Status)) { + delete CapPin; + } else { + Pin -> Context = reinterpret_cast (CapPin); + } + + } + + // + // If we succeeded so far, stash the video info header away and change + // our allocator framing to reflect the fact that only now do we know + // the framing requirements based on the connection format. + // + PKS_VIDEOINFOHEADER VideoInfoHeader = NULL; + + if (NT_SUCCESS (Status)) { + + VideoInfoHeader = CapPin -> CaptureVideoInfoHeader (); + if (!VideoInfoHeader) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(Status)) { + // + // We need to edit the descriptor to ensure we don't mess up any other + // pins using the descriptor or touch read-only memory. + // + Status = KsEdit ( + Pin, + &Pin -> Descriptor, + AVSHWS_POOLTAG); + + if (NT_SUCCESS (Status)) { + + // + // If the edits proceeded without running out of memory, adjust + // the framing based on the video info header. + // + Status = KsEdit ( + Pin, + &Pin -> Descriptor -> AllocatorFraming, + AVSHWS_POOLTAG); + + if (NT_SUCCESS (Status)) { + + // + // We've KsEdit'ed this... I'm safe to cast away constness as + // long as the edit succeeded. + // + PKSALLOCATOR_FRAMING_EX Framing = + const_cast ( + Pin -> Descriptor -> AllocatorFraming + ); + + Framing -> FramingItem [0].Frames = 2; + + // + // The physical and optimal ranges must be biSizeImage. We only + // support one frame size, precisely the size of each capture + // image. + // + Framing -> FramingItem [0].PhysicalRange.MinFrameSize = + Framing -> FramingItem [0].PhysicalRange.MaxFrameSize = + Framing -> FramingItem [0].FramingRange.Range.MinFrameSize = + Framing -> FramingItem [0].FramingRange.Range.MaxFrameSize = + VideoInfoHeader -> bmiHeader.biSizeImage; + + Framing -> FramingItem [0].PhysicalRange.Stepping = + Framing -> FramingItem [0].FramingRange.Range.Stepping = + 0; + + } + + } + } + + if (NT_SUCCESS (Status)) { + // + // Adjust the stream header size. The video packets have extended + // header info (KS_FRAME_INFO). + // + Pin -> StreamHeaderSize = sizeof (KSSTREAM_HEADER) + + sizeof (KS_FRAME_INFO); + + } + return Status; +} + +/*************************************************/ + + +PKS_VIDEOINFOHEADER +CCapturePin:: +CaptureVideoInfoHeader ( + ) + +/*++ + +Routine Description: + + Capture the video info header out of the connection format. This + is what we use to base synthesized images off. + +Arguments: + + None + +Return Value: + + The captured video info header or NULL if there is insufficient + memory. + +--*/ + +{ + + PAGED_CODE(); + + PKS_VIDEOINFOHEADER ConnectionHeader = + &((reinterpret_cast + (m_Pin -> ConnectionFormat)) -> + VideoInfoHeader); + + m_VideoInfoHeader = reinterpret_cast ( + ExAllocatePoolWithTag ( + NonPagedPool, + KS_SIZE_VIDEOHEADER (ConnectionHeader), + AVSHWS_POOLTAG + ) + ); + + if (!m_VideoInfoHeader) + return NULL; + + // + // Bag the newly allocated header space. This will get cleaned up + // automatically when the pin closes. + // + NTSTATUS Status = + KsAddItemToObjectBag ( + m_Pin -> Bag, + reinterpret_cast (m_VideoInfoHeader), + NULL + ); + + if (!NT_SUCCESS (Status)) { + + ExFreePool (m_VideoInfoHeader); + return NULL; + + } else { + + // + // Copy the connection format video info header into the newly + // allocated "captured" video info header. + // + RtlCopyMemory ( + m_VideoInfoHeader, + ConnectionHeader, + KS_SIZE_VIDEOHEADER (ConnectionHeader) + ); + + } + + return m_VideoInfoHeader; + +} + + +NTSTATUS +CCapturePin:: +Process ( + ) + +/*++ + +Routine Description: + + The process dispatch for the pin bridges to this location. + We handle setting up scatter gather mappings, etc... + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + PKSSTREAM_POINTER Leading; + + _DbgPrintF(DEBUGLVL_VERBOSE, ("Process")); + + Leading = KsPinGetLeadingEdgeStreamPointer ( + m_Pin, + KSSTREAM_POINTER_STATE_LOCKED + ); + + while (NT_SUCCESS (Status) && Leading) { + + PKSSTREAM_POINTER ClonePointer; + PSTREAM_POINTER_CONTEXT SPContext = NULL; + + // + // If no data is present in the Leading edge stream pointer, just + // move on to the next frame + // + if ( NULL == Leading -> StreamHeader -> Data ) { + Status = KsStreamPointerAdvance(Leading); + continue; + } + // + // For optimization sake in this particular sample, I will only keep + // one clone stream pointer per frame. This complicates the logic + // here but simplifies the completions. + // + // I'm also choosing to do this since I need to keep track of the + // virtual addresses corresponding to each mapping since I'm faking + // DMA. It simplifies that too. + // + if (!m_PreviousStreamPointer) { + // + // First thing we need to do is clone the leading edge. This allows + // us to keep reference on the frames while they're in DMA. + // + Status = KsStreamPointerClone ( + Leading, + NULL, + sizeof (STREAM_POINTER_CONTEXT), + &ClonePointer + ); + + // + // I use this for easy chunking of the buffer. We're not really + // dealing with physical addresses. This keeps track of what + // virtual address in the buffer the current scatter / gather + // mapping corresponds to for the fake hardware. + // + if (NT_SUCCESS (Status)) { + + // + // Set the stream header data used to 0. We update this + // in the DMA completions. For queues with DMA, we must + // update this field ourselves. + // + ClonePointer -> StreamHeader -> DataUsed = 0; + + SPContext = reinterpret_cast + (ClonePointer -> Context); + + SPContext -> BufferVirtual = + reinterpret_cast ( + ClonePointer -> StreamHeader -> Data + ); + } + + } else { + + ClonePointer = m_PreviousStreamPointer; + SPContext = reinterpret_cast + (ClonePointer -> Context); + Status = STATUS_SUCCESS; + } + + // + // If the clone failed, likely we're out of resources. Break out + // of the loop for now. We may end up starving DMA. + // + if (!NT_SUCCESS (Status)) { + KsStreamPointerUnlock (Leading, FALSE); + break; + } + + // + // Program the fake hardware. I would use Clone -> OffsetOut.*, but + // because of the optimization of one stream pointer per frame, it + // doesn't make complete sense. + // + ULONG MappingsUsed = + m_Device -> ProgramScatterGatherMappings ( + ClonePointer, + &(SPContext -> BufferVirtual), + Leading -> OffsetOut.Mappings, + Leading -> OffsetOut.Remaining + ); + + // + // In order to keep one clone per frame and simplify the fake DMA + // logic, make a check to see if we completely used the mappings in + // the leading edge. Set a flag. + // + if (MappingsUsed == Leading -> OffsetOut.Remaining) { + m_PreviousStreamPointer = NULL; + } else { + m_PreviousStreamPointer = ClonePointer; + } + + if (MappingsUsed) { + // + // If any mappings were added to scatter / gather queues, + // advance the leading edge by that number of mappings. If + // we run off the end of the queue, Status will be + // STATUS_DEVICE_NOT_READY. Otherwise, the leading edge will + // point to a new frame. The previous one will not have been + // dismissed (unless "DMA" completed) since there's a clone + // pointer referencing the frames. + // + Status = + KsStreamPointerAdvanceOffsets ( + Leading, + 0, + MappingsUsed, + FALSE + ); + } else { + + // + // The hardware was incapable of adding more entries. The S/G + // table is full. + // + Status = STATUS_PENDING; + break; + + } + + } + + // + // If the leading edge failed to lock (this is always possible, remember + // that locking CAN occassionally fail), don't blow up passing NULL + // into KsStreamPointerUnlock. Also, set m_PendIo to kick us later... + // + if (!Leading) { + + m_PendIo = TRUE; + + // + // If the lock failed, there's no point in getting called back + // immediately. The lock could fail due to insufficient memory, + // etc... In this case, we don't want to get called back immediately. + // Return pending. The m_PendIo flag will cause us to get kicked + // later. + // + Status = STATUS_PENDING; + } + + // + // If we didn't run the leading edge off the end of the queue, unlock it. + // + if (NT_SUCCESS (Status) && Leading) { + KsStreamPointerUnlock (Leading, FALSE); + } else { + // + // DEVICE_NOT_READY indicates that the advancement ran off the end + // of the queue. We couldn't lock the leading edge. + // + if (Status == STATUS_DEVICE_NOT_READY) Status = STATUS_SUCCESS; + } + + // + // If we failed with something that requires pending, set the pending I/O + // flag so we know we need to start it again in a completion DPC. + // + if (!NT_SUCCESS (Status) || Status == STATUS_PENDING) { + m_PendIo = TRUE; + } + + _DbgPrintF(DEBUGLVL_VERBOSE, ("Leaving Process...")); + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +CleanupReferences ( + ) + +/*++ + +Routine Description: + + Clean up any references we're holding on frames after we abruptly + stop the hardware. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + PKSSTREAM_POINTER Clone = KsPinGetFirstCloneStreamPointer (m_Pin); + PKSSTREAM_POINTER NextClone = NULL; + + // + // Walk through the clones, deleting them, and setting DataUsed to + // zero since we didn't use any data! + // + while (Clone) { + + NextClone = KsStreamPointerGetNextClone (Clone); + + Clone -> StreamHeader -> DataUsed = 0; + KsStreamPointerDelete (Clone); + + Clone = NextClone; + + } + + return STATUS_SUCCESS; + +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +SetState ( + IN KSSTATE ToState, + IN KSSTATE FromState + ) + +/*++ + +Routine Description: + + This is called when the caputre pin transitions state. The routine + attempts to acquire / release any hardware resources and start up + or shut down capture based on the states we are transitioning to + and away from. + +Arguments: + + ToState - + The state we're transitioning to + + FromState - + The state we're transitioning away from + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + switch (ToState) { + + case KSSTATE_STOP: + + // + // First, stop the hardware if we actually did anything to it. + // + if (m_HardwareState != HardwareStopped) { + Status = m_Device -> Stop (); + NT_ASSERT (NT_SUCCESS (Status)); + + m_HardwareState = HardwareStopped; + } + + // + // We've stopped the "fake hardware". It has cleared out + // it's scatter / gather tables and will no longer be + // completing clones. We had locks on some frames that were, + // however, in hardware. This will clean them up. An + // alternative location would be in the reset dispatch. + // Note, however, that the reset dispatch can occur in any + // state and this should be understood. + // + // Some hardware may fill all S/G mappings before stopping... + // in this case, you may not have to do this. The + // "fake hardware" here simply stops filling mappings and + // cleans its scatter / gather tables out on the Stop call. + // + Status = CleanupReferences (); + + // + // Release any hardware resources related to this pin. + // + if (m_AcquiredResources) { + // + // If we got an interface to the clock, we must release it. + // + if (m_Clock) { + m_Clock -> Release (); + m_Clock = NULL; + } + + m_Device -> ReleaseHardwareResources ( + ); + + m_AcquiredResources = FALSE; + } + + break; + + case KSSTATE_ACQUIRE: + // + // Acquire any hardware resources related to this pin. We should + // only acquire them here -- **NOT** at filter create time. + // This means we do not fail creation of a filter because of + // limited hardware resources. + // + if (FromState == KSSTATE_STOP) { + Status = m_Device -> AcquireHardwareResources ( + this, + m_VideoInfoHeader + ); + + if (NT_SUCCESS (Status)) { + m_AcquiredResources = TRUE; + + // + // Attempt to get an interface to the master clock. + // This will fail if one has not been assigned. Since + // one must be assigned while the pin is still in + // KSSTATE_STOP, this is a guranteed method of getting + // the clock should one be assigned. + // + if (!NT_SUCCESS ( + KsPinGetReferenceClockInterface ( + m_Pin, + &m_Clock + ) + )) { + + // + // If we could not get an interface to the clock, + // don't use one. + // + m_Clock = NULL; + + } + + } else { + m_AcquiredResources = FALSE; + } + + } else { + // + // Standard transport pins will always receive transitions in + // +/- 1 manner. This means we'll always see a PAUSE->ACQUIRE + // transition before stopping the pin. + // + // The below is done because on DirectX 8.0, when the pin gets + // a message to stop, the queue is inaccessible. The reset + // which comes on every stop happens after this (at which time + // the queue is inaccessible also). So, for compatibility with + // DirectX 8.0, I am stopping the "fake" hardware at this + // point and cleaning up all references we have on frames. See + // the comments above regarding the CleanupReferences call. + // + // If this sample were targeting XP only, the below code would + // not be here. Again, I only do this so the sample does not + // hang when it is stopped running on a configuration such as + // Win2K + DX8. + // + if (m_HardwareState != HardwareStopped) { + Status = m_Device -> Stop (); + NT_ASSERT (NT_SUCCESS (Status)); + + m_HardwareState = HardwareStopped; + } + + Status = CleanupReferences (); + } + + m_FrameNumber = 0; + m_DroppedFrames = 0; + break; + + case KSSTATE_PAUSE: + // + // Stop the hardware simulation if we're coming down from run. + // + if (FromState == KSSTATE_RUN) { + + m_PresentationTime = 0; + Status = m_Device -> Pause (TRUE); + + if (NT_SUCCESS (Status)) { + m_HardwareState = HardwarePaused; + } + + } + m_FrameNumber = 0; + break; + + case KSSTATE_RUN: + // + // Start the hardware simulation or unpause it depending on + // whether we're initially running or we've paused and restarted. + // + if (m_HardwareState == HardwarePaused) { + Status = m_Device -> Pause (FALSE); + } else { + Status = m_Device -> Start (); + } + + if (NT_SUCCESS (Status)) { + m_HardwareState = HardwareRunning; + } + + break; + + } + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +IntersectHandler ( + IN PKSFILTER Filter, + IN PIRP Irp, + IN PKSP_PIN PinInstance, + IN PKSDATARANGE CallerDataRange, + IN PKSDATARANGE DescriptorDataRange, + IN ULONG BufferSize, + OUT PVOID Data OPTIONAL, + OUT PULONG DataSize + ) + +/*++ + +Routine Description: + + This routine handles video pin intersection queries by determining the + intersection between two data ranges. + +Arguments: + + Filter - + Contains a void pointer to the filter structure. + + Irp - + Contains a pointer to the data intersection property request. + + PinInstance - + Contains a pointer to a structure indicating the pin in question. + + CallerDataRange - + Contains a pointer to one of the data ranges supplied by the client + in the data intersection request. The format type, subtype and + specifier are compatible with the DescriptorDataRange. + + DescriptorDataRange - + Contains a pointer to one of the data ranges from the pin descriptor + for the pin in question. The format type, subtype and specifier are + compatible with the CallerDataRange. + + BufferSize - + Contains the size in bytes of the buffer pointed to by the Data + argument. For size queries, this value will be zero. + + Data - + Optionally contains a pointer to the buffer to contain the data + format structure representing the best format in the intersection + of the two data ranges. For size queries, this pointer will be + NULL. + + DataSize - + Contains a pointer to the location at which to deposit the size + of the data format. This information is supplied by the function + when the format is actually delivered and in response to size + queries. + +Return Value: + + STATUS_SUCCESS if there is an intersection and it fits in the supplied + buffer, STATUS_BUFFER_OVERFLOW for successful size queries, + STATUS_NO_MATCH if the intersection is empty, or + STATUS_BUFFER_TOO_SMALL if the supplied buffer is too small. + +--*/ + +{ + PAGED_CODE(); + + const GUID VideoInfoSpecifier = + {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_VIDEOINFO)}; + + NT_ASSERT(Filter); + NT_ASSERT(Irp); + NT_ASSERT(PinInstance); + NT_ASSERT(CallerDataRange); + NT_ASSERT(DescriptorDataRange); + NT_ASSERT(DataSize); + + ULONG DataFormatSize; + + // + // Specifier FORMAT_VideoInfo for VIDEOINFOHEADER + // + if (IsEqualGUID(CallerDataRange->Specifier, VideoInfoSpecifier) && + CallerDataRange -> FormatSize >= sizeof (KS_DATARANGE_VIDEO)) { + + PKS_DATARANGE_VIDEO callerDataRange = + reinterpret_cast (CallerDataRange); + + PKS_DATARANGE_VIDEO descriptorDataRange = + reinterpret_cast (DescriptorDataRange); + + PKS_DATAFORMAT_VIDEOINFOHEADER FormatVideoInfoHeader; + + // + // Check that the other fields match + // + if ((callerDataRange->bFixedSizeSamples != + descriptorDataRange->bFixedSizeSamples) || + (callerDataRange->bTemporalCompression != + descriptorDataRange->bTemporalCompression) || + (callerDataRange->StreamDescriptionFlags != + descriptorDataRange->StreamDescriptionFlags) || + (callerDataRange->MemoryAllocationFlags != + descriptorDataRange->MemoryAllocationFlags) || + (RtlCompareMemory (&callerDataRange->ConfigCaps, + &descriptorDataRange->ConfigCaps, + sizeof (KS_VIDEO_STREAM_CONFIG_CAPS)) != + sizeof (KS_VIDEO_STREAM_CONFIG_CAPS))) + { + return STATUS_NO_MATCH; + } + + // + // KS_SIZE_VIDEOHEADER() below is relying on bmiHeader.biSize from + // the caller's data range. This **MUST** be validated; the + // extended bmiHeader size (biSize) must not extend past the end + // of the range buffer. Possible arithmetic overflow is also + // checked for. + // + { + ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER ( + &callerDataRange->VideoInfoHeader + ); + + ULONG DataRangeSize = + FIELD_OFFSET (KS_DATARANGE_VIDEO, VideoInfoHeader) + + VideoHeaderSize; + + // + // Check that biSize does not extend past the buffer. The + // first two checks are for arithmetic overflow on the + // operations to compute the alleged size. (On unsigned + // math, a+b < a iff an arithmetic overflow occurred). + // + if ( + VideoHeaderSize < callerDataRange-> + VideoInfoHeader.bmiHeader.biSize || + DataRangeSize < VideoHeaderSize || + DataRangeSize > callerDataRange -> DataRange.FormatSize + ) { + + return STATUS_INVALID_PARAMETER; + + } + + } + + DataFormatSize = + sizeof (KSDATAFORMAT) + + KS_SIZE_VIDEOHEADER (&callerDataRange->VideoInfoHeader); + + + // + // If the passed buffer size is 0, it indicates that this is a size + // only query. Return the size of the intersecting data format and + // pass back STATUS_BUFFER_OVERFLOW. + // + if (BufferSize == 0) { + + *DataSize = DataFormatSize; + return STATUS_BUFFER_OVERFLOW; + + } + + // + // Verify that the provided structure is large enough to + // accept the result. + // + if (BufferSize < DataFormatSize) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Copy over the KSDATAFORMAT, followed by the actual VideoInfoHeader + // + *DataSize = DataFormatSize; + + FormatVideoInfoHeader = PKS_DATAFORMAT_VIDEOINFOHEADER( Data ); + + // + // Copy over the KSDATAFORMAT. This is precisely the same as the + // KSDATARANGE (it's just the GUIDs, etc... not the format information + // following any data format. + // + RtlCopyMemory ( + &FormatVideoInfoHeader->DataFormat, + DescriptorDataRange, + sizeof (KSDATAFORMAT)); + + FormatVideoInfoHeader->DataFormat.FormatSize = DataFormatSize; + + // + // Copy over the callers requested VIDEOINFOHEADER + // + + RtlCopyMemory ( + &FormatVideoInfoHeader->VideoInfoHeader, + &callerDataRange->VideoInfoHeader, + KS_SIZE_VIDEOHEADER (&callerDataRange->VideoInfoHeader) + ); + + // + // Calculate biSizeImage for this request, and put the result in both + // the biSizeImage field of the bmiHeader AND in the SampleSize field + // of the DataFormat. + // + // Note that for compressed sizes, this calculation will probably not + // be just width * height * bitdepth + // + FormatVideoInfoHeader->VideoInfoHeader.bmiHeader.biSizeImage = + FormatVideoInfoHeader->DataFormat.SampleSize = + KS_DIBSIZE (FormatVideoInfoHeader->VideoInfoHeader.bmiHeader); + + // + // REVIEW - Perform other validation such as cropping and scaling checks + // + + return STATUS_SUCCESS; + + } // End of VIDEOINFOHEADER specifier + + return STATUS_NO_MATCH; +} + +/*************************************************/ + +BOOL +MultiplyCheckOverflow ( + ULONG a, + ULONG b, + ULONG *pab + ) + +/*++ + +Routine Description: + + Perform a 32 bit unsigned multiplication and check for arithmetic overflow. + +Arguments: + + a - + First operand + + b - + Second operand + + pab - + Result + +Return Value: + + TRUE - + no overflow + + FALSE - + overflow occurred + +--*/ + +{ + PAGED_CODE(); + + *pab = a * b; + if ((a == 0) || (((*pab) / a) == b)) { + return TRUE; + } + return FALSE; +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +DispatchSetFormat ( + IN PKSPIN Pin, + IN PKSDATAFORMAT OldFormat OPTIONAL, + IN PKSMULTIPLE_ITEM OldAttributeList OPTIONAL, + IN const KSDATARANGE *DataRange, + IN const KSATTRIBUTE_LIST *AttributeRange OPTIONAL + ) + +/*++ + +Routine Description: + + This is the set data format dispatch for the capture pin. It is called + in two circumstances. + + 1: before Pin's creation dispatch has been made to verify that + Pin -> ConnectionFormat is an acceptable format for the range + DataRange. In this case OldFormat is NULL. + + 2: after Pin's creation dispatch has been made and an initial format + selected in order to change the format for the pin. In this case, + OldFormat will not be NULL. + + Validate that the format is acceptible and perform the actions necessary + to change format if appropriate. + +Arguments: + + Pin - + The pin this format is being set on. The format itself will be in + Pin -> ConnectionFormat. + + OldFormat - + The previous format used on this pin. If this is NULL, it is an + indication that Pin's creation dispatch has not yet been made and + that this is a request to validate the initial format and not to + change formats. + + OldAttributeList - + The old attribute list for the prior format + + DataRange - + A range out of our list of data ranges which was determined to be + at least a partial match for Pin -> ConnectionFormat. If the format + there is unacceptable for the range, STATUS_NO_MATCH should be + returned. + + AttributeRange - + The attribute range + +Return Value: + + Success / Failure + + STATUS_SUCCESS - + The format is acceptable / the format has been changed + + STATUS_NO_MATCH - + The format is not-acceptable / the format has not been changed + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_NO_MATCH; + + const GUID VideoInfoSpecifier = + {STATICGUIDOF(KSDATAFORMAT_SPECIFIER_VIDEOINFO)}; + + CCapturePin *CapPin = NULL; + + // + // Find the pin, if it exists yet. OldFormat will be an indication of + // this. If we're changing formats, OldFormat will be non-NULL. + // + // You cannot use Pin -> Context to make the determination. AVStream + // preinitializes this to the filter's context. + // + if (OldFormat) { + CapPin = reinterpret_cast (Pin -> Context); + } + + if (IsEqualGUID (Pin -> ConnectionFormat -> Specifier, + VideoInfoSpecifier) && + Pin -> ConnectionFormat -> FormatSize >= + sizeof (KS_DATAFORMAT_VIDEOINFOHEADER)) { + + PKS_DATAFORMAT_VIDEOINFOHEADER ConnectionFormat = + reinterpret_cast + (Pin -> ConnectionFormat); + + // + // DataRange comes out of OUR data range list. I know the range + // is valid as such. + // + const KS_DATARANGE_VIDEO *VIRange = + reinterpret_cast + (DataRange); + + // + // Check that bmiHeader.biSize is valid since we use it later. + // + ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER ( + &ConnectionFormat -> VideoInfoHeader + ); + + ULONG DataFormatSize = FIELD_OFFSET ( + KS_DATAFORMAT_VIDEOINFOHEADER, VideoInfoHeader + ) + VideoHeaderSize; + + if ( + VideoHeaderSize < ConnectionFormat-> + VideoInfoHeader.bmiHeader.biSize || + DataFormatSize < VideoHeaderSize || + DataFormatSize > ConnectionFormat -> DataFormat.FormatSize + ) { + + Status = STATUS_INVALID_PARAMETER; + + } + + // + // Check that the format is a match for the selected range. + // + else if ( + (ConnectionFormat -> VideoInfoHeader.bmiHeader.biWidth != + VIRange -> VideoInfoHeader.bmiHeader.biWidth) || + + (ConnectionFormat -> VideoInfoHeader.bmiHeader.biHeight != + VIRange -> VideoInfoHeader.bmiHeader.biHeight) || + + (ConnectionFormat -> VideoInfoHeader.bmiHeader.biCompression != + VIRange -> VideoInfoHeader.bmiHeader.biCompression) + + ) { + + Status = STATUS_NO_MATCH; + + } else { + + // + // Compute the minimum size of our buffers to validate against. + // The image synthesis routines synthesize |biHeight| rows of + // biWidth pixels in either RGB24 or UYVY. In order to ensure + // safe synthesis into the buffer, we need to know how large an + // image this will produce. + // + // I do this explicitly because of the method that the data is + // synthesized. A variation of this may or may not be necessary + // depending on the mechanism the driver in question fills the + // capture buffers. The important thing is to ensure that they + // aren't overrun during capture. + // + ULONG ImageSize; + + if (!MultiplyCheckOverflow ( + (ULONG)ConnectionFormat->VideoInfoHeader.bmiHeader.biWidth, + (ULONG)abs (ConnectionFormat-> + VideoInfoHeader.bmiHeader.biHeight), + &ImageSize + )) { + + Status = STATUS_INVALID_PARAMETER; + } + + // + // We only support KS_BI_RGB (24) and KS_BI_YUV422 (16), so + // this is valid for those formats. + // + else if (!MultiplyCheckOverflow ( + ImageSize, + (ULONG)(ConnectionFormat-> + VideoInfoHeader.bmiHeader.biBitCount / 8), + &ImageSize + )) { + + Status = STATUS_INVALID_PARAMETER; + + } + + // + // Valid for the formats we use. Otherwise, this would be + // checked later. + // + else if (ConnectionFormat->VideoInfoHeader.bmiHeader.biSizeImage < + ImageSize) { + + Status = STATUS_INVALID_PARAMETER; + + } else { + + // + // We can accept the format. + // + Status = STATUS_SUCCESS; + + // + // OldFormat is an indication that this is a format change. + // Since I do not implement the + // KSPROPERTY_CONNECTION_PROPOSEDATAFORMAT, by default, I do + // not handle dynamic format changes. + // + // If something changes while we're in the stop state, we're + // fine to handle it since we haven't "configured the hardware" + // yet. + // + if (OldFormat) { + // + // If we're in the stop state, we can handle just about any + // change. We don't support dynamic format changes. + // + if (Pin -> DeviceState == KSSTATE_STOP) { + if (!CapPin -> CaptureVideoInfoHeader ()) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } else { + // + // Because we don't accept dynamic format changes, we + // should never get here. Just being over-protective. + // + Status = STATUS_INVALID_DEVICE_STATE; + } + } + + } + + } + + } + return Status; +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +void +CCapturePin:: +CompleteMappings ( + IN ULONG NumMappings + ) + +/*++ + +Routine Description: + + Called to notify the pin that a given number of scatter / gather + mappings have completed. Let the buffers go if possible. + We're called at DPC. + +Arguments: + + NumMappings - + The number of mappings that have completed. + +Return Value: + + None + +--*/ + +{ + + ULONG MappingsRemaining = NumMappings; + + // + // Walk through the clones list and delete clones whose time has come. + // The list is guaranteed to be kept in the order they were cloned. + // + PKSSTREAM_POINTER Clone = KsPinGetFirstCloneStreamPointer (m_Pin); + + while (MappingsRemaining && Clone) { + + PKSSTREAM_POINTER NextClone = KsStreamPointerGetNextClone (Clone); + +#if defined(_X86_) + // + // Count up the number of bytes we've completed and mark this + // in the Stream Header. In mapped queues + // (KSPIN_FLAG_GENERATE_MAPPINGS), this is the responsibility of + // the minidriver. In non-mapped queues, AVStream performs this. + // + ULONG MappingsToCount = + (MappingsRemaining > Clone -> OffsetOut.Remaining) ? + Clone -> OffsetOut.Remaining : + MappingsRemaining; + + // + // Update DataUsed according to the mappings. + // + for (ULONG CurMapping = 0; CurMapping < MappingsToCount; CurMapping++) { + Clone -> StreamHeader -> DataUsed += + Clone -> OffsetOut.Mappings [CurMapping].ByteCount; + } +#endif + + // + // If we have completed all remaining mappings in this clone, it + // is an indication that the clone is ready to be deleted and the + // buffer released. Set anything required in the stream header which + // has not yet been set. If we have a clock, we can timestamp the + // sample. + // +#if !defined(_X86_) + if (Clone -> StreamHeader -> DataUsed >= Clone -> OffsetOut.Remaining) { +#else + if (MappingsRemaining >= Clone -> OffsetOut.Remaining) { +#endif + Clone -> StreamHeader -> Duration = + m_VideoInfoHeader -> AvgTimePerFrame; + + Clone -> StreamHeader -> PresentationTime.Numerator = + Clone -> StreamHeader -> PresentationTime.Denominator = 1; + + // + // If a clock has been assigned, timestamp the packets with the + // time shown on the clock. + // + if (m_Clock) { + + LONGLONG ClockTime = m_Clock -> GetTime (); + + Clone -> StreamHeader -> PresentationTime.Time = ClockTime; + + Clone -> StreamHeader -> OptionsFlags = + KSSTREAM_HEADER_OPTIONSF_TIMEVALID | + KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; + + } else { + // + // If there is no clock, don't time stamp the packets. + // + Clone -> StreamHeader -> PresentationTime.Time = 0; + + } + + // + // Increment the frame number. This is the total count of frames which + // have attempted capture. + // + m_FrameNumber++; + + // + // Double check the Stream Header size. AVStream makes no guarantee + // that because StreamHeaderSize is set to a specific size that you + // will get that size. If the proper data type handlers are not + // installed, the stream header will be of default size. + // + if ( Clone -> StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) + + sizeof (KS_FRAME_INFO)) { + + PKS_FRAME_INFO FrameInfo = reinterpret_cast ( + Clone -> StreamHeader + 1 + ); + + FrameInfo -> ExtendedHeaderSize = sizeof (KS_FRAME_INFO); + FrameInfo -> dwFrameFlags = KS_VIDEO_FLAG_FRAME; + FrameInfo -> PictureNumber = (LONGLONG)m_FrameNumber; + + // I don't really have a way to tell if the device has dropped a frame + // or was not able to send a frame on time. + FrameInfo -> DropCount = (LONGLONG)m_DroppedFrames; + } + + + // + // If all of the mappings in this clone have been completed, + // delete the clone. We've already updated DataUsed above. + // + +#if !defined(_X86_) + MappingsRemaining--; +#else + MappingsRemaining -= Clone -> OffsetOut.Remaining; +#endif + KsStreamPointerDelete (Clone); + + } else { + // + // If only part of the mappings in this clone have been completed, + // update the pointers. Since we're guaranteed this won't advance + // to a new frame by the check above, it won't fail. + // +#if !defined(_X86_) + (void)KsStreamPointerAdvanceOffsets ( + Clone, + 0, + Clone -> StreamHeader -> DataUsed, + FALSE + ); + +#else + (void)KsStreamPointerAdvanceOffsets ( + Clone, + 0, + MappingsRemaining, + FALSE + ); + +#endif + MappingsRemaining = 0; + + } + + // + // Go to the next clone. + // + Clone = NextClone; + + } + + // + // If we've used all the mappings in hardware and pended, we can kick + // processing to happen again if we've completed mappings. + // + if (m_PendIo) { + m_PendIo = TRUE; + KsPinAttemptProcessing (m_Pin, TRUE); + } + +} + +/************************************************************************** + + DISPATCH AND DESCRIPTOR LAYOUT + +**************************************************************************/ + +// +// FormatRGB24Bpp_Capture: +// +// This is the data range description of the RGB24 capture format we support. +// +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_Capture = { + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + D_X * D_Y * 3, // SampleSize + 0, // Reserved + + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7d, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB24, + STATICGUIDOF (KSDATAFORMAT_SPECIFIER_VIDEOINFO) // aka. FORMAT_VideoInfo + }, + + TRUE, // BOOL, bFixedSizeSamples (all samples same size?) + FALSE, // BOOL, bTemporalCompression (all I frames?) + 0, // Reserved (was StreamDescriptionFlags) + 0, // Reserved (was MemoryAllocationFlags + // (KS_VIDEO_ALLOC_*)) + // + // _KS_VIDEO_STREAM_CONFIG_CAPS + // + { + STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ), // GUID + KS_AnalogVideo_None, // AnalogVideoStandard + D_X,D_Y, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + D_X,D_Y, // MinCroppingSize, smallest rcSrc cropping rect allowed + D_X,D_Y, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + D_X, D_Y, // MinOutputSize, smallest bitmap stream can produce + D_X, D_Y, // MaxOutputSize, largest bitmap stream can produce + 8, // OutputGranularityX, granularity of output bitmap size + 1, // OutputGranularityY; + 0, // StretchTapsX (0 no stretch, 1 pix dup, 2 interp...) + 0, // StretchTapsY + 0, // ShrinkTapsX + 0, // ShrinkTapsY + 333667, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 3 * 30 * D_X * D_Y, // MinBitsPerSecond; + 8 * 3 * 30 * D_X * D_Y // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0,0,0,0, // RECT rcSource; + 0,0,0,0, // RECT rcTarget; + D_X * D_Y * 3 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 333667, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + D_X, // LONG biWidth; + D_Y, // LONG biHeight; + 1, // WORD biPlanes; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + D_X * D_Y * 3, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +// +// FormatYUY2_Capture: +// +// This is the data range description of the YUY2 format we support. +// +const +KS_DATARANGE_VIDEO +FormatYUY2_Capture = { + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X * DMAX_Y * 2, // SampleSize + 0, // Reserved + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0x32595559, 0x0000, 0x0010, 0x80, 0x00, + 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, //aka. MEDIASUBTYPE_YUY2, + STATICGUIDOF (KSDATAFORMAT_SPECIFIER_VIDEOINFO) // aka. FORMAT_VideoInfo + }, + + TRUE, // BOOL, bFixedSizeSamples (all samples same size?) + FALSE, // BOOL, bTemporalCompression (all I frames?) + 0, // Reserved (was StreamDescriptionFlags) + 0, // Reserved (was MemoryAllocationFlags + // (KS_VIDEO_ALLOC_*)) + + // + // _KS_VIDEO_STREAM_CONFIG_CAPS + // + { + STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ), // GUID + KS_AnalogVideo_None, // AnalogVideoStandard + DMAX_X, DMAX_Y, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + D_X,D_Y, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X, DMAX_Y, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + D_X, D_Y, // MinOutputSize, smallest bitmap stream can produce + DMAX_X, DMAX_Y, // MaxOutputSize, largest bitmap stream can produce + 8, // OutputGranularityX, granularity of output bitmap size + 1, // OutputGranularityY; + 0, // StretchTapsX (0 no stretch, 1 pix dup, 2 interp...) + 0, // StretchTapsY + 0, // ShrinkTapsX + 0, // ShrinkTapsY + 333667, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 2 * 30 * D_X * D_Y, // MinBitsPerSecond; + 8 * 2 * 30 * DMAX_X * DMAX_Y, // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X * DMAX_Y * 2 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 333667, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X, // LONG biWidth; + DMAX_Y, // LONG biHeight; + 1, // WORD biPlanes; + 16, // WORD biBitCount; + FOURCC_YUY2, // DWORD biCompression; + DMAX_X * DMAX_Y * 2, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +// +// CapturePinDispatch: +// +// This is the dispatch table for the capture pin. It provides notifications +// about creation, closure, processing, data formats, etc... +// +const +KSPIN_DISPATCH +CapturePinDispatch = { + CCapturePin::DispatchCreate, // Pin Create + NULL, // Pin Close + CCapturePin::DispatchProcess, // Pin Process + NULL, // Pin Reset + CCapturePin::DispatchSetFormat, // Pin Set Data Format + CCapturePin::DispatchSetState, // Pin Set Device State + NULL, // Pin Connect + NULL, // Pin Disconnect + NULL, // Clock Dispatch + NULL // Allocator Dispatch +}; + +// +// CapturePinAllocatorFraming: +// +// This is the simple framing structure for the capture pin. Note that this +// will be modified via KsEdit when the actual capture format is determined. +// +DECLARE_SIMPLE_FRAMING_EX ( + CapturePinAllocatorFraming, + STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED), + KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | + KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY, + 2, + 0, + 2 * PAGE_SIZE, + 2 * PAGE_SIZE + ); + +// +// CapturePinDataRanges: +// +// This is the list of data ranges supported on the capture pin. We support +// two: one RGB24, and one YUY2. +// +const +PKSDATARANGE +CapturePinDataRanges [CAPTURE_PIN_DATA_RANGE_COUNT] = { + (PKSDATARANGE) &FormatYUY2_Capture, + (PKSDATARANGE) &FormatRGB24Bpp_Capture + }; diff --git a/avstream/avshws/capture.h b/avstream/avshws/capture.h new file mode 100644 index 00000000..502e01e3 --- /dev/null +++ b/avstream/avshws/capture.h @@ -0,0 +1,313 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + capture.h + + Abstract: + + This file contains header for the video capture pin on the capture + filter. The capture sample performs "fake" DMA directly into + the capture buffers. Common buffer DMA will work slightly differently. + + For common buffer DMA, the general technique would be DPC schedules + processing with KsPinAttemptProcessing. The processing routine grabs + the leading edge, copies data out of the common buffer and advances. + Cloning would not be necessary with this technique. It would be + similiar to the way "AVSSamp" works, but it would be pin-centric. + + History: + + created 3/8/2001 + +**************************************************************************/ +#include +// +// STREAM_POINTER_CONTEXT: +// +// This is the context structure we associate with all clone stream pointers. +// It allows the mapping code to rip apart the buffer into chunks the same +// size as the scatter/gather mappings in order to fake scatter / gather +// bus-master DMA. +// +typedef struct _STREAM_POINTER_CONTEXT { + + PUCHAR BufferVirtual; + +} STREAM_POINTER_CONTEXT, *PSTREAM_POINTER_CONTEXT; + +// +// CCapturePin: +// +// The video capture pin class. +// +class CCapturePin : + public ICaptureSink { + +private: + + // + // The AVStream pin we're associated with. + // + PKSPIN m_Pin; + + // + // Pointer to the internal device object for our capture device. + // We access the "fake" hardware through this object. + // + CCaptureDevice *m_Device; + + // + // The state we've put the hardware into. This allows us to keep track + // of whether to do things like unpausing or restarting. + // + HARDWARE_STATE m_HardwareState; + + // + // The clock we've been assigned. As with other capture filters, we do + // not expose a clock. If one has been assigned, we will use it to + // time stamp packets (plus a reasonable delta to work the capture stream + // in a preview graph). + // + PIKSREFERENCECLOCK m_Clock; + + // + // The captured video info header. The settings for "fake" hardware will be + // programmed via this video info header. + // + PKS_VIDEOINFOHEADER m_VideoInfoHeader; + + // + // If we are unable to insert all of the mappings in a stream pointer into + // the "fake" hardware's scatter / gather table, we set this to the + // stream pointer that's incomplete. This is done both to make the + // relasing easier and to make it easier to fake the scatter / gather + // hardware. + // + PKSSTREAM_POINTER m_PreviousStreamPointer; + + // + // An indication of whether or not we pended I/O for some reason. If this + // is set, the DPC will resume I/O when any mappings are completed. + // + BOOLEAN m_PendIo; + + // + // An indication of whether or not this pin has acquired the necessary + // hardware resources to operate. When the pin reaches KSSTATE_ACQUIRE, + // we attempt to acquire the hardware. This flag will be set based on + // our success / failure. + // + BOOLEAN m_AcquiredResources; + + // + // Presentation time for the sample + // + LONGLONG m_PresentationTime; + + LONGLONG m_FrameNumber; + LONGLONG m_DroppedFrames; + + // + // CleanupReferences(): + // + // Clean up any references we hold on frames in the queue. This is called + // when we abruptly stop the fake hardware. + // + NTSTATUS + CleanupReferences ( + ); + + // + // SetState(): + // + // This is the state transition handler for the capture pin. It attempts + // to acquire resources for the capture pin (or releasing them if + // necessary) and starts and stops the hardware as required. + // + NTSTATUS + SetState ( + IN KSSTATE ToState, + IN KSSTATE FromState + ); + + // + // Process(): + // + // This is the processing dispatch for the capture pin. It handles + // programming the scatter / gather tables for the hardware as buffers + // become available. This processing routine is designed for a direct + // into the capture buffers kind of DMA as opposed to common-buffer + // and copy strategies. + // + NTSTATUS + Process ( + ); + // + // CaptureVideoInfoHeader(): + // + // This routine stashes the video info header set on the pin connection + // in the CCapturePin object. This is used to base hardware settings. + // + PKS_VIDEOINFOHEADER + CaptureVideoInfoHeader ( + ); + + // + // Cleanup(): + // + // This is the free callback from the bagged item (CCapturePin). If we + // do not provide a callback when we bag the CCapturePin, ExFreePool + // would be called. This is not desirable for C++ constructed objects. + // We merely delete the object here. + // + static + void + Cleanup ( + IN CCapturePin *Pin + ) + { + delete Pin; + } + +public: + // + // CCapturePin(): + // + // The capture pin's constructor. Initialize any non-0, non-NULL fields + // (since new will have zero'ed the memory anyway) and set up our + // device level pointers for access during capture routines. + // + CCapturePin ( + IN PKSPIN Pin + ); + + // + // ~CCapturePin(): + // + // The capture pin's destructor. + // + ~CCapturePin ( + ) + { + } + + // + // ICaptureSink::CompleteMappings() + // + // This is the capture sink notification mechanism for mapping completion. + // When the device DPC detects that a given number of mappings have been + // completed by the fake hardware, it signals the capture sink of this + // through this method. + // + virtual + void + CompleteMappings ( + IN ULONG NumMappings + ); + + /************************************************* + + Dispatch Routines + + *************************************************/ + + // + // DispatchCreate(): + // + // This is the creation dispatch for the capture pin. It creates + // the CCapturePin object and associates it with the AVStream object + // bagging it in the process. + // + static + NTSTATUS + DispatchCreate ( + IN PKSPIN Pin, + IN PIRP Irp + ); + + // + // DispatchSetState(): + // + // This is the set device state dispatch for the pin. The routine bridges + // to SetState() in the context of the CCapturePin. + // + static + NTSTATUS + DispatchSetState ( + IN PKSPIN Pin, + IN KSSTATE ToState, + IN KSSTATE FromState + ) + { + return + (reinterpret_cast (Pin -> Context)) -> + SetState (ToState, FromState); + } + + // + // DispatchSetFormat(): + // + // This is the set data format dispatch for the pin. This will be called + // BEFORE pin creation to validate that a data format selected is a match + // for the range pulled out of our range list. It will also be called + // for format changes. + // + // If OldFormat is NULL, this is an indication that it's the initial + // call and not a format change. Even fixed format pins get this call + // once. + // + static + NTSTATUS + DispatchSetFormat ( + IN PKSPIN Pin, + IN PKSDATAFORMAT OldFormat OPTIONAL, + IN PKSMULTIPLE_ITEM OldAttributeList OPTIONAL, + IN const KSDATARANGE *DataRange, + IN const KSATTRIBUTE_LIST *AttributeRange OPTIONAL + ); + + // + // DispatchProcess(): + // + // This is the processing dispatch for the capture pin. The routine + // bridges to Process() in the context of the CCapturePin. + // + static + NTSTATUS + DispatchProcess ( + IN PKSPIN Pin + ) + { + return + (reinterpret_cast (Pin -> Context)) -> + Process (); + + } + + // + // IntersectHandler(): + // + // This is the data intersection handler for the capture pin. This + // determines an optimal format in the intersection of two ranges, + // one local and one possibly foreign. If there is no compatible format, + // STATUS_NO_MATCH is returned. + // + static + NTSTATUS + IntersectHandler ( + IN PKSFILTER Filter, + IN PIRP Irp, + IN PKSP_PIN PinInstance, + IN PKSDATARANGE CallerDataRange, + IN PKSDATARANGE DescriptorDataRange, + IN ULONG BufferSize, + OUT PVOID Data OPTIONAL, + OUT PULONG DataSize + ); + +}; \ No newline at end of file diff --git a/avstream/avshws/device.cpp b/avstream/avshws/device.cpp new file mode 100644 index 00000000..ce4818b8 --- /dev/null +++ b/avstream/avshws/device.cpp @@ -0,0 +1,875 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + device.cpp + + Abstract: + + This file contains the device level implementation of the AVStream + hardware sample. Note that this is not the "fake" hardware. The + "fake" hardware is in hwsim.cpp. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#include "avshws.h" +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +NTSTATUS +CCaptureDevice:: +DispatchCreate ( + IN PKSDEVICE Device + ) + +/*++ + +Routine Description: + + Create the capture device. This is the creation dispatch for the + capture device. + +Arguments: + + Device - + The AVStream device being created. + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status; + + CCaptureDevice *CapDevice = new (NonPagedPool) CCaptureDevice (Device); + + if (!CapDevice) { + // + // Return failure if we couldn't create the pin. + // + Status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + // + // Add the item to the object bag if we were successful. + // Whenever the device goes away, the bag is cleaned up and + // we will be freed. + // + // For backwards compatibility with DirectX 8.0, we must grab + // the device mutex before doing this. For Windows XP, this is + // not required, but it is still safe. + // + KsAcquireDevice (Device); + Status = KsAddItemToObjectBag ( + Device -> Bag, + reinterpret_cast (CapDevice), + reinterpret_cast (CCaptureDevice::Cleanup) + ); + KsReleaseDevice (Device); + + if (!NT_SUCCESS (Status)) { + delete CapDevice; + } else { + Device -> Context = reinterpret_cast (CapDevice); + } + + } + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CCaptureDevice:: +PnpStart ( + IN PCM_RESOURCE_LIST TranslatedResourceList, + IN PCM_RESOURCE_LIST UntranslatedResourceList + ) + +/*++ + +Routine Description: + + Called at Pnp start. We start up our virtual hardware simulation. + +Arguments: + + TranslatedResourceList - + The translated resource list from Pnp + + UntranslatedResourceList - + The untranslated resource list from Pnp + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + // + // Normally, we'd do things here like parsing the resource lists and + // connecting our interrupt. Since this is a simulation, there isn't + // much to parse. The parsing and connection should be the same as + // any WDM driver. The sections that will differ are illustrated below + // in setting up a simulated DMA. + // + + NTSTATUS Status = STATUS_SUCCESS; + + if (!m_Device -> Started) { + // Create the Filter for the device + KsAcquireDevice(m_Device); + Status = KsCreateFilterFactory( m_Device->FunctionalDeviceObject, + &CaptureFilterDescriptor, + L"GLOBAL", + NULL, + KSCREATE_ITEM_FREEONSTOP, + NULL, + NULL, + NULL ); + KsReleaseDevice(m_Device); + + } + // + // By PnP, it's possible to receive multiple starts without an intervening + // stop (to reevaluate resources, for example). Thus, we only perform + // creations of the simulation on the initial start and ignore any + // subsequent start. Hardware drivers with resources should evaluate + // resources and make changes on 2nd start. + // + if (NT_SUCCESS(Status) && (!m_Device -> Started)) { + + m_HardwareSimulation = new (NonPagedPool) CHardwareSimulation (this); + if (!m_HardwareSimulation) { + // + // If we couldn't create the hardware simulation, fail. + // + Status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + Status = KsAddItemToObjectBag ( + m_Device -> Bag, + reinterpret_cast (m_HardwareSimulation), + reinterpret_cast (CHardwareSimulation::Cleanup) + ); + + if (!NT_SUCCESS (Status)) { + delete m_HardwareSimulation; + } + } +#if defined(_X86_) + // + // DMA operations illustrated in this sample are applicable only for 32bit platform. + // + INTERFACE_TYPE InterfaceBuffer; + ULONG InterfaceLength; + DEVICE_DESCRIPTION DeviceDescription; + + if (NT_SUCCESS (Status)) { + // + // Set up DMA... + // + // Ordinarilly, we'd be using InterfaceBuffer or + // InterfaceTypeUndefined if !NT_SUCCESS (IfStatus) as the + // InterfaceType below; however, for the purposes of this sample, + // we lie and say we're on the PCI Bus. Otherwise, we're using map + // registers on x86 32 bit physical to 32 bit logical and this isn't + // what I want to show in this sample. + // + // + // NTSTATUS IfStatus = + + IoGetDeviceProperty ( + m_Device -> PhysicalDeviceObject, + DevicePropertyLegacyBusType, + sizeof (INTERFACE_TYPE), + &InterfaceBuffer, + &InterfaceLength + ); + + // + // Initialize our fake device description. We claim to be a + // bus-mastering 32-bit scatter/gather capable piece of hardware. + // + DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION; + DeviceDescription.DmaChannel = ((ULONG) ~0); + DeviceDescription.InterfaceType = PCIBus; + DeviceDescription.DmaWidth = Width32Bits; + DeviceDescription.DmaSpeed = Compatible; + DeviceDescription.ScatterGather = TRUE; + DeviceDescription.Master = TRUE; + DeviceDescription.Dma32BitAddresses = TRUE; + DeviceDescription.AutoInitialize = FALSE; + DeviceDescription.MaximumLength = (ULONG) -1; + + // + // Get a DMA adapter object from the system. + // + m_DmaAdapterObject = IoGetDmaAdapter ( + m_Device -> PhysicalDeviceObject, + &DeviceDescription, + &m_NumberOfMapRegisters + ); + + if (!m_DmaAdapterObject) { + Status = STATUS_UNSUCCESSFUL; + } + + } + + if (NT_SUCCESS (Status)) { + // + // Initialize our DMA adapter object with AVStream. This is + // **ONLY** necessary **IF** you are doing DMA directly into + // capture buffers as this sample does. For this, + // KSPIN_FLAG_GENERATE_MAPPINGS must be specified on a queue. + // + + // + // The (1 << 20) below is the maximum size of a single s/g mapping + // that this hardware can handle. Note that I have pulled this + // number out of thin air for the "fake" hardware. + // + KsDeviceRegisterAdapterObject ( + m_Device, + m_DmaAdapterObject, + (1 << 20), + sizeof (KSMAPPING) + ); + + } +#endif + } + + return Status; + +} + +/*************************************************/ + + +void +CCaptureDevice:: +PnpStop ( + ) + +/*++ + +Routine Description: + + This is the pnp stop dispatch for the capture device. It releases any + adapter object previously allocated by IoGetDmaAdapter during Pnp Start. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + if (m_DmaAdapterObject) { + // + // Return the DMA adapter back to the system. + // + m_DmaAdapterObject -> DmaOperations -> + PutDmaAdapter (m_DmaAdapterObject); + + m_DmaAdapterObject = NULL; + } + +} + +/*************************************************/ + + +NTSTATUS +CCaptureDevice:: +AcquireHardwareResources ( + IN ICaptureSink *CaptureSink, + IN PKS_VIDEOINFOHEADER VideoInfoHeader + ) + +/*++ + +Routine Description: + + Acquire hardware resources for the capture hardware. If the + resources are already acquired, this will return an error. + The hardware configuration must be passed as a VideoInfoHeader. + +Arguments: + + CaptureSink - + The capture sink attempting to acquire resources. When scatter / + gather mappings are completed, the capture sink specified here is + what is notified of the completions. + + VideoInfoHeader - + Information about the capture stream. This **MUST** remain + stable until the caller releases hardware resources. Note + that this could also be guaranteed by bagging it in the device + object bag as well. + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + // + // If we're the first pin to go into acquire (remember we can have + // a filter in another graph going simultaneously), grab the resources. + // + if (InterlockedCompareExchange ( + &m_PinsWithResources, + 1, + 0) == 0) { + + m_VideoInfoHeader = VideoInfoHeader; + + // + // If there's an old hardware simulation sitting around for some + // reason, blow it away. + // + if (m_ImageSynth) { + delete m_ImageSynth; + m_ImageSynth = NULL; + } + + // + // Create the necessary type of image synthesizer. + // + if (m_VideoInfoHeader -> bmiHeader.biBitCount == 24 && + m_VideoInfoHeader -> bmiHeader.biCompression == KS_BI_RGB) { + + // + // If we're RGB24, create a new RGB24 synth. RGB24 surfaces + // can be in either orientation. The origin is lower left if + // height < 0. Otherwise, it's upper left. + // + m_ImageSynth = new (NonPagedPool, 'RysI') + CRGB24Synthesizer ( + m_VideoInfoHeader -> bmiHeader.biHeight >= 0 + ); + + } else + if (m_VideoInfoHeader -> bmiHeader.biBitCount == 16 && + (m_VideoInfoHeader -> bmiHeader.biCompression == FOURCC_YUY2)) { + + // + // If we're UYVY, create the YUV synth. + // + m_ImageSynth = new(NonPagedPool, 'YysI') CYUVSynthesizer; + + } + else + // + // We don't synthesize anything but RGB 24 and UYVY. + // + Status = STATUS_INVALID_PARAMETER; + + if (NT_SUCCESS (Status) && !m_ImageSynth) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + + } + + if (NT_SUCCESS (Status)) { + // + // If everything has succeeded thus far, set the capture sink. + // + m_CaptureSink = CaptureSink; + + } else { + // + // If anything failed in here, we release the resources we've + // acquired. + // + ReleaseHardwareResources (); + } + + } else { + + // + // TODO: Better status code? + // + Status = STATUS_SHARING_VIOLATION; + + } + + return Status; + +} + +/*************************************************/ + + +void +CCaptureDevice:: +ReleaseHardwareResources ( + ) + +/*++ + +Routine Description: + + Release hardware resources. This should only be called by + an object which has acquired them. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + // + // Blow away the image synth. + // + if (m_ImageSynth) { + delete m_ImageSynth; + m_ImageSynth = NULL; + + } + + m_VideoInfoHeader = NULL; + m_CaptureSink = NULL; + + // + // Release our "lock" on hardware resources. This will allow another + // pin (perhaps in another graph) to acquire them. + // + InterlockedExchange ( + &m_PinsWithResources, + 0 + ); + +} + +/*************************************************/ + + +NTSTATUS +CCaptureDevice:: +Start ( + ) + +/*++ + +Routine Description: + + Start the capture device based on the video info header we were told + about when resources were acquired. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + m_LastMappingsCompleted = 0; + m_InterruptTime = 0; + + return + m_HardwareSimulation -> Start ( + m_ImageSynth, + m_VideoInfoHeader -> AvgTimePerFrame, + m_VideoInfoHeader -> bmiHeader.biWidth, + ABS (m_VideoInfoHeader -> bmiHeader.biHeight), + m_VideoInfoHeader -> bmiHeader.biSizeImage + ); + + +} + +/*************************************************/ + + +NTSTATUS +CCaptureDevice:: +Pause ( + IN BOOLEAN Pausing + ) + +/*++ + +Routine Description: + + Pause or unpause the hardware simulation. This is an effective start + or stop without resetting counters and formats. Note that this can + only be called to transition from started -> paused -> started. Calling + this without starting the hardware with Start() does nothing. + +Arguments: + + Pausing - + An indicatation of whether we are pausing or unpausing + + TRUE - + Pause the hardware simulation + + FALSE - + Unpause the hardware simulation + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + return + m_HardwareSimulation -> Pause ( + Pausing + ); + +} + +/*************************************************/ + + +NTSTATUS +CCaptureDevice:: +Stop ( + ) + +/*++ + +Routine Description: + + Stop the capture device. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + return + m_HardwareSimulation -> Stop (); + +} + +/*************************************************/ + + +ULONG +CCaptureDevice:: +ProgramScatterGatherMappings ( + IN PKSSTREAM_POINTER Clone, + IN PUCHAR *Buffer, + IN PKSMAPPING Mappings, + IN ULONG MappingsCount + ) + +/*++ + +Routine Description: + + Program the scatter / gather mappings for the "fake" hardware. + +Arguments: + + Buffer - + Points to a pointer to the virtual address of the topmost + scatter / gather chunk. The pointer will be updated as the + device "programs" mappings. Reason for this is that we get + the physical addresses and sizes, but must calculate the virtual + addresses... This is used as scratch space for that. + + Mappings - + An array of mappings to program + + MappingsCount - + The count of mappings in the array + +Return Value: + + The number of mappings successfully programmed + +--*/ + +{ + + PAGED_CODE(); + + + + return + m_HardwareSimulation -> ProgramScatterGatherMappings ( + Clone, + Buffer, + Mappings, + MappingsCount, + sizeof (KSMAPPING) + ); + +} + +/************************************************************************* + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +ULONG +CCaptureDevice:: +QueryInterruptTime ( + ) + +/*++ + +Routine Description: + + Return the number of frame intervals that have elapsed since the + start of the device. This will be the frame number. + +Arguments: + + None + +Return Value: + + The interrupt time of the device (the number of frame intervals that + have elapsed since the start of the device). + +--*/ + +{ + + return m_InterruptTime; + +} + +/*************************************************/ + + +void +CCaptureDevice:: +Interrupt ( + ) + +/*++ + +Routine Description: + + This is the "faked" interrupt service routine for this device. It + is called at dispatch level by the hardware simulation. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + + m_InterruptTime++; + + // + // Realistically, we'd do some hardware manipulation here and then queue + // a DPC. Since this is fake hardware, we do what's necessary here. This + // is pretty much what the DPC would look like short of the access + // of hardware registers (ReadNumberOfMappingsCompleted) which would likely + // be done in the ISR. + // + ULONG NumMappingsCompleted = + m_HardwareSimulation -> ReadNumberOfMappingsCompleted (); + + // + // Inform the capture sink that a given number of scatter / gather + // mappings have completed. + // + m_CaptureSink -> CompleteMappings ( + NumMappingsCompleted - m_LastMappingsCompleted + ); + + m_LastMappingsCompleted = NumMappingsCompleted; + +} + +/************************************************************************** + + DESCRIPTOR AND DISPATCH LAYOUT + +**************************************************************************/ + +// +// CaptureFilterDescriptor: +// +// The filter descriptor for the capture device. +DEFINE_KSFILTER_DESCRIPTOR_TABLE (FilterDescriptors) { + &CaptureFilterDescriptor +}; + +// +// CaptureDeviceDispatch: +// +// This is the dispatch table for the capture device. Plug and play +// notifications as well as power management notifications are dispatched +// through this table. +// +const +KSDEVICE_DISPATCH +CaptureDeviceDispatch = { + CCaptureDevice::DispatchCreate, // Pnp Add Device + CCaptureDevice::DispatchPnpStart, // Pnp Start + NULL, // Post-Start + NULL, // Pnp Query Stop + NULL, // Pnp Cancel Stop + CCaptureDevice::DispatchPnpStop, // Pnp Stop + NULL, // Pnp Query Remove + NULL, // Pnp Cancel Remove + NULL, // Pnp Remove + NULL, // Pnp Query Capabilities + NULL, // Pnp Surprise Removal + NULL, // Power Query Power + NULL, // Power Set Power + NULL // Pnp Query Interface +}; + +// +// CaptureDeviceDescriptor: +// +// This is the device descriptor for the capture device. It points to the +// dispatch table and contains a list of filter descriptors that describe +// filter-types that this device supports. Note that the filter-descriptors +// can be created dynamically and the factories created via +// KsCreateFilterFactory as well. +// +const +KSDEVICE_DESCRIPTOR +CaptureDeviceDescriptor = { + &CaptureDeviceDispatch, + 0, + NULL +}; + +/************************************************************************** + + INITIALIZATION CODE + +**************************************************************************/ + + +extern "C" DRIVER_INITIALIZE DriverEntry; + +extern "C" +NTSTATUS +DriverEntry ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + Driver entry point. Pass off control to the AVStream initialization + function (KsInitializeDriver) and return the status code from it. + +Arguments: + + DriverObject - + The WDM driver object for our driver + + RegistryPath - + The registry path for our registry info + +Return Value: + + As from KsInitializeDriver + +--*/ + +{ + // + // Simply pass the device descriptor and parameters off to AVStream + // to initialize us. This will cause filter factories to be set up + // at add & start. Everything is done based on the descriptors passed + // here. + // + return + KsInitializeDriver ( + DriverObject, + RegistryPath, + &CaptureDeviceDescriptor + ); + +} diff --git a/avstream/avshws/device.h b/avstream/avshws/device.h new file mode 100644 index 00000000..2bd9899d --- /dev/null +++ b/avstream/avshws/device.h @@ -0,0 +1,294 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + device.h + + Abstract: + + The header for the device level of the simulated hardware. This is + not actually the hardware simulation itself. The hardware simulation + is contained in hwsim.*, image.*. + + History: + + created 3/9/2001 + +**************************************************************************/ + +class CCaptureDevice : + public IHardwareSink { + +private: + + // + // The AVStream device we're associated with. + // + PKSDEVICE m_Device; + + // + // Number of pins with resources acquired. This is used as a locking + // mechanism for resource acquisition on the device. + // + LONG m_PinsWithResources; + + // + // Since we don't have physical hardware, this provides the hardware + // simulation. m_HardwareSimulation provides the fake ISR, fake DPC, + // etc... m_ImageSynth provides RGB24 and UYVY image synthesis and + // overlay in software. + // + CHardwareSimulation *m_HardwareSimulation; + CImageSynthesizer *m_ImageSynth; + + // + // The number of ISR's that have occurred since capture started. + // + ULONG m_InterruptTime; + + // + // The last reading of mappings completed. + // + ULONG m_LastMappingsCompleted; + + // + // The Dma adapter object we acquired through IoGetDmaAdapter() during + // Pnp start. This must be initialized with AVStream in order to perform + // Dma directly into the capture buffers. + // + PADAPTER_OBJECT m_DmaAdapterObject; + + // + // The number of map registers returned from IoGetDmaAdapter(). + // + ULONG m_NumberOfMapRegisters; + + // + // The capture sink. When we complete scatter / gather mappings, we + // notify the capture sink. + // + ICaptureSink *m_CaptureSink; + + // + // The video info header we're basing hardware settings on. The pin + // provides this to us when acquiring resources and must guarantee its + // stability until resources are released. + // + PKS_VIDEOINFOHEADER m_VideoInfoHeader; + + // + // Cleanup(): + // + // This is the free callback for the bagged capture device. Not providing + // one will call ExFreePool, which is not what we want for a constructed + // C++ object. This simply deletes the capture device. + // + static + void + Cleanup ( + IN CCaptureDevice *CapDevice + ) + { + delete CapDevice; + } + + // + // PnpStart(): + // + // This is the Pnp start routine for our simulated hardware. Note that + // DispatchStart bridges to here in the context of the CCaptureDevice. + // + NTSTATUS + PnpStart ( + IN PCM_RESOURCE_LIST TranslatedResourceList, + IN PCM_RESOURCE_LIST UntranslatedResourceList + ); + + // + // PnpStop(): + // + // This is the Pnp stop routine for our simulated hardware. Note that + // DispatchStop bridges to here in the context of the CCaptureDevice. + // + void + PnpStop ( + ); + +public: + + // + // CCaptureDevice(): + // + // The capture device class constructor. Since everything should have + // been zero'ed by the new operator, don't bother setting anything to + // zero or NULL. Only initialize non-NULL, non-0 fields. + // + CCaptureDevice ( + IN PKSDEVICE Device + ) : + m_Device (Device) + { + } + + // + // ~CCaptureDevice(): + // + // The capture device destructor. + // + ~CCaptureDevice ( + ) + { + } + + // + // DispatchCreate(): + // + // This is the Add Device dispatch for the capture device. It creates + // the CCaptureDevice and associates it with the device via the bag. + // + static + NTSTATUS + DispatchCreate ( + IN PKSDEVICE Device + ); + // + // DispatchPnpStart(): + // + // This is the Pnp Start dispatch for the capture device. It simply + // bridges to PnpStart() in the context of the CCaptureDevice. + // + static + NTSTATUS + DispatchPnpStart ( + IN PKSDEVICE Device, + IN PIRP Irp, + IN PCM_RESOURCE_LIST TranslatedResourceList, + IN PCM_RESOURCE_LIST UntranslatedResourceList + ) + { + return + (reinterpret_cast (Device -> Context)) -> + PnpStart ( + TranslatedResourceList, + UntranslatedResourceList + ); + } + + // + // DispatchPnpStop(): + // + // This is the Pnp stop dispatch for the capture device. It simply + // bridges to PnpStop() in the context of the CCaptureDevice. + // + static + void + DispatchPnpStop ( + IN PKSDEVICE Device, + IN PIRP Irp + ) + { + return + (reinterpret_cast (Device -> Context)) -> + PnpStop ( + ); + } + + // + // AcquireHardwareResources(): + // + // Called to acquire hardware resources for the device based on a given + // video info header. This will fail if another object has already + // acquired hardware resources since we emulate a single capture + // device. + // + NTSTATUS + AcquireHardwareResources ( + IN ICaptureSink *CaptureSink, + IN PKS_VIDEOINFOHEADER VideoInfoHeader + ); + + // + // ReleaseHardwareResources(): + // + // Called to release hardware resources for the device. + // + void + ReleaseHardwareResources ( + ); + + // + // Start(): + // + // Called to start the hardware simulation. This causes us to simulate + // interrupts, simulate filling buffers with synthesized data, etc... + // + NTSTATUS + Start ( + ); + + // + // Pause(): + // + // Called to pause or unpause the hardware simulation. This will be + // indentical to a start or stop but it will not reset formats and + // counters. + // + NTSTATUS + Pause ( + IN BOOLEAN Pausing + ); + + // + // Stop(): + // + // Called to stop the hardware simulation. This causes interrupts to + // stop issuing. When this call returns, the "fake" hardware has + // stopped accessing all s/g buffers, etc... + // + NTSTATUS + Stop ( + ); + + // + // ProgramScatterGatherMappings(): + // + // Called to program the hardware simulation's scatter / gather table. + // This synchronizes with the "fake" ISR and hardware simulation via + // a spinlock. + // + ULONG + ProgramScatterGatherMappings ( + IN PKSSTREAM_POINTER Clone, + IN PUCHAR *Buffer, + IN PKSMAPPING Mappings, + IN ULONG MappingsCount + ); + + // + // QueryInterruptTime(): + // + // Determine the frame number that this frame corresponds to. + // + ULONG + QueryInterruptTime ( + ); + + // + // IHardwareSink::Interrupt(): + // + // The interrupt service routine as called through the hardware sink + // interface. The "fake" hardware uses this method to inform the device + // of a "fake" ISR. The routine is called at dispatch level and must + // be in locked code. + // + virtual + void + Interrupt ( + ); + + LONG GetDroppedFrameCount(){return m_HardwareSimulation->GetSkippedFrameCount();}; +}; diff --git a/avstream/avshws/filter.cpp b/avstream/avshws/filter.cpp new file mode 100644 index 00000000..be0012cb --- /dev/null +++ b/avstream/avshws/filter.cpp @@ -0,0 +1,203 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + filter.cpp + + Abstract: + + This file contains the filter level implementation for the + capture filter. + + History: + + created 3/12/2001 + +**************************************************************************/ + +#include "avshws.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +NTSTATUS +CCaptureFilter:: +DispatchCreate ( + IN PKSFILTER Filter, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the creation dispatch for the capture filter. It creates + the CCaptureFilter object, associates it with the AVStream filter + object, and bag the CCaptureFilter for later cleanup. + +Arguments: + + Filter - + The AVStream filter being created + + Irp - + The creation Irp + +Return Value: + + Success / failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + CCaptureFilter *CapFilter = new (NonPagedPool) CCaptureFilter (Filter); + + if (!CapFilter) { + // + // Return failure if we couldn't create the filter. + // + Status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + // + // Add the item to the object bag if we we were successful. + // Whenever the filter closes, the bag is cleaned up and we will be + // freed. + // + Status = KsAddItemToObjectBag ( + Filter -> Bag, + reinterpret_cast (CapFilter), + reinterpret_cast (CCaptureFilter::Cleanup) + ); + + if (!NT_SUCCESS (Status)) { + delete CapFilter; + } else { + Filter -> Context = reinterpret_cast (CapFilter); + } + + } + + return Status; + +} + +/************************************************************************** + + DESCRIPTOR AND DISPATCH LAYOUT + +**************************************************************************/ + +GUID g_PINNAME_VIDEO_CAPTURE = {STATIC_PINNAME_VIDEO_CAPTURE}; + +// +// CaptureFilterCategories: +// +// The list of category GUIDs for the capture filter. +// +const +GUID +CaptureFilterCategories [CAPTURE_FILTER_CATEGORIES_COUNT] = { + STATICGUIDOF (KSCATEGORY_VIDEO), + STATICGUIDOF (KSCATEGORY_CAPTURE), + STATICGUIDOF (KSCATEGORY_VIDEO_CAMERA) +}; + +// +// CaptureFilterPinDescriptors: +// +// The list of pin descriptors on the capture filter. +// +const +KSPIN_DESCRIPTOR_EX +CaptureFilterPinDescriptors [CAPTURE_FILTER_PIN_COUNT] = { + // + // Video Capture Pin + // + { + &CapturePinDispatch, + NULL, + { + 0, // Interfaces (NULL, 0 == default) + NULL, + 0, // Mediums (NULL, 0 == default) + NULL, + SIZEOF_ARRAY(CapturePinDataRanges),// Range Count + CapturePinDataRanges, // Ranges + KSPIN_DATAFLOW_OUT, // Dataflow + KSPIN_COMMUNICATION_BOTH, // Communication + &PIN_CATEGORY_CAPTURE, // Category + &g_PINNAME_VIDEO_CAPTURE, // Name + 0 // Reserved + }, +#ifdef _X86_ + KSPIN_FLAG_GENERATE_MAPPINGS | // Pin Flags +#endif + KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY, + 1, // Instances Possible + 1, // Instances Necessary + &CapturePinAllocatorFraming, // Allocator Framing + reinterpret_cast + (CCapturePin::IntersectHandler) + } +}; + +// +// CaptureFilterDispatch: +// +// This is the dispatch table for the capture filter. It provides notification +// of creation, closure, processing (for filter-centrics, not for the capture +// filter), and resets (for filter-centrics, not for the capture filter). +// +const +KSFILTER_DISPATCH +CaptureFilterDispatch = { + CCaptureFilter::DispatchCreate, // Filter Create + NULL, // Filter Close + NULL, // Filter Process + NULL // Filter Reset +}; + + +// +// CaptureFilterDescription: +// +// The descriptor for the capture filter. We don't specify any topology +// since there's only one pin on the filter. Realistically, there would +// be some topological relationships here because there would be input +// pins from crossbars and the like. +// +const +KSFILTER_DESCRIPTOR +CaptureFilterDescriptor = { + &CaptureFilterDispatch, // Dispatch Table + NULL, // Automation Table + KSFILTER_DESCRIPTOR_VERSION, // Version + 0, // Flags + &KSNAME_Filter, // Reference GUID + DEFINE_KSFILTER_PIN_DESCRIPTORS (CaptureFilterPinDescriptors), + DEFINE_KSFILTER_CATEGORIES (CaptureFilterCategories), + 0, + sizeof (KSNODE_DESCRIPTOR), + NULL, + 0, + NULL, + NULL // Component ID +}; \ No newline at end of file diff --git a/avstream/avshws/filter.h b/avstream/avshws/filter.h new file mode 100644 index 00000000..e5cb9895 --- /dev/null +++ b/avstream/avshws/filter.h @@ -0,0 +1,88 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + filter.h + + Abstract: + + This file contains the filter level header for the capture filter. + + History: + + created 3/12/2001 + +**************************************************************************/ + +class CCaptureFilter { + +private: + + // + // The AVStream filter object associated with this CCaptureFilter. + // + PKSFILTER m_Filter; + + // + // Cleanup(): + // + // This is the bag cleanup callback for the CCaptureFilter. Not providing + // one would cause ExFreePool to be used. This is not good for C++ + // constructed objects. We simply delete the object here. + // + static + void + Cleanup ( + IN CCaptureFilter *CapFilter + ) + { + delete CapFilter; + } + +public: + + // + // CCaptureFilter(): + // + // The capture filter object constructor. Since the new operator will + // have zeroed the memory, do not bother initializing any NULL or 0 + // fields. Only initialize non-NULL, non-0 fields. + // + CCaptureFilter ( + IN PKSFILTER Filter + ) : + m_Filter (Filter) + { + } + + // + // ~CCaptureFilter(): + // + // The capture filter destructor. + // + ~CCaptureFilter ( + ) + { + } + + // + // DispatchCreate(): + // + // This is the filter creation dispatch for the capture filter. It + // creates the CCaptureFilter object, associates it with the AVStream + // object, and bags it for easy cleanup later. + // + static + NTSTATUS + DispatchCreate ( + IN PKSFILTER Filter, + IN PIRP Irp + ); + +}; + + diff --git a/avstream/avshws/hwsim.cpp b/avstream/avshws/hwsim.cpp new file mode 100644 index 00000000..8966f7a1 --- /dev/null +++ b/avstream/avshws/hwsim.cpp @@ -0,0 +1,868 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + hwsim.cpp + + Abstract: + + This file contains the hardware simulation. It fakes "DMA" transfers, + scatter gather mapping handling, ISR's, etc... The ISR routine in + here will be called when an ISR would be generated by the fake hardware + and it will directly call into the device level ISR for more accurate + simulation. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#include "avshws.h" + + +/*************************************************/ +KDEFERRED_ROUTINE SimulatedInterrupt; + +void +SimulatedInterrupt ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArg1, + IN PVOID SystemArg2 + ) +{ + CHardwareSimulation* HardwareSim = (CHardwareSimulation*)DeferredContext; + + if (HardwareSim) + { + HardwareSim -> FakeHardware (); + } +} + + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + + +CHardwareSimulation:: +CHardwareSimulation ( + IN IHardwareSink *HardwareSink + ) : + m_HardwareSink (HardwareSink), + m_ScatterGatherMappingsMax (SCATTER_GATHER_MAPPINGS_MAX) + +/*++ + +Routine Description: + + Construct a hardware simulation + +Arguments: + + HardwareSink - + The hardware sink interface. This is used to trigger + fake interrupt service routines from. + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + // + // Initialize the DPC's, timer's, and locks necessary to simulate + // this capture hardware. + // + KeInitializeDpc ( + &m_IsrFakeDpc, + SimulatedInterrupt, + this + ); + + KeInitializeEvent ( + &m_HardwareEvent, + SynchronizationEvent, + FALSE + ); + + KeInitializeTimer (&m_IsrTimer); + + KeInitializeSpinLock (&m_ListLock); + +} + +/*************************************************/ + + +CHardwareSimulation * +CHardwareSimulation:: +Initialize ( + IN KSOBJECT_BAG Bag, + IN IHardwareSink *HardwareSink + ) + +/*++ + +Routine Description: + + Initialize the hardware simulation + +Arguments: + + HardwareSink - + The hardware sink interface. This is what ISR's will be + triggered through. + +Return Value: + + A fully initialized hardware simulation or NULL if the simulation + could not be initialized. + +--*/ + +{ + + PAGED_CODE(); + + CHardwareSimulation *HwSim = + new (NonPagedPool) CHardwareSimulation (HardwareSink); + + return HwSim; + +} + +/*************************************************/ + + +NTSTATUS +CHardwareSimulation:: +Start ( + IN CImageSynthesizer *ImageSynth, + IN LONGLONG TimePerFrame, + IN ULONG Width, + IN ULONG Height, + IN ULONG ImageSize + ) + +/*++ + +Routine Description: + + Start the hardware simulation. This will kick the interrupts on, + begin issuing DPC's, filling in capture information, etc... + We keep track of starvation starting at this point. + +Arguments: + + ImageSynth - + The image synthesizer to use to generate pictures to display + on the capture buffer. + + TimePerFrame - + The time per frame... we issue interrupts this often. + + Width - + The image width + + Height - + The image height + + ImageSize - + The size of the image. We allocate a temporary scratch buffer + based on this size to fake hardware. + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + m_ImageSynth = ImageSynth; + m_TimePerFrame = TimePerFrame; + m_ImageSize = ImageSize; + m_Height = Height; + m_Width = Width; + + InitializeListHead (&m_ScatterGatherMappings); + m_NumMappingsCompleted = 0; + m_ScatterGatherMappingsQueued = 0; + m_NumFramesSkipped = 0; + m_InterruptTime = 0; + + KeQuerySystemTime (&m_StartTime); + + // + // Allocate a scratch buffer for the synthesizer. + // + m_SynthesisBuffer = reinterpret_cast ( + ExAllocatePoolWithTag ( + NonPagedPool, + m_ImageSize, + AVSHWS_POOLTAG + ) + ); + + if (!m_SynthesisBuffer) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + // + // If everything is ok, start issuing interrupts. + // + if (NT_SUCCESS (Status)) { + + // + // Initialize the entry lookaside. + // + ExInitializeNPagedLookasideList ( + &m_ScatterGatherLookaside, + NULL, + NULL, + 0, + sizeof (SCATTER_GATHER_ENTRY), + 'nEGS', + 0 + ); + + // + // Set up the synthesizer with the width, height, and scratch buffer. + // + m_ImageSynth -> SetImageSize (m_Width, m_Height); + m_ImageSynth -> SetBuffer (m_SynthesisBuffer); + + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + m_TimePerFrame; + + m_HardwareState = HardwareRunning; + KeSetTimer (&m_IsrTimer, NextTime, &m_IsrFakeDpc); + + } + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CHardwareSimulation:: +Pause ( + BOOLEAN Pausing + ) + +/*++ + +Routine Description: + + Pause the hardware simulation... When the hardware simulation is told + to pause, it stops issuing interrupts, etc... but it does not reset + the counters + +Arguments: + + Pausing - + Indicates whether the hardware is pausing or not. + + TRUE - + Pause the hardware + + FALSE - + Unpause the hardware from a previous pause + + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + if (Pausing && m_HardwareState == HardwareRunning) { + // + // If we were running, stop completing mappings, etc... + // + m_StopHardware = TRUE; + + KeWaitForSingleObject ( + &m_HardwareEvent, + Suspended, + KernelMode, + FALSE, + NULL + ); + + NT_ASSERT (m_StopHardware == FALSE); + + m_HardwareState = HardwarePaused; + + } else if (!Pausing && m_HardwareState == HardwarePaused) { + + // + // For unpausing the hardware, we need to compute the relative time + // and restart interrupts. + // + LARGE_INTEGER UnpauseTime; + + KeQuerySystemTime (&UnpauseTime); + m_InterruptTime = (ULONG) ( + (UnpauseTime.QuadPart - m_StartTime.QuadPart) / + m_TimePerFrame + ); + + UnpauseTime.QuadPart = m_StartTime.QuadPart + + (m_InterruptTime + 1) * m_TimePerFrame; + + m_HardwareState = HardwareRunning; + KeSetTimer (&m_IsrTimer, UnpauseTime, &m_IsrFakeDpc); + + } + + return STATUS_SUCCESS; + +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +NTSTATUS +CHardwareSimulation:: +Stop ( + ) + +/*++ + +Routine Description: + + Stop the hardware simulation.... Wait until the hardware simulation + has successfully stopped and then return. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + KIRQL Irql; + // + // If the hardware is told to stop while it's running, we need to + // halt the interrupts first. If we're already paused, this has + // already been done. + // + if (m_HardwareState == HardwareRunning) { + + m_StopHardware = TRUE; + + KeWaitForSingleObject ( + &m_HardwareEvent, + Suspended, + KernelMode, + FALSE, + NULL + ); + + NT_ASSERT (m_StopHardware == FALSE); + + } + + m_HardwareState = HardwareStopped; + + // + // The image synthesizer may still be around. Just for safety's + // sake, NULL out the image synthesis buffer and toast it. + // + m_ImageSynth -> SetBuffer (NULL); + + if (m_SynthesisBuffer) { + ExFreePool (m_SynthesisBuffer); + m_SynthesisBuffer = NULL; + } + + // + // Protect the S/G list + // + KeAcquireSpinLock (&m_ListLock, &Irql); + // + // Free S/G buffer + // + // + while (m_ScatterGatherMappingsQueued > 0) { + LIST_ENTRY *listEntry = RemoveHeadList (&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + // + // Release the scatter / gather entry back to our lookaside. + // + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast (SGEntry) + ); + } + + m_NumMappingsCompleted = 0; + m_ScatterGatherBytesQueued = 0; + // + // Delete the scatter / gather lookaside for this run. + // + ExDeleteNPagedLookasideList (&m_ScatterGatherLookaside); + + KeReleaseSpinLock (&m_ListLock, Irql); + + return STATUS_SUCCESS; + +} + + +ULONG +CHardwareSimulation:: +ReadNumberOfMappingsCompleted ( + ) + +/*++ + +Routine Description: + + Read the number of scatter / gather mappings which have been + completed (TOTAL NUMBER) since the last reset of the simulated + hardware + +Arguments: + + None + +Return Value: + + Total number of completed mappings. + +--*/ + +{ + + // + // Don't care if this is being updated this moment in the DPC... I only + // need a number to return which isn't too great (too small is ok). + // In real hardware, this wouldn't be done this way anyway. + // + return m_NumMappingsCompleted; + +} + +/*************************************************/ + + +ULONG +CHardwareSimulation:: +ProgramScatterGatherMappings ( + IN PKSSTREAM_POINTER Clone, + IN PUCHAR *Buffer, + IN PKSMAPPING Mappings, + IN ULONG MappingsCount, + IN ULONG MappingStride + ) + +/*++ + +Routine Description: + + Program the scatter gather mapping list. This shoves a bunch of + entries on a list for access during the fake interrupt. Note that + we have physical addresses here only for simulation. We really + access via the virtual address.... although we chunk it into multiple + buffers to more realistically simulate S/G + +Arguments: + + Buffer - + The virtual address of the buffer mapped by the mapping list + + Mappings - + The KSMAPPINGS array corresponding to the buffer + + MappingsCount - + The number of mappings in the mappings array + + MappingStride - + The mapping stride used in initialization of AVStream DMA + +Return Value: + + Number of mappings actually inserted. + +--*/ + +{ + + KIRQL Irql; + + ULONG MappingsInserted = 0; + + // + // Protect our S/G list with a spinlock. + // + KeAcquireSpinLock (&m_ListLock, &Irql); + + // + // Loop through the scatter / gather list and break the buffer up into + // chunks equal to the scatter / gather mappings. Stuff the virtual + // addresses of these chunks on a list somewhere. We update the buffer + // pointer the caller passes as a more convenient way of doing this. + // + // If I could just remap physical in the list to virtual easily here, + // I wouldn't need to do it. + // +#if !defined(_X86_) + do + { + PSCATTER_GATHER_ENTRY Entry = + reinterpret_cast ( + ExAllocateFromNPagedLookasideList ( + &m_ScatterGatherLookaside + ) + ); + + if (!Entry) { + break; + } + Entry -> Virtual = *Buffer; + Entry -> ByteCount = MappingsCount; + Entry -> CloneEntry = Clone; + + // + // Move forward a specific number of bytes in chunking this into + // mapping sized va buffers. + // + *Buffer += MappingsCount; + Mappings = reinterpret_cast ( + (reinterpret_cast (Mappings) + MappingStride) + ); + + InsertTailList (&m_ScatterGatherMappings, &(Entry -> ListEntry)); + MappingsInserted = MappingsCount; + m_ScatterGatherMappingsQueued++; + m_ScatterGatherBytesQueued += MappingsCount; + + } + while(FALSE); + +#else + for (ULONG MappingNum = 0; + MappingNum < MappingsCount && + m_ScatterGatherMappingsQueued < m_ScatterGatherMappingsMax; + MappingNum++) { + + PSCATTER_GATHER_ENTRY Entry = + reinterpret_cast ( + ExAllocateFromNPagedLookasideList ( + &m_ScatterGatherLookaside + ) + ); + + if (!Entry) { + break; + } + + Entry -> Virtual = *Buffer; + Entry -> ByteCount = Mappings -> ByteCount; + + // + // Move forward a specific number of bytes in chunking this into + // mapping sized va buffers. + // + *Buffer += Entry -> ByteCount; + Mappings = reinterpret_cast ( + (reinterpret_cast (Mappings) + MappingStride) + ); + + InsertTailList (&m_ScatterGatherMappings, &(Entry -> ListEntry)); + MappingsInserted++; + m_ScatterGatherMappingsQueued++; + m_ScatterGatherBytesQueued += Entry -> ByteCount; + + } +#endif + + KeReleaseSpinLock (&m_ListLock, Irql); + + return MappingsInserted; + +} + +/*************************************************/ + + +NTSTATUS +CHardwareSimulation:: +FillScatterGatherBuffers ( + ) + +/*++ + +Routine Description: + + The hardware has synthesized a buffer in scratch space and we're to + fill scatter / gather buffers. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + + // + // We're using this list lock to protect our scatter / gather lists instead + // of some hardware mechanism / KeSynchronizeExecution / whatever. + // + KeAcquireSpinLockAtDpcLevel (&m_ListLock); + + PUCHAR Buffer = reinterpret_cast (m_SynthesisBuffer); + ULONG BufferRemaining = m_ImageSize; + + // + // For simplification, if there aren't enough scatter / gather buffers + // queued, we don't partially fill the ones that are available. We just + // skip the frame and consider it starvation. + // + // This could be enforced by only programming scatter / gather mappings + // for a buffer if all of them fit in the table also... + // + while (BufferRemaining && + m_ScatterGatherMappingsQueued > 0 && + m_ScatterGatherBytesQueued >= BufferRemaining) { + + LIST_ENTRY *listEntry = RemoveHeadList (&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + // + // Since we're software, we'll be accessing this by virtual address... + // + ULONG BytesToCopy = + (BufferRemaining < SGEntry -> ByteCount) ? + BufferRemaining : + SGEntry -> ByteCount; + + LONG Width = m_Width*(m_ImageSynth->GetBytesPerPixel()); + + LONG Stride = Width; + if(SGEntry->CloneEntry->StreamHeader->Size >= sizeof(KSSTREAM_HEADER)+sizeof(KS_FRAME_INFO)) + { + PKS_FRAME_INFO FrameInfo = reinterpret_cast (SGEntry->CloneEntry->StreamHeader+1); + if(FrameInfo->lSurfacePitch != 0) + { + Stride = FrameInfo->lSurfacePitch; + if(FrameInfo->lSurfacePitch < 0) + { + Stride = -Stride; + } + } + } + + for(ULONG y = 0; y < m_Height; y++) + { + RtlCopyMemory((SGEntry->Virtual+(ULONG)Stride*y), Buffer, Width); + Buffer += Width; + BytesToCopy -= Width; + BufferRemaining -= Width; + } + + m_NumMappingsCompleted++; + m_ScatterGatherBytesQueued -= SGEntry -> ByteCount; + + // + // Release the scatter / gather entry back to our lookaside. + // + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast (SGEntry) + ); + + } + + KeReleaseSpinLockFromDpcLevel (&m_ListLock); + + if (BufferRemaining) return STATUS_INSUFFICIENT_RESOURCES; + else return STATUS_SUCCESS; + +} + +/*************************************************/ + + +void +CHardwareSimulation:: +FakeHardware ( + ) + +/*++ + +Routine Description: + + Simulate an interrupt and what the hardware would have done in the + time since the previous interrupt. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + + m_InterruptTime++; + + // + // The hardware can be in a pause state in which case, it issues interrupts + // but does not complete mappings. In this case, don't bother synthesizing + // a frame and doing the work of looking through the mappings table. + // + if (m_HardwareState == HardwareRunning) { + + // + // Generate a "time stamp" just to overlay it onto the capture image. + // It makes it more exciting than bars that do nothing. + // + LONGLONG PtsRel = ((m_InterruptTime + 1) * m_TimePerFrame); + + ULONG Min = (ULONG)(PtsRel / 600000000); + ULONG RemMin = (ULONG)(PtsRel % 600000000); + ULONG Sec = (ULONG)(RemMin / 10000000); + ULONG RemSec = (ULONG)(RemMin % 10000000); + ULONG Hund = (ULONG)(RemSec / 100000); + + // + // Synthesize a buffer in scratch space. + // + m_ImageSynth -> SynthesizeBars (); + + CHAR Text [256]; + Text[0] = '\0'; + (void) RtlStringCbPrintfA(Text, sizeof(Text), "%ld:%02ld.%02ld", Min, Sec, Hund); + + // + // Overlay a clock onto the scratch space image. + // + m_ImageSynth -> OverlayText ( + POSITION_CENTER, + (m_Height - 28), + 1, + Text, + BLACK, + WHITE + ); + + // + // Overlay a counter of skipped frames onto the scratch image. + // + (void) RtlStringCbPrintfA(Text, sizeof(Text), "Skipped: %ld", m_NumFramesSkipped); + m_ImageSynth -> OverlayText ( + 10, + 10, + 1, + Text, + TRANSPARENT, + BLUE + ); + + // + // Fill scatter gather buffers + // + if (!NT_SUCCESS (FillScatterGatherBuffers ())) { + InterlockedIncrement (PLONG (&m_NumFramesSkipped)); + } + + } + + // + // Issue an interrupt to our hardware sink. This is a "fake" interrupt. + // It will occur at DISPATCH_LEVEL. + // + m_HardwareSink -> Interrupt (); + + // + // Reschedule the timer if the hardware isn't being stopped. + // + if (!m_StopHardware) { + + // + // Reschedule the timer for the next interrupt time. + // + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + + (m_TimePerFrame * (m_InterruptTime + 1)); + + KeSetTimer (&m_IsrTimer, NextTime, &m_IsrFakeDpc); + + } else { + // + // If someone is waiting on the hardware to stop, raise the stop + // event and clear the flag. + // + m_StopHardware = FALSE; + KeSetEvent (&m_HardwareEvent, IO_NO_INCREMENT, FALSE); + } + +} diff --git a/avstream/avshws/hwsim.h b/avstream/avshws/hwsim.h new file mode 100644 index 00000000..21ddcb41 --- /dev/null +++ b/avstream/avshws/hwsim.h @@ -0,0 +1,290 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + hwsim.cpp + + Abstract: + + This file is the hardware simulation header. + + The simulation fakes "DMA" transfers, scatter gather mapping handling, + ISR's, etc... The ISR routine in here will be called when an ISR + would be generated by the fake hardware and it will directly call into + the device level ISR for more accurate simulation. + + History: + + created 3/9/2001 + +**************************************************************************/ + +// +// SCATTER_GATHER_MAPPINGS_MAX: +// +// The maximum number of entries in the hardware's scatter/gather list. I +// am making this so large for a few reasons: +// +// 1) we're faking this with uncompressed surfaces -- +// these are large buffers which will map to a lot of s/g entries +// 2) the fake hardware implementation requires at least one frame's +// worth of s/g entries to generate a frame +// +#define SCATTER_GATHER_MAPPINGS_MAX 128 + +// +// SCATTER_GATHER_ENTRY: +// +// This structure is used to keep the scatter gather table for the fake +// hardware as a doubly linked list. +// +typedef struct _SCATTER_GATHER_ENTRY { + + LIST_ENTRY ListEntry; + PKSSTREAM_POINTER CloneEntry; + PUCHAR Virtual; + ULONG ByteCount; + +} SCATTER_GATHER_ENTRY, *PSCATTER_GATHER_ENTRY; + +// +// CHardwareSimulation: +// +// The hardware simulation class. +// +class CHardwareSimulation { + +private: + + // + // The image synthesizer. This is a piece of code which actually draws + // the requested images. + // + CImageSynthesizer *m_ImageSynth; + + // + // The synthesis buffer. This is a private buffer we use to generate the + // capture image in. The fake "scatter / gather" mappings are filled + // in from this buffer during each interrupt. + // + PUCHAR m_SynthesisBuffer; + + // + // Key information regarding the frames we generate. + // + LONGLONG m_TimePerFrame; + ULONG m_Width; + ULONG m_Height; + ULONG m_ImageSize; + + // + // Scatter gather mappings for the simulated hardware. + // + KSPIN_LOCK m_ListLock; + LIST_ENTRY m_ScatterGatherMappings; + + // + // Lookaside for memory for the scatter / gather entries on the scatter / + // gather list. + // + NPAGED_LOOKASIDE_LIST m_ScatterGatherLookaside; + + // + // The current state of the fake hardware. + // + HARDWARE_STATE m_HardwareState; + + // + // The pause / stop hardware flag and event. + // + BOOLEAN m_StopHardware; + KEVENT m_HardwareEvent; + + // + // Maximum number of scatter / gather mappins in the s/g table of the + // fake hardware. + // + ULONG m_ScatterGatherMappingsMax; + + // + // Number of scatter / gather mappings that have been completed (total) + // since the start of the hardware or any reset. + // + ULONG m_NumMappingsCompleted; + + // + // Number of scatter / gather mappings that are queued for this hardware. + // + ULONG m_ScatterGatherMappingsQueued; + ULONG m_ScatterGatherBytesQueued; + + // + // Number of frames skipped due to lack of scatter / gather mappings. + // + ULONG m_NumFramesSkipped; + + // + // The "Interrupt Time". Number of "fake" interrupts that have occurred + // since the hardware was started. + // + ULONG m_InterruptTime; + + // + // The system time at start. + // + LARGE_INTEGER m_StartTime; + + // + // The DPC used to "fake" ISR + // + KDPC m_IsrFakeDpc; + KTIMER m_IsrTimer; + + // + // The hardware sink that will be used for interrupt notifications. + // + IHardwareSink *m_HardwareSink; + + // + // FillScatterGatherBuffers(): + // + // This is called by the hardware simulation to fill a series of scatter / + // gather buffers with synthesized data. + // + NTSTATUS + FillScatterGatherBuffers ( + ); + +public: + + LONG GetSkippedFrameCount() + { + return InterlockedExchange((LONG*)&this->m_NumFramesSkipped, this->m_NumFramesSkipped); + } + // + // CHardwareSimulation(): + // + // The hardware simulation constructor. Since the new operator will + // have zeroed the memory, only initialize non-NULL, non-0 fields. + // + CHardwareSimulation ( + IN IHardwareSink *HardwareSink + ); + + // + // ~CHardwareSimulation(): + // + // The hardware simulation destructor. + // + ~CHardwareSimulation ( + ) + { + } + + // + // Cleanup(): + // + // This is the free callback for the bagged hardware sim. Not providing + // one will call ExFreePool, which is not what we want for a constructed + // C++ object. This simply deletes the simulation. + // + static + void + Cleanup ( + IN CHardwareSimulation *HwSim + ) + { + delete HwSim; + } + + // + // FakeHardware(): + // + // Called from the simulated interrupt. First we fake the hardware's + // actions (at DPC) then we call the "Interrupt service routine" on + // the hardware sink. + // + void + FakeHardware ( + ); + + // + // Start(): + // + // "Start" the fake hardware. This will start issuing interrupts and + // DPC's. + // + // The frame rate, image size, and a synthesizer must be provided. + // + NTSTATUS + Start ( + CImageSynthesizer *ImageSynth, + IN LONGLONG TimePerFrame, + IN ULONG Width, + IN ULONG Height, + IN ULONG ImageSize + ); + + // + // Pause(): + // + // "Pause" or "unpause" the fake hardware. This will stop issuing + // interrupts or DPC's on a pause and restart them on an unpause. Note + // that this will not reset counters as a Stop() would. + // + NTSTATUS + Pause ( + IN BOOLEAN Pausing + ); + + // + // Stop(): + // + // "Stop" the fake hardware. This will stop issuing interrupts and + // DPC's. + // + NTSTATUS + Stop ( + ); + + // + // ProgramScatterGatherMappings(): + // + // Program a series of scatter gather mappings into the fake hardware. + // + ULONG + ProgramScatterGatherMappings ( + IN PKSSTREAM_POINTER Clone, + IN PUCHAR *Buffer, + IN PKSMAPPING Mappings, + IN ULONG MappingsCount, + IN ULONG MappingStride + ); + + // + // Initialize(): + // + // Initialize a piece of simulated hardware. + // + static + CHardwareSimulation * + Initialize ( + IN KSOBJECT_BAG Bag, + IN IHardwareSink *HardwareSink + ); + + // + // ReadNumberOfMappingsCompleted(): + // + // Read the number of mappings completed since the last hardware reset. + // + ULONG + ReadNumberOfMappingsCompleted ( + ); + +}; + diff --git a/avstream/avshws/image.cpp b/avstream/avshws/image.cpp new file mode 100644 index 00000000..8ab35624 --- /dev/null +++ b/avstream/avshws/image.cpp @@ -0,0 +1,605 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + image.cpp + + Abstract: + + The image synthesis and overlay code. These objects provide image + synthesis (pixel, color-bar, etc...) onto RGB24 and UYVY buffers as + well as software string overlay into these buffers. + + This entire file, data and all, must be in locked segments. + + History: + + created 1/16/2001 + +**************************************************************************/ + +#include "avshws.h" + +/************************************************************************** + + Constants + +**************************************************************************/ + +// +// g_FontData: +// +// The following is an 8x8 bitmapped font for use in the text overlay +// code. +// +UCHAR g_FontData [256][8] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e}, + {0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e}, + {0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00}, + {0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00}, + {0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c}, + {0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c}, + {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00}, + {0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff}, + {0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00}, + {0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff}, + {0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78}, + {0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18}, + {0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0}, + {0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0}, + {0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99}, + {0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00}, + {0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00}, + {0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18}, + {0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00}, + {0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00}, + {0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78}, + {0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00}, + {0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff}, + {0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00}, + {0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00}, + {0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00}, + {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00}, + {0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00}, + {0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00}, + {0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00}, + {0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00}, + {0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00}, + {0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00}, + {0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00}, + {0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00}, + {0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00}, + {0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00}, + {0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60}, + {0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00}, + {0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00}, + {0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00}, + {0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00}, + {0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00}, + {0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00}, + {0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00}, + {0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00}, + {0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00}, + {0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00}, + {0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00}, + {0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00}, + {0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60}, + {0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00}, + {0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00}, + {0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00}, + {0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00}, + {0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00}, + {0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00}, + {0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00}, + {0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00}, + {0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00}, + {0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00}, + {0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00}, + {0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00}, + {0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00}, + {0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00}, + {0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00}, + {0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00}, + {0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00}, + {0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00}, + {0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, + {0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00}, + {0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00}, + {0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00}, + {0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00}, + {0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00}, + {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00}, + {0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00}, + {0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00}, + {0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00}, + {0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00}, + {0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00}, + {0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00}, + {0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00}, + {0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, + {0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00}, + {0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00}, + {0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00}, + {0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00}, + {0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00}, + {0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8}, + {0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00}, + {0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78}, + {0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00}, + {0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00}, + {0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00}, + {0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0}, + {0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e}, + {0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00}, + {0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00}, + {0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00}, + {0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00}, + {0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00}, + {0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00}, + {0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00}, + {0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8}, + {0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00}, + {0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00}, + {0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, + {0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00}, + {0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00}, + {0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78}, + {0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00}, + {0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38}, + {0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00}, + {0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00}, + {0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00}, + {0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00}, + {0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00}, + {0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00}, + {0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00}, + {0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00}, + {0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8}, + {0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00}, + {0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00}, + {0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18}, + {0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00}, + {0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30}, + {0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7}, + {0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70}, + {0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00}, + {0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00}, + {0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00}, + {0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00}, + {0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00}, + {0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00}, + {0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00}, + {0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00}, + {0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00}, + {0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f}, + {0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03}, + {0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00}, + {0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00}, + {0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88}, + {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa}, + {0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee}, + {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18}, + {0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18}, + {0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00}, + {0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18}, + {0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36}, + {0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36}, + {0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00}, + {0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00}, + {0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18}, + {0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36}, + {0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36}, + {0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}, + {0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0}, + {0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}, + {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00}, + {0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0}, + {0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00}, + {0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00}, + {0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00}, + {0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00}, + {0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0}, + {0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc}, + {0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00}, + {0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00}, + {0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00}, + {0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00}, + {0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0}, + {0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00}, + {0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00}, + {0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00}, + {0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00}, + {0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00}, + {0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00}, + {0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18}, + {0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70}, + {0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00}, + {0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00}, + {0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00}, + {0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c}, + {0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00}, + {0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +// +// Standard definition of EIA-189-A color bars. The actual color definitions +// are either in CRGB24Synthesizer or CYUVSynthesizer. +// +const COLOR g_ColorBars[] = + {WHITE, YELLOW, CYAN, GREEN, MAGENTA, RED, BLUE, BLACK}; + +const UCHAR CRGB24Synthesizer::Colors [MAX_COLOR][3] = { + {0, 0, 0}, // BLACK + {255, 255, 255}, // WHITE + {0, 255, 255}, // YELLOW + {255, 255, 0}, // CYAN + {0, 255, 0}, // GREEN + {255, 0, 255}, // MAGENTA + {0, 0, 255}, // RED + {255, 0, 0}, // BLUE + {128, 128, 128} // GREY +}; + +const UCHAR CYUVSynthesizer::Colors [MAX_COLOR][3] = { + {128, 16, 128}, // BLACK + {128, 235, 128}, // WHITE + {16, 211, 146}, // YELLOW + {166, 170, 16}, // CYAN + {54, 145, 34}, // GREEN + {202, 106, 222}, // MAGENTA + {90, 81, 240}, // RED + {240, 41, 109}, // BLUE + {128, 125, 128}, // GREY +}; + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +void +CImageSynthesizer:: +SynthesizeBars ( + ) + +/*++ + +Routine Description: + + Synthesize EIA-189-A standard color bars onto the Image. The image + in question is the current synthesis buffer. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + const COLOR *CurColor = g_ColorBars; + ULONG ColorCount = SIZEOF_ARRAY (g_ColorBars); + + // + // Set the default cursor... + // + GetImageLocation (0, 0); + + // + // Synthesize a single line. + // + PUCHAR ImageStart = m_Cursor; + for (ULONG x = 0; x < m_Width; x++) + PutPixel (g_ColorBars [((x * ColorCount) / m_Width)]); + + PUCHAR ImageEnd = m_Cursor; + + // + // Copy the synthesized line to all subsequent lines. + // + for (ULONG line = 1; line < m_Height; line++) { + + GetImageLocation (0, line); + + RtlCopyMemory ( + m_Cursor, + ImageStart, + ImageEnd - ImageStart + ); + } +} + +/*************************************************/ + + +void +CImageSynthesizer:: +OverlayText ( + _In_ ULONG LocX, + _In_ ULONG LocY, + _In_ ULONG Scaling, + _In_ LPSTR Text, + _In_ COLOR BgColor, + _In_ COLOR FgColor + ) + +/*++ + +Routine Description: + + Overlay text onto the synthesized image. Clip to fit the image + if the overlay does not fit. The image buffer used is the set + synthesis buffer. + +Arguments: + + LocX - + The X location on the image to begin the overlay. This MUST + be inside the image. POSITION_CENTER may be used to indicate + horizontal centering. + + LocY - + The Y location on the image to begin the overlay. This MUST + be inside the image. POSITION_CENTER may be used to indicate + vertical centering. + + Scaling - + Normally, the overlay is done in 8x8 font. A scaling of + 2 indicates 16x16, 3 indicates 24x24 and so forth. + + Text - + A character string containing the information to overlay + + BgColor - + The background color of the overlay window. For transparency, + indicate TRANSPARENT here. + + FgColor - + The foreground color for the text overlay. + +Return Value: + + None + +--*/ + +{ + + NT_ASSERT ((LocX <= m_Width || LocX == POSITION_CENTER) && + (LocY <= m_Height || LocY == POSITION_CENTER)); + + ULONG StrLen = 0; + CHAR* CurChar; + + // + // Determine the character length of the string. + // + for (CurChar = Text; CurChar && *CurChar; CurChar++) + StrLen++; + + // + // Determine the physical size of the string plus border. There is + // a definable NO_CHARACTER_SEPARATION. If this is defined, there will + // be no added space between font characters. Otherwise, one empty pixel + // column is added between characters. + // + #ifndef NO_CHARACTER_SEPARATION + ULONG LenX = (StrLen * (Scaling << 3)) + 1 + StrLen; + #else // NO_CHARACTER_SEPARATION + ULONG LenX = (StrLen * (Scaling << 3)) + 2; + #endif // NO_CHARACTER_SEPARATION + + ULONG LenY = 2 + (Scaling << 3); + + // + // Adjust for center overlays. + // + // NOTE: If the overlay doesn't fit into the synthesis buffer, this + // merely left aligns the overlay and clips off the right side. + // + if (LocX == POSITION_CENTER) { + if (LenX >= m_Width) { + LocX = 0; + } else { + LocX = (m_Width >> 1) - (LenX >> 1); + } + } + + if (LocY == POSITION_CENTER) { + if (LenY >= m_Height) { + LocY = 0; + } else { + LocY = (m_Height >> 1) - (LenY >> 1); + } + } + + // + // Determine the amount of space available on the synthesis buffer. + // We will clip anything that finds itself outside the synthesis buffer. + // + ULONG SpaceX = m_Width - LocX; + ULONG SpaceY = m_Height - LocY; + + // + // Set the default cursor position. + // + GetImageLocation (LocX, LocY); + + // + // Overlay a background color row. + // + if (BgColor != TRANSPARENT && SpaceY) { + for (ULONG x = 0; x < LenX && x < SpaceX; x++) { + PutPixel (BgColor); + } + } + LocY++; + if (SpaceY) SpaceY--; + + // + // Loop across each row of the image. + // + for (ULONG row = 0; row < 8 && SpaceY; row++) { + // + // Generate a line. + // + GetImageLocation (LocX, LocY++); + + PUCHAR ImageStart = m_Cursor; + + ULONG CurSpaceX = SpaceX; + if (CurSpaceX) { + PutPixel (BgColor); + CurSpaceX--; + } + + // + // Generate the row'th row of the overlay. + // + CurChar = Text; + while (CurChar && *CurChar) { + + UCHAR CharBase = g_FontData [*CurChar++][row]; + for (ULONG mask = 0x80; mask && CurSpaceX; mask >>= 1) { + for (ULONG scale = 0; scale < Scaling && CurSpaceX; scale++) { + if (CharBase & mask) { + PutPixel (FgColor); + } else { + PutPixel (BgColor); + } + CurSpaceX--; + } + } + + // + // Separate each character by one space. Account for the border + // space at the end by placing the separator after the last + // character also. + // + #ifndef NO_CHARACTER_SEPARATION + if (CurSpaceX) { + PutPixel (BgColor); + CurSpaceX--; + } + #endif // NO_CHARACTER_SEPARATION + + } + + // + // If there is no separation character defined, account for the + // border. + // + #ifdef NO_CHARACTER_SEPARATION + if (CurSpaceX) { + PutPixel (BgColor); + CurSpaceX--; + } + #endif // NO_CHARACTER_SEPARATION + + + PUCHAR ImageEnd = m_Cursor; + // + // Copy the line downward scale times. + // + for (ULONG scale = 1; scale < Scaling && SpaceY; scale++) { + GetImageLocation (LocX, LocY++); + RtlCopyMemory (m_Cursor, ImageStart, ImageEnd - ImageStart); + SpaceY--; + } + + } + + // + // Add the bottom section of the overlay. + // + GetImageLocation (LocX, LocY); + if (BgColor != TRANSPARENT && SpaceY) { + for (ULONG x = 0; x < LenX && x < SpaceX; x++) { + PutPixel (BgColor); + } + } + +} diff --git a/avstream/avshws/image.h b/avstream/avshws/image.h new file mode 100644 index 00000000..95574479 --- /dev/null +++ b/avstream/avshws/image.h @@ -0,0 +1,478 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + image.h + + Abstract: + + The image synthesis and overlay header. These objects provide image + synthesis (pixel, color-bar, etc...) onto RGB24 and UYVY buffers as + well as software string overlay into these buffers. + + History: + + created 1/16/2001 + +**************************************************************************/ + +/************************************************************************** + + Constants + +**************************************************************************/ + +// +// COLOR: +// +// Pixel color for placement onto the synthesis buffer. +// +typedef enum { + + BLACK = 0, + WHITE, + YELLOW, + CYAN, + GREEN, + MAGENTA, + RED, + BLUE, + GREY, + + MAX_COLOR, + TRANSPARENT, + +} COLOR; + +// +// POSITION_CENTER: +// +// Only useful for text overlay. This can be substituted for LocX or LocY +// in order to center the text screen on the synthesis buffer. +// +#define POSITION_CENTER ((ULONG)-1) + +/************************************************* + + CImageSynthesizer + + This class synthesizes images in various formats for output from the + capture filter. It is capable of performing various text overlays onto + the image surface. + +*************************************************/ + +class CImageSynthesizer { + +protected: + + // + // The width and height the synthesizer is set to. + // + ULONG m_Width; + ULONG m_Height; + + // + // The synthesis buffer. All scan conversion happens in the synthesis + // buffer. This must be set with SetBuffer() before any scan conversion + // routines are called. + // + PUCHAR m_SynthesisBuffer; + + // + // The default cursor. This is a pointer into the synthesis buffer where + // a non specific PutPixel will be placed. + // + PUCHAR m_Cursor; + +public: + + // + // PutPixel(): + // + // Place a pixel at the specified image cursor and move right + // by one pixel. No bounds checking... wrap around occurs. + // + virtual void + PutPixel ( + PUCHAR *ImageLocation, + COLOR Color + ) = 0; + + // + // PutPixel(): + // + // Place a pixel at the default image cursor and move right + // by one pixel. No bounds checking... wrap around occurs. + // + // If the derived class doesn't provide an implementation, provide + // one. + // + virtual void + PutPixel ( + COLOR Color + ) + { + PutPixel (&m_Cursor, Color); + } + + virtual long + GetBytesPerPixel() = 0; + + + // + // GetImageLocation(): + // + // Get the location into the image buffer for a specific X/Y location. + // This also sets the synthesizer's default cursor to the position + // LocX, LocY. + // + virtual PUCHAR + GetImageLocation ( + ULONG LocX, + ULONG LocY + ) = 0; + + // + // SetImageSize(): + // + // Set the image size of the synthesis buffer. + // + void + SetImageSize ( + ULONG Width, + ULONG Height + ) + { + m_Width = Width; + m_Height = Height; + } + + // + // SetBuffer(): + // + // Set the buffer the synthesizer generates images to. + // + void + SetBuffer ( + PUCHAR SynthesisBuffer + ) + { + m_SynthesisBuffer = SynthesisBuffer; + } + + // + // SynthesizeBars(): + // + // Synthesize EIA-189-A standard color bars. + // + void + SynthesizeBars ( + ); + + // + // OverlayText(): + // + // Overlay a text string onto the image. + // + void + OverlayText ( + _In_ ULONG LocX, + _In_ ULONG LocY, + _In_ ULONG Scaling, + _In_ LPSTR Text, + _In_ COLOR BgColor, + _In_ COLOR FgColor + ); + + // + // DEFAULT CONSTRUCTOR + // + CImageSynthesizer ( + ) : + m_Width (0), + m_Height (0), + m_SynthesisBuffer (NULL) + { + } + + // + // CONSTRUCTOR: + // + CImageSynthesizer ( + ULONG Width, + ULONG Height + ) : + m_Width (Width), + m_Height (Height), + m_SynthesisBuffer (NULL) + { + } + + // + // DESTRUCTOR: + // + virtual + ~CImageSynthesizer ( + ) + { + } + +}; + +/************************************************* + + CRGB24Synthesizer + + Image synthesizer for RGB24 format. + +*************************************************/ + +class CRGB24Synthesizer : public CImageSynthesizer { + +private: + + const static UCHAR Colors [MAX_COLOR][3]; + + BOOLEAN m_FlipVertical; + +public: + + // + // PutPixel(): + // + // Place a pixel at a specific cursor location. *ImageLocation must + // reside within the synthesis buffer. + // + virtual void + PutPixel ( + PUCHAR *ImageLocation, + COLOR Color + ) + { + if (Color != TRANSPARENT) { + *(*ImageLocation)++ = Colors [(ULONG)Color][0]; + *(*ImageLocation)++ = Colors [(ULONG)Color][1]; + *(*ImageLocation)++ = Colors [(ULONG)Color][2]; + } else { + *ImageLocation += 3; + } + } + + // + // PutPixel(): + // + // Place a pixel at the default cursor location. The cursor location + // must be set via GetImageLocation(x, y). + // + virtual void + PutPixel ( + COLOR Color + ) + { + if (Color != TRANSPARENT) { + *m_Cursor++ = Colors [(ULONG)Color][0]; + *m_Cursor++ = Colors [(ULONG)Color][1]; + *m_Cursor++ = Colors [(ULONG)Color][2]; + } else { + m_Cursor += 3; + } + } + + virtual long + GetBytesPerPixel () + { + return 3; + } + + virtual PUCHAR + GetImageLocation ( + ULONG LocX, + ULONG LocY + ) + { + if (m_FlipVertical) { + return (m_Cursor = + (m_SynthesisBuffer + 3 * + (LocX + (m_Height - 1 - LocY) * m_Width)) + ); + } else { + return (m_Cursor = + (m_SynthesisBuffer + 3 * (LocX + LocY * m_Width)) + ); + } + } + + // + // DEFAULT CONSTRUCTOR: + // + CRGB24Synthesizer ( + BOOLEAN FlipVertical + ) : + m_FlipVertical (FlipVertical) + { + } + + // + // CONSTRUCTOR: + // + CRGB24Synthesizer ( + BOOLEAN FlipVertical, + ULONG Width, + ULONG Height + ) : + CImageSynthesizer (Width, Height), + m_FlipVertical (FlipVertical) + { + } + + // + // DESTRUCTOR: + // + virtual + ~CRGB24Synthesizer ( + ) + { + } + +}; + +/************************************************* + + CYUVSynthesizer + + Image synthesizer for YUV format. + +*************************************************/ + +class CYUVSynthesizer : public CImageSynthesizer { + +private: + + const static UCHAR Colors [MAX_COLOR][3]; + + BOOLEAN m_Parity; + +public: + + // + // PutPixel(): + // + // Place a pixel at a specific cursor location. *ImageLocation must + // reside within the synthesis buffer. + // + virtual void + PutPixel ( + PUCHAR *ImageLocation, + COLOR Color + ) + { + + BOOLEAN Parity = (((*ImageLocation - m_SynthesisBuffer) & 0x2) != 0); + +#if DBG + // + // Check that the current pixel points to a valid start pixel + // in the UYVY buffer. + // + BOOLEAN Odd = (((*ImageLocation - m_SynthesisBuffer) & 0x1) != 0); + NT_ASSERT ((m_Parity && Odd) || (!m_Parity && !Odd)); +#endif // DBG + + if (Color != TRANSPARENT) { + if (Parity) { + *(*ImageLocation)++ = Colors [(ULONG)Color][2]; + } else { + *(*ImageLocation)++ = Colors [(ULONG)Color][1]; + *(*ImageLocation)++ = Colors [(ULONG)Color][0]; + *(*ImageLocation)++ = Colors [(ULONG)Color][1]; + } + } else { + *ImageLocation += (Parity ? 1 : 3); + } + + } + + // + // PutPixel(): + // + // Place a pixel at the default cursor location. The cursor location + // must be set via GetImageLocation(x, y). + // + virtual void + PutPixel ( + COLOR Color + ) + + { + + if (Color != TRANSPARENT) { + if (m_Parity) { + *m_Cursor++ = Colors [(ULONG)Color][2]; + } else { + *m_Cursor++ = Colors [(ULONG)Color][1]; + *m_Cursor++ = Colors [(ULONG)Color][0]; + *m_Cursor++ = Colors [(ULONG)Color][1]; + } + } else { + m_Cursor += (m_Parity ? 1 : 3); + } + + m_Parity = !m_Parity; + + } + + virtual long + GetBytesPerPixel () + { + return 2; + } + + virtual PUCHAR + GetImageLocation ( + ULONG LocX, + ULONG LocY + ) + { + + m_Cursor = m_SynthesisBuffer + ((LocX + LocY * m_Width) << 1); + if (m_Parity = ((LocX & 1) != 0)) + m_Cursor++; + + return m_Cursor; + } + + // + // DEFAULT CONSTRUCTOR: + // + CYUVSynthesizer ( + ) + { + } + + // + // CONSTRUCTOR: + // + CYUVSynthesizer ( + ULONG Width, + ULONG Height + ) : + CImageSynthesizer (Width, Height) + { + } + + // + // DESTRUCTOR: + // + virtual + ~CYUVSynthesizer ( + ) + { + } + +}; + diff --git a/avstream/avshws/purecall.c b/avstream/avshws/purecall.c new file mode 100644 index 00000000..ea61fbff --- /dev/null +++ b/avstream/avshws/purecall.c @@ -0,0 +1,50 @@ +/************************************************************************** + + AVStream Simulated Hardware Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + purecall.c + + Abstract: + + This file contains the _purecall stub necessary for virtual function + usage in drivers on 98 gold. + + History: + + created 9/16/02 + +**************************************************************************/ + +/************************************************* + + Function: + + _purecall + + Description: + + _purecall stub for virtual function usage + + Arguments: + + None + + Return Value: + + 0 + +*************************************************/ +#pragma warning (disable : 4100 4131) +int __cdecl +_purecall ( + VOID + ) + +{ + return 0; +} + diff --git a/avstream/samplemft0/Mft0.cpp b/avstream/samplemft0/Mft0.cpp new file mode 100644 index 00000000..044a4e71 --- /dev/null +++ b/avstream/samplemft0/Mft0.cpp @@ -0,0 +1,975 @@ +// Mft0.cpp : Implementation of CMft0 + +#include "stdafx.h" +#include "Mft0.h" +#include "SampleHelpers.h" +#include + +// CMft0 +STDMETHODIMP CMft0::UpdateDsp(UINT32 uiPercentOfScreen) +{ + m_percentOfScreen = uiPercentOfScreen; + return S_OK; +} + +STDMETHODIMP CMft0::Enable(void) +{ + m_bEnableEffects = TRUE; + return S_OK; +} + +STDMETHODIMP CMft0::Disable(void) +{ + m_bEnableEffects = FALSE; + return S_OK; +} + +STDMETHODIMP CMft0::GetDspSetting(UINT* puiPercentOfScreen, BOOL* pIsEnabled) +{ + HRESULT hr = S_OK; + do { + CHK_NULL_PTR_BRK(puiPercentOfScreen); + CHK_NULL_PTR_BRK(pIsEnabled); + *puiPercentOfScreen = m_percentOfScreen; + *pIsEnabled = m_bEnableEffects; + } while (FALSE); + + return S_OK; +} + +STDMETHODIMP CMft0::GetIids( + /* [out] */ _Out_ ULONG *iidCount, + /* [size_is][size_is][out] */ _Outptr_result_buffer_maybenull_(*iidCount) IID **iids) +{ + HRESULT hr = S_OK; + do { + CHK_NULL_PTR_BRK(iidCount); + CHK_NULL_PTR_BRK(iids); + *iids = NULL; + *iidCount = 0; + } while (FALSE); + + return hr; +} + +STDMETHODIMP CMft0::GetRuntimeClassName( + /* [out] */ _Outptr_result_maybenull_ HSTRING *className) +{ + HRESULT hr = S_OK; + if(className != nullptr) + { + hr = WindowsCreateString(NULL, 0, className); + if(FAILED(hr)){ + hr = E_OUTOFMEMORY; + } + }else { + hr = E_INVALIDARG; + } + return hr; +} + +STDMETHODIMP CMft0::GetTrustLevel( + /* [out] */ _Out_ TrustLevel *trustLevel) +{ + HRESULT hr = S_OK; + do { + CHK_NULL_PTR_BRK(trustLevel); + *trustLevel = TrustLevel::BaseTrust; + } while (FALSE); + + return hr; +} + + +STDMETHODIMP CMft0::GetStreamLimits( + /* [out] */ _Out_ DWORD *pdwInputMinimum, + /* [out] */ _Out_ DWORD *pdwInputMaximum, + /* [out] */ _Out_ DWORD *pdwOutputMinimum, + /* [out] */ _Out_ DWORD *pdwOutputMaximum) +{ + HRESULT hr = S_OK; + do { + if ((pdwInputMinimum == NULL) || + (pdwInputMaximum == NULL) || + (pdwOutputMinimum == NULL) || + (pdwOutputMaximum == NULL)) + { + hr = E_POINTER; + break; + } + + // This MFT has a fixed number of streams. + *pdwInputMinimum = 1; + *pdwInputMaximum = 1; + *pdwOutputMinimum = 1; + *pdwOutputMaximum = 1; + } while (FALSE); + + return hr; +} + + +STDMETHODIMP CMft0::GetStreamCount( + /* [out] */ _Out_ DWORD *pcInputStreams, + /* [out] */ _Out_ DWORD *pcOutputStreams) +{ + HRESULT hr = S_OK; + + do { + if ((pcInputStreams == NULL) || (pcOutputStreams == NULL)) + + { + hr = E_POINTER; + break; + } + + // This MFT has a fixed number of streams. + *pcInputStreams = 1; + *pcOutputStreams = 1; + } while (FALSE); + + return hr; +} +STDMETHODIMP CMft0::GetStreamIDs( + DWORD dwInputIDArraySize, + /* [size_is][out] */ _Out_writes_(dwInputIDArraySize) DWORD *pdwInputIDs, + DWORD dwOutputIDArraySize, + /* [size_is][out] */ _Out_writes_(dwOutputIDArraySize) DWORD *pdwOutputIDs) +{ + dwOutputIDArraySize = 0; + dwInputIDArraySize = 0; + pdwInputIDs = NULL; + pdwOutputIDs = NULL; + return E_NOTIMPL; +} + +STDMETHODIMP CMft0::GetInputStreamInfo( + DWORD dwInputStreamID, + /* [out] */ _Out_ MFT_INPUT_STREAM_INFO *pStreamInfo) +{ + HRESULT hr = S_OK; + EnterCriticalSection(&m_critSec); + + do { + CHK_NULL_BRK(pStreamInfo); + if (!IsValidInputStream(dwInputStreamID)) + { + CHK_LOG_BRK(MF_E_INVALIDSTREAMNUMBER); + } + if(m_pInputType) { + pStreamInfo->cbAlignment = 0; + pStreamInfo->cbSize = 0; + pStreamInfo->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER ; + pStreamInfo->hnsMaxLatency = 0; + } else { + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + } + } while (FALSE); + + LeaveCriticalSection(&m_critSec); + return hr; +} + + +STDMETHODIMP CMft0::GetOutputStreamInfo( + DWORD dwOutputStreamID, + /* [out] */ _Out_ MFT_OUTPUT_STREAM_INFO *pStreamInfo) +{ + HRESULT hr = S_OK; + EnterCriticalSection(&m_critSec); + + do { + CHK_NULL_BRK(pStreamInfo); + if (!IsValidInputStream(dwOutputStreamID)) + { + CHK_LOG_BRK(MF_E_INVALIDSTREAMNUMBER); + } + if(m_pOutputType) { + pStreamInfo->cbAlignment = 0; + pStreamInfo->cbSize = 0; + pStreamInfo->dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; + } else { + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + } + + } while (FALSE); + + LeaveCriticalSection(&m_critSec); + return hr; +} + +STDMETHODIMP CMft0::GetAttributes( + /* [out] */ _Outptr_result_maybenull_ IMFAttributes **ppAttributes) +{ + HRESULT hr = S_OK; + + do { + CHK_NULL_PTR_BRK(ppAttributes); + if(!m_pGlobalAttributes) { + CHK_LOG_BRK(MFCreateAttributes(&m_pGlobalAttributes, 3)); + CHK_LOG_BRK(m_pGlobalAttributes->SetUINT32(MF_TRANSFORM_ASYNC, FALSE)); + CHK_LOG_BRK(m_pGlobalAttributes->SetString(MFT_ENUM_HARDWARE_URL_Attribute, L"Sample_CameraExtensionMft")); + CHK_LOG_BRK(m_pGlobalAttributes->SetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, TRUE)); + } + *ppAttributes = m_pGlobalAttributes; + (*ppAttributes)->AddRef(); + } while (FALSE); + + return hr; +} + +STDMETHODIMP CMft0::GetInputStreamAttributes( + DWORD dwInputStreamID, + /* [out] */ _Outptr_result_maybenull_ IMFAttributes **ppAttributes) +{ + HRESULT hr = S_OK; + + do { + if(dwInputStreamID > 0) { + hr = MF_E_INVALIDSTREAMNUMBER; + break; + } + CHK_NULL_PTR_BRK(ppAttributes); + if(!m_pInputAttributes){ + CHK_LOG_BRK(MFCreateAttributes(&m_pInputAttributes, 2)); + CHK_LOG_BRK(m_pInputAttributes->SetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, TRUE)); + CHK_LOG_BRK(m_pInputAttributes->SetString(MFT_ENUM_HARDWARE_URL_Attribute, L"Sample_CameraExtensionMft")); + } + *ppAttributes = m_pInputAttributes; + (*ppAttributes)->AddRef(); + } while (FALSE); + + return hr; +} + +STDMETHODIMP CMft0::GetOutputStreamAttributes( + DWORD dwOutputStreamID, + /* [out] */ _Outptr_result_maybenull_ IMFAttributes **ppAttributes) +{ + HRESULT hr = S_OK; + + do { + if(dwOutputStreamID > 0) { + hr = MF_E_INVALIDSTREAMNUMBER; + break; + } + CHK_NULL_PTR_BRK(ppAttributes); + CHK_NULL_PTR_BRK(m_pInputAttributes); + + *ppAttributes = m_pInputAttributes; + (*ppAttributes)->AddRef(); + } while (FALSE); + + return hr; +} + +STDMETHODIMP CMft0::DeleteInputStream( + DWORD dwStreamID) +{ + HRESULT hr = S_OK; + if(dwStreamID > 0) { + hr = MF_E_INVALIDSTREAMNUMBER; + } else { + hr = E_NOTIMPL; + } + return hr; +} + +STDMETHODIMP CMft0::AddInputStreams( + DWORD cStreams, + /* [in] */ _In_ DWORD *adwStreamIDs) +{ + HRESULT hr = S_OK; + + if( !adwStreamIDs || cStreams > 0) { + hr = E_INVALIDARG; + } else { + hr = E_NOTIMPL; + } + return hr; +} + +STDMETHODIMP CMft0::GetInputAvailableType( + DWORD dwInputStreamID, + DWORD dwTypeIndex, + /* [out] */ _Outptr_result_maybenull_ IMFMediaType **ppType) +{ + HRESULT hr = S_OK; + IUnknown *pUnk = NULL; + IMFAttributes *pSourceAttributes = NULL; + UINT32 uiSourceStreamId = 0; + wchar_t *pszName; + + EnterCriticalSection(&m_critSec); + + do { + CHK_BOOL_BRK(IsValidInputStream(dwInputStreamID)); + + if(!m_pSourceTransform && m_pInputAttributes) { + CHK_LOG_BRK(m_pInputAttributes->GetUnknown(MFT_CONNECTED_STREAM_ATTRIBUTE, IID_PPV_ARGS(&pSourceAttributes))); + CHK_LOG_BRK(pSourceAttributes->GetUnknown(MF_DEVICESTREAM_EXTENSION_PLUGIN_CONNECTION_POINT, IID_PPV_ARGS(&pUnk))); + CHK_LOG_BRK(pUnk->QueryInterface(__uuidof(IMFTransform), (void**)&m_pSourceTransform)); + CHK_LOG_BRK(pSourceAttributes->GetGUID( MF_DEVICESTREAM_STREAM_CATEGORY, &m_stStreamType)); + CHK_LOG_BRK(pSourceAttributes->GetUINT32( MF_DEVICESTREAM_STREAM_ID, &uiSourceStreamId)); + + if(m_stStreamType == PINNAME_VIDEO_CAPTURE) { + wprintf(L"Stream type: PINNAME_VIDEO_CAPTURE\n"); + } else if(m_stStreamType == PINNAME_VIDEO_PREVIEW) { + wprintf(L"Stream type: PINNAME_VIDEO_PREVIEW\n"); + } else if(m_stStreamType == PINNAME_VIDEO_STILL) { + wprintf(L"Stream type: PINNAME_VIDEO_STILL\n"); + } else if(m_stStreamType == PINNAME_IMAGE) { + wprintf(L"Stream type: PINNAME_IMAGE\n"); + } else { + StringFromCLSID(m_stStreamType, &pszName); + if(pszName){ + wprintf(L"Stream type: %s\n", pszName); + CoTaskMemFree(pszName); + } + } + CHK_LOG_BRK((m_stStreamType == PINNAME_VIDEO_PREVIEW || m_stStreamType == PINNAME_VIDEO_CAPTURE) ? S_OK : E_UNEXPECTED); + CHK_LOG_BRK(GenerateMFMediaTypeListFromDevice(uiSourceStreamId)); + } + + CHK_LOG_BRK(GetMediaType(dwInputStreamID, dwTypeIndex, ppType)); + } while (FALSE); + + LeaveCriticalSection(&m_critSec); + SAFERELEASE(pUnk); + SAFERELEASE(pSourceAttributes); + return hr; +} + +STDMETHODIMP CMft0::GetOutputAvailableType( + DWORD dwOutputStreamID, + DWORD dwTypeIndex, + /* [out] */ _Outptr_result_maybenull_ IMFMediaType **ppType) +{ + HRESULT hr = S_OK; + IUnknown *pUnk = NULL; + IMFAttributes *pSourceAttributes = NULL; + UINT32 uiSourceStreamId = 0; + wchar_t *pszName; + + EnterCriticalSection(&m_critSec); + + do { + CHK_BOOL_BRK(IsValidOutputStream(dwOutputStreamID)); + if(!m_pSourceTransform && m_pInputAttributes) { + CHK_LOG_BRK(m_pInputAttributes->GetUnknown(MFT_CONNECTED_STREAM_ATTRIBUTE, IID_PPV_ARGS(&pSourceAttributes))); + CHK_LOG_BRK(pSourceAttributes->GetUnknown(MF_DEVICESTREAM_EXTENSION_PLUGIN_CONNECTION_POINT, IID_PPV_ARGS(&pUnk))); + CHK_LOG_BRK(pUnk->QueryInterface(__uuidof(IMFTransform), (void**)&m_pSourceTransform)); + CHK_LOG_BRK(pSourceAttributes->GetGUID( MF_DEVICESTREAM_STREAM_CATEGORY, &m_stStreamType)); + CHK_LOG_BRK(pSourceAttributes->GetUINT32( MF_DEVICESTREAM_STREAM_ID, &uiSourceStreamId)); + if(m_stStreamType == PINNAME_VIDEO_CAPTURE) { + wprintf(L"Stream type: PINNAME_VIDEO_CAPTURE\n"); + } else if(m_stStreamType == PINNAME_VIDEO_PREVIEW) { + wprintf(L"Stream type: PINNAME_VIDEO_PREVIEW\n"); + } else if(m_stStreamType == PINNAME_VIDEO_STILL) { + wprintf(L"Stream type: PINNAME_VIDEO_STILL\n"); + } else if(m_stStreamType == PINNAME_IMAGE) { + wprintf(L"Stream type: PINNAME_IMAGE\n"); + } else { + StringFromCLSID(m_stStreamType, &pszName); + wprintf(L"Stream type: %s\n", pszName); + } + CHK_LOG_BRK((m_stStreamType == PINNAME_VIDEO_PREVIEW || m_stStreamType == PINNAME_VIDEO_CAPTURE) ? S_OK : E_UNEXPECTED); + CHK_LOG_BRK(GenerateMFMediaTypeListFromDevice(uiSourceStreamId)); + } + + CHK_LOG_BRK(GetMediaType(dwOutputStreamID, dwTypeIndex, ppType)); + } while (FALSE); + + LeaveCriticalSection(&m_critSec); + SAFERELEASE(pUnk); + SAFERELEASE(pSourceAttributes); + return hr; +} + +STDMETHODIMP CMft0::SetInputType( + DWORD dwInputStreamID, + /* [in] */ _In_opt_ IMFMediaType *pType, + DWORD dwFlags) +{ + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0); + // Validate flags. + do { + if(bReallySet) { + CComPtr pFullType; + CHK_LOG_BRK(IsMediaTypeSupported(dwInputStreamID, pType, &pFullType)); + SAFERELEASE(m_pInputType); + SAFERELEASE(m_pOutputType); + m_pOutputType = pFullType; + m_pOutputType->AddRef(); + m_pInputType = pFullType; + m_pInputType->AddRef(); + } else { + CHK_LOG_BRK(IsMediaTypeSupported(dwInputStreamID, pType)); + } + } while(FALSE); + + LeaveCriticalSection(&m_critSec); + return hr; +} + +STDMETHODIMP CMft0::SetOutputType( + DWORD dwOutputStreamID, + /* [in] */ _In_opt_ IMFMediaType *pType, + DWORD dwFlags) +{ + HRESULT hr = S_OK; + + do { + CHK_LOG_BRK(SetInputType(dwOutputStreamID, pType, dwFlags)); + } while (FALSE); + + return hr; +} + +STDMETHODIMP CMft0::GetInputCurrentType( + DWORD dwInputStreamID, + /* [out] */ _Outptr_result_maybenull_ IMFMediaType **ppType) +{ + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + do { + CHK_NULL_BRK(ppType); + + if (!IsValidInputStream(dwInputStreamID)) + { + CHK_LOG_BRK(MF_E_INVALIDSTREAMNUMBER); + } + else if(m_pInputType) + { + *ppType = m_pInputType; + (*ppType)->AddRef(); + } + else + { + CHK_LOG_BRK(MF_E_TRANSFORM_TYPE_NOT_SET); + } + } while (FALSE); + + LeaveCriticalSection(&m_critSec); + return hr; +} + +STDMETHODIMP CMft0::GetOutputCurrentType( + DWORD dwOutputStreamID, + /* [out] */ _Outptr_result_maybenull_ IMFMediaType **ppType) +{ + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + do { + CHK_NULL_BRK(ppType); + + if (!IsValidOutputStream(dwOutputStreamID)) + { + CHK_LOG_BRK(MF_E_INVALIDSTREAMNUMBER); + } + else if(m_pOutputType) + { + *ppType = m_pOutputType; + (*ppType)->AddRef(); + } + else + { + CHK_LOG_BRK(MF_E_TRANSFORM_TYPE_NOT_SET); + } + } while (FALSE); + + LeaveCriticalSection(&m_critSec); + return hr; +} + +STDMETHODIMP CMft0::GetInputStatus( + DWORD dwInputStreamID, + /* [out] */ _Out_ DWORD *pdwFlags) +{ + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + do { + CHK_BOOL_BRK(pdwFlags); + + if (!IsValidInputStream(dwInputStreamID)) + { + CHK_LOG_BRK(MF_E_INVALIDSTREAMNUMBER); + } + + // If we already have an input sample, we don't accept + // another one until the client calls ProcessOutput or Flush. + if (m_pSample == NULL) + { + *pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA; + } + else + { + *pdwFlags = 0; + } + } while (FALSE); + + LeaveCriticalSection(&m_critSec); + return hr; +} + +STDMETHODIMP CMft0::GetOutputStatus( + /* [out] */ _Out_ DWORD *pdwFlags) +{ + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + do { + CHK_NULL_BRK(pdwFlags); + // We can produce an output sample if (and only if) + // we have an input sample. + if (m_pSample != NULL) + { + *pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY; + } + else + { + *pdwFlags = 0; + } + } while (FALSE); + + LeaveCriticalSection(&m_critSec); + return hr; +} + +STDMETHODIMP CMft0::SetOutputBounds( + LONGLONG hnsLowerBound, + LONGLONG hnsUpperBound) +{ + UNREFERENCED_PARAMETER(hnsLowerBound); + UNREFERENCED_PARAMETER(hnsUpperBound); + return S_OK; +} + +STDMETHODIMP CMft0::ProcessEvent( + DWORD dwInputStreamID, + /* [in] */ _In_opt_ IMFMediaEvent *pEvent) +{ + UNREFERENCED_PARAMETER(dwInputStreamID); + UNREFERENCED_PARAMETER(pEvent); + return E_NOTIMPL; +} + +STDMETHODIMP CMft0::ProcessMessage( + MFT_MESSAGE_TYPE eMessage, + ULONG_PTR ulParam) +{ + UNREFERENCED_PARAMETER(ulParam); + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + switch (eMessage) + { + case MFT_MESSAGE_COMMAND_FLUSH: + // Flush the MFT. + hr = OnFlush(); + break; + + case MFT_MESSAGE_COMMAND_DRAIN: + // Drain: Tells the MFT not to accept any more input until + // all of the pending output has been processed. That is our + // default behevior already, so there is nothing to do. + break; + + case MFT_MESSAGE_SET_D3D_MANAGER: + // The pipeline should never send this message unless the MFT + // has the MF_SA_D3D_AWARE attribute set to TRUE. However, if we + // do get this message, it's invalid and we don't implement it. + hr = E_NOTIMPL; + break; + + // The remaining messages do not require any action from this MFT. + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + case MFT_MESSAGE_NOTIFY_END_STREAMING: + case MFT_MESSAGE_NOTIFY_END_OF_STREAM: + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: + break; + } + + LeaveCriticalSection(&m_critSec); + return hr; +} + +STDMETHODIMP CMft0::ProcessInput( + DWORD dwInputStreamID, + IMFSample *pSample, + DWORD dwFlags) +{ + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + do { + CHK_NULL_BRK(pSample); + + CHK_BOOL_BRK(dwFlags == 0) + + DWORD dwBufferCount = 0; + + if (!IsValidInputStream(dwInputStreamID)) + { + CHK_LOG_BRK(hr = MF_E_INVALIDSTREAMNUMBER); + } + + if (!m_pInputType || !m_pOutputType) + { + CHK_LOG_BRK(MF_E_NOTACCEPTING); // Client must set input and output types. + } + + if (m_pSample != NULL) + { + CHK_LOG_BRK(MF_E_NOTACCEPTING); // We already have an input sample. + } + + // Validate the number of buffers. There should only be a single buffer to hold the video frame. + CHK_LOG_BRK(pSample->GetBufferCount(&dwBufferCount)); + + if (dwBufferCount == 0) + { + CHK_LOG_BRK(E_FAIL); + } + if (dwBufferCount > 1) + { + CHK_LOG_BRK(MF_E_SAMPLE_HAS_TOO_MANY_BUFFERS); + } + + // Cache the sample. We do the actual work in ProcessOutput. + m_pSample = pSample; + pSample->AddRef(); // Hold a reference count on the sample. + } while (FALSE); + + LeaveCriticalSection(&m_critSec); + return hr; +} + +STDMETHODIMP CMft0::ProcessOutput( + DWORD dwFlags, + DWORD cOutputBufferCount, + /* [size_is][out][in] */ MFT_OUTPUT_DATA_BUFFER *pOutputSamples, + /* [out] */ DWORD *pdwStatus) +{ + HRESULT hr = S_OK; + + IMFMediaBuffer *pMediaBufInput = NULL, + *pMediaBufOutput = NULL; + IMFSample *pOutputIMFSample = NULL; + LONGLONG hnsDuration = 0, + hnsTime = 0; + + EnterCriticalSection(&m_critSec); + + do { + CHK_BOOL_BRK(dwFlags == 0); + CHK_NULL_BRK(pOutputSamples); + CHK_NULL_BRK(pdwStatus); + + // Must be exactly one output buffer. + CHK_BOOL_BRK(cOutputBufferCount == 1); + + // If we don't have an input sample, we need some input before + // we can generate any output. + if (m_pSample == NULL) + { + hr = MF_E_TRANSFORM_NEED_MORE_INPUT; + break; + } + + CHK_LOG_BRK(m_pSample->ConvertToContiguousBuffer(&pMediaBufInput)); + + MFCreateSample(&pOutputIMFSample); + + if(!pOutputSamples[0].pSample){ + CHK_LOG_BRK(m_pSample->ConvertToContiguousBuffer(&pMediaBufOutput)); + + } else { + CHK_LOG_BRK(pOutputSamples[0].pSample->ConvertToContiguousBuffer(&pMediaBufOutput)); + } + + pOutputIMFSample->AddBuffer(pMediaBufOutput); + + CHK_LOG_BRK(OnProcessOutput(pMediaBufInput, pMediaBufOutput)); + pOutputSamples[0].pSample = pOutputIMFSample; + pOutputIMFSample->AddRef(); + + pOutputSamples[0].dwStatus = 0; + *pdwStatus = 0; + if (SUCCEEDED(m_pSample->GetSampleDuration(&hnsDuration))) + { + CHK_LOG_BRK(pOutputSamples[0].pSample->SetSampleDuration(hnsDuration)); + } + + if (SUCCEEDED(m_pSample->GetSampleTime(&hnsTime))) + { + CHK_LOG_BRK(pOutputSamples[0].pSample->SetSampleTime(hnsTime)); + } + } while (FALSE); + + SAFERELEASE(pOutputIMFSample); + SAFERELEASE(pMediaBufInput); + SAFERELEASE(pMediaBufOutput); + SAFERELEASE(m_pSample); // Release our input sample. + LeaveCriticalSection(&m_critSec); + return hr; +} + +BOOL CMft0::IsValidInputStream(DWORD dwInputStreamID) +{ + return dwInputStreamID == 0; +} + +// IsValidOutputStream: Returns TRUE if dwOutputStreamID is a valid output stream identifier. +BOOL CMft0::IsValidOutputStream(DWORD dwOutputStreamID) +{ + //update + return dwOutputStreamID == 0; +} + + +STDMETHODIMP CMft0::OnProcessOutput(IMFMediaBuffer *pIn, IMFMediaBuffer *pOut) +{ + HRESULT hr = S_OK; + LONG lDefaultStride = 0, + lSrcStride = 0, + lDestStride = 0; + UINT uiWidth = 0, + uiHeight = 0; + BYTE *pDest = NULL, + *pSrc = NULL; + //BOOL bCompressed = TRUE; + GUID stSubType = {0}; + + do { + CHK_NULL_PTR_BRK(m_pSample); + CHK_NULL_PTR_BRK(m_pInputType); + + CHK_LOG_BRK(m_pInputType->GetGUID(MF_MT_SUBTYPE, &stSubType)); + + if(((stSubType == MFVideoFormat_RGB8) || (stSubType == MFVideoFormat_RGB555) || + (stSubType == MFVideoFormat_RGB565) || (stSubType == MFVideoFormat_RGB24) || + (stSubType == MFVideoFormat_RGB32) || (stSubType == MFVideoFormat_ARGB32) || + (stSubType == MFVideoFormat_AI44) || (stSubType == MFVideoFormat_AYUV) || + (stSubType == MFVideoFormat_I420) || (stSubType == MFVideoFormat_IYUV) || + (stSubType == MFVideoFormat_NV11) || (stSubType == MFVideoFormat_NV12) || + (stSubType == MFVideoFormat_UYVY) || (stSubType == MFVideoFormat_Y41P) || + (stSubType == MFVideoFormat_Y41T) || (stSubType == MFVideoFormat_Y42T) || + (stSubType == MFVideoFormat_YUY2) || (stSubType == MFVideoFormat_YV12) || + (stSubType == MFVideoFormat_P010) || (stSubType == MFVideoFormat_P016) || + (stSubType == MFVideoFormat_P210) || (stSubType == MFVideoFormat_P216) || + (stSubType == MFVideoFormat_v210) || (stSubType == MFVideoFormat_v216) || + (stSubType == MFVideoFormat_v410) || (stSubType == MFVideoFormat_Y210) || + (stSubType == MFVideoFormat_Y216) || (stSubType == MFVideoFormat_Y410) || + (stSubType == MFVideoFormat_Y416)) && m_bEnableEffects) + { + CHK_LOG_BRK(GetDefaultStride(&lDefaultStride)); + CHK_LOG_BRK(MFGetAttributeSize(m_pInputType, MF_MT_FRAME_SIZE, &uiWidth, &uiHeight)); + VideoBufferLock inputLock(pIn); + VideoBufferLock outputLock(pOut); + + // Lock the input buffer. + CHK_LOG_BRK(inputLock.LockBuffer(lDefaultStride, uiHeight, &pSrc, &lSrcStride)); + + // Lock the output buffer. + CHK_LOG_BRK(outputLock.LockBuffer(lDefaultStride, uiHeight, &pDest, &lDestStride)); + long lines = uiHeight; + if(m_percentOfScreen != -1 && m_percentOfScreen != 0) { + lines = (UINT)(uiHeight * (1.0- m_percentOfScreen/100.00)); + } + for(long i = 0; i < (long)uiHeight; i++) { + if(lDestStride < 0) { + if(i >= lines) { + memset(pDest+i*lDestStride, 0, abs(lDefaultStride)); + } else { + memcpy(pDest+i*lDestStride, pSrc+i*lDefaultStride, abs(lDefaultStride)); + } + } else { + if(i >= lines) { + memset(pDest+i*lDestStride, 0, lDestStride); + } else { + memcpy(pDest+i*lDestStride, pSrc+i*lDestStride, lDestStride); + } + } + } + } + } while (FALSE); + + return hr; +} + +STDMETHODIMP CMft0::GetDefaultStride(LONG *plStride) +{ + LONG lStride = 0; + + // Try to get the default stride from the media type. + HRESULT hr = m_pInputType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride); + if (FAILED(hr)) + { + // Attribute not set. Try to calculate the default stride. + GUID subtype = GUID_NULL; + UINT32 width = 0; + UINT32 height = 0; + + // Get the subtype and the image size. + hr = m_pInputType->GetGUID(MF_MT_SUBTYPE, &subtype); + if (SUCCEEDED(hr)) + { + hr = MFGetAttributeSize(m_pInputType, MF_MT_FRAME_SIZE, &width, &height); + } + if (SUCCEEDED(hr)) + { + hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride); + } + + // Set the attribute for later reference. + if (SUCCEEDED(hr)) + { + (void)m_pInputType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride)); + } + } + + if (SUCCEEDED(hr)) + { + *plStride = lStride; + } + + return hr; +} + +STDMETHODIMP CMft0::OnFlush() +{ + HRESULT hr = S_OK; + + // For this MFT, flushing just means releasing the input sample. + SAFERELEASE(m_pSample); + + return hr; +} + +STDMETHODIMP CMft0::GetMediaType( DWORD dwStreamId, DWORD dwTypeIndex, IMFMediaType **ppType) +{ + HRESULT hr = S_OK; + + do { + CHK_NULL_PTR_BRK(ppType); + if(dwStreamId != 0) + CHK_LOG_BRK(MF_E_INVALIDSTREAMNUMBER); + if(dwTypeIndex >= m_listOfMediaTypes.GetCount()) + CHK_LOG_BRK(MF_E_NO_MORE_TYPES); + + *ppType = m_listOfMediaTypes[dwTypeIndex]; + (*ppType)->AddRef(); + } while (FALSE); + + return hr; +} + +STDMETHODIMP CMft0::IsMediaTypeSupported(UINT uiStreamId, IMFMediaType *pIMFMediaType, IMFMediaType **ppIMFMediaTypeFull) +{ + HRESULT hr = S_OK; + + do { + CHK_NULL_PTR_BRK(pIMFMediaType); + + if(uiStreamId != 0) + { + CHK_LOG_BRK(MF_E_INVALIDINDEX); + } + BOOL bFound =FALSE; + for(UINT i = 0; i< m_listOfMediaTypes.GetCount(); i++) + { + DWORD dwResult = 0; + hr = m_listOfMediaTypes[i]->IsEqual(pIMFMediaType, &dwResult); + if(hr == S_FALSE) + { + + if((dwResult & MF_MEDIATYPE_EQUAL_MAJOR_TYPES) && + (dwResult& MF_MEDIATYPE_EQUAL_FORMAT_TYPES) && + (dwResult& MF_MEDIATYPE_EQUAL_FORMAT_DATA)) + { + hr = S_OK; + } + } + if(hr == S_OK) + { + bFound = TRUE; + if(ppIMFMediaTypeFull) { + *ppIMFMediaTypeFull = m_listOfMediaTypes[i]; + (*ppIMFMediaTypeFull)->AddRef(); + } + break; + } + else if(FAILED(hr)) + { + CHK_LOG_BRK(hr); + } + + } + if(bFound == FALSE) + { + CHK_LOG_BRK(MF_E_INVALIDMEDIATYPE); + } + } while (FALSE); + + return hr; +} + +STDMETHODIMP CMft0::GenerateMFMediaTypeListFromDevice(UINT uiStreamId) +{ + HRESULT hr = S_OK; + GUID stSubType = {0}; + + do { + CHK_NULL_PTR_BRK(m_pSourceTransform); + + m_listOfMediaTypes.RemoveAll(); + for(UINT iMediaType = 0; TRUE; iMediaType++) + { + CComPtr pMediaType; + hr = m_pSourceTransform->GetOutputAvailableType(uiStreamId, iMediaType, &pMediaType); + if(hr != S_OK) + break; + CHK_LOG_BRK(pMediaType->GetGUID(MF_MT_SUBTYPE, &stSubType)); + + if(((stSubType == MFVideoFormat_RGB8) || (stSubType == MFVideoFormat_RGB555) || + (stSubType == MFVideoFormat_RGB565) || (stSubType == MFVideoFormat_RGB24) || + (stSubType == MFVideoFormat_RGB32) || (stSubType == MFVideoFormat_ARGB32) || + (stSubType == MFVideoFormat_AI44) || (stSubType == MFVideoFormat_AYUV) || + (stSubType == MFVideoFormat_I420) || (stSubType == MFVideoFormat_IYUV) || + (stSubType == MFVideoFormat_NV11) || (stSubType == MFVideoFormat_NV12) || + (stSubType == MFVideoFormat_UYVY) || (stSubType == MFVideoFormat_Y41P) || + (stSubType == MFVideoFormat_Y41T) || (stSubType == MFVideoFormat_Y42T) || + (stSubType == MFVideoFormat_YUY2) || (stSubType == MFVideoFormat_YV12) || + (stSubType == MFVideoFormat_P010) || (stSubType == MFVideoFormat_P016) || + (stSubType == MFVideoFormat_P210) || (stSubType == MFVideoFormat_P216) || + (stSubType == MFVideoFormat_v210) || (stSubType == MFVideoFormat_v216) || + (stSubType == MFVideoFormat_v410) || (stSubType == MFVideoFormat_Y210) || + (stSubType == MFVideoFormat_Y216) || (stSubType == MFVideoFormat_Y410) || + (stSubType == MFVideoFormat_Y416)) && m_bEnableEffects) + { + m_listOfMediaTypes[(ULONG)(m_listOfMediaTypes.GetCount())] = pMediaType; + } + } + + } while (FALSE); + + if(hr == MF_E_NO_MORE_TYPES) { + hr = S_OK; + } + + return hr; +} \ No newline at end of file diff --git a/avstream/samplemft0/Mft0.h b/avstream/samplemft0/Mft0.h new file mode 100644 index 00000000..0cc85733 --- /dev/null +++ b/avstream/samplemft0/Mft0.h @@ -0,0 +1,224 @@ +// Mft0.h : Declaration of the CMft0 + +#pragma once +#include "resource.h" // main symbols +#include "SampleMft0.h" +#include "SampleHelpers.h" + +#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA) +#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms." +#endif + +using namespace ATL; + +// CMft0 + +class ATL_NO_VTABLE CMft0 : + public CComObjectRootEx, + public CComCoClass, + public IMft0, + public IMFTransform, + public IInspectable +{ +public: + CMft0() : m_pSample(NULL), + m_pInputType(NULL), + m_pOutputType(NULL), + m_pInputAttributes(0), + m_pGlobalAttributes(0), + m_pSourceTransform(0), + m_bEnableEffects(TRUE), + m_uiStreamId(0), + m_percentOfScreen(50), + m_nRefCount(1) + { + InitializeCriticalSection(&m_critSec); + } + + DECLARE_REGISTRY_RESOURCEID(IDR_MFT0) + + DECLARE_NOT_AGGREGATABLE(CMft0) + + BEGIN_COM_MAP(CMft0) + COM_INTERFACE_ENTRY(IMft0) + COM_INTERFACE_ENTRY(IMFTransform) + COM_INTERFACE_ENTRY(IInspectable) + END_COM_MAP() + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + HRESULT FinalConstruct() + { + return S_OK; + } + + void FinalRelease() + { + } + +public: + STDMETHOD(UpdateDsp)(UINT32 uiPercentOfScreen); + STDMETHOD(Enable)(void); + STDMETHOD(Disable)(void); + STDMETHOD(GetDspSetting)(UINT* puiPercentOfScreen, BOOL* pIsEnabled); + + STDMETHODIMP GetIids( + /* [out] */ _Out_ ULONG *iidCount, + /* [size_is][size_is][out] */ _Outptr_result_buffer_maybenull_(*iidCount) IID **iids); + + STDMETHODIMP GetRuntimeClassName( + /* [out] */ _Outptr_result_maybenull_ HSTRING *className); + + STDMETHODIMP GetTrustLevel( + /* [out] */ _Out_ TrustLevel *trustLevel); + + STDMETHODIMP GetStreamLimits( + /* [out] */ _Out_ DWORD *pdwInputMinimum, + /* [out] */ _Out_ DWORD *pdwInputMaximum, + /* [out] */ _Out_ DWORD *pdwOutputMinimum, + /* [out] */ _Out_ DWORD *pdwOutputMaximum); + + STDMETHODIMP GetStreamCount( + /* [out] */ _Out_ DWORD *pcInputStreams, + /* [out] */ _Out_ DWORD *pcOutputStreams); + + STDMETHODIMP GetStreamIDs( + DWORD dwInputIDArraySize, + /* [size_is][out] */ _Out_writes_(dwInputIDArraySize) DWORD *pdwInputIDs, + DWORD dwOutputIDArraySize, + /* [size_is][out] */ _Out_writes_(dwOutputIDArraySize) DWORD *pdwOutputIDs); + + STDMETHODIMP GetInputStreamInfo( + DWORD dwInputStreamID, + /* [out] */ _Out_ MFT_INPUT_STREAM_INFO *pStreamInfo); + + STDMETHODIMP GetOutputStreamInfo( + DWORD dwOutputStreamID, + /* [out] */ _Out_ MFT_OUTPUT_STREAM_INFO *pStreamInfo); + + STDMETHODIMP GetAttributes( + /* [out] */ _Outptr_result_maybenull_ IMFAttributes **pAttributes); + + STDMETHODIMP GetInputStreamAttributes( + DWORD dwInputStreamID, + /* [out] */ _Outptr_result_maybenull_ IMFAttributes **pAttributes); + + STDMETHODIMP GetOutputStreamAttributes( + DWORD dwOutputStreamID, + /* [out] */ _Outptr_result_maybenull_ IMFAttributes **pAttributes); + + STDMETHODIMP DeleteInputStream( + DWORD dwStreamID); + + STDMETHODIMP AddInputStreams( + DWORD cStreams, + /* [in] */ _In_ DWORD *adwStreamIDs); + + STDMETHODIMP GetInputAvailableType( + DWORD dwInputStreamID, + DWORD dwTypeIndex, + /* [out] */ _Outptr_result_maybenull_ IMFMediaType **ppType); + + STDMETHODIMP GetOutputAvailableType( + DWORD dwOutputStreamID, + DWORD dwTypeIndex, + /* [out] */ _Outptr_result_maybenull_ IMFMediaType **ppType); + + STDMETHODIMP SetInputType( + DWORD dwInputStreamID, + /* [in] */ _In_opt_ IMFMediaType *pType, + DWORD dwFlags); + + STDMETHODIMP SetOutputType( + DWORD dwOutputStreamID, + /* [in] */ _In_opt_ IMFMediaType *pType, + DWORD dwFlags); + + STDMETHODIMP GetInputCurrentType( + DWORD dwInputStreamID, + /* [out] */ _Outptr_result_maybenull_ IMFMediaType **ppType); + + STDMETHODIMP GetOutputCurrentType( + DWORD dwOutputStreamID, + /* [out] */ _Outptr_result_maybenull_ IMFMediaType **ppType); + + STDMETHODIMP GetInputStatus( + DWORD dwInputStreamID, + /* [out] */ _Out_ DWORD *pdwFlags); + + STDMETHODIMP GetOutputStatus( + /* [out] */ _Out_ DWORD *pdwFlags); + + STDMETHODIMP SetOutputBounds( + LONGLONG hnsLowerBound, + LONGLONG hnsUpperBound); + + STDMETHODIMP ProcessEvent( + DWORD dwInputStreamID, + /* [in] */ _In_opt_ IMFMediaEvent *pEvent); + + STDMETHODIMP ProcessMessage( + MFT_MESSAGE_TYPE eMessage, + ULONG_PTR ulParam); + + STDMETHODIMP ProcessInput( + DWORD dwInputStreamID, + IMFSample *pSample, + DWORD dwFlags); + + STDMETHODIMP ProcessOutput( + DWORD dwFlags, + DWORD cOutputBufferCount, + /* [size_is][out][in] */ MFT_OUTPUT_DATA_BUFFER *pOutputSamples, + /* [out] */ DWORD *pdwStatus); + +protected: + virtual ~CMft0() + { + SAFERELEASE(m_pInputAttributes); + SAFERELEASE(m_pGlobalAttributes); + SAFERELEASE(m_pSourceTransform); + SAFERELEASE(m_pInputType); + SAFERELEASE(m_pOutputType); + SAFERELEASE(m_pSample); + DeleteCriticalSection(&m_critSec); + } + + STDMETHODIMP GetMediaType(DWORD dwStreamID, DWORD dwTypeIndex, IMFMediaType **ppType); + STDMETHODIMP IsMediaTypeSupported(UINT uiStreamId, IMFMediaType *pIMFMediaType, IMFMediaType **ppIMFMediaTypeFull = NULL); + STDMETHODIMP GenerateMFMediaTypeListFromDevice(UINT uiStreamId); + + // HasPendingOutput: Returns TRUE if the MFT is holding an input sample. + BOOL HasPendingOutput() const { return m_pSample != NULL; } + + // IsValidInputStream: Returns TRUE if dwInputStreamID is a valid input stream identifier. + BOOL IsValidInputStream(DWORD dwInputStreamID); + + // IsValidOutputStream: Returns TRUE if dwOutputStreamID is a valid output stream identifier. + BOOL IsValidOutputStream(DWORD dwOutputStreamID); + + STDMETHODIMP GetDefaultStride(LONG *plStride); + + STDMETHODIMP OnProcessOutput(IMFMediaBuffer *pIn, IMFMediaBuffer *pOut); + + STDMETHODIMP OnFlush(); + + CRITICAL_SECTION m_critSec; + + IMFSample *m_pSample; // Input sample. + IMFMediaType *m_pInputType; // Input media type. + IMFMediaType *m_pOutputType; // Output media type. + + // Image transform function. (Changes based on the media type.) + IMFAttributes *m_pInputAttributes; + IMFAttributes *m_pGlobalAttributes; + GUID m_stStreamType; + IMFTransform *m_pSourceTransform; + volatile BOOL m_bEnableEffects; + UINT m_uiStreamId; + UINT m_percentOfScreen; + UINT m_nRefCount; + CAtlMap> m_listOfMediaTypes; +}; + +OBJECT_ENTRY_AUTO(__uuidof(Mft0), CMft0) diff --git a/avstream/samplemft0/Mft0.rgs b/avstream/samplemft0/Mft0.rgs new file mode 100644 index 00000000..9ff876f4 --- /dev/null +++ b/avstream/samplemft0/Mft0.rgs @@ -0,0 +1,15 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {7BB640D9-33A4-4759-B290-F41A31DCF848} = s 'Mft0 Class' + { + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + TypeLib = s '{DE05674A-C564-4C0E-9B7C-E1519F7AA767}' + Version = s '1.0' + } + } +} diff --git a/avstream/samplemft0/ReadMe.md b/avstream/samplemft0/ReadMe.md new file mode 100644 index 00000000..94a09507 --- /dev/null +++ b/avstream/samplemft0/ReadMe.md @@ -0,0 +1,28 @@ +Driver MFT Sample +================= + +Provides a *driver MFT* for use with a camera's Windows Store device app.A *driver MFT* is a Media Foundation Transform that's used with a specific camera when capturing video. The driver MFT is also known as MFT0 because it is the first MFT applied to the video stream captured from the camera. This MFT can provide a video effect or other processing when capturing photos or video from the camera. It can be distributed along with the driver package for a camera. + +In this sample, the driver MFT, when enabled, replaces a portion of the captured video with a green box. To test this sample, download the [Windows Store device app for camera sample](http://go.microsoft.com/fwlink/p/?linkid=249442) and the [Camera Capture UI sample](http://go.microsoft.com/fwlink/p/?linkid=249441). The [Windows Store device app for camera sample](http://go.microsoft.com/fwlink/p/?linkid=249442) provides a *Windows Store device app* that controls the effect implemented by the driver MFT. The [Camera Capture UI sample](http://go.microsoft.com/fwlink/p/?linkid=249441) provides a way to invoke the *Windows Store device app*. + +This sample is designed to be used with a specific camera. To run the sample, you need the your camera's device ID and device metadata package. + + +Related topics +-------------- + +**Concepts** + +[Windows Store device apps for cameras](http://go.microsoft.com/fwlink/p/?LinkId=306683) + +[Windows 8 device experience](http://go.microsoft.com/fwlink/p/?linkid=241442) + +[Media Foundation Transforms](http://msdn.microsoft.com/en-us/library/windows/hardware/ms703138) + +[Roadmap for Developing Streaming Media Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff568130) + +**Samples** + +[Windows Store device app for camera sample](http://go.microsoft.com/fwlink/p/?linkid=249442) + +[Camera Capture UI sample](http://go.microsoft.com/fwlink/p/?linkid=249441%20) diff --git a/avstream/samplemft0/SampleHelpers.h b/avstream/samplemft0/SampleHelpers.h new file mode 100644 index 00000000..870e2cd3 --- /dev/null +++ b/avstream/samplemft0/SampleHelpers.h @@ -0,0 +1,143 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once +#include "stdafx.h" + +#define WIDEN2(x) L ## x +#define WIDEN(x) WIDEN2(x) +#define __WFILE__ WIDEN(__FILE__) + + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(WPPCameraExtensionMFt_Sample, \ + (31A88041,BBF0,4D2C,A752,16EB1E51859C), \ + WPP_DEFINE_BIT(AllMessages) \ + ) + +#define SAFEFREE(x) \ + if(x) { \ + free(x); \ + x = NULL; \ + } +#define SAFERELEASE(x) \ + if(x) { \ + x->Release(); \ + x = NULL; \ + } + +#define CHK_LOG_BRK(exp) \ + if(FAILED(hr = (exp))) { \ + wprintf(L"HR=%08x File: %S Ln: %d\n", hr, __FILE__, __LINE__); \ + break; \ + } + +#define CHK_NULL_BRK(exp) \ + if((exp) == NULL) { \ + hr = E_OUTOFMEMORY; \ + wprintf(L"HR=%08x File: %S Ln: %d\n", hr, __FILE__, __LINE__); \ + break; \ + } +#define CHK_NULL_PTR_BRK(exp) \ + if((exp) == NULL) { \ + hr = E_INVALIDARG; \ + wprintf(L"HR=%08x File: %S Ln: %d\n", hr, __FILE__, __LINE__); \ + break; \ + } + +#define CHK_BOOL_BRK(exp) \ + if(!exp) { \ + hr = E_FAIL; \ + wprintf(L"HR=%08x File: %S Ln: %d\n", hr, __FILE__, __LINE__); \ + break; \ + } + + +class VideoBufferLock +{ +public: + VideoBufferLock(IMFMediaBuffer *pBuffer) : m_p2DBuffer(NULL) + { + m_pBuffer = pBuffer; + m_pBuffer->AddRef(); + + // Query for the 2-D buffer interface. OK if this fails. + m_pBuffer->QueryInterface(IID_IMF2DBuffer, (void**)&m_p2DBuffer); + } + + ~VideoBufferLock() + { + UnlockBuffer(); + SAFERELEASE(m_pBuffer); + SAFERELEASE(m_p2DBuffer); + } + + // LockBuffer: + // Locks the buffer. Returns a pointer to scan line 0 and returns the stride. + + // The caller must provide the default stride as an input parameter, in case + // the buffer does not expose IMF2DBuffer. You can calculate the default stride + // from the media type. + + HRESULT LockBuffer( + LONG lDefaultStride, // Minimum stride (with no padding). + DWORD dwHeightInPixels, // Height of the image, in pixels. + BYTE **ppbScanLine0, // Receives a pointer to the start of scan line 0. + LONG *plStride // Receives the actual stride. + ) + { + HRESULT hr = S_OK; + + // Use the 2-D version if available. + if (m_p2DBuffer) + { + hr = m_p2DBuffer->Lock2D(ppbScanLine0, plStride); + } + else + { + // Use non-2D version. + BYTE *pData = NULL; + + hr = m_pBuffer->Lock(&pData, NULL, NULL); + if (SUCCEEDED(hr)) + { + *plStride = lDefaultStride; + if (lDefaultStride < 0) + { + // Bottom-up orientation. Return a pointer to the start of the + // last row *in memory* which is the top row of the image. + *ppbScanLine0 = pData + abs(lDefaultStride) * (dwHeightInPixels - 1); + } + else + { + // Top-down orientation. Return a pointer to the start of the + // buffer. + *ppbScanLine0 = pData; + } + } + } + return hr; + } + + HRESULT UnlockBuffer() + { + if (m_p2DBuffer) + { + return m_p2DBuffer->Unlock2D(); + } + else + { + return m_pBuffer->Unlock(); + } + } + +private: + IMFMediaBuffer *m_pBuffer; + IMF2DBuffer *m_p2DBuffer; +}; + diff --git a/avstream/samplemft0/SampleMft0.cpp b/avstream/samplemft0/SampleMft0.cpp new file mode 100644 index 00000000..997f05af --- /dev/null +++ b/avstream/samplemft0/SampleMft0.cpp @@ -0,0 +1,83 @@ +// SampleMft0.cpp : Implementation of DLL Exports. + + +#include "stdafx.h" +#include "resource.h" +#include "SampleMft0.h" +#include "dllmain.h" + + +// Used to determine whether the DLL can be unloaded by OLE. +STDAPI DllCanUnloadNow(void) +{ +#ifdef _MERGE_PROXYSTUB + HRESULT hr = PrxDllCanUnloadNow(); + if (hr != S_OK) + return hr; +#endif + return _AtlModule.DllCanUnloadNow(); +} + +// Returns a class factory to create an object of the requested type. +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ +#ifdef _MERGE_PROXYSTUB + if (PrxDllGetClassObject(rclsid, riid, ppv) == S_OK) + return S_OK; +#endif + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + +// DllRegisterServer - Adds entries to the system registry. +STDAPI DllRegisterServer(void) +{ + // registers object, typelib and all interfaces in typelib + HRESULT hr = _AtlModule.DllRegisterServer(); +#ifdef _MERGE_PROXYSTUB + if (FAILED(hr)) + return hr; + hr = PrxDllRegisterServer(); +#endif + return hr; +} + +// DllUnregisterServer - Removes entries from the system registry. +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _AtlModule.DllUnregisterServer(); +#ifdef _MERGE_PROXYSTUB + if (FAILED(hr)) + return hr; + hr = PrxDllRegisterServer(); + if (FAILED(hr)) + return hr; + hr = PrxDllUnregisterServer(); +#endif + return hr; +} + +// DllInstall - Adds/Removes entries to the system registry per user per machine. +STDAPI DllInstall(BOOL bInstall, _In_opt_ LPCWSTR pszCmdLine) +{ + UNREFERENCED_PARAMETER(pszCmdLine); + HRESULT hr = E_FAIL; + static const wchar_t szUserSwitch[] = L"user"; + + + if (bInstall) + { + hr = DllRegisterServer(); + if (FAILED(hr)) + { + DllUnregisterServer(); + } + } + else + { + hr = DllUnregisterServer(); + } + + return hr; +} + + diff --git a/avstream/samplemft0/SampleMft0.def b/avstream/samplemft0/SampleMft0.def new file mode 100644 index 00000000..f4c9fe1c --- /dev/null +++ b/avstream/samplemft0/SampleMft0.def @@ -0,0 +1,16 @@ +; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +; ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +; THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +; PARTICULAR PURPOSE. +; Copyright (c) Microsoft Corporation. All rights reserved + +; SampleMft0.def : Declares the module parameters. + +LIBRARY + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + DllInstall PRIVATE diff --git a/avstream/samplemft0/SampleMft0.idl b/avstream/samplemft0/SampleMft0.idl new file mode 100644 index 00000000..3b59f79a --- /dev/null +++ b/avstream/samplemft0/SampleMft0.idl @@ -0,0 +1,48 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved + +// SampleMft0.idl : IDL source for SampleMft0 +// + +// This file will be processed by the MIDL tool to +// produce the type library (SampleMft0.tlb) and marshalling code. + +import "oaidl.idl"; +import "ocidl.idl"; +import "Inspectable.idl"; +import "mftransform.idl"; +[ + object, + uuid(F5208B72-A37A-457E-A309-AE3060780E21), + oleautomation, + nonextensible, + pointer_default(unique) +] +interface IMft0 : IUnknown{ + [id(1)] HRESULT UpdateDsp([in] UINT32 uiPercentOfScreen); + [id(2)] HRESULT Enable(void); + [id(3)] HRESULT Disable(void); + [id(4)] HRESULT GetDspSetting([out] UINT* puiPercentOfScreen, [out] BOOL* pIsEnabled); +}; +[ + uuid(DE05674A-C564-4C0E-9B7C-E1519F7AA767), + version(1.0), +] +library SampleMft0Lib +{ + importlib("stdole2.tlb"); + [ + uuid(7BB640D9-33A4-4759-B290-F41A31DCF848) + ] + coclass Mft0 + { + [default] interface IMft0; + interface IInspectable; + interface IMFTransform; + }; +}; + diff --git a/avstream/samplemft0/SampleMft0.rc b/avstream/samplemft0/SampleMft0.rc new file mode 100644 index 00000000..60d75128 Binary files /dev/null and b/avstream/samplemft0/SampleMft0.rc differ diff --git a/avstream/samplemft0/SampleMft0.rgs b/avstream/samplemft0/SampleMft0.rgs new file mode 100644 index 00000000..e7d37400 --- /dev/null +++ b/avstream/samplemft0/SampleMft0.rgs @@ -0,0 +1,3 @@ +HKCR +{ +} diff --git a/avstream/samplemft0/SampleMft0.sln b/avstream/samplemft0/SampleMft0.sln new file mode 100644 index 00000000..fc84dbf4 --- /dev/null +++ b/avstream/samplemft0/SampleMft0.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleMft0", "SampleMft0.vcxproj", "{3661ABB3-1FA5-44BC-8027-1B444A275EEE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3661ABB3-1FA5-44BC-8027-1B444A275EEE}.Debug|Win32.ActiveCfg = Debug|Win32 + {3661ABB3-1FA5-44BC-8027-1B444A275EEE}.Debug|Win32.Build.0 = Debug|Win32 + {3661ABB3-1FA5-44BC-8027-1B444A275EEE}.Release|Win32.ActiveCfg = Release|Win32 + {3661ABB3-1FA5-44BC-8027-1B444A275EEE}.Release|Win32.Build.0 = Release|Win32 + {3661ABB3-1FA5-44BC-8027-1B444A275EEE}.Debug|x64.ActiveCfg = Debug|x64 + {3661ABB3-1FA5-44BC-8027-1B444A275EEE}.Debug|x64.Build.0 = Debug|x64 + {3661ABB3-1FA5-44BC-8027-1B444A275EEE}.Release|x64.ActiveCfg = Release|x64 + {3661ABB3-1FA5-44BC-8027-1B444A275EEE}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/avstream/samplemft0/SampleMft0.vcxproj b/avstream/samplemft0/SampleMft0.vcxproj new file mode 100644 index 00000000..0bf821f3 --- /dev/null +++ b/avstream/samplemft0/SampleMft0.vcxproj @@ -0,0 +1,221 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {3661ABB3-1FA5-44BC-8027-1B444A275EEE} + $(MSBuildProjectName) + Debug + Win32 + {47328477-96F3-446D-8354-FC9AC4B6154D} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + SampleMft0 + Static + + + SampleMft0 + Static + + + SampleMft0 + Static + + + SampleMft0 + Static + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(IntDir) + + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;comdlg32.lib;gdi32.lib;kernel32.lib;mf.lib;mfplat.lib;mfuuid.lib;ntdll.lib;ole32.lib;Oleaut32.lib;runtimeobject.lib;Shlwapi.lib;user32.lib;uuid.lib;version.lib;winmm.lib + SampleMft0.def + + + + + + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;comdlg32.lib;gdi32.lib;kernel32.lib;mf.lib;mfplat.lib;mfuuid.lib;ntdll.lib;ole32.lib;Oleaut32.lib;runtimeobject.lib;Shlwapi.lib;user32.lib;uuid.lib;version.lib;winmm.lib + SampleMft0.def + + + + + + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;comdlg32.lib;gdi32.lib;kernel32.lib;mf.lib;mfplat.lib;mfuuid.lib;ntdll.lib;ole32.lib;Oleaut32.lib;runtimeobject.lib;Shlwapi.lib;user32.lib;uuid.lib;version.lib;winmm.lib + SampleMft0.def + + + + + + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;comdlg32.lib;gdi32.lib;kernel32.lib;mf.lib;mfplat.lib;mfuuid.lib;ntdll.lib;ole32.lib;Oleaut32.lib;runtimeobject.lib;Shlwapi.lib;user32.lib;uuid.lib;version.lib;winmm.lib + SampleMft0.def + + + + + + + + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.h.pch + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.h.pch + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.h.pch + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Create + $(IntDir)\stdafx.h.pch + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/avstream/samplemft0/SampleMft0.vcxproj.Filters b/avstream/samplemft0/SampleMft0.vcxproj.Filters new file mode 100644 index 00000000..b8df8584 --- /dev/null +++ b/avstream/samplemft0/SampleMft0.vcxproj.Filters @@ -0,0 +1,45 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {60B49576-F845-4721-98B6-4B94304459BD} + + + h;hpp;hxx;hm;inl;inc;xsd + {176EFA30-A762-4CC7-98E3-21230AC1411A} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {87076883-275D-4C8B-84E7-B0108688E1C1} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/avstream/samplemft0/dllmain.cpp b/avstream/samplemft0/dllmain.cpp new file mode 100644 index 00000000..df86088a --- /dev/null +++ b/avstream/samplemft0/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Implementation of DllMain. + +#include "stdafx.h" +#include "resource.h" +#include "SampleMft0.h" +#include "dllmain.h" + +CSampleMft0Module _AtlModule; + +// DLL Entry Point +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ +#ifdef _MERGE_PROXYSTUB + if (!PrxDllMain(hInstance, dwReason, lpReserved)) + return FALSE; +#endif + hInstance; + return _AtlModule.DllMain(dwReason, lpReserved); +} diff --git a/avstream/samplemft0/dllmain.h b/avstream/samplemft0/dllmain.h new file mode 100644 index 00000000..04e95ab3 --- /dev/null +++ b/avstream/samplemft0/dllmain.h @@ -0,0 +1,10 @@ +// dllmain.h : Declaration of module class. + +class CSampleMft0Module : public ATL::CAtlDllModuleT< CSampleMft0Module > +{ +public : + DECLARE_LIBID(LIBID_SampleMft0Lib) + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_SAMPLEMFT0, "{F9ED475B-DC47-458A-A138-55D7E8A57AF0}") +}; + +extern class CSampleMft0Module _AtlModule; diff --git a/avstream/samplemft0/resource.h b/avstream/samplemft0/resource.h new file mode 100644 index 00000000..d20cf5a1 Binary files /dev/null and b/avstream/samplemft0/resource.h differ diff --git a/avstream/samplemft0/stdafx.cpp b/avstream/samplemft0/stdafx.cpp new file mode 100644 index 00000000..feacb859 --- /dev/null +++ b/avstream/samplemft0/stdafx.cpp @@ -0,0 +1,13 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved + + +// stdafx.cpp : source file that includes just the standard includes +// SampleMft0.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" diff --git a/avstream/samplemft0/stdafx.h b/avstream/samplemft0/stdafx.h new file mode 100644 index 00000000..86d812ec --- /dev/null +++ b/avstream/samplemft0/stdafx.h @@ -0,0 +1,42 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, +// but are changed infrequently + +#pragma once + +#ifndef STRICT +#define STRICT +#endif + +#include "targetver.h" + +#define _ATL_APARTMENT_THREADED + +#define _ATL_NO_AUTOMATIC_NAMESPACE + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit + + +#define ATL_NO_ASSERT_ON_DESTROY_NONEXISTENT_WINDOW + +#include "resource.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include \ No newline at end of file diff --git a/avstream/samplemft0/stdafxsrc.cpp b/avstream/samplemft0/stdafxsrc.cpp new file mode 100644 index 00000000..1577c4e3 --- /dev/null +++ b/avstream/samplemft0/stdafxsrc.cpp @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/avstream/samplemft0/targetver.h b/avstream/samplemft0/targetver.h new file mode 100644 index 00000000..fc349fa5 --- /dev/null +++ b/avstream/samplemft0/targetver.h @@ -0,0 +1,15 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/bluetooth/serialhcibus/Fdo.c b/bluetooth/serialhcibus/Fdo.c new file mode 100644 index 00000000..c7fcc4ff --- /dev/null +++ b/bluetooth/serialhcibus/Fdo.c @@ -0,0 +1,2165 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + Fdo.c + +Abstract: + + This module contains routines to handle the function driver + aspect of the bus driver. + +Environment: + + kernel mode only + +--*/ + +#include "driver.h" +#include +#include "fdo.tmh" + +#define BTHX_VALID_WRITE_PACKET_TYPE(type) (type == HciPacketCommand || type == HciPacketAclData) +#define BTHX_VALID_READ_PACKET_TYPE(type) (type == HciPacketEvent || type == HciPacketAclData) + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, FdoCreateOneChildDevice) +#pragma alloc_text (PAGE, FdoRemoveOneChildDevice) +#pragma alloc_text (PAGE, FdoCreateAllChildren) +#pragma alloc_text (PAGE, FdoFindConnectResources) +#pragma alloc_text (PAGE, FdoDevPrepareHardware) +#pragma alloc_text (PAGE, FdoDevReleaseHardware) +#pragma alloc_text (PAGE, FdoDevSelfManagedIoInit) +#pragma alloc_text (PAGE, FdoDevSelfManagedIoCleanup) +#pragma alloc_text (PAGE, FdoDevD0Exit) +#pragma alloc_text (PAGE, HlpInitializeFdoExtension) +#pragma alloc_text (PAGE, FdoWriteToDeviceSync) +#endif + +// +// Child device node, PDO(s), could be enumerated statically if number of PDOs are known +// at driver start, or dynamic enuermation mechanism is used. Both methods are presented +// in this code, but only one can be chosen using the define macro (see sources file). +// +#ifdef DYNAMIC_ENUM + +typedef struct _ENABLE_PDO_CONTEXT { + WDFDEVICE Fdo; +} ENABLE_PDO_CONTEXT, *PENABLE_PDO_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(ENABLE_PDO_CONTEXT, GetEnablePdoWorkItemContext) + +// Timeout used to delay dev node enuemeration +ULONG g_WaitToEnablePDO = 20000; // MSec + +VOID +DeviceEnablePDOWorker( + _In_ WDFWORKITEM _WorkItem + ) +/*++ +Routine Description: + + A work item function to dynamically enuermate a PDO. + +Arguments: + + _pWorkItem - work item that contains a context to help carrying out its task + +Return Value: +--*/ +{ + PENABLE_PDO_CONTEXT Context; + LARGE_INTEGER RemoteWakeTimeout; + + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP, ("+DeviceEnablePDOWorker")); + Context = GetEnablePdoWorkItemContext(_WorkItem); + + RemoteWakeTimeout.QuadPart = WDF_REL_TIMEOUT_IN_MS(g_WaitToEnablePDO); + KeDelayExecutionThread(KernelMode, FALSE, &RemoteWakeTimeout); + + DoTrace(LEVEL_INFO, TFLAG_PNP, ("+Complete the wait")); + + Status = FdoCreateOneChildDeviceDynamic(Context->Fdo, + BT_PDO_HARDWARE_IDS, + sizeof(BT_PDO_HARDWARE_IDS)/sizeof(WCHAR), + BLUETOOTH_FUNC_IDS ); + + DoTrace(LEVEL_INFO, TFLAG_POWER, ("-DeviceEnablePDOWorker %!STATUS!", Status)); + +} + +NTSTATUS +FdoEvtDeviceListCreatePdo( + WDFCHILDLIST DeviceList, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription, + PWDFDEVICE_INIT ChildInit + ) +/*++ + +Routine Description: + + Called by the framework in response to Query-Device relation when + a new PDO for a child device needs to be created. + +Arguments: + + DeviceList - Handle to the default WDFCHILDLIST created by the framework as part + of FDO. + + IdentificationDescription - Decription of the new child device. + + ChildInit - It's a opaque structure used in collecting device settings + and passed in as a parameter to CreateDevice. + +Return Value: + + NT Status code. + +--*/ +{ + PPDO_IDENTIFICATION_DESCRIPTION pDesc; + + PAGED_CODE(); + + pDesc = CONTAINING_RECORD(IdentificationDescription, + PDO_IDENTIFICATION_DESCRIPTION, + Header); + + return PdoCreateDynamic(WdfChildListGetDevice(DeviceList), + ChildInit, + pDesc->HardwareIds, + pDesc->SerialNo); +} + + +NTSTATUS +FdoCreateOneChildDeviceDynamic( + _In_ WDFDEVICE _Device, + _In_ PWCHAR _HardwareIds, + _In_ size_t _CchHardwareIds, + _In_ ULONG _SerialNo + ) + +/*++ + +Routine Description: + + The trigger event has been signalled that a new device on the bus has arrived. + + We therefore create a description structure in stack, fill in information about + the child device and call WdfChildListAddOrUpdateChildDescriptionAsPresent + to add the device. + +--*/ + +{ + PDO_IDENTIFICATION_DESCRIPTION Description; + NTSTATUS Status; + + PAGED_CODE (); + + // + // Initialize the description with the information about the newly + // plugged in device. + // + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&Description.Header, + sizeof(Description)); + + Description.SerialNo = _SerialNo; + Description.CchHardwareIds = _CchHardwareIds; + Description.HardwareIds = _HardwareIds; + + // + // Call the framework to add this child to the childlist. This call + // will internaly call our DescriptionCompare callback to check + // whether this device is a new device or existing device. If + // it's a new device, the framework will call DescriptionDuplicate to create + // a copy of this description in nonpaged pool. + // The actual creation of the child device will happen when the framework + // receives QUERY_DEVICE_RELATION request from the PNP manager in + // response to InvalidateDeviceRelations call made as part of adding + // a new child. + // + Status = WdfChildListAddOrUpdateChildDescriptionAsPresent(WdfFdoGetDefaultChildList(_Device), + &Description.Header, + NULL); // AddressDescription + + if (Status == STATUS_OBJECT_NAME_EXISTS) { + // + // The description is already present in the list, the serial number is + // not unique, return error. + // + Status = STATUS_INVALID_PARAMETER; + } + + return Status; +} + +#endif // ifdef DYNAMIC_ENUM + + +NTSTATUS +FdoCreateOneChildDevice( + _In_ WDFDEVICE _Device, + _In_ PWSTR _HardwareIds, + _In_ ULONG _SerialNo + ) +/*++ + +Routine Description: + + Create a new PDO, initialize it, add it to the list of PDOs for this + FDO bus. + +Arguments: + + _Device - WDF device object + + _HardwareIDs - hardware Id for a device + + _SerialNo - Unique ID for a child DO + +Returns: + + Status + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN IsUnique = TRUE; + WDFDEVICE ChildDevice; + PPDO_EXTENSION PdoExtension; + PFDO_EXTENSION FdoExtension; + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP, ("+ FdoCreateOneChildDevice() HWID: %S", _HardwareIds)); + + // + // First make sure that we don't already have another device with the + // same serial number. + // Framework creates a collection of all the child devices we have + // created so far. So acquire the handle to the collection and lock + // it before walking the item. + // + FdoExtension = FdoGetExtension(_Device); + ChildDevice = NULL; + + // + // We need an additional lock to synchronize addition because + // WdfFdoLockStaticChildListForIteration locks against anyone immediately + // updating the static child list (the changes are put on a queue until the + // list has been unlocked). This type of lock does not enforce our concept + // of unique IDs on the bus (ie SerialNo). + // + // Without our additional lock, 2 threads could execute this function, both + // find that the requested SerialNo is not in the list and attempt to add + // it. If that were to occur, 2 PDOs would have the same unique SerialNo, + // which is incorrect. + // + // We must use a passive level lock because you can only call WdfDeviceCreate + // at PASSIVE_LEVEL. + // + WdfWaitLockAcquire(FdoExtension->ChildLock, NULL); + WdfFdoLockStaticChildListForIteration(_Device); + + while ((ChildDevice = WdfFdoRetrieveNextStaticChild(_Device, + ChildDevice, + WdfRetrieveAddedChildren)) != NULL) { + // + // WdfFdoRetrieveNextStaticChild returns reported and to be reported + // children (ie children who have been added but not yet reported to PNP). + // + // A surprise removed child will not be returned in this list. + // + PdoExtension = PdoGetExtension(ChildDevice); + + // + // It's okay to plug in another device with the same serial number + // as long as the previous one is in a surprise-removed state. The + // previous one would be in that state after the device has been + // physically removed, if somebody has an handle open to it. + // + if (_SerialNo == PdoExtension->SerialNo) { + IsUnique = FALSE; + Status = STATUS_INVALID_PARAMETER; + break; + } + } + + if (IsUnique) { + // + // Create a new child device. It is OK to create and add a child while + // the list locked for enumeration. The enumeration lock applies only + // to enumeration, not addition or removal. + // + Status = PdoCreate(_Device, _HardwareIds, _SerialNo); + } + + WdfFdoUnlockStaticChildListFromIteration(_Device); + WdfWaitLockRelease(FdoExtension->ChildLock); + + DoTrace(LEVEL_INFO, TFLAG_PNP, ("- FdoCreateOneChildDevice() %!STATUS!", Status)); + + return Status; +} + +NTSTATUS +FdoRemoveOneChildDevice( + WDFDEVICE _Device, + ULONG _SerialNo + ) +/*++ + +Routine Description: + + The application has told us a device has departed from the bus. + + We therefore need to flag the PDO as no longer present + and then tell Plug and Play about it. + +Arguments: + + _Device - WDF device object + + _SerialNo - Unique ID for a child DO + +Returns: + + Status + +--*/ + +{ + PPDO_EXTENSION PdoExtension; + BOOLEAN Found = FALSE; + BOOLEAN PlugOutAll; + WDFDEVICE ChildDevice; + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + PlugOutAll = (0 == _SerialNo) ? TRUE : FALSE; + + ChildDevice = NULL; + + WdfFdoLockStaticChildListForIteration(_Device); + + while ((ChildDevice = WdfFdoRetrieveNextStaticChild(_Device, + ChildDevice, + WdfRetrieveAddedChildren)) != NULL) { + if (PlugOutAll) { + + Status = WdfPdoMarkMissing(ChildDevice); + if(!NT_SUCCESS(Status)) { + DoTrace(LEVEL_INFO, TFLAG_PNP, ("WdfPdoMarkMissing failed 0x%x\n", Status)); + break; + } + + Found = TRUE; + } + else { + PdoExtension = PdoGetExtension(ChildDevice); + + if (_SerialNo == PdoExtension->SerialNo) { + + Status = WdfPdoMarkMissing(ChildDevice); + if(!NT_SUCCESS(Status)) { + DoTrace(LEVEL_INFO, TFLAG_PNP, ("WdfPdoMarkMissing failed 0x%x\n", Status)); + break; + } + + Found = TRUE; + break; + } + } + } + + WdfFdoUnlockStaticChildListFromIteration(_Device); + + if (Found) { + Status = STATUS_SUCCESS; + } + + return Status; +} + + +NTSTATUS +FdoCreateAllChildren( + _In_ WDFDEVICE _Device + ) +/*++ +Routine Description: + + The routine enables you to statically enumerate child device functions + during start. + +Arguments: + + _Device - WDF device object + +Returns: + + Status + +--*/ +{ + NTSTATUS Status; + PFDO_EXTENSION FdoExtension; + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP, (" + FdoCreateAllChildren")); + + // + // Bus driver enumerates all child devnode in this function. + // Vendor Specific: retrieve all statically saved devnode info + // HWID, COMPATID, etc. + // + + // + // This sample code only enuemrate the Bluetooth function as the only + // child device. + // + Status = FdoCreateOneChildDevice(_Device, + BT_PDO_HARDWARE_IDS, + BLUETOOTH_FUNC_IDS); + + FdoExtension = FdoGetExtension(_Device); + if (NT_SUCCESS(Status)) { + FdoExtension->IsRadioEnabled = TRUE; + } + + return Status; +} + + + +NTSTATUS +HlpInitializeFdoExtension( + WDFDEVICE _Device + ) +/*++ +Routine Description: + + This helper function initialize the device context. + +Arguments: + + _Device - WDF Device object + +Return Value: + + Status + +--*/ +{ + PFDO_EXTENSION FdoExtension; + WDF_OBJECT_ATTRIBUTES Attributes; + NTSTATUS Status; + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+HlpInitializeFdoExtension")); + + FdoExtension = FdoGetExtension(_Device); + FdoExtension->WdfDevice = _Device; + + // + // Set Bluetooth (PDO) capabilities + // MaxAclTransferInSize - is used by the host to notify the Bluetooth controller + // in HCI_Host_Buffer_Size command to set the maximum size of the data portion + // of an HCI ACL packet that will be sent from the controller to the host. + // BthMini will only send down an HCI read request with this data buffer size. + // + FdoExtension->BthXCaps.MaxAclTransferInSize = MAX_HCI_ACLDATA_SIZE; + FdoExtension->BthXCaps.ScoSupport = ScoSupportHCIBypass; // Only option + FdoExtension->BthXCaps.MaxScoChannels = 1; // Limit to 1 HCIBypass channel + FdoExtension->BthXCaps.IsDeviceIdleCapable = TRUE; // Disable Idle to S0 and wake + FdoExtension->BthXCaps.IsDeviceWakeCapable = FALSE; // Wake from Sx + + // + // Preallocate Request + // + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Attributes.ParentObject = _Device; + + Status = WdfRequestCreate(&Attributes, FdoExtension->IoTargetSerial, &FdoExtension->RequestIoctlSync); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfRequestCreate failed %!STATUS!", Status)); + goto Exit; + } + + FdoExtension->HardwareErrorDetected = FALSE; + + Status = WdfRequestCreate(&Attributes, FdoExtension->IoTargetSerial, &FdoExtension->RequestWaitOnError); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfRequestCreate failed %!STATUS!", Status)); + goto Exit; + } + + Status = WdfMemoryCreatePreallocated(&Attributes, + &FdoExtension->SerErrorMask, + sizeof(FdoExtension->SerErrorMask), + &FdoExtension->WaitMaskMemory); + + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfMemoryCreatePreallocated failed %!STATUS!", Status)); + goto Exit; + } + + KeInitializeSpinLock(&FdoExtension->QueueAccessLock); + +Exit: + + return Status; + +} + + +VOID +FdoEvtDeviceDisarmWake( + _In_ WDFDEVICE _Device + ) +/*++ +Routine Description: + + + This function is invoked by the framework after the bus driver determines + that an event has awakened the device, and after the bus driver subsequently + completes the wait/wake IRP. + + This function perform any hardware operations that are needed to disable + the device's ability to trigger a wake signal after the power has been lowered. + +Arguments: + + _Device - WDF Device object + +Return Value: + + VOID + +--*/ +{ + UNREFERENCED_PARAMETER(_Device); + DoTrace(LEVEL_INFO, TFLAG_PNP,(" FdoEvtDeviceDisarmWake")); +} + + +NTSTATUS +FdoEvtDeviceArmWake( + _In_ WDFDEVICE _Device + ) +/*++ +Routine Description: + + This function is invoked while the device is still in the D0 device power state, + before the bus driver lowers the device's power state but after the framework + has sent a wait/wake IRP on behalf of the driver. + +Arguments: + + _Device - WDF Device object + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + UNREFERENCED_PARAMETER(_Device); + + DoTrace(LEVEL_INFO, TFLAG_PNP,(" FdoEvtDeviceArmWake")); + + return Status; +} + +NTSTATUS +FdoFindConnectResources( + _In_ WDFDEVICE _Device, + _In_ WDFCMRESLIST _ResourcesRaw, + _In_ WDFCMRESLIST _ResourcesTranslated + ) +/*++ + +Routine Description: + + This routine enuermates and finds specific connection resources and cache them. + +Arguments: + + _Device - Supplies a handle to a framework device object. + + _ResourcesRaw - Supplies a handle to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + _ResourcesTranslated - Supplies a handle to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + NT Status code. + +--*/ + +{ + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + PFDO_EXTENSION FdoExtension; + ULONG Index; + ULONG ResourceCount = 0; + NTSTATUS Status; + BOOLEAN UartConnectionIdIsFound = FALSE; + + UNREFERENCED_PARAMETER(_ResourcesRaw); + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoFindConnectResources")); + + FdoExtension = FdoGetExtension(_Device); + + Status = STATUS_SUCCESS; + + // + // Walk through the resource list and find and cache expected resources. + // + + ResourceCount = WdfCmResourceListGetCount(_ResourcesTranslated); + + for (Index = 0; Index < ResourceCount; Index++) + { + Descriptor = WdfCmResourceListGetDescriptor(_ResourcesTranslated, Index); + + switch(Descriptor->Type) + { + case CmResourceTypeConnection: + + // + // Cache connetion ID that this BT Peripheral device is connected to + // - UART (must exist) + // - GPIO (optional) + // + + if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL) && + (Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_UART)) + { + NT_ASSERT(UartConnectionIdIsFound == FALSE && L"More than one set of UART connection"); + + UartConnectionIdIsFound = TRUE; + + FdoExtension->UARTConnectionId.LowPart = Descriptor->u.Connection.IdLowPart; + FdoExtension->UARTConnectionId.HighPart = Descriptor->u.Connection.IdHighPart; + + DoTrace(LEVEL_INFO, TFLAG_PNP,(" UART ConnectionID (0x%x, 0x%x)", + FdoExtension->UARTConnectionId.HighPart, FdoExtension->UARTConnectionId.LowPart)); + } + else if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL) && + (Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C)) + { + + FdoExtension->I2CConnectionId.LowPart = Descriptor->u.Connection.IdLowPart; + FdoExtension->I2CConnectionId.HighPart = Descriptor->u.Connection.IdHighPart; + + DoTrace(LEVEL_INFO, TFLAG_PNP,(" I2C ConnectionID (0x%x, 0x%x)", + FdoExtension->I2CConnectionId.HighPart, FdoExtension->I2CConnectionId.LowPart)); + } + else if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_GPIO) && + (Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_GPIO_IO)) + { + + FdoExtension->GPIOConnectionId.LowPart = Descriptor->u.Connection.IdLowPart; + FdoExtension->GPIOConnectionId.HighPart = Descriptor->u.Connection.IdHighPart; + + DoTrace(LEVEL_INFO, TFLAG_PNP,(" GPIO ConnectionID (0x%x, 0x%x)", + FdoExtension->GPIOConnectionId.HighPart, FdoExtension->GPIOConnectionId.LowPart)); + } + break; + + case CmResourceTypeInterrupt: + + // + // NT Interrupt to support HOST_WAKE for remote wake (TBD) + // + + default: + DoTrace(LEVEL_INFO, TFLAG_PNP,(" Resource type %d not used.", Descriptor->Type)); + break; + } + + } + + + // + // Expect to find UART controller + // + if (!UartConnectionIdIsFound) + { + Status = STATUS_NOT_FOUND; + } + + DoTrace(LEVEL_INFO, TFLAG_PNP,("-FdoFindConnectResources ResourceCount %d, %!STATUS!", ResourceCount, Status)); + + return Status; +} + + + +NTSTATUS +FdoOpenDevice( + _In_ WDFDEVICE _Device, + _Out_ WDFIOTARGET *_pIoTarget + ) +/*++ +Routine Description: + + This function search for a serial port and create a remote IO Target object, + which will be used to send control and data. + +Arguments: + + _Device - WDF Device object + + _pIoTarget - IO Target object to be created in this function + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + WDFIOTARGET IoTargetSerial; + PFDO_EXTENSION FdoExtension = NULL; + WCHAR TargetDeviceNameBuffer[100]; + PWSTR SymbolicLinkList = NULL; + UNICODE_STRING TargetDeviceName; + + WDF_IO_TARGET_OPEN_PARAMS OpenParams; + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoOpenDevice")); + + + Status = WdfIoTargetCreate(_Device, + WDF_NO_OBJECT_ATTRIBUTES, + &IoTargetSerial); + + if (!NT_SUCCESS(Status)) + { + goto Exit; + } + + FdoExtension = FdoGetExtension(_Device); + + // + // On SoC platform, a valid connection ID to a UART is set; if not, the legacy way + // of enumerating serial device interface is used. + // + + if (ValidConnectionID(FdoExtension->UARTConnectionId)) + { + RtlInitEmptyUnicodeString(&TargetDeviceName, + TargetDeviceNameBuffer, + sizeof(TargetDeviceNameBuffer)); + + Status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&TargetDeviceName, + FdoExtension->UARTConnectionId.LowPart, + FdoExtension->UARTConnectionId.HighPart); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_INFO, TFLAG_PNP,(" Failed to construct the open path %!STATUS!", Status)); + goto Exit; + } + } + else + { + // Query the system for device with SERIAL interface + Status = IoGetDeviceInterfaces(&GUID_DEVINTERFACE_COMPORT, + NULL, + 0, + &SymbolicLinkList // List of symbolic names; separate by NULL, EOL with NULL+NULL. + ); + + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_INFO, TFLAG_PNP,("IoGetDeviceInterfaces(): %!STATUS!", Status)); + goto Exit; + } + + // Check for empty list + if (*SymbolicLinkList == L'\0') + { + Status = STATUS_DEVICE_DOES_NOT_EXIST; + goto Exit; + } + + // A list of devices is returned, we use only the first one. + // ACPI component will enuermate us and this step is not necessary. + RtlInitUnicodeString(&TargetDeviceName, SymbolicLinkList); + } + + DoTrace(LEVEL_INFO, TFLAG_PNP, (" Symbolic Name '%S'", TargetDeviceName.Buffer)); + + // + // Open the "remote" IO Target (device) using its symbolic link. + // + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, + &TargetDeviceName, + STANDARD_RIGHTS_ALL); + OpenParams.ShareAccess = 0; // Explicite: Exclusive access + + // + // Open this serial device (Io Target) in order to send IOCTL_SERIAL_* control to it. + // + Status = WdfIoTargetOpen(IoTargetSerial, + &OpenParams); + + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_INFO, TFLAG_PNP, ( " WdfIoTargetOpen failed %!STATUS!", Status)); + WdfObjectDelete(IoTargetSerial); + goto Exit; + } + + *_pIoTarget = IoTargetSerial; + +Exit: + + if (SymbolicLinkList) + { + ExFreePool(SymbolicLinkList); + SymbolicLinkList = NULL; + } + + return Status; +} + + +NTSTATUS +FdoSetIdleSettings( + _In_ WDFDEVICE _Device, + _In_ IDLE_CAP_STATE _IdleCapState + ) +/*++ +Routine Description: + + This function defines how device idle (Dx) is support while system is in + (S0) for the Serial Hci device (not its child node, which is supported + in the PDO). + + If its Enuemrator is "ROOT" (in the case of using a Bluetooth dev board), + its Idle support is IdleCannotWakeFromS0. Its power capabilities are + limited to D0 and D3; it is basically on or off, and there is no Idle + while in S0. + + Vendor: If its Enumerator is ACPI, then it might be possible to support + idle while in S0. This is vendor specific. + +Arguments: + + _Device - WDF Device object + + IDLE_CAP_STATE - The idle capability state to enter + +Return Value: + + NTSTATUS + +--*/ +{ + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS IdleSettings; + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN AssignS0IdleSettings = TRUE; + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoSetIdleSettings")); + + switch (_IdleCapState) + { + case IdleCapActiveOnly: + + // + // By default ACPI supports D0 active, and idle to D3 without remote wake. + // While in D3, only host (e.g. IO request) can wake the device to D0. + // + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&IdleSettings, + IdleCannotWakeFromS0); + + // Low Dx state to enter after IdleTimeout has expired and Idle is enabled. + IdleSettings.DxState = PowerDeviceD3; + IdleSettings.IdleTimeout = IdleTimeoutDefaultValue; // Use default (~5 seconds) + IdleSettings.IdleTimeoutType = DriverManagedIdleTimeout; // Driver is in control (typically for out of SoC). + + // Idle to DxState is not initially disable, and do not allow user control to enable it (as this is active only). + IdleSettings.UserControlOfIdleSettings = IdleDoNotAllowUserControl; + IdleSettings.Enabled = WdfFalse; + + // Do not wake from D3 to D0 due to system wake (Sx to S0); ie only host app can wake. + IdleSettings.PowerUpIdleDeviceOnSystemWake = WdfFalse; + break; + + case IdleCapCanWake: + + // + // If it has a child PDO and there is a controller (GPIO) being configured to support wake, + // this state can be supported. + // + // Vendor: in order to support idle in S0 for this ACPI enumerated device, specify that the device + // can wake in S0. For example, if it can wake from D2 in S0, this should be set in its device section: + // + // Name(_S0W, 0x2) + // + // Additionally, the wake interrupt, e.g. HOST_WAKE, will need to be known by ACPI (instead of exposing + // it directly to this driver as system resource); so that, ACPI will do the arming and wake on this + // driver's behalf with Dx state transition. + // + + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&IdleSettings, + IdleCanWakeFromS0); + + // Low Dx state to enter after IdleTimeout has expired and Idle is enabled. + IdleSettings.DxState = PowerDeviceD2; + IdleSettings.IdleTimeout = 0; // May want to enter D2 immediately and invoke arm wake callback. + IdleSettings.IdleTimeoutType = DriverManagedIdleTimeout; // Driver is in control (typically for out of SoC). + + // Idle to DxState is initially enable, but allow user control as well (e.g to turn off idle support). + IdleSettings.UserControlOfIdleSettings = IdleAllowUserControl; + IdleSettings.Enabled = WdfTrue; + + // + // Note: wiil invoke EvtDeviceArmWakeFromS0 callback before entering DxState; + // Driver can arm for HOST_WAKE interrrupt in the callback. + // + break; + + case IdleCapCanTurnOff: + + // + // If there is no child PDO (e.g. in Radio off mode), in effect the BT radio can be turned off + // to enter D3 state. All unused controllers (e.g. GPIO) can be turned off, also + // the Bluetooth function block. While in D3 state, only host can wake the device. + // + // Here is one approach to prevent the FDO from entering DxState while its PDO is in Dx and there is no pending IO: + // + // The PDO can hold a reference on its parent to prevent the parent from going into DxState. This is done in + // PrepareHardware with WdfDeviceStopIdle() and releasing that reference + // in the PDO's ReleaseHardware with WdfDeviceResumeIdle(). This applies to the case when the PDO is disabled. + // In the resource rebalancing case, the FDO may enter D3 shortly and then resume to D0. + // + + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&IdleSettings, + IdleCannotWakeFromS0); + + // Low Dx state to enter after IdleTimeout has expired and Idle is enabled. + IdleSettings.DxState = PowerDeviceD3; + IdleSettings.IdleTimeout = IdleTimeoutDefaultValue; + IdleSettings.IdleTimeoutType = DriverManagedIdleTimeout; // Driver is in control (typically for out of SoC). + + // Idle to DxState is initially enabled, but allow user control as well (e.g. do not turn off). + IdleSettings.UserControlOfIdleSettings = IdleAllowUserControl; + IdleSettings.Enabled = WdfTrue; + + // Do not wake from D3 to D0 due to system wake (Sx to S0); ie only host app can wake. + IdleSettings.PowerUpIdleDeviceOnSystemWake = WdfFalse; + break; + + default: + AssignS0IdleSettings = FALSE; + break; + } + + if (AssignS0IdleSettings) + { + Status = WdfDeviceAssignS0IdleSettings(_Device, + &IdleSettings); + } + + DoTrace(LEVEL_INFO, TFLAG_PNP,("-FdoSetIdleSettings %!STATUS!", Status)); + return Status; +} + + +NTSTATUS +FdoDevPrepareHardware( + _In_ WDFDEVICE _Device, + _In_ WDFCMRESLIST _ResourcesRaw, + _In_ WDFCMRESLIST _ResourcesTranslated + ) +/*++ +Routine Description: + + This PnP CB function allocate hardware related resource allocation and + perform device initialization. + +Arguments: + + _Device - WDF Device object + + _ResourcesRaw - (Not referenced) + + _ResourcesTranslated - (Not referenced) + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status; + PFDO_EXTENSION FdoExtension; + + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevPrepareHardware")); + + // + // Acquire connection ID of connected controllers (UART and GPIO) + // + Status = FdoFindConnectResources(_Device, + _ResourcesRaw, + _ResourcesTranslated); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" Failed to find connection ID of target UART controller %!STATUS!", Status)); + + // Log(Informational): no UART Connection ID resource + + // Can still use the legacy approach to find it based on its serial interface GUID. + } + + + FdoExtension = FdoGetExtension(_Device); + + // + // Open Bluetooth UART device as a remote IO Target + // + Status = FdoOpenDevice(_Device, &FdoExtension->IoTargetSerial); + + if (!NT_SUCCESS(Status) || FdoExtension->IoTargetSerial == NULL) + { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" FdoOpenDevice failed %!STATUS!", Status)); + + // Log(Error): Failed to open UART controller + goto Exit; + } + + // + // Initialize content of this device extension + // + Status = HlpInitializeFdoExtension(_Device); + + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" HlpInitializeFdoExtension failed %!STATUS!", Status)); + goto Exit; + } + + // + // Set device's idle configuration if it is capable + // + Status = FdoSetIdleSettings(_Device, + IdleCapCanTurnOff); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" FdoSetIdleSettings failed %!STATUS!", Status)); + // goto Exit; + } + + // Enable serial bus device + if (ValidConnectionID(FdoExtension->GPIOConnectionId)) { + Status = DeviceEnable(_Device, TRUE); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_PNP,("DeviceEnable failed %!STATUS!", Status)); + goto Exit; + } + } + + // Power On serial bus device + if (ValidConnectionID(FdoExtension->I2CConnectionId)) { + Status = DevicePowerOn(_Device); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_PNP,("DevicePowerOn failed %!STATUS!", Status)); + goto Exit; + } + } + + // + // Configure local UART controller + // + FdoExtension->DeviceInitialized = DeviceInitialize(FdoExtension, + FdoExtension->IoTargetSerial, + FdoExtension->RequestIoctlSync, + TRUE); + if (!IsDeviceInitialized(FdoExtension)) + { + // Can have issue if this UART device cannot be initalized + Status = STATUS_DEVICE_NOT_READY; + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" DeviceInitialize failed %!STATUS!", Status)); + + // Log(Error): Failed to intialize/configure the device + goto Exit; + } + +#ifdef DYNAMIC_ENUM + // + // This code segment is for testing: spawn a work item to do dynamic enuermation + // of a Bluetooth dev node (PDO); the actual implementation could be to query + // the peripheral device for what function blocks that it can support, or + // to listen for a published interface of its dependent controller driver + // to start the enuermation after driver has started. + // + { + WDF_OBJECT_ATTRIBUTES ObjAttributes; + WDF_WORKITEM_CONFIG WorkitemConfig; + WDFWORKITEM WorkItem; + PENABLE_PDO_CONTEXT Context; + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP, ("+CreateWorkItem to enable PDO")); + + WDF_OBJECT_ATTRIBUTES_INIT(&ObjAttributes); + + WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&ObjAttributes, + ENABLE_PDO_CONTEXT); + ObjAttributes.ParentObject = _Device; + + WDF_WORKITEM_CONFIG_INIT(&WorkitemConfig, DeviceEnablePDOWorker); + Status = WdfWorkItemCreate(&WorkitemConfig, &ObjAttributes, &WorkItem); + + if (NT_SUCCESS(Status)) + { + // Get and initialize the context + Context = GetEnablePdoWorkItemContext(WorkItem); + Context->Fdo = _Device; + + // Initialize work item context + WdfWorkItemEnqueue(WorkItem); + } + } +#else + // + // Perform static PDO enumertion by reading child device info saved in the registry. + // But the info needs to be populated first by acquired supported device for supported + // child devices. + // + Status = FdoCreateAllChildren(_Device); +#endif + +Exit: + + DoTrace(LEVEL_INFO, TFLAG_PNP, ("-FdoDevPrepareHardware %!STATUS!", Status)); + + return Status; +} + + +NTSTATUS +FdoDevReleaseHardware( + _In_ WDFDEVICE _Device, + _In_ WDFCMRESLIST _ResourcesTranslated + ) +/*++ +Routine Description: + + This PnP CB function free resource allocated in FdoDevPrepareHardware. + +Arguments: + + _Device - WDF Device object + + _ResourcesTranslated - (Not referenced) + +Return Value: + + NTSTATUS + +--*/ +{ + PFDO_EXTENSION FdoExtension; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_ResourcesTranslated); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+PnpReleaseHardware")); + + FdoExtension = FdoGetExtension(_Device); + + if (FdoExtension->IoTargetSerial) + { + WdfObjectDelete(FdoExtension->IoTargetSerial); + FdoExtension->IoTargetSerial = NULL; + } + + return STATUS_SUCCESS; +} + + + +NTSTATUS +FdoDevSelfManagedIoInit( + _In_ WDFDEVICE _Device +) +/*++ +Routine Description: + + This PnP CB function is invoked once and will perform IO related resource allocation + and start the read pump. + +Arguments: + + _Device - WDF Device object + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status; + PFDO_EXTENSION FdoExtension; + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevSelfManagedIoInit")); + + // + // Preallocate resources needed to perform read opeations + // + Status = ReadResourcesAllocate(_Device); + + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadResourcesAllocate failed %!STATUS!", Status)); + goto Exit; + } + + // Issue pending IO request to prefetch HCI event and data + FdoExtension = FdoGetExtension(_Device); + FdoExtension->ReadContext.RequestState = REQUEST_COMPLETE; + + // Start the read pump + FdoExtension->ReadPumpRunning = TRUE; + Status = ReadH4Packet(&FdoExtension->ReadContext, + FdoExtension->ReadRequest, + FdoExtension->ReadMemory, + FdoExtension->ReadBuffer, + INITIAL_H4_READ_SIZE); + + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4Packet failed %!STATUS!", Status)); + goto Exit; + } + +Exit: + + return Status; +} + + +VOID +FdoDevSelfManagedIoCleanup( + _In_ WDFDEVICE _Device + ) +/*++ +Routine Description: + + This PnP CB function is invoked once and will be used here to free resource + that was alocated in its corresponding SelfMagedInit fucntion. + +Arguments: + + _Device - WDF Device object + +Return Value: + + none + +--*/ +{ + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevSelfManagedIoCleanup")); + + // + // Cancel and free resources + // + ReadResourcesFree(_Device); + + return; +} + + +NTSTATUS +FdoDevD0Entry( + _In_ WDFDEVICE _Device, + _In_ WDF_POWER_DEVICE_STATE _PreviousState + ) +/*++ +Routine Description: + + This PnP CB function is invoked after device has enter D0 (working) state. Most + of initilization of hardware is already performed in PrepareHardware CB but will + be performed again if the device was resume from non-D0 state. + +Arguments: + + _Device - WDF Device object + + PreviousState - Next power state it is entering from D0 + +Return Value: + + NTSTATUS + +--*/ +{ + PFDO_EXTENSION FdoExtension = FdoGetExtension(_Device); + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(_PreviousState); + + DoTrace(LEVEL_INFO, TFLAG_UART, ("+FdoDevD0Entry")); + + // Reset error count upon resume to D0 + FdoExtension->OutOfSyncErrorCount = 0; + + // Initialize serial port settings if re-enter D0 + if (!IsDeviceInitialized(FdoExtension)) { + + // Enable serial bus device + if (ValidConnectionID(FdoExtension->GPIOConnectionId)) { + Status = DeviceEnable(_Device, TRUE); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_PNP,("DeviceEnable failed %!STATUS!", Status)); + goto Done; + } + } + + // Power On serial bus device + if (ValidConnectionID(FdoExtension->I2CConnectionId)) { + Status = DevicePowerOn(_Device); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_PNP,("DevicePowerOn failed %!STATUS!", Status)); + goto Done; + } + } + + // + // The local UART may need to be re-initialized to match the remote UART if its context + // was lost, but the assumption here is that the UART controller driver does save and + // restore its context. + // +#ifdef REQUIRE_REINITIALIZE + + // Reinitialize serial bus device + FdoExtension->DeviceInitialized = DeviceInitialize(FdoExtension, + FdoExtension->IoTargetSerial, + FdoExtension->RequestIoctlSync, + FALSE); + if (!IsDeviceInitialized(FdoExtension)) { + Status = STATUS_DEVICE_NOT_READY; + DoTrace(LEVEL_ERROR, TFLAG_PNP, ("DeviceInitialize failed!")); + goto Done; + } + +#else + // Set to TRUE in order to restart the read pump + FdoExtension->DeviceInitialized = TRUE; +#endif + + // Restart the IOTarget to receiving request + Status = WdfIoTargetStart(FdoExtension->IoTargetSerial); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_PNP, ("WdfIoTargetStart failed %!STATUS!", Status)); + goto Done; + } + + // Restart read pump + DoTrace(LEVEL_INFO, TFLAG_IO, (" Restarting read pump")); + Status = ReadH4Packet(&FdoExtension->ReadContext, + FdoExtension->ReadRequest, + FdoExtension->ReadMemory, + FdoExtension->ReadBuffer, + INITIAL_H4_READ_SIZE); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_IO, ("ReadH4Packet [0] failed %!STATUS!", Status)); + goto Done; + } + } + +Done: + + DoTrace(LEVEL_INFO, TFLAG_UART, ("-FdoDevD0Entry %!STATUS!", Status)); + + return Status; +} + + +NTSTATUS +FdoDevD0Exit( + _In_ WDFDEVICE _Device, + _In_ WDF_POWER_DEVICE_STATE _TargetState + ) +/*++ +Routine Description: + + This PnP CB function is invoked when device has exited D0 (working) state. + It stops the queue and can be restarted later, and mark the device uninitialize + and will be initialized again when resumes to D0. + +Arguments: + + _Device - WDF Device object + + _TargetState - Next power state it is entering from D0 + +Return Value: + + NTSTATUS + +--*/ +{ + PFDO_EXTENSION FdoExtension = FdoGetExtension(_Device); + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_TargetState); + + DoTrace(LEVEL_INFO, TFLAG_UART, ("+FdoDevD0Exit D0-> D%d", _TargetState-WdfPowerDeviceD0)); + + // Cancel IO requests that are already in the IO queue, + // wait for their completion before this function is returned. + // Can restart this queue at later time. + WdfIoTargetStop(FdoExtension->IoTargetSerial, WdfIoTargetCancelSentIo); + + // Delete GPIO IoTarget to disable the device and this will + // require device to be re-initialized when it re-enters D0. + if (FdoExtension->IoTargetGPIO) + { + WdfObjectDelete(FdoExtension->IoTargetGPIO); + FdoExtension->IoTargetGPIO = NULL; + } + FdoExtension->DeviceInitialized = FALSE; + + // + // Note: Do not delete the UART's IoTarget. + // + + DoTrace(LEVEL_INFO, TFLAG_UART, ("-FdoDevD0Exit")); + + return STATUS_SUCCESS; +} + + +NTSTATUS +HCIContextValidate( + ULONG _Index, + PBTHX_HCI_READ_WRITE_CONTEXT _HCIContext + ) +/*++ +Routine Description: + + This function validate the incoming data context and print out (WPP) trace. + +Arguments: + + _Index - count number of HCI command/event/data that has been completed (0 based). + _HCIContext - Context to be valdiated + +Return Value: + + NTSTATUS - STATUS_SUCCESS or STATUS_INVALID_PARAMETER + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG Index; + + DoTrace(LEVEL_INFO, TFLAG_HCI,("+HCIContextValidate")); + + switch ((BTHX_HCI_PACKET_TYPE) _HCIContext->Type) + { + case HciPacketCommand: + { + PHCI_COMMAND_PACKET HciCommand = (PHCI_COMMAND_PACKET) _HCIContext->Data; + DoTrace(LEVEL_INFO, TFLAG_HCI, (" -> HCI Command [%d] OpCode: 0x%x, nParams: %d ---->", + _Index, + HciCommand->Opcode, + HciCommand->ParamsCount)); + + for (Index = 0; Index < MinToPrint((ULONG) HciCommand->ParamsCount, MAX_COMMAND_PARAMS_TO_DISPLAY); Index++) + { + DoTrace(LEVEL_INFO, TFLAG_HCI, (" [%d] 0x%.2x", + Index, HciCommand->Params[Index])); + } + + if (!WithinRange(MIN_HCI_CMD_SIZE, _HCIContext->DataLen, MAX_HCI_CMD_SIZE)) + { + Status = STATUS_INVALID_PARAMETER; + DoTrace(LEVEL_ERROR, TFLAG_HCI,(" HciPacketCommand %!STATUS!", Status)); + break; + } + } + break; + + case HciPacketEvent: + { + PHCI_EVENT_PACKET HciEvent = (PHCI_EVENT_PACKET) _HCIContext->Data; + DoTrace(LEVEL_INFO, TFLAG_HCI, (" <- HCI Event [%d] EventCode: 0x%x (%S), nParams: %d", + _Index, + HciEvent->EventCode, + HciEvent->EventCode == CommandComplete ? L"Complete" : + HciEvent->EventCode == CommandStatus ? L"Status(Async)!!" : L"??", + HciEvent->ParamsCount)); + + // Note if CommandStatus is returned, there will be another event to complete this command. + + for (Index = 0; Index < MinToPrint((ULONG) HciEvent->ParamsCount, MAX_EVENT_PARAMS_TO_DISPLAY); Index++) + { + DoTrace(LEVEL_VERBOSE, TFLAG_HCI, (" [%d] 0x%.2x", + Index, HciEvent->Params[Index])); + } + + if (!WithinRange(MIN_HCI_EVENT_SIZE, _HCIContext->DataLen, MAX_HCI_EVENT_SIZE)) + { + Status = STATUS_INVALID_PARAMETER; + DoTrace(LEVEL_ERROR, TFLAG_HCI,(" HciPacketEvent %!STATUS!", Status)); + break; + } + } + break; + + case HciPacketAclData: + { + PHCI_ACLDATA_PACKET AclData = (PHCI_ACLDATA_PACKET) _HCIContext->Data; + DoTrace(LEVEL_INFO, TFLAG_HCI, (" HCI Data [%d] (Handle:0x%x, PB:%x, BC:%x, Length:%d)", + _Index, + AclData->ConnectionHandle, + AclData->PBFlag, + AclData->BCFlag, + AclData->DataLength)); + + for (Index = 0; Index < (ULONG) (AclData->DataLength > 8 ? 8 : AclData->DataLength); Index++) + { + DoTrace(LEVEL_VERBOSE, TFLAG_HCI, (" [%d] 0x%.2x", + Index, AclData->Data[Index])); + } + + if (!WithinRange(1, AclData->DataLength, MAX_HCI_ACLDATA_SIZE)) + { + Status = STATUS_INVALID_PARAMETER; + DoTrace(LEVEL_ERROR, TFLAG_HCI,(" HciPacketAclData data (%d) exceeds its max %d, %!STATUS!", + AclData->DataLength, MAX_HCI_ACLDATA_SIZE, Status)); + break; + } + } + break; + + default: + DoTrace(LEVEL_ERROR, TFLAG_HCI, (" Packet type %d unexpected!", _HCIContext->Type)); + Status = STATUS_INVALID_PARAMETER; + break; + } + + NT_ASSERT(NT_SUCCESS(Status) && L"Invlaid data is detected!"); + + DoTrace(LEVEL_INFO, TFLAG_HCI,("-HCIContextValidate %!STATUS!", Status)); + + return Status; +} + + + + +NTSTATUS +FdoWriteDeviceIO( + _In_ WDFREQUEST _RequestFromBthport, + _In_ WDFDEVICE _Device, + _In_ PFDO_EXTENSION _FdoExtension, + _In_ PBTHX_HCI_READ_WRITE_CONTEXT _HCIContext + ) +/*++ + +Routine Description: + + This function send an HCI packet to target device. + +Arguments: + + _RequestFromBthport - Request from upper layer that initiate this transfer + _Device - WDF Device Object + _FdoExtension - Device's context + _HCIContext - Context used to process this HCI + +Return Value: + + NTSTATUS + +--*/ +{ + WDF_OBJECT_ATTRIBUTES ObjAttributes; + NTSTATUS Status; + WDFREQUEST RequestToUART; + PUART_WRITE_CONTEXT TransferContext = NULL; + ULONG DataLength; + PVOID Data = NULL; + + DoTrace(LEVEL_INFO, TFLAG_DATA,("+FdoWriteDeviceIO")); + + + if (!IsDeviceInitialized(_FdoExtension)) + { + Status = STATUS_DEVICE_NOT_READY; + DoTrace(LEVEL_ERROR, TFLAG_IO, (" FdoWriteDeviceIO: cannot attach IO %!STATUS!", Status)); + goto Done; + } + + // + // Add a context to this existing WDFREQUEST for cancellation purpose + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&ObjAttributes, + UART_WRITE_CONTEXT); + + Status = WdfObjectAllocateContext(_RequestFromBthport, + &ObjAttributes, + &TransferContext); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfObjectAllocateContext failed %!STATUS!", Status)); + goto Done; + } + + Status = HLP_AllocateResourceForWrite( + _Device, + _FdoExtension->IoTargetSerial, + &RequestToUART); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_IO,(" HLP_WriteDeviceIO %!STATUS!", Status)); + goto Done; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&ObjAttributes); + ObjAttributes.ParentObject = _Device; + + // Reuse the data buffer coming from upper layer; UART's HCI packet starts with + // packet type, and then follows by the actual HCI packet. + Data = (PVOID) &_HCIContext->Type; + DataLength = (ULONG) sizeof(_HCIContext->Type) + _HCIContext->DataLen; + + _Analysis_assume_(DataLength > 0); + Status = WdfMemoryCreatePreallocated(&ObjAttributes, + Data, + DataLength, + &TransferContext->Memory); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryCreatePreallocated failed %!STATUS!", Status)); + goto Done; + } + + Status = WdfIoTargetFormatRequestForWrite(_FdoExtension->IoTargetSerial, + RequestToUART, + TransferContext->Memory, + NULL, + NULL); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoTargetFormatRequestForRead failed %!STATUS!", Status)); + goto Done; + } + + // Setup transfer context + TransferContext->FdoExtension = _FdoExtension; + TransferContext->HCIContext = _HCIContext; + TransferContext->RequestFromBthport = _RequestFromBthport; + TransferContext->RequestCompletePath = REQUEST_PATH_NONE; + TransferContext->RequestToUART = RequestToUART; + TransferContext->HCIPacket = Data; + TransferContext->HCIPacketLen = DataLength; + + // + // Both Requests are typically accessed by the completion routine, and in rare case also + // by the cancellation callback. Take a reference on them to ensure they stays valid in both cases. + // + WdfObjectReference(RequestToUART); + WdfObjectReference(_RequestFromBthport); + + + // Mark cancellable of the Request in our possession from upper layer + // Cannot mark the request that we will forward to lower driver cancellable. + // Only if the Request from upper layer is cancelled, we will then cancel the + // Request that is sent to lower driver. + WdfRequestMarkCancelable(_RequestFromBthport, CB_RequestFromBthportCancel); + + WdfRequestSetCompletionRoutine(RequestToUART, CR_WriteDeviceIO, TransferContext); + + // This request will be delivered to its IoTarget asynchronously (the default option). It should return + // STATUS_PENDING unless there is an error in its delivery to its IoTarget. After it has been delivered + // successfully, its completion function will be called for any outcome - success, failure, or cancellation. + if (!WdfRequestSend(RequestToUART, _FdoExtension->IoTargetSerial, WDF_NO_SEND_OPTIONS)) + { + NTSTATUS StatusTemp; + + // Get failure status, and this request will be completed by its caller of this function with this status. + Status = WdfRequestGetStatus(RequestToUART); + + // Unmark cancellable before it is completed. + StatusTemp = WdfRequestUnmarkCancelable(_RequestFromBthport); + + // Balance the reference count for both Requests due to failure. + WdfObjectDereference(RequestToUART); + WdfObjectDereference(_RequestFromBthport); + + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestSend failed %!STATUS! and UnmarkCancelable %!STATUS!", Status, StatusTemp)); + goto Done; + } + else + { + // Request has been delivered to UART driver asychronously. It will be completed in its completion function + // after IoTarget (UART driver) completes its delivery to the BT controller. + } + +Done: + + if (!NT_SUCCESS(Status)) + { + HLP_FreeResourceForWrite(TransferContext); + } + + DoTrace(LEVEL_INFO, TFLAG_IO, ("-FdoWriteDeviceIO %!STATUS!", Status)); + + return Status; +} + + +NTSTATUS +FdoWriteToDeviceSync( + _In_ WDFIOTARGET _IoTargetSerial, + _In_ WDFREQUEST _RequestWriteSync, + _In_ ULONG _IoControlCode, + _In_opt_ ULONG _InBufferSize, + _In_opt_ PVOID _InBuffer, + _Out_ PULONG_PTR _BytesWritten +) +/*++ +Routine Description: + + This helper function send a synchronous write or Ioctl Request to device with + timeout (to prevent hang). + +Arguments: + + _IoTargetSerial - Serial port IO Target where to issue this request to + _RequestWriteSync - caller allocated WDF Request + _IoControlCode - IOCTL control code; if 0, it is a Write request. + _InBufferSize - Input buffer size + _InBuffer - (optional) Input buffer + _BytesWritten - Bytes written to device; this is driver dependent; a write + could be successfully (and fully) written with 0 BytesWritten. + +Return Value: + + NTSTATUS - STATUS_SUCCESS or Status from issuing this request + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + WDF_REQUEST_REUSE_PARAMS RequestReuseParams; + WDF_REQUEST_SEND_OPTIONS Options; + WDF_MEMORY_DESCRIPTOR MemoryDescriptor; + ULONG_PTR BytesWritten = 0; + BOOLEAN HasInputParam = FALSE; + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_IO,("+FdoWriteToDeviceSync")); + + + WDF_REQUEST_REUSE_PARAMS_INIT(&RequestReuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS); + Status = WdfRequestReuse(_RequestWriteSync, &RequestReuseParams); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestReuse failed %!STATUS!", Status)); + goto Done; + } + + if (_InBuffer && _InBufferSize) { + HasInputParam = TRUE; + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&MemoryDescriptor, + _InBuffer, + _InBufferSize); + } + + WDF_REQUEST_SEND_OPTIONS_INIT(&Options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&Options, WDF_REL_TIMEOUT_IN_SEC(MAX_WRITE_TIMEOUT_IN_SEC)); + + if (_IoControlCode) + { + Status = WdfIoTargetSendIoctlSynchronously(_IoTargetSerial, + NULL, + _IoControlCode, + HasInputParam ? &MemoryDescriptor : NULL, // InputBuffer + NULL, // OutputBuffer + &Options, // RequestOptions + &BytesWritten // BytesReturned + ); + } + else + { + Status = WdfIoTargetSendWriteSynchronously(_IoTargetSerial, + NULL, + HasInputParam ? &MemoryDescriptor : NULL, // InputBuffer + NULL, // DeviceOffset + &Options, // RequestOptions + &BytesWritten // BytesReturned + ); + } + + DoTrace(LEVEL_INFO, TFLAG_IO,("-FdoWriteToDeviceSync: %d BytesWritten %!STATUS!", (ULONG) BytesWritten, Status)); + + if (NT_SUCCESS(Status)) + { + *_BytesWritten = BytesWritten; + } + +Done: + return Status; +} + + +VOID +FdoIoQuDeviceControl( + _In_ WDFQUEUE _Queue, + _In_ WDFREQUEST _Request, + _In_ size_t _OutputBufferLength, + _In_ size_t _InputBufferLength, + _In_ ULONG _IoControlCode + ) +/*++ + +Routine Description: + + This routine is the dispatch routine for device control requests. + +Arguments: + + _Queue - Handle to the framework queue object that is associated + with the I/O request. + _Request - Handle to a framework request object. + + _OutputBufferLength - length of the request's output buffer, + if an output buffer is available. + _InputBufferLength - length of the request's input buffer, + if an input buffer is available. + + _IoControlCode - the driver-defined or system-defined I/O control code + (IOCTL) that is associated with the request. + +Return Value: + + VOID + +--*/ +{ + WDFMEMORY ReqInMemory = NULL, ReqOutMemory = NULL; + PVOID InBuffer = NULL, OutBuffer = NULL; + size_t InBufferSize = 0, OutBufferSize = 0; + PFDO_EXTENSION FdoExtension; + NTSTATUS Status = STATUS_SUCCESS; + WDFDEVICE Device; + BOOLEAN CompleteRequest = FALSE; + ULONG ControlCode = (_IoControlCode & 0x00003ffc) >> 2; + KIRQL Irql; + BTHX_HCI_PACKET_TYPE PacketType; + PBTHX_HCI_READ_WRITE_CONTEXT HCIContext; + + + DoTrace(LEVEL_INFO, TFLAG_IOCTL,("+IoDeviceControl - InBufLen:%d, OutBufLen:%d", + (ULONG) _InputBufferLength, (ULONG) _OutputBufferLength)); + + Device = WdfIoQueueGetDevice(_Queue); + + FdoExtension = FdoGetExtension(Device); + + if (_InputBufferLength) + { + Status = WdfRequestRetrieveInputMemory(_Request, &ReqInMemory); + if (NT_SUCCESS(Status)) + { + InBuffer = WdfMemoryGetBuffer(ReqInMemory, &InBufferSize); + } + } + + if (_OutputBufferLength) + { + Status = WdfRequestRetrieveOutputMemory(_Request, &ReqOutMemory); + if (NT_SUCCESS(Status)) + { + OutBuffer = WdfMemoryGetBuffer(ReqOutMemory, &OutBufferSize); + } + } + + switch (_IoControlCode) + { + case IOCTL_BTHX_WRITE_HCI: + DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" IOCTL_BTHX_WRITE_HCI ---------->")); + // Validate input and output parameters + if (!InBuffer || InBufferSize < sizeof(BTHX_HCI_READ_WRITE_CONTEXT) || + !OutBuffer || OutBufferSize != sizeof(BTHX_HCI_PACKET_TYPE)) + { + Status = STATUS_INVALID_PARAMETER; + DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_BTHX_WRITE_HCI %!STATUS!", Status)); + break; + } + + HCIContext = (PBTHX_HCI_READ_WRITE_CONTEXT) InBuffer; + + PacketType = (BTHX_HCI_PACKET_TYPE) HCIContext->Type; + + if (!BTHX_VALID_WRITE_PACKET_TYPE(PacketType)) + { + Status = STATUS_INVALID_PARAMETER; + DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" Mismach Write HCI packet type and IOCTL %!STATUS!", Status)); + break; + } + + if (PacketType == HciPacketCommand) + { + InterlockedIncrement(&FdoExtension->CntCommandReq); + } + else + { + InterlockedIncrement(&FdoExtension->CntWriteDataReq); + } + + Status = FdoWriteDeviceIO(_Request, + Device, + FdoExtension, + HCIContext); + break; + + case IOCTL_BTHX_READ_HCI: + DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" IOCTL_BTHX_READ_HCI <----------")); + // Validate input and output parameters + if (!InBuffer || InBufferSize != sizeof(BTHX_HCI_PACKET_TYPE) || + !OutBuffer || OutBufferSize < sizeof(BTHX_HCI_READ_WRITE_CONTEXT)) + { + Status = STATUS_INVALID_PARAMETER; + DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_BTHX_READ_HCI %!STATUS!", Status)); + break; + } + + PacketType = *((BTHX_HCI_PACKET_TYPE *) InBuffer); + + if (!BTHX_VALID_READ_PACKET_TYPE(PacketType)) + { + Status = STATUS_INVALID_PARAMETER; + DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_BTHX_READ_HCI %!STATUS!", Status)); + break; + } + + if (PacketType == HciPacketEvent) + { + KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql); + // Queue the new request to preserve sequential order + Status = WdfRequestForwardToIoQueue(_Request, FdoExtension->ReadEventQueue); + if (NT_SUCCESS(Status)) + { + InterlockedIncrement(&FdoExtension->EventQueueCount); + InterlockedIncrement(&FdoExtension->CntEventReq); + } + KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); + + if (NT_SUCCESS(Status)) + { + Status = ReadRequestComplete(FdoExtension, + HciPacketEvent, + 0, NULL, + FdoExtension->ReadEventQueue, + &FdoExtension->EventQueueCount, + &FdoExtension->ReadEventList, + &FdoExtension->EventListCount); + } + + } + else if (PacketType == HciPacketAclData) + { + KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql); + // Queue the new request to preserve sequential order + Status = WdfRequestForwardToIoQueue(_Request, FdoExtension->ReadDataQueue); + if (NT_SUCCESS(Status)) + { + InterlockedIncrement(&FdoExtension->DataQueueCount); + InterlockedIncrement(&FdoExtension->CntReadDataReq); + } + KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); + + if (NT_SUCCESS(Status)) + { + Status = ReadRequestComplete(FdoExtension, + HciPacketAclData, + 0, NULL, + FdoExtension->ReadDataQueue, + &FdoExtension->DataQueueCount, + &FdoExtension->ReadDataList, + &FdoExtension->DataListCount); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_BTHX_READ_HCI %!STATUS!", Status)); + break; + } + break; + + case IOCTL_BTHX_GET_VERSION: + CompleteRequest = TRUE; + DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BTHX_GET_VERSION")); + + if (OutBuffer && OutBufferSize >= sizeof(BTHX_VERSION)) + { + RtlCopyMemory(OutBuffer, &Microsoft_BTHX_DDI_Version, sizeof(BTHX_VERSION)); + WdfRequestCompleteWithInformation(_Request, Status, sizeof(BTHX_VERSION)); + return; + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + break; + + case IOCTL_BTHX_SET_VERSION: + CompleteRequest = TRUE; + DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BTHX_SET_VERSION")); + + if (InBuffer && InBufferSize >= sizeof(BTHX_VERSION)) + { + BTHX_VERSION SupportedVersion = *((BTHX_VERSION *)InBuffer); + + DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BTHX_SET_VERSION 0x%x", SupportedVersion.Version)); + + WdfRequestComplete(_Request, Status); + return; + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + break; + + case IOCTL_BTHX_QUERY_CAPABILITIES: + CompleteRequest = TRUE; + DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BTHX_QUERY_CAPABILITIES")); + + if (OutBuffer && OutBufferSize >= sizeof(BTHX_CAPABILITIES)) + { + BTHX_CAPABILITIES *pCaps = (BTHX_CAPABILITIES *) OutBuffer; + + RtlCopyMemory(pCaps, &FdoExtension->BthXCaps, sizeof(BTHX_CAPABILITIES)); + WdfRequestCompleteWithInformation(_Request, Status, sizeof(BTHX_CAPABILITIES)); + return; + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + break; + + // + // This IOCTL is used to support radio on/off feature by doing the following + // 1. Power up/down the Bluetooth radio function, and + // 2. Add/remove a PDO for Bluetooth devnode; + // + case IOCTL_BUSENUM_SET_RADIO_ONOFF_VENDOR_SPECFIC: + CompleteRequest = TRUE; + DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BUSENUM_SET_RADIO_ONOFF_VENDOR_SPECFIC")); + if (InBuffer && InBufferSize >= sizeof(BOOLEAN)) { + BOOLEAN IsRadioEnabled = *((BOOLEAN *) InBuffer); + + if (IsRadioEnabled) { + if (!FdoExtension->IsRadioEnabled) { + + // + // 1. Power up the Bluetooth function of this device; + // + Status = DevicePowerOn(Device); + + if (NT_SUCCESS(Status)) { + + // + // 2. Create a PDO for the Bluetooth devnode; + // + Status = FdoCreateOneChildDevice(Device, + BT_PDO_HARDWARE_IDS, + BLUETOOTH_FUNC_IDS); + if (NT_SUCCESS(Status)) { + FdoExtension->IsRadioEnabled = TRUE; + } + } + DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" EnableRadio %!STATUS!", Status)); + } + else { + Status = STATUS_SUCCESS; + DoTrace(LEVEL_WARNING, TFLAG_IOCTL,(" Already enabled!")); + } + } + else { + if (FdoExtension->IsRadioEnabled) { + + // + // 1. Remove the PDO for the Bluetooth devnode; + // + Status = FdoRemoveOneChildDevice(Device, + BLUETOOTH_FUNC_IDS); + if (NT_SUCCESS(Status)) { + FdoExtension->IsRadioEnabled = FALSE; + + // + // 2. Power down the Bluetooth function (at least the antenna) of this device; + // + Status = DevicePowerOff(Device); + } + + DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" DisableRadio %!STATUS!", Status)); + } + else { + Status = STATUS_SUCCESS; + DoTrace(LEVEL_WARNING, TFLAG_IOCTL,(" Already disabled!")); + } + } + } + else { + Status = STATUS_INVALID_PARAMETER; + } + break; + + default: + DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" IOCTL_(0x%x, Func %d)", _IoControlCode, ControlCode)); + Status = STATUS_NOT_SUPPORTED; + break; + } + + if (!NT_SUCCESS(Status) || CompleteRequest) + { + WdfRequestComplete(_Request, Status); + } + + return; +} diff --git a/bluetooth/serialhcibus/Io.h b/bluetooth/serialhcibus/Io.h new file mode 100644 index 00000000..76abb82a --- /dev/null +++ b/bluetooth/serialhcibus/Io.h @@ -0,0 +1,251 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + io.h + +Abstract: + + Common header definitions and structs for read and write (IO) operation + +Author: + +Environment: + + Kernel mode only + + +Revision History: + +--*/ + +#ifndef __IO_H__ +#define __IO_H__ + +// +// 255 bytes of data + 3 bytes for HCI cmd hdr (2-byte opcode + 1-byte Parameter). +// +#define MIN_HCI_CMD_SIZE (3) +#define MAX_HCI_CMD_SIZE (258) + +// +// 255 bytes of data + 2 byte hdr (1-byte event code + 1-byte parameter). +// +#define MIN_HCI_EVENT_SIZE (2) +#define HCI_EVENT_HEADER_SIZE (2) +#define MAX_HCI_EVENT_SIZE (257) + +// +// Can be variable but usually 1021-byte (largest 3-DH5 ACL packet size) +// +#define HCI_ACL_HEADER_SIZE (4) +#define HCI_MAX_ACL_PAYLOAD_SIZE (1021) +#define MIN_HCI_ACLDATA_SIZE HCI_ACL_HEADER_SIZE +#define MAX_HCI_ACLDATA_SIZE (HCI_ACL_HEADER_SIZE + HCI_MAX_ACL_PAYLOAD_SIZE) + +#define INITIAL_H4_READ_SIZE (1+HCI_EVENT_HEADER_SIZE) +#define MAX_H4_HCI_PACKET_SIZE (1+HCI_ACL_HEADER_SIZE + HCI_MAX_ACL_PAYLOAD_SIZE) // include packet type + +#define BUFFER_AND_SIZE_ADJUSTED(Buffer, Size, SegmentCount, Increment) {Buffer += Increment; Size -= Increment; SegmentCount += Increment;} + +#include + +// +// Standard HCI packet structs for Command, Event and ACL Data +// +typedef struct _HCI_COMMAND_PACKET { + UINT16 Opcode; + UCHAR ParamsCount; // 0..255 + UCHAR Params[1]; +} HCI_COMMAND_PACKET, *PHCI_COMMAND_PACKET; +#define HCI_COMMAND_HEADER_LEN FIELD_OFFSET(HCI_COMMAND_PACKET, Params) + +typedef struct _HCI_EVENT_PACKET { + UCHAR EventCode; + UCHAR ParamsCount; // 0..255 + UCHAR Params[1]; +} HCI_EVENT_PACKET, *PHCI_EVENT_PACKET; +#define HCI_EVENT_HEADER_LEN FIELD_OFFSET(HCI_EVENT_PACKET, Params) + +typedef struct _HCI_ACLDATA_PACKET { + UINT16 ConnectionHandle : 12; + UINT16 PBFlag : 2; + UINT16 BCFlag : 2; + UINT16 DataLength; // 0..65535 + UCHAR Data[1]; +} HCI_ACLDATA_PACKET, *PHCI_ACLDATA_PACKET; +#define HCI_ACLDATA_HEADER_LEN FIELD_OFFSET(HCI_ACLDATA_PACKET, Data) + +// +// UART packet that has a leading packet type over standard HCI packet +// + +typedef struct _H4_PACKET { + UCHAR Type; + union { + HCI_COMMAND_PACKET Command; + HCI_EVENT_PACKET Event; + HCI_ACLDATA_PACKET AclData; + UCHAR Raw[MAX_HCI_ACLDATA_SIZE]; + } Packet; +} H4_PACKET, *PH4_PACKET; + +typedef struct _UART_COMMAND_PACKET { + UCHAR Type; + HCI_COMMAND_PACKET Packet; +} UART_COMMAND_PACKET, *PUART_COMMAND_PACKET; + +typedef struct _UART_EVENT_PACKET { + UCHAR Type; + HCI_EVENT_PACKET Packet; +} UART_EVENT_PACKET, *PUART_EVENT_PACKET; + +typedef struct _UART_ACLDATA_PACKET { + UCHAR Type; + HCI_ACLDATA_PACKET Packet; +} UART_ACLDATA_PACKET, *PUART_ACLDATA_PACKET; + +#include + + +typedef struct _FDO_EXTENSION *PFDO_EXTENSION; + + +#define REQUEST_PATH_NONE 0x00000000 +#define REQUEST_PATH_CANCELLATION 0x00000001 +#define REQUEST_PATH_COMPLETION 0x00000002 + + +// +// Context used for data transfer to device (write) +// +typedef struct _UART_WRITE_CONTEXT { + + // + // Back pointer to the FDO's extension + // + PFDO_EXTENSION FdoExtension; + + // + // Request from BthPort upper driver + // + WDFREQUEST RequestFromBthport; + + // + // Flag(Bit) to determine ownership for completing RequestFromBthport + // + LONG RequestCompletePath; + + // + // Request to perform this transfer to UART device + // + WDFREQUEST RequestToUART; + + // + // Memory object for data + // + WDFMEMORY Memory; + + // + // The caller's transfer context. + // + PBTHX_HCI_READ_WRITE_CONTEXT HCIContext; + + // + // Pointer to the data buffer from client's incoming data; not a copy. + // + PVOID HCIPacket; + + // + // Packet length, including packet type. + // + ULONG HCIPacketLen; + +} UART_WRITE_CONTEXT, *PUART_WRITE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(UART_WRITE_CONTEXT, GetWriteRequestContext) + + +// +// State machine used for reading incoming data streaming to form HCI event or data packet +// +typedef enum { + GET_PKT_TYPE = 1, // For UART/H4, UCHAR of packet type (Event or Data) + GET_PKT_HEADER = 2, // Get length to determine remaining payload + GET_PKT_PAYLOAD = 3 // Data payload +} UART_READ_STATE; + + +// +// A list to store prefetched (read) HCI packets utill they are retrieved. +// +typedef struct _HCI_PACKET_ENTRY { + LIST_ENTRY DataEntry; + ULONG PacketLen; + _Field_size_bytes_(PacketLen) UCHAR Packet[1]; +} HCI_PACKET_ENTRY, *PHCI_PACKET_ENTRY; + +// +// Use to track request completion path +// +typedef enum _READ_REQUEST_STATE { + REQUEST_SENT = 1, // Request is being sent + REQUEST_PENDING = 2, // Request is pending first - asynchronous completion + REQUEST_COMPLETE = 3 // Request has completedly first - synchronous completion +} READ_REQUEST_STATE; + + +// +// Context used for reading UART operation to form HCI data or event packet +// +typedef struct _UART_READ_CONTEXT { + + // + // Status of this request + // + NTSTATUS Status; + + // + // Back pointer to the device extension + // + PFDO_EXTENSION FdoExtension; + + // + // State machine for the read Request + // + READ_REQUEST_STATE RequestState; + + // + // State machine of repeat read (read pump) to complete an HCI packet + // + UART_READ_STATE ReadSegmentState; + + // + // Bytes read for each Segment (Type, Header, and Paylaod) of a partial H4 packet below + // + ULONG BytesReadNextSegment; + + // + // Bytes to read in order to have a full packet (only meaningful in GET_PKT_PAYLOAD state. + // + ULONG BytesToRead4FullPacket; + + // + // A union of H4 packet + // + H4_PACKET H4Packet; + +} UART_READ_CONTEXT, *PUART_READ_CONTEXT; + +#define MAX_HARDWARE_ERROR_COUNT 0 // Do not allow any error this time + + +// Timeout value for synchronous read and write requests +#define MAX_WRITE_TIMEOUT_IN_SEC 1 // unit = second +#define MAX_READ_TIMEOUT_IN_SEC 1 + + +#endif + diff --git a/bluetooth/serialhcibus/ReadMe.md b/bluetooth/serialhcibus/ReadMe.md new file mode 100644 index 00000000..871bb9e1 --- /dev/null +++ b/bluetooth/serialhcibus/ReadMe.md @@ -0,0 +1,59 @@ +Bluetooth Serial HCI Bus Driver +=============================== + +The purpose of this sample is to demonstrate how to implement a basic bus driver to support the new [Bluetooth Extensibility transport DDIs](http://msdn.microsoft.com/en-us/library/windows/hardware/ff536585) over the UART transport. Such a serial bus driver can support a multi-radio device over the UART transport and utilize a common Bluetooth HCI packet for communication. The lower edge of this driver interfaces with a UART controller following the Bluetooth SIG’s UART (H4) transport protocol. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +**Note**: This sample driver is generic i.e. it is not designed for a specific device and allows for a vendor to adopt and enhance it for supporting Bluetooth + +This sample driver, as is, may not properly function for a device until all vendor-specific device requirements (e.g. device initialization) have been incorporated + +It is recommended to use WDK whose version matches the target Windows build version or newer for the development of the serial bus driver. + +**FILE MANIFEST** + +**WDK header file** + +BthXDDI.h – this has the constants, struct, and IOCTL definitions for the Bluetooth extensibility transport. This header file is included in WDK. + +**Common code section** + +driver.c – driver initialization + +driver.h – common header file for driver.c and includes other header files + +Fdo.c – functions for function device object (FDO) and BTHX DDI processing + +io.c – functions that perform IO read pump via UART controller + +Io.h – header for io.c + +pdo.c – PDO (Bluetooth function) enumeration and IOCTL processing + +public.h – header to share with application to support Radio On/Off (“Airplane modeâ€) + +Note: The goal is to keep the common code section the same, so the vendor will only need to update those code sections in the device specific directory. + +**Device-specific code section** + +Debugdef.h – WPP trace GUID; user should use a new GUID (unique per driver) + +device.c – device specific functions to implement: + +--DeviceInitialize() – to perform UART and Bluetooth device initialization; + +--DeviceEnable() – (optional) to bring serial bus device out of disable/reset state. + +--DevicePowerOn() – (optional) to power on the device. + +--DeviceEnableWakeControl() – (optional) to arm for device wake signal + +--DeviceDisableWakeControl() – (optional) to disarm for device wake signal + +device.h – header file for device.c + +driver.rc – driver version and name + +SerialBusWdk.inx – device specific INF file to install this driver. The vendor will need to add the hardware ID to match the “\_HID†for the Serial Bus Device (Bluetooth) in the DSDT.asl file. For example, in SerialBusWDK.inx, the hardware ID is “ACPI\\\\_BTH0†where “\†could be a 4 digit vendor name diff --git a/bluetooth/serialhcibus/WDK/Debugdef.h b/bluetooth/serialhcibus/WDK/Debugdef.h new file mode 100644 index 00000000..96958c58 --- /dev/null +++ b/bluetooth/serialhcibus/WDK/Debugdef.h @@ -0,0 +1,49 @@ +#ifndef __DEBUGDEF_H__ +#define __DEBUGDEF_H__ + +// +// SerialBusWdk's control Guid +// {a2bf23a4-bc00-4903-81f2-6fbd82ad2d13} +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(serialbuswdk,(a2bf23a4,bc00,4903,81f2,6fbd82ad2d13), \ + WPP_DEFINE_BIT(TFLAG_PNP) \ + WPP_DEFINE_BIT(TFLAG_POWER) \ + WPP_DEFINE_BIT(TFLAG_UART) \ + WPP_DEFINE_BIT(TFLAG_IOCTL) \ + WPP_DEFINE_BIT(TFLAG_IO) \ + WPP_DEFINE_BIT(TFLAG_DATA) \ + WPP_DEFINE_BIT(TFLAG_HCI)) + +// +// Define shorter versions of the ETW trace levels +// +#define LEVEL_CRITICAL TRACE_LEVEL_CRITICAL +#define LEVEL_ERROR TRACE_LEVEL_ERROR +#define LEVEL_WARNING TRACE_LEVEL_WARNING +#define LEVEL_INFO TRACE_LEVEL_INFORMATION +#define LEVEL_VERBOSE TRACE_LEVEL_VERBOSE + +#define WPP_LEVEL_FLAG_ENABLED(lvl, component) \ + (WPP_LEVEL_ENABLED(component) && WPP_CONTROL(WPP_BIT_ ## component).Level >=lvl) + +#define WPP_LEVEL_FLAG_LOGGER(lvl, component) \ + WPP_LEVEL_LOGGER(component) + +// +// IFR enable macros +// +#define WPP_RECORDER_LEVEL_FLAG_ARGS(lvl, component) \ + WPP_CONTROL(WPP_BIT_ ## component).AutoLogContext, 0, WPP_BIT_ ## component +#define WPP_RECORDER_LEVEL_FLAG_FILTER(lvl, component) \ + (lvl < TRACE_LEVEL_VERBOSE || WPP_CONTROL(WPP_BIT_ ## component).AutoLogVerboseEnabled) + +// +// Use for WPP trace +// +#define WithinRange(min, value, max) (min <= value && value <= max) +#define MinToPrint(val1, val2) (val1 < val2 ? val1 : val2) +#define MAX_EVENT_PARAMS_TO_DISPLAY 8 // maximun number of event parameter to be printed (WPP) +#define MAX_COMMAND_PARAMS_TO_DISPLAY 8 // maximun number of vommand parameter to be printed (WPP) + +#endif // __DEBUGDEF_H__ diff --git a/bluetooth/serialhcibus/WDK/SerialBusWdk.inx b/bluetooth/serialhcibus/WDK/SerialBusWdk.inx new file mode 100644 index 00000000..b6e81096 --- /dev/null +++ b/bluetooth/serialhcibus/WDK/SerialBusWdk.inx @@ -0,0 +1,88 @@ +;/*++ +; +;Copyright (c) 2010 Microsoft Corporation All rights Reserved +; +;Module Name: +; +; SerialBusWdk.INF +; +;Abstract: +; INF file for installing a serial bus driver over UART transport +; +;Installation Notes: +; For testing purpose, use Devcon.exe to install +; +; "devcon install SerialBusWdk.inf SerialBusWdk_RootEnum" +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=System +ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} +Provider=%MSFTSAMPLE% +DriverVer=06/21/2006,6.2.7923.0 +CatalogFile=SerialBusWdk.cat + +[DestinationDirs] +DefaultDestDir = 12 + + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +SerialBusWdk.sys = 1,, + + +[ControlFlags] +ExcludeFromSelect=* + +[Manufacturer] +%StdMfg%=VendorModels,NT$ARCH$ + +;***************************************** +; Serial bus driver install Section +;***************************************** + +[VendorModels.NT$ARCH$] +%Bus.DeviceDesc%=Bus_Vendor, SerialBusWdk_RootEnum + +[Bus_Vendor.NT] +CopyFiles=Bus.CopyFiles + +[Bus.CopyFiles] +SerialBusWdk.sys + +[Bus_Vendor.NT.HW] +AddReg=Bus_Vendor.NT.AddReg + +[Bus_Vendor.NT.AddReg] +; Reference: http://msdn.microsoft.com/en-us/library/ff546320.aspx +HKR,,DeviceCharacteristics,0x10001,0x0100 ; Use same security checks on relative opens +; Reference: http://msdn.microsoft.com/en-us/library/windows/hardware/ff563667(v=vs.85).aspx +HKR,,Security,,"D:P(A;;GA;;;BA)(A;;GA;;;SY)(A;;GA;;;LS)" ; DACL Protected to allow generic-all access to built-in Admin group, system, and Local Service. + +;-------------- Service installation +[Bus_Vendor.NT.Services] +AddService = SerialBusWdk,%SPSVCINST_ASSOCSERVICE%, Bus_Service_Inst + +; -------------- statbus driver install sections +[Bus_Service_Inst] +DisplayName = %Bus.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\SerialBusWdk.sys + +[Strings] +;Non-Localizable + +SPSVCINST_ASSOCSERVICE= 0x00000002 + +;Localizable +MSFTSAMPLE = "Microsoft Sample" +StdMfg = "(Standard system devices)" +DiskId1 = "Serial Bus Driver over UART Installation Disk #1" +Bus.DeviceDesc = "Serial Bus Driver over UART Bus Enumerator" +Bus.SVCDESC = "Serial Bus Driver over UART Bus Enumerator" \ No newline at end of file diff --git a/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj new file mode 100644 index 00000000..770cb2e7 --- /dev/null +++ b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj @@ -0,0 +1,210 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {B126E3A5-91B5-41B5-9B40-719D474AA513} + $(MSBuildProjectName) + 1 + Debug + Win32 + {0929A3E4-68D7-482B-93C5-539571CF51D7} + + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + debugdef.h + DoTrace(LEVEL,FLAG,(MSG,...)) + + + $(InfArch) + true + .\$(IntDir)\SerialBusWdk.inf + + + true + true + debugdef.h + DoTrace(LEVEL,FLAG,(MSG,...)) + + + + SerialBusWdk + + + SerialBusWdk + + + SerialBusWdk + + + SerialBusWdk + + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\Ntstrsafe.lib + + + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\Ntstrsafe.lib + + + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\Ntstrsafe.lib + + + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\ + %(PreprocessorDefinitions);RESHUB_USE_HELPER_ROUTINES + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\Ntstrsafe.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters new file mode 100644 index 00000000..83b788e4 --- /dev/null +++ b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters @@ -0,0 +1,51 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {4614A6EB-0DDA-4E7D-B6B7-1AC9BF5F6AAA} + + + h;hpp;hxx;hm;inl;inc;xsd + {A95E3765-07D5-45E1-B82E-8D9EF61CD4BC} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {4E3D8A20-8B42-428D-AAEB-A5BA3C5C33C5} + + + inf;inv;inx;mof;mc; + {C6A2211E-78CA-4EFF-85FD-A921F4F8EB29} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/bluetooth/serialhcibus/WDK/device.c b/bluetooth/serialhcibus/WDK/device.c new file mode 100644 index 00000000..11859e9f --- /dev/null +++ b/bluetooth/serialhcibus/WDK/device.c @@ -0,0 +1,257 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + Device.c + +Abstract: + + This file handles device specific operations. + +Environment: + + Kernel mode only + +--*/ + +#include "driver.h" +#include "Device.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, DeviceQueryDeviceParameters) +#endif + +#define STR_BAUDRATE L"BaudRateIndex" + +VOID +DeviceQueryDeviceParameters( + _In_ WDFDRIVER _Driver +) +/*++ +Routine Description: + + Query driver's registry location for device specific parameter, such as baudrate. + + HLM\system\CCS\Services\serialhcibus\Parameters\ + + KeyName/Type/value + +Arguments: + + _Driver - WDF Driver object + +Return Value: + + None + +--*/ +{ + WDFKEY Key; + NTSTATUS Status; + UNICODE_STRING ValueName; + ULONG Value = 0; + + PAGED_CODE(); + + Status = WdfDriverOpenParametersRegistryKey(_Driver, + GENERIC_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &Key + ); + if (NT_SUCCESS(Status)) { + + RtlInitUnicodeString(&ValueName, STR_BAUDRATE); + Status = WdfRegistryQueryULong(Key, &ValueName, &Value); + + if (NT_SUCCESS(Status)) { + // Vendor: can cache and use this values. + } + + WdfRegistryClose(Key); + } + +} + + +NTSTATUS +DeviceEnableWakeControl( + _In_ WDFDEVICE _Device, + _In_ SYSTEM_POWER_STATE _PowerState + ) +/*++ + +Routine Description: + + Vendor: This is a device specific function, and it arms the wake mechanism + for this driver to receive the wake signal. This could be using an + HOST_WAKE GPIO interrupt, or inband CTS/RTS mechanism. + +Arguments: + + _Device - WDF Device object + _PowerState - Context used for reading data from target UART device + +Return Value: + + NTSTATUS + +--*/ +{ + UNREFERENCED_PARAMETER(_Device); + UNREFERENCED_PARAMETER(_PowerState); + + return STATUS_SUCCESS; +} + +VOID +DeviceDisableWakeControl( + WDFDEVICE _Device + ) +/*++ + +Routine Description: + + Vendor: This is a device specific function, and it disarms the wake mechanism + for this driver to receive the wake signal. + +Arguments: + + _Device - WDF Device object + +Return Value: + + VOID + +--*/ +{ + UNREFERENCED_PARAMETER(_Device); + + return; +} + +BOOLEAN +DeviceInitialize( + _In_ PFDO_EXTENSION _FdoExtension, + _In_ WDFIOTARGET _IoTargetSerial, + _In_ WDFREQUEST _RequestSync, + _In_ BOOLEAN _IsUartReset + ) +/*++ +Routine Description: + + This function perform device specific operations to intialize in order + to bring the device to operational state. + +Arguments: + + _FdoExtension - Function device object extension + + _IoTargetSerial - IO Target to issue request to serial port + + _RequestSync - A reuseable WDF Request to issue serial control + + -IsUartReset - Is UART Reset is required + +Return Value: + + TRUE if initialization is completed and successful; FALSE otherwise. + +--*/ +{ + UNREFERENCED_PARAMETER(_FdoExtension); + UNREFERENCED_PARAMETER(_IoTargetSerial); + UNREFERENCED_PARAMETER(_RequestSync); + UNREFERENCED_PARAMETER(_IsUartReset); + + // + // Vendor specifc operation; + // + + return TRUE; +} + +NTSTATUS +DeviceEnable( + _In_ WDFDEVICE _Device, + _In_ BOOLEAN _IsEnabled + ) + +/*++ + +Routine Description: + + This function enable/wake serial bus device. + +Arguments: + + _Device - Supplies a handle to the framework device object. + + _IsEnabled - Boolean to enable or disable the BT device. + + +Return Value: + + NTSTATUS code. + +--*/ + +{ + UNREFERENCED_PARAMETER(_Device); + UNREFERENCED_PARAMETER(_IsEnabled); + + return STATUS_SUCCESS; +} + + +NTSTATUS +DevicePowerOn( + _In_ WDFDEVICE _Device +) +/*++ + +Routine Description: + + This routine powers on the serial bus device + +Arguments: + + _Device - Supplies a handle to the framework device object. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(_Device); + + return STATUS_SUCCESS; +} + +NTSTATUS +DevicePowerOff( + _In_ WDFDEVICE _Device +) +/*++ + +Routine Description: + + This routine powers off the serial bus device + +Arguments: + + _Device - Supplies a handle to the framework device object. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(_Device); + + return STATUS_SUCCESS; +} + diff --git a/bluetooth/serialhcibus/WDK/device.h b/bluetooth/serialhcibus/WDK/device.h new file mode 100644 index 00000000..1827493d --- /dev/null +++ b/bluetooth/serialhcibus/WDK/device.h @@ -0,0 +1,56 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + device.h + +Abstract: + + Header definitions and structs that are device specific + +Author: + +Environment: + + Kernel mode only + + +Revision History: + +--*/ + +#ifndef __DEVICE_H__ +#define __DEVICE_H__ + +#pragma warning(disable:4214) // bit field types other than int + + +#define BT_PDO_HARDWARE_IDS L"SerialBusWdk\\UART_H4" +#define BT_PDO_COMPATIBLE_IDS L"MS_BTHX_BTHMINI" +#define BT_PDO_DEVICE_LOCATION L"Serial HCI Bus - Bluetooth Function" + + +// +// 255 bytes of data + 3 bytes for HCI cmd hdr (2-byte opcode + 1-byte Parameter). +// +#define MIN_HCI_CMD_SIZE (3) +#define MAX_HCI_CMD_SIZE (258) + +// +// 255 bytes of data + 2 byte hdr (1-byte event code + 1-byte parameter). +// +#define MIN_HCI_EVENT_SIZE (2) +#define MAX_HCI_EVENT_SIZE (257) + +// +// Can be variable but usually 1021-byte (largest 3-DH5 ACL packet size) +// +#define HCI_ACL_HEADER_SIZE (4) +#define HCI_MAX_ACL_PAYLOAD_SIZE (1021) +#define MIN_HCI_ACLDATA_SIZE HCI_ACL_HEADER_SIZE +#define MAX_HCI_ACLDATA_SIZE (HCI_ACL_HEADER_SIZE + HCI_MAX_ACL_PAYLOAD_SIZE) + +#endif + diff --git a/bluetooth/serialhcibus/WDK/driver.rc b/bluetooth/serialhcibus/WDK/driver.rc new file mode 100644 index 00000000..1d3dd7a9 --- /dev/null +++ b/bluetooth/serialhcibus/WDK/driver.rc @@ -0,0 +1,14 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Serial bus driver over UART using a common HCI packet" +#define VER_INTERNALNAME_STR "SerialBusWdk.sys" +#define VER_ORIGINALFILENAME_STR "SerialBusWdk.sys" + +#include "common.ver" + +//MofResourceName MOFDATA driver.bmf + diff --git a/bluetooth/serialhcibus/driver.c b/bluetooth/serialhcibus/driver.c new file mode 100644 index 00000000..8bb5b6aa --- /dev/null +++ b/bluetooth/serialhcibus/driver.c @@ -0,0 +1,378 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + driver.C + +Abstract: + + This module contains routines to handle the function driver + aspect of the bus driver. + +Environment: + + kernel mode only + +--*/ + +#include "driver.h" +#include "driver.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, DriverCleanup) +#pragma alloc_text (PAGE, DriverSetDeviceCallbackEvents) +#pragma alloc_text (PAGE, DriverDeviceAdd) +#pragma alloc_text (INIT, DriverEntry) +#endif + +VOID +DriverCleanup( + _In_ WDFOBJECT _Object + ) +/*++ + +Routine Description: + + This callback function performs operations that must take place before the + driver is unloaded. Free all the resources allocated in DriverEntry. + +Arguments: + + _Object - handle to a WDF Driver object. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+DriverCleanup")); + + WPP_CLEANUP( WdfDriverWdmGetDriverObject( _Object )); +} + + +VOID +DriverSetDeviceCallbackEvents( + _In_ PWDFDEVICE_INIT _DeviceInit + ) +// Initialize device callback events +{ + WDF_POWER_POLICY_EVENT_CALLBACKS PowerPolicyCallbacks; + WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks; + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+DriverSetDeviceCallbackEvents")); + + // + // Set event callbacks + // 1. Pnp & Power events + // 2. Power Policy events + // + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks); + + // + // Register PnP callback + // + PnpPowerCallbacks.EvtDevicePrepareHardware = FdoDevPrepareHardware; + PnpPowerCallbacks.EvtDeviceReleaseHardware = FdoDevReleaseHardware; + + // + // Register Power callback + // + PnpPowerCallbacks.EvtDeviceD0Entry = FdoDevD0Entry; + PnpPowerCallbacks.EvtDeviceD0Exit = FdoDevD0Exit; + PnpPowerCallbacks.EvtDeviceSelfManagedIoInit = FdoDevSelfManagedIoInit; + PnpPowerCallbacks.EvtDeviceSelfManagedIoCleanup = FdoDevSelfManagedIoCleanup; + + WdfDeviceInitSetPnpPowerEventCallbacks(_DeviceInit, + &PnpPowerCallbacks); + + // + // This driver can manage arm and disarm wake signal to support + // idle while S0/Sx. + // + WDF_POWER_POLICY_EVENT_CALLBACKS_INIT(&PowerPolicyCallbacks); + + // + // Register power policy callback. This is device specific. The ArmWake + // callback function can enable/disable external event that triggers a + // wake signal. + // These functions are invoked only if Idle capability is also set; that is, + // IdleSettings.IdleCaps == IdleCanWakeFromS0 + // + PowerPolicyCallbacks.EvtDeviceArmWakeFromS0 = FdoEvtDeviceArmWake; + PowerPolicyCallbacks.EvtDeviceDisarmWakeFromS0 = FdoEvtDeviceDisarmWake; + + WdfDeviceInitSetPowerPolicyEventCallbacks(_DeviceInit, + &PowerPolicyCallbacks); +} + + +NTSTATUS +DriverDeviceAdd( + IN WDFDRIVER _Driver, + IN PWDFDEVICE_INIT _DeviceInit + ) +/*++ +Routine Description: + + DriverDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. We create and initialize a device object to + represent a new instance of toaster bus. + +Arguments: + + _Driver - Handle to a framework driver object created in DriverEntry + + _DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + WDF_IO_QUEUE_CONFIG QueueConfig; + WDF_OBJECT_ATTRIBUTES Attributes; + NTSTATUS Status; + WDFDEVICE Device; + PFDO_EXTENSION FdoExtension; + WDFQUEUE Queue; + PNP_BUS_INFORMATION BusInfo; + WDF_DEVICE_STATE DeviceState; +#ifdef DYNAMIC_ENUM + WDF_CHILD_LIST_CONFIG Config; +#endif + + + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP, ("+DriverDeviceAdd: 0x%p", _Driver)); + + // + // Get device specific parameters, such as baudrate + // + DeviceQueryDeviceParameters(_Driver); + + // + // Set PnP, Power and Power Policy event callback + // + DriverSetDeviceCallbackEvents(_DeviceInit); + + // + // Initialize all the properties specific to the device. + // Framework has default values for the one that are not + // set explicitly here. So please read the doc and make sure + // you are okay with the defaults. + // + WdfDeviceInitSetDeviceType(_DeviceInit, FILE_DEVICE_BUS_EXTENDER); + +#ifdef DYNAMIC_ENUM + + // + // WDF_ DEVICE_LIST_CONFIG describes how the framework should handle + // dynamic child enumeration on behalf of the driver writer. + // Since we are a bus driver, we need to specify identification description + // for our child devices. This description will serve as the identity of our + // child device. Since the description is opaque to the framework, we + // have to provide bunch of callbacks to compare, copy, or free + // any other resources associated with the description. + // + WDF_CHILD_LIST_CONFIG_INIT(&Config, + sizeof(PDO_IDENTIFICATION_DESCRIPTION), + FdoEvtDeviceListCreatePdo // callback to create a child device. + ); + + // Do not register function pointers and use default option unless customization is + // required. Consult MSDN or other WDK documentation for their usage. + + // + // Tell the framework to use the built-in childlist to track the state + // of the device based on the configuration we just created. + // + WdfFdoInitSetDefaultChildListConfig(_DeviceInit, + &Config, + WDF_NO_OBJECT_ATTRIBUTES); +#endif + + // + // Initialize Attributes structure to specify size and accessor function + // for storing device context. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, FDO_EXTENSION); + + // + // Create a framework device object to represent FDO of this bus driver. In response + // to this call, framework creates a WDM deviceobject. + // Can no longer access the WDFDEVICE_INIT structure after this call. + // + Status = WdfDeviceCreate(&_DeviceInit, + &Attributes, + &Device); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfDeveiceCreate failed %!STATUS!", Status)); + return Status; + } + + + // + // Allow serial bus driver to be disabled + // + WDF_DEVICE_STATE_INIT(&DeviceState); + DeviceState.NotDisableable = WdfFalse; + WdfDeviceSetDeviceState(Device, &DeviceState); + + + // + // Get the device context. + // + FdoExtension = FdoGetExtension(Device); + + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Attributes.ParentObject = Device; + + // + // Purpose of this lock is documented in FdoCreateOneChildDevice routine. + // + Status = WdfWaitLockCreate(&Attributes, &FdoExtension->ChildLock); + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Create a power-managed IO Queue + // + // Configure a default queue so that requests that are not + // configure-forwarded using WdfDeviceConfigureRequestDispatching to go to + // other queues get dispatched here. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, + WdfIoQueueDispatchParallel); + QueueConfig.PowerManaged = WdfTrue; + // Queue's callback event + QueueConfig.EvtIoDeviceControl = FdoIoQuDeviceControl; + + // + // By default, Static Driver Verifier (SDV) displays a warning if it + // doesn't find the EvtIoStop callback on a power-managed queue. + // The 'assume' below causes SDV to suppress this warning. + // + // No need to handle EvtIoStop/Resume: + // + // Condition: When there is a device state change from D0 to Dx, it is processed as a + // device stop event, and the caller (BthMini) will cancel all pending IOs. + // + // 1. Write command/data Requests are marked cancellable by serial bus driver, and + // the cancellation routine will handle the cancellation. + // + // 2. Read event/data Requests have their separate queues (with manual dispatch); + // when a request is in the queue, WDF owns the requests and can cancel the request + // in response to a cancel reqeust (IoCancelIrp) from the caller (BthMini). + // + + __analysis_assume(QueueConfig.EvtIoStop != 0); + Status = WdfIoQueueCreate(Device, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &Queue); + __analysis_assume(QueueConfig.EvtIoStop == 0); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfIoQueueCreate failed %!STATUS!", Status)); + return Status; + } + + // + // Create device interface for this device. The interface will be + // enabled by the framework when we return from StartDevice successfully. + // Use this interface to support Bluetooth Radio on/off scenario + // + Status = WdfDeviceCreateDeviceInterface(Device, + &GUID_DEVINTERFACE_BLUETOOTH_RADIO_ONOFF_VENDOR_SPECIFIC, + NULL /* No Reference String */ ); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfDeviceCreateDeviceInterface failed %!STATUS!", Status)); + return Status; + } + + // + // This value is used in responding to the IRP_MN_QUERY_BUS_INFORMATION + // for the child devices. This is an optional information provided to + // uniquely idenitfy the bus the device is connected. + // + BusInfo.BusTypeGuid = GUID_SERENUM_BUS_ENUMERATOR; + BusInfo.LegacyBusType = PNPBus; + BusInfo.BusNumber = 0; + + WdfDeviceSetBusInformationForChildren(Device, &BusInfo); + + // + // Note: Do static PDO enumeration in FdoDevPrepareHardware PnP callback + // + + DoTrace(LEVEL_INFO, TFLAG_PNP, ("-DriverDeviceAdd: exit %!STATUS!", Status)); + + return Status; +} + + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT _DriverObject, + _In_ PUNICODE_STRING _RegistryPath + ) +/*++ +Routine Description: + + Initialize the call backs structure of Driver Framework. + +Arguments: + + _DriverObject - pointer to the driver object + + _RegistryPath - pointer to a unicode string representing the path, + to driver-specific key in the registry. + +Return Value: + + NT Status Code + +--*/ +{ + WDF_DRIVER_CONFIG Config; + NTSTATUS Status; + WDF_OBJECT_ATTRIBUTES Attributes; + + + WDF_DRIVER_CONFIG_INIT(&Config, DriverDeviceAdd); + Config.DriverPoolTag = POOLTAG_UARTHCIBUSSAMPLE; + + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Attributes.EvtCleanupCallback = DriverCleanup; + + // + // Create a framework driver object to represent our driver. + // + Status = WdfDriverCreate(_DriverObject, + _RegistryPath, + &Attributes, + &Config, + WDF_NO_HANDLE); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_PNP, ("WdfDriverCreate failed %!STATUS!", Status)); + return Status; + } + + WPP_INIT_TRACING(_DriverObject, _RegistryPath); + + return Status; + +} + diff --git a/bluetooth/serialhcibus/driver.h b/bluetooth/serialhcibus/driver.h new file mode 100644 index 00000000..d82f1f55 --- /dev/null +++ b/bluetooth/serialhcibus/driver.h @@ -0,0 +1,530 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + driver.h + +Abstract: + + This module contains the common private declarations for + for the Serial HCI bus driver. + +Environment: + + kernel mode only + +--*/ + +#ifndef DRIVER_H +#define DRIVER_H + +#include +#include + +#define NTSTRSAFE_LIB +#include + +#define INITGUID +#include +#include // Constants and types for access Serial device + +#include // BT Extensible Transport DDI + +#include "device.h" // Device specific +#include "io.h" // Read pump +#include "debugdef.h" // WPP trace +#include "public.h" // Share between driver and application + +#ifdef DEFINE_GUID + +// +// Container ID for internally connected device +// +DEFINE_GUID(GUID_CONTAINERID_INTERNALLY_CONNECTED_DEVICE, + 0x00000000, 0x0000, 0x0000, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff); + +//{00000000-0000-0000-ffff-ffffffffffff} + +#endif // #ifdef DEFINE_GUID + +// +// Define HCI event code +// +#ifndef CommandComplete +#define CommandComplete 0x0e +#endif +#ifndef CommandStatus +#define CommandStatus 0x0f +#endif + +#define POOLTAG_UARTHCIBUSSAMPLE 'SBHS' // 'S'erial 'H'ci 'B'us 'S'ample +#undef ExAllocatePool +#define ExAllocatePool(type, size) \ + ExAllocatePoolWithTag(type, size, POOLTAG_UARTHCIBUSSAMPLE) + +// +// An ID used to uniquely identify Bluetooth function from other function +// of this multifunction device. +// +#define BLUETOOTH_FUNC_IDS 0x1001 + + + +// +// Device's idle state capability +// +typedef enum _IDLE_CAP_STATE { + IdleCapActiveOnly = 1, // Support active only (cannot idle) + IdleCapCanWake = 2, // Can enter D2 (idle) and remote wake to save power while in idle state. + IdleCapCanTurnOff = 3 // Can enter D3 (off) and not remote wake to save max power while device is off. +} IDLE_CAP_STATE; + + +#ifdef DYNAMIC_ENUM +// +// The goal of the identification and address description abstractions is that enough +// information is stored for a discovered device so that when it appears on the bus, +// the framework (with the help of the driver writer) can determine if it is a new or +// existing device. The identification and address descriptions are opaque structures +// to the framework, they are private to the driver writer. The only thing the framework +// knows about these descriptions is what their size is. +// The identification contains the bus specific information required to recognize +// an instance of a device on its the bus. The identification information usually +// contains device IDs along with any serial or slot numbers. +// For some buses (like USB and PCI), the identification of the device is sufficient to +// address the device on the bus; in these instances there is no need for a separate +// address description. Once reported, the identification description remains static +// for the lifetime of the device. For example, the identification description that the +// PCI bus driver would use for a child would contain the vendor ID, device ID, +// subsystem ID, revision, and class for the device. This sample uses only identification +// description. +// On other busses (like 1394 and auto LUN SCSI), the device is assigned a dynamic +// address by the hardware (which may reassigned and updated periodically); in these +// instances the driver will use the address description to encapsulate this dynamic piece +// of data. For example in a 1394 driver, the address description would contain the +// device's current generation count while the identification description would contain +// vendor name, model name, unit spec ID, and unit software version. +// +typedef struct _PDO_IDENTIFICATION_DESCRIPTION +{ + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER Header; // should contain this header + + // + // Unique serail number of the device on the bus + // + ULONG SerialNo; + + size_t CchHardwareIds; + + _Field_size_bytes_(CchHardwareIds) PWCHAR HardwareIds; + +} PDO_IDENTIFICATION_DESCRIPTION, *PPDO_IDENTIFICATION_DESCRIPTION; +#endif // #ifdef DYNAMIC_ENUM + + +typedef struct _UART_READ_CONTEXT *PUART_READ_CONTEXT; + +// +// Bus driver's FDO (Function Device Object) extension structure used to maintain device +// properties and state. +// + + +typedef struct _FDO_EXTENSION +{ + WDFWAITLOCK ChildLock; + + // + // Radio On/Off state + // + BOOLEAN IsRadioEnabled; + + // + // WDF Device handle + // + WDFDEVICE WdfDevice; + + // + // Serial port IO Target where we send IOCTL/READ/WRITE reuquest to + // + WDFIOTARGET IoTargetSerial; + + // + // (optional) GPIO IO Target to enable serial bus device + // + WDFIOTARGET IoTargetGPIO; + + // + // Bluetooth child dev node (PDO) capabilities + // + BTHX_CAPABILITIES BthXCaps; + + // + // Indicator if UART is properly initialize; may require re-inialization + // when tranistion from exiting D0 to resume D0. + // + BOOLEAN DeviceInitialized; + + // + // Cached UART controller connection IDs + // + LARGE_INTEGER UARTConnectionId; + + // + // Cached I2C controller connection IDs + // + LARGE_INTEGER I2CConnectionId; + + // + // Cached GPIO controller connection IDs + // + LARGE_INTEGER GPIOConnectionId; + + // + // Preallocate WDF Requests for synchronous operation like serial port settings + // + WDFREQUEST RequestIoctlSync; + + // + // Preallocate WDF Requests to wait on serial error event + // + WDFREQUEST RequestWaitOnError; + + // + // Data return from serial event wait mask IOCTL + // + ULONG SerErrorMask; + + // + // WDM memory use for Wait Mask event + // + WDFMEMORY WaitMaskMemory; + + // + // Set if a hardware error (e.g. data overrun in UART FIFO) is detected + // + BOOLEAN HardwareErrorDetected; + + // + // Indication the state of the read pump (TRUE = active) + // + BOOLEAN ReadPumpRunning; + + // + // Track number of out-of-sync error that has been detected + // + ULONG OutOfSyncErrorCount; + + // + // Locks for synchronization for list and queue + // + KSPIN_LOCK QueueAccessLock; + + // + // Track next packet read (one and only one) + // + UART_READ_CONTEXT ReadContext; + + // + // Preallocated local WDF requested and memory object that is reused to + // implement read pump + // + WDFREQUEST ReadRequest; + WDFMEMORY ReadMemory; + UCHAR ReadBuffer[MAX_H4_HCI_PACKET_SIZE]; + +#if DBG + // + // Track last completed HCI packet + // + UCHAR LastPacket[MAX_H4_HCI_PACKET_SIZE]; + ULONG LastPacketLength; +#endif + // + // WDF Queue for HCI event Request and total number of such request recevied + // + WDFQUEUE ReadEventQueue; + LONG EventQueueCount; + + // + // List to store (prefetched) incoming HCI events and number of entries + // + LIST_ENTRY ReadEventList; + LONG EventListCount; + + + // + // WDF Queue for HCI read data Request and total number of such request recevied + // + WDFQUEUE ReadDataQueue; + LONG DataQueueCount; + + // + // List to store (prefetched) incoming HCI data and number of entries + // + LIST_ENTRY ReadDataList; + LONG DataListCount; + + + // + // Counts used to track HCI requests received and completed for various packet types + // + LONG CntCommandReq; // Track total number of HCI command Requests + LONG CntCommandCompleted; // Number of HCI Command completed + + LONG CntEventReq; // Track total number of HCI Event Requests + LONG CntEventCompleted; // Number of HCI Command completed + + LONG CntWriteDataReq; // Track total number of HCI Write Data requests + LONG CntWriteDataCompleted; // Number of HCI (write) Data completed + + LONG CntReadDataReq; // Track total number of HCI Read Data Requests + LONG CntReadDataCompleted; // Number of HCI (Read) Data completed +} FDO_EXTENSION, *PFDO_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_EXTENSION, FdoGetExtension) + + +// +// Can send IO only if the device (UART) is in the initialized state. +// +#define IsDeviceInitialized(FdoExtension) (FdoExtension->DeviceInitialized) + + +#define ValidConnectionID(ConnectionId) (ConnectionId.QuadPart != 0) + + +// +// Bus driver's child PDO (Physical Device Object) extension structure used to maintain this +// PDO's device properties and state. +// + +typedef struct _PDO_EXTENSION +{ + // + // Back pointer to FDO_EXTENSION + // + PFDO_EXTENSION FdoExtension; + + // + // Unique serial number of the device on the bus + // + ULONG SerialNo; + +} PDO_EXTENSION, *PPDO_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_EXTENSION, PdoGetExtension) + + +// +// Prototypes of functions +// + + +// +// Driver.c +// + +EVT_WDF_OBJECT_CONTEXT_CLEANUP DriverCleanup; + +VOID +DriverSetDeviceCallbackEvents( + _In_ PWDFDEVICE_INIT _DeviceInit + ); + +EVT_WDF_DRIVER_DEVICE_ADD DriverDeviceAdd; + +DRIVER_INITIALIZE DriverEntry; + + +// +// FDO.c +// + +NTSTATUS +HlpInitializeFdoExtension(WDFDEVICE _Device); + +NTSTATUS +FdoWriteDeviceIO(_In_ WDFREQUEST _RequestFromBthport, + _In_ WDFDEVICE _Device, + _In_ PFDO_EXTENSION _FdoExtension, + _In_ PBTHX_HCI_READ_WRITE_CONTEXT _HCIContext); + +NTSTATUS +FdoWriteToDeviceSync(_In_ WDFIOTARGET _IoTargetSerial, + _In_ WDFREQUEST _RequestWriteSync, + _In_ ULONG _IoControlCode, + _In_opt_ ULONG _InBufferSize, + _In_opt_ PVOID _InBuffer, + _Out_ PULONG_PTR _BytesWritten); + +NTSTATUS +DeviceConfigWaitOnError(_In_ WDFIOTARGET _IoTargetSerial, + _In_ WDFREQUEST _RequestWaitOnError, + _In_ WDFMEMORY _WaitMaskMemory, + _In_ PULONG _ErrorResult, + _In_ PFDO_EXTENSION _FdoExtension); + + +NTSTATUS +HCIContextValidate(ULONG Index, + PBTHX_HCI_READ_WRITE_CONTEXT _HCIContext); + + +// Power policy events +EVT_WDF_DEVICE_ARM_WAKE_FROM_S0 FdoEvtDeviceArmWake; +EVT_WDF_DEVICE_DISARM_WAKE_FROM_S0 FdoEvtDeviceDisarmWake; + +EVT_WDF_DEVICE_ARM_WAKE_FROM_SX FdoEvtDeviceArmWake; +EVT_WDF_DEVICE_DISARM_WAKE_FROM_SX FdoEvtDeviceDisarmWake; + +// PnP events +EVT_WDF_DEVICE_PREPARE_HARDWARE FdoDevPrepareHardware; +EVT_WDF_DEVICE_RELEASE_HARDWARE FdoDevReleaseHardware; + +// Power events +EVT_WDF_DEVICE_D0_ENTRY FdoDevD0Entry; +EVT_WDF_DEVICE_D0_EXIT FdoDevD0Exit; + +EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT FdoDevSelfManagedIoInit; +EVT_WDF_DEVICE_SELF_MANAGED_IO_CLEANUP FdoDevSelfManagedIoCleanup; + +// Queue +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL FdoIoQuDeviceControl; + +// PDO creation + +#ifdef DYNAMIC_ENUM +EVT_WDF_CHILD_LIST_CREATE_DEVICE FdoEvtDeviceListCreatePdo; + +NTSTATUS +PdoCreateDynamic(_In_ WDFDEVICE Device, + _In_ PWDFDEVICE_INIT DeviceInit, + _In_ PWCHAR HardwareIds, + _In_ ULONG SerialNo); + +NTSTATUS +FdoCreateOneChildDeviceDynamic(_In_ WDFDEVICE _Device, + _In_ PWCHAR _HardwareIds, + _In_ size_t _CchHardwareIds, + _In_ ULONG _SerialNo); +#endif + +EVT_WDF_DEVICE_DISABLE_WAKE_AT_BUS PdoDevDisableWakeAtBus; +EVT_WDF_DEVICE_ENABLE_WAKE_AT_BUS PdoDevEnableWakeAtBus; + +NTSTATUS +FdoCreateOneChildDevice(_In_ WDFDEVICE _Device, + _In_ PWCHAR _HardwareIds, + _In_ ULONG _SerialNo); + +NTSTATUS +FdoCreateAllChildren(_In_ WDFDEVICE _Device); + +NTSTATUS +FdoRemoveOneChildDevice(WDFDEVICE _Device, + ULONG _SerialNo); + +NTSTATUS +FdoFindConnectResources(_In_ WDFDEVICE _Device, + _In_ WDFCMRESLIST _ResourcesRaw, + _In_ WDFCMRESLIST _ResourcesTranslated); + + +// +// Pdo.c +// + +EVT_WDF_DEVICE_PREPARE_HARDWARE PdoDevPrepareHardware; +EVT_WDF_DEVICE_RELEASE_HARDWARE PdoDevReleaseHardware; + +EVT_WDF_DEVICE_D0_ENTRY PdoDevD0Entry; +EVT_WDF_DEVICE_D0_EXIT PdoDevD0Exit; + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL PdoIoQuDeviceControl; + +NTSTATUS +PdoCreate(_In_ WDFDEVICE _Device, + _In_ PWSTR _HardwareIds, + _In_ ULONG _SerialNo); + +VOID +PdoDevDisableWakeAtBus(_In_ WDFDEVICE _Device); + +NTSTATUS +PdoDevEnableWakeAtBus(_In_ WDFDEVICE _Device, + _In_ SYSTEM_POWER_STATE _PowerState); + +// +// Define in io.c +// +NTSTATUS ReadResourcesAllocate(_In_ WDFDEVICE _Device); +VOID ReadResourcesFree(_In_ WDFDEVICE _Device); + +NTSTATUS +HLP_AllocateResourceForWrite(_In_ WDFDEVICE _Device, + _In_ WDFIOTARGET _IoTargetSerial, + _Out_ WDFREQUEST *_pRequest); + +VOID +HLP_FreeResourceForWrite(PUART_WRITE_CONTEXT _TransferContext); + +EVT_WDF_REQUEST_CANCEL CB_RequestFromBthportCancel; + +EVT_WDF_REQUEST_COMPLETION_ROUTINE CR_WriteDeviceIO; + +NTSTATUS +ReadRequestComplete(_In_ PFDO_EXTENSION _FdoExtension, + _In_ UCHAR _Type, + _In_ ULONG _PacketLength, + _In_reads_bytes_opt_(_PacketLength) PUCHAR _Packet, + _Inout_ WDFQUEUE _Queue, + _Inout_ PLONG _QueueCount, + _Inout_ PLIST_ENTRY _ListHead, + _Inout_ PLONG _ListCount); + +EVT_WDF_REQUEST_COMPLETION_ROUTINE ReadH4PacketCompletionRoutine; + +NTSTATUS +ReadH4Packet(_In_ PUART_READ_CONTEXT _ReadContext, + _In_ WDFREQUEST _WdfRequest, + _In_ WDFMEMORY _WdfMemory, + _Pre_notnull_ _Pre_writable_byte_size_(_BufferLen) PVOID _Buffer, + _In_ ULONG _BufferLen); + + +// +// Device.c +// + +VOID +DeviceQueryDeviceParameters(_In_ WDFDRIVER _Driver); + +BOOLEAN +DeviceInitialize(_In_ PFDO_EXTENSION _FdoExtension, + _In_ WDFIOTARGET _IoTargetSerial, + _In_ WDFREQUEST _RequestSync, + _In_ BOOLEAN _ResetUart); + +NTSTATUS +DeviceEnableWakeControl(_In_ WDFDEVICE _Device, + _In_ SYSTEM_POWER_STATE _PowerState); +void +DeviceDisableWakeControl(WDFDEVICE _Device); + +NTSTATUS +DeviceEnable(_In_ WDFDEVICE _Device, + _In_ BOOLEAN _Enabled); + +NTSTATUS +DevicePowerOn(_In_ WDFDEVICE _Device); + +NTSTATUS +DevicePowerOff(_In_ WDFDEVICE _Device); + +#endif + + + diff --git a/bluetooth/serialhcibus/io.c b/bluetooth/serialhcibus/io.c new file mode 100644 index 00000000..0b4d64ac --- /dev/null +++ b/bluetooth/serialhcibus/io.c @@ -0,0 +1,1592 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + IO.c + +Abstract: + + This module contains routines that perform read/write IO operations. + +Environment: + + Kernel mode only + +Revision History: + +--*/ + +#include "driver.h" +#include "IO.tmh" + +#pragma warning(disable:4127) // conditional expression is constant + +#ifdef ALLOC_PRAGMA +#endif + +VOID +CB_RequestFromBthportCancel( + _In_ WDFREQUEST _RequestFromUpper + ) +/*++ + +Routine Description: + + Request from upper layer that driver owns is being canceled. Its associated + Request to lower (UART) driver will be canceled and then this Request will + be completed with STATUS_CANCELLED. + + There are different paths for the Request from upper layer: + + 1. Completion routine is invoked without cancellation (typical path) + 2. Cancellation routine is invoked while lower Request is pending. The lower + request could be completed either + a. Synchronously - completion routine is invoked before + WdfRequestCancelSentRequest() is returned in the cancellation routine; or + b. Asynchronously - completion routine is invoked at later time after + WdfRequestCancelSentRequest has returned. + 3. Race conditions when both the cancelation and completion routine have independently started + a. Cancellation routine is ahead and the request is completed with cancellation status. + b. Completion routine is ahead and the request is completed with the status from the lower request. + +Arguments: + + _RequestFromUpper - WDF Request to be cancelled + +Return Value: + + none + +--*/ +{ + PUART_WRITE_CONTEXT TransferContext; + WDFREQUEST RequestToUART; + WDFMEMORY Memory; + BOOLEAN CancelSuccess; + LONG CompletePath = REQUEST_PATH_NONE; + + + DoTrace(LEVEL_WARNING, TFLAG_IO, ("+CB_RequestFromBthportCancel: Request(%p) from upper driver", _RequestFromUpper)); + + TransferContext = GetWriteRequestContext(_RequestFromUpper); + NT_ASSERT(TransferContext && L"TransferContext is not valid!"); + + // Cancel the write Request that was previously submitted to its I/O target + RequestToUART = TransferContext->RequestToUART; + Memory = TransferContext->Memory; + + + // + // The below operation can return one of the following values. + // REQUEST_PATH_NONE + // This value was returned due to one of the following conditions + // 1. The completion routine was not yet run. + // 2. The completion routine was run and it relinquished the control of completing the request from bthport to the cancel routine. + // + // No matter what causes this value to be returned, this function is now responsible for completing the request from bthport. + // + // REQUEST_PATH_COMPLETION + // The completion routine was already called. + // The completion routine has not yet had a chance to relinquish control of completing the request from bthport. + // + // This function does not have the control to complete the request from bthport. + // + CompletePath = InterlockedOr(&TransferContext->RequestCompletePath, REQUEST_PATH_CANCELLATION); + + if (REQUEST_PATH_NONE == CompletePath) { + + DoTrace(LEVEL_WARNING, TFLAG_IO, (" >CancelSentRequest(%p) to IO Target", RequestToUART)); + CancelSuccess = WdfRequestCancelSentRequest(RequestToUART); + DoTrace(LEVEL_WARNING, TFLAG_IO, (" Memory) + { + WdfObjectDelete(_TransferContext->Memory); + _TransferContext->Memory = NULL; + } + + if (_TransferContext->RequestToUART) + { + WdfObjectDelete(_TransferContext->RequestToUART); + _TransferContext->RequestToUART = NULL; + + } + } +} + + +VOID +CR_WriteDeviceIO( + _In_ WDFREQUEST _Request, + _In_ WDFIOTARGET _Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS _Params, + _In_ WDFCONTEXT _Context + ) +/*++ + +Routine Description: + + This is the completion function for sending HCI packet to the lower layer. + This function can also complete the request from the upper layer; see the + description in the cancellation function for detail on the handling of possible + race conditions. + + A RequestCompletionPath flag in the write Context is used with atomic Interlocked function + to ensure deterministic operation in both the cancellation and this completion functions. + + If the cancellation function has been called, the WdfRequestUnmarkCancelable in the completion function will return STATUS_CANCELLED. + This return code is used to determine to handle the processing either as a typical completion, or as a cancellation and be in sync + with the cancellation function. + + Here are what are performed in either situations: + + 1. Typical completion (completion function only) + - WdfRequestUnmarkCancelable() returns not STATUS_CANCELLED + Exercise its typical completion code path + - Retrieve data transfer information for success case + - Dereference(RequestUART) - will not be accessed by cancellation function + - Complete(RequestFromUpper) & Delete(its Memory Object) + + - Delete(RequestUART) + - Dereference(RequestFromUpper) + + 2. Cancellation (both functions) + A: Cancellation Function + WdfRequestCancelSentRequest(RequestToUART) to cancel RequestToUART + - Dereference(RequestToUART) after cancel is sent + - Complete(RequestFromUpper) & Delete(its Memory Object) + + B: Completion function + WdfRequestUnmarkCancelable() returns STATUS_CANCELLED + Exercise its cancellation code path + - Delete(RequestToUART) + - Dereference(RequestFromUpper) + + Note: Code path A & B have no synchronization object to ensure their order of execution, but reference is taken on the Requests to ensure + that they stay valid until last access. + + RequestToUART - take a reference to protect against being used by the cancellation function; it is de-referenced by the + - completion function - in its typical completion code path, or + - cancellation function - after finishing accessing it (to sent cancel) + + RequestFromBthport - take a reference to protect against being completed by the cancellation function and then its context + is later accessed by the completion function; this can happen if the completion function is completed + asynchronously after WdfRequestCancelSentRequest() is returned; it is de-referenced by the + - completion function - right before it exits. + +Arguments: + + _Request - WDF Request allocated by this driver + _Target - WDF IO Target + _Params - Completion parameters + _Context - Context used to process this request + +Return Value: + + none + +--*/ +{ + NTSTATUS Status; + PUART_WRITE_CONTEXT TransferContext; + PFDO_EXTENSION FdoExtension; + WDFREQUEST RequestFromBthport; + ULONG BytesDataWritten = 0; + LONG CompletePath = REQUEST_PATH_NONE; + + UNREFERENCED_PARAMETER(_Target); + + Status = _Params->IoStatus.Status; + TransferContext = (PUART_WRITE_CONTEXT) _Context; + + DoTrace(LEVEL_INFO, TFLAG_DATA,("+CR_WriteDeviceIO: %!STATUS!, Request %p, Context %p", + Status, _Request, _Context)); + + NT_ASSERT( (Status == STATUS_SUCCESS || Status == STATUS_CANCELLED) && L"WriteHCI request failed!"); + + // + // Request to be completed to upper layer. + // + RequestFromBthport = TransferContext->RequestFromBthport; + + // + // The below operation can return one of the following values. + // REQUEST_PATH_NONE + // This value was returned either because + // 1. This is the normal operation for this function and the request from bthport has to be completed. + // 2. The request from bthport has already been cancelled, but the cancellation routine has not yet been called (race condition). + // + // No matter what causes this value to be returned, it is safe to call WdfRequestUnmarkCancelable on the request from bthport + // + // REQUEST_PATH_CANCELLATION + // The cancellation routine was already called. + // + // This function does not have the control to complete the request from bthport. + // + CompletePath = InterlockedOr(&TransferContext->RequestCompletePath, REQUEST_PATH_COMPLETION); + + // Mark RequestFromBthPort not cancellable as it is about to be completed. + if (REQUEST_PATH_NONE != CompletePath) + { + DoTrace(LEVEL_ERROR, TFLAG_IO,(" Request %p is in the process of being cancelled", RequestFromBthport)); + } + else + { + // + // Call WdfRequestUnmarkCancelable() to check whether this request has already been cancelled. + // + if (STATUS_CANCELLED == WdfRequestUnmarkCancelable(RequestFromBthport)) { + // + // The request from bthport has already been cancelled. + // Try to relinquish control of completing the request from bthport to the cancellation routine. It is possible that the cancellation routine + // has already been executed. In this case, this routine will have to complete the request from bthport. + // + // The below operation can return one of the following values. + // REQUEST_PATH_CANCELLATION | REQUEST_PATH_COMPLETION + // The cancellation routine was called. The cancellation will not complete the request, so this function will have to complete it. + // + // REQUEST_PATH_COMPLETION + // The cancellation routine has not yet been called. + // The InterlockedCompareExchange successfully masked the REQUEST_PATH_COMPLETE bit and so the completin routine + // will complete this request. + // + CompletePath = InterlockedCompareExchange(&TransferContext->RequestCompletePath, + REQUEST_PATH_NONE, + REQUEST_PATH_COMPLETION); + + // + // Since the cancellation was already called and it will not complete the request, reset the value of complete to + // REQUEST_PATH_NONE so that the request from bthport will be completed. + // + if (CompletePath & REQUEST_PATH_CANCELLATION) { + CompletePath = REQUEST_PATH_NONE; + } + } + + if (REQUEST_PATH_NONE == CompletePath) { + + // Dereference this request as cancellation function is not invoked to access it. + WdfObjectDereference(_Request); + + // + // Return data transfer information to caller for success Status + // + if (NT_SUCCESS(Status)) + { + WDFMEMORY ReqOutMemory = NULL; + ULONG BytesWritten; + PULONG OutBuffer = NULL; + size_t OutBufferSize = 0; + + BytesWritten = (ULONG) _Params->Parameters.Write.Length; + + DoTrace(LEVEL_INFO, TFLAG_DATA,(" Packet: Type %d, DataLen %d, BytesWritten %d", + TransferContext->HCIContext->Type, + TransferContext->HCIContext->DataLen, + BytesWritten)); + + NT_ASSERT(BytesWritten == TransferContext->HCIPacketLen && "Unexpected incomplete HCI Write!"); + + if (BytesWritten != TransferContext->HCIPacketLen) + { + // return a generic failure for an incomplete transfer + Status = STATUS_UNSUCCESSFUL; + goto Done; + } + + // + // return data bytes written in the OutputParameter + // + Status = WdfRequestRetrieveOutputMemory(RequestFromBthport, &ReqOutMemory); + if (NT_SUCCESS(Status)) + { + OutBuffer = (PULONG) WdfMemoryGetBuffer(ReqOutMemory, &OutBufferSize); + if (OutBufferSize >= sizeof(ULONG)) + { + // Set OutputParameter value and its size + *OutBuffer = TransferContext->HCIContext->DataLen; + BytesDataWritten = sizeof(ULONG); + } + } + } + else + { + // Return the status as is. + } + } + } + +Done: + + if (REQUEST_PATH_NONE == CompletePath) + { + // Increment the completion count based on packet type. + FdoExtension = TransferContext->FdoExtension; + + if (TransferContext->HCIContext->Type == (UCHAR) HciPacketCommand) + { + InterlockedIncrement(&FdoExtension->CntCommandCompleted); + } + else if (TransferContext->HCIContext->Type == (UCHAR) HciPacketAclData) + { + InterlockedIncrement(&FdoExtension->CntWriteDataCompleted); + } + + DoTrace(LEVEL_INFO, TFLAG_IO,(" WriteDeviceIO: Request %p complete with %!STATUS! and %d BytesDataWritten", + RequestFromBthport, Status, BytesDataWritten)); + + // Delete this memory object that is no longer needed. + WdfObjectDelete(TransferContext->Memory); + + // Cannot access this Request and its context after it is completed. + WdfRequestCompleteWithInformation(RequestFromBthport, Status, BytesDataWritten); + + } + + // Delete this request in its completion function. + WdfObjectDelete(_Request); + + // Done accessing it in this function. This request is either completed in this function for the typical completion situation or in the cancellation function. + WdfObjectDereference(RequestFromBthport); + + DoTrace(LEVEL_INFO, TFLAG_IO,("-CR_WriteDeviceIO")); +} + + + +VOID +ReadSegmentStateSet( + PUART_READ_CONTEXT _ReadContext, + UART_READ_STATE _NewState + ) +/*++ + +Routine Description: + + This helper centralize the setting of read state. It can be used to detect + possible incorrect state transition. + +Arguments: + + _ReadContext - read context which has existing state + _NewState - new read state + +Return Value: + + none + +--*/ +{ + UART_READ_STATE OldState = _ReadContext->ReadSegmentState; + + DoTrace(LEVEL_INFO, TFLAG_IO, ("+<<<< -- %s to %s state -- >>>>", + OldState == GET_PKT_TYPE ? "Type" : + OldState == GET_PKT_HEADER ? "Header" : + OldState == GET_PKT_PAYLOAD ? "Payload" : "Unknown", + _NewState == GET_PKT_TYPE ? "Type" : + _NewState == GET_PKT_HEADER ? "Header" : + _NewState == GET_PKT_PAYLOAD ? "Payload" : "Unknown" )); + + // Validate the state transition + switch (_NewState) + { + case GET_PKT_TYPE: + // Intialize the context for a new packet + _ReadContext->BytesReadNextSegment = 0; + _ReadContext->H4Packet.Type = 0; + _ReadContext->BytesToRead4FullPacket = 0; + RtlZeroMemory(_ReadContext->H4Packet.Packet.Raw, HCI_ACLDATA_HEADER_LEN); + break; + case GET_PKT_HEADER: + case GET_PKT_PAYLOAD: + // Reset segment count + _ReadContext->BytesReadNextSegment = 0; + break; + } + + _ReadContext->ReadSegmentState = _NewState; +} + + // Full packet: match to a Request and complete it. +NTSTATUS +ReadH4PacketComplete( + PFDO_EXTENSION _FdoExtension, + UCHAR _Type, + _In_reads_bytes_(_BufferLength) PUCHAR _Buffer, + ULONG _BufferLength + ) +{ + NTSTATUS Status = STATUS_SUCCESS; + + DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4PacketComplete %S Packet Length %d", + _Type == (UCHAR) HciPacketEvent ? L"Event" : L"AclData", _BufferLength )); + +#if DBG + // Tracking last completed packet + RtlCopyMemory(_FdoExtension->LastPacket, _Buffer, _BufferLength); + _FdoExtension->LastPacketLength = _BufferLength; +#endif + + if (_Type == (UCHAR) HciPacketEvent) + { + ReadRequestComplete(_FdoExtension, + HciPacketEvent, + _BufferLength, + _Buffer, + _FdoExtension->ReadEventQueue, + &_FdoExtension->EventQueueCount, + &_FdoExtension->ReadEventList, + &_FdoExtension->EventListCount); + } + else + { + ReadRequestComplete(_FdoExtension, + HciPacketAclData, + _BufferLength, + _Buffer, + _FdoExtension->ReadDataQueue, + &_FdoExtension->DataQueueCount, + &_FdoExtension->ReadDataList, + &_FdoExtension->DataListCount); + } + + DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadH4PacketComplete %!STATUS!", Status)); + + return Status; +} + + +NTSTATUS +ReadH4PacketReassemble( + _Inout_ PUART_READ_CONTEXT _ReadContext, + _In_ ULONG _BytesRead, + _In_reads_bytes_(_BytesRead) PUCHAR _Buffer + ) +/*++ + +Routine Description: + + A function enforce a state machine to process reading data to form a + complete HCI packet. + +Arguments: + + _ReadContext - read context + _BytesRead - bytes of data read and is in the output buffer + _OutBuffer - Buffer that contain the data + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG BytesRemained = _BytesRead; + PUCHAR Buffer = _Buffer; + PFDO_EXTENSION FdoExtension = _ReadContext->FdoExtension; + PH4_PACKET H4Packet; + ULONG PacketLen; + ULONG BytesToRead; + + + DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4PacketReassemble: %d _BytesRead, ReadSegmentState %d", + _BytesRead, _ReadContext->ReadSegmentState)); + + // + // By design, it will take two reads to complete an H4 packets. + // + // First Read (5 bytes = 1 + 4 = Type + Larger of (ACLDataHeader:4, EvetnHeader:2)) + // + // - Event + // Complete (1 + 2 ), this is an Event packet without any param. + // Complete (1 + 2 + 1), event with 1 param + // * These two outcome requires interval timeout to complete the read (ask for 5). + // Complete (1 + 2 + 2), event with 2 params + // * if completed with one read, do the First read again. + // + // Partial (1 + 2 + 2 + ParamCount-2), this will complete in next read + // BytesToRead = ParamCount - 2 + // + // - ACL Data + // Partial (1 + 4 + DataLength), this packet will be complete in next read + // ByteToRead = DataLength + // Second read + // - Event/AclData + // Complete (5 + BytesToRead) + // + + while (NT_SUCCESS(Status) && BytesRemained > 0) { + + // Process read buffer based on its read state + switch (_ReadContext->ReadSegmentState) { + case GET_PKT_TYPE: + H4Packet = (PH4_PACKET) Buffer; + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1); + + if (H4Packet->Type == (UCHAR) HciPacketEvent) { + DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] ---------- ")); + _ReadContext->BytesToRead4FullPacket = HCI_EVENT_HEADER_SIZE; + } + else if (H4Packet->Type == (UCHAR) HciPacketAclData) { + DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] ---------- ")); + _ReadContext->BytesToRead4FullPacket = HCI_ACL_HEADER_SIZE; + } + else { + // + // Abort the read operation here but can consider to traverse the data + // until a valid packet type is found. + // + Status = STATUS_INVALID_PARAMETER; // discard and read again + DoTrace(LEVEL_ERROR, TFLAG_IO, (" Unexpected PacketType %d", H4Packet->Type)); + NT_ASSERT(FALSE && L"Detected unknown packet type"); + goto OutOfSync; + } + + // Proceed to read packet header + _ReadContext->H4Packet.Type = H4Packet->Type; // Valid packet type is cached. + ReadSegmentStateSet(_ReadContext, GET_PKT_HEADER); + break; + + case GET_PKT_HEADER: + if (_ReadContext->H4Packet.Type == (UCHAR) HciPacketEvent) { + if (_ReadContext->BytesReadNextSegment == 0 && BytesRemained) { + _ReadContext->H4Packet.Packet.Event.EventCode = *Buffer; + DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] Code 0x%x", _ReadContext->H4Packet.Packet.Event.EventCode)); + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1); + _ReadContext->BytesToRead4FullPacket = 1; // Read the ParamsCount if needed + } + + if (_ReadContext->BytesReadNextSegment == 1 && BytesRemained) { + _ReadContext->H4Packet.Packet.Event.ParamsCount = *Buffer; + DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] ParamsCount 0x%x", _ReadContext->H4Packet.Packet.Event.ParamsCount)); + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1); + + if (_ReadContext->H4Packet.Packet.Event.ParamsCount == 0) { + // Full packet: match to a Request and complete it. + PacketLen = HCI_EVENT_HEADER_LEN + _ReadContext->H4Packet.Packet.Event.ParamsCount; + DoTrace(LEVEL_INFO, TFLAG_DATA, (" [Event completed] PacketLen %d", PacketLen)); + Status = ReadH4PacketComplete(FdoExtension, + _ReadContext->H4Packet.Type, + (PUCHAR) &_ReadContext->H4Packet.Packet.Event, + PacketLen); + // Read next packet + ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE); + } + // Read the remainder of a full (Event) packet + else { + if (BytesRemained < _ReadContext->H4Packet.Packet.Event.ParamsCount) { + _ReadContext->BytesToRead4FullPacket = + _ReadContext->H4Packet.Packet.Event.ParamsCount - BytesRemained; + } + + // Process to read packet payload + ReadSegmentStateSet(_ReadContext, GET_PKT_PAYLOAD); + } + } + } + else { + + if (_ReadContext->BytesReadNextSegment == 0 && BytesRemained) { + _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer; + DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[0] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment])); + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1); + _ReadContext->BytesToRead4FullPacket = 3; // Read the remaining Dta header if needed + } + + if (_ReadContext->BytesReadNextSegment == 1 && BytesRemained) { + _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer; + DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[1] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment])); + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1); + _ReadContext->BytesToRead4FullPacket = 2; // Read the remaining Dta header if needed + } + + if (_ReadContext->BytesReadNextSegment == 2 && BytesRemained) { + _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer; + DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[2] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment])); + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1); + _ReadContext->BytesToRead4FullPacket = 1; // Read the remaining Dta header if needed + } + + if (_ReadContext->BytesReadNextSegment == 3 && BytesRemained) { + _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment] = *Buffer; + DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Header[3] 0x%x", _ReadContext->H4Packet.Packet.Raw[_ReadContext->BytesReadNextSegment])); + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, 1); + + // Read the reamainder of a full (Data) packet + if (BytesRemained < _ReadContext->H4Packet.Packet.AclData.DataLength) { + _ReadContext->BytesToRead4FullPacket = + _ReadContext->H4Packet.Packet.AclData.DataLength - BytesRemained; + } + + // Process to read packet payload + ReadSegmentStateSet(_ReadContext, GET_PKT_PAYLOAD); + } + } + break; + + case GET_PKT_PAYLOAD: + if (_ReadContext->H4Packet.Type == (UCHAR) HciPacketEvent) { + + BytesToRead = _ReadContext->H4Packet.Packet.Event.ParamsCount - _ReadContext->BytesReadNextSegment; + + if (BytesRemained >= BytesToRead) { + // Full packet + RtlCopyMemory(&_ReadContext->H4Packet.Packet.Event.Params[_ReadContext->BytesReadNextSegment], + Buffer, + BytesToRead); + DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] Payload[%d + %d] = FULL", + _ReadContext->BytesReadNextSegment, + BytesToRead)); + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesToRead); + + // Full packet: match to a Request and complete it. + PacketLen = HCI_EVENT_HEADER_LEN + _ReadContext->H4Packet.Packet.Event.ParamsCount; + Status = ReadH4PacketComplete(FdoExtension, + _ReadContext->H4Packet.Type, + (PUCHAR) &_ReadContext->H4Packet.Packet.Event, + PacketLen); + // Read next packet + ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE); + } + else { + // Partial packet + RtlCopyMemory(&_ReadContext->H4Packet.Packet.Event.Params[_ReadContext->BytesReadNextSegment], + Buffer, + BytesRemained); + DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] Payload[%d + %d] = Partial; %d to read", + _ReadContext->BytesReadNextSegment, + BytesRemained, + BytesToRead - BytesRemained)); + _ReadContext->BytesReadNextSegment += BytesRemained; + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesRemained); + + // Remaining event params to read + _ReadContext->BytesToRead4FullPacket = + _ReadContext->H4Packet.Packet.Event.ParamsCount - _ReadContext->BytesReadNextSegment; + } + } + else { + + if (_ReadContext->H4Packet.Packet.AclData.DataLength > HCI_MAX_ACL_PAYLOAD_SIZE) { + Status = STATUS_INVALID_PARAMETER; // discard and read again + DoTrace(LEVEL_ERROR, TFLAG_IO, (" Unexpected ACL DataLength %d > Presetted maximum size %d", + _ReadContext->H4Packet.Packet.AclData.DataLength, + HCI_MAX_ACL_PAYLOAD_SIZE)); + NT_ASSERT(FALSE && L"Max ACL DataLength exceeded the presetted Max"); + goto OutOfSync; + } + + BytesToRead = _ReadContext->H4Packet.Packet.AclData.DataLength - _ReadContext->BytesReadNextSegment; + + if (BytesRemained >= BytesToRead) { + // Process full packet + RtlCopyMemory(&_ReadContext->H4Packet.Packet.AclData.Data[_ReadContext->BytesReadNextSegment], + Buffer, + BytesToRead); + DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Payload[%d + %d] = FULL", + _ReadContext->BytesReadNextSegment, + BytesToRead)); + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesToRead); + + // Full packet: try match to a Request in queue (if any) and complete it. + PacketLen = HCI_ACLDATA_HEADER_LEN + _ReadContext->H4Packet.Packet.AclData.DataLength; + Status = ReadH4PacketComplete(FdoExtension, + _ReadContext->H4Packet.Type, + (PUCHAR) &_ReadContext->H4Packet.Packet.AclData, + PacketLen); + // Next packet + ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE); + } + else { + // Process partial packet + RtlCopyMemory(&_ReadContext->H4Packet.Packet.AclData.Data[_ReadContext->BytesReadNextSegment], + Buffer, + BytesRemained); + DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Payload[%d + %d] = Partial; %d to read", + _ReadContext->BytesReadNextSegment, + BytesRemained, + BytesToRead - BytesRemained)); + _ReadContext->BytesReadNextSegment += BytesRemained; + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesRemained); + + // Remaining data to read + _ReadContext->BytesToRead4FullPacket = + _ReadContext->H4Packet.Packet.AclData.DataLength - _ReadContext->BytesReadNextSegment; + } + } + break; + + default: + DoTrace(LEVEL_ERROR, TFLAG_IO, (" Unknown ReadSegmentState")); + break; + } + } + + return Status; + +OutOfSync: + + DoTrace(LEVEL_ERROR, TFLAG_IO, (" Out-of-sync error detected in ProcessReadBuffer() %!STATUS!", Status)); + + return Status; +} + + +VOID +ReadH4PacketCompletionRoutine( + _In_ WDFREQUEST _Request, + _In_ WDFIOTARGET _Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS _Params, + _In_ WDFCONTEXT _Context + ) +/*++ + +Routine Description: + + This is CR function for reading data from device. It process the data read and + send down another request unless there is an error or the request is being + canceled. + +Arguments: + + _Request - a caller allocated WDF Request + _Target - WDF IO Target + _Params - Completion parameters + _Context - Context of this request + +Return Value: + + none + +--*/ +{ + NTSTATUS Status; + PUART_READ_CONTEXT ReadContext; + PFDO_EXTENSION FdoExtension; + ULONG BytesRead; + WDFMEMORY ReadMemory; + PUCHAR OutBuffer; + size_t OutBufferSize; + READ_REQUEST_STATE PreviousState; + + + UNREFERENCED_PARAMETER(_Request); + UNREFERENCED_PARAMETER(_Target); + + // Operation result + Status = _Params->IoStatus.Status; + BytesRead = (ULONG) _Params->Parameters.Read.Length; + + ReadContext = (PUART_READ_CONTEXT) _Context; + ReadContext->Status = Status; + + // Set to REQUEST_COMPLETE if skip REQUEST_PENDING state. + PreviousState = InterlockedCompareExchange((PLONG)&ReadContext->RequestState, + REQUEST_COMPLETE, + REQUEST_SENT); + + DoTrace(LEVEL_WARNING, TFLAG_DATA, ("+ReadH4PacketCompletionRoutine %!STATUS! %d BytesRead %S)", + Status, BytesRead, PreviousState == REQUEST_PENDING ? L"Async" : L"*Sync*")); + + FdoExtension = (PFDO_EXTENSION) ReadContext->FdoExtension; + + // + // The return status can either be + // - successful (buffer completely filled), + // - timeout (buffer not completed filled prior to interval timeout expired + // - cancellation + // - failure + // + if (NT_SUCCESS(Status) || Status == STATUS_IO_TIMEOUT || Status == STATUS_TIMEOUT) { + // Continue to process + } + else { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4PacketCompletionRoutine failed %!STATUS!", Status)); + if (Status == STATUS_CANCELLED) { + // + // Under regualr operational state, IO Target will only cancel a request + // when it is ready to abort (e.g. device removal). + // + } + + goto Exit; + } + + ReadMemory = _Params->Parameters.Read.Buffer; + OutBuffer = (PUCHAR) WdfMemoryGetBuffer(ReadMemory, &OutBufferSize); + NT_ASSERT(OutBufferSize >= BytesRead); + DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4PacketCompletionRoutine %d BytesRead pBuffer %p", BytesRead, OutBuffer)); + + // + // Process a read buffer if there is data + // + if (OutBuffer && BytesRead) + { + // + // Process the incoming data to form partial or full H4 packet + // + Status = ReadH4PacketReassemble(ReadContext, + BytesRead, + OutBuffer); + + // If data stream error, ignore the packet and start over. + if (!NT_SUCCESS(Status)) + { + FdoExtension->OutOfSyncErrorCount++; + DoTrace(LEVEL_ERROR, TFLAG_IO, (" ====> [%d] 0x%x <=====", + FdoExtension->OutOfSyncErrorCount, + *OutBuffer)); + NT_ASSERT(NT_SUCCESS(Status) && L"Encountered an out-of-sync condition!"); + + // Prepare to read next data packet, starting with packet type. + ReadSegmentStateSet(ReadContext, GET_PKT_TYPE); + + // Log(Error): log statistic of the read pump until this error + + // + // If there is a (knonw) hardware error or if we have exceeded maximun hardware count, + // the link is no longer reliable. Need to report to the upper layer via a read request. + // + if (FdoExtension->HardwareErrorDetected && FdoExtension->OutOfSyncErrorCount > MAX_HARDWARE_ERROR_COUNT) + { + // + // Complete an event or read data request with STATUS_DEVICE_DATA_ERROR error to trigger + // BthMini/BthPort to handle the situation. IT can perform HCI_RESET to restore the + // data channel. + // +#ifdef REPORT_HARDWARE_ERROR + WDFREQUEST Request; + + DoTrace(LEVEL_ERROR, TFLAG_IO, (" ++++ Report a hardware error; OutOfSyncCount %d", FdoExtension->OutOfSyncErrorCount)); + + KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql); + // Complete a read (event or data) request with a specific error to indicate hardware error. + Status = WdfIoQueueRetrieveNextRequest(FdoExtension->ReadEventQueue, &Request); + + // if there is no event request, find a read data request. + if (Status == STATUS_NO_MORE_ENTRIES) + { + Status = WdfIoQueueRetrieveNextRequest(FdoExtension->ReadDataQueue, &Request); + } + KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); + + + if (NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" Complete a request with STATUS_DEVICE_DATA_ERROR")); + WdfRequestComplete(Request, STATUS_DEVICE_DATA_ERROR); + } +#endif // REPORT_HARDWARE_ERROR + Status = STATUS_DEVICE_DATA_ERROR; + + // abort and stop read pump + goto Exit; + + } + else + { + + DoTrace(LEVEL_ERROR, TFLAG_IO, (" Detect out-of-sync error but read ahead...")); + + // Reset hardware error. + FdoExtension->HardwareErrorDetected = FALSE; + + // try next + goto ReadNext; + } + } + } + else + { + NT_ASSERT(Status == STATUS_TIMEOUT); + } + +ReadNext: + + if (PreviousState == REQUEST_PENDING) + { + ULONG BytesToRead; + + // + // Determine what is the size of the buffer to send down. + // + BytesToRead = (ReadContext->ReadSegmentState == GET_PKT_TYPE ? INITIAL_H4_READ_SIZE : + ReadContext->BytesToRead4FullPacket ? ReadContext->BytesToRead4FullPacket : + sizeof(FdoExtension->ReadBuffer)); + + DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4Packet(Read Buffer Size %d bytes)", BytesToRead)); + + // Issue next read here since this request was complete asychronously + // i.e. pending first and then this completion routein is invoked. + ReadH4Packet(ReadContext, + FdoExtension->ReadRequest, + FdoExtension->ReadMemory, + FdoExtension->ReadBuffer, + BytesToRead); + } + else + { + // Fall through and leave this fucntion if this request was completed synchronously; + // i.e. this function is invoked first and then return to the RequestSent function. + } + + DoTrace(LEVEL_INFO, TFLAG_IO, ("-CR_ReadReadIO (fall though)")); + + return; + +Exit: + + if (!NT_SUCCESS(Status)) + { + NT_ASSERT(Status == STATUS_CANCELLED); + FdoExtension->ReadPumpRunning = FALSE; + DoTrace(LEVEL_WARNING, TFLAG_IO, (" Pump has stopped!")); + } + + DoTrace(LEVEL_INFO, TFLAG_IO, ("-CR_ReadReadIO (error)")); +} + + +NTSTATUS +ReadH4Packet( + _In_ PUART_READ_CONTEXT _ReadContext, + _In_ WDFREQUEST _WdfRequest, + _In_ WDFMEMORY _WdfMemory, + _Pre_notnull_ _Pre_writable_byte_size_ (_BufferLen) PVOID _Buffer, + _In_ ULONG _BufferLen + ) +/*++ + +Routine Description: + + Initiate the reading of an HCI packet (event or data) by sending down a read request. + +Arguments: + + _ReadContext - Context used for reading data from target UART device + +Return Value: + + NTSTATUS + +--*/ +{ + PFDO_EXTENSION FdoExtension; + WDF_REQUEST_REUSE_PARAMS RequestReuseParams; + NTSTATUS Status; + + + DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4Packet")); + + FdoExtension = _ReadContext->FdoExtension; + + if (0 == _BufferLen) { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4Packet: _BufferLen cannot be 0")); + Status = STATUS_INVALID_PARAMETER; + goto Done; + } + + while (TRUE) { + + DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4Packet - ")); + NT_ASSERT(_ReadContext->RequestState != REQUEST_SENT); + + if (!IsDeviceInitialized(FdoExtension)) { + Status = STATUS_DEVICE_NOT_READY; + DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4Packet: cannot attach IO %!STATUS!", Status)); + goto Done; + } + + // + // Issue a read event request + // + WDF_REQUEST_REUSE_PARAMS_INIT(&RequestReuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS); + Status = WdfRequestReuse(_WdfRequest, &RequestReuseParams); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestReuse failed %!STATUS!", Status)); + goto Done; + } + + Status = WdfMemoryAssignBuffer(_WdfMemory, _Buffer, _BufferLen); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryAssignBuffer failed %!STATUS!", Status)); + goto Done; + } + + Status = WdfIoTargetFormatRequestForRead(FdoExtension->IoTargetSerial, + _WdfRequest, + _WdfMemory, + NULL, NULL); + + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoTargetFormatRequestForRead failed %!STATUS!", Status)); + goto Done; + } + + // Note: This request is sent to UART driver so it cannot be marked cancellable. + // But it can be canceled by issuing WdfRequestCancelSentRequest(). + + WdfRequestSetCompletionRoutine(_WdfRequest, + ReadH4PacketCompletionRoutine, + _ReadContext); + + InterlockedExchange((PLONG)&_ReadContext->RequestState, REQUEST_SENT); + + if (FALSE == WdfRequestSend(_WdfRequest, + FdoExtension->IoTargetSerial, + WDF_NO_SEND_OPTIONS)) + { + Status = WdfRequestGetStatus(_WdfRequest); + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestSend failed %!STATUS!", Status)); + + // Not much we can do if cannot send this request; data pump will be stopped! + goto Done; + } + else + { + READ_REQUEST_STATE PreviousState; + + // Set to REQUEST_PENDING if it is in the REQUEST_SENT state. + PreviousState = InterlockedCompareExchange((PLONG) &_ReadContext->RequestState, + REQUEST_PENDING, + REQUEST_SENT); + + DoTrace(LEVEL_WARNING, TFLAG_IO, (" WdfRequestSend ReqState: %d -> %d", + PreviousState, _ReadContext->RequestState)); + + if (PreviousState == REQUEST_SENT) + { + // Request is still pending, and will be completed asychronously in the + // completion routine where it can issue next read. + Status = STATUS_PENDING; + break; + } + else + { + Status = FdoExtension->ReadContext.Status; + if (NT_SUCCESS(Status)) + { + // Previous request has been complete synchronously in the + // completion routine; do next read in this function. + } + else + { + // No tolerance for error + break; + } + } + } + } + +Done: + + if (!NT_SUCCESS(Status)) + { + NT_ASSERT(Status == STATUS_CANCELLED); + FdoExtension->ReadPumpRunning = FALSE; + } + + DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadH4Packet %!STATUS!", Status)); + + return Status; +} + +__inline +PHCI_PACKET_ENTRY +HLP_CreatePacketEntry( + _In_ ULONG _PacketLength, + _In_reads_bytes_(_PacketLength) PUCHAR _Packet + ) +{ + PHCI_PACKET_ENTRY PacketEntry = NULL; + + PacketEntry = (PHCI_PACKET_ENTRY)ExAllocatePool(NonPagedPoolNx, sizeof(HCI_PACKET_ENTRY) + _PacketLength); + if (PacketEntry != NULL) { + InitializeListHead(&PacketEntry->DataEntry); + RtlCopyMemory(PacketEntry->Packet, _Packet, _PacketLength); + PacketEntry->PacketLen = _PacketLength; + } + + return PacketEntry; +} + +NTSTATUS +ReadRequestComplete( + _In_ PFDO_EXTENSION _FdoExtension, + _In_ UCHAR _PacketType, + _In_ ULONG _PacketLength, + _In_reads_bytes_opt_(_PacketLength) PUCHAR _Packet, + _Inout_ WDFQUEUE _Queue, + _Inout_ PLONG _QueueCount, + _Inout_ PLIST_ENTRY _ListHead, + _Inout_ PLONG _ListCount + ) +/*++ +Routine Description: + + This helper function processes both complete HCI Data packet from the device to find + a pending Request, or find a completed HCI packet in a list to complete a Request. + +Arguments: + + _FdoExtension - Device context + _PacketType - HCI packet type (either Event or Data for incoming data) + _ListHead - List where to retrieve completed HCI packet + _Request - Request that is used to complete a read if a corresponding HCI packet is available. + +Return Value: + + NTSTATUS - STATUS_SUCCESS Or STATUS_INSUFFICIENT_RESOURCE + +--*/ +{ + KIRQL Irql; + WDFREQUEST Request = NULL; + NTSTATUS Status = STATUS_SUCCESS; + PHCI_PACKET_ENTRY PacketEntry = NULL; + WDFMEMORY ReqOutMemory; + size_t BufferSize = 0, BytesToReturn; + PBTHX_HCI_READ_WRITE_CONTEXT HCIContext; + BOOLEAN CompleteRequest = FALSE; + + DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadRequestComplete")); + + // + // (ReqQueue, PktList) + // C0. ( empty, empty) -> Add packet to list + // C1. ( empty, !empty) -> Add packet to list + // C2. (!empty, empty) -> DequeueAndCompletRequest(Packet) + // C3. (!empty, !empty) -> Error! Cannot both empty at this function entry. + // + + KeAcquireSpinLock(&_FdoExtension->QueueAccessLock, &Irql); + + if (_Packet) { + + Status = WdfIoQueueRetrieveNextRequest(_Queue, &Request); + if (Status == STATUS_SUCCESS) { + // Case 2: Typical code path + InterlockedDecrement(_QueueCount); + DoTrace(LEVEL_INFO, TFLAG_IO, (" (C2) Complete a request %p, _Packet %p, _PacketLength %d", + Request, _Packet, _PacketLength)); + + CompleteRequest = TRUE; + + // Case 3: An error condition if List is not empty + NT_ASSERT(IsListEmpty(_ListHead)); + } + else { + // Case 0: + PacketEntry = HLP_CreatePacketEntry(_PacketLength, _Packet); + if (PacketEntry == NULL) { + // Error condition + Status = STATUS_INSUFFICIENT_RESOURCES; + DoTrace(LEVEL_ERROR, TFLAG_IO, (" (C0/Error) Could not allocate HCI_PACKET_ENTRY %!STATUS!", Status)); + // This packet will be dropped; but nothing we can do as system resource is depleted! + } + else { + // Cache this packet to Packet List + InsertTailList(_ListHead, &PacketEntry->DataEntry); + InterlockedIncrement(_ListCount); + DoTrace(LEVEL_INFO, TFLAG_IO, (" (C0) Queuing packet with list count %d", *_ListCount)); + } + } + } + else { + if (!IsListEmpty(_ListHead)) { + Status = WdfIoQueueRetrieveNextRequest(_Queue, &Request); + if (Status == STATUS_SUCCESS) { + // Case 2: Has Packet in the list while a new request arrives + InterlockedDecrement(_QueueCount); + + PacketEntry = (PHCI_PACKET_ENTRY) RemoveHeadList(_ListHead); + _Packet = PacketEntry->Packet; + _PacketLength = PacketEntry->PacketLen; + InterlockedDecrement(_ListCount); + + DoTrace(LEVEL_INFO, TFLAG_IO, (" (C2) Complete a request %p, _Packet %p, _PacketLength %d", + Request, _Packet, _PacketLength)); + + CompleteRequest = TRUE; + } + else { + NT_ASSERT(FALSE && L"Failed to retrieve a request just queued!"); + } + } + else { + // Case 1: Request is pre-pening and queued. + Status = STATUS_PENDING; + DoTrace(LEVEL_INFO, TFLAG_IO, (" (C1) Read request is queued")); + } + } + + KeReleaseSpinLock(&_FdoExtension->QueueAccessLock, Irql); + + if (!CompleteRequest) { + goto Done; + } + + // Complete this request + Status = WdfRequestRetrieveOutputMemory(Request, &ReqOutMemory); + if (Status != STATUS_SUCCESS) { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" Could not retrieve output buffer")); + WdfRequestCompleteWithInformation(Request, Status, (ULONG_PTR)0); + goto Done; + } + + HCIContext = WdfMemoryGetBuffer(ReqOutMemory, &BufferSize); + BytesToReturn = FIELD_OFFSET(BTHX_HCI_READ_WRITE_CONTEXT, Data) + _PacketLength; + + // This should not happen because BthMini should have sent down largest buffer according to device's capability. + NT_ASSERT(BytesToReturn <= BufferSize); + + // Transfer data to Request's output buffer + HCIContext->Type = _PacketType; + HCIContext->DataLen = _PacketLength; + if (BytesToReturn <= BufferSize) { + RtlCopyMemory(&HCIContext->Data, _Packet, _PacketLength); + } + else { + Status = STATUS_BUFFER_TOO_SMALL; + BytesToReturn = 0; + } + + // Validate and print out (WPP) HCI packet info + HCIContextValidate(HCIContext->Type == (UCHAR) HciPacketEvent ? + _FdoExtension->CntEventCompleted : _FdoExtension->CntReadDataCompleted, + HCIContext); + + // + // Release memory allocated for a completed packet entry; it was not removed from the packet list. + // + if (PacketEntry) { + ExFreePool(PacketEntry); + } + + if (HCIContext->Type == (UCHAR) HciPacketEvent) { + InterlockedIncrement(&_FdoExtension->CntEventCompleted); + DoTrace(LEVEL_INFO, TFLAG_DATA, (" [%d] HciPacketEvent completing %!STATUS!, %d BytesToReturn", + _FdoExtension->CntEventCompleted, Status, (ULONG) BytesToReturn)); + } + else if (HCIContext->Type == (UCHAR) HciPacketAclData) { + InterlockedIncrement(&_FdoExtension->CntReadDataCompleted); + DoTrace(LEVEL_INFO, TFLAG_DATA, (" [%d] HciPacketAclData completing %!STATUS!, %d BytesToReturn", + _FdoExtension->CntReadDataCompleted, Status, (ULONG) BytesToReturn)); + } + + DoTrace(LEVEL_INFO, TFLAG_IO, (" Completing Request(%p) %!STATUS!, %d BytesToReturn", + Request, Status, (ULONG) BytesToReturn)); + + // + // return only the actual data read, not including BTHX_HCI_READ_WRITE_CONTEXT + // + WdfRequestCompleteWithInformation(Request, Status, BytesToReturn); + +Done: + + DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadRequestComplete: %!STATUS!", Status)); + + return Status; +} + + +VOID +ReadResourcesFree( + _In_ WDFDEVICE _Device +) +/*++ +Routine Description: + + This helper function free resource allocated in its corresponding allocation + function. + +Arguments: + + _Device - WDF Device object + +Return + + VOID + +--*/ +{ + PFDO_EXTENSION FdoExtension; + KIRQL Irql; + + DoTrace(LEVEL_INFO, TFLAG_IO,("+ReadResourcesFree")); + + FdoExtension = FdoGetExtension(_Device); + + // + // Note: The Request(s) in WDFQUEUE (Event and ReadData) WDFQUEUEs + // are managed by WDF, which will dequeue and cancel them for us. + // WdfIoQueueRetrieveNextRequest() returns STATUS_WDF_PAUSED since this + // function is invoked after entered D0. + // + + // + // Free resources allocated earlier + // + + while(!IsListEmpty(&FdoExtension->ReadEventList)) + { + PHCI_PACKET_ENTRY PacketEntry; + + KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql); + PacketEntry = (PHCI_PACKET_ENTRY)RemoveHeadList(&FdoExtension->ReadEventList); + InterlockedDecrement(&FdoExtension->EventListCount); + KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); + + if (PacketEntry) + { + ExFreePool(PacketEntry); + PacketEntry = NULL; + } + } + NT_ASSERT(FdoExtension->EventListCount == 0); + + while(!IsListEmpty(&FdoExtension->ReadDataList)) + { + PHCI_PACKET_ENTRY PacketEntry; + + KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql); + PacketEntry = (PHCI_PACKET_ENTRY)RemoveHeadList(&FdoExtension->ReadDataList); + InterlockedDecrement(&FdoExtension->DataListCount); + KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); + + if (PacketEntry) + { + ExFreePool(PacketEntry); + PacketEntry = NULL; + } + } + NT_ASSERT(FdoExtension->DataListCount == 0); + + if (FdoExtension->ReadRequest) + { + WdfObjectDelete(FdoExtension->ReadRequest); + FdoExtension->ReadRequest = NULL; + } +} + + +NTSTATUS +ReadResourcesAllocate( + _In_ WDFDEVICE _Device +) +/*++ +Routine Description: + + This helper function allocates resource (queues and lists) for managing read IOs + Request from upper layer or for data pump with the device. + +Arguments: + + _Device - WDF Device object + +Return Value: + + NTSTATUS - STATUS_SUCCESS Or STATUS_INSUFFICIENT_RESOURCE + +--*/ +{ + NTSTATUS Status; + PFDO_EXTENSION FdoExtension; + WDF_IO_QUEUE_CONFIG QueueConfig; + WDF_OBJECT_ATTRIBUTES ObjAttributes; + + DoTrace(LEVEL_INFO, TFLAG_IO,("+ReadResourcesAllocate")); + + FdoExtension = FdoGetExtension(_Device); + + + // HCI_EVENT + // Create WDF Queue for pending Read Event Request(s), and + // Initialize a List for pre-fetched Event + WDF_IO_QUEUE_CONFIG_INIT(&QueueConfig, + WdfIoQueueDispatchManual); + + Status = WdfIoQueueCreate(_Device, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoExtension->ReadEventQueue); + + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoQueueCreate(Event) %!STATUS!", Status)); + goto Done; + } + + InitializeListHead(&FdoExtension->ReadEventList); + + FdoExtension->EventListCount = 0; + FdoExtension->EventQueueCount = 0; + + + // HCI_DATA + // Create WDF Queue for pending Read Data Request(s), and + // Initialize a List for pre-fetched Data + Status = WdfIoQueueCreate(_Device, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoExtension->ReadDataQueue); + + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoQueueCreate(Data) %!STATUS!", Status)); + goto Done; + } + + InitializeListHead(&FdoExtension->ReadDataList); + + FdoExtension->DataListCount = 0; + FdoExtension->DataQueueCount = 0; + + + // Track request from top and HCI packets from device + FdoExtension->CntCommandReq = 0; + FdoExtension->CntCommandCompleted = 0; + + FdoExtension->CntEventReq = 0; + FdoExtension->CntEventCompleted = 0; + + FdoExtension->CntWriteDataReq = 0; + FdoExtension->CntWriteDataCompleted = 0; + + FdoExtension->CntReadDataReq = 0; + FdoExtension->CntReadDataCompleted = 0; + + + // Create a WDF Request + WDF_OBJECT_ATTRIBUTES_INIT(&ObjAttributes); + ObjAttributes.ParentObject = _Device; + + Status = WdfRequestCreate(&ObjAttributes, + FdoExtension->IoTargetSerial, + &FdoExtension->ReadRequest); + + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfRequestCreate(ReadRequest) failed %!STATUS!", Status)); + goto Done; + } + + // Initialize the ReadContext and its initial ReadSegmentState + RtlZeroMemory(&FdoExtension->ReadContext, sizeof(UART_READ_CONTEXT)); + FdoExtension->ReadContext.FdoExtension = FdoExtension; + ReadSegmentStateSet(&FdoExtension->ReadContext, GET_PKT_TYPE); + + Status = WdfMemoryCreatePreallocated(&ObjAttributes, + &FdoExtension->ReadBuffer, + sizeof(FdoExtension->ReadBuffer), + &FdoExtension->ReadMemory); + + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryCreatePreallocated(ReadMemory) failed %!STATUS!", Status)); + goto Done; + } + +Done: + + DoTrace(LEVEL_INFO, TFLAG_IO,("-ReadResourcesAllocate %!STATUS!", Status)); + if (!NT_SUCCESS(Status)) + { + ReadResourcesFree(_Device); + } + + return Status; +} + + diff --git a/bluetooth/serialhcibus/pdo.c b/bluetooth/serialhcibus/pdo.c new file mode 100644 index 00000000..74ee6350 --- /dev/null +++ b/bluetooth/serialhcibus/pdo.c @@ -0,0 +1,972 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + Pdo.c + +Abstract: + + This module create a PDO and handles plug & play calls for the child device (PDO). + +Environment: + + kernel mode only + +--*/ + +#include "driver.h" +#include "pdo.tmh" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, PdoCreate) +#pragma alloc_text(PAGE, PdoDevD0Exit) +#pragma alloc_text(PAGE, PdoDevD0Entry) +#pragma alloc_text(PAGE, PdoDevPrepareHardware) +#pragma alloc_text(PAGE, PdoDevReleaseHardware) +#endif + +#define MAX_ID_LEN 80 + + + +#ifdef DYNAMIC_ENUM + + +NTSTATUS +PdoCreateDynamic( + _In_ WDFDEVICE _Device, + _In_ PWDFDEVICE_INIT _DeviceInit, + _In_ PWCHAR _HardwareIds, + _In_ ULONG _SerialNo + ) +/*++ + +Routine Description: + + This routine creates and initialize a PDO. + +Arguments: + +Return Value: + + NT Status code. + +--*/ +{ + NTSTATUS Status; + PPDO_EXTENSION PdoExtension = NULL; + WDFDEVICE ChildDevice = NULL; + WDF_OBJECT_ATTRIBUTES pdoAttributes; + WDF_DEVICE_PNP_CAPABILITIES pnpCaps; + WDF_DEVICE_POWER_CAPABILITIES powerCaps; + DECLARE_CONST_UNICODE_STRING(compatId, BT_PDO_COMPATIBLE_IDS); + DECLARE_CONST_UNICODE_STRING(deviceLocation, L"Serial HCI Bus - Bluetooth Function"); + DECLARE_UNICODE_STRING_SIZE(buffer, MAX_ID_LEN); + DECLARE_UNICODE_STRING_SIZE(deviceId, MAX_ID_LEN); + + WDF_IO_QUEUE_CONFIG QueueConfig; + WDFQUEUE Queue; + + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_Device); + + KdPrint(("Entered PdoCreateDynamic\n")); + + // + // Set DeviceType + // + WdfDeviceInitSetDeviceType(_DeviceInit, FILE_DEVICE_BUS_EXTENDER); + + // + // Provide DeviceID, HardwareIDs, CompatibleIDs and InstanceId + // + RtlInitUnicodeString(&deviceId, _HardwareIds); + + Status = WdfPdoInitAssignDeviceID(_DeviceInit, &deviceId); + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // NOTE: same string is used to initialize hardware id too + // + Status = WdfPdoInitAddHardwareID(_DeviceInit, &deviceId); + if (!NT_SUCCESS(Status)) { + return Status; + } + + Status = WdfPdoInitAddCompatibleID(_DeviceInit, &compatId ); + if (!NT_SUCCESS(Status)) { + return Status; + } + + Status = RtlUnicodeStringPrintf(&buffer, L"%02d", _SerialNo); + if (!NT_SUCCESS(Status)) { + return Status; + } + + Status = WdfPdoInitAssignInstanceID(_DeviceInit, &buffer); + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Provide a description about the device. This text is usually read from + // the device. In the case of USB device, this text comes from the string + // descriptor. This text is displayed momentarily by the PnP manager while + // it's looking for a matching INF. If it finds one, it uses the Device + // Description from the INF file or the friendly name created by + // coinstallers to display in the device manager. FriendlyName takes + // precedence over the DeviceDesc from the INF file. + // + Status = RtlUnicodeStringPrintf( &buffer, + L"SerialHciBus_%02d", + _SerialNo ); + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // You can call WdfPdoInitAddDeviceText multiple times, adding device + // text for multiple locales. When the system displays the text, it + // chooses the text that matches the current locale, if available. + // Otherwise it will use the string for the default locale. + // The driver can specify the driver's default locale by calling + // WdfPdoInitSetDefaultLocale. + // + Status = WdfPdoInitAddDeviceText(_DeviceInit, + &buffer, + &deviceLocation, + 0x409 ); + if (!NT_SUCCESS(Status)) { + return Status; + } + + WdfPdoInitSetDefaultLocale(_DeviceInit, 0x409); + + // + // Initialize the attributes to specify the size of PDO device extension. + // All the state information private to the PDO will be tracked here. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, PDO_EXTENSION); + + // + // Allow to forward requests to its FDO of this bus driver by using + // WdfRequestForwardToParentDeviceIoQueue()in the DeviceIoControl callback. + // + WdfPdoInitAllowForwardingRequestToParent(_DeviceInit); + + // + // Create a framework device object to represent PDO of this bus driver. In response + // to this call, framework creates a WDM deviceobject. + // + Status = WdfDeviceCreate(&_DeviceInit, + &pdoAttributes, + &ChildDevice); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + + // + // Note: Once the device is created successfully, framework frees the + // _DeviceInit memory and sets the _DeviceInit to NULL. So don't + // call any WdfDeviceInit functions after that. + // + + // + // Initalize the PDO extension + // + PdoExtension = PdoGetExtension(ChildDevice); + + RtlZeroMemory(PdoExtension, sizeof(PDO_EXTENSION)); + + PdoExtension->FdoExtension = FdoGetExtension(_Device); + + PdoExtension->SerialNo = _SerialNo; + + + + // + // Set some properties for the child device. + // + WDF_DEVICE_PNP_CAPABILITIES_INIT(&PnpCaps); // Zeros this structure (note: WdfFalse is 0) + + // + // Bus driver sets this value to WdfFalse for this embedded device, which cannot + // be physically removed; its FDO must not set/override this. + // + PnpCaps.Removable = WdfFalse; + + // + // Bus driver sets this value to WdfTrue. FDO can override this value (when this irp is on its + // way up) if it determines that this device cannot be safely surprise (not orderly) removed + // without data loss. + // + PnpCaps.SurpriseRemovalOK = WdfTrue; + + pnpCaps.Address = _SerialNo; + pnpCaps.UINumber = _SerialNo; + + WdfDeviceSetPnpCapabilities(ChildDevice, &pnpCaps); + + WDF_DEVICE_POWER_CAPABILITIES_INIT(&powerCaps); + + powerCaps.DeviceD1 = WdfFalse; + powerCaps.DeviceD2 = WdfTrue; + + powerCaps.WakeFromD0 = WdfFalse; + powerCaps.WakeFromD1 = WdfFalse; + powerCaps.WakeFromD2 = WdfTrue; + powerCaps.WakeFromD3 = WdfTrue; + + powerCaps.DeviceWake = PowerDeviceD2; + + powerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0; + powerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD2; + powerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD2; + powerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD2; + powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3; + powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3; + + WdfDeviceSetPowerCapabilities(ChildDevice, &powerCaps); + + + // + // Configure a default queue so that requests that are not + // configure-forwarded using WdfDeviceConfigureRequestDispatching to goto + // other queues get dispatched here. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, WdfIoQueueDispatchParallel); + + // + // Cannot be power managed queue (dispatch only at D0) as + // BthMini issues BthX DDI to get version and capabilities + // before enter D0. A deadlock occurs if this is power managed. + // + QueueConfig.PowerManaged = WdfFalse; + + QueueConfig.EvtIoDeviceControl = PdoIoQuDeviceControl; + + Status = WdfIoQueueCreate(ChildDevice, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &Queue); + DoTrace(LEVEL_INFO, TFLAG_PNP, (" WdfIoQueueCreate (%!STATUS!)", Status)); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + +Cleanup: + + + // + // Call WdfDeviceInitFree if you encounter an error before the + // device is created. Once the device is created, framework + // NULLs the DeviceInit value. + // + if (!NT_SUCCESS(Status)) { + + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" -PdoCreateDynamic: exit %!STATUS!", Status)); + + if(ChildDevice) { + WdfObjectDelete(ChildDevice); + } + } + else + { + DoTrace(LEVEL_INFO, TFLAG_PNP, (" -PdoCreateDynamic: exit %!STATUS!", Status)); + } + + + return Status; +} + +#endif + + + +NTSTATUS +PdoCreate( + _In_ WDFDEVICE _Device, + _In_ PWSTR _HardwareIds, + _In_ ULONG _SerialNo +) +/*++ + +Routine Description: + + This routine creates and initialize a PDO to service a Bluetooth function. + +Arguments: + + _Device - A framework device object + + _HardwareIds - a hardware ID for this device + + -SerialNo - serial number of the child DO + +Return Value: + + NT Status code. + +--*/ +{ + NTSTATUS Status; + PWDFDEVICE_INIT DeviceInit = NULL; + WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks; + PPDO_EXTENSION PdoExtension = NULL; + WDFDEVICE ChildDevice = NULL; + WDF_OBJECT_ATTRIBUTES Attributes; + WDF_DEVICE_PNP_CAPABILITIES PnpCaps; + WDF_DEVICE_POWER_CAPABILITIES PowerCaps; + UNICODE_STRING StaticString = {0}; + UNICODE_STRING DeviceId; + DECLARE_UNICODE_STRING_SIZE(Buffer, MAX_ID_LEN); + UNICODE_STRING ContainerID = {0}; + WDF_PDO_EVENT_CALLBACKS Callbacks; + WDF_IO_QUEUE_CONFIG QueueConfig; + WDFQUEUE Queue; + + DoTrace(LEVEL_INFO, TFLAG_PNP, (" +PdoCreate: HWID(%S), compatID(%S)", _HardwareIds, BT_PDO_COMPATIBLE_IDS)); + + PAGED_CODE(); + + // + // Allocate a WDFDEVICE_INIT structure and set the properties + // so that we can create a device object for the child. + // + DeviceInit = WdfPdoInitAllocate(_Device); + if (DeviceInit == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + // + // Set DeviceType + // + WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER); + + // + // Provide DeviceID, HardwareIDs, CompatibleIDs and InstanceId + // + RtlInitUnicodeString(&DeviceId, _HardwareIds); + Status = WdfPdoInitAssignDeviceID(DeviceInit, &DeviceId); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // Note: same string is used to initialize hardware id + // + Status = WdfPdoInitAddHardwareID(DeviceInit, &DeviceId); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + RtlInitUnicodeString(&StaticString, BT_PDO_COMPATIBLE_IDS); + Status = WdfPdoInitAddCompatibleID(DeviceInit, &StaticString); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + Status = RtlUnicodeStringPrintf(&Buffer, L"%02d", _SerialNo); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + Status = WdfPdoInitAssignInstanceID(DeviceInit, &Buffer); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // Assign the containerID for an internally connected device + // + Status = RtlStringFromGUID(&GUID_CONTAINERID_INTERNALLY_CONNECTED_DEVICE, &ContainerID); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_PNP, ("Failed to generate the ContainerID, %!STATUS!", Status));; + goto Cleanup; + } + + Status = WdfPdoInitAssignContainerID(DeviceInit, &ContainerID); + if (!NT_SUCCESS(Status)) { + DoTrace(LEVEL_ERROR, TFLAG_PNP, ("Failed to assign the ContainerID, %!STATUS!", Status)); + goto Cleanup; + } + + // + // Provide a description about the device. This text is usually read from + // the device. This text is displayed momentarily by the PnP manager while + // it's looking for a matching INF. If it finds one, it uses the Device + // Description from the INF file or the friendly name created by + // coinstallers to display in the device manager. FriendlyName takes + // precedence over the DeviceDesc from the INF file. + // + Status = RtlUnicodeStringPrintf(&Buffer, L"SerialHciBus_%02d", _SerialNo ); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // You can call WdfPdoInitAddDeviceText multiple times, adding device + // text for multiple locales. When the system displays the text, it + // chooses the text that matches the current locale, if available. + // Otherwise it will use the string for the default locale. + // The driver can specify the driver's default locale by calling + // WdfPdoInitSetDefaultLocale. + // + RtlInitUnicodeString(&StaticString, BT_PDO_DEVICE_LOCATION); + Status = WdfPdoInitAddDeviceText(DeviceInit, + &Buffer, + &StaticString, + 0x409); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + WdfPdoInitSetDefaultLocale(DeviceInit, 0x409); + + // + // Initialize the attributes to specify the size of PDO device extension. + // All the state information private to the PDO will be tracked here. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, PDO_EXTENSION); + + // + // Set power callbacks to handle idle/active transition of the Bluetooth function + // + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks); + + // + // Register PnP callback + // + PnpPowerCallbacks.EvtDevicePrepareHardware = PdoDevPrepareHardware; + PnpPowerCallbacks.EvtDeviceReleaseHardware = PdoDevReleaseHardware; + + // + // Register for Power callback + // + PnpPowerCallbacks.EvtDeviceD0Entry = PdoDevD0Entry; + PnpPowerCallbacks.EvtDeviceD0Exit = PdoDevD0Exit; + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, + &PnpPowerCallbacks); + + // + // Allow to forward requests to its FDO of this bus driver by using + // WdfRequestForwardToParentDeviceIoQueue()in the DeviceIoControl callback. + // + WdfPdoInitAllowForwardingRequestToParent(DeviceInit); + + // + // Register to handle bus level power management (arm for wake?) + // + WDF_PDO_EVENT_CALLBACKS_INIT(&Callbacks); + + // + // Arm the device for wake: + // + // When the device is powered down, the framework calls the bus driver's + // EvtDeviceEnableWakeAtBus callback function at the beginning of the shutdown + // sequence, while the child device is still in the D0 state. In this callback + // function, the bus driver must do whatever is required at the bus level to + // enable the wake signal. + // + Callbacks.EvtDeviceEnableWakeAtBus = PdoDevEnableWakeAtBus; + + // + // Disarm the device for wake: + // + // If the child device triggered a wake signal, the system and the framework + // return the device to D0. The framework calls the bus driver's + // EvtDeviceDisableWakeAtBus callback function during startup of the child + // device. In this callback function, the bus driver should do whatever is + // required at the bus level to disable the wake signal, so that the device + // can no longer trigger it. Thus, EvtDeviceDisableWakeAtBus reverses the + // actions of EvtDeviceEnableWakeAtBus. + // + Callbacks.EvtDeviceDisableWakeAtBus = PdoDevDisableWakeAtBus; + + WdfPdoInitSetEventCallbacks(DeviceInit, &Callbacks); + + // + // Create a framework device object to represent PDO of this bus driver. In response + // to this call, framework creates a WDM deviceobject. + // + Status = WdfDeviceCreate(&DeviceInit, + &Attributes, + &ChildDevice); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // Note: Once the device is created successfully, framework frees the + // DeviceInit memory and sets the DeviceInit to NULL. So don't + // call any WdfDeviceInit functions after that. + // + + // + // Initalize the PDO extension + // + PdoExtension = PdoGetExtension(ChildDevice); + + RtlZeroMemory(PdoExtension, sizeof(PDO_EXTENSION)); + + PdoExtension->FdoExtension = FdoGetExtension(_Device); + + PdoExtension->SerialNo = _SerialNo; + + + // + // Set PnP and Power capabilities for this child device. + // + WDF_DEVICE_PNP_CAPABILITIES_INIT(&PnpCaps); // Zeros this structure (note: WdfFalse is 0) + + // + // Bus driver sets this value to WdfFalse for this embedded device, which cannot + // be physically removed; its FDO must not set/override this. + // + PnpCaps.Removable = WdfFalse; + + // + // Bus driver sets this value to WdfTrue. FDO can override this value (when this irp is on its + // way up) if it determines that this device cannot be safely surprise (not orderly) removed + // without data loss. + // + PnpCaps.SurpriseRemovalOK = WdfTrue; + + PnpCaps.Address = _SerialNo; + PnpCaps.UINumber = _SerialNo; + + WdfDeviceSetPnpCapabilities(ChildDevice, &PnpCaps); + + WDF_DEVICE_POWER_CAPABILITIES_INIT(&PowerCaps); + + PowerCaps.DeviceD1 = WdfFalse; + PowerCaps.DeviceD2 = WdfTrue; + + PowerCaps.WakeFromD0 = WdfFalse; + PowerCaps.WakeFromD1 = WdfFalse; + PowerCaps.WakeFromD2 = WdfTrue; + PowerCaps.WakeFromD3 = WdfTrue; + + PowerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0; + PowerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD2; + PowerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD2; + PowerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD2; + PowerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3; + PowerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3; + + PowerCaps.DeviceWake = PowerDeviceD2; // Lowest-powered Dx state to send wake signal to system + + WdfDeviceSetPowerCapabilities(ChildDevice, &PowerCaps); + + // + // Configure a default queue so that requests that are not + // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto + // other queues get dispatched here. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, WdfIoQueueDispatchParallel); + + // + // Cannot be power managed queue (dispatch only at D0) as + // BthMini issues BthX DDI to get version and capabilities + // before enter D0. A deadlock occurs if this is power managed. + // + QueueConfig.PowerManaged = WdfFalse; + + QueueConfig.EvtIoDeviceControl = PdoIoQuDeviceControl; + + Status = WdfIoQueueCreate(ChildDevice, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &Queue); + DoTrace(LEVEL_INFO, TFLAG_PNP, (" WdfIoQueueCreate (%!STATUS!)", Status)); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // Add this device to the FDO's collection of children. + // After the child device is added to the static collection successfully, + // driver must call WdfPdoMarkMissing to get the device deleted. It + // shouldn't delete the child device directly by calling WdfObjectDelete. + // + Status = WdfFdoAddStaticChild(_Device, ChildDevice); + DoTrace(LEVEL_INFO, TFLAG_PNP, (" WdfFdoAddStaticChild (%!STATUS!)", Status)); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + +Cleanup: + + // + // Call WdfDeviceInitFree if you encounter an error before the + // device is created. Once the device is created, framework + // NULLs the DeviceInit value. + // + if (!NT_SUCCESS(Status)) { + + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" -PdoCreate: exit %!STATUS!", Status)); + + if (DeviceInit != NULL) { + WdfDeviceInitFree(DeviceInit); + } + + if(ChildDevice) { + WdfObjectDelete(ChildDevice); + } + } + else + { + DoTrace(LEVEL_INFO, TFLAG_PNP, (" -PdoCreate: exit %!STATUS!", Status)); + } + + if (NULL != ContainerID.Buffer) { + RtlFreeUnicodeString(&ContainerID); + } + + return Status; +} + +NTSTATUS +PdoDevPrepareHardware( + _In_ WDFDEVICE _Device, + _In_ WDFCMRESLIST _ResourcesRaw, + _In_ WDFCMRESLIST _ResourcesTranslated + ) +/*++ +Routine Description: + + This PnP CB function take a refernce of its parent so it will not enter DxState while in S0Idle. + +Arguments: + + _Device - WDF Device object + + _ResourcesRaw - (Not referenced) + + _ResourcesTranslated - (Not referenced) + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + WDFDEVICE ParentDevice; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_ResourcesRaw); + UNREFERENCED_PARAMETER(_ResourcesTranslated); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+PdoDevPrepareHardware")); + + ParentDevice = WdfPdoGetParent(_Device); + + // + // Take a reference to avoid FDO to enter DxState in IdleS0 + // + Status = WdfDeviceStopIdle(ParentDevice, FALSE); + DoTrace(LEVEL_INFO, TFLAG_PNP, ("WdfDeviceStopIdle %!STATUS!", Status)); + + // + // Any failure Status code should be invesigated to ensure that the reference count is balanced. + // + NT_ASSERT(NT_SUCCESS(Status)); + + DoTrace(LEVEL_INFO, TFLAG_PNP, ("-PdoDevPrepareHardware")); + + return Status; +} + + +NTSTATUS +PdoDevReleaseHardware( + _In_ WDFDEVICE _Device, + _In_ WDFCMRESLIST _ResourcesTranslated + ) +/*++ +Routine Description: + + This PnP CB function release a refcount of its parent so it can enter DxState in S0Idle. + +Arguments: + + _Device - WDF Device object + + _ResourcesTranslated - (Not referenced) + +Return Value: + + NTSTATUS + +--*/ +{ + WDFDEVICE ParentDevice; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_ResourcesTranslated); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+PdoDevReleaseHardware")); + + ParentDevice = WdfPdoGetParent(_Device); + + // + // Release a reference to allow FDO to enter DxState in IdleS0 + // + WdfDeviceResumeIdle(ParentDevice); + + return STATUS_SUCCESS; +} + + + +NTSTATUS +PdoDevD0Entry( + _In_ WDFDEVICE _Device, + _In_ WDF_POWER_DEVICE_STATE _PreviousState + ) +/*++ +Routine Description: + + This PnPPower CB function is invoked after device has entered D0 (working) state. + +Arguments: + + _Device - WDF Device object + + PreviousState - Previous device power state + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_Device); + UNREFERENCED_PARAMETER(_PreviousState); + + DoTrace(LEVEL_INFO, TFLAG_UART, ("+PdoDevD0Entry")); + + // + // Can bring the Bluetooth function back to active state + // + + + DoTrace(LEVEL_INFO, TFLAG_UART, ("-PdoDevD0Entry %!STATUS!", Status)); + + return Status; +} + + +NTSTATUS +PdoDevD0Exit( + _In_ WDFDEVICE _Device, + _In_ WDF_POWER_DEVICE_STATE _TargetState + ) +/*++ +Routine Description: + + This PnP CB function is invoked when device has exited D0 (working) state. + +Arguments: + + _Device - WDF Device object + + _TargetState - Next device power state that it is about to enter + +Return Value: + + NTSTATUS + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_Device); + UNREFERENCED_PARAMETER(_TargetState); + + DoTrace(LEVEL_INFO, TFLAG_UART, ("+PdoDevD0Exit: D0 -> D%d", _TargetState-WdfPowerDeviceD0)); + + // + // Can prepare the Bluetooth function to enter lower power device state + // + + + DoTrace(LEVEL_INFO, TFLAG_UART, ("-PdoDevD0Exit")); + + return STATUS_SUCCESS; +} + + +VOID +PdoDevDisableWakeAtBus( + _In_ WDFDEVICE _Device + ) +/*++ + +Routine Description: + + This framework callback routine performs bus-level operations that disable + the ability of one of the bus's devices to trigger a wake-up signal. + +Arguments: + + _Device - Framework device object + +Return Value: + + VOID + +--*/ +{ + // + // Do not mark this function pageable to potentially reduce power up time. + // + + DoTrace(LEVEL_INFO, TFLAG_POWER,("<==(D)== PdoDevDisableWakeAtBus")); + + // + // Device specific implementation to disarm for wake + // + DeviceDisableWakeControl(_Device); +} + +NTSTATUS +PdoDevEnableWakeAtBus( + _In_ WDFDEVICE _Device, + _In_ SYSTEM_POWER_STATE _PowerState + ) +/*++ + +Routine Description: + + This framework callback routine performs bus-level operations that enable + one of the bus's devices to trigger a wake-up signal. + +Arguments: + + _Device - Framework device object + + _PowerState - identifies the system power state that the system or device will wake from. + +Return Value: + + NTSTATUS + +--*/ +{ + // + // Do not mark this function pageable to potentially reduce power up time. + // + + DoTrace(LEVEL_INFO, TFLAG_POWER,("==(E)==> PdoDevEnableWakeAtBus from %S", + _PowerState == PowerSystemWorking ? L"S0" : L"Sx")); + + // + // Device specific implementation to arm for wake + // + return DeviceEnableWakeControl(_Device, _PowerState); +} + +VOID +PdoIoQuDeviceControl( + _In_ WDFQUEUE _Queue, + _In_ WDFREQUEST _Request, + _In_ size_t _OutputBufferLength, + _In_ size_t _InputBufferLength, + _In_ ULONG _IoControlCode + ) +/*++ + +Routine Description: + + This routine is the dispatch routine for device control requests. This routine can be invoke + at the DISPATCH level from BthPort/mini. + +Arguments: + + _Queue - Handle to the framework queue object that is associated + with the I/O request. + _Request - Handle to a framework request object. + + _OutputBufferLength - length of the request's output buffer, + if an output buffer is available. + _InputBufferLength - length of the request's input buffer, + if an input buffer is available. + + _IoControlCode - the driver-defined or system-defined I/O control code + (IOCTL) that is associated with the request. + +Return Value: + + VOID + +--*/ +{ + WDFDEVICE Device = NULL; + NTSTATUS Status = STATUS_INVALID_PARAMETER; + WDF_REQUEST_FORWARD_OPTIONS ForwardOptions; + WDFDEVICE ParentDevice; + ULONG ControlCode = (_IoControlCode & 0x00003ffc) >> 2; + + UNREFERENCED_PARAMETER(_OutputBufferLength); + UNREFERENCED_PARAMETER(_InputBufferLength); + + DoTrace(LEVEL_INFO, TFLAG_IOCTL,("+IoDeviceControl - InBufLen:%d, OutBufLen:%d", + (ULONG) _InputBufferLength, (ULONG) _OutputBufferLength)); + + switch (_IoControlCode) { + case IOCTL_BTHX_GET_VERSION: + case IOCTL_BTHX_SET_VERSION: + case IOCTL_BTHX_QUERY_CAPABILITIES: + case IOCTL_BTHX_WRITE_HCI: + case IOCTL_BTHX_READ_HCI: + Device = WdfIoQueueGetDevice(_Queue); + WDF_REQUEST_FORWARD_OPTIONS_INIT(&ForwardOptions); + ForwardOptions.Flags = WDF_REQUEST_FORWARD_OPTION_SEND_AND_FORGET; + ParentDevice = WdfPdoGetParent(Device); + + // + // Forward known IOCTLs to FDO to process + // + Status = WdfRequestForwardToParentDeviceIoQueue(_Request, + WdfDeviceGetDefaultQueue(ParentDevice), + &ForwardOptions); + break; + + default: + // + // Complete this unexptected IOCTL with default STATUS_INVALID_PARAMETER. + // + DoTrace(LEVEL_ERROR, TFLAG_IOCTL,("Unexpected IOCTL_(0x%x, Func %d)", _IoControlCode, ControlCode)); + break; + } + + if (!NT_SUCCESS(Status)){ + DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_(0x%x, Func %d) failed %!STATUS!", _IoControlCode, ControlCode, Status)); + WdfRequestComplete(_Request, Status); + return; + } + + return; +} + + diff --git a/bluetooth/serialhcibus/public.h b/bluetooth/serialhcibus/public.h new file mode 100644 index 00000000..5ef495d0 --- /dev/null +++ b/bluetooth/serialhcibus/public.h @@ -0,0 +1,47 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + public.h + +Abstract: + + This module contains the common declarations shared by driver + and user applications. + +Environment: + + user and kernel + +--*/ + +#ifndef __PUBLIC_H +#define __PUBLIC_H + +#ifdef DEFINE_GUID + +// +// Vendor: Define a device interface GUID for Bluetooth Radion On/off. +// Need to use uuidgen to create your own GUID instead of reusing this one. +// +DEFINE_GUID(GUID_DEVINTERFACE_BLUETOOTH_RADIO_ONOFF_VENDOR_SPECIFIC, + 0x98899865, 0x63de, 0x427b, 0x84, 0x77, 0x6c, 0xb7, 0x4d, 0x9f, 0xf2, 0xa7); +//{98899865-63de-427b-8477-6cb74d9ff2a7} + +#endif // #ifdef DEFINE_GUID + + +// +// IOCTL definitions to support Radio on/off +// +#define FILE_DEVICE_BUSENUM FILE_DEVICE_BUS_EXTENDER +#define BUSENUM_IOCTL(id, access) CTL_CODE(FILE_DEVICE_BUSENUM, \ + (id), \ + METHOD_BUFFERED, \ + access) + +#define IOCTL_BUSENUM_SET_RADIO_ONOFF_VENDOR_SPECFIC BUSENUM_IOCTL(0x1, FILE_WRITE_DATA) + +#endif diff --git a/bluetooth/serialhcibus/serialhcibus.sln b/bluetooth/serialhcibus/serialhcibus.sln new file mode 100644 index 00000000..06bf3320 --- /dev/null +++ b/bluetooth/serialhcibus/serialhcibus.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SerialBusWdk", "WDK\SerialBusWdk.vcxproj", "{B126E3A5-91B5-41B5-9B40-719D474AA513}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B126E3A5-91B5-41B5-9B40-719D474AA513}.Debug|Win32.ActiveCfg = Debug|Win32 + {B126E3A5-91B5-41B5-9B40-719D474AA513}.Debug|Win32.Build.0 = Debug|Win32 + {B126E3A5-91B5-41B5-9B40-719D474AA513}.Release|Win32.ActiveCfg = Release|Win32 + {B126E3A5-91B5-41B5-9B40-719D474AA513}.Release|Win32.Build.0 = Release|Win32 + {B126E3A5-91B5-41B5-9B40-719D474AA513}.Debug|x64.ActiveCfg = Debug|x64 + {B126E3A5-91B5-41B5-9B40-719D474AA513}.Debug|x64.Build.0 = Debug|x64 + {B126E3A5-91B5-41B5-9B40-719D474AA513}.Release|x64.ActiveCfg = Release|x64 + {B126E3A5-91B5-41B5-9B40-719D474AA513}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/cdfs/ReadMe.md b/filesys/cdfs/ReadMe.md new file mode 100644 index 00000000..de052d82 --- /dev/null +++ b/filesys/cdfs/ReadMe.md @@ -0,0 +1,10 @@ +CDFS File System Driver +======================= + +The CD-ROM file system driver (cdfs) sample is a sample file system driver that you can use to write new file systems. + +Cdfs is a read-only file system that addresses various issues such as accessing data on disk, interacting with the cache manager, and handling various I/O operations such as opening files, performing reads on a file, retrieving information on a file, and performing various control operations on the file system. The Cdfs file system is included with the Microsoft Windows operating system. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + diff --git a/filesys/cdfs/allocsup.c b/filesys/cdfs/allocsup.c new file mode 100644 index 00000000..6a92817c --- /dev/null +++ b/filesys/cdfs/allocsup.c @@ -0,0 +1,933 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + AllocSup.c + +Abstract: + + This module implements the Allocation support routines for Cdfs. + + The data structure used here is the CD_MCB. There is an entry in + the Mcb for each dirent for a file. The entry will map the offset + within some file to a starting disk offset and number of bytes. + The Mcb also contains the interleave information for an extent. + An interleave consists of a number of blocks with data and a + (possibly different) number of blocks to skip. Any number of + data/skip pairs may exist in an extent but the data and skip sizes + are the same throughout the extent. + + We store the following information into an Mcb entry for an extent. + + FileOffset Offset in file for start of extent + DiskOffset Offset on disk for start of extent + ByteCount Number of file bytes in extent, no skip bytes + DataBlockByteCount Number of bytes in each data block + TotalBlockByteCount Number of bytes is data block and skip block + + The disk offset in the Mcb has already been biased by the size of + the Xar block if present. All of the byte count fields are aligned + on logical block boundaries. If this is a directory or path table + then the file offset has been biased to round the initial disk + offset down to a sector boundary. The biasing is done when loading + the values into an Mcb entry. + + An XA file has a header prepended to the file and each sector is 2352 + bytes. The allocation information ignores the header and only deals + with 2048 byte sectors. Callers into the allocation package have + adjusted the starting offset value to reflect 2048 sectors. On return + from this package the caller will have to convert from 2048 sector values + into raw XA sector values. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_ALLOCSUP) + +// +// Local support routines +// + +ULONG +CdFindMcbEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG FileOffset + ); + +VOID +CdDiskOffsetFromMcbEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PCD_MCB_ENTRY McbEntry, + _In_ LONGLONG FileOffset, + _Out_ PLONGLONG DiskOffset, + _Out_ PULONG ByteCount + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdAddInitialAllocation) +#pragma alloc_text(PAGE, CdAddAllocationFromDirent) +#pragma alloc_text(PAGE, CdDiskOffsetFromMcbEntry) +#pragma alloc_text(PAGE, CdFindMcbEntry) +#pragma alloc_text(PAGE, CdInitializeMcb) +#pragma alloc_text(PAGE, CdLookupAllocation) +#pragma alloc_text(PAGE, CdTruncateAllocation) +#pragma alloc_text(PAGE, CdUninitializeMcb) +#endif + + +_Requires_lock_held_(_Global_critical_region_) +VOID +// PREFast currently has no way to express the Fcb==Fcb->Vcb->VolumeDasdFcb early return +#pragma warning(suppress: 6001 6101) +CdLookupAllocation ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG FileOffset, + _Out_ PLONGLONG DiskOffset, + _Out_ PULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine looks through the mapping information for the file + to find the logical diskoffset and number of bytes at that offset. + We only deal with logical 2048 byte sectors here. + + If the mapping isn't present we will look it up on disk now. + This routine assumes we are looking up a valid range in the file. This + routine raises if it can't find mapping for the file offset. + + The Fcb may not be locked prior to calling this routine. We will always + acquire it here. + +Arguments: + + Fcb - Fcb representing this stream. + + FileOffset - Lookup the allocation beginning at this point. + + DiskOffset - Address to store the logical disk offset. + + ByteCount - Address to store the number of contiguous bytes beginning + at DiskOffset above. + +Return Value: + + None. + +--*/ + +{ + BOOLEAN FirstPass = TRUE; + ULONG McbEntryOffset; + PFCB ParentFcb = NULL; + BOOLEAN CleanupParent = FALSE; + + BOOLEAN UnlockFcb = FALSE; + + LONGLONG CurrentFileOffset; + ULONG CurrentMcbOffset; + PCD_MCB_ENTRY CurrentMcbEntry; + + DIRENT_ENUM_CONTEXT DirContext = {0}; + DIRENT Dirent = {0}; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // For DASD IO we already have clamped the read to the volume limits. + // We'll allow reading beyond those limits for extended DASD IO, so + // no MCB lookup here. + // + + if (Fcb == Fcb->Vcb->VolumeDasdFcb) { + + *DiskOffset = FileOffset; + return; + } + + // + // Use a try finally to facilitate cleanup. + // + + try { + + // + // We use a loop to perform the lookup. If we don't find the mapping in the + // first pass then we look up all of the allocation and then look again. + + while (TRUE) { + + // + // + // Lookup the entry containing this file offset. + // + + CdLockFcb( IrpContext, Fcb ); + UnlockFcb = TRUE; + + McbEntryOffset = CdFindMcbEntry( IrpContext, Fcb, FileOffset ); + + // + // If within the Mcb then we use the data out of this entry and are + // done. + // + + if (McbEntryOffset < Fcb->Mcb.CurrentEntryCount) { + + CdDiskOffsetFromMcbEntry( IrpContext, + Fcb->Mcb.McbArray + McbEntryOffset, + FileOffset, + DiskOffset, + ByteCount ); + + break; + + // + // If this is not the first pass then the disk is corrupt. + // + + } else if (!FirstPass) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + CdUnlockFcb( IrpContext, Fcb ); + UnlockFcb = FALSE; + + // + // Initialize the search dirent structures. + // + + CdInitializeDirContext( IrpContext, &DirContext ); + CdInitializeDirent( IrpContext, &Dirent ); + + // + // Otherwise we need to walk the dirents for this file until we find + // the one containing this entry. The parent Fcb should always be + // present. + // + + ParentFcb = Fcb->ParentFcb; + CdAcquireFileShared( IrpContext, ParentFcb ); + CleanupParent = TRUE; + + // + // Do an unsafe test to see if we need to create a file object. + // + + CdVerifyOrCreateDirStreamFile( IrpContext, ParentFcb); + + // + // Initialize the local variables to indicate the first dirent + // and lookup the first dirent. + // + + CurrentFileOffset = 0; + CurrentMcbOffset = 0; + + CdLookupDirent( IrpContext, + ParentFcb, + CdQueryFidDirentOffset( Fcb->FileId ), + &DirContext ); + + // + // If we are adding allocation to the Mcb then add all of it. + // + + while (TRUE ) { + + // + // Update the dirent from the on-disk dirent. + // + + CdUpdateDirentFromRawDirent( IrpContext, ParentFcb, &DirContext, &Dirent ); + + // + // Add this dirent to the Mcb if not already present. + // + + CdLockFcb( IrpContext, Fcb ); + UnlockFcb = TRUE; + + if (CurrentMcbOffset >= Fcb->Mcb.CurrentEntryCount) { + + CdAddAllocationFromDirent( IrpContext, Fcb, CurrentMcbOffset, CurrentFileOffset, &Dirent ); + } + + CdUnlockFcb( IrpContext, Fcb ); + UnlockFcb = FALSE; + + // + // If this is the last dirent for the file then exit. + // + + if (!FlagOn( Dirent.DirentFlags, CD_ATTRIBUTE_MULTI )) { + + break; + } + + // + // If we couldn't find another entry then the directory is corrupt because + // the last dirent for a file doesn't exist. + // + + if (!CdLookupNextDirent( IrpContext, ParentFcb, &DirContext, &DirContext )) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + // + // Update our loop variables. + // + + CurrentMcbEntry = Fcb->Mcb.McbArray + CurrentMcbOffset; + CurrentFileOffset += CurrentMcbEntry->ByteCount; + CurrentMcbOffset += 1; + } + + // + // All of the allocation is loaded. Go back and look up the mapping again. + // It better be there this time. + // + + FirstPass = FALSE; + } + + } finally { + + if (CleanupParent) { + + // + // Release the parent and cleanup the dirent structures. + // + + CdReleaseFile( IrpContext, ParentFcb ); + + CdCleanupDirContext( IrpContext, &DirContext ); + CdCleanupDirent( IrpContext, &Dirent ); + } + + if (UnlockFcb) { CdUnlockFcb( IrpContext, Fcb ); } + } + + return; +} + + +VOID +CdAddAllocationFromDirent ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ ULONG McbEntryOffset, + _In_ LONGLONG StartingFileOffset, + _In_ PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine is called to add an entry into the Cd Mcb. We grow the Mcb + as necessary and update the new entry. + + NOTE - The Fcb has already been locked prior to makeing this call. + +Arguments: + + Fcb - Fcb containing the Mcb to update. + + McbEntryOffset - Offset into the Mcb array to add this data. + + StartingFileOffset - Offset in bytes from the start of the file. + + Dirent - Dirent containing the on-disk data for this entry. + +Return Value: + + None + +--*/ + +{ + ULONG NewArraySize; + PVOID NewMcbArray; + PCD_MCB_ENTRY McbEntry; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + ASSERT_LOCKED_FCB( Fcb ); + + // + // If we need to grow the Mcb then do it now. + // + + if (McbEntryOffset >= Fcb->Mcb.MaximumEntryCount) { + + // + // Allocate a new buffer and copy the old data over. + // + + NewArraySize = Fcb->Mcb.MaximumEntryCount * 2 * sizeof( CD_MCB_ENTRY ); + + NewMcbArray = FsRtlAllocatePoolWithTag( CdPagedPool, + NewArraySize, + TAG_MCB_ARRAY ); + + RtlZeroMemory( NewMcbArray, NewArraySize ); + RtlCopyMemory( NewMcbArray, + Fcb->Mcb.McbArray, + Fcb->Mcb.MaximumEntryCount * sizeof( CD_MCB_ENTRY )); + + // + // Deallocate the current array unless it is embedded in the Fcb. + // + + if (Fcb->Mcb.MaximumEntryCount != 1) { + + CdFreePool( &Fcb->Mcb.McbArray ); + } + + // + // Now update the Mcb with the new array. + // + + Fcb->Mcb.MaximumEntryCount *= 2; + Fcb->Mcb.McbArray = NewMcbArray; + } + + // + // Update the new entry with the input data. + // + + McbEntry = Fcb->Mcb.McbArray + McbEntryOffset; + + // + // Start with the location and length on disk. + // + + McbEntry->DiskOffset = LlBytesFromBlocks( Fcb->Vcb, Dirent->StartingOffset ); + McbEntry->ByteCount = Dirent->DataLength; + + // + // Round the byte count up to a logical block boundary if this is + // the last extent. + // + + if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_MULTI )) { + + McbEntry->ByteCount = BlockAlign( Fcb->Vcb, McbEntry->ByteCount ); + } + + // + // The file offset is the logical position within this file. + // We know this is correct regardless of whether we bias the + // file size or disk offset. + // + + McbEntry->FileOffset = StartingFileOffset; + + // + // Convert the interleave information from logical blocks to + // bytes. + // + + if (Dirent->FileUnitSize != 0) { + + McbEntry->DataBlockByteCount = LlBytesFromBlocks( Fcb->Vcb, Dirent->FileUnitSize ); + McbEntry->TotalBlockByteCount = McbEntry->DataBlockByteCount + + LlBytesFromBlocks( Fcb->Vcb, Dirent->InterleaveGapSize ); + + // + // If the file is not interleaved then the size of the data block + // and total block are the same as the byte count. + // + + } else { + + McbEntry->DataBlockByteCount = + McbEntry->TotalBlockByteCount = McbEntry->ByteCount; + } + + // + // Update the number of entries in the Mcb. The Mcb is never sparse + // so whenever we add an entry it becomes the last entry in the Mcb. + // + + Fcb->Mcb.CurrentEntryCount = McbEntryOffset + 1; + + return; +} + + +VOID +CdAddInitialAllocation ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ ULONG StartingBlock, + _In_ LONGLONG DataLength + ) + +/*++ + +Routine Description: + + This routine is called to set up the initial entry in an Mcb. + + This routine handles the single initial entry for a directory file. We will + round the start block down to a sector boundary. Our caller has already + biased the DataLength with any adjustments. This is used for the case + where there is a single entry and we want to align the data on a sector + boundary. + +Arguments: + + Fcb - Fcb containing the Mcb to update. + + StartingBlock - Starting logical block for this directory. This is + the start of the actual data. We will bias this by the sector + offset of the data. + + DataLength - Length of the data. + +Return Value: + + None + +--*/ + +{ + PCD_MCB_ENTRY McbEntry; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + ASSERT_LOCKED_FCB( Fcb ); + NT_ASSERT( 0 == Fcb->Mcb.CurrentEntryCount); + NT_ASSERT( CDFS_NTC_FCB_DATA != Fcb->NodeTypeCode); + + // + // Update the new entry with the input data. + // + + McbEntry = Fcb->Mcb.McbArray; + + // + // Start with the location and length on disk. + // + + McbEntry->DiskOffset = LlBytesFromBlocks( Fcb->Vcb, StartingBlock ); + McbEntry->DiskOffset -= Fcb->StreamOffset; + + McbEntry->ByteCount = DataLength; + + // + // The file offset is the logical position within this file. + // We know this is correct regardless of whether we bias the + // file size or disk offset. + // + + McbEntry->FileOffset = 0; + + // + // If the file is not interleaved then the size of the data block + // and total block are the same as the byte count. + // + + McbEntry->DataBlockByteCount = + McbEntry->TotalBlockByteCount = McbEntry->ByteCount; + + // + // Update the number of entries in the Mcb. The Mcb is never sparse + // so whenever we add an entry it becomes the last entry in the Mcb. + // + + Fcb->Mcb.CurrentEntryCount = 1; + + return; +} + + +VOID +CdTruncateAllocation ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ LONGLONG StartingFileOffset + ) + +/*++ + +Routine Description: + + This routine truncates the Mcb for a file by eliminating all of the Mcb + entries from the entry which contains the given offset. + + The Fcb should be locked when this routine is called. + +Arguments: + + Fcb - Fcb containing the Mcb to truncate. + + StartingFileOffset - Offset in the file to truncate the Mcb from. + +Return Value: + + None + +--*/ + +{ + ULONG McbEntryOffset; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + ASSERT_LOCKED_FCB( Fcb ); + + // + // Find the entry containg this starting offset. + // + + McbEntryOffset = CdFindMcbEntry( IrpContext, Fcb, StartingFileOffset ); + + // + // Now set the current size of the mcb to this point. + // + + Fcb->Mcb.CurrentEntryCount = McbEntryOffset; + + return; +} + + +_At_(Fcb->NodeByteSize, _In_range_(>=, FIELD_OFFSET( FCB, FcbType ))) +VOID +CdInitializeMcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_updates_bytes_(Fcb->NodeByteSize) PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called to initialize the Mcb in an Fcb. We initialize + this with an entry count of one and point to the entry in the Fcb + itself. + + Fcb should be acquired exclusively when this is called. + +Arguments: + + Fcb - Fcb containing the Mcb to initialize. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // Set the entry counts to show there is one entry in the array and + // it is unused. + // + + Fcb->Mcb.MaximumEntryCount = 1; + Fcb->Mcb.CurrentEntryCount = 0; + + Fcb->Mcb.McbArray = &Fcb->McbEntry; + + return; +} + + +_At_(Fcb->NodeByteSize, _In_range_(>=, FIELD_OFFSET( FCB, FcbType ))) +_When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_PATH_TABLE, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_INDEX))) +_When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_INDEX, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_INDEX))) +_When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_DATA, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_DATA))) +VOID +CdUninitializeMcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_updates_bytes_(Fcb->NodeByteSize) PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called to cleanup an Mcb in an Fcb. We look at the + maximum run count in the Fcb and if greater than one we will deallocate + the buffer. + + Fcb should be acquired exclusively when this is called. + +Arguments: + + Fcb - Fcb containing the Mcb to uninitialize. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // If the count is greater than one then this is an allocated buffer. + // + + if (Fcb->Mcb.MaximumEntryCount > 1) { + + CdFreePool( &Fcb->Mcb.McbArray ); + } + + return; +} + + +// +// Local suupport routine +// + +ULONG +CdFindMcbEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG FileOffset + ) + +/*++ + +Routine Description: + + This routine is called to find the Mcb entry which contains the file + offset at the given point. If the file offset is not currently in the + Mcb then we return the offset of the entry to add. + + Fcb should be locked when this is called. + +Arguments: + + Fcb - Fcb containing the Mcb to uninitialize. + + FileOffset - Return the Mcb entry which contains this file offset. + +Return Value: + + ULONG - Offset in the Mcb of the entry for this offset. + +--*/ + +{ + ULONG CurrentMcbOffset; + PCD_MCB_ENTRY CurrentMcbEntry; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + ASSERT_LOCKED_FCB( Fcb ); + + // + // We expect a linear search will be sufficient here. + // + + CurrentMcbOffset = 0; + CurrentMcbEntry = Fcb->Mcb.McbArray; + + while (CurrentMcbOffset < Fcb->Mcb.CurrentEntryCount) { + + // + // Check if the offset lies within the current Mcb position. + // + + if (FileOffset < CurrentMcbEntry->FileOffset + CurrentMcbEntry->ByteCount) { + + break; + } + + // + // Move to the next entry. + // + + CurrentMcbOffset += 1; + CurrentMcbEntry += 1; + } + + // + // This is the offset containing this file offset (or the point + // where an entry should be added). + // + + return CurrentMcbOffset; +} + + +// +// Local support routine +// + +VOID +CdDiskOffsetFromMcbEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PCD_MCB_ENTRY McbEntry, + _In_ LONGLONG FileOffset, + _Out_ PLONGLONG DiskOffset, + _Out_ PULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine is called to return the diskoffset and length of the file + data which begins at offset 'FileOffset'. We have the Mcb entry which + contains the mapping and interleave information. + + NOTE - This routine deals with data in 2048 byte logical sectors. If + this is an XA file then our caller has already converted from + 'raw' file bytes to 'cooked' file bytes. + +Arguments: + + McbEntry - Entry in the Mcb containing the allocation information. + + FileOffset - Starting Offset in the file to find the matching disk + offsets. + + DiskOffset - Address to store the starting disk offset for this operation. + + ByteCount - Address to store number of contiguous bytes starting at this + disk offset. + +Return Value: + + None + +--*/ + +{ + LONGLONG ExtentOffset; + + LONGLONG CurrentDiskOffset; + LONGLONG CurrentExtentOffset; + + LONGLONG LocalByteCount; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + ASSERT_IRP_CONTEXT( IrpContext ); + + // + // Extent offset is the difference between the file offset and the start + // of the extent. + // + + ExtentOffset = FileOffset - McbEntry->FileOffset; + + // + // Optimize the non-interleave case. + // + + if (McbEntry->ByteCount == McbEntry->DataBlockByteCount) { + + *DiskOffset = McbEntry->DiskOffset + ExtentOffset; + + LocalByteCount = McbEntry->ByteCount - ExtentOffset; + + } else { + + // + // Walk though any interleave until we reach the current offset in + // this extent. + // + + CurrentExtentOffset = McbEntry->DataBlockByteCount; + CurrentDiskOffset = McbEntry->DiskOffset; + + while (CurrentExtentOffset <= ExtentOffset) { + + CurrentDiskOffset += McbEntry->TotalBlockByteCount; + CurrentExtentOffset += McbEntry->DataBlockByteCount; + } + + // + // We are now positioned at the data block containing the starting + // file offset we were given. The disk offset is the offset of + // the start of this block plus the extent offset into this block. + // The byte count is the data block byte count minus our offset into + // this block. + // + + *DiskOffset = CurrentDiskOffset + (ExtentOffset + McbEntry->DataBlockByteCount - CurrentExtentOffset); + + // + // Make sure we aren't past the end of the data length. This is possible + // if we only use part of the last data block on an interleaved file. + // + + if (CurrentExtentOffset > McbEntry->ByteCount) { + + CurrentExtentOffset = McbEntry->ByteCount; + } + + LocalByteCount = CurrentExtentOffset - ExtentOffset; + } + + // + // If the byte count exceeds our limit then cut it to fit in 32 bits. + // + + if (LocalByteCount > MAXULONG) { + + *ByteCount = MAXULONG; + + } else { + + *ByteCount = (ULONG) LocalByteCount; + } + + return; +} + diff --git a/filesys/cdfs/cachesup.c b/filesys/cdfs/cachesup.c new file mode 100644 index 00000000..eea1fd28 --- /dev/null +++ b/filesys/cdfs/cachesup.c @@ -0,0 +1,671 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + Cache.c + +Abstract: + + This module implements the cache management routines for the Cdfs + FSD and FSP, by calling the Common Cache Manager. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CACHESUP) + +// +// Local debug trace level +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCompleteMdl) +#pragma alloc_text(PAGE, CdCreateInternalStream) +#pragma alloc_text(PAGE, CdDeleteInternalStream) +#pragma alloc_text(PAGE, CdPurgeVolume) +#endif + + +VOID +CdCreateInternalStream ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Inout_ PFCB Fcb, + _In_ PUNICODE_STRING Name + ) + +/*++ + +Routine Description: + + This function creates an internal stream file for interaction + with the cache manager. The Fcb here can be for either a + directory stream or for a path table stream. + +Arguments: + + Vcb - Vcb for this volume. + + Fcb - Points to the Fcb for this file. It is either an Index or + Path Table Fcb. + +Return Value: + + None. + +--*/ + +{ + PFILE_OBJECT StreamFile = NULL; + BOOLEAN DecrementReference = FALSE; + + BOOLEAN CleanupDirContext = FALSE; + BOOLEAN UpdateFcbSizes = FALSE; + + DIRENT Dirent = {0}; + DIRENT_ENUM_CONTEXT DirContext = {0}; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // We may only have the Fcb shared. Lock the Fcb and do a + // safe test to see if we need to really create the file object. + // + + CdLockFcb( IrpContext, Fcb ); + + if (Fcb->FileObject != NULL) { + + CdUnlockFcb( IrpContext, Fcb ); + return; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Create the internal stream. The Vpb should be pointing at our volume + // device object at this point. + // + + StreamFile = IoCreateStreamFileObjectLite( NULL, Vcb->Vpb->RealDevice ); + + if (StreamFile == NULL) { + + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Initialize the fields of the file object. + // + + StreamFile->ReadAccess = TRUE; + StreamFile->WriteAccess = FALSE; + StreamFile->DeleteAccess = FALSE; + + StreamFile->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject; + + // + // Set the file object type and increment the Vcb counts. + // + + CdSetFileObject( IrpContext, + StreamFile, + StreamFileOpen, + Fcb, + NULL ); + + // + // We'll give stream file objects a name to aid IO profiling etc. We + // NULL this in CdDeleteInternalStream before OB deletes the file object, + // and before CdRemovePrefix is called (which frees Fcb names). + // + + StreamFile->FileName = *Name; + + // + // We will reference the current Fcb twice to keep it from going + // away in the error path. Otherwise if we dereference it + // below in the finally clause a close could cause the Fcb to + // be deallocated. + // + + CdLockVcb( IrpContext, Vcb ); + CdIncrementReferenceCounts( IrpContext, Fcb, 2, 0 ); + CdUnlockVcb( IrpContext, Vcb ); + DecrementReference = TRUE; + + // + // Initialize the cache map for the file. + // + + CcInitializeCacheMap( StreamFile, + (PCC_FILE_SIZES)&Fcb->AllocationSize, + TRUE, + &CdData.CacheManagerCallbacks, + Fcb ); + + // + // Go ahead and store the stream file into the Fcb. + // + + Fcb->FileObject = StreamFile; + StreamFile = NULL; + + // + // If this is the first file object for a directory then we need to + // read the self entry for this directory and update the sizes + // in the Fcb. We know that the Fcb has been initialized so + // that we have a least one sector available to read. + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED )) { + + ULONG NewDataLength; + + // + // Initialize the search structures. + // + + CdInitializeDirContext( IrpContext, &DirContext ); + CdInitializeDirent( IrpContext, &Dirent ); + CleanupDirContext = TRUE; + + // + // Read the dirent from disk and transfer the data to the + // in-memory dirent. + // + + CdLookupDirent( IrpContext, + Fcb, + Fcb->StreamOffset, + &DirContext ); + + CdUpdateDirentFromRawDirent( IrpContext, Fcb, &DirContext, &Dirent ); + + // + // Verify that this really for the self entry. We do this by + // updating the name in the dirent and then checking that it matches + // one of the hard coded names. + // + + CdUpdateDirentName( IrpContext, &Dirent, FALSE ); + + if (Dirent.CdFileName.FileName.Buffer != CdUnicodeSelfArray) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If the data sizes are different then update the header + // and Mcb for this Fcb. + // + + NewDataLength = BlockAlign( Vcb, Dirent.DataLength + Fcb->StreamOffset ); + + if (NewDataLength == 0) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + if (NewDataLength != Fcb->FileSize.QuadPart) { + + Fcb->AllocationSize.QuadPart = + Fcb->FileSize.QuadPart = + Fcb->ValidDataLength.QuadPart = NewDataLength; + + CcSetFileSizes( Fcb->FileObject, (PCC_FILE_SIZES) &Fcb->AllocationSize ); + + CdTruncateAllocation( IrpContext, Fcb, 0 ); + CdAddInitialAllocation( IrpContext, + Fcb, + Dirent.StartingOffset, + NewDataLength ); + + UpdateFcbSizes = TRUE; + } + + // + // Check for the existence flag and transform to hidden. + // + + if (FlagOn( Dirent.DirentFlags, CD_ATTRIBUTE_HIDDEN )) { + + SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_HIDDEN ); + } + + // + // Convert the time to NT time. + // + + CdConvertCdTimeToNtTime( IrpContext, + Dirent.CdTime, + (PLARGE_INTEGER) &Fcb->CreationTime ); + + // + // Update the Fcb flags to indicate we have read the + // self entry. + // + + SetFlag( Fcb->FcbState, FCB_STATE_INITIALIZED ); + + // + // If we updated the sizes then we want to purge the file. Go + // ahead and unpin and then purge the first page. + // + + CdCleanupDirContext( IrpContext, &DirContext ); + CdCleanupDirent( IrpContext, &Dirent ); + CleanupDirContext = FALSE; + + if (UpdateFcbSizes) { + + CcPurgeCacheSection( &Fcb->FcbNonpaged->SegmentObject, + NULL, + 0, + FALSE ); + } + } + + } finally { + + // + // Cleanup any dirent structures we may have used. + // + + if (CleanupDirContext) { + + CdCleanupDirContext( IrpContext, &DirContext ); + CdCleanupDirent( IrpContext, &Dirent ); + } + + // + // If we raised then we need to dereference the file object. + // + + if (StreamFile != NULL) { + + // + // Null the name pointer, since the stream file object never actually + // 'owns' the names, we just point it to existing ones. + // + + StreamFile->FileName.Buffer = NULL; + StreamFile->FileName.MaximumLength = StreamFile->FileName.Length = 0; + + ObDereferenceObject( StreamFile ); + Fcb->FileObject = NULL; + } + + // + // Dereference and unlock the Fcb. + // + + if (DecrementReference) { + + CdLockVcb( IrpContext, Vcb ); + CdDecrementReferenceCounts( IrpContext, Fcb, 1, 0 ); + CdUnlockVcb( IrpContext, Vcb ); + } + + CdUnlockFcb( IrpContext, Fcb ); + } + + return; +} + + +VOID +CdDeleteInternalStream ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb + ) + +/*++ + +Routine Description: + + This function creates an internal stream file for interaction + with the cache manager. The Fcb here can be for either a + directory stream or for a path table stream. + +Arguments: + + Fcb - Points to the Fcb for this file. It is either an Index or + Path Table Fcb. + +Return Value: + + None. + +--*/ + +{ + PFILE_OBJECT FileObject; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // Lock the Fcb. + // + + CdLockFcb( IrpContext, Fcb ); + + // + // Capture the file object. + // + + FileObject = Fcb->FileObject; + Fcb->FileObject = NULL; + + // + // It is now safe to unlock the Fcb. + // + + CdUnlockFcb( IrpContext, Fcb ); + + // + // Dereference the file object if present. + // + + if (FileObject != NULL) { + + if (FileObject->PrivateCacheMap != NULL) { + + CcUninitializeCacheMap( FileObject, NULL, NULL ); + } + + // + // Null the name pointer, since the stream file object never actually + // 'owns' the names, we just point it to existing ones. + // + + FileObject->FileName.Buffer = NULL; + FileObject->FileName.MaximumLength = FileObject->FileName.Length = 0; + + ObDereferenceObject( FileObject ); + } +} + + +NTSTATUS +CdCompleteMdl ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the function of completing Mdl reads. + It should be called only from CdFsdRead. + +Arguments: + + Irp - Supplies the originating Irp. + +Return Value: + + NTSTATUS - Will always be STATUS_SUCCESS. + +--*/ + +{ + PFILE_OBJECT FileObject; + + PAGED_CODE(); + + // + // Do completion processing. + // + + FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject; + + CcMdlReadComplete( FileObject, Irp->MdlAddress ); + + // + // Mdl is now deallocated. + // + + Irp->MdlAddress = NULL; + + // + // Complete the request and exit right away. + // + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; +} + + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdPurgeVolume ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _In_ BOOLEAN DismountUnderway + ) + +/*++ + +Routine Description: + + This routine is called to purge the volume. The purpose is to make all the stale file + objects in the system go away in order to lock the volume. + + The Vcb is already acquired exclusively. We will lock out all file operations by + acquiring the global file resource. Then we will walk through all of the Fcb's and + perform the purge. + +Arguments: + + Vcb - Vcb for the volume to purge. + + DismountUnderway - Indicates that we are trying to delete all of the objects. + We will purge the Path Table and VolumeDasd and dereference all + internal streams. + +Return Value: + + NTSTATUS - The first failure of the purge operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + PVOID RestartKey = NULL; + PFCB ThisFcb = NULL; + PFCB NextFcb; + + BOOLEAN RemovedFcb; + + PAGED_CODE(); + + ASSERT_EXCLUSIVE_VCB( Vcb); + + // + // Force any remaining Fcb's in the delayed close queue to be closed. + // + + CdFspClose( Vcb ); + + // + // Acquire the global file resource. + // + + CdAcquireAllFiles( IrpContext, Vcb ); + + // + // Loop through each Fcb in the Fcb Table and perform the flush. + // + + while (TRUE) { + + // + // Lock the Vcb to lookup the next Fcb. + // + + CdLockVcb( IrpContext, Vcb ); + NextFcb = CdGetNextFcb( IrpContext, Vcb, &RestartKey ); + + // + // Reference the NextFcb if present. + // + + if (NextFcb != NULL) { + + NextFcb->FcbReference += 1; + } + + // + // If the last Fcb is present then decrement reference count and call teardown + // to see if it should be removed. + // + + if (ThisFcb != NULL) { + + ThisFcb->FcbReference -= 1; + + CdUnlockVcb( IrpContext, Vcb ); + + CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb ); + + } else { + + CdUnlockVcb( IrpContext, Vcb ); + } + + // + // Break out of the loop if no more Fcb's. + // + + if (NextFcb == NULL) { + + break; + } + + // + // Move to the next Fcb. + // + + ThisFcb = NextFcb; + + // + // If there is a image section then see if that can be closed. + // + + if (ThisFcb->FcbNonpaged->SegmentObject.ImageSectionObject != NULL) { + + MmFlushImageSection( &ThisFcb->FcbNonpaged->SegmentObject, MmFlushForWrite ); + } + + // + // If there is a data section then purge this. If there is an image + // section then we won't be able to. Remember this if it is our first + // error. + // + + if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) && + !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject, + NULL, + 0, + FALSE ) && + (Status == STATUS_SUCCESS)) { + + Status = STATUS_UNABLE_TO_DELETE_SECTION; + } + + // + // Dereference the internal stream if dismounting. + // + + if (DismountUnderway && + (SafeNodeType( ThisFcb ) != CDFS_NTC_FCB_DATA) && + (ThisFcb->FileObject != NULL)) { + + CdDeleteInternalStream( IrpContext, ThisFcb ); + } + } + + // + // Now look at the path table and volume Dasd Fcb's. + // + + if (DismountUnderway) { + + if (Vcb->PathTableFcb != NULL) { + + ThisFcb = Vcb->PathTableFcb; + InterlockedIncrement( (LONG*)&Vcb->PathTableFcb->FcbReference ); + + if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) && + !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject, + NULL, + 0, + FALSE ) && + (Status == STATUS_SUCCESS)) { + + Status = STATUS_UNABLE_TO_DELETE_SECTION; + } + + CdDeleteInternalStream( IrpContext, ThisFcb ); + + InterlockedDecrement( (LONG*)&ThisFcb->FcbReference ); + + CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb ); + } + + if (Vcb->VolumeDasdFcb != NULL) { + + ThisFcb = Vcb->VolumeDasdFcb; + InterlockedIncrement( (LONG*)&ThisFcb->FcbReference ); + + if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) && + !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject, + NULL, + 0, + FALSE ) && + (Status == STATUS_SUCCESS)) { + + Status = STATUS_UNABLE_TO_DELETE_SECTION; + } + + InterlockedDecrement( (LONG*)&ThisFcb->FcbReference ); + + CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb ); + } + } + + // + // Release all of the files. + // + + CdReleaseAllFiles( IrpContext, Vcb ); + + return Status; +} + + diff --git a/filesys/cdfs/cd.h b/filesys/cdfs/cd.h new file mode 100644 index 00000000..f830210a --- /dev/null +++ b/filesys/cdfs/cd.h @@ -0,0 +1,534 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Cd.h + +Abstract: + + This module defines the on-disk structure of the Cdfs file system. + + +--*/ + +#ifndef _CDFS_ +#define _CDFS_ + +// +// Sector size on Cdrom disks is hard-coded to 2048 +// + +#ifndef SECTOR_SIZE +#define SECTOR_SIZE (2048) +#endif + +#define RAW_SECTOR_SIZE (2352) +#define SECTOR_MASK (SECTOR_SIZE - 1) +#define INVERSE_SECTOR_MASK ~(SECTOR_SIZE - 1) + +#ifndef SECTOR_SHIFT +#define SECTOR_SHIFT (11) +#endif + +#define XA_SECTOR_SIZE (2352) + +// +// Cdfs file id is a large integer. +// + +typedef LARGE_INTEGER FILE_ID; +typedef FILE_ID *PFILE_ID; + +// +// The following constants are values from the disk. +// + +#define FIRST_VD_SECTOR (16) + +#define VOL_ID_LEN (5) +#define ESC_SEQ_LEN (3) + +#define VERSION_1 (1) + +#define VD_TERMINATOR (255) +#define VD_PRIMARY (1) +#define VD_SECONDARY (2) + +#define VOLUME_ID_LENGTH (32) + +// +// Leave the following so that CdfsBoot.c will compile +// + +#define CD_SECTOR_SIZE (2048) + +#define ISO_VOL_ID "CD001" +#define HSG_VOL_ID "CDROM" + +#define ISO_ATTR_MULTI 0x0080 +#define ISO_ATTR_DIRECTORY 0x0002 + +#define MIN_DIR_REC_SIZE (sizeof( RAW_DIR_REC ) - MAX_FILE_ID_LENGTH) + +#define RVD_STD_ID( r, i ) (i ? r->StandardId : \ + ((PRAW_HSG_VD) r)->StandardId ) + +#define RVD_DESC_TYPE( r, i ) (i ? r->DescType : \ + ((PRAW_HSG_VD) r)->DescType ) + +#define RVD_VERSION( r, i ) (i ? r->Version : \ + ((PRAW_HSG_VD) r)->Version ) + +#define RVD_LB_SIZE( r, i ) (i ? r->LogicalBlkSzI : \ + ((PRAW_HSG_VD) r)->LogicalBlkSzI ) + +#define RVD_VOL_SIZE( r, i ) (i ? r->VolSpaceI : \ + ((PRAW_HSG_VD) r)->VolSpaceI ) + +#define RVD_ROOT_DE( r, i ) (i ? r->RootDe : \ + ((PRAW_HSG_VD) r)->RootDe ) + +#define DE_FILE_FLAGS( iso, de ) (iso ? de->FlagsISO : de->FlagsHSG) + +// +// Data track flag for track entries in TOC +// + +#define TOC_DATA_TRACK (0x04) +#define TOC_LAST_TRACK (0xaa) + + +// +// There is considerable rearrangement of the volume descriptors for +// ISO and HSG. However, within each standard the same structure can +// be used for both the primary and secondary descriptors. +// +// Both of these structures are aligned correctly so that no +// special macros will be needed to unpack them. +// + +// +// Declaration of length of root directory entry in volume descriptor +// + +#define LEN_ROOT_DE (34) + +// +// Maximum length of file ID on the disk. We allow file size beyond the ISO 9660 +// standard. +// + +#define MAX_FILE_ID_LENGTH (255) + + +typedef struct _RAW_ISO_VD { + + UCHAR DescType; // volume type: 1 = standard, 2 = coded + UCHAR StandardId[5]; // volume structure standard id = CD001 + UCHAR Version; // volume structure version number = 1 + UCHAR VolumeFlags; // volume flags + UCHAR SystemId[32]; // system identifier + UCHAR VolumeId[32]; // volume identifier + UCHAR Reserved[8]; // reserved 8 = 0 + ULONG VolSpaceI; // size of the volume in LBN's Intel + ULONG VolSpaceM; // size of the volume in LBN's Motorola + UCHAR CharSet[32]; // character set bytes 0 = ASCII + USHORT VolSetSizeI; // volume set size Intel + USHORT VolSetSizeM; // volume set size Motorola + USHORT VolSeqNumI; // volume set sequence number Intel + USHORT VolSeqNumM; // volume set sequence number Motorola + USHORT LogicalBlkSzI; // logical block size Intel + USHORT LogicalBlkSzM; // logical block size Motorola + ULONG PathTableSzI; // path table size in bytes Intel + ULONG PathTableSzM; // path table size in bytes Motorola + ULONG PathTabLocI[2]; // LBN of 2 path tables Intel + ULONG PathTabLocM[2]; // LBN of 2 path tables Motorola + UCHAR RootDe[LEN_ROOT_DE];// dir entry of the root directory + UCHAR VolSetId[128]; // volume set identifier + UCHAR PublId[128]; // publisher identifier + UCHAR PreparerId[128]; // data preparer identifier + UCHAR AppId[128]; // application identifier + UCHAR Copyright[37]; // file name of copyright notice + UCHAR Abstract[37]; // file name of abstract + UCHAR Bibliograph[37]; // file name of bibliography + UCHAR CreateDate[17]; // volume creation date and time + UCHAR ModDate[17]; // volume modification date and time + UCHAR ExpireDate[17]; // volume expiration date and time + UCHAR EffectDate[17]; // volume effective date and time + UCHAR FileStructVer; // file structure version number = 1 + UCHAR Reserved3; // reserved + UCHAR ResApp[512]; // reserved for application + UCHAR Reserved4[653]; // remainder of 2048 bytes reserved + +} RAW_ISO_VD; +typedef RAW_ISO_VD *PRAW_ISO_VD; + + +typedef struct _RAW_HSG_VD { + + ULONG BlkNumI; // logical block number Intel + ULONG BlkNumM; // logical block number Motorola + UCHAR DescType; // volume type: 1 = standard, 2 = coded + UCHAR StandardId[5]; // volume structure standard id = CDROM + UCHAR Version; // volume structure version number = 1 + UCHAR VolumeFlags; // volume flags + UCHAR SystemId[32]; // system identifier + UCHAR VolumeId[32]; // volume identifier + UCHAR Reserved[8]; // reserved 8 = 0 + ULONG VolSpaceI; // size of the volume in LBN's Intel + ULONG VolSpaceM; // size of the volume in LBN's Motorola + UCHAR CharSet[32]; // character set bytes 0 = ASCII + USHORT VolSetSizeI; // volume set size Intel + USHORT VolSetSizeM; // volume set size Motorola + USHORT VolSeqNumI; // volume set sequence number Intel + USHORT VolSeqNumM; // volume set sequence number Motorola + USHORT LogicalBlkSzI; // logical block size Intel + USHORT LogicalBlkSzM; // logical block size Motorola + ULONG PathTableSzI; // path table size in bytes Intel + ULONG PathTableSzM; // path table size in bytes Motorola + ULONG PathTabLocI[4]; // LBN of 4 path tables Intel + ULONG PathTabLocM[4]; // LBN of 4 path tables Motorola + UCHAR RootDe[LEN_ROOT_DE];// dir entry of the root directory + UCHAR VolSetId[128]; // volume set identifier + UCHAR PublId[128]; // publisher identifier + UCHAR PreparerId[128]; // data preparer identifier + UCHAR AppId[128]; // application identifier + UCHAR Copyright[32]; // file name of copyright notice + UCHAR Abstract[32]; // file name of abstract + UCHAR CreateDate[16]; // volume creation date and time + UCHAR ModDate[16]; // volume modification date and time + UCHAR ExpireDate[16]; // volume expiration date and time + UCHAR EffectDate[16]; // volume effective date and time + UCHAR FileStructVer; // file structure version number + UCHAR Reserved3; // reserved + UCHAR ResApp[512]; // reserved for application + UCHAR Reserved4[680]; // remainder of 2048 bytes reserved + +} RAW_HSG_VD; +typedef RAW_HSG_VD *PRAW_HSG_VD; + + +typedef struct _RAW_JOLIET_VD { + + UCHAR DescType; // volume type: 2 = coded + UCHAR StandardId[5]; // volume structure standard id = CD001 + UCHAR Version; // volume structure version number = 1 + UCHAR VolumeFlags; // volume flags + UCHAR SystemId[32]; // system identifier + UCHAR VolumeId[32]; // volume identifier + UCHAR Reserved[8]; // reserved 8 = 0 + ULONG VolSpaceI; // size of the volume in LBN's Intel + ULONG VolSpaceM; // size of the volume in LBN's Motorola + UCHAR CharSet[32]; // character set bytes 0 = ASCII, Joliett Seq here + USHORT VolSetSizeI; // volume set size Intel + USHORT VolSetSizeM; // volume set size Motorola + USHORT VolSeqNumI; // volume set sequence number Intel + USHORT VolSeqNumM; // volume set sequence number Motorola + USHORT LogicalBlkSzI; // logical block size Intel + USHORT LogicalBlkSzM; // logical block size Motorola + ULONG PathTableSzI; // path table size in bytes Intel + ULONG PathTableSzM; // path table size in bytes Motorola + ULONG PathTabLocI[2]; // LBN of 2 path tables Intel + ULONG PathTabLocM[2]; // LBN of 2 path tables Motorola + UCHAR RootDe[LEN_ROOT_DE];// dir entry of the root directory + UCHAR VolSetId[128]; // volume set identifier + UCHAR PublId[128]; // publisher identifier + UCHAR PreparerId[128]; // data preparer identifier + UCHAR AppId[128]; // application identifier + UCHAR Copyright[37]; // file name of copyright notice + UCHAR Abstract[37]; // file name of abstract + UCHAR Bibliograph[37]; // file name of bibliography + UCHAR CreateDate[17]; // volume creation date and time + UCHAR ModDate[17]; // volume modification date and time + UCHAR ExpireDate[17]; // volume expiration date and time + UCHAR EffectDate[17]; // volume effective date and time + UCHAR FileStructVer; // file structure version number = 1 + UCHAR Reserved3; // reserved + UCHAR ResApp[512]; // reserved for application + UCHAR Reserved4[653]; // remainder of 2048 bytes reserved + +} RAW_JOLIET_VD; +typedef RAW_JOLIET_VD *PRAW_JOLIET_VD; + +// +// Macros to access the different volume descriptors. +// + +#define CdRvdId(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->StandardId : \ + ((PRAW_ISO_VD) (R))->StandardId \ +) + +#define CdRvdVersion(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->Version : \ + ((PRAW_ISO_VD) (R))->Version \ +) + +#define CdRvdDescType(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->DescType : \ + ((PRAW_ISO_VD) (R))->DescType \ +) + +#define CdRvdEsc(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->CharSet : \ + ((PRAW_ISO_VD) (R))->CharSet \ +) + +#define CdRvdVolId(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->VolumeId : \ + ((PRAW_ISO_VD) (R))->VolumeId \ +) + +#define CdRvdBlkSz(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->LogicalBlkSzI :\ + ((PRAW_ISO_VD) (R))->LogicalBlkSzI \ +) + +#define CdRvdPtLoc(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->PathTabLocI[0]:\ + ((PRAW_ISO_VD) (R))->PathTabLocI[0] \ +) + +#define CdRvdPtSz(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->PathTableSzI : \ + ((PRAW_ISO_VD) (R))->PathTableSzI \ +) + +#define CdRvdDirent(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->RootDe : \ + ((PRAW_ISO_VD) (R))->RootDe \ +) + +#define CdRvdVolSz(R,F) ( \ + FlagOn( (F), VCB_STATE_HSG ) ? \ + ((PRAW_HSG_VD) (R))->VolSpaceI : \ + ((PRAW_ISO_VD) (R))->VolSpaceI \ +) + + +// +// This structure is used to overlay a region of a disk sector +// to retrieve a single directory entry. There is a difference +// in the file flags between the ISO and HSG version and a +// additional byte in the ISO for the offset from Greenwich time. +// +// The disk structure is aligned on a word boundary, so any 32 +// bit fields will be represented as an array of 16 bit fields. +// + +typedef struct _RAW_DIRENT { + + UCHAR DirLen; + UCHAR XarLen; + UCHAR FileLoc[4]; + UCHAR FileLocMot[4]; + UCHAR DataLen[4]; + UCHAR DataLenMot[4]; + UCHAR RecordTime[6]; + UCHAR FlagsHSG; + UCHAR FlagsISO; + UCHAR IntLeaveSize; + UCHAR IntLeaveSkip; + UCHAR Vssn[2]; + UCHAR VssnMot[2]; + UCHAR FileIdLen; + UCHAR FileId[MAX_FILE_ID_LENGTH]; + +} RAW_DIRENT; +typedef RAW_DIRENT RAW_DIR_REC; +typedef RAW_DIRENT *PRAW_DIR_REC; +typedef RAW_DIRENT *PRAW_DIRENT; + +#define CD_ATTRIBUTE_HIDDEN (0x01) +#define CD_ATTRIBUTE_DIRECTORY (0x02) +#define CD_ATTRIBUTE_ASSOC (0x04) +#define CD_ATTRIBUTE_MULTI (0x80) + +#define CD_BASE_YEAR (1900) + +#define MIN_RAW_DIRENT_LEN (FIELD_OFFSET( RAW_DIRENT, FileId ) + 1) + +#define BYTE_COUNT_8_DOT_3 (24) + +#define SHORT_NAME_SHIFT (5) + +// +// The following macro recovers the correct flag field. +// + +#define CdRawDirentFlags(IC,RD) ( \ + FlagOn( (IC)->Vcb->VcbState, VCB_STATE_HSG) ? \ + (RD)->FlagsHSG : \ + (RD)->FlagsISO \ +) + +// +// The following macro converts from CD time to NT time. On ISO +// 9660 media, we now pay attention to the GMT offset (integer +// increments of 15 minutes offset from GMT). HSG does not record +// this field. +// +// The restriction to the interval [-48, 52] comes from 9660 8.4.26.1 +// +// VOID +// CdConvertCdTimeToNtTime ( +// _In_ PIRP_CONTEXT IrpContext, +// _In_ PCHAR CdTime, +// _Out_ PLARGE_INTEGER NtTime +// ); +// + +#define GMT_OFFSET_TO_NT ((LONGLONG) 15 * 60 * 1000 * 1000 * 10) + +#define CdConvertCdTimeToNtTime(IC,CD,NT) { \ + TIME_FIELDS _TimeField; \ + CHAR GmtOffset; \ + _TimeField.Year = (CSHORT) *((PCHAR) CD) + CD_BASE_YEAR; \ + _TimeField.Month = (CSHORT) *(Add2Ptr( CD, 1, PCHAR )); \ + _TimeField .Day = (CSHORT) *(Add2Ptr( CD, 2, PCHAR )); \ + _TimeField.Hour = (CSHORT) *(Add2Ptr( CD, 3, PCHAR )); \ + _TimeField.Minute = (CSHORT) *(Add2Ptr( CD, 4, PCHAR )); \ + _TimeField.Second = (CSHORT) *(Add2Ptr( CD, 5, PCHAR )); \ + _TimeField.Milliseconds = (CSHORT) 0; \ + RtlTimeFieldsToTime( &_TimeField, NT ); \ + if (!FlagOn((IC)->Vcb->VcbState, VCB_STATE_HSG) && \ + ((GmtOffset = *(Add2Ptr( CD, 6, PCHAR ))) != 0 ) && \ + (GmtOffset >= -48 && GmtOffset <= 52)) { \ + (NT)->QuadPart += -GmtOffset * GMT_OFFSET_TO_NT; \ + } \ +} + + +// +// The on-disk representation of a Path Table entry differs between +// the ISO version and the HSG version. The fields are the same +// and the same size, but the positions are different. +// + +typedef struct _RAW_PATH_ISO { + + UCHAR DirIdLen; + UCHAR XarLen; + USHORT DirLoc[2]; + USHORT ParentNum; + UCHAR DirId[MAX_FILE_ID_LENGTH]; + +} RAW_PATH_ISO; +typedef RAW_PATH_ISO *PRAW_PATH_ISO; +typedef RAW_PATH_ISO RAW_PATH_ENTRY; +typedef RAW_PATH_ISO *PRAW_PATH_ENTRY; + +typedef struct _RAW_PATH_HSG { + + USHORT DirLoc[2]; + UCHAR XarLen; + UCHAR DirIdLen; + USHORT ParentNum; + UCHAR DirId[MAX_FILE_ID_LENGTH]; + +} RAW_PATH_HSG; +typedef RAW_PATH_HSG *PRAW_PATH_HSG; + +#define MIN_RAW_PATH_ENTRY_LEN (FIELD_OFFSET( RAW_PATH_ENTRY, DirId ) + 1) + +// +// The following macros are used to recover the different fields of the +// Path Table entries. The macro to recover the disk location of the +// directory must copy it into a different variable for alignment reasons. +// +// CdRawPathIdLen - Length of directory name in bytes +// CdRawPathXar - Number of Xar blocks +// CdRawPathLoc - Address of unaligned ulong for disk offset in blocks +// + +#define CdRawPathIdLen(IC, RP) ( \ + FlagOn( (IC)->Vcb->VcbState, VCB_STATE_HSG ) ? \ + ((PRAW_PATH_HSG) (RP))->DirIdLen : \ + (RP)->DirIdLen \ +) + +#define CdRawPathXar(IC, RP) ( \ + FlagOn( (IC)->Vcb->VcbState, VCB_STATE_HSG ) ? \ + ((PRAW_PATH_HSG) (RP))->XarLen : \ + (RP)->XarLen \ +) + +#define CdRawPathLoc(IC, RP) ( \ + FlagOn( (IC)->Vcb->VcbState, VCB_STATE_HSG ) ? \ + ((PRAW_PATH_HSG) (RP))->DirLoc : \ + (RP)->DirLoc \ +) + + +// +// System use are for XA data. The following is the system use area for +// directory entries on XA data disks. +// + +typedef struct _SYSTEM_USE_XA { + + // + // Owner ID. Not used in this version. + // + + UCHAR OwnerId[4]; + + // + // Extent attributes. Only interested if mode2 form2 or digital audio. + // This is stored big endian. We will define the attribute flags so + // we can ignore this fact. + // + + USHORT Attributes; + + // + // XA signature. This value must be 'XA'. + // + + USHORT Signature; + + // + // File Number. + // + + UCHAR FileNumber; + + // + // Not used in this version. + // + + UCHAR Reserved[5]; + +} SYSTEM_USE_XA; +typedef SYSTEM_USE_XA *PSYSTEM_USE_XA; + +#define SYSTEM_USE_XA_FORM1 (0x0008) +#define SYSTEM_USE_XA_FORM2 (0x0010) +#define SYSTEM_USE_XA_DA (0x0040) + +#define SYSTEM_XA_SIGNATURE (0x4158) + +typedef enum _XA_EXTENT_TYPE { + + Form1Data = 0, + Mode2Form2Data, + CDAudio + +} XA_EXTENT_TYPE; +typedef XA_EXTENT_TYPE *PXA_EXTENT_TYPE; + +#endif // _CDFS_ + + + diff --git a/filesys/cdfs/cddata.c b/filesys/cdfs/cddata.c new file mode 100644 index 00000000..19648e36 --- /dev/null +++ b/filesys/cdfs/cddata.c @@ -0,0 +1,1209 @@ + +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + CdData.c + +Abstract: + + This module declares the global data used by the Cdfs file system. + + This module also handles the dispath routines in the Fsd threads as well as + handling the IrpContext and Irp through the exception path. + + +--*/ + +#include "CdProcs.h" + +#ifdef CD_SANITY +BOOLEAN CdTestTopLevel = TRUE; +BOOLEAN CdTestRaisedStatus = TRUE; +BOOLEAN CdBreakOnAnyRaise = FALSE; +BOOLEAN CdTraceRaises = FALSE; +NTSTATUS CdInterestingExceptionCodes[] = { STATUS_DISK_CORRUPT_ERROR, + STATUS_FILE_CORRUPT_ERROR, + 0, 0, 0, 0, 0, 0, 0, 0 }; +#endif + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CDDATA) + +// +// Global data structures +// + +CD_DATA CdData; +FAST_IO_DISPATCH CdFastIoDispatch; + +// +// Reserved directory strings. +// + +WCHAR CdUnicodeSelfArray[] = { L'.' }; +WCHAR CdUnicodeParentArray[] = { L'.', L'.' }; + +UNICODE_STRING CdUnicodeDirectoryNames[] = { + { 2, 2, CdUnicodeSelfArray}, + { 4, 4, CdUnicodeParentArray} +}; + +// +// Volume descriptor identifier strings. +// + +CHAR CdHsgId[] = { 'C', 'D', 'R', 'O', 'M' }; +CHAR CdIsoId[] = { 'C', 'D', '0', '0', '1' }; +CHAR CdXaId[] = { 'C', 'D', '-', 'X', 'A', '0', '0', '1' }; + +// +// Volume label for audio disks. +// + +WCHAR CdAudioLabel[] = { L'A', L'u', L'd', L'i', L'o', L' ', L'C', L'D' }; +USHORT CdAudioLabelLength = sizeof( CdAudioLabel ); + +// +// Pseudo file names for audio disks. +// + +CHAR CdAudioFileName[] = { 'T', 'r', 'a', 'c', 'k', '0', '0', '.', 'c', 'd', 'a' }; +UCHAR CdAudioFileNameLength = sizeof( CdAudioFileName ); +ULONG CdAudioDirentSize = FIELD_OFFSET( RAW_DIRENT, FileId ) + sizeof( CdAudioFileName ) + sizeof( SYSTEM_USE_XA ); +ULONG CdAudioDirentsPerSector = SECTOR_SIZE / (FIELD_OFFSET( RAW_DIRENT, FileId ) + sizeof( CdAudioFileName ) + sizeof( SYSTEM_USE_XA )); +ULONG CdAudioSystemUseOffset = FIELD_OFFSET( RAW_DIRENT, FileId ) + sizeof( CdAudioFileName ); + +// +// Escape sequences for mounting Unicode volumes. +// + +PCHAR CdJolietEscape[] = { "%/@", "%/C", "%/E" }; + +// +// Audio Play Files consist completely of this header block. These +// files are readable in the root of any audio disc regardless of +// the capabilities of the drive. +// +// The "Unique Disk ID Number" is a calculated value consisting of +// a combination of parameters, including the number of tracks and +// the starting locations of those tracks. +// +// Applications interpreting CDDA RIFF files should be advised that +// additional RIFF file chunks may be added to this header in the +// future in order to add information, such as the disk and song title. +// + +LONG CdAudioPlayHeader[] = { + 0x46464952, // Chunk ID = 'RIFF' + 4 * 11 - 8, // Chunk Size = (file size - 8) + 0x41444443, // 'CDDA' + 0x20746d66, // 'fmt ' + 24, // Chunk Size (of 'fmt ' subchunk) = 24 + 0x00000001, // WORD Format Tag, WORD Track Number + 0x00000000, // DWORD Unique Disk ID Number + 0x00000000, // DWORD Track Starting Sector (LBN) + 0x00000000, // DWORD Track Length (LBN count) + 0x00000000, // DWORD Track Starting Sector (MSF) + 0x00000000 // DWORD Track Length (MSF) +}; + +// Audio Philes begin with this header block to identify the data as a +// PCM waveform. AudioPhileHeader is coded as if it has no data included +// in the waveform. Data must be added in 2352-byte multiples. +// +// Fields marked 'ADJUST' need to be adjusted based on the size of the +// data: Add (nSectors*2352) to the DWORDs at offsets 1*4 and 10*4. +// +// File Size of TRACK??.WAV = nSectors*2352 + sizeof(AudioPhileHeader) +// RIFF('WAVE' fmt(1, 2, 44100, 176400, 16, 4) data( ) +// +// The number of sectors in a CD-XA CD-DA file is (DataLen/2048). +// CDFS will expose these files to applications as if they were just +// 'WAVE' files, adjusting the file size so that the RIFF file is valid. +// +// NT NOTE: We do not do any fidelity adjustment. These are presented as raw +// 2352 byte sectors - 95 has the glimmer of an idea to allow CDFS to expose +// the CDXA CDDA data at different sampling rates in a virtual directory +// structure, but we will never do that. +// + +LONG CdXAAudioPhileHeader[] = { + 0x46464952, // Chunk ID = 'RIFF' + -8, // Chunk Size = (file size - 8) ADJUST1 + 0x45564157, // 'WAVE' + 0x20746d66, // 'fmt ' + 16, // Chunk Size (of 'fmt ' subchunk) = 16 + 0x00020001, // WORD Format Tag WORD nChannels + 44100, // DWORD nSamplesPerSecond + 2352 * 75, // DWORD nAvgBytesPerSec + 0x00100004, // WORD nBlockAlign WORD nBitsPerSample + 0x61746164, // 'data' + -44 // ADJUST2 +}; + +// +// XA Files begin with this RIFF header block to identify the data as +// raw CD-XA sectors. Data must be added in 2352-byte multiples. +// +// This header is added to all CD-XA files which are marked as having +// mode2form2 sectors. +// +// Fields marked 'ADJUST' need to be adjusted based on the size of the +// data: Add file size to the marked DWORDS. +// +// File Size of TRACK??.WAV = nSectors*2352 + sizeof(XAFileHeader) +// +// RIFF('CDXA' FMT(Owner, Attr, 'X', 'A', FileNum, 0) data ( ) +// + +LONG CdXAFileHeader[] = { + 0x46464952, // Chunk ID = 'RIFF' + -8, // Chunk Size = (file size - 8) ADJUST + 0x41584443, // 'CDXA' + 0x20746d66, // 'fmt ' + 16, // Chunk Size (of CDXA chunk) = 16 + 0, // DWORD Owner ID + 0x41580000, // WORD Attributes + // BYTE Signature byte 1 'X' + // BYTE Signature byte 2 'A' + 0, // BYTE File Number + 0, // BYTE Reserved[7] + 0x61746164, // 'data' + -44 // ADJUST +}; + +#ifdef CDFS_TELEMETRY_DATA + +// +// Telemetry Data for reporting +// + +CDFS_TELEMETRY_DATA_CONTEXT CdTelemetryData; + +#endif // CDFS_TELEMETRY_DATA + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdFastIoCheckIfPossible) +#pragma alloc_text(PAGE, CdSerial32) +#pragma alloc_text(PAGE, CdSetThreadContext) +#endif + + +NTSTATUS +CdFsdDispatch ( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the driver entry to all of the Fsd dispatch points. + + Conceptually the Io routine will call this routine on all requests + to the file system. We case on the type of request and invoke the + correct handler for this type of request. There is an exception filter + to catch any exceptions in the CDFS code as well as the CDFS process + exception routine. + + This routine allocates and initializes the IrpContext for this request as + well as updating the top-level thread context as necessary. We may loop + in this routine if we need to retry the request for any reason. The + status code STATUS_CANT_WAIT is used to indicate this. Suppose the disk + in the drive has changed. An Fsd request will proceed normally until it + recognizes this condition. STATUS_VERIFY_REQUIRED is raised at that point + and the exception code will handle the verify and either return + STATUS_CANT_WAIT or STATUS_PENDING depending on whether the request was + posted. + +Arguments: + + DeviceObject - Supplies the volume device object for this request + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + THREAD_CONTEXT ThreadContext = {0}; + PIRP_CONTEXT IrpContext = NULL; + BOOLEAN Wait; + +#ifdef CD_SANITY + PVOID PreviousTopLevel; +#endif + + NTSTATUS Status; + +#if DBG + + KIRQL SaveIrql = KeGetCurrentIrql(); + +#endif + + ASSERT_OPTIONAL_IRP( Irp ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + FsRtlEnterFileSystem(); + +#ifdef CD_SANITY + PreviousTopLevel = IoGetTopLevelIrp(); +#endif + + // + // Loop until this request has been completed or posted. + // + + do { + + // + // Use a try-except to handle the exception cases. + // + + try { + + // + // If the IrpContext is NULL then this is the first pass through + // this loop. + // + + if (IrpContext == NULL) { + + // + // Decide if this request is waitable an allocate the IrpContext. + // If the file object in the stack location is NULL then this + // is a mount which is always waitable. Otherwise we look at + // the file object flags. + // + + if (IoGetCurrentIrpStackLocation( Irp )->FileObject == NULL) { + + Wait = TRUE; + + } else { + + Wait = CanFsdWait( Irp ); + } + + IrpContext = CdCreateIrpContext( Irp, Wait ); + + // + // Update the thread context information. + // + + CdSetThreadContext( IrpContext, &ThreadContext ); + +#ifdef CD_SANITY + NT_ASSERT( !CdTestTopLevel || + SafeNodeType( IrpContext->TopLevel ) == CDFS_NTC_IRP_CONTEXT ); +#endif + + // + // Otherwise cleanup the IrpContext for the retry. + // + + } else { + + // + // Set the MORE_PROCESSING flag to make sure the IrpContext + // isn't inadvertently deleted here. Then cleanup the + // IrpContext to perform the retry. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); + CdCleanupIrpContext( IrpContext, FALSE ); + } + + // + // Case on the major irp code. + // + + switch (IrpContext->MajorFunction) { + + case IRP_MJ_CREATE : + + Status = CdCommonCreate( IrpContext, Irp ); + break; + + case IRP_MJ_CLOSE : + + Status = CdCommonClose( IrpContext, Irp ); + break; + + case IRP_MJ_READ : + + // + // If this is an Mdl complete request, don't go through + // common read. + // + + if (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE )) { + + Status = CdCompleteMdl( IrpContext, Irp ); + + } else { + + Status = CdCommonRead( IrpContext, Irp ); + } + + break; + + case IRP_MJ_WRITE : + + Status = CdCommonWrite( IrpContext, Irp ); + break; + + case IRP_MJ_QUERY_INFORMATION : + + Status = CdCommonQueryInfo( IrpContext, Irp ); + break; + + case IRP_MJ_SET_INFORMATION : + + Status = CdCommonSetInfo( IrpContext, Irp ); + break; + + case IRP_MJ_QUERY_VOLUME_INFORMATION : + + Status = CdCommonQueryVolInfo( IrpContext, Irp ); + break; + + case IRP_MJ_DIRECTORY_CONTROL : + + Status = CdCommonDirControl( IrpContext, Irp ); + break; + + case IRP_MJ_FILE_SYSTEM_CONTROL : + + Status = CdCommonFsControl( IrpContext, Irp ); + break; + + case IRP_MJ_DEVICE_CONTROL : + + Status = CdCommonDevControl( IrpContext, Irp ); + break; + + case IRP_MJ_LOCK_CONTROL : + + Status = CdCommonLockControl( IrpContext, Irp ); + break; + + case IRP_MJ_CLEANUP : + + Status = CdCommonCleanup( IrpContext, Irp ); + break; + + case IRP_MJ_PNP : + + Status = CdCommonPnp( IrpContext, Irp ); + break; + + case IRP_MJ_SHUTDOWN : + + Status = CdCommonShutdown( IrpContext, Irp ); + break; + + default : + + Status = STATUS_INVALID_DEVICE_REQUEST; + CdCompleteRequest( IrpContext, Irp, Status ); + } + + } except( CdExceptionFilter( IrpContext, GetExceptionInformation() )) { + + Status = CdProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + } while (Status == STATUS_CANT_WAIT); + +#ifdef CD_SANITY + NT_ASSERT( !CdTestTopLevel || + (PreviousTopLevel == IoGetTopLevelIrp()) ); +#endif + + FsRtlExitFileSystem(); + + NT_ASSERT( SaveIrql == KeGetCurrentIrql( )); + + return Status; +} + + +#ifdef CD_SANITY + +VOID +CdRaiseStatusEx ( + _In_ PIRP_CONTEXT IrpContext, + _In_ NTSTATUS Status, + _In_ BOOLEAN NormalizeStatus, + _In_opt_ ULONG FileId, + _In_opt_ ULONG Line + ) +{ + BOOLEAN BreakIn = FALSE; + + AssertVerifyDevice( IrpContext, Status); + + if (CdTraceRaises) { + + DbgPrint( "%p CdRaiseStatusEx 0x%x @ fid %d, line %d\n", PsGetCurrentThread(), Status, FileId, Line); + } + + if (CdTestRaisedStatus && !CdBreakOnAnyRaise) { + + ULONG Index; + + for (Index = 0; + Index < (sizeof( CdInterestingExceptionCodes) / sizeof( CdInterestingExceptionCodes[0])); + Index++) { + + if ((STATUS_SUCCESS != CdInterestingExceptionCodes[Index]) && + (CdInterestingExceptionCodes[Index] == Status)) { + + BreakIn = TRUE; + break; + } + } + } + + if (BreakIn || CdBreakOnAnyRaise) { + + DbgPrint( "CDFS: Breaking on raised status %08x (BI=%d,BA=%d)\n", Status, BreakIn, CdBreakOnAnyRaise); + DbgPrint( "CDFS: (FILEID %d LINE %d)\n", FileId, Line); + DbgPrint( "CDFS: Contact CDFS.SYS component owner for triage.\n"); + DbgPrint( "CDFS: 'eb %p 0;eb %p 0' to disable this alert.\n", &CdTestRaisedStatus, &CdBreakOnAnyRaise); + + NT_ASSERT(FALSE); + } + + if (NormalizeStatus) { + + IrpContext->ExceptionStatus = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR); + } + else { + + IrpContext->ExceptionStatus = Status; + } + + IrpContext->RaisedAtLineFile = (FileId << 16) | Line; + + ExRaiseStatus( IrpContext->ExceptionStatus); +} + +#endif + + +LONG +CdExceptionFilter ( + _Inout_ PIRP_CONTEXT IrpContext, + _In_ PEXCEPTION_POINTERS ExceptionPointer + ) + +/*++ + +Routine Description: + + This routine is used to decide whether we will handle a raised exception + status. If CDFS explicitly raised an error then this status is already + in the IrpContext. We choose which is the correct status code and + either indicate that we will handle the exception or bug-check the system. + +Arguments: + + ExceptionCode - Supplies the exception code to being checked. + +Return Value: + + ULONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks + +--*/ + +{ + NTSTATUS ExceptionCode; + BOOLEAN TestStatus = TRUE; + + ASSERT_OPTIONAL_IRP_CONTEXT( IrpContext ); + + ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode; + + // + // If the exception is STATUS_IN_PAGE_ERROR, get the I/O error code + // from the exception record. + // + + if ((ExceptionCode == STATUS_IN_PAGE_ERROR) && + (ExceptionPointer->ExceptionRecord->NumberParameters >= 3)) { + + ExceptionCode = + (NTSTATUS)ExceptionPointer->ExceptionRecord->ExceptionInformation[2]; + } + + // + // If there is an Irp context then check which status code to use. + // + + if (ARGUMENT_PRESENT( IrpContext )) { + + if (IrpContext->ExceptionStatus == STATUS_SUCCESS) { + + // + // Store the real status into the IrpContext. + // + + IrpContext->ExceptionStatus = ExceptionCode; + + } else { + + // + // No need to test the status code if we raised it ourselves. + // + + TestStatus = FALSE; + } + } + + AssertVerifyDevice( IrpContext, IrpContext->ExceptionStatus ); + + // + // Bug check if this status is not supported. + // + + if (TestStatus && !FsRtlIsNtstatusExpected( ExceptionCode )) { + +#pragma prefast( suppress: __WARNING_USE_OTHER_FUNCTION, "We're corrupted." ) + CdBugCheck( (ULONG_PTR) ExceptionPointer->ExceptionRecord, + (ULONG_PTR) ExceptionPointer->ContextRecord, + (ULONG_PTR) ExceptionPointer->ExceptionRecord->ExceptionAddress ); + + } + + return EXCEPTION_EXECUTE_HANDLER; +} + + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdProcessException ( + _In_opt_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _In_ NTSTATUS ExceptionCode + ) + +/*++ + +Routine Description: + + This routine processes an exception. It either completes the request + with the exception status in the IrpContext, sends this off to the Fsp + workque or causes it to be retried in the current thread if a verification + is needed. + + If the volume needs to be verified (STATUS_VERIFY_REQUIRED) and we can + do the work in the current thread we will translate the status code + to STATUS_CANT_WAIT to indicate that we need to retry the request. + +Arguments: + + Irp - Supplies the Irp being processed + + ExceptionCode - Supplies the normalized exception status being handled + +Return Value: + + NTSTATUS - Returns the results of either posting the Irp or the + saved completion status. + +--*/ + +{ + PDEVICE_OBJECT Device = NULL; + PVPB Vpb; + PETHREAD Thread; + + ASSERT_OPTIONAL_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // If there is not an irp context, then complete the request with the + // current status code. + // + + if (!ARGUMENT_PRESENT( IrpContext )) { + + CdCompleteRequest( NULL, Irp, ExceptionCode ); + return ExceptionCode; + } + + // + // Get the real exception status from the IrpContext. + // + + ExceptionCode = IrpContext->ExceptionStatus; + + // + // Check if we are posting this request. One of the following must be true + // if we are to post a request. + // + // - Status code is STATUS_CANT_WAIT and the request is asynchronous + // or we are forcing this to be posted. + // + // - Status code is STATUS_VERIFY_REQUIRED and we are at APC level + // or higher, or within a guarded region. Can't wait for IO in + // the verify path in this case. + // + // Set the MORE_PROCESSING flag in the IrpContext to keep if from being + // deleted if this is a retryable condition. + // + // + // Note that (children of) CdFsdPostRequest can raise (Mdl allocation). + // + + try { + + if (ExceptionCode == STATUS_CANT_WAIT) { + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST )) { + + ExceptionCode = CdFsdPostRequest( IrpContext, Irp ); + } + } + else if ((ExceptionCode == STATUS_VERIFY_REQUIRED) && + FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL ) && + KeAreAllApcsDisabled()) { + + ExceptionCode = CdFsdPostRequest( IrpContext, Irp ); + } + } + except( CdExceptionFilter( IrpContext, GetExceptionInformation() )) { + + ExceptionCode = GetExceptionCode(); + } + + // + // If we posted the request or our caller will retry then just return here. + // + + if ((ExceptionCode == STATUS_PENDING) || + (ExceptionCode == STATUS_CANT_WAIT)) { + + return ExceptionCode; + } + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); + + // + // If we are not a top level request then we just complete the request + // with the current status code. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL )) { + + CdCompleteRequest( IrpContext, Irp, ExceptionCode ); + return ExceptionCode; + } + + // + // Store this error into the Irp for posting back to the Io system. + // + + Irp->IoStatus.Status = ExceptionCode; + + if (IoIsErrorUserInduced( ExceptionCode )) { + + // + // Check for the various error conditions that can be caused by, + // and possibly resolved my the user. + // + + if (ExceptionCode == STATUS_VERIFY_REQUIRED) { + + // + // Now we are at the top level file system entry point. + // + // If we have already posted this request then the device to + // verify is in the original thread. Find this via the Irp. + // + + Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread ); + IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL ); + + // + // If there is no device in that location then check in the + // current thread. + // + + if (Device == NULL) { + + Device = IoGetDeviceToVerify( PsGetCurrentThread() ); + IoSetDeviceToVerify( PsGetCurrentThread(), NULL ); + + NT_ASSERT( Device != NULL ); + + } + + // + // It turns out some storage drivers really do set invalid non-NULL device + // objects to verify. + // + // To work around this, completely ignore the device to verify in the thread, + // and just use our real device object instead. + // + + if (IrpContext->Vcb) { + + Device = IrpContext->Vcb->Vpb->RealDevice; + } + + // + // Let's not BugCheck just because the device to verify is somehow still NULL. + // + + if (Device == NULL) { + + ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR; + + CdCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + } + + // + // CdPerformVerify() will do the right thing with the Irp. + // If we return STATUS_CANT_WAIT then the current thread + // can retry the request. + // + + return CdPerformVerify( IrpContext, Irp, Device ); + } + + // + // The other user induced conditions generate an error unless + // they have been disabled for this request. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS )) { + + CdCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + + } + // + // Generate a pop-up. + // + else { + + if (IoGetCurrentIrpStackLocation( Irp )->FileObject != NULL) { + + Vpb = IoGetCurrentIrpStackLocation( Irp )->FileObject->Vpb; + + } else { + + Vpb = NULL; + } + + + // + // The device to verify is either in my thread local storage + // or that of the thread that owns the Irp. + // + + Thread = Irp->Tail.Overlay.Thread; + Device = IoGetDeviceToVerify( Thread ); + + if (Device == NULL) { + + Thread = PsGetCurrentThread(); + Device = IoGetDeviceToVerify( Thread ); + + NT_ASSERT( Device != NULL ); + } + + // + // It turns out some storage drivers really do set invalid non-NULL device + // objects to verify. + // + // To work around this, completely ignore the device to verify in the thread, + // and just use our real device object instead. + // + + if (IrpContext->Vcb) { + + Device = IrpContext->Vcb->Vpb->RealDevice; + } + + // + // Let's not BugCheck just because the device to verify is somehow still NULL. + // + + if (Device == NULL) { + + CdCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + } + + // + // This routine actually causes the pop-up. It usually + // does this by queuing an APC to the callers thread, + // but in some cases it will complete the request immediately, + // so it is very important to IoMarkIrpPending() first. + // + + IoMarkIrpPending( Irp ); + IoRaiseHardError( Irp, Vpb, Device ); + + // + // We will be handing control back to the caller here, so + // reset the saved device object. + // + + IoSetDeviceToVerify( Thread, NULL ); + + // + // The Irp will be completed by Io or resubmitted. In either + // case we must clean up the IrpContext here. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + return STATUS_PENDING; + } + } + + // + // This is just a run of the mill error. + // + + CdCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; +} + + +VOID +CdCompleteRequest ( + _Inout_opt_ PIRP_CONTEXT IrpContext, + _Inout_opt_ PIRP Irp, + _In_ NTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine completes a Irp and cleans up the IrpContext. Either or + both of these may not be specified. + +Arguments: + + Irp - Supplies the Irp being processed. + + Status - Supplies the status to complete the Irp with + +Return Value: + + None. + +--*/ + +{ + ASSERT_OPTIONAL_IRP_CONTEXT( IrpContext ); + ASSERT_OPTIONAL_IRP( Irp ); + + // + // Cleanup the IrpContext if passed in here. + // + + if (ARGUMENT_PRESENT( IrpContext )) { + + CdCleanupIrpContext( IrpContext, FALSE ); + } + + // + // If we have an Irp then complete the irp. + // + + if (ARGUMENT_PRESENT( Irp )) { + + // + // Clear the information field in case we have used this Irp + // internally. + // + + if (NT_ERROR( Status ) && + FlagOn( Irp->Flags, IRP_INPUT_OPERATION )) { + + Irp->IoStatus.Information = 0; + } + + Irp->IoStatus.Status = Status; + + AssertVerifyDeviceIrp( Irp ); + + IoCompleteRequest( Irp, IO_CD_ROM_INCREMENT ); + } + + return; +} + + +VOID +CdSetThreadContext ( + _Inout_ PIRP_CONTEXT IrpContext, + _In_ PTHREAD_CONTEXT ThreadContext + ) + +/*++ + +Routine Description: + + This routine is called at each Fsd/Fsp entry point set up the IrpContext + and thread local storage to track top level requests. If there is + not a Cdfs context in the thread local storage then we use the input one. + Otherwise we use the one already there. This routine also updates the + IrpContext based on the state of the top-level context. + + If the TOP_LEVEL flag in the IrpContext is already set when we are called + then we force this request to appear top level. + +Arguments: + + ThreadContext - Address on stack for local storage if not already present. + + ForceTopLevel - We force this request to appear top level regardless of + any previous stack value. + +Return Value: + + None + +--*/ + +{ + PTHREAD_CONTEXT CurrentThreadContext; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + + // + // Get the current top-level irp out of the thread storage. + // If NULL then this is the top-level request. + // + + CurrentThreadContext = (PTHREAD_CONTEXT) IoGetTopLevelIrp(); + + if (CurrentThreadContext == NULL) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL ); + } + + // + // Initialize the input context unless we are using the current + // thread context block. We use the new block if our caller + // specified this or the existing block is invalid. + // + // The following must be true for the current to be a valid Cdfs context. + // + // Structure must lie within current stack. + // Address must be ULONG aligned. + // Cdfs signature must be present. + // + // If this is not a valid Cdfs context then use the input thread + // context and store it in the top level context. + // +#pragma warning(suppress: 6011) // Bug in PREFast around bitflag operations + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL ) || + (!IoWithinStackLimits( (ULONG_PTR)CurrentThreadContext, sizeof( THREAD_CONTEXT ) ) || + FlagOn( (ULONG_PTR) CurrentThreadContext, 0x3 ) || + (CurrentThreadContext->Cdfs != 0x53464443))) { + + ThreadContext->Cdfs = 0x53464443; + ThreadContext->SavedTopLevelIrp = (PIRP) CurrentThreadContext; + ThreadContext->TopLevelIrpContext = IrpContext; + IoSetTopLevelIrp( (PIRP) ThreadContext ); + + IrpContext->TopLevel = IrpContext; + IrpContext->ThreadContext = ThreadContext; + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS ); + + // + // Otherwise use the IrpContext in the thread context. + // + + } else { + + IrpContext->TopLevel = CurrentThreadContext->TopLevelIrpContext; + } + + return; +} + + +_Function_class_(FAST_IO_CHECK_IF_POSSIBLE) +_IRQL_requires_same_ +_Success_(return != FALSE) +BOOLEAN +CdFastIoCheckIfPossible ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _In_ BOOLEAN CheckForReadOperation, + _Pre_notnull_ + _When_(return != FALSE, _Post_equal_to_(_Old_(IoStatus))) + _When_(return == FALSE, _Post_valid_) + PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine checks if fast i/o is possible for a read/write operation + +Arguments: + + FileObject - Supplies the file object used in the query + + FileOffset - Supplies the starting byte offset for the read/write operation + + Length - Supplies the length, in bytes, of the read/write operation + + Wait - Indicates if we can wait + + LockKey - Supplies the lock key + + CheckForReadOperation - Indicates if this is a check for a read or write + operation + + IoStatus - Receives the status of the operation if our return value is + FastIoReturnError + +Return Value: + + BOOLEAN - TRUE if fast I/O is possible and FALSE if the caller needs + to take the long route. + +--*/ + +{ + PFCB Fcb; + TYPE_OF_OPEN TypeOfOpen; + LARGE_INTEGER LargeLength; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Wait ); + UNREFERENCED_PARAMETER( DeviceObject ); + + // + // Decode the type of file object we're being asked to process and + // make sure that is is only a user file open. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + if ((TypeOfOpen != UserFileOpen) || !CheckForReadOperation) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + return TRUE; + } + + LargeLength.QuadPart = Length; + + // + // Check whether the file locks will allow for fast io. + // + + if ((Fcb->FileLock == NULL) || + FsRtlFastCheckLockForRead( Fcb->FileLock, + FileOffset, + &LargeLength, + LockKey, + FileObject, + PsGetCurrentProcess() )) { + + return TRUE; + } + + return FALSE; +} + + +ULONG +CdSerial32 ( + _In_reads_bytes_(ByteCount) PCHAR Buffer, + _In_ ULONG ByteCount + ) +/*++ + +Routine Description: + + This routine is called to generate a 32 bit serial number. This is + done by doing four separate checksums into an array of bytes and + then treating the bytes as a ULONG. + +Arguments: + + Buffer - Pointer to the buffer to generate the ID for. + + ByteCount - Number of bytes in the buffer. + +Return Value: + + ULONG - The 32 bit serial number. + +--*/ + +{ + union { + UCHAR Bytes[4]; + ULONG SerialId; + } Checksum; + + PAGED_CODE(); + + // + // Initialize the serial number. + // + + Checksum.SerialId = 0; + + // + // Continue while there are more bytes to use. + // + + while (ByteCount--) { + + // + // Increment this sub-checksum. + // + + Checksum.Bytes[ByteCount & 0x3] += *(Buffer++); + } + + // + // Return the checksums as a ULONG. + // + + return Checksum.SerialId; +} + + diff --git a/filesys/cdfs/cddata.h b/filesys/cdfs/cddata.h new file mode 100644 index 00000000..395b1862 --- /dev/null +++ b/filesys/cdfs/cddata.h @@ -0,0 +1,273 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + CdData.c + +Abstract: + + This module declares the global data used by the Cdfs file system. + + +--*/ + +#ifndef _CDDATA_ +#define _CDDATA_ + +// +// Global data structures +// + +extern CD_DATA CdData; +extern FAST_IO_DISPATCH CdFastIoDispatch; + +// +// Global constants +// + +// +// This is the number of times a mounted Vcb will be referenced on behalf +// of the system. The counts include the following references. +// +// 1 reference - shows the volume is mounted +// 1 reference - 1 for VolumeDasdFcb. +// 2 references - 1 for RootIndexFcb, 1 for internal stream. +// 2 references - 1 for PathTableFcb, 1 for internal stream. +// +// For user references we add one for the reference in each of the internal +// Fcb's. +// + +#define CDFS_RESIDUAL_REFERENCE (6) +#define CDFS_RESIDUAL_USER_REFERENCE (3) + +// +// Reserved directory strings +// + +extern WCHAR CdUnicodeSelfArray[]; +extern WCHAR CdUnicodeParentArray[]; + +extern UNICODE_STRING CdUnicodeDirectoryNames[]; + +// +// Volume descriptor identifier strings. +// + +extern CHAR CdHsgId[]; +extern CHAR CdIsoId[]; +extern CHAR CdXaId[]; + +// +// Volume label for audio disks. +// + +extern WCHAR CdAudioLabel[]; +extern USHORT CdAudioLabelLength; + +// +// Pseudo file names for audio disks. +// + +extern CHAR CdAudioFileName[]; +extern UCHAR CdAudioFileNameLength; +extern ULONG CdAudioDirentSize; +extern ULONG CdAudioDirentsPerSector; +extern ULONG CdAudioSystemUseOffset; + +#define AUDIO_NAME_ONES_OFFSET (6) +#define AUDIO_NAME_TENS_OFFSET (5) + +// +// Escape sequences for mounting Unicode volumes. +// + +extern PCHAR CdJolietEscape[]; + +// +// Hardcoded header for RIFF files. +// + +extern LONG CdXAFileHeader[]; +extern LONG CdAudioPlayHeader[]; +extern LONG CdXAAudioPhileHeader[]; + +#ifdef CDFS_TELEMETRY_DATA + +// +// Globals for Telemetry data. +// + +extern CDFS_TELEMETRY_DATA_CONTEXT CdTelemetryData; + +#endif // CDFS_TELEMETRY_DATA + +// +// The following assertion macros ensure that the indicated structure +// is valid +// +// ASSERT_STRUCT( _In_ PVOID Struct, _In_ CSHORT NodeType ); +// ASSERT_OPTIONAL_STRUCT( _In_opt_ PVOID Struct, _In_ CSHORT NodeType ); +// +// ASSERT_VCB( _In_ PVCB Vcb ); +// ASSERT_OPTIONAL_VCB( _In_opt_ PVCB Vcb ); +// +// ASSERT_FCB( _In_ PFCB Fcb ); +// ASSERT_OPTIONAL_FCB( _In_opt_ PFCB Fcb ); +// +// ASSERT_FCB_NONPAGED( _In_ PFCB_NONPAGED FcbNonpaged ); +// ASSERT_OPTIONAL_FCB( _In_opt_ PFCB_NONPAGED FcbNonpaged ); +// +// ASSERT_CCB( _In_ PSCB Ccb ); +// ASSERT_OPTIONAL_CCB( _In_opt_ PSCB Ccb ); +// +// ASSERT_IRP_CONTEXT( _In_ PIRP_CONTEXT IrpContext ); +// ASSERT_OPTIONAL_IRP_CONTEXT( _In_opt_ PIRP_CONTEXT IrpContext ); +// +// ASSERT_IRP( _In_ PIRP Irp ); +// ASSERT_OPTIONAL_IRP( _In_opt_ PIRP Irp ); +// +// ASSERT_FILE_OBJECT( _In_ PFILE_OBJECT FileObject ); +// ASSERT_OPTIONAL_FILE_OBJECT( _In_opt_ PFILE_OBJECT FileObject ); +// +// The following macros are used to check the current thread owns +// the indicated resource +// +// ASSERT_EXCLUSIVE_RESOURCE( _In_ PERESOURCE Resource ); +// +// ASSERT_SHARED_RESOURCE( _In_ PERESOURCE Resource ); +// +// ASSERT_RESOURCE_NOT_MINE( _In_ PERESOURCE Resource ); +// +// The following macros are used to check whether the current thread +// owns the resoures in the given structures. +// +// ASSERT_EXCLUSIVE_CDDATA +// +// ASSERT_EXCLUSIVE_VCB( _In_ PVCB Vcb ); +// +// ASSERT_SHARED_VCB( _In_ PVCB Vcb ); +// +// ASSERT_EXCLUSIVE_FCB( _In_ PFCB Fcb ); +// +// ASSERT_SHARED_FCB( _In_ PFCB Fcb ); +// +// ASSERT_EXCLUSIVE_FILE( _In_ PFCB Fcb ); +// +// ASSERT_SHARED_FILE( _In_ PFCB Fcb ); +// +// ASSERT_LOCKED_VCB( _In_ PVCB Vcb ); +// +// ASSERT_NOT_LOCKED_VCB( _In_ PVCB Vcb ); +// +// ASSERT_LOCKED_FCB( _In_ PFCB Fcb ); +// +// ASSERT_NOT_LOCKED_FCB( _In_ PFCB Fcb ); +// + +// +// Turn on the sanity checks if this is DBG or CD_FREE_ASSERTS +// + +#if DBG +#undef CD_SANITY +#define CD_SANITY +#endif + +#ifdef CD_SANITY + +#define ASSERT_STRUCT(S,T) NT_ASSERT( SafeNodeType( S ) == (T) ) +#define ASSERT_OPTIONAL_STRUCT(S,T) NT_ASSERT( ((S) == NULL) || (SafeNodeType( S ) == (T)) ) + +#define ASSERT_VCB(V) ASSERT_STRUCT( (V), CDFS_NTC_VCB ) +#define ASSERT_OPTIONAL_VCB(V) ASSERT_OPTIONAL_STRUCT( (V), CDFS_NTC_VCB ) + +#define ASSERT_FCB(F) \ + NT_ASSERT( (SafeNodeType( F ) == CDFS_NTC_FCB_DATA ) || \ + (SafeNodeType( F ) == CDFS_NTC_FCB_INDEX ) || \ + (SafeNodeType( F ) == CDFS_NTC_FCB_PATH_TABLE ) ) + +#define ASSERT_OPTIONAL_FCB(F) \ + NT_ASSERT( ((F) == NULL) || \ + (SafeNodeType( F ) == CDFS_NTC_FCB_DATA ) || \ + (SafeNodeType( F ) == CDFS_NTC_FCB_INDEX ) || \ + (SafeNodeType( F ) == CDFS_NTC_FCB_PATH_TABLE ) ) + +#define ASSERT_FCB_NONPAGED(FN) ASSERT_STRUCT( (FN), CDFS_NTC_FCB_NONPAGED ) +#define ASSERT_OPTIONAL_FCB_NONPAGED(FN) ASSERT_OPTIONAL_STRUCT( (FN), CDFS_NTC_FCB_NONPAGED ) + +#define ASSERT_CCB(C) ASSERT_STRUCT( (C), CDFS_NTC_CCB ) +#define ASSERT_OPTIONAL_CCB(C) ASSERT_OPTIONAL_STRUCT( (C), CDFS_NTC_CCB ) + +#define ASSERT_IRP_CONTEXT(IC) ASSERT_STRUCT( (IC), CDFS_NTC_IRP_CONTEXT ) +#define ASSERT_OPTIONAL_IRP_CONTEXT(IC) ASSERT_OPTIONAL_STRUCT( (IC), CDFS_NTC_IRP_CONTEXT ) + +#define ASSERT_IRP(I) ASSERT_STRUCT( (I), IO_TYPE_IRP ) +#define ASSERT_OPTIONAL_IRP(I) ASSERT_OPTIONAL_STRUCT( (I), IO_TYPE_IRP ) + +#define ASSERT_FILE_OBJECT(FO) ASSERT_STRUCT( (FO), IO_TYPE_FILE ) +#define ASSERT_OPTIONAL_FILE_OBJECT(FO) ASSERT_OPTIONAL_STRUCT( (FO), IO_TYPE_FILE ) + +#define ASSERT_EXCLUSIVE_RESOURCE(R) NT_ASSERT( ExIsResourceAcquiredExclusiveLite( R )) + +#define ASSERT_SHARED_RESOURCE(R) NT_ASSERT( ExIsResourceAcquiredSharedLite( R )) + +#define ASSERT_RESOURCE_NOT_MINE(R) NT_ASSERT( !ExIsResourceAcquiredSharedLite( R )) + +#define ASSERT_EXCLUSIVE_CDDATA NT_ASSERT( ExIsResourceAcquiredExclusiveLite( &CdData.DataResource )) +#define ASSERT_EXCLUSIVE_VCB(V) NT_ASSERT( ExIsResourceAcquiredExclusiveLite( &(V)->VcbResource )) +#define ASSERT_SHARED_VCB(V) NT_ASSERT( ExIsResourceAcquiredSharedLite( &(V)->VcbResource )) + +#define ASSERT_EXCLUSIVE_FCB(F) NT_ASSERT( ExIsResourceAcquiredExclusiveLite( &(F)->FcbNonpaged->FcbResource )) +#define ASSERT_SHARED_FCB(F) NT_ASSERT( ExIsResourceAcquiredSharedLite( &(F)->FcbNonpaged->FcbResource )) + +#define ASSERT_EXCLUSIVE_FILE(F) NT_ASSERT( ExIsResourceAcquiredExclusiveLite( (F)->Resource )) +#define ASSERT_SHARED_FILE(F) NT_ASSERT( ExIsResourceAcquiredSharedLite( (F)->Resource )) + +#define ASSERT_LOCKED_VCB(V) NT_ASSERT( (V)->VcbLockThread == PsGetCurrentThread() ) +#define ASSERT_NOT_LOCKED_VCB(V) NT_ASSERT( (V)->VcbLockThread != PsGetCurrentThread() ) + +#define ASSERT_LOCKED_FCB(F) NT_ASSERT( !FlagOn( (F)->FcbState, FCB_STATE_IN_FCB_TABLE) || ((F)->FcbLockThread == PsGetCurrentThread())) +#define ASSERT_NOT_LOCKED_FCB(F) NT_ASSERT( (F)->FcbLockThread != PsGetCurrentThread() ) + +#else + +#define DebugBreakOnStatus(S) { NOTHING; } + +#define ASSERT_STRUCT(S,T) { NOTHING; } +#define ASSERT_OPTIONAL_STRUCT(S,T) { NOTHING; } +#define ASSERT_VCB(V) { NOTHING; } +#define ASSERT_OPTIONAL_VCB(V) { NOTHING; } +#define ASSERT_FCB(F) { NOTHING; } +#define ASSERT_OPTIONAL_FCB(F) { NOTHING; } +#define ASSERT_FCB_NONPAGED(FN) { NOTHING; } +#define ASSERT_OPTIONAL_FCB(FN) { NOTHING; } +#define ASSERT_CCB(C) { NOTHING; } +#define ASSERT_OPTIONAL_CCB(C) { NOTHING; } +#define ASSERT_IRP_CONTEXT(IC) { NOTHING; } +#define ASSERT_OPTIONAL_IRP_CONTEXT(IC) { NOTHING; } +#define ASSERT_IRP(I) { NOTHING; } +#define ASSERT_OPTIONAL_IRP(I) { NOTHING; } +#define ASSERT_FILE_OBJECT(FO) { NOTHING; } +#define ASSERT_OPTIONAL_FILE_OBJECT(FO) { NOTHING; } +#define ASSERT_EXCLUSIVE_RESOURCE(R) { NOTHING; } +#define ASSERT_SHARED_RESOURCE(R) { NOTHING; } +#define ASSERT_RESOURCE_NOT_MINE(R) { NOTHING; } +#define ASSERT_EXCLUSIVE_CDDATA { NOTHING; } +#define ASSERT_EXCLUSIVE_VCB(V) { NOTHING; } +#define ASSERT_SHARED_VCB(V) { NOTHING; } +#define ASSERT_EXCLUSIVE_FCB(F) { NOTHING; } +#define ASSERT_SHARED_FCB(F) { NOTHING; } +#define ASSERT_EXCLUSIVE_FILE(F) { NOTHING; } +#define ASSERT_SHARED_FILE(F) { NOTHING; } +#define ASSERT_LOCKED_VCB(V) { NOTHING; } +#define ASSERT_NOT_LOCKED_VCB(V) { NOTHING; } +#define ASSERT_LOCKED_FCB(F) { NOTHING; } +#define ASSERT_NOT_LOCKED_FCB(F) { NOTHING; } + +#endif + +#endif // _CDDATA_ + diff --git a/filesys/cdfs/cdfs.rc b/filesys/cdfs/cdfs.rc new file mode 100644 index 00000000..5d4dcafc --- /dev/null +++ b/filesys/cdfs/cdfs.rc @@ -0,0 +1,14 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "CD-ROM File System Driver" +#define VER_INTERNALNAME_STR "cdfs.sys" + +#include "common.ver" + diff --git a/filesys/cdfs/cdfs.sln b/filesys/cdfs/cdfs.sln new file mode 100644 index 00000000..1dbf3f5c --- /dev/null +++ b/filesys/cdfs/cdfs.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cdfs", "cdfs.vcxproj", "{CF3DA8BB-C366-42EF-B62B-F3D799F94B72}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Debug|Win32.ActiveCfg = Debug|Win32 + {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Debug|Win32.Build.0 = Debug|Win32 + {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Release|Win32.ActiveCfg = Release|Win32 + {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Release|Win32.Build.0 = Release|Win32 + {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Debug|x64.ActiveCfg = Debug|x64 + {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Debug|x64.Build.0 = Debug|x64 + {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Release|x64.ActiveCfg = Release|x64 + {CF3DA8BB-C366-42EF-B62B-F3D799F94B72}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/cdfs/cdfs.vcxproj b/filesys/cdfs/cdfs.vcxproj new file mode 100644 index 00000000..8e87f675 --- /dev/null +++ b/filesys/cdfs/cdfs.vcxproj @@ -0,0 +1,330 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CF3DA8BB-C366-42EF-B62B-F3D799F94B72} + $(MSBuildProjectName) + false + Debug + Win32 + {1340E057-CDDF-46D6-B5D9-394AF1B2CA07} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + cdfs + + + cdfs + + + cdfs + + + cdfs + + + + true + Level4 + + + + + true + Level4 + + + + + true + Level4 + + + + + true + Level4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Create + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + cdprocs.h + Use + $(IntDir)\cdprocs.h.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/cdfs/cdfs.vcxproj.Filters b/filesys/cdfs/cdfs.vcxproj.Filters new file mode 100644 index 00000000..e5978b9a --- /dev/null +++ b/filesys/cdfs/cdfs.vcxproj.Filters @@ -0,0 +1,115 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {6041F7C5-67BE-4EBD-A1B8-53A22C207899} + + + h;hpp;hxx;hm;inl;inc;xsd + {7B185A7C-1201-4E8B-80F4-79EC615CB7F0} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {2979BE05-350C-4B63-96FF-B319CCBD776F} + + + inf;inv;inx;mof;mc; + {217A878D-9867-4135-9B60-4809FBE6E1CE} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/cdfs/cdinit.c b/filesys/cdfs/cdinit.c new file mode 100644 index 00000000..5ec9d503 --- /dev/null +++ b/filesys/cdfs/cdinit.c @@ -0,0 +1,387 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + CdInit.c + +Abstract: + + This module implements the DRIVER_INITIALIZATION routine for Cdfs + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CDINIT) + +// Tell prefast the function type. +DRIVER_INITIALIZE DriverEntry; + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + + +// tell prefast this is a driver unload function +DRIVER_UNLOAD CdUnload; + +VOID +CdUnload( + _In_ PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +CdInitializeGlobalData ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT FileSystemDeviceObject + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, CdUnload) +#pragma alloc_text(INIT, CdInitializeGlobalData) +#endif + + +// +// Local support routine +// + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This is the initialization routine for the Cdrom file system + device driver. This routine creates the device object for the FileSystem + device and performs all other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operation. + +--*/ + +{ + NTSTATUS Status; + UNICODE_STRING UnicodeString; + PDEVICE_OBJECT CdfsFileSystemDeviceObject; + FS_FILTER_CALLBACKS FilterCallbacks; + + UNREFERENCED_PARAMETER( RegistryPath ); + + // + // Create the device object. + // + + RtlInitUnicodeString( &UnicodeString, L"\\Cdfs" ); + + Status = IoCreateDevice( DriverObject, + 0, + &UnicodeString, + FILE_DEVICE_CD_ROM_FILE_SYSTEM, + 0, + FALSE, + &CdfsFileSystemDeviceObject ); + + if (!NT_SUCCESS( Status )) { + return Status; + } + +#pragma prefast(push) +#pragma prefast(disable: 28155, "the dispatch routine has the correct type, prefast is just being paranoid.") +#pragma prefast(disable: 28168, "the dispatch routine has the correct type, prefast is just being paranoid.") +#pragma prefast(disable: 28169, "the dispatch routine has the correct type, prefast is just being paranoid.") +#pragma prefast(disable: 28175, "we're allowed to change these.") + + DriverObject->DriverUnload = CdUnload; + + // + // Note that because of the way data caching is done, we set neither + // the Direct I/O or Buffered I/O bit in DeviceObject->Flags. If + // data is not in the cache, or the request is not buffered, we may, + // set up for Direct I/O by hand. + // + + // + // Initialize the driver object with this driver's entry points. + // + // NOTE - Each entry in the dispatch table must have an entry in + // the Fsp/Fsd dispatch switch statements. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = + DriverObject->MajorFunction[IRP_MJ_CLOSE] = + DriverObject->MajorFunction[IRP_MJ_READ] = + DriverObject->MajorFunction[IRP_MJ_WRITE] = + DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = + DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = + DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION]= + DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = + DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = + DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = + DriverObject->MajorFunction[IRP_MJ_PNP] = + DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH) CdFsdDispatch; +#pragma prefast(pop) + +#pragma prefast(suppress: 28175, "this is a file system driver, we're allowed to touch FastIoDispatch.") + DriverObject->FastIoDispatch = &CdFastIoDispatch; + + // + // Initialize the filter callbacks we use. + // + + RtlZeroMemory( &FilterCallbacks, + sizeof(FS_FILTER_CALLBACKS) ); + + FilterCallbacks.SizeOfFsFilterCallbacks = sizeof(FS_FILTER_CALLBACKS); + FilterCallbacks.PreAcquireForSectionSynchronization = CdFilterCallbackAcquireForCreateSection; + + Status = FsRtlRegisterFileSystemFilterCallbacks( DriverObject, + &FilterCallbacks ); + + if (!NT_SUCCESS( Status )) { + + IoDeleteDevice( CdfsFileSystemDeviceObject ); + return Status; + } + + // + // Initialize the global data structures + // + + Status = CdInitializeGlobalData( DriverObject, CdfsFileSystemDeviceObject ); + if (!NT_SUCCESS (Status)) { + IoDeleteDevice (CdfsFileSystemDeviceObject); + return Status; + } + + // + // Register the file system as low priority with the I/O system. This will cause + // CDFS to receive mount requests after a) other filesystems currently registered + // and b) other normal priority filesystems that may be registered later. + // + + CdfsFileSystemDeviceObject->Flags |= DO_LOW_PRIORITY_FILESYSTEM; + + IoRegisterFileSystem( CdfsFileSystemDeviceObject ); + ObReferenceObject (CdfsFileSystemDeviceObject); + +#ifdef CDFS_TELEMETRY_DATA + // + // Initialize Telemetry + // + + CdInitializeTelemetry(); + +#endif + + // + // And return to our caller + // + + return( STATUS_SUCCESS ); +} + + +VOID +CdUnload( + _In_ PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine unload routine for CDFS. + +Arguments: + + DriverObject - Supplies the driver object for CDFS. + +Return Value: + + None. + +--*/ +{ + PIRP_CONTEXT IrpContext; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DriverObject ); + + // + // Free any IRP contexts + // + while (1) { + IrpContext = (PIRP_CONTEXT) PopEntryList( &CdData.IrpContextList) ; + if (IrpContext == NULL) { + break; + } + CdFreePool(&IrpContext); + } + + IoFreeWorkItem (CdData.CloseItem); + ExDeleteResourceLite( &CdData.DataResource ); + ObDereferenceObject (CdData.FileSystemDeviceObject); +} + +// +// Local support routine +// + +NTSTATUS +CdInitializeGlobalData ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT FileSystemDeviceObject + ) + +/*++ + +Routine Description: + + This routine initializes the global cdfs data structures. + +Arguments: + + DriverObject - Supplies the driver object for CDFS. + + FileSystemDeviceObject - Supplies the device object for CDFS. + +Return Value: + + None. + +--*/ + +{ + // + // Start by initializing the FastIoDispatch Table. + // + + RtlZeroMemory( &CdFastIoDispatch, sizeof( FAST_IO_DISPATCH )); + + CdFastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH); + +#pragma prefast(push) +#pragma prefast(disable:28155, "these are all correct") + + CdFastIoDispatch.FastIoCheckIfPossible = CdFastIoCheckIfPossible; // CheckForFastIo + CdFastIoDispatch.FastIoRead = FsRtlCopyRead; // Read + CdFastIoDispatch.FastIoQueryBasicInfo = CdFastQueryBasicInfo; // QueryBasicInfo + CdFastIoDispatch.FastIoQueryStandardInfo = CdFastQueryStdInfo; // QueryStandardInfo + CdFastIoDispatch.FastIoLock = CdFastLock; // Lock + CdFastIoDispatch.FastIoUnlockSingle = CdFastUnlockSingle; // UnlockSingle + CdFastIoDispatch.FastIoUnlockAll = CdFastUnlockAll; // UnlockAll + CdFastIoDispatch.FastIoUnlockAllByKey = CdFastUnlockAllByKey; // UnlockAllByKey + + // + // This callback has been replaced by CdFilterCallbackAcquireForCreateSection. + // + + CdFastIoDispatch.AcquireFileForNtCreateSection = NULL; + CdFastIoDispatch.ReleaseFileForNtCreateSection = CdReleaseForCreateSection; + CdFastIoDispatch.FastIoQueryNetworkOpenInfo = CdFastQueryNetworkInfo; // QueryNetworkInfo + + CdFastIoDispatch.MdlRead = FsRtlMdlReadDev; + CdFastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev; + CdFastIoDispatch.PrepareMdlWrite = FsRtlPrepareMdlWriteDev; + CdFastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev; + +#pragma prefast(pop) + + // + // Initialize the CdData structure. + // + + RtlZeroMemory( &CdData, sizeof( CD_DATA )); + + CdData.NodeTypeCode = CDFS_NTC_DATA_HEADER; + CdData.NodeByteSize = sizeof( CD_DATA ); + + CdData.DriverObject = DriverObject; + CdData.FileSystemDeviceObject = FileSystemDeviceObject; + + InitializeListHead( &CdData.VcbQueue ); + + ExInitializeResourceLite( &CdData.DataResource ); + + // + // Initialize the cache manager callback routines + // + + CdData.CacheManagerCallbacks.AcquireForLazyWrite = &CdAcquireForCache; + CdData.CacheManagerCallbacks.ReleaseFromLazyWrite = &CdReleaseFromCache; + CdData.CacheManagerCallbacks.AcquireForReadAhead = &CdAcquireForCache; + CdData.CacheManagerCallbacks.ReleaseFromReadAhead = &CdReleaseFromCache; + + CdData.CacheManagerVolumeCallbacks.AcquireForLazyWrite = &CdNoopAcquire; + CdData.CacheManagerVolumeCallbacks.ReleaseFromLazyWrite = &CdNoopRelease; + CdData.CacheManagerVolumeCallbacks.AcquireForReadAhead = &CdNoopAcquire; + CdData.CacheManagerVolumeCallbacks.ReleaseFromReadAhead = &CdNoopRelease; + + // + // Initialize the lock mutex and the async and delay close queues. + // + + ExInitializeFastMutex( &CdData.CdDataMutex ); + InitializeListHead( &CdData.AsyncCloseQueue ); + InitializeListHead( &CdData.DelayedCloseQueue ); + + CdData.CloseItem = IoAllocateWorkItem (FileSystemDeviceObject); + if (CdData.CloseItem == NULL) { + + ExDeleteResourceLite( &CdData.DataResource ); + return STATUS_INSUFFICIENT_RESOURCES; + } + // + // Do the initialization based on the system size. + // + + switch (MmQuerySystemSize()) { + + case MmSmallSystem: + + CdData.IrpContextMaxDepth = 4; + CdData.MaxDelayedCloseCount = 8; + CdData.MinDelayedCloseCount = 2; + break; + + case MmMediumSystem: + + CdData.IrpContextMaxDepth = 8; + CdData.MaxDelayedCloseCount = 24; + CdData.MinDelayedCloseCount = 6; + break; + + case MmLargeSystem: + + CdData.IrpContextMaxDepth = 32; + CdData.MaxDelayedCloseCount = 72; + CdData.MinDelayedCloseCount = 18; + break; + } + return STATUS_SUCCESS; +} + diff --git a/filesys/cdfs/cdprocs.h b/filesys/cdfs/cdprocs.h new file mode 100644 index 00000000..6645e539 --- /dev/null +++ b/filesys/cdfs/cdprocs.h @@ -0,0 +1,2263 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + CdProcs.h + +Abstract: + + This module defines all of the globally used procedures in the Cdfs + file system. + + +--*/ + +#ifndef _CDPROCS_ +#define _CDPROCS_ + +#pragma warning( disable: 4127 ) // conditional expression is constant + +#pragma warning( push ) +#pragma warning( disable: 4201 ) // nonstandard extension used : nameless struct/union +#pragma warning( disable: 4214 ) // nonstandard extension used : bit field types + +#include + +#include +#include +#include + +#ifndef INLINE +#define INLINE __inline +#endif + +#include "nodetype.h" +#include "Cd.h" +#include "CdStruc.h" +#include "CdData.h" + +#ifdef CDFS_TELEMETRY_DATA + +#include +#include +#include + +#endif // CDFS_TELEMETRY_DATA + +#pragma warning( pop ) + +//**** x86 compiler bug **** + +#if defined(_M_IX86) +#undef Int64ShraMod32 +#define Int64ShraMod32(a, b) ((LONGLONG)(a) >> (b)) +#endif + +#ifndef Min +#define Min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef Max +#define Max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +// +// Here are the different pool tags. +// + +#define TAG_CCB 'ccdC' // Ccb +#define TAG_CDROM_TOC 'ctdC' // TOC +#define TAG_DIRENT_NAME 'nddC' // CdName in dirent +#define TAG_ENUM_EXPRESSION 'eedC' // Search expression for enumeration +#define TAG_FCB_DATA 'dfdC' // Data Fcb +#define TAG_FCB_INDEX 'ifdC' // Index Fcb +#define TAG_FCB_NONPAGED 'nfdC' // Nonpaged Fcb +#define TAG_FCB_TABLE 'tfdC' // Fcb Table entry +#define TAG_FILE_NAME 'nFdC' // Filename buffer +#define TAG_GEN_SHORT_NAME 'sgdC' // Generated short name +#define TAG_IO_BUFFER 'fbdC' // Temporary IO buffer +#define TAG_IO_CONTEXT 'oidC' // Io context for async reads +#define TAG_IRP_CONTEXT 'cidC' // Irp Context +#define TAG_IRP_CONTEXT_LITE 'lidC' // Irp Context lite +#define TAG_MCB_ARRAY 'amdC' // Mcb array +#define TAG_PATH_ENTRY_NAME 'nPdC' // CdName in path entry +#define TAG_PREFIX_ENTRY 'epdC' // Prefix Entry +#define TAG_PREFIX_NAME 'npdC' // Prefix Entry name +#define TAG_SPANNING_PATH_TABLE 'psdC' // Buffer for spanning path table +#define TAG_UPCASE_NAME 'nudC' // Buffer for upcased name +#define TAG_VOL_DESC 'dvdC' // Buffer for volume descriptor +#define TAG_VPB 'pvdC' // Vpb allocated in filesystem + +// +// Tag all of our allocations if tagging is turned on +// + +#ifdef POOL_TAGGING + +#undef FsRtlAllocatePool +#undef FsRtlAllocatePoolWithQuota +#define FsRtlAllocatePool(a,b) FsRtlAllocatePoolWithTag(a,b,'sfdC') +#define FsRtlAllocatePoolWithQuota(a,b) FsRtlAllocatePoolWithQuotaTag(a,b,'sfdC') + +#endif // POOL_TAGGING + + +// +// File access check routine, implemented in AcChkSup.c +// + +// +// BOOLEAN +// CdIllegalFcbAccess ( +// _In_ PIRP_CONTEXT IrpContext, +// _In_ TYPE_OF_OPEN TypeOfOpen, +// _In_ ACCESS_MASK DesiredAccess +// ); +// + +#define CdIllegalFcbAccess(IC,T,DA) ( \ + BooleanFlagOn( (DA), \ + ((T) != UserVolumeOpen ? \ + (FILE_WRITE_ATTRIBUTES | \ + FILE_WRITE_DATA | \ + FILE_WRITE_EA | \ + FILE_ADD_FILE | \ + FILE_ADD_SUBDIRECTORY | \ + FILE_APPEND_DATA) : 0) | \ + FILE_DELETE_CHILD | \ + DELETE | \ + WRITE_DAC )) + + +// +// Allocation support routines, implemented in AllocSup.c +// +// These routines are for querying allocation on individual streams. +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdLookupAllocation ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG FileOffset, + _Out_ PLONGLONG DiskOffset, + _Out_ PULONG ByteCount + ); + +VOID +CdAddAllocationFromDirent ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ ULONG McbEntryOffset, + _In_ LONGLONG StartingFileOffset, + _In_ PDIRENT Dirent + ); + +VOID +CdAddInitialAllocation ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ ULONG StartingBlock, + _In_ LONGLONG DataLength + ); + +VOID +CdTruncateAllocation ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ LONGLONG StartingFileOffset + ); + +_At_(Fcb->NodeByteSize, _In_range_(>=, FIELD_OFFSET( FCB, FcbType ))) +VOID +CdInitializeMcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_updates_bytes_(Fcb->NodeByteSize) PFCB Fcb + ); + +_At_(Fcb->NodeByteSize, _In_range_(>=, FIELD_OFFSET( FCB, FcbType ))) +_When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_PATH_TABLE, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_INDEX))) +_When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_INDEX, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_INDEX))) +_When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_DATA, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_DATA))) +VOID +CdUninitializeMcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_updates_bytes_(Fcb->NodeByteSize) PFCB Fcb + ); + + +// +// Buffer control routines for data caching, implemented in CacheSup.c +// + +VOID +CdCreateInternalStream ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Inout_ PFCB Fcb, + _In_ PUNICODE_STRING Name + ); + +VOID +CdDeleteInternalStream ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb + ); + +NTSTATUS +CdCompleteMdl ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdPurgeVolume ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _In_ BOOLEAN DismountUnderway + ); + +VOID +INLINE +CdVerifyOrCreateDirStreamFile ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb + ) +{ + // + // Unsafe test to see if call / lock neccessary. + // + + if (NULL == Fcb->FileObject) { + + CdCreateInternalStream( IrpContext, + Fcb->Vcb, + Fcb, + &Fcb->FileNamePrefix.ExactCaseName.FileName); + } +} + + +// +// VOID +// CdUnpinData ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PBCB *Bcb +// ); +// + +#define CdUnpinData(IC,B) \ + if (*(B) != NULL) { CcUnpinData( *(B) ); *(B) = NULL; } + + +// +// Device I/O routines, implemented in DevIoSup.c +// +// These routines perform the actual device read and writes. They only affect +// the on disk structure and do not alter any other data structures. +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdFreeDirCache ( + _In_ PIRP_CONTEXT IrpContext + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdNonCachedRead ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdNonCachedXARead ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdVolumeDasdWrite ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount + ); + +BOOLEAN +CdReadSectors ( + _In_ PIRP_CONTEXT IrpContext, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount, + _In_ BOOLEAN ReturnError, + _Out_writes_bytes_(ByteCount) PVOID Buffer, + _In_ PDEVICE_OBJECT TargetDeviceObject + ); + +NTSTATUS +CdCreateUserMdl ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG BufferLength, + _In_ BOOLEAN RaiseOnError, + _In_ LOCK_OPERATION Operation + ); + +NTSTATUS +FASTCALL +CdPerformDevIoCtrl ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG IoControlCode, + _In_ PDEVICE_OBJECT Device, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _In_ BOOLEAN InternalDeviceIoControl, + _In_ BOOLEAN OverrideVerify, + _Out_opt_ PIO_STATUS_BLOCK Iosb + ); + +NTSTATUS +CdPerformDevIoCtrlEx ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG IoControlCode, + _In_ PDEVICE_OBJECT Device, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _In_ BOOLEAN InternalDeviceIoControl, + _In_ BOOLEAN OverrideVerify, + _Out_opt_ PIO_STATUS_BLOCK Iosb + ); + +NTSTATUS +CdHijackIrpAndFlushDevice ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _In_ PDEVICE_OBJECT TargetDeviceObject + ); + + +// +// VOID +// CdMapUserBuffer ( +// _In_ PIRP_CONTEXT IrpContext +// _Out_ PVOID UserBuffer +// ); +// +// Returns pointer to sys address. Will raise on failure. +// +// +// VOID +// CdLockUserBuffer ( +// _Inout_ PIRP_CONTEXT IrpContext, +// _In_ ULONG BufferLength +// ); +// + +#define CdMapUserBuffer(IC, UB) { \ + *(UB) = (PVOID) ( ((IC)->Irp->MdlAddress == NULL) ? \ + (IC)->Irp->UserBuffer : \ + (MmGetSystemAddressForMdlSafe( (IC)->Irp->MdlAddress, NormalPagePriority | MdlMappingNoExecute))); \ + if (NULL == *(UB)) { \ + CdRaiseStatus( (IC), STATUS_INSUFFICIENT_RESOURCES); \ + } \ + } + + +#define CdLockUserBuffer(IC,BL,OP) { \ + if ((IC)->Irp->MdlAddress == NULL) { \ + (VOID) CdCreateUserMdl( (IC), (BL), TRUE, (OP) ); \ + } \ +} + + +// +// Dirent support routines, implemented in DirSup.c +// + +VOID +CdLookupDirent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ ULONG DirentOffset, + _Out_ PDIRENT_ENUM_CONTEXT DirContext + ); + +BOOLEAN +CdLookupNextDirent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PDIRENT_ENUM_CONTEXT CurrentDirContext, + _Inout_ PDIRENT_ENUM_CONTEXT NextDirContext + ); + +_At_(Dirent->CdTime, _Post_notnull_) +VOID +CdUpdateDirentFromRawDirent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PDIRENT_ENUM_CONTEXT DirContext, + _Inout_ PDIRENT Dirent + ); + +VOID +CdUpdateDirentName ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PDIRENT Dirent, + _In_ ULONG IgnoreCase + ); + +_Success_(return != FALSE) BOOLEAN +CdFindFile ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PCD_NAME Name, + _In_ BOOLEAN IgnoreCase, + _Inout_ PFILE_ENUM_CONTEXT FileContext, + _Out_ PCD_NAME *MatchingName + ); + +BOOLEAN +CdFindDirectory ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PCD_NAME Name, + _In_ BOOLEAN IgnoreCase, + _Inout_ PFILE_ENUM_CONTEXT FileContext + ); + +_At_(FileContext->ShortName.FileName.MaximumLength, _In_range_(>=, BYTE_COUNT_8_DOT_3)) +BOOLEAN +CdFindFileByShortName ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PCD_NAME Name, + _In_ BOOLEAN IgnoreCase, + _In_ ULONG ShortNameDirentOffset, + _Inout_ PFILE_ENUM_CONTEXT FileContext + ); + +BOOLEAN +CdLookupNextInitialFileDirent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Inout_ PFILE_ENUM_CONTEXT FileContext + ); + +VOID +CdLookupLastFileDirent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PFILE_ENUM_CONTEXT FileContext + ); + +VOID +CdCleanupFileContext ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFILE_ENUM_CONTEXT FileContext + ); + +// +// VOID +// CdInitializeFileContext ( +// _In_ PIRP_CONTEXT IrpContext, +// _Out_ PFILE_ENUM_CONTEXT FileContext +// ); +// +// +// VOID +// CdInitializeDirent ( +// _In_ PIRP_CONTEXT IrpContext, +// _Out_ PDIRENT Dirent +// ); +// +// VOID +// CdInitializeDirContext ( +// _In_ PIRP_CONTEXT IrpContext, +// _Out_ PDIRENT_ENUM_CONTEXT DirContext +// ); +// +// VOID +// CdCleanupDirent ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PDIRENT Dirent +// ); +// +// VOID +// CdCleanupDirContext ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PDIRENT_ENUM_CONTEXT DirContext +// ); +// +// VOID +// CdLookupInitialFileDirent ( +// _In_ PIRP_CONTEXT IrpContext, +// _In_ PFCB Fcb, +// _Out_ PFILE_ENUM_CONTEXT FileContext, +// _In_ ULONG DirentOffset +// ); +// + +#define CdInitializeFileContext(IC,FC) { \ + RtlZeroMemory( FC, sizeof( FILE_ENUM_CONTEXT )); \ + (FC)->PriorDirent = &(FC)->Dirents[0]; \ + (FC)->InitialDirent = &(FC)->Dirents[1]; \ + (FC)->CurrentDirent = &(FC)->Dirents[2]; \ + (FC)->ShortName.FileName.MaximumLength = BYTE_COUNT_8_DOT_3; \ + (FC)->ShortName.FileName.Buffer = (FC)->ShortNameBuffer; \ +} + +#define CdInitializeDirent(IC,D) \ + RtlZeroMemory( D, sizeof( DIRENT )) + +#define CdInitializeDirContext(IC,DC) \ + RtlZeroMemory( DC, sizeof( DIRENT_ENUM_CONTEXT )) + +#define CdCleanupDirent(IC,D) { \ + if (FlagOn( (D)->Flags, DIRENT_FLAG_ALLOC_BUFFER )) { \ + CdFreePool( &(D)->CdFileName.FileName.Buffer ); \ + } \ +} + +#define CdCleanupDirContext(IC,DC) \ + CdUnpinData( (IC), &(DC)->Bcb ) + +#define CdLookupInitialFileDirent(IC,F,FC,DO) \ + CdLookupDirent( IC, \ + F, \ + DO, \ + &(FC)->InitialDirent->DirContext ); \ + CdUpdateDirentFromRawDirent( IC, \ + F, \ + &(FC)->InitialDirent->DirContext, \ + &(FC)->InitialDirent->Dirent ) + + +// +// The following routines are used to manipulate the fscontext fields +// of the file object, implemented in FilObSup.c +// + +// +// Type of opens. FilObSup.c depends on this order. +// + +typedef enum _TYPE_OF_OPEN { + + UnopenedFileObject = 0, + StreamFileOpen, + UserVolumeOpen, + UserDirectoryOpen, + UserFileOpen, + BeyondValidType + +} TYPE_OF_OPEN; +typedef TYPE_OF_OPEN *PTYPE_OF_OPEN; + +_When_(TypeOfOpen == UnopenedFileObject, _At_(Fcb, _In_opt_)) +_When_(TypeOfOpen != UnopenedFileObject, _At_(Fcb, _In_)) +VOID +CdSetFileObject ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _In_ TYPE_OF_OPEN TypeOfOpen, + PFCB Fcb, + _In_opt_ PCCB Ccb + ); + +_When_(return == UnopenedFileObject, _At_(*Fcb, _Post_null_)) +_When_(return != UnopenedFileObject, _At_(Fcb, _Outptr_)) +_When_(return == UnopenedFileObject, _At_(*Ccb, _Post_null_)) +_When_(return != UnopenedFileObject, _At_(Ccb, _Outptr_)) +TYPE_OF_OPEN +CdDecodeFileObject ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFILE_OBJECT FileObject, + PFCB *Fcb, + PCCB *Ccb + ); + +TYPE_OF_OPEN +CdFastDecodeFileObject ( + _In_ PFILE_OBJECT FileObject, + _Out_ PFCB *Fcb + ); + + +// +// Name support routines, implemented in NameSup.c +// + +_Post_satisfies_(_Old_(CdName->FileName.Length) >= + CdName->FileName.Length + CdName->VersionString.Length) +VOID +CdConvertNameToCdName ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PCD_NAME CdName + ); + +VOID +CdConvertBigToLittleEndian ( + _In_ PIRP_CONTEXT IrpContext, + _In_reads_bytes_(ByteCount) PCHAR BigEndian, + _In_ ULONG ByteCount, + _Out_writes_bytes_(ByteCount) PCHAR LittleEndian + ); + +VOID +CdUpcaseName ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PCD_NAME Name, + _Inout_ PCD_NAME UpcaseName + ); + +VOID +CdDissectName ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PUNICODE_STRING RemainingName, + _Out_ PUNICODE_STRING FinalName + ); + +BOOLEAN +CdIsLegalName ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PUNICODE_STRING FileName + ); + +BOOLEAN +CdIs8dot3Name ( + _In_ PIRP_CONTEXT IrpContext, + _In_ UNICODE_STRING FileName + ); + +VOID +CdGenerate8dot3Name ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PUNICODE_STRING FileName, + _In_ ULONG DirentOffset, + _Out_writes_bytes_to_(BYTE_COUNT_8_DOT_3, *ShortByteCount) PWCHAR ShortFileName, + _Out_ PUSHORT ShortByteCount + ); + +BOOLEAN +CdIsNameInExpression ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PCD_NAME CurrentName, + _In_ PCD_NAME SearchExpression, + _In_ ULONG WildcardFlags, + _In_ BOOLEAN CheckVersion + ); + +ULONG +CdShortNameDirentOffset ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PUNICODE_STRING Name + ); + +FSRTL_COMPARISON_RESULT +CdFullCompareNames ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PUNICODE_STRING NameA, + _In_ PUNICODE_STRING NameB + ); + + +// +// Filesystem control operations. Implemented in Fsctrl.c +// + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(Vcb->VcbResource) +NTSTATUS +CdLockVolumeInternal ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _In_opt_ PFILE_OBJECT FileObject + ); + +NTSTATUS +CdUnlockVolumeInternal ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _In_opt_ PFILE_OBJECT FileObject + ); + + +// +// Path table enumeration routines. Implemented in PathSup.c +// + +VOID +CdLookupPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG PathEntryOffset, + _In_ ULONG Ordinal, + _In_ BOOLEAN VerifyBounds, + _Inout_ PCOMPOUND_PATH_ENTRY CompoundPathEntry + ); + +BOOLEAN +CdLookupNextPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PPATH_ENUM_CONTEXT PathContext, + _Inout_ PPATH_ENTRY PathEntry + ); + +_Success_(return != FALSE) +BOOLEAN +CdFindPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB ParentFcb, + _In_ PCD_NAME DirName, + _In_ BOOLEAN IgnoreCase, + _Inout_ PCOMPOUND_PATH_ENTRY CompoundPathEntry + ); + +VOID +CdUpdatePathEntryName ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PPATH_ENTRY PathEntry, + _In_ BOOLEAN IgnoreCase + ); + +// +// VOID +// CdInitializeCompoundPathEntry ( +// _In_ PIRP_CONTEXT IrpContext, +// _Out_ PCOMPOUND_PATH_ENTRY CompoundPathEntry +// ); +// +// VOID +// CdCleanupCompoundPathEntry ( +// _In_ PIRP_CONTEXT IrpContext, +// _Out_ PCOMPOUND_PATH_ENTRY CompoundPathEntry +// ); +// + +#define CdInitializeCompoundPathEntry(IC,CP) \ + RtlZeroMemory( CP, sizeof( COMPOUND_PATH_ENTRY )) + +#define CdCleanupCompoundPathEntry(IC,CP) { \ + CdUnpinData( (IC), &(CP)->PathContext.Bcb ); \ + if ((CP)->PathContext.AllocatedData) { \ + CdFreePool( &(CP)->PathContext.Data ); \ + } \ + if (FlagOn( (CP)->PathEntry.Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER )) { \ + CdFreePool( &(CP)->PathEntry.CdDirName.FileName.Buffer ); \ + } \ +} + + +// +// Largest matching prefix searching routines, implemented in PrefxSup.c +// + +VOID +CdInsertPrefix ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ PCD_NAME Name, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ShortNameMatch, + _Inout_ PFCB ParentFcb + ); + +VOID +CdRemovePrefix ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdFindPrefix ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB *CurrentFcb, + _Inout_ PUNICODE_STRING RemainingName, + _In_ BOOLEAN IgnoreCase + ); + + +// +// Synchronization routines. Implemented in Resrcsup.c +// +// The following routines/macros are used to synchronize the in-memory structures. +// +// Routine/Macro Synchronizes Subsequent +// +// CdAcquireCdData Volume Mounts/Dismounts,Vcb Queue CdReleaseCdData +// CdAcquireVcbExclusive Vcb for open/close CdReleaseVcb +// CdAcquireVcbShared Vcb for open/close CdReleaseVcb +// CdAcquireAllFiles Locks out operations to all files CdReleaseAllFiles +// CdAcquireFileExclusive Locks out file operations CdReleaseFile +// CdAcquireFileShared Files for file operations CdReleaseFile +// CdAcquireFcbExclusive Fcb for open/close CdReleaseFcb +// CdAcquireFcbShared Fcb for open/close CdReleaseFcb +// CdLockCdData Fields in CdData CdUnlockCdData +// CdLockVcb Vcb fields, FcbReference, FcbTable CdUnlockVcb +// CdLockFcb Fcb fields, prefix table, Mcb CdUnlockFcb +// + +typedef enum _TYPE_OF_ACQUIRE { + + AcquireExclusive, + AcquireShared, + AcquireSharedStarveExclusive + +} TYPE_OF_ACQUIRE, *PTYPE_OF_ACQUIRE; + +_Requires_lock_held_(_Global_critical_region_) +_When_(Type == AcquireExclusive && return != FALSE, _Acquires_exclusive_lock_(*Resource)) +_When_(Type == AcquireShared && return != FALSE, _Acquires_shared_lock_(*Resource)) +_When_(Type == AcquireSharedStarveExclusive && return != FALSE, _Acquires_shared_lock_(*Resource)) +_When_(IgnoreWait == FALSE, _Post_satisfies_(return == TRUE)) +BOOLEAN +CdAcquireResource ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PERESOURCE Resource, + _In_ BOOLEAN IgnoreWait, + _In_ TYPE_OF_ACQUIRE Type + ); + +// +// BOOLEAN +// CdAcquireCdData ( +// _In_ PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdReleaseCdData ( +// _In_ PIRP_CONTEXT IrpContext +// ); +// +// BOOLEAN +// CdAcquireVcbExclusive ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PVCB Vcb, +// _In_ BOOLEAN IgnoreWait +// ); +// +// BOOLEAN +// CdAcquireVcbShared ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PVCB Vcb, +// _In_ BOOLEAN IgnoreWait +// ); +// +// VOID +// CdReleaseVcb ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PVCB Vcb +// ); +// +// VOID +// CdAcquireAllFiles ( +// _In_ PIRP_CONTEXT, +// _In_ PVCB Vcb +// ); +// +// VOID +// CdReleaseAllFiles ( +// _In_ PIRP_CONTEXT, +// _In_ PVCB Vcb +// ); +// +// VOID +// CdAcquireFileExclusive ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb, +// ); +// +// VOID +// CdAcquireFileShared ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// +// VOID +// CdReleaseFile ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// +// BOOLEAN +// CdAcquireFcbExclusive ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb, +// _In_ BOOLEAN IgnoreWait +// ); +// +// BOOLEAN +// CdAcquireFcbShared ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb, +// _In_ BOOLEAN IgnoreWait +// ); +// +// BOOLEAN +// CdReleaseFcb ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// +// VOID +// CdLockCdData ( +// ); +// +// VOID +// CdUnlockCdData ( +// ); +// +// VOID +// CdLockVcb ( +// _In_ PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdUnlockVcb ( +// _In_ PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdLockFcb ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// +// VOID +// CdUnlockFcb ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// + + +#define CdAcquireCacheForRead( IC) \ + ExAcquireResourceSharedLite( &(IC)->Vcb->SectorCacheResource, TRUE) + +#define CdAcquireCacheForUpdate( IC) \ + ExAcquireResourceExclusiveLite( &(IC)->Vcb->SectorCacheResource, TRUE) + +#define CdReleaseCache( IC) \ + ExReleaseResourceLite( &(IC)->Vcb->SectorCacheResource); + +#define CdConvertCacheToShared( IC) \ + ExConvertExclusiveToSharedLite( &(IC)->Vcb->SectorCacheResource); + +#define CdAcquireCdData(IC) \ + ExAcquireResourceExclusiveLite( &CdData.DataResource, TRUE ) + +#define CdReleaseCdData(IC) \ + ExReleaseResourceLite( &CdData.DataResource ) + +#define CdAcquireVcbExclusive(IC,V,I) \ + CdAcquireResource( (IC), &(V)->VcbResource, (I), AcquireExclusive ) + +#define CdAcquireVcbShared(IC,V,I) \ + CdAcquireResource( (IC), &(V)->VcbResource, (I), AcquireShared ) + +#define CdReleaseVcb(IC,V) \ + ExReleaseResourceLite( &(V)->VcbResource ) + +#define CdAcquireAllFiles(IC,V) \ + CdAcquireResource( (IC), &(V)->FileResource, FALSE, AcquireExclusive ) + +#define CdReleaseAllFiles(IC,V) \ + ExReleaseResourceLite( &(V)->FileResource ) + +#define CdAcquireFileExclusive(IC,F) \ + CdAcquireResource( (IC), (F)->Resource, FALSE, AcquireExclusive ) + +#define CdAcquireFileShared(IC,F) \ + CdAcquireResource( (IC), (F)->Resource, FALSE, AcquireShared ) + +#define CdAcquireFileSharedStarveExclusive(IC,F) \ + CdAcquireResource( (IC), (F)->Resource, FALSE, AcquireSharedStarveExclusive ) + +#define CdReleaseFile(IC,F) \ + ExReleaseResourceLite( (F)->Resource ) + +#define CdAcquireFcbExclusive(IC,F,I) \ + CdAcquireResource( (IC), &(F)->FcbNonpaged->FcbResource, (I), AcquireExclusive ) + +#define CdAcquireFcbShared(IC,F,I) \ + CdAcquireResource( (IC), &(F)->FcbNonpaged->FcbResource, (I), AcquireShared ) + +#define CdReleaseFcb(IC,F) \ + ExReleaseResourceLite( &(F)->FcbNonpaged->FcbResource ) + +#define CdLockCdData() \ + ExAcquireFastMutex( &CdData.CdDataMutex ); \ + CdData.CdDataLockThread = PsGetCurrentThread() + +#define CdUnlockCdData() \ + CdData.CdDataLockThread = NULL; \ + ExReleaseFastMutex( &CdData.CdDataMutex ) + +#define CdLockVcb(IC,V) \ + ExAcquireFastMutex( &(V)->VcbMutex ); \ + NT_ASSERT( NULL == (V)->VcbLockThread); \ + (V)->VcbLockThread = PsGetCurrentThread() + +#define CdUnlockVcb(IC,V) \ + NT_ASSERT( NULL != (V)->VcbLockThread); \ + (V)->VcbLockThread = NULL; \ + ExReleaseFastMutex( &(V)->VcbMutex ) + +#if defined(_PREFAST_) + +_Success_(return) +_IRQL_saves_global_(OldIrql, FastMutex) +BOOLEAN DummySaveIrql(_Inout_ PFAST_MUTEX FastMutex); + +_Success_(return) +_IRQL_restores_global_(OldIrql, FastMutex) +BOOLEAN DummyRestoreIrql(_Inout_ PFAST_MUTEX FastMutex); +#endif // _PREFAST_ + +#define CdLockFcb(IC,F) { \ + PVOID _CurrentThread = PsGetCurrentThread(); \ + if (_CurrentThread != (F)->FcbLockThread) { \ + ExAcquireFastMutex( &(F)->FcbNonpaged->FcbMutex ); \ + NT_ASSERT( (F)->FcbLockCount == 0 ); \ + _Analysis_assume_( (F)->FcbLockCount == 0 ); \ + (F)->FcbLockThread = _CurrentThread; \ + } \ + else \ + { \ + _Analysis_assume_lock_held_( (F)->FcbNonpaged->FcbMutex ); \ + _Analysis_assume_(FALSE != DummySaveIrql(&(F)->FcbNonpaged->FcbMutex)); \ + } \ + (F)->FcbLockCount += 1; \ +} + +#define CdUnlockFcb(IC,F) { \ + (F)->FcbLockCount -= 1; \ + if ((F)->FcbLockCount == 0) { \ + (F)->FcbLockThread = NULL; \ + ExReleaseFastMutex( &(F)->FcbNonpaged->FcbMutex ); \ + } \ + else \ + { \ + _Analysis_assume_lock_not_held_( (F)->FcbNonpaged->FcbMutex ); \ + _Analysis_assume_(FALSE != DummyRestoreIrql(&(F)->FcbNonpaged->FcbMutex)); \ + } \ +} + +// +// The following macro is used to retrieve the oplock structure within +// the Fcb. This structure was moved to the advanced Fcb header +// in Win8. +// + +#if (NTDDI_VERSION >= NTDDI_WIN8) + +#define CdGetFcbOplock(F) &(F)->Header.Oplock + +#else + +#define CdGetFcbOplock(F) &(F)->Oplock + +#endif + +BOOLEAN +CdNoopAcquire ( + _In_ PVOID Fcb, + _In_ BOOLEAN Wait + ); + +VOID +CdNoopRelease ( + _In_ PVOID Fcb + ); + +_Requires_lock_held_(_Global_critical_region_) +_When_(return!=0, _Acquires_shared_lock_(*Fcb->Resource)) +BOOLEAN +CdAcquireForCache ( + _Inout_ PFCB Fcb, + _In_ BOOLEAN Wait + ); + +_Requires_lock_held_(_Global_critical_region_) +_Releases_lock_(*Fcb->Resource) +VOID +CdReleaseFromCache ( + _Inout_ PFCB Fcb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdFilterCallbackAcquireForCreateSection ( + _In_ PFS_FILTER_CALLBACK_DATA CallbackData, + _Unreferenced_parameter_ PVOID *CompletionContext + ); + +_Function_class_(FAST_IO_RELEASE_FILE) +_Requires_lock_held_(_Global_critical_region_) +VOID +CdReleaseForCreateSection ( + _In_ PFILE_OBJECT FileObject + ); + + +// +// In-memory structure support routines. Implemented in StrucSup.c +// + +VOID +CdInitializeVcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _In_ __drv_aliasesMem PDEVICE_OBJECT TargetDeviceObject, + _In_ __drv_aliasesMem PVPB Vpb, + _In_ __drv_aliasesMem PCDROM_TOC_LARGE CdromToc, + _In_ ULONG TocLength, + _In_ ULONG TocTrackCount, + _In_ ULONG TocDiskFlags, + _In_ ULONG BlockFactor, + _In_ ULONG MediaChangeCount + ); + +VOID +CdUpdateVcbFromVolDescriptor ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _In_reads_bytes_opt_(SECTOR_SIZE) PCHAR RawIsoVd + ); + +VOID +CdDeleteVcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb + ); + +PFCB +CdCreateFcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ FILE_ID FileId, + _In_ NODE_TYPE_CODE NodeTypeCode, + _Out_opt_ PBOOLEAN FcbExisted + ); + +VOID +CdInitializeFcbFromPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_opt_ PFCB ParentFcb, + _In_ PPATH_ENTRY PathEntry + ); + +VOID +CdInitializeFcbFromFileContext ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ PFCB ParentFcb, + _In_ PFILE_ENUM_CONTEXT FileContext + ); + +PCCB +CdCreateCcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ ULONG Flags + ); + +VOID +CdDeleteCcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ __drv_freesMem( Pool ) PCCB Ccb + ); + +_When_(RaiseOnError || return, _At_(Fcb->FileLock, _Post_notnull_)) +_When_(RaiseOnError, _At_(IrpContext, _Pre_notnull_)) +BOOLEAN +CdCreateFileLock ( + _In_opt_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ BOOLEAN RaiseOnError + ); + +VOID +CdDeleteFileLock ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_LOCK FileLock + ); + +_Ret_valid_ PIRP_CONTEXT +CdCreateIrpContext ( + _In_ PIRP Irp, + _In_ BOOLEAN Wait + ); + +VOID +CdCleanupIrpContext ( + _In_ PIRP_CONTEXT IrpContext, + _In_ BOOLEAN Post + ); + +VOID +CdInitializeStackIrpContext ( + _Out_ PIRP_CONTEXT IrpContext, + _In_ PIRP_CONTEXT_LITE IrpContextLite + ); + +// +// PIRP_CONTEXT_LITE +// CdCreateIrpContextLite ( +// _In_ PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdFreeIrpContextLite ( +// _Inout_ PIRP_CONTEXT_LITE IrpContextLite +// ); +// + +#define CdCreateIrpContextLite(IC) \ + ExAllocatePoolWithTag( CdNonPagedPool, sizeof( IRP_CONTEXT_LITE ), TAG_IRP_CONTEXT_LITE ) + +#define CdFreeIrpContextLite(ICL) \ + CdFreePool( &(ICL) ) + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdTeardownStructures ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB StartingFcb, + _Out_ PBOOLEAN RemovedStartingFcb + ); + +// +// VOID +// CdIncrementCleanupCounts ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// +// VOID +// CdDecrementCleanupCounts ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// +// VOID +// CdIncrementReferenceCounts ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb, +// _In_ ULONG ReferenceCount +// _In_ ULONG UserReferenceCount +// ); +// +// VOID +// CdDecrementReferenceCounts ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb, +// _In_ ULONG ReferenceCount +// _In_ ULONG UserReferenceCount +// ); +// +// VOID +// CdIncrementFcbReference ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// +// VOID +// CdDecrementFcbReference ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// + +#define CdIncrementCleanupCounts(IC,F) { \ + ASSERT_LOCKED_VCB( (F)->Vcb ); \ + (F)->FcbCleanup += 1; \ + (F)->Vcb->VcbCleanup += 1; \ +} + +#define CdDecrementCleanupCounts(IC,F) { \ + ASSERT_LOCKED_VCB( (F)->Vcb ); \ + (F)->FcbCleanup -= 1; \ + (F)->Vcb->VcbCleanup -= 1; \ +} + +#define CdIncrementReferenceCounts(IC,F,C,UC) { \ + ASSERT_LOCKED_VCB( (F)->Vcb ); \ + (F)->FcbReference += (C); \ + (F)->FcbUserReference += (UC); \ + (F)->Vcb->VcbReference += (C); \ + (F)->Vcb->VcbUserReference += (UC); \ +} + +#define CdDecrementReferenceCounts(IC,F,C,UC) { \ + ASSERT_LOCKED_VCB( (F)->Vcb ); \ + (F)->FcbReference -= (C); \ + (F)->FcbUserReference -= (UC); \ + (F)->Vcb->VcbReference -= (C); \ + (F)->Vcb->VcbUserReference -= (UC); \ +} + +// +// PCD_IO_CONTEXT +// CdAllocateIoContext ( +// ); +// +// VOID +// CdFreeIoContext ( +// PCD_IO_CONTEXT IoContext +// ); +// + +#define CdAllocateIoContext() \ + FsRtlAllocatePoolWithTag( CdNonPagedPool, \ + sizeof( CD_IO_CONTEXT ), \ + TAG_IO_CONTEXT ) + +#define CdFreeIoContext(IO) CdFreePool( &(IO) ) + +PFCB +CdLookupFcbTable ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _In_ FILE_ID FileId + ); + +PFCB +CdGetNextFcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _In_ PVOID *RestartKey + ); + +NTSTATUS +CdProcessToc ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PDEVICE_OBJECT TargetDeviceObject, + _In_ PCDROM_TOC_LARGE CdromToc, + _Inout_ PULONG Length, + _Out_ PULONG TrackCount, + _Inout_ PULONG DiskFlags + ); + +// +// For debugging purposes we sometimes want to allocate our structures from nonpaged +// pool so that in the kernel debugger we can walk all the structures. +// + +#define CdPagedPool PagedPool +#define CdNonPagedPool NonPagedPoolNx +#define CdNonPagedPoolCacheAligned NonPagedPoolNxCacheAligned + + +// +// Verification support routines. Contained in verfysup.c +// + + +INLINE +BOOLEAN +CdOperationIsDasdOpen ( + _In_ PIRP_CONTEXT IrpContext + ) +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->Irp); + + return ((IrpContext->MajorFunction == IRP_MJ_CREATE) && + (IrpSp->FileObject->FileName.Length == 0) && + (IrpSp->FileObject->RelatedFileObject == NULL)); +} + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdPerformVerify ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _In_ PDEVICE_OBJECT DeviceToVerify + ); + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +CdCheckForDismount ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _In_ BOOLEAN Force + ); + +BOOLEAN +CdMarkDevForVerifyIfVcbMounted ( + _Inout_ PVCB Vcb + ); + +VOID +CdVerifyVcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb + ); + +BOOLEAN +CdVerifyFcbOperation ( + _In_opt_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb + ); + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +CdDismountVcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb + ); + + +// +// Macros to abstract device verify flag changes. +// + +#define CdUpdateMediaChangeCount( V, C) (V)->MediaChangeCount = (C) +#define CdUpdateVcbCondition( V, C) (V)->VcbCondition = (C) + +#define CdMarkRealDevForVerify( DO) SetFlag( (DO)->Flags, DO_VERIFY_VOLUME) + +#define CdMarkRealDevVerifyOk( DO) ClearFlag( (DO)->Flags, DO_VERIFY_VOLUME) + + +#define CdRealDevNeedsVerify( DO) BooleanFlagOn( (DO)->Flags, DO_VERIFY_VOLUME) + +// +// BOOLEAN +// CdIsRawDevice ( +// _In_ PIRP_CONTEXT IrpContext, +// _In_ NTSTATUS Status +// ); +// + +#define CdIsRawDevice(IC,S) ( \ + ((S) == STATUS_DEVICE_NOT_READY) || \ + ((S) == STATUS_NO_MEDIA_IN_DEVICE) \ +) + + +// +// Work queue routines for posting and retrieving an Irp, implemented in +// workque.c +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdFsdPostRequest ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdPrePostIrp ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdOplockComplete ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + + +// +// Miscellaneous support routines +// + +// +// This macro returns TRUE if a flag in a set of flags is on and FALSE +// otherwise +// + +//#ifndef BooleanFlagOn +//#define BooleanFlagOn(F,SF) ( \ +// (BOOLEAN)(((F) & (SF)) != 0) \ +//) +//#endif + +//#ifndef SetFlag +//#define SetFlag(Flags,SingleFlag) { \ +// (Flags) |= (SingleFlag); \ +//} +//#endif + +//#ifndef ClearFlag +//#define ClearFlag(Flags,SingleFlag) { \ +// (Flags) &= ~(SingleFlag); \ +//} +//#endif + +// +// CAST +// Add2Ptr ( +// _In_ PVOID Pointer, +// _In_ ULONG Increment +// _In_ (CAST) +// ); +// +// ULONG +// PtrOffset ( +// _In_ PVOID BasePtr, +// _In_ PVOID OffsetPtr +// ); +// + +#define Add2Ptr(PTR,INC,CAST) ((CAST)((PUCHAR)(PTR) + (INC))) + +#define PtrOffset(BASE,OFFSET) ((ULONG)((ULONG_PTR)(OFFSET) - (ULONG_PTR)(BASE))) + +// +// This macro takes a pointer (or ulong) and returns its rounded up word +// value +// + +#define WordAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 1) & 0xfffffffe) \ + ) + +// +// This macro takes a pointer (or ulong) and returns its rounded up longword +// value +// + +#define LongAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 3) & 0xfffffffc) \ + ) + +// +// This macro takes a pointer (or ulong) and returns its rounded up quadword +// value +// + +#define QuadAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 7) & 0xfffffff8) \ + ) + +// +// The following macros round up and down to sector boundaries. +// + +#define SectorAlign(L) ( \ + ((((ULONG)(L)) + (SECTOR_SIZE - 1)) & ~(SECTOR_SIZE - 1)) \ +) + +#define LlSectorAlign(L) ( \ + ((((LONGLONG)(L)) + (SECTOR_SIZE - 1)) & ~(SECTOR_SIZE - 1)) \ +) + +#define SectorTruncate(L) ( \ + ((ULONG)(L)) & ~(SECTOR_SIZE - 1) \ +) + +#define LlSectorTruncate(L) ( \ + ((LONGLONG)(L)) & ~(SECTOR_SIZE - 1) \ +) + +#define BytesFromSectors(L) ( \ + ((ULONG) (L)) << SECTOR_SHIFT \ +) + +#define SectorsFromBytes(L) ( \ + ((ULONG) (L)) >> SECTOR_SHIFT \ +) + +INLINE +ULONG +SectorsFromLlBytes( + ULONGLONG Bytes +) { + + return (ULONG)(Bytes >> SECTOR_SHIFT); +} + +#define LlBytesFromSectors(L) ( \ + Int64ShllMod32( (LONGLONG)(L), SECTOR_SHIFT ) \ +) + +#define LlSectorsFromBytes(L) ( \ + Int64ShraMod32( (LONGLONG)(L), SECTOR_SHIFT ) \ +) + +#define SectorOffset(L) ( \ + ((ULONG)(ULONG_PTR) (L)) & SECTOR_MASK \ +) + +#define SectorBlockOffset(V,LB) ( \ + ((ULONG) (LB)) & ((V)->BlocksPerSector - 1) \ +) + +#define BytesFromBlocks(V,B) ( \ + (ULONG) (B) << (V)->BlockToByteShift \ +) + +#define LlBytesFromBlocks(V,B) ( \ + Int64ShllMod32( (LONGLONG) (B), (V)->BlockToByteShift ) \ +) + +#define BlockAlign(V,L) ( \ + ((ULONG)(L) + (V)->BlockMask) & (V)->BlockInverseMask \ +) + +// +// Carefully make sure the mask is sign extended to 64bits +// + +#define LlBlockAlign(V,L) ( \ + ((LONGLONG)(L) + (V)->BlockMask) & (LONGLONG)((LONG)(V)->BlockInverseMask) \ +) + +#define BlockOffset(V,L) ( \ + ((ULONG) (L)) & (V)->BlockMask \ +) + +#define RawSectorAlign( B) ((((B)+(RAW_SECTOR_SIZE - 1)) / RAW_SECTOR_SIZE) * RAW_SECTOR_SIZE) + +// +// The following types and macros are used to help unpack the packed and +// misaligned fields found in the Bios parameter block +// + +typedef union _UCHAR1 { + UCHAR Uchar[1]; + UCHAR ForceAlignment; +} UCHAR1, *PUCHAR1; + +typedef union _UCHAR2 { + UCHAR Uchar[2]; + USHORT ForceAlignment; +} UCHAR2, *PUCHAR2; + +typedef union _UCHAR4 { + UCHAR Uchar[4]; + ULONG ForceAlignment; +} UCHAR4, *PUCHAR4; + +typedef union _USHORT2 { + USHORT Ushort[2]; + ULONG ForceAlignment; +} USHORT2, *PUSHORT2; + +// +// This macro copies an unaligned src byte to an aligned dst byte +// + +#define CopyUchar1(Dst,Src) { \ + *((UCHAR1 *)(Dst)) = *((UNALIGNED UCHAR1 *)(Src)); \ + } + +// +// This macro copies an unaligned src word to an aligned dst word +// + +#define CopyUchar2(Dst,Src) { \ + *((UCHAR2 *)(Dst)) = *((UNALIGNED UCHAR2 *)(Src)); \ + } + +// +// This macro copies an unaligned src longword to an aligned dsr longword +// + +#define CopyUchar4(Dst,Src) { \ + *((UCHAR4 *)(Dst)) = *((UNALIGNED UCHAR4 *)(Src)); \ + } + +// +// This macro copies an unaligned src longword to an aligned dsr longword +// accessing the source on a word boundary. +// + +#define CopyUshort2(Dst,Src) { \ + *((USHORT2 *)(Dst)) = *((UNALIGNED USHORT2 *)(Src));\ + } + +// +// This macro copies an unaligned src longword to a dst longword, +// performing an little/big endian swap. +// + +#define SwapCopyUchar4(Dst,Src) { \ + *((UNALIGNED UCHAR1 *)(Dst)) = *((UNALIGNED UCHAR1 *)(Src) + 3); \ + *((UNALIGNED UCHAR1 *)(Dst) + 1) = *((UNALIGNED UCHAR1 *)(Src) + 2); \ + *((UNALIGNED UCHAR1 *)(Dst) + 2) = *((UNALIGNED UCHAR1 *)(Src) + 1); \ + *((UNALIGNED UCHAR1 *)(Dst) + 3) = *((UNALIGNED UCHAR1 *)(Src)); \ +} + +VOID +CdLbnToMmSsFf ( + _In_ ULONG Blocks, + _Out_writes_(3) PUCHAR Msf + ); + +// +// Following routines handle entry in and out of the filesystem. They are +// contained in CdData.c +// + +// NTSTATUS +// CdFsdDispatch ( +// _In_ PDEVICE_OBJECT DeviceObject, +// _Inout_ PIRP Irp +// ); + +DRIVER_DISPATCH CdFsdDispatch; + +LONG +CdExceptionFilter ( + _Inout_ PIRP_CONTEXT IrpContext, + _In_ PEXCEPTION_POINTERS ExceptionPointer + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdProcessException ( + _In_opt_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _In_ NTSTATUS ExceptionCode + ); + +VOID +CdCompleteRequest ( + _Inout_opt_ PIRP_CONTEXT IrpContext, + _Inout_opt_ PIRP Irp, + _In_ NTSTATUS Status + ); + +// +// VOID +// CdRaiseStatus ( +// _In_ PRIP_CONTEXT IrpContext, +// _In_ NT_STATUS Status +// ); +// +// VOID +// CdNormalizeAndRaiseStatus ( +// _In_ PRIP_CONTEXT IrpContext, +// _In_ NT_STATUS Status +// ); +// + +#if 0 +#define AssertVerifyDevice(C, S) \ + NT_ASSERT( (C) == NULL || \ + FlagOn( (C)->Flags, IRP_CONTEXT_FLAG_IN_FSP ) || \ + !((S) == STATUS_VERIFY_REQUIRED && \ + IoGetDeviceToVerify( PsGetCurrentThread() ) == NULL )); + +#define AssertVerifyDeviceIrp(I) \ + NT_ASSERT( (I) == NULL || \ + !(((I)->IoStatus.Status) == STATUS_VERIFY_REQUIRED && \ + ((I)->Tail.Overlay.Thread == NULL || \ + IoGetDeviceToVerify( (I)->Tail.Overlay.Thread ) == NULL ))); +#else +#define AssertVerifyDevice(C, S) +#define AssertVerifyDeviceIrp(I) +#endif + + +#ifdef CD_SANITY + +DECLSPEC_NORETURN +VOID +CdRaiseStatusEx ( + _In_ PIRP_CONTEXT IrpContext, + _In_ NTSTATUS Status, + _In_ BOOLEAN NormalizeStatus, + _In_opt_ ULONG FileId, + _In_opt_ ULONG Line + ); + +#else + +INLINE +DECLSPEC_NORETURN +VOID +CdRaiseStatusEx( + _In_ PIRP_CONTEXT IrpContext, + _In_ NTSTATUS Status, + _In_ BOOLEAN NormalizeStatus, + _In_ ULONG Fileid, + _In_ ULONG Line + ) +{ + if (NormalizeStatus) { + + IrpContext->ExceptionStatus = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR); + } + else { + + IrpContext->ExceptionStatus = Status; + } + + IrpContext->RaisedAtLineFile = (Fileid << 16) | Line; + + ExRaiseStatus( IrpContext->ExceptionStatus ); +} + +#endif + +#define CdRaiseStatus( IC, S) CdRaiseStatusEx( (IC), (S), FALSE, BugCheckFileId, __LINE__); +#define CdNormalizeAndRaiseStatus( IC, S) CdRaiseStatusEx( (IC), (S), TRUE, BugCheckFileId, __LINE__); + +// +// Following are the fast entry points. +// + +// _Success_(return != FALSE) +// BOOLEAN +// CdFastQueryBasicInfo ( +// _In_ PFILE_OBJECT FileObject, +// _In_ BOOLEAN Wait, +// _Out_ PFILE_BASIC_INFORMATION Buffer, +// _Out_ PIO_STATUS_BLOCK IoStatus, +// _In_ PDEVICE_OBJECT DeviceObject +// ); + +FAST_IO_QUERY_BASIC_INFO CdFastQueryBasicInfo; + +// _Success_(return != FALSE) +// BOOLEAN +// CdFastQueryStdInfo ( +// _In_ PFILE_OBJECT FileObject, +// _In_ BOOLEAN Wait, +// _Out_ PFILE_STANDARD_INFORMATION Buffer, +// _Out_ PIO_STATUS_BLOCK IoStatus, +// _In_ PDEVICE_OBJECT DeviceObject +// ); + +FAST_IO_QUERY_STANDARD_INFO CdFastQueryStdInfo; + +// BOOLEAN +// CdFastLock ( +// _In_ PFILE_OBJECT FileObject, +// _In_ PLARGE_INTEGER FileOffset, +// _In_ PLARGE_INTEGER Length, +// _In_ PEPROCESS ProcessId, +// _In_ ULONG Key, +// _In_ BOOLEAN FailImmediately, +// _In_ BOOLEAN ExclusiveLock, +// _Out_ PIO_STATUS_BLOCK IoStatus, +// _In_ PDEVICE_OBJECT DeviceObject +// ); + +FAST_IO_LOCK CdFastLock; + +// BOOLEAN +// CdFastUnlockSingle ( +// _In_ PFILE_OBJECT FileObject, +// _In_ PLARGE_INTEGER FileOffset, +// _In_ PLARGE_INTEGER Length, +// _In_ PEPROCESS ProcessId, +// _In_ ULONG Key, +// _Out_ PIO_STATUS_BLOCK IoStatus, +// _In_ PDEVICE_OBJECT DeviceObject +// ); + +FAST_IO_UNLOCK_SINGLE CdFastUnlockSingle; + +// BOOLEAN +// CdFastUnlockAll ( +// _In_ PFILE_OBJECT FileObject, +// _In_ PEPROCESS ProcessId, +// _Out_ PIO_STATUS_BLOCK IoStatus, +// _In_ PDEVICE_OBJECT DeviceObject +// ); + +FAST_IO_UNLOCK_ALL CdFastUnlockAll; + +// BOOLEAN +// CdFastUnlockAllByKey ( +// _In_ PFILE_OBJECT FileObject, +// _In_ PVOID ProcessId, +// _In_ ULONG Key, +// _Out_ PIO_STATUS_BLOCK IoStatus, +// _In_ PDEVICE_OBJECT DeviceObject +// ); + +FAST_IO_UNLOCK_ALL_BY_KEY CdFastUnlockAllByKey; + +// BOOLEAN +// CdFastIoCheckIfPossible ( +// _In_ PFILE_OBJECT FileObject, +// _In_ PLARGE_INTEGER FileOffset, +// _In_ ULONG Length, +// _In_ BOOLEAN Wait, +// _In_ ULONG LockKey, +// _In_ BOOLEAN CheckForReadOperation, +// _Out_ PIO_STATUS_BLOCK IoStatus, +// _In_ PDEVICE_OBJECT DeviceObject +// ); + +FAST_IO_CHECK_IF_POSSIBLE CdFastIoCheckIfPossible; + +// _Success_(return != FALSE) +// BOOLEAN +// CdFastQueryNetworkInfo ( +// _In_ PFILE_OBJECT FileObject, +// _In_ BOOLEAN Wait, +// _Out_ PFILE_NETWORK_OPEN_INFORMATION Buffer, +// _Out_ PIO_STATUS_BLOCK IoStatus, +// _In_ PDEVICE_OBJECT DeviceObject +// ); + +FAST_IO_QUERY_NETWORK_OPEN_INFO CdFastQueryNetworkInfo; + +// +// Following are the routines to handle the top level thread logic. +// + +VOID +CdSetThreadContext ( + _Inout_ PIRP_CONTEXT IrpContext, + _In_ PTHREAD_CONTEXT ThreadContext + ); + + +// +// VOID +// CdRestoreThreadContext ( +// _Inout_ PIRP_CONTEXT IrpContext +// ); +// + +#define CdRestoreThreadContext(IC) \ + (IC)->ThreadContext->Cdfs = 0; \ + IoSetTopLevelIrp( (IC)->ThreadContext->SavedTopLevelIrp ); \ + (IC)->ThreadContext = NULL + +ULONG +CdSerial32 ( + _In_reads_bytes_(ByteCount) PCHAR Buffer, + _In_ ULONG ByteCount + ); + +// +// The following macro is used to determine if an FSD thread can block +// for I/O or wait for a resource. It returns TRUE if the thread can +// block and FALSE otherwise. This attribute can then be used to call +// the FSD & FSP common work routine with the proper wait value. +// + +#define CanFsdWait(I) IoIsOperationSynchronous(I) + +// +// The following macro is used to set the fast i/o possible bits in the +// FsRtl header. +// +// FastIoIsNotPossible - If the Fcb is bad or there are oplocks on the file. +// +// FastIoIsQuestionable - If there are file locks. +// +// FastIoIsPossible - In all other cases. +// +// + +#define CdIsFastIoPossible(F) ((BOOLEAN) \ + ((((F)->Vcb->VcbCondition != VcbMounted ) || \ + !FsRtlOplockIsFastIoPossible( CdGetFcbOplock(F) )) ? \ + \ + FastIoIsNotPossible : \ + \ + ((((F)->FileLock != NULL) && FsRtlAreThereCurrentFileLocks( (F)->FileLock )) ? \ + \ + FastIoIsQuestionable : \ + \ + FastIoIsPossible)) \ +) + + +// +// The FSP level dispatch/main routine. This is the routine that takes +// IRP's off of the work queue and calls the appropriate FSP level +// work routine. +// + +// VOID +// CdFspDispatch ( // implemented in FspDisp.c +// _Inout_ PIRP_CONTEXT IrpContext +// ); + +WORKER_THREAD_ROUTINE CdFspDispatch; + +VOID +CdFspClose ( // implemented in Close.c + _In_opt_ PVCB Vcb + ); + +// +// The following routines are the entry points for the different operations +// based on the IrpSp major functions. +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonCreate ( // Implemented in Create.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonClose ( // Implemented in Close.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonRead ( // Implemented in Read.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonWrite ( // Implemented in Write.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonQueryInfo ( // Implemented in FileInfo.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonSetInfo ( // Implemented in FileInfo.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonQueryVolInfo ( // Implemented in VolInfo.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonDirControl ( // Implemented in DirCtrl.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonFsControl ( // Implemented in FsCtrl.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +NTSTATUS +CdCommonDevControl ( // Implemented in DevCtrl.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +NTSTATUS +CdCommonLockControl ( // Implemented in LockCtrl.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonCleanup ( // Implemented in Cleanup.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonPnp ( // Implemented in Pnp.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonShutdown ( // Implemented in Shutdown.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + + + +// +// The following macros are used to establish the semantics needed +// to do a return from within a try-finally clause. As a rule every +// try clause must end with a label call try_exit. For example, +// +// try { +// : +// : +// +// try_exit: NOTHING; +// } finally { +// +// : +// : +// } +// +// Every return statement executed inside of a try clause should use the +// try_return macro. If the compiler fully supports the try-finally construct +// then the macro should be +// +// #define try_return(S) { return(S); } +// +// If the compiler does not support the try-finally construct then the macro +// should be +// +// #define try_return(S) { S; goto try_exit; } +// + +#define try_return(S) { S; goto try_exit; } +#define try_leave(S) { S; leave; } + +// +// Encapsulate safe pool freeing +// + +INLINE +VOID +CdFreePool( + _Inout_ _At_(*Pool, __drv_freesMem(Mem) _Post_null_) PVOID *Pool + ) +{ + if (*Pool != NULL) { + + ExFreePool(*Pool); + *Pool = NULL; + } +} + +#ifdef CDFS_TELEMETRY_DATA + +// +// CDFS Telemetry. Current implementation uses the Telemetry TraceLogging APIs. +// +// The Telemetry TraceLoggingWrite() routines use a lot of stack space. We must +// therefor wrap all our telemetry points with our own routines, and add a guard to +// make sure there's enough stack space to call these routines. +// +// These telemetry routines should not be called on high-performance code paths. +// + +TRACELOGGING_DECLARE_PROVIDER( CdTelemetryProvider ); + +VOID +CdInitializeTelemetry ( + VOID + ); + +DECLSPEC_NOINLINE +VOID +CdTelemetryMount ( + __in PGUID VolumeGuid, + __in NTSTATUS Status, + __in PVCB Vcb + ); + +// +// Every additional argument passed to TraceLoggingWrite() consumes an additional +// 16 to 32 bytes extra stack space. Having 512 bytes reserved space should be +// sufficient for up to 20 arguments or so. This will be less of course if our +// wrapper routines also declare their own local variables. +// + +#define CDFS_TELEMETRY_STACK_THRESHOLD_DEFAULT 512 // for "small" telemetry points +#define CDFS_TELEMETRY_STACK_THRESHOLD_LARGE 2048 // for "large" telemetry points + +INLINE +BOOLEAN +CdTelemetryGuard ( + __in ULONG StackSpaceNeeded ) +/*++ + +Routine Description: + + This routine returns TRUE only when: + + 1) There is an ETW listener, AND + 2) There is enough free stack space to safely call the Telemetry TraceLogging APIs + + We'll also count how many times there wasn't enough stack space, and include this + value as part of the periodic cdfs Telemetry. + +Arguments: + + StackSpaceNeeded - Stack space needed in bytes + +--*/ +{ + ASSERT( IoGetRemainingStackSize() >= StackSpaceNeeded ); + + if (CdTelemetryProvider->LevelPlus1 <= 5) { + + // + // Bail out early if there are no ETW listeners + // + + return FALSE; + } + + if (IoGetRemainingStackSize() < StackSpaceNeeded) { + + // + // Count how many times it was unsafe to generate telemetry because of + // not enough stack space. + // + + InterlockedIncrement( &CdTelemetryData.MissedTelemetryPoints ); + + return FALSE; + } + + return TRUE; +} + +#define CdTelemetryMountSafe( VolumeGuid, Status, Vcb ) \ + if (CdTelemetryGuard( CDFS_TELEMETRY_STACK_THRESHOLD_LARGE )) { \ + CdTelemetryMount( VolumeGuid, Status, Vcb ); \ + } + +#if DBG +#define CDFS_TELEMETRY_PERIODIC_INTERVAL CdTelemetryData.PeriodicInterval +#else +#define CDFS_TELEMETRY_PERIODIC_INTERVAL INTERVAL_ONE_DAY +#endif + +#else // CDFS_TELEMETRY_DATA + +// +// When CDFS_TELEMETRY_DATA is not defined then the CdTelemetry___Safe() routines +// expand to nothing. This minimizes the cdfs.sys binary footprint. This also +// means that the places where these Safe() routines are called do not +// have to have to be surrounded by #ifdef CDFS_TELEMETRY_DATA .. #endif +// + + +#define CdTelemetryMountSafe( ... ) NOTHING + +#endif // CDFS_TELEMETRY_DATA + +#endif // _CDPROCS_ + + diff --git a/filesys/cdfs/cdprocssrc.c b/filesys/cdfs/cdprocssrc.c new file mode 100644 index 00000000..a370cab1 --- /dev/null +++ b/filesys/cdfs/cdprocssrc.c @@ -0,0 +1 @@ +#include "cdprocs.h" \ No newline at end of file diff --git a/filesys/cdfs/cdstruc.h b/filesys/cdfs/cdstruc.h new file mode 100644 index 00000000..2a356e76 --- /dev/null +++ b/filesys/cdfs/cdstruc.h @@ -0,0 +1,1898 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + CdStruc.h + +Abstract: + + This module defines the data structures that make up the major internal + part of the Cdfs file system. + + In-Memory structures: + + The global data structures with the CdDataRecord. It contains a pointer + to a File System Device object and a queue of Vcb's. There is a Vcb for + every currently or previously mounted volumes. We may be in the process + of tearing down the Vcb's which have been dismounted. The Vcb's are + allocated as an extension to a volume device object. + + +--------+ + | CdData | +--------+ + | | --> |FilSysDo| + | | | | + | | <+ +--------+ + +--------+ | + | + | +--------+ +--------+ + | |VolDo | |VolDo | + | | | | | + | +--------+ +--------+ + +> |Vcb | <-> |Vcb | <-> ... + | | | | + +--------+ +--------+ + + + Each Vcb contains a table of all the Fcbs for the volume indexed by + their FileId. Each Vcb contains a pointer to the root directory of + the volume. Each directory Fcb contains a queue of child Fcb's for + its children. There can also be detached subtrees due to open operations + by Id where the Fcb's are not connected to the root. + + The following diagram shows the root structure. + + +--------+ +--------+ + | Vcb |---->| Fcb |-----------------------------------------------+ + | | | Table |--------------------------------------------+ | | + | |--+ | |-----------------------------------------+ | | | + +--------+ | +--------+ | | | + | | | | | | | + | | | +--------------------+ | | | + | V +---------+ | | | | + | +--------+ | | | | | + | |RootFcb | V V | | | + +->| | +--------+ +--------+ | | | + | |-->|Child | |Child | | | | + +--------+ | Fcb |<-->| Fcb |<--> ... | | | + | | | | | | | + +--------+ +--------+ | | | + | | | + (Freestanding sub-tree) | | | + +--------+ | | | + |OpenById|<-----------------------------------------+ | | + | Dir | +--------+ | | + | |--->|OpenById|<------------------------------+ | + +--------+ | Child | +--------+ | + | Dir |--->|OpenById|<-------------------+ + +--------+ | Child | + | File | + +--------+ + + Attached to each Directory Fcb is a prefix table containing the names + of children of this directory for which there is an Fcb. Not all Fcb's + will necessarily have an entry in this table. + + +--------+ +--------+ + | Dir | | Prefix | + | Fcb |----->| Table |--------------------+ + | | | |-------+ | + +--------+ +--------+ | | + | | | | + | | | | + | V V V + | +--------+ +--------+ +--------+ +--------+ + | | Fcb | | Fcb | | Fcb | | Fcb | + +---------->| |<-->| |<-->| |<-->| | + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + + + Each file object open on a CDROM volume contains two context pointers. The + first will point back to the Fcb for the file object. The second, if present, + points to a Ccb (ContextControlBlock) which contains the per-handle information. + This includes the state of any directory enumeration. + + +--------+ +--------+ +--------+ + | Fcb |<------| File | | Ccb | + | | | Object|--->| | + | | | | | | + +--------+ +--------+ +--------+ + ^ ^ + | | +--------+ +--------+ + | | | File | | Ccb | + | +---------| Object|--->| | + | | | | | + | +--------+ +--------+ + | + | +--------+ + | |Stream | + +--------------| File | + | Object| + +--------+ + + + Synchronization: + + 1. A resource in the CdData synchronizes access to the Vcb queue. This + is used during mount/verify/dismount operations. + + 2. A resource in the Vcb is used to synchronize access to Vcb for + open/close operations. Typically acquired shared, it + is acquired exclusively to lock out these operations. + + 3. A second resource in the Vcb is used to synchronize all file operations. + Typically acquired shared, it is acquired exclusively to lock + out all file operations. Acquiring both Vcb resources will lock + the entire volume. + + 4. A resource in the nonpaged Fcb will synchronize open/close operations + on an Fcb. + + 5. A fast mutex in the Vcb will protect access to the Fcb table and + the open counts in the Vcb. It is also used to modify the reference + counts in all Fcbs. This mutex cannot be acquired + exclusely and is an end resource. + + 6. A fast mutex in the Fcb will synchronize access to all Fcb fields + which aren't synchronized in some other way. A thread may acquire + mutexes for multiple Fcb's as long as it works it way toward the + root of the tree. This mutex can also be acquired recursively. + + 7. Normal locking order is CdData/Vcb/Fcb starting at any point in this + chain. The Vcb is required prior to acquiring resources for multiple + files. Shared ownership of the Vcb is sufficient in this case. + + 8. Normal locking order when acquiring multiple Fcb's is from some + starting Fcb and walking towards the root of tree. Create typically + walks down the tree. In this case we will attempt to acquire the + next node optimistically and if that fails we will reference + the current node in the tree, release it and acquire the next node. + At that point it will be safe to reacquire the parent node. + + 9. Locking order for the Fcb (via the fast mutex) will be from leaf of + tree back towards the root. No other resource may be acquired + after locking the Vcb (other than in-page reads). + + 10. Cleanup operations only lock the Vcb and Fcb long enough to change the + critical counts and share access fields. No reason to synchronize + otherwise. None of the structures can go away from beneath us + in this case. + + +--*/ + +#ifndef _CDSTRUC_ +#define _CDSTRUC_ + +typedef PVOID PBCB; //**** Bcb's are now part of the cache module + +#define BYTE_COUNT_EMBEDDED_NAME (32) + + +// +// The CD_MCB is used to store the mapping of logical file offset to +// logical disk offset. NOTE - This package only deals with the +// logical 2048 sectors. Translating to 'raw' sectors happens in +// software. We will embed a single MCB_ENTRY in the Fcb since this +// will be the typical case. +// + +typedef struct _CD_MCB { + + // + // Size and current count of the Mcb entries. + // + + ULONG MaximumEntryCount; + ULONG CurrentEntryCount; + + // + // Pointer to the start of the Mcb entries. + // + + struct _CD_MCB_ENTRY *McbArray; + +} CD_MCB; +typedef CD_MCB *PCD_MCB; + +typedef struct _CD_MCB_ENTRY { + + // + // Starting offset and number of bytes described by this entry. + // The Byte count is rounded to a logical block boundary if this is + // the last block. + // + + LONGLONG DiskOffset; + LONGLONG ByteCount; + + // + // Starting offset in the file of mapping described by this dirent. + // + + LONGLONG FileOffset; + + // + // Data length and block length. Data length is the length of each + // data block. Total length is the length of each data block and + // the skip size. + // + + LONGLONG DataBlockByteCount; + LONGLONG TotalBlockByteCount; + +} CD_MCB_ENTRY; +typedef CD_MCB_ENTRY *PCD_MCB_ENTRY; + + +// +// Cd name structure. The following structure is used to represent the +// full Cdrom name. This name can be stored in either Unicode or ANSI +// format. +// + +typedef struct _CD_NAME { + + // + // String containing name without the version number. + // The maximum length field for filename indicates the + // size of the buffer allocated for the two parts of the name. + // + + UNICODE_STRING FileName; + + // + // String containging the version number. + // + + UNICODE_STRING VersionString; + +} CD_NAME; +typedef CD_NAME *PCD_NAME; + +// +// Following is the splay link structure for the prefix lookup. +// The names can be in either Unicode string or Ansi string format. +// + +typedef struct _NAME_LINK { + + RTL_SPLAY_LINKS Links; + UNICODE_STRING FileName; + +} NAME_LINK; +typedef NAME_LINK *PNAME_LINK; + + +// +// Prefix entry. There is one of these for each name in the prefix table. +// An Fcb will have one of these embedded for the long name and an optional +// pointer to the short name entry. +// + +typedef struct _PREFIX_ENTRY { + + // + // Pointer to the Fcb for this entry. + // + + struct _FCB *Fcb; + + // + // Flags field. Used to indicate if the name is in the prefix table. + // + + ULONG PrefixFlags; + + // + // Exact case name match. + // + + NAME_LINK ExactCaseName; + + // + // Case-insensitive name link. + // + + NAME_LINK IgnoreCaseName; + + WCHAR FileNameBuffer[ BYTE_COUNT_EMBEDDED_NAME ]; + +} PREFIX_ENTRY; +typedef PREFIX_ENTRY *PPREFIX_ENTRY; + +#define PREFIX_FLAG_EXACT_CASE_IN_TREE (0x00000001) +#define PREFIX_FLAG_IGNORE_CASE_IN_TREE (0x00000002) + + +// +// The CD_DATA record is the top record in the CDROM file system in-memory +// data structure. This structure must be allocated from non-paged pool. +// + +typedef struct _CD_DATA { + + // + // The type and size of this record (must be CDFS_NTC_DATA_HEADER) + // + + _Field_range_(==, CDFS_NTC_DATA_HEADER) NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // A pointer to the Driver object we were initialized with + // + + PDRIVER_OBJECT DriverObject; + + // + // Vcb queue. + // + + LIST_ENTRY VcbQueue; + + // + // The following fields are used to allocate IRP context structures + // using a lookaside list, and other fixed sized structures from a + // small cache. We use the CdData mutex to protext these structures. + // + + ULONG IrpContextDepth; + ULONG IrpContextMaxDepth; + SINGLE_LIST_ENTRY IrpContextList; + + // + // Filesystem device object for CDFS. + // + + PDEVICE_OBJECT FileSystemDeviceObject; + + // + // Following are used to manage the async and delayed close queue. + // + // FspCloseActive - Indicates whether there is a thread processing the + // two close queues. + // ReduceDelayedClose - Indicates that we have hit the upper threshold + // for the delayed close queue and need to reduce it to lower threshold. + // Flags - CD flags. + // AsyncCloseQueue - Queue of IrpContext waiting for async close operation. + // AsyncCloseCount - Number of entries on the async close queue. + // + // DelayedCloseQueue - Queue of IrpContextLite waiting for delayed close + // operation. + // MaxDelayedCloseCount - Trigger delay close work at this threshold. + // MinDelayedCloseCount - Turn off delay close work at this threshold. + // DelayedCloseCount - Number of entries on the delayted close queue. + // + // CloseItem - Workqueue item used to start FspClose thread. + // + + LIST_ENTRY AsyncCloseQueue; + ULONG AsyncCloseCount; + BOOLEAN FspCloseActive; + BOOLEAN ReduceDelayedClose; + USHORT Flags; + + // + // The following fields describe the deferred close file objects. + // + + LIST_ENTRY DelayedCloseQueue; + ULONG DelayedCloseCount; + ULONG MaxDelayedCloseCount; + ULONG MinDelayedCloseCount; + + // + // Fast mutex used to lock the fields of this structure. + // + + PVOID CdDataLockThread; + FAST_MUTEX CdDataMutex; + + // + // A resource variable to control access to the global CDFS data record + // + + ERESOURCE DataResource; + + // + // Cache manager call back structure, which must be passed on each call + // to CcInitializeCacheMap. + // + + CACHE_MANAGER_CALLBACKS CacheManagerCallbacks; + CACHE_MANAGER_CALLBACKS CacheManagerVolumeCallbacks; + + // + // This is the ExWorkerItem that does both kinds of deferred closes. + // + + PIO_WORKITEM CloseItem; + +} CD_DATA; +typedef CD_DATA *PCD_DATA; + + +#define CD_FLAGS_SHUTDOWN (0x0001) + + +// +// Since DVD drives allow > 100 "sessions", we need to use a larger TOC +// than the legacy CD definition. The maximum is theoretically 0xaa-16 (max +// number of open tracks in a session), but it's quite possible that some +// drive does not enforce this, so we'll go with 169 (track 0xaa is always the +// leadout). +// + +#define MAXIMUM_NUMBER_TRACKS_LARGE 0xAA + +typedef struct _CDROM_TOC_LARGE { + + // + // Header + // + + UCHAR Length[2]; // add two bytes for this field + UCHAR FirstTrack; + UCHAR LastTrack; + + // + // Track data + // + + TRACK_DATA TrackData[ MAXIMUM_NUMBER_TRACKS_LARGE]; + +} CDROM_TOC_LARGE, *PCDROM_TOC_LARGE; + +typedef struct _CD_SECTOR_CACHE_CHUNK { + + ULONG BaseLbn; + PUCHAR Buffer; + +} CD_SECTOR_CACHE_CHUNK, *PCD_SECTOR_CACHE_CHUNK; + +#define CD_SEC_CACHE_CHUNKS 4 +#define CD_SEC_CHUNK_BLOCKS 0x18 + +// +// The Vcb (Volume control block) record corresponds to every +// volume mounted by the file system. They are ordered in a queue off +// of CdData.VcbQueue. +// +// The Vcb will be in several conditions during its lifespan. +// +// NotMounted - Disk is not currently mounted (i.e. removed +// from system) but cleanup and close operations are +// supported. +// +// MountInProgress - State of the Vcb from the time it is +// created until it is successfully mounted or the mount +// fails. +// +// Mounted - Volume is currently in the mounted state. +// +// Invalid - User has invalidated the volume. Only legal operations +// are cleanup and close. +// +// DismountInProgress - We have begun the process of tearing down the +// Vcb. It can be deleted when all the references to it +// have gone away. +// + +typedef enum _VCB_CONDITION { + + VcbNotMounted = 0, + VcbMountInProgress, + VcbMounted, + VcbInvalid, + VcbDismountInProgress + +} VCB_CONDITION; + +typedef struct _VCB { + + // + // The type and size of this record (must be CDFS_NTC_VCB) + // + + _Field_range_(==, CDFS_NTC_VCB) NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // Vpb for this volume. + // + + PVPB Vpb; + + // + // Device object for the driver below us. + // + + PDEVICE_OBJECT TargetDeviceObject; + + // + // File object used to lock the volume. + // + + PFILE_OBJECT VolumeLockFileObject; + + // + // Link into queue of Vcb's in the CdData structure. We will create a union with + // a LONGLONG to force the Vcb to be quad-aligned. + // + + union { + + LIST_ENTRY VcbLinks; + LONGLONG Alignment; + }; + + // + // State flags and condition for the Vcb. + // + + ULONG VcbState; + VCB_CONDITION VcbCondition; + + // + // Various counts for this Vcb. + // + // VcbCleanup - Open handles left on this system. + // VcbReference - Number of reasons this Vcb is still present. + // VcbUserReference - Number of user file objects still present. + // + + ULONG VcbCleanup; + __volatile ULONG VcbReference; + __volatile ULONG VcbUserReference; + + // + // Fcb for the Volume Dasd file, root directory and the Path Table. + // + + struct _FCB *VolumeDasdFcb; + struct _FCB *RootIndexFcb; + struct _FCB *PathTableFcb; + + // + // Location of current session and offset of volume descriptors. + // + + ULONG BaseSector; + ULONG VdSectorOffset; + ULONG PrimaryVdSectorOffset; + + // + // Following is a sector from the last non-cached read of an XA file. + // Also the cooked offset on the disk. + // + + PVOID XASector; + LONGLONG XADiskOffset; + + // + // Vcb resource. This is used to synchronize open/cleanup/close operations. + // + + ERESOURCE VcbResource; + + // + // File resource. This is used to synchronize all file operations except + // open/cleanup/close. + // + + ERESOURCE FileResource; + + // + // Vcb fast mutex. This is used to synchronize the fields in the Vcb + // when modified when the Vcb is not held exclusively. Included here + // are the count fields and Fcb table. + // + // We also use this to synchronize changes to the Fcb reference field. + // + + FAST_MUTEX VcbMutex; + PVOID VcbLockThread; + + // + // The following is used to synchronize the dir notify package. + // + + PNOTIFY_SYNC NotifySync; + + // + // The following is the head of a list of notify Irps. + // + + LIST_ENTRY DirNotifyList; + + // + // Logical block size for this volume as well constant values + // associated with the block size. + // + + ULONG BlockSize; + ULONG BlockToSectorShift; + ULONG BlockToByteShift; + ULONG BlocksPerSector; + ULONG BlockMask; + ULONG BlockInverseMask; + + // + // Fcb table. Synchronized with the Vcb fast mutex. + // + + RTL_GENERIC_TABLE FcbTable; + + // + // Volume TOC. Cache this information for quick lookup. + // + + PCDROM_TOC_LARGE CdromToc; + ULONG TocLength; + ULONG TrackCount; + ULONG DiskFlags; + + // + // Block factor to determine last session information. + // + + ULONG BlockFactor; + + // + // Media change count from device driver for bulletproof detection + // of media movement + // + + ULONG MediaChangeCount; + + // + // For raw reads, CDFS must obey the port maximum transfer restrictions. + // + + ULONG MaximumTransferRawSectors; + ULONG MaximumPhysicalPages; + + // + // Preallocated VPB for swapout, so we are not forced to consider + // must succeed pool. + // + + PVPB SwapVpb; + + // + // Directory block cache. Read large numbers of blocks on directory + // reads, hoping to benefit from the fact that most mastered/pressed + // discs clump metadata in one place thus allowing us to crudely + // pre-cache and reduce seeks back to directory data during app install, + // file copy etc. + // + // Note that the purpose of this is to PRE cache unread data, + // not cache already read data (since Cc already provides that), thus + // speeding initial access to the volume. + // + + PUCHAR SectorCacheBuffer; + CD_SECTOR_CACHE_CHUNK SecCacheChunks[ CD_SEC_CACHE_CHUNKS]; + ULONG SecCacheLRUChunkIndex; + + PIRP SectorCacheIrp; + KEVENT SectorCacheEvent; + ERESOURCE SectorCacheResource; + +#if DBG + ULONG SecCacheHits; + ULONG SecCacheMisses; +#endif +} VCB, *PVCB; + +#define VCB_STATE_HSG (0x00000001) +#define VCB_STATE_ISO (0x00000002) +#define VCB_STATE_JOLIET (0x00000004) +#define VCB_STATE_LOCKED (0x00000010) +#define VCB_STATE_REMOVABLE_MEDIA (0x00000020) +#define VCB_STATE_CDXA (0x00000040) +#define VCB_STATE_AUDIO_DISK (0x00000080) +#define VCB_STATE_NOTIFY_REMOUNT (0x00000100) +#define VCB_STATE_VPB_NOT_ON_DEVICE (0x00000200) +#define VCB_STATE_SHUTDOWN (0x00000400) +#define VCB_STATE_DISMOUNTED (0x00000800) + + +// +// The Volume Device Object is an I/O system device object with a +// workqueue and an VCB record appended to the end. There are multiple +// of these records, one for every mounted volume, and are created during +// a volume mount operation. The work queue is for handling an overload +// of work requests to the volume. +// + +typedef struct _VOLUME_DEVICE_OBJECT { + + DEVICE_OBJECT DeviceObject; + + // + // The following field tells how many requests for this volume have + // either been enqueued to ExWorker threads or are currently being + // serviced by ExWorker threads. If the number goes above + // a certain threshold, put the request on the overflow queue to be + // executed later. + // + + __volatile ULONG PostedRequestCount; + + // + // The following field indicates the number of IRP's waiting + // to be serviced in the overflow queue. + // + + ULONG OverflowQueueCount; + + // + // The following field contains the queue header of the overflow queue. + // The Overflow queue is a list of IRP's linked via the IRP's ListEntry + // field. + // + + LIST_ENTRY OverflowQueue; + + // + // The following spinlock protects access to all the above fields. + // + + KSPIN_LOCK OverflowQueueSpinLock; + + // + // This is the file system specific volume control block. + // + + VCB Vcb; + +} VOLUME_DEVICE_OBJECT; +typedef VOLUME_DEVICE_OBJECT *PVOLUME_DEVICE_OBJECT; + + +// +// The following two structures are the separate union structures for +// data and index Fcb's. The path table is actually the same structure +// as the index Fcb since it uses the first few fields. +// + +typedef enum _FCB_CONDITION { + FcbGood = 1, + FcbBad, + FcbNeedsToBeVerified +} FCB_CONDITION; + +typedef struct _FCB_DATA { + +#if (NTDDI_VERSION < NTDDI_WIN8) + // + // The following field is used by the oplock module + // to maintain current oplock information. + // + + OPLOCK Oplock; +#endif + + // + // The following field is used by the filelock module + // to maintain current byte range locking information. + // A file lock is allocated as needed. + // + + PFILE_LOCK FileLock; + +} FCB_DATA; +typedef FCB_DATA *PFCB_DATA; + +typedef struct _FCB_INDEX { + + // + // Internal stream file. + // + + PFILE_OBJECT FileObject; + + // + // Offset of first entry in stream. This is for case where directory + // or path table does not begin on a sector boundary. This value is + // added to all offset values to determine the real offset. + // + + ULONG StreamOffset; + + // + // List of child fcbs. + // + + LIST_ENTRY FcbQueue; + + // + // Ordinal number for this directory. Combine this with the path table offset + // in the FileId and you have a starting point in the path table. + // + + ULONG Ordinal; + + // + // Children path table start. This is the offset in the path table + // for the first child of the directory. A value of zero indicates + // that we haven't found the first child yet. If there are no child + // directories we will position at a point in the path table so that + // subsequent searches will fail quickly. + // + + ULONG ChildPathTableOffset; + ULONG ChildOrdinal; + + // + // Root of splay trees for exact and ignore case prefix trees. + // + + PRTL_SPLAY_LINKS ExactCaseRoot; + PRTL_SPLAY_LINKS IgnoreCaseRoot; + +} FCB_INDEX; +typedef FCB_INDEX *PFCB_INDEX; + +typedef struct _FCB_NONPAGED { + + // + // Type and size of this record must be CDFS_NTC_FCB_NONPAGED + // + + _Field_range_(==, CDFS_NTC_FCB_NONPAGED) NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // The following field contains a record of special pointers used by + // MM and Cache to manipluate section objects. Note that the values + // are set outside of the file system. However the file system on an + // open/create will set the file object's SectionObject field to + // point to this field + // + + SECTION_OBJECT_POINTERS SegmentObject; + + // + // This is the resource structure for this Fcb. + // + + ERESOURCE FcbResource; + + // + // This is the FastMutex for this Fcb. + // + + FAST_MUTEX FcbMutex; + + // + // This is the mutex that is inserted into the FCB_ADVANCED_HEADER + // FastMutex field + // + + FAST_MUTEX AdvancedFcbHeaderMutex; + +} FCB_NONPAGED; +typedef FCB_NONPAGED *PFCB_NONPAGED; + +// +// The Fcb/Dcb record corresponds to every open file and directory, and to +// every directory on an opened path. +// + +typedef struct _FCB { + + // + // The following field is used for fast I/O. It contains the node + // type code and size, indicates if fast I/O is possible, contains + // allocation, file, and valid data size, a resource, and call back + // pointers for FastIoRead and FastMdlRead. + // + // + // Node type codes for the Fcb must be one of the following. + // + // CDFS_NTC_FCB_PATH_TABLE + // CDFS_NTC_FCB_INDEX + // CDFS_NTC_FCB_DATA + // + + // + // Common Fsrtl Header. The named header is for the fieldoff.c output. We + // use the unnamed header internally. + // + + union{ + + FSRTL_ADVANCED_FCB_HEADER Header; + FSRTL_ADVANCED_FCB_HEADER; + }; + + // + // Vcb for this Fcb. + // + + PVCB Vcb; + + // + // Parent Fcb for this Fcb. This may be NULL if this file was opened + // by ID, also for the root Fcb. + // + + struct _FCB *ParentFcb; + + // + // Links to the queue of Fcb's in the parent. + // + + LIST_ENTRY FcbLinks; + + // + // FileId for this file. + // + + FILE_ID FileId; + + // + // Counts on this Fcb. Cleanup count represents the number of open handles + // on this Fcb. Reference count represents the number of reasons this Fcb + // is still present. It includes file objects, children Fcb and anyone + // who wants to prevent this Fcb from going away. Cleanup count is synchronized + // with the FcbResource. The reference count is synchronized with the + // VcbMutex. + // + + ULONG FcbCleanup; + __volatile ULONG FcbReference; + ULONG FcbUserReference; + + // + // State flags for this Fcb. + // + + ULONG FcbState; + + // + // NT style attributes for the Fcb. + // + + ULONG FileAttributes; + + // + // CDXA attributes for this file. + // + + USHORT XAAttributes; + + // + // File number from the system use area. + // + + UCHAR XAFileNumber; + + // + // This is the thread and count for the thread which has locked this + // Fcb. + // + + PVOID FcbLockThread; + ULONG FcbLockCount; + + // + // Pointer to the Fcb non-paged structures. + // + + PFCB_NONPAGED FcbNonpaged; + + // + // Share access structure. + // + + SHARE_ACCESS ShareAccess; + + // + // Mcb for the on disk mapping and a single map entry. + // + + CD_MCB_ENTRY McbEntry; + CD_MCB Mcb; + + // + // Embed the prefix entry for the longname. Store an optional pointer + // to a prefix structure for the short name. + // + + PPREFIX_ENTRY ShortNamePrefix; + PREFIX_ENTRY FileNamePrefix; + + // + // Time stamp for this file. + // + + LONGLONG CreationTime; + + union{ + + ULONG FcbType; + FCB_DATA; + FCB_INDEX; + }; + +} FCB; +typedef FCB *PFCB; + +#define FCB_STATE_INITIALIZED (0x00000001) +#define FCB_STATE_IN_FCB_TABLE (0x00000002) +#define FCB_STATE_MODE2FORM2_FILE (0x00000004) +#define FCB_STATE_MODE2_FILE (0x00000008) +#define FCB_STATE_DA_FILE (0x00000010) + +// +// These file types are read as raw 2352 byte sectors +// + +#define FCB_STATE_RAWSECTOR_MASK ( FCB_STATE_MODE2FORM2_FILE | \ + FCB_STATE_MODE2_FILE | \ + FCB_STATE_DA_FILE ) + +#define SIZEOF_FCB_DATA \ + (FIELD_OFFSET( FCB, FcbType ) + sizeof( FCB_DATA )) + +#define SIZEOF_FCB_INDEX \ + (FIELD_OFFSET( FCB, FcbType ) + sizeof( FCB_INDEX )) + + +// +// The Ccb record is allocated for every file object +// + +typedef struct _CCB { + + // + // Type and size of this record (must be CDFS_NTC_CCB) + // + + _Field_range_(==, CDFS_NTC_CCB) NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // Flags. Indicates flags to apply for the current open. + // + + ULONG Flags; + + // + // Fcb for the file being opened. + // + + PFCB Fcb; + + // + // We store state information in the Ccb for a directory + // enumeration on this handle. + // + + // + // Offset in the directory stream to base the next enumeration. + // + + ULONG CurrentDirentOffset; + CD_NAME SearchExpression; + +} CCB; +typedef CCB *PCCB; + +#define CCB_FLAG_OPEN_BY_ID (0x00000001) +#define CCB_FLAG_OPEN_RELATIVE_BY_ID (0x00000002) +#define CCB_FLAG_IGNORE_CASE (0x00000004) +#define CCB_FLAG_OPEN_WITH_VERSION (0x00000008) +#define CCB_FLAG_DISMOUNT_ON_CLOSE (0x00000010) +#define CCB_FLAG_ALLOW_EXTENDED_DASD_IO (0x00000020) + +// +// Following flags refer to index enumeration. +// + +#define CCB_FLAG_ENUM_NAME_EXP_HAS_WILD (0x00010000) +#define CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD (0x00020000) +#define CCB_FLAG_ENUM_MATCH_ALL (0x00040000) +#define CCB_FLAG_ENUM_VERSION_MATCH_ALL (0x00080000) +#define CCB_FLAG_ENUM_RETURN_NEXT (0x00100000) +#define CCB_FLAG_ENUM_INITIALIZED (0x00200000) +#define CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY (0x00400000) + + +// +// The Irp Context record is allocated for every orginating Irp. It is +// created by the Fsd dispatch routines, and deallocated by the CdComplete +// request routine +// + +typedef struct _IRP_CONTEXT { + + // + // Type and size of this record (must be CDFS_NTC_IRP_CONTEXT) + // + + _Field_range_(==, CDFS_NTC_IRP_CONTEXT) NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // Originating Irp for the request. + // + + PIRP Irp; + + // + // Vcb for this operation. When this is NULL it means we were called + // with our filesystem device object instead of a volume device object. + // (Mount will fill this in once the Vcb is created) + // + + PVCB Vcb; + + // + // Exception encountered during the request. Any error raised explicitly by + // the file system will be stored here. Any other error raised by the system + // is stored here after normalizing it. + // + + NTSTATUS ExceptionStatus; + ULONG RaisedAtLineFile; + + // + // Flags for this request. + // + + ULONG Flags; + + // + // Real device object. This represents the physical device closest to the media. + // + + PDEVICE_OBJECT RealDevice; + + // + // Io context for a read request. + // Address of Fcb for teardown oplock in create case. + // + + union { + + struct _CD_IO_CONTEXT *IoContext; + PFCB *TeardownFcb; + }; + + // + // Top level irp context for this thread. + // + + struct _IRP_CONTEXT *TopLevel; + + // + // Major and minor function codes. + // + + UCHAR MajorFunction; + UCHAR MinorFunction; + + // + // Pointer to the top-level context if this IrpContext is responsible + // for cleaning it up. + // + + struct _THREAD_CONTEXT *ThreadContext; + + // + // This structure is used for posting to the Ex worker threads. + // + + WORK_QUEUE_ITEM WorkQueueItem; + +} IRP_CONTEXT; +typedef IRP_CONTEXT *PIRP_CONTEXT; + +#define IRP_CONTEXT_FLAG_ON_STACK (0x00000001) +#define IRP_CONTEXT_FLAG_MORE_PROCESSING (0x00000002) +#define IRP_CONTEXT_FLAG_WAIT (0x00000004) +#define IRP_CONTEXT_FLAG_FORCE_POST (0x00000008) +#define IRP_CONTEXT_FLAG_TOP_LEVEL (0x00000010) +#define IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS (0x00000020) +#define IRP_CONTEXT_FLAG_IN_FSP (0x00000040) +#define IRP_CONTEXT_FLAG_IN_TEARDOWN (0x00000080) +#define IRP_CONTEXT_FLAG_ALLOC_IO (0x00000100) +#define IRP_CONTEXT_FLAG_DISABLE_POPUPS (0x00000200) +#define IRP_CONTEXT_FLAG_FORCE_VERIFY (0x00000400) + +// +// Flags used for create. +// + +#define IRP_CONTEXT_FLAG_FULL_NAME (0x10000000) +#define IRP_CONTEXT_FLAG_TRAIL_BACKSLASH (0x20000000) + +// +// The following flags need to be cleared when a request is posted. +// + +#define IRP_CONTEXT_FLAGS_CLEAR_ON_POST ( \ + IRP_CONTEXT_FLAG_MORE_PROCESSING | \ + IRP_CONTEXT_FLAG_WAIT | \ + IRP_CONTEXT_FLAG_FORCE_POST | \ + IRP_CONTEXT_FLAG_TOP_LEVEL | \ + IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS | \ + IRP_CONTEXT_FLAG_IN_FSP | \ + IRP_CONTEXT_FLAG_IN_TEARDOWN | \ + IRP_CONTEXT_FLAG_DISABLE_POPUPS \ +) + +// +// The following flags need to be cleared when a request is retried. +// + +#define IRP_CONTEXT_FLAGS_CLEAR_ON_RETRY ( \ + IRP_CONTEXT_FLAG_MORE_PROCESSING | \ + IRP_CONTEXT_FLAG_IN_TEARDOWN | \ + IRP_CONTEXT_FLAG_DISABLE_POPUPS \ +) + +// +// The following flags are set each time through the Fsp loop. +// + +#define IRP_CONTEXT_FSP_FLAGS ( \ + IRP_CONTEXT_FLAG_WAIT | \ + IRP_CONTEXT_FLAG_TOP_LEVEL | \ + IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS | \ + IRP_CONTEXT_FLAG_IN_FSP \ +) + + +// +// Following structure is used to queue a request to the delayed close queue. +// This structure should be the minimum block allocation size. +// + +typedef struct _IRP_CONTEXT_LITE { + + // + // Type and size of this record (must be CDFS_NTC_IRP_CONTEXT_LITE) + // + + _Field_range_(==, CDFS_NTC_IRP_CONTEXT_LITE) NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // Fcb for the file object being closed. + // + + PFCB Fcb; + + // + // List entry to attach to delayed close queue. + // + + LIST_ENTRY DelayedCloseLinks; + + // + // User reference count for the file object being closed. + // + + ULONG UserReference; + + // + // Real device object. This represents the physical device closest to the media. + // + + PDEVICE_OBJECT RealDevice; + +} IRP_CONTEXT_LITE; +typedef IRP_CONTEXT_LITE *PIRP_CONTEXT_LITE; + + +// +// Context structure for asynchronous I/O calls. Most of these fields +// are actually only required for the ReadMultiple routines, but +// the caller must allocate one as a local variable anyway before knowing +// whether there are multiple requests are not. Therefore, a single +// structure is used for simplicity. +// + +typedef struct _CD_IO_CONTEXT { + + // + // These two fields are used for multiple run Io + // + + __volatile LONG IrpCount; + PIRP MasterIrp; + __volatile NTSTATUS Status; + BOOLEAN AllocatedContext; + + union { + + // + // This element handles the asynchronous non-cached Io + // + + struct { + + PERESOURCE Resource; + ERESOURCE_THREAD ResourceThreadId; + ULONG RequestedByteCount; + }; + + // + // and this element handles the synchronous non-cached Io. + // + + KEVENT SyncEvent; + }; + +} CD_IO_CONTEXT; +typedef CD_IO_CONTEXT *PCD_IO_CONTEXT; + + +// +// Following structure is used to track the top level request. Each Cdfs +// Fsd and Fsp entry point will examine the top level irp location in the +// thread local storage to determine if this request is top level and/or +// top level Cdfs. The top level Cdfs request will remember the previous +// value and update that location with a stack location. This location +// can be accessed by recursive Cdfs entry points. +// + +typedef struct _THREAD_CONTEXT { + + // + // CDFS signature. Used to confirm structure on stack is valid. + // + + ULONG Cdfs; + + // + // Previous value in top-level thread location. We restore this + // when done. + // + + PIRP SavedTopLevelIrp; + + // + // Top level Cdfs IrpContext. Initial Cdfs entry point on stack + // will store the IrpContext for the request in this stack location. + // + + PIRP_CONTEXT TopLevelIrpContext; + +} THREAD_CONTEXT; +typedef THREAD_CONTEXT *PTHREAD_CONTEXT; + + +// +// The following structure is used for enumerating the entries in the +// path table. We will always map this two sectors at a time so we don't +// have to worry about entries which span sectors. We move through +// one sector at a time though. We will unpin and remap after +// crossing a sector boundary. +// +// The only special case is where we span a cache view. In that case +// we will allocate a buffer and read both pieces into it. +// +// This strategy takes advantage of the CC enhancement which allows +// overlapping ranges. +// + +typedef struct _PATH_ENUM_CONTEXT { + + // + // Pointer to the current sector and the offset of this sector to + // the beginning of the path table. The Data pointer may be + // a pool block in the case where we cross a cache view + // boundary. Also the length of the data for this block. + // + + PVOID Data; + ULONG BaseOffset; + ULONG DataLength; + + // + // Bcb for the sector. (We may actually have pinned two sectors) + // This will be NULL for the case where we needed to allocate a + // buffer in the case where we span a cache view. + // + + PBCB Bcb; + + // + // Offset to current entry within the current data block. + // + + ULONG DataOffset; + + // + // Did we allocate the buffer for the entry. + // + + BOOLEAN AllocatedData; + + // + // End of Path Table. This tells us whether the current data + // block includes the end of the path table. This is the + // only block where we need to do a careful check about whether + // the path table entry fits into the buffer. + // + // Also once we have reached the end of the path table we don't + // need to remap the data as we move into the final sector. + // We always look at the last two sectors together. + // + + BOOLEAN LastDataBlock; + +} PATH_ENUM_CONTEXT; +typedef PATH_ENUM_CONTEXT *PPATH_ENUM_CONTEXT; + +#define VACB_MAPPING_MASK (VACB_MAPPING_GRANULARITY - 1) +#define LAST_VACB_SECTOR_OFFSET (VACB_MAPPING_GRANULARITY - SECTOR_SIZE) + + +// +// Path Entry. This is our representation of the on disk data. +// + +typedef struct _PATH_ENTRY { + + // + // Directory number and offset. This is the ordinal and the offset from + // the beginning of the path table stream for this entry. + // + // + + ULONG Ordinal; + ULONG PathTableOffset; + + // + // Logical block Offset on the disk for this entry. We already bias + // this by any Xar blocks. + // + + ULONG DiskOffset; + + // + // Length of on-disk path table entry. + // + + ULONG PathEntryLength; + + // + // Parent number. + // + + ULONG ParentOrdinal; + + // + // DirName length and Id. Typically the pointer here points to the raw on-disk + // bytes. We will point to a fixed self entry if this is the root directory. + // + + ULONG DirNameLen; + PCHAR DirName; + + // + // Following are the flags used to cleanup this structure. + // + + ULONG Flags; + + // + // The following is the filename string and version number strings. We embed a buffer + // large enough to hold two 8.3 names. One for exact case and one for case insensitive. + // + + CD_NAME CdDirName; + CD_NAME CdCaseDirName; + + WCHAR NameBuffer[BYTE_COUNT_EMBEDDED_NAME / sizeof( WCHAR ) * 2]; + +} PATH_ENTRY; +typedef PATH_ENTRY *PPATH_ENTRY; + +#define PATH_ENTRY_FLAG_ALLOC_BUFFER (0x00000001) + + +// +// Compound path entry. This structure combines the on-disk entries +// with the in-memory structures. +// + +typedef struct _COMPOUND_PATH_ENTRY { + + PATH_ENUM_CONTEXT PathContext; + PATH_ENTRY PathEntry; + +} COMPOUND_PATH_ENTRY; +typedef COMPOUND_PATH_ENTRY *PCOMPOUND_PATH_ENTRY; + + +// +// The following is used for enumerating through a directory via the +// dirents. +// + +typedef struct _DIRENT_ENUM_CONTEXT { + + // + // Pointer the current sector and the offset of this sector within + // the directory file. Also the data length of this pinned block. + // + + PVOID Sector; + ULONG BaseOffset; + ULONG DataLength; + + // + // Bcb for the sector. + // + + PBCB Bcb; + + // + // Offset to the current dirent within this sector. + // + + ULONG SectorOffset; + + // + // Length to next dirent. A zero indicates to move to the next sector. + // + + ULONG NextDirentOffset; + +} DIRENT_ENUM_CONTEXT; +typedef DIRENT_ENUM_CONTEXT *PDIRENT_ENUM_CONTEXT; + + +// +// Following structure is used to smooth out the differences in the HSG, ISO +// and Joliett directory entries. +// + +typedef struct _DIRENT { + + // + // Offset in the Directory of this entry. Note this includes + // any bytes added to the beginning of the directory to pad + // down to a sector boundary. + // + + ULONG DirentOffset; + + ULONG DirentLength; + + // + // Starting offset on the disk including any Xar blocks. + // + + ULONG StartingOffset; + + // + // DataLength of the data. If not the last block then this should + // be an integral number of logical blocks. + // + + ULONG DataLength; + + // + // The following field is the time stamp out of the directory entry. + // Use a pointer into the dirent for this. + // + + PCHAR CdTime; + + // + // The following field is the dirent file flags field. + // + + UCHAR DirentFlags; + + // + // Following field is a Cdfs flag field used to clean up this structure. + // + + UCHAR Flags; + + // + // The following fields indicate the file unit size and interleave gap + // for interleaved files. Each of these are in logical blocks. + // + + ULONG FileUnitSize; + ULONG InterleaveGapSize; + + // + // System use offset. Zero value indicates no system use area. + // + + ULONG SystemUseOffset; + + // + // CDXA attributes and file number for this file. + // + + USHORT XAAttributes; + UCHAR XAFileNumber; + + // + // Filename length and ID. We copy the length (in bytes) and keep + // a pointer to the start of the name. + // + + ULONG FileNameLen; + PCHAR FileName; + + // + // The following are the filenames stored by name and version numbers. + // The fixed buffer here can hold two Unicode 8.3 names. This allows + // us to upcase the name into a fixed buffer. + // + + CD_NAME CdFileName; + CD_NAME CdCaseFileName; + + // + // Data stream type. Indicates if this is audio, XA mode2 form2 or cooked sectors. + // + + XA_EXTENT_TYPE ExtentType; + + WCHAR NameBuffer[BYTE_COUNT_EMBEDDED_NAME / sizeof( WCHAR ) * 2]; + +} DIRENT; +typedef DIRENT *PDIRENT; + +#define DIRENT_FLAG_ALLOC_BUFFER (0x01) +#define DIRENT_FLAG_CONSTANT_ENTRY (0x02) + +#define DIRENT_FLAG_NOT_PERSISTENT (0) + + +// +// Following structure combines the on-disk information with the normalized +// structure. +// + +typedef struct _COMPOUND_DIRENT { + + DIRENT_ENUM_CONTEXT DirContext; + DIRENT Dirent; + +} COMPOUND_DIRENT; +typedef COMPOUND_DIRENT *PCOMPOUND_DIRENT; + + +// +// The following structure is used to enumerate the files in a directory. +// It contains three DirContext/Dirent pairs and then self pointers to +// know which of these is begin used how. +// + +typedef struct _FILE_ENUM_CONTEXT { + + // + // Pointers to the current compound dirents below. + // + // PriorDirent - Initial dirent for the last file encountered. + // InitialDirent - Initial dirent for the current file. + // CurrentDirent - Second or later dirent for the current file. + // + + PCOMPOUND_DIRENT PriorDirent; + PCOMPOUND_DIRENT InitialDirent; + PCOMPOUND_DIRENT CurrentDirent; + + // + // Flags indicating the state of the search. + // + + ULONG Flags; + + // + // This is an accumulation of the file sizes of the different extents + // of a single file. + // + + LONGLONG FileSize; + + // + // Short name for this file. + // + + CD_NAME ShortName; + WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ]; + + // + // Array of compound dirents. + // + + COMPOUND_DIRENT Dirents[3]; + +} FILE_ENUM_CONTEXT; +typedef FILE_ENUM_CONTEXT *PFILE_ENUM_CONTEXT; + +#define FILE_CONTEXT_MULTIPLE_DIRENTS (0x00000001) + + +// +// RIFF header. Prepended to the data of a file containing XA sectors. +// This is a hard-coded structure except that we bias the 'ChunkSize' and +// 'RawSectors' fields with the file size. We also copy the attributes flag +// from the system use area in the dirent. We always initialize this +// structure by copying the XAFileHeader. +// + +typedef struct _RIFF_HEADER { + + ULONG ChunkId; + LONG ChunkSize; + ULONG SignatureCDXA; + ULONG SignatureFMT; + ULONG XAChunkSize; + ULONG OwnerId; + USHORT Attributes; + USHORT SignatureXA; + UCHAR FileNumber; + UCHAR Reserved[7]; + ULONG SignatureData; + ULONG RawSectors; + +} RIFF_HEADER; +typedef RIFF_HEADER *PRIFF_HEADER; + +// +// Audio play header for CDDA tracks. +// + +typedef struct _AUDIO_PLAY_HEADER { + + ULONG Chunk; + ULONG ChunkSize; + ULONG SignatureCDDA; + ULONG SignatureFMT; + ULONG FMTChunkSize; + USHORT FormatTag; + USHORT TrackNumber; + ULONG DiskID; + ULONG StartingSector; + ULONG SectorCount; + UCHAR TrackAddress[4]; + UCHAR TrackLength[4]; + +} AUDIO_PLAY_HEADER; +typedef AUDIO_PLAY_HEADER *PAUDIO_PLAY_HEADER; + + +// +// Some macros for supporting the use of a Generic Table +// containing all the FCB/DCBs and indexed by their FileId. +// +// For directories: +// +// The HighPart contains the path table offset of this directory in the +// path table. +// +// The LowPart contains zero except for the upper bit which is +// set to indicate that this is a directory. +// +// For files: +// +// The HighPart contains the path table offset of the parent directory +// in the path table. +// +// The LowPart contains the byte offset of the dirent in the parent +// directory file. +// +// A directory is always entered into the Fcb Table as if it's +// dirent offset was zero. This enables any child to look in the FcbTable +// for it's parent by searching with the same HighPart but with zero +// as the value for LowPart. +// +// The Id field is a LARGE_INTEGER where the High and Low parts can be +// accessed separately. +// +// The following macros are used to access the Fid fields. +// +// CdQueryFidDirentOffset - Accesses the Dirent offset field +// CdQueryFidPathTableNumber - Accesses the PathTable offset field +// CdSetFidDirentOffset - Sets the Dirent offset field +// CdSetFidPathTableNumber - Sets the PathTable ordinal field +// CdFidIsDirectory - Queries if directory bit is set +// CdFidSetDirectory - Sets directory bit +// + +#define FID_DIR_MASK 0x80000000 // high order bit means directory. + +#define CdQueryFidDirentOffset(I) ((I).LowPart & ~FID_DIR_MASK) +#define CdQueryFidPathTableOffset(I) ((I).HighPart) +#define CdSetFidDirentOffset(I,D) ((I).LowPart = D) +#define CdSetFidPathTableOffset(I,P) ((I).HighPart = P) +#define CdFidIsDirectory(I) FlagOn( (I).LowPart, FID_DIR_MASK ) +#define CdFidSetDirectory(I) SetFlag( (I).LowPart, FID_DIR_MASK ) + +#define CdSetFidFromParentAndDirent(I,F,D) { \ + CdSetFidPathTableOffset( (I), CdQueryFidPathTableOffset( (F)->FileId )); \ + CdSetFidDirentOffset( (I), (D)->DirentOffset ); \ + if (FlagOn( (D)->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { \ + CdFidSetDirectory((I)); \ + } \ +} + +#ifdef CDFS_TELEMETRY_DATA +// ============================================================================ +// ============================================================================ +// +// Telemetry +// +// ============================================================================ +// ============================================================================ + +typedef struct _CDFS_TELEMETRY_DATA_CONTEXT { + + // + // Number of times there was not enough stack space to generate telemetry + // + + volatile LONG MissedTelemetryPoints; + + // + // System Time of the last periodic telemtry event. System Time + // is according to KeQuerySystemTime() + // + + LARGE_INTEGER LastPeriodicTelemetrySystemTime; + + // + // TickCount of the last periodic telemtry event. TickCount is + // according to KeQueryTickCount() + // + + LARGE_INTEGER LastPeriodicTelemetryTickCount; + + // + // Hint for Worker thread whether to generate + // periodic telemetry or not + // + + BOOLEAN GeneratePeriodicTelemetry; + + // + // Guid for ID parity with other file systems telemetry. + // + + GUID VolumeGuid; + + +#if DBG + + // + // For DBG builds we want a machanism to change the frequency of + // periodic events + // + + LONGLONG PeriodicInterval; + +#endif + + // + // File system statistics at time of last period telemetry event + // + + FILESYSTEM_STATISTICS CommonStats; + +} CDFS_TELEMETRY_DATA_CONTEXT, *PCDFS_TELEMETRY_DATA_CONTEXT; + +#endif // CDFS_TELEMETRY_DATA + +#endif // _CDSTRUC_ + diff --git a/filesys/cdfs/cleanup.c b/filesys/cdfs/cleanup.c new file mode 100644 index 00000000..a0d8ce43 --- /dev/null +++ b/filesys/cdfs/cleanup.c @@ -0,0 +1,364 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Cleanup.c + +Abstract: + + This module implements the File Cleanup routine for Cdfs called by the + dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CLEANUP) + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonCleanup ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for cleanup of a file/directory called by both + the fsd and fsp threads. + + Cleanup is invoked whenever the last handle to a file object is closed. + This is different than the Close operation which is invoked when the last + reference to a file object is deleted. + + The function of cleanup is to essentially "cleanup" the file/directory + after a user is done with it. The Fcb/Dcb remains around (because MM + still has the file object referenced) but is now available for another + user to open (i.e., as far as the user is concerned the is now closed). + + See close for a more complete description of what close does. + + We do no synchronization in this routine until we get to the point + where we modify the counts, share access and volume lock field. + + We need to update the Fcb and Vcb to show that a user handle has been closed. + The following structures and fields are affected. + + Vcb: + + VolumeLockFileObject - Did the user lock the volume with this file object. + VcbState - Check if we are unlocking the volume here. + VcbCleanup - Count of outstanding handles on the volume. + DirNotifyQueue - If this file object has pending DirNotify Irps. + + Fcb: + + ShareAccess - If this is a user handle. + FcbCleanup - Count of outstanding handles on this Fcb. + Oplock - Any outstanding oplocks on this file object. + FileLock - Any outstanding filelocks on this file object. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ + +{ + PFILE_OBJECT FileObject; + TYPE_OF_OPEN TypeOfOpen; + + BOOLEAN SendUnlockNotification = FALSE; + BOOLEAN AttemptTeardown = FALSE; + BOOLEAN VcbAcquired = FALSE; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + KIRQL SavedIrql; + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // If we were called with our file system device object instead of a + // volume device object, just complete this request with STATUS_SUCCESS. + // + + if (IrpContext->Vcb == NULL) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Get the file object out of the Irp and decode the type of open. + // + + FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject; + + TypeOfOpen = CdDecodeFileObject( IrpContext, + FileObject, + &Fcb, + &Ccb ); + + // + // No work here for either an UnopenedFile object or a StreamFileObject. + // + + if (TypeOfOpen <= StreamFileOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; + } + + // + // Keep a local pointer to the Vcb. + // + + Vcb = Fcb->Vcb; + + // + // Synchronise with reads while we set the cleanup complete + // flag on this fileobject. Once this flag is set, any further + // reads will be rejected (CdVerifyFcbOperation) + // + + CdAcquireFileExclusive( IrpContext, Fcb); + + // + // Set the flag in the FileObject to indicate that cleanup is complete. + // + + SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE ); + + CdReleaseFile( IrpContext, Fcb); + + if (TypeOfOpen == UserVolumeOpen) { + + // + // For a force dismount, physically disconnect this Vcb from the device so + // a new mount can occur. Vcb deletion cannot happen at this time since + // there is a reference on it associated with this very request, but we'll + // call check for dismount again later after we process this close. + // + + if (FlagOn( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE )) { + + CdAcquireCdData( IrpContext ); + + CdCheckForDismount( IrpContext, Vcb, TRUE ); + + CdReleaseCdData( IrpContext ); + + // + // If this handle actually wrote something, flush the device buffers, + // and then set the verify bit now just to be safe (in case there is no + // dismount). + // + + } else if (FlagOn( FileObject->Flags, FO_FILE_MODIFIED )) { + + CdHijackIrpAndFlushDevice( IrpContext, Irp, Vcb->TargetDeviceObject ); + + CdMarkDevForVerifyIfVcbMounted( Vcb ); + } + } + + // + // Acquire the current file. + // + + CdAcquireFcbExclusive( IrpContext, Fcb, FALSE ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Case on the type of open that we are trying to cleanup. + // + + switch (TypeOfOpen) { + + case UserDirectoryOpen: + + // + // Check if we need to complete any dir notify Irps on this file object. + // + + FsRtlNotifyCleanup( Vcb->NotifySync, + &Vcb->DirNotifyList, + Ccb ); + + break; + + case UserFileOpen: + + // + // Coordinate the cleanup operation with the oplock state. + // Oplock cleanup operations can always cleanup immediately so no + // need to check for STATUS_PENDING. + // + + FsRtlCheckOplock( CdGetFcbOplock(Fcb), + Irp, + IrpContext, + NULL, + NULL ); + + // + // Unlock all outstanding file locks. + // + + if (Fcb->FileLock != NULL) { + + FsRtlFastUnlockAll( Fcb->FileLock, + FileObject, + IoGetRequestorProcess( Irp ), + NULL ); + } + + // + // Cleanup the cache map. + // + + CcUninitializeCacheMap( FileObject, NULL, NULL ); + + // + // Check the fast io state. + // + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + + break; + + case UserVolumeOpen: + + break; + + default : + +#pragma prefast( suppress:__WARNING_USE_OTHER_FUNCTION, "argument bogus" ) + CdBugCheck( TypeOfOpen, 0, 0 ); + } + + // + // Now lock the Vcb in order to modify the fields in the in-memory + // structures. + // + + CdLockVcb( IrpContext, Vcb ); + + // + // Decrement the cleanup counts in the Vcb and Fcb. + // + + CdDecrementCleanupCounts( IrpContext, Fcb ); + + // + // If the cleanup count hit zero and the volume is not mounted, we + // will want to try to spark teardown. + // + + AttemptTeardown = (Vcb->VcbCleanup == 0 && Vcb->VcbCondition == VcbNotMounted); + + // + // If this file object has locked the volume then perform the unlock operation. + // We do this regardless of explicit or implicit (no share DASD open) lock. + // + + if (FileObject == Vcb->VolumeLockFileObject) { + + NT_ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_LOCKED)); + + IoAcquireVpbSpinLock( &SavedIrql ); + + ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED); + ClearFlag( Vcb->VcbState, VCB_STATE_LOCKED ); + Vcb->VolumeLockFileObject = NULL; + SendUnlockNotification = TRUE; + + IoReleaseVpbSpinLock( SavedIrql ); + } + + CdUnlockVcb( IrpContext, Vcb ); + + // + // We must clean up the share access at this time, since we may not + // get a Close call for awhile if the file was mapped through this + // File Object. + // + + IoRemoveShareAccess( FileObject, &Fcb->ShareAccess ); + + } finally { + + CdReleaseFcb( IrpContext, Fcb ); + + if (SendUnlockNotification) { + + FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_UNLOCK ); + } + } + + // + // If appropriate, try to spark teardown by purging the volume. Should + // this very fileobject we were cleaning up be the last reason for the + // volume to remain, teardown will commence on completion of this Irp. + // + + if (AttemptTeardown) { + + // + // Preacquire CdData here, since the purges will generate closes which + // may acquire CdData if there is a possibility of tearing the volume + // down. + // + + CdAcquireCdData( IrpContext); + + try { + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + VcbAcquired = TRUE; + + CdPurgeVolume( IrpContext, Vcb, FALSE ); + + } finally { + + if (VcbAcquired) { CdReleaseVcb( IrpContext, Vcb ); } + + CdReleaseCdData( IrpContext); + } + } + + // + // If this is a normal termination then complete the request + // + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; +} + + diff --git a/filesys/cdfs/close.c b/filesys/cdfs/close.c new file mode 100644 index 00000000..5e455875 --- /dev/null +++ b/filesys/cdfs/close.c @@ -0,0 +1,1019 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Close.c + +Abstract: + + This module implements the File Close routine for Cdfs called by the + Fsd/Fsp dispatch routines. + + The close operation interacts with both the async and delayed close queues + in the CdData structure. Since close may be called recursively we may + violate the locking order in acquiring the Vcb or Fcb. In this case + we may move the request to the async close queue. If this is the last + reference on the Fcb and there is a chance the user may reopen this + file again soon we would like to defer the close. In this case we + may move the request to the async close queue. + + Once we are past the decode file operation there is no need for the + file object. If we are moving the request to either of the work + queues then we remember all of the information from the file object and + complete the request with STATUS_SUCCESS. The Io system can then + reuse the file object and we can complete the request when convenient. + + The async close queue consists of requests which we would like to + complete as soon as possible. They are queued using the original + IrpContext where some of the fields have been overwritten with + information from the file object. We will extract this information, + cleanup the IrpContext and then call the close worker routine. + + The delayed close queue consists of requests which we would like to + defer the close for. We keep size of this list within a range + determined by the size of the system. We let it grow to some maximum + value and then shrink to some minimum value. We allocate a small + structure which contains the key information from the file object + and use this information along with an IrpContext on the stack + to complete the request. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CLOSE) + +// +// Local support routines +// + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +CdCommonClosePrivate ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _In_ PFCB Fcb, + _In_ ULONG UserReference, + _In_ BOOLEAN FromFsd + ); + +VOID +CdQueueClose ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ ULONG UserReference, + _In_ BOOLEAN DelayedClose + ); + +PIRP_CONTEXT +CdRemoveClose ( + _In_opt_ PVCB Vcb + ); + +// Tell prefast this is a workitem routine +IO_WORKITEM_ROUTINE CdCloseWorker; + +VOID +CdCloseWorker ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdFspClose) +#pragma alloc_text(PAGE, CdCommonClose) +#pragma alloc_text(PAGE, CdCommonClosePrivate) +#pragma alloc_text(PAGE, CdQueueClose) +#pragma alloc_text(PAGE, CdRemoveClose) +#pragma alloc_text(PAGE, CdCloseWorker) +#endif + + +VOID +CdFspClose ( + _In_opt_ PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine is called to process the close queues in the CdData. If the + Vcb is passed then we want to remove all of the closes for this Vcb. + Otherwise we will do as many of the delayed closes as we need to do. + +Arguments: + + Vcb - If specified then we are looking for all of the closes for the + given Vcb. + +Return Value: + + None + +--*/ + +{ + PIRP_CONTEXT IrpContext; + IRP_CONTEXT StackIrpContext; + + THREAD_CONTEXT ThreadContext = {0}; + + PFCB Fcb; + ULONG UserReference; + + ULONG VcbHoldCount = 0; + PVCB CurrentVcb = NULL; + + BOOLEAN PotentialVcbTeardown = FALSE; + + PAGED_CODE(); + + FsRtlEnterFileSystem(); + + // + // Continue processing until there are no more closes to process. + // + + while ((IrpContext = CdRemoveClose( Vcb )) != NULL) { + + // + // If we don't have an IrpContext then use the one on the stack. + // Initialize it for this request. + // + + if (SafeNodeType( IrpContext ) != CDFS_NTC_IRP_CONTEXT ) { + + // + // Update the local values from the IrpContextLite. + // + + Fcb = ((PIRP_CONTEXT_LITE) IrpContext)->Fcb; + UserReference = ((PIRP_CONTEXT_LITE) IrpContext)->UserReference; + + // + // Update the stack irp context with the values from the + // IrpContextLite. + // + + CdInitializeStackIrpContext( &StackIrpContext, + (PIRP_CONTEXT_LITE) IrpContext ); + + // + // Free the IrpContextLite. + // + + CdFreeIrpContextLite( (PIRP_CONTEXT_LITE) IrpContext ); + + // + // Remember we have the IrpContext from the stack. + // + + IrpContext = &StackIrpContext; + + // + // Otherwise cleanup the existing IrpContext. + // + + } else { + + // + // Remember the Fcb and user reference count. + // + + Fcb = (PFCB) IrpContext->Irp; + IrpContext->Irp = NULL; + + UserReference = (ULONG) IrpContext->ExceptionStatus; + IrpContext->ExceptionStatus = STATUS_SUCCESS; + } + + _Analysis_assume_(Fcb != NULL && Fcb->Vcb != NULL); + + // + // We have an IrpContext. Now we need to set the top level thread + // context. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FSP_FLAGS ); + + // + // If we were given a Vcb then there is a request on top of this. + // + + if (ARGUMENT_PRESENT( Vcb )) { + + ClearFlag( IrpContext->Flags, + IRP_CONTEXT_FLAG_TOP_LEVEL | IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS ); + } + + CdSetThreadContext( IrpContext, &ThreadContext ); + + // + // If we have hit the maximum number of requests to process without + // releasing the Vcb then release the Vcb now. If we are holding + // a different Vcb to this one then release the previous Vcb. + // + // In either case acquire the current Vcb. + // + // We use the MinDelayedCloseCount from the CdData since it is + // a convenient value based on the system size. Only thing we are trying + // to do here is prevent this routine starving other threads which + // may need this Vcb exclusively. + // + // Note that the check for potential teardown below is unsafe. We'll + // repeat later within the cddata lock. + // + + PotentialVcbTeardown = !ARGUMENT_PRESENT( Vcb ) && + (Fcb->Vcb->VcbCondition != VcbMounted) && + (Fcb->Vcb->VcbCondition != VcbMountInProgress) && + (Fcb->Vcb->VcbCleanup == 0); + + if (PotentialVcbTeardown || + (VcbHoldCount > CdData.MinDelayedCloseCount) || + (Fcb->Vcb != CurrentVcb)) { + + if (CurrentVcb != NULL) { + + CdReleaseVcb( IrpContext, CurrentVcb ); + } + + if (PotentialVcbTeardown) { + + CdAcquireCdData( IrpContext ); + + // + // Repeat the checks with global lock held. The volume could have + // been remounted while we didn't hold the lock. + // + + PotentialVcbTeardown = !ARGUMENT_PRESENT( Vcb ) && + (Fcb->Vcb->VcbCondition != VcbMounted) && + (Fcb->Vcb->VcbCondition != VcbMountInProgress) && + (Fcb->Vcb->VcbCleanup == 0); + + if (!PotentialVcbTeardown) { + + CdReleaseCdData( IrpContext); + } + } + + CurrentVcb = Fcb->Vcb; + + _Analysis_assume_( CurrentVcb != NULL ); + + CdAcquireVcbShared( IrpContext, CurrentVcb, FALSE ); + + VcbHoldCount = 0; + + } else { + + VcbHoldCount += 1; + } + + // + // Call our worker routine to perform the close operation. + // + + CdCommonClosePrivate( IrpContext, CurrentVcb, Fcb, UserReference, FALSE ); + + // + // If the reference count on this Vcb is below our residual reference + // then check if we should dismount the volume. + // + + if (PotentialVcbTeardown) { + + CdReleaseVcb( IrpContext, CurrentVcb ); + CdCheckForDismount( IrpContext, CurrentVcb, FALSE ); + + CurrentVcb = NULL; + + CdReleaseCdData( IrpContext ); + PotentialVcbTeardown = FALSE; + } + + // + // Complete the current request to cleanup the IrpContext. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + } + + // + // Release any Vcb we may still hold. + // + + if (CurrentVcb != NULL) { + + CdReleaseVcb( IrpContext, CurrentVcb ); + + } + +#pragma prefast(suppress:26165, "Esp:1153") + FsRtlExitFileSystem(); +} + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonClose ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the Fsd entry for the close operation. We decode the file + object to find the CDFS structures and type of open. We call our internal + worker routine to perform the actual work. If the work wasn't completed + then we post to one of our worker queues. The Ccb isn't needed after this + point so we delete the Ccb and return STATUS_SUCCESS to our caller in all + cases. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + TYPE_OF_OPEN TypeOfOpen; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + ULONG UserReference = 0; + + BOOLEAN PotentialVcbTeardown = FALSE; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // If we were called with our file system device object instead of a + // volume device object, just complete this request with STATUS_SUCCESS. + // + + if (IrpContext->Vcb == NULL) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Decode the file object to get the type of open and Fcb/Ccb. + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, + IoGetCurrentIrpStackLocation( Irp )->FileObject, + &Fcb, + &Ccb ); + + // + // No work to do for unopened file objects. + // + + if (TypeOfOpen == UnopenedFileObject) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; + } + + Vcb = Fcb->Vcb; + + // + // Clean up any CCB associated with this open. + // + + if (Ccb != NULL) { + + UserReference = 1; + + // + // We can always deallocate the Ccb if present. + // + + CdDeleteCcb( IrpContext, Ccb ); + } + + // + // If this is the last reference to a user file or directory on a + // currently mounted volume, then post it to the delayed close queue. Note + // that the VcbCondition check is unsafe, but it doesn't really matter - + // we just might delay the volume teardown a little by posting this close. + // + + if ((Vcb->VcbCondition == VcbMounted) && + (Fcb->FcbReference == 1) && + ((TypeOfOpen == UserFileOpen) || + (TypeOfOpen == UserDirectoryOpen))) { + + CdQueueClose( IrpContext, Fcb, UserReference, TRUE ); + IrpContext = NULL; + + // + // Otherwise try to process this close. Post to the async close queue + // if we can't acquire all of the resources. + // + + } + else { + + // + // If we may be dismounting this volume then acquire the CdData + // resource. + // + // Since we now must make volumes go away as soon as reasonable after + // the last user handles closes, key off of the cleanup count. It is + // OK to do this more than neccesary. Since this Fcb could be holding + // a number of other Fcbs (and thus their references), a simple check + // on reference count is not appropriate. + // + // Do an unsafe check first to avoid taking the (global) cddata lock in the + // common case. + // + + if ((Vcb->VcbCleanup == 0) && + (Vcb->VcbCondition != VcbMounted)) { + + // + // Possible dismount. Acquire CdData to synchronise with the remount path + // before looking at the vcb condition again. + // + + CdAcquireCdData( IrpContext ); + + if ((Vcb->VcbCleanup == 0) && + (Vcb->VcbCondition != VcbMounted) && + (Vcb->VcbCondition != VcbMountInProgress) && + FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL_CDFS )) { + + PotentialVcbTeardown = TRUE; + } + else { + + // + // We can't dismount this volume now, there are other references or + // it's just been remounted. + // + } + + // + // Drop the global lock if we don't need it anymore. + // + + if (!PotentialVcbTeardown) { + + CdReleaseCdData( IrpContext ); + } + } + + // + // Call the worker routine to perform the actual work. This routine + // should never raise except for a fatal error. + // + + if (!CdCommonClosePrivate( IrpContext, Vcb, Fcb, UserReference, TRUE )) { + + // + // If we didn't complete the request then post the request as needed. + // + + CdQueueClose( IrpContext, Fcb, UserReference, FALSE ); + IrpContext = NULL; + + // + // Check whether we should be dismounting the volume and then complete + // the request. + // + + } + else if (PotentialVcbTeardown) { + + CdCheckForDismount( IrpContext, Vcb, FALSE ); + } + } + + // + // Always complete this request with STATUS_SUCCESS. + // + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + if (PotentialVcbTeardown) { + + CdReleaseCdData( IrpContext ); + } + + // + // Always return STATUS_SUCCESS for closes. + // + + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +CdCommonClosePrivate ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _In_ PFCB Fcb, + _In_ ULONG UserReference, + _In_ BOOLEAN FromFsd + ) + +/*++ + +Routine Description: + + This is the worker routine for the close operation. We can be called in + an Fsd thread or from a worker Fsp thread. If called from the Fsd thread + then we acquire the resources without waiting. Otherwise we know it is + safe to wait. + + We check to see whether we should post this request to the delayed close + queue. If we are to process the close here then we acquire the Vcb and + Fcb. We will adjust the counts and call our teardown routine to see + if any of the structures should go away. + +Arguments: + + Vcb - Vcb for this volume. + + Fcb - Fcb for this request. + + UserReference - Number of user references for this file object. This is + zero for an internal stream. + + FromFsd - This request was called from an Fsd thread. Indicates whether + we should wait to acquire resources. + + DelayedClose - Address to store whether we should try to put this on + the delayed close queue. Ignored if this routine can process this + close. + +Return Value: + + BOOLEAN - TRUE if this thread processed the close, FALSE otherwise. + +--*/ + +{ + BOOLEAN RemovedFcb; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // Try to acquire the Vcb and Fcb. If we can't acquire them then return + // and let our caller know he should post the request to the async + // queue. + // + + if (CdAcquireVcbShared( IrpContext, Vcb, FromFsd )) { + + if (!CdAcquireFcbExclusive( IrpContext, Fcb, FromFsd )) { + + // + // We couldn't get the Fcb. Release the Vcb and let our caller + // know to post this request. + // + + CdReleaseVcb( IrpContext, Vcb ); + return FALSE; + } + + // + // We didn't get the Vcb. Let our caller know to post this request. + // + + } else { + + return FALSE; + } + + // + // Lock the Vcb and decrement the reference counts. + // + + CdLockVcb( IrpContext, Vcb ); + CdDecrementReferenceCounts( IrpContext, Fcb, 1, UserReference ); + CdUnlockVcb( IrpContext, Vcb ); + + // + // Call our teardown routine to see if this object can go away. + // If we don't remove the Fcb then release it. + // + + CdTeardownStructures( IrpContext, Fcb, &RemovedFcb ); + + if (!RemovedFcb) { + + CdReleaseFcb( IrpContext, Fcb ); + } + else { + _Analysis_assume_lock_not_held_(Fcb->FcbNonpaged->FcbResource); + } + + // + // Release the Vcb and return to our caller. Let him know we completed + // this request. + // + + CdReleaseVcb( IrpContext, Vcb ); + + return TRUE; +} + +VOID +CdCloseWorker ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context + ) +/*++ + +Routine Description: + + Worker routine to call CsFspClose. + +Arguments: + + DeviceObject - Filesystem registration device object + + Context - Callers context + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Context ); + + CdFspClose (NULL); +} + + +VOID +CdQueueClose ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ ULONG UserReference, + _In_ BOOLEAN DelayedClose + ) + +/*++ + +Routine Description: + + This routine is called to queue a request to either the async or delayed + close queue. For the delayed queue we need to allocate a smaller + structure to contain the information about the file object. We do + that so we don't put the larger IrpContext structures into this long + lived queue. If we can allocate this structure then we put this + on the async queue instead. + +Arguments: + + Fcb - Fcb for this file object. + + UserReference - Number of user references for this file object. This is + zero for an internal stream. + + DelayedClose - Indicates whether this should go on the async or delayed + close queue. + +Return Value: + + None + +--*/ + +{ + PIRP_CONTEXT_LITE IrpContextLite = NULL; + BOOLEAN StartWorker = FALSE; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_FCB( Fcb ); + + // + // Start with the delayed queue request. We can move this to the async + // queue if there is an allocation failure. + // + + if (DelayedClose) { + + // + // Try to allocate non-paged pool for the IRP_CONTEXT_LITE. + // + + IrpContextLite = CdCreateIrpContextLite( IrpContext ); + } + + // + // We want to clear the top level context in this thread if + // necessary. Call our cleanup routine to do the work. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); + CdCleanupIrpContext( IrpContext, TRUE ); + + // + // Synchronize with the CdData lock. + // + + CdLockCdData(); + + // + // If we have an IrpContext then put the request on the delayed close queue. + // + + if (IrpContextLite != NULL) { + + // + // Initialize the IrpContextLite. + // + + IrpContextLite->NodeTypeCode = CDFS_NTC_IRP_CONTEXT_LITE; + IrpContextLite->NodeByteSize = sizeof( IRP_CONTEXT_LITE ); + IrpContextLite->Fcb = Fcb; + IrpContextLite->UserReference = UserReference; + IrpContextLite->RealDevice = IrpContext->RealDevice; + + // + // Add this to the delayed close list and increment + // the count. + // + + InsertTailList( &CdData.DelayedCloseQueue, + &IrpContextLite->DelayedCloseLinks ); + + CdData.DelayedCloseCount += 1; + + // + // If we are above our threshold then start the delayed + // close operation. + // + + if (CdData.DelayedCloseCount > CdData.MaxDelayedCloseCount) { + + CdData.ReduceDelayedClose = TRUE; + + if (!CdData.FspCloseActive) { + + CdData.FspCloseActive = TRUE; + StartWorker = TRUE; + } + } + + // + // Unlock the CdData. + // + + CdUnlockCdData(); + + // + // Cleanup the IrpContext. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + // + // Otherwise drop into the async case below. + // + + } else { + + // + // Store the information about the file object into the IrpContext. + // + + IrpContext->Irp = (PIRP) Fcb; + IrpContext->ExceptionStatus = (NTSTATUS) UserReference; + + // + // Add this to the async close list and increment the count. + // + + InsertTailList( &CdData.AsyncCloseQueue, + &IrpContext->WorkQueueItem.List ); + + CdData.AsyncCloseCount += 1; + + // + // Remember to start the Fsp close thread if not currently started. + // + + if (!CdData.FspCloseActive) { + + CdData.FspCloseActive = TRUE; + + StartWorker = TRUE; + } + + // + // Unlock the CdData. + // + + CdUnlockCdData(); + } + + // + // Start the FspClose thread if we need to. + // + + if (StartWorker) { + + IoQueueWorkItem( CdData.CloseItem, CdCloseWorker, CriticalWorkQueue, NULL ); + } + + // + // Return to our caller. + // + + return; +} + + +// +// Local support routine +// + +PIRP_CONTEXT +CdRemoveClose ( + _In_opt_ PVCB Vcb + ) + +/*++ + +Routine Description: + +Arguments: + + This routine is called to scan the async and delayed close queues looking + for a suitable entry. If the Vcb is specified then we scan both queues + looking for an entry with the same Vcb. Otherwise we will look in the + async queue first for any close item. If none found there then we look + in the delayed close queue provided that we have triggered the delayed + close operation. + +Return Value: + + PIRP_CONTEXT - NULL if no work item found. Otherwise it is the pointer to + either the IrpContext or IrpContextLite for this request. + +--*/ + +{ + PIRP_CONTEXT IrpContext = NULL; + PIRP_CONTEXT NextIrpContext; + PIRP_CONTEXT_LITE NextIrpContextLite; + + PLIST_ENTRY Entry; + + PAGED_CODE(); + + ASSERT_OPTIONAL_VCB( Vcb ); + + // + // Lock the CdData to perform the scan. + // + + CdLockCdData(); + + // + // First check the list of async closes. + // + + Entry = CdData.AsyncCloseQueue.Flink; + + while (Entry != &CdData.AsyncCloseQueue) { + + // + // Extract the IrpContext. + // + + NextIrpContext = CONTAINING_RECORD( Entry, + IRP_CONTEXT, + WorkQueueItem.List ); + + // + // If no Vcb was specified or this Vcb is for our volume + // then perform the close. + // + + if (!ARGUMENT_PRESENT( Vcb ) || (NextIrpContext->Vcb == Vcb)) { + + RemoveEntryList( Entry ); + CdData.AsyncCloseCount -= 1; + + IrpContext = NextIrpContext; + break; + } + + // + // Move to the next entry. + // + + Entry = Entry->Flink; + } + + // + // If we didn't find anything look through the delayed close + // queue. + // + // We will only check the delayed close queue if we were given + // a Vcb or the delayed close operation is active. + // + + if ((IrpContext == NULL) && + (ARGUMENT_PRESENT( Vcb ) || + (CdData.ReduceDelayedClose && + (CdData.DelayedCloseCount > CdData.MinDelayedCloseCount)))) { + + Entry = CdData.DelayedCloseQueue.Flink; + + while (Entry != &CdData.DelayedCloseQueue) { + + // + // Extract the IrpContext. + // + + NextIrpContextLite = CONTAINING_RECORD( Entry, + IRP_CONTEXT_LITE, + DelayedCloseLinks ); + + // + // If no Vcb was specified or this Vcb is for our volume + // then perform the close. + // + + if (!ARGUMENT_PRESENT( Vcb ) || (NextIrpContextLite->Fcb->Vcb == Vcb)) { + + RemoveEntryList( Entry ); + CdData.DelayedCloseCount -= 1; + + IrpContext = (PIRP_CONTEXT) NextIrpContextLite; + break; + } + + // + // Move to the next entry. + // + + Entry = Entry->Flink; + } + } + + // + // If the Vcb wasn't specified and we couldn't find an entry + // then turn off the Fsp thread. + // + + if (!ARGUMENT_PRESENT( Vcb ) && (IrpContext == NULL)) { + + CdData.FspCloseActive = FALSE; + CdData.ReduceDelayedClose = FALSE; + } + + // + // Unlock the CdData. + // + + CdUnlockCdData(); + + return IrpContext; +} + + + diff --git a/filesys/cdfs/create.c b/filesys/cdfs/create.c new file mode 100644 index 00000000..695121df --- /dev/null +++ b/filesys/cdfs/create.c @@ -0,0 +1,2962 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Create.c + +Abstract: + + This module implements the File Create routine for Cdfs called by the + Fsd/Fsp dispatch routines. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_CREATE) + +// +// Local support routines +// + +_When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedCcb, _In_)) +_When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedCcb, _In_opt_)) +_When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedFileName, _In_)) +_When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedFileName, _In_opt_)) +NTSTATUS +CdNormalizeFileNames ( + _Inout_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _In_ BOOLEAN OpenByFileId, + _In_ BOOLEAN IgnoreCase, + _In_ TYPE_OF_OPEN RelatedTypeOfOpen, + PCCB RelatedCcb, + PUNICODE_STRING RelatedFileName, + _Inout_ PUNICODE_STRING FileName, + _Inout_ PCD_NAME RemainingName + ); + +_Requires_lock_held_(_Global_critical_region_) +_Acquires_exclusive_lock_((*CurrentFcb)->FcbNonpaged->FcbResource) +NTSTATUS +CdOpenByFileId ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PVCB Vcb, + _Inout_ PFCB *CurrentFcb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdOpenExistingFcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFCB *CurrentFcb, + _In_ TYPE_OF_OPEN TypeOfOpen, + _In_ BOOLEAN IgnoreCase, + _In_opt_ PCCB RelatedCcb + ); + +_Requires_lock_held_(_Global_critical_region_) +_Acquires_lock_((*CurrentFcb)->FcbNonpaged->FcbResource) +NTSTATUS +CdOpenDirectoryFromPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PVCB Vcb, + _Inout_ PFCB *CurrentFcb, + _In_ PCD_NAME DirName, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ShortNameMatch, + _In_ PPATH_ENTRY PathEntry, + _In_ BOOLEAN PerformUserOpen, + _In_opt_ PCCB RelatedCcb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdOpenFileFromFileContext ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PVCB Vcb, + _Inout_ PFCB *CurrentFcb, + _In_ PCD_NAME FileName, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ShortNameMatch, + _In_ PFILE_ENUM_CONTEXT FileContext, + _In_opt_ PCCB RelatedCcb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCompleteFcbOpen ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PVCB Vcb, + _Inout_ PFCB *CurrentFcb, + _In_ TYPE_OF_OPEN TypeOfOpen, + _In_ ULONG UserCcbFlags, + _In_ ACCESS_MASK DesiredAccess + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonCreate) +#pragma alloc_text(PAGE, CdCompleteFcbOpen) +#pragma alloc_text(PAGE, CdNormalizeFileNames) +#pragma alloc_text(PAGE, CdOpenByFileId) +#pragma alloc_text(PAGE, CdOpenDirectoryFromPathEntry) +#pragma alloc_text(PAGE, CdOpenExistingFcb) +#pragma alloc_text(PAGE, CdOpenFileFromFileContext) +#endif + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +#pragma prefast(suppress:26165, "Esp:1153") +CdCommonCreate ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for opening a file called by both the + Fsp and Fsd threads. + + The file can be opened either by name or by file Id either with or without + a relative name. The file name field in the file object passed to this routine + contains either a unicode string or a 64 bit value which is the file Id. + If this is not a Joliet disk then we will convert the unicode name to + an Oem string in this routine. If there is a related file object with + a name then we will already have converted that name to Oem. + + We will store the full name for the file in the file object on a successful + open. We will allocate a larger buffer if necessary and combine the + related and file object names. The only exception is the relative open + when the related file object is for an OpenByFileId file. If we need to + allocate a buffer for a case insensitive name then we allocate it at + the tail of the buffer we will store into the file object. The upcased + portion will begin immediately after the name defined by the FileName + in the file object. + + Once we have the full name in the file object we don't want to split the + name in the event of a retry. We use a flag in the IrpContext to indicate + that the name has been split. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - This is the status from this open operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PFILE_OBJECT FileObject; + + COMPOUND_PATH_ENTRY CompoundPathEntry = {0}; + BOOLEAN CleanupCompoundPathEntry = FALSE; + + FILE_ENUM_CONTEXT FileContext = {0}; + BOOLEAN CleanupFileContext = FALSE; + BOOLEAN FoundEntry; + + PVCB Vcb; + + BOOLEAN OpenByFileId; + BOOLEAN IgnoreCase; + ULONG CreateDisposition; + + BOOLEAN ShortNameMatch; + ULONG ShortNameDirentOffset; + + BOOLEAN VolumeOpen = FALSE; + + // + // We will be acquiring and releasing file Fcb's as we move down the + // directory tree during opens. At any time we need to know the deepest + // point we have traversed down in the tree in case we need to cleanup + // any structures created here. + // + // CurrentFcb - represents this point. If non-null it means we have + // acquired it and need to release it in finally clause. + // + // NextFcb - represents the NextFcb to walk to but haven't acquired yet. + // + + TYPE_OF_OPEN RelatedTypeOfOpen = UnopenedFileObject; + PFILE_OBJECT RelatedFileObject; + PCCB RelatedCcb = NULL; + + PFCB NextFcb; + PFCB CurrentFcb = NULL; + + // + // During the open we need to combine the related file object name + // with the remaining name. We also may need to upcase the file name + // in order to do a case-insensitive name comparison. We also need + // to restore the name in the file object in the event that we retry + // the request. We use the following string variables to manage the + // name. We will can put these strings into either Unicode or Ansi + // form. + // + // FileName - Pointer to name as currently stored in the file + // object. We store the full name into the file object early in + // the open operation. + // + // RelatedFileName - Pointer to the name in the related file object. + // + // RemainingName - String containing remaining name to parse. + // + // MatchingName - Address of name structure in FileContext which matched. + // We need this to know whether we matched the long or short name. + // + + PUNICODE_STRING FileName; + PUNICODE_STRING RelatedFileName = NULL; + + CD_NAME RemainingName = {0}; + CD_NAME FinalName; + PCD_NAME MatchingName = NULL; + + PAGED_CODE(); + + // + // If we were called with our file system device object instead of a + // volume device object, just complete this request with STATUS_SUCCESS. + // + + if (IrpContext->Vcb == NULL) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Get create parameters from the Irp. + // + + OpenByFileId = BooleanFlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID ); + IgnoreCase = !BooleanFlagOn( IrpSp->Flags, SL_CASE_SENSITIVE ); + CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff; + + // + // Do some preliminary checks to make sure the operation is supported. + // We fail in the following cases immediately. + // + // - Open a paging file. + // - Open a target directory. + // - Open a file with Eas. + // - Create a file. + // + + if (FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE | SL_OPEN_TARGET_DIRECTORY) || + (IrpSp->Parameters.Create.EaLength != 0) || + (CreateDisposition == FILE_CREATE)) { + + CdCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); + return STATUS_ACCESS_DENIED; + } + +#if (NTDDI_VERSION >= NTDDI_WIN7) + // + // CDFS does not support FILE_OPEN_REQUIRING_OPLOCK + // + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_REQUIRING_OPLOCK )) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } +#endif + + // + // Copy the Vcb to a local. Assume the starting directory is the root. + // + + Vcb = IrpContext->Vcb; + NextFcb = Vcb->RootIndexFcb; + + // + // Reference our input parameters to make things easier + // + + FileObject = IrpSp->FileObject; + RelatedFileObject = NULL; + + FileName = &FileObject->FileName; + + // + // Set up the file object's Vpb pointer in case anything happens. + // This will allow us to get a reasonable pop-up. + // + + if ((FileObject->RelatedFileObject != NULL) && !OpenByFileId) { + + RelatedFileObject = FileObject->RelatedFileObject; + FileObject->Vpb = RelatedFileObject->Vpb; + + RelatedTypeOfOpen = CdDecodeFileObject( IrpContext, RelatedFileObject, &NextFcb, &RelatedCcb ); + + // + // Fail the request if this is not a user file object. + // + + if (RelatedTypeOfOpen < UserVolumeOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Remember the name in the related file object. + // + + RelatedFileName = &RelatedFileObject->FileName; + } + + // + // If we haven't initialized the names then make sure the strings are valid. + // If this an OpenByFileId then verify the file id buffer. + // + // After this routine returns we know that the full name is in the + // FileName buffer and the buffer will hold the upcased portion + // of the name yet to parse immediately after the full name in the + // buffer. Any trailing backslash has been removed and the flag + // in the IrpContext will indicate whether we removed the + // backslash. + // + + Status = CdNormalizeFileNames( IrpContext, + Vcb, + OpenByFileId, + IgnoreCase, + RelatedTypeOfOpen, + RelatedCcb, + RelatedFileName, + FileName, + &RemainingName ); + + // + // Return the error code if not successful. + // + + if (!NT_SUCCESS( Status )) { + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // We want to acquire the Vcb. Exclusively for a volume open, shared otherwise. + // The file name is empty for a volume open. + // + + if ((FileName->Length == 0) && + (RelatedTypeOfOpen <= UserVolumeOpen) && + !OpenByFileId) { + + VolumeOpen = TRUE; + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + } else { + + CdAcquireVcbShared( IrpContext, Vcb, FALSE ); + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify that the Vcb is not in an unusable condition. This routine + // will raise if not usable. + // + + CdVerifyVcb( IrpContext, Vcb ); + + // + // If the Vcb is locked then we cannot open another file + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED )) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // If we are opening this file by FileId then process this immediately + // and exit. + // + + if (OpenByFileId) { + + // + // We only allow Dasd opens of audio disks. Fail this request at + // this point. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) { + + try_return( Status = STATUS_INVALID_DEVICE_REQUEST ); + } + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // Make sure we can wait for this request. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + try_return( Status = CdOpenByFileId( IrpContext, + IrpSp, + Vcb, + &CurrentFcb )); + } + + // + // If we are opening this volume Dasd then process this immediately + // and exit. + // + + if (VolumeOpen) { + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // If they wanted to open a directory, surprise. + // + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + try_return( Status = STATUS_NOT_A_DIRECTORY ); + } + + // + // Acquire the Fcb first. + // + + CurrentFcb = Vcb->VolumeDasdFcb; + CdAcquireFcbExclusive( IrpContext, CurrentFcb, FALSE ); + + try_return( Status = CdOpenExistingFcb( IrpContext, + IrpSp, + &CurrentFcb, + UserVolumeOpen, + FALSE, + NULL )); + } + + // + // At this point CurrentFcb points to the deepest Fcb for this open + // in the tree. Let's acquire this Fcb to keep it from being deleted + // beneath us. + // + + CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); + CurrentFcb = NextFcb; + + // + // Do a prefix search if there is more of the name to parse. + // + + if (RemainingName.FileName.Length != 0) { + + // + // Do the prefix search to find the longest matching name. + // + + CdFindPrefix( IrpContext, + &CurrentFcb, + &RemainingName.FileName, + IgnoreCase ); + } + + // + // If the remaining name length is zero then we have found our + // target. + // + + if (RemainingName.FileName.Length == 0) { + + // + // If this is a file so verify the user didn't want to open + // a directory. + // + + if (SafeNodeType( CurrentFcb ) == CDFS_NTC_FCB_DATA) { + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) || + FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + try_return( Status = STATUS_NOT_A_DIRECTORY ); + } + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + try_return( Status = CdOpenExistingFcb( IrpContext, + IrpSp, + &CurrentFcb, + UserFileOpen, + IgnoreCase, + RelatedCcb )); + + // + // This is a directory. Verify the user didn't want to open + // as a file. + // + + } else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { + + try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); + + // + // Open the file as a directory. + // + + } else { + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + try_return( Status = CdOpenExistingFcb( IrpContext, + IrpSp, + &CurrentFcb, + UserDirectoryOpen, + IgnoreCase, + RelatedCcb )); + } + } + + // + // We have more work to do. We have a starting Fcb which we own shared. + // We also have the remaining name to parse. Walk through the name + // component by component looking for the full name. + // + + // + // Our starting Fcb better be a directory. + // + + if (!FlagOn( CurrentFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND ); + } + + // + // If we can't wait then post this request. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Make sure the final name has no version string. + // + + FinalName.VersionString.Length = 0; + + while (TRUE) { + + ShortNameMatch = FALSE; + + // + // Split off the next component from the name. + // + + CdDissectName( IrpContext, + &RemainingName.FileName, + &FinalName.FileName ); + + // + // Go ahead and look this entry up in the path table. + // + + CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CleanupCompoundPathEntry = TRUE; + + FoundEntry = CdFindPathEntry( IrpContext, + CurrentFcb, + &FinalName, + IgnoreCase, + &CompoundPathEntry ); + + // + // If we didn't find the entry then check if the current name + // is a possible short name. + // + + if (!FoundEntry) { + + ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &FinalName.FileName ); + + // + // If there is an embedded short name offset then look for the + // matching long name in the directory. + // + + if (ShortNameDirentOffset != MAXULONG) { + + if (CleanupFileContext) { + + CdCleanupFileContext( IrpContext, &FileContext ); + } + + CdInitializeFileContext( IrpContext, &FileContext ); + CleanupFileContext = TRUE; + + FoundEntry = CdFindFileByShortName( IrpContext, + CurrentFcb, + &FinalName, + IgnoreCase, + ShortNameDirentOffset, + &FileContext ); + + // + // If we found an entry and it is a directory then look + // this up in the path table. + // + + if (FoundEntry) { + + ShortNameMatch = TRUE; + + if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags, + CD_ATTRIBUTE_DIRECTORY )) { + + CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); + + FoundEntry = CdFindPathEntry( IrpContext, + CurrentFcb, + &FileContext.InitialDirent->Dirent.CdCaseFileName, + IgnoreCase, + &CompoundPathEntry ); + + // + // We better find this entry. + // + + if (!FoundEntry) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Upcase the name with the short name if case + // insensitive. + // + + if (IgnoreCase) { + + CdUpcaseName( IrpContext, &FinalName, &FinalName ); + } + + // + // We found a matching file. If we are at the last + // entry then break out of the loop and open the + // file below. Otherwise we return an error. + // + + } else if (RemainingName.FileName.Length == 0) { + + // + // Break out of the loop. We will process the dirent + // below. + // + + MatchingName = &FileContext.ShortName; + break; + + } else { + + try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND ); + } + } + } + + // + // We didn't find the name in either the path table or as + // a short name in a directory. If the remaining name + // length is zero then break out of the loop to search + // the directory. + // + + if (!FoundEntry) { + + if (RemainingName.FileName.Length == 0) { + + break; + + // + // Otherwise this path could not be cracked. + // + + } else { + + try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND ); + } + } + } + + // + // If this is an ignore case open then copy the exact case + // in the file object name. If it was a short name match then + // the name must be upcase already. + // + + if (IgnoreCase && !ShortNameMatch) { + + RtlCopyMemory( FinalName.FileName.Buffer, + CompoundPathEntry.PathEntry.CdDirName.FileName.Buffer, + CompoundPathEntry.PathEntry.CdDirName.FileName.Length ); + } + + // + // If we have found the last component then open this as a directory + // and return to our caller. + // + + if (RemainingName.FileName.Length == 0) { + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { + + try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + try_return( Status = CdOpenDirectoryFromPathEntry( IrpContext, + IrpSp, + Vcb, + &CurrentFcb, + &FinalName, + IgnoreCase, + ShortNameMatch, + &CompoundPathEntry.PathEntry, + TRUE, + RelatedCcb )); + } + + // + // Otherwise open an Fcb for this intermediate index Fcb. + // + + CdOpenDirectoryFromPathEntry( IrpContext, + IrpSp, + Vcb, + &CurrentFcb, + &FinalName, + IgnoreCase, + ShortNameMatch, + &CompoundPathEntry.PathEntry, + FALSE, + NULL ); + + CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CleanupCompoundPathEntry = FALSE; + } + + // + // We need to scan the current directory for a matching file name + // if we don't already have one. + // + + if (!FoundEntry) { + + if (CleanupFileContext) { + + CdCleanupFileContext( IrpContext, &FileContext ); + } + + CdInitializeFileContext( IrpContext, &FileContext ); + CleanupFileContext = TRUE; + + // + // Split our search name into separate components. + // + + CdConvertNameToCdName( IrpContext, &FinalName ); + + FoundEntry = CdFindFile( IrpContext, + CurrentFcb, + &FinalName, + IgnoreCase, + &FileContext, + &MatchingName ); + } + + // + // If we didn't find a match then check if the name is invalid to + // determine which error code to return. + // + + if (!FoundEntry) { + + if ((CreateDisposition == FILE_OPEN) || + (CreateDisposition == FILE_OVERWRITE)) { + + try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND ); + } + + // + // Any other operation return STATUS_ACCESS_DENIED. + // + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // If this is a directory then the disk is corrupt because it wasn't + // in the Path Table. + // + + if (FlagOn( FileContext.InitialDirent->Dirent.Flags, CD_ATTRIBUTE_DIRECTORY )) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + // + // Make sure our opener didn't want a directory. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) || + FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + try_return( Status = STATUS_NOT_A_DIRECTORY ); + } + + // + // The only create disposition we allow is OPEN. + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // If this is an ignore case open then copy the exact case + // in the file object name. Any version portion should + // already be upcased. + // + + if (IgnoreCase) { + + RtlCopyMemory( FinalName.FileName.Buffer, + MatchingName->FileName.Buffer, + MatchingName->FileName.Length ); + } + + // + // Open the file using the file context. We already have the + // first and last dirents. + // + + try_return( Status = CdOpenFileFromFileContext( IrpContext, + IrpSp, + Vcb, + &CurrentFcb, + &FinalName, + IgnoreCase, + (BOOLEAN) (MatchingName == &FileContext.ShortName), + &FileContext, + RelatedCcb )); + + try_exit: NOTHING; + } finally { + + // + // Cleanup the PathEntry if initialized. + // + + if (CleanupCompoundPathEntry) { + + CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); + } + + // + // Cleanup the FileContext if initialized. + // + + if (CleanupFileContext) { + + CdCleanupFileContext( IrpContext, &FileContext ); + } + + // + // The result of this open could be success, pending or some error + // condition. + // + + if (AbnormalTermination()) { + + + // + // In the error path we start by calling our teardown routine if we + // have a CurrentFcb and its not the volume Dasd Fcb. + // + + if ((CurrentFcb != NULL) && + (CurrentFcb != Vcb->VolumeDasdFcb)) { + + BOOLEAN RemovedFcb; + + CdTeardownStructures( IrpContext, CurrentFcb, &RemovedFcb ); + + if (RemovedFcb) { + + CurrentFcb = NULL; + } + } + + // + // No need to complete the request. + // + + IrpContext = NULL; + Irp = NULL; + + // + // If we posted this request through the oplock package we need + // to show that there is no reason to complete the request. + // + + } else if (Status == STATUS_PENDING) { + + IrpContext = NULL; + Irp = NULL; + } + + // + // Release the Current Fcb if still acquired. + // + + if (CurrentFcb != NULL) { + _Analysis_assume_lock_held_(CurrentFcb->FcbNonpaged->FcbResource); + CdReleaseFcb( IrpContext, CurrentFcb ); + } + + // + // Release the Vcb. + // + + CdReleaseVcb( IrpContext, Vcb ); + + // + // Call our completion routine. It will handle the case where either + // the Irp and/or IrpContext are gone. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + } + + return Status; +} + + +// +// Local support routine +// +_When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedCcb, _In_)) +_When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedCcb, _In_opt_)) +_When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedFileName, _In_)) +_When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedFileName, _In_opt_)) +NTSTATUS +CdNormalizeFileNames ( + _Inout_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _In_ BOOLEAN OpenByFileId, + _In_ BOOLEAN IgnoreCase, + _In_ TYPE_OF_OPEN RelatedTypeOfOpen, + PCCB RelatedCcb, + PUNICODE_STRING RelatedFileName, + _Inout_ PUNICODE_STRING FileName, + _Inout_ PCD_NAME RemainingName + ) + +/*++ + +Routine Description: + + This routine is called to store the full name and upcased name into the + filename buffer. We only upcase the portion yet to parse. We also + check for a trailing backslash and lead-in double backslashes. This + routine also verifies the mode of the related open against the name + currently in the filename. + +Arguments: + + Vcb - Vcb for this volume. + + OpenByFileId - Indicates if the filename should be a 64 bit FileId. + + IgnoreCase - Indicates if this open is a case-insensitive operation. + + RelatedTypeOfOpen - Indicates the type of the related file object. + + RelatedCcb - Ccb for the related open. Ignored if no relative open. + + RelatedFileName - FileName buffer for related open. Ignored if no + relative open. + + FileName - FileName to update in this routine. The name should + either be a 64-bit FileId or a Unicode string. + + RemainingName - Name with the remaining portion of the name. This + will begin after the related name and any separator. For a + non-relative open we also step over the initial separator. + +Return Value: + + NTSTATUS - STATUS_SUCCESS if the names are OK, appropriate error code + otherwise. + +--*/ + +{ + ULONG RemainingNameLength = 0; + ULONG RelatedNameLength = 0; + ULONG SeparatorLength = 0; + + ULONG BufferLength; + + UNICODE_STRING NewFileName; + + PAGED_CODE(); + + // + // If this is the first pass then we need to build the full name and + // check for name compatibility. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME )) { + + // + // Deal with the regular file name case first. + // + + if (!OpenByFileId) { + + // + // This is here because the Win32 layer can't avoid sending me double + // beginning backslashes. + // + + if ((FileName->Length > sizeof( WCHAR )) && + (FileName->Buffer[1] == L'\\') && + (FileName->Buffer[0] == L'\\')) { + + // + // If there are still two beginning backslashes, the name is bogus. + // + + if ((FileName->Length > 2 * sizeof( WCHAR )) && + (FileName->Buffer[2] == L'\\')) { + + return STATUS_OBJECT_NAME_INVALID; + } + + // + // Slide the name down in the buffer. + // + + FileName->Length -= sizeof( WCHAR ); + + RtlMoveMemory( FileName->Buffer, + FileName->Buffer + 1, + FileName->Length ); + } + + // + // Check for a trailing backslash. Don't strip off if only character + // in the full name or for relative opens where this is illegal. + // + + if (((FileName->Length > sizeof( WCHAR)) || + ((FileName->Length == sizeof( WCHAR )) && (RelatedTypeOfOpen == UserDirectoryOpen))) && + (FileName->Buffer[ (FileName->Length/2) - 1 ] == L'\\')) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ); + FileName->Length -= sizeof( WCHAR ); + } + + // + // Remember the length we need for this portion of the name. + // + + RemainingNameLength = FileName->Length; + + // + // If this is a related file object then we verify the compatibility + // of the name in the file object with the relative file object. + // + + if (RelatedTypeOfOpen != UnopenedFileObject) { + + // + // If the filename length was zero then it must be legal. + // If there are characters then check with the related + // type of open. + // + + if (FileName->Length != 0) { + + // + // The name length must always be zero for a volume open. + // + + if (RelatedTypeOfOpen <= UserVolumeOpen) { + + return STATUS_INVALID_PARAMETER; + + // + // The remaining name cannot begin with a backslash. + // + + } else if (FileName->Buffer[0] == L'\\' ) { + + return STATUS_INVALID_PARAMETER; + + // + // If the related file is a user file then there + // is no file with this path. + // + + } else if (RelatedTypeOfOpen == UserFileOpen) { + + return STATUS_OBJECT_PATH_NOT_FOUND; + } + } + + // + // Remember the length of the related name when building + // the full name. We leave the RelatedNameLength and + // SeparatorLength at zero if the relative file is opened + // by Id. + // + + if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) { + + // + // Add a separator if the name length is non-zero + // unless the relative Fcb is at the root. + // + + if ((FileName->Length != 0) && + (RelatedCcb->Fcb != Vcb->RootIndexFcb)) { + + SeparatorLength = sizeof( WCHAR ); + } + + RelatedNameLength = RelatedFileName->Length; + } + + // + // The full name is already in the filename. It must either + // be length 0 or begin with a backslash. + // + + } else if (FileName->Length != 0) { + + if (FileName->Buffer[0] != L'\\') { + + return STATUS_INVALID_PARAMETER; + } + + // + // We will want to trim the leading backslash from the + // remaining name we return. + // + + RemainingNameLength -= sizeof( WCHAR ); + SeparatorLength = sizeof( WCHAR ); + } + + // + // Now see if the buffer is large enough to hold the full name. + // + + BufferLength = RelatedNameLength + SeparatorLength + RemainingNameLength; + + // + // Check for an overflow of the maximum filename size. + // + + if (BufferLength > MAXUSHORT) { + + return STATUS_INVALID_PARAMETER; + } + + // + // Now see if we need to allocate a new buffer. + // + + if (FileName->MaximumLength < BufferLength) { + + NewFileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, + BufferLength, + TAG_FILE_NAME ); + + NewFileName.MaximumLength = (USHORT) BufferLength; + + } else { + + NewFileName.Buffer = FileName->Buffer; + NewFileName.MaximumLength = FileName->MaximumLength; + } + + // + // If there is a related name then we need to slide the remaining bytes up and + // insert the related name. Otherwise the name is in the correct position + // already. + // + + if (RelatedNameLength != 0) { + + // + // Store the remaining name in its correct position. + // + + if (RemainingNameLength != 0) { + + RtlMoveMemory( Add2Ptr( NewFileName.Buffer, RelatedNameLength + SeparatorLength, PVOID ), + FileName->Buffer, + RemainingNameLength ); + } + + RtlCopyMemory( NewFileName.Buffer, + RelatedFileName->Buffer, + RelatedNameLength ); + + // + // Add the separator if needed. + // + + if (SeparatorLength != 0) { + + *(Add2Ptr( NewFileName.Buffer, RelatedNameLength, PWCHAR )) = L'\\'; + } + + // + // Update the filename value we got from the user. + // + + if (NewFileName.Buffer != FileName->Buffer) { + + if (FileName->Buffer != NULL) { + + CdFreePool( &FileName->Buffer ); + } + + FileName->Buffer = NewFileName.Buffer; + FileName->MaximumLength = NewFileName.MaximumLength; + } + + // + // Copy the name length to the user's filename. + // + + FileName->Length = (USHORT) (RelatedNameLength + SeparatorLength + RemainingNameLength); + } + + // + // Now update the remaining name to parse. + // + + RemainingName->FileName.MaximumLength = + RemainingName->FileName.Length = (USHORT) RemainingNameLength; + RemainingName->VersionString.Length = 0; + + RemainingName->FileName.Buffer = Add2Ptr( FileName->Buffer, + RelatedNameLength + SeparatorLength, + PWCHAR ); + + // + // Upcase the name if necessary. + // + + if (IgnoreCase && (RemainingNameLength != 0)) { + + CdUpcaseName( IrpContext, + RemainingName, + RemainingName ); + } + + // + // Do a quick check to make sure there are no wildcards. + // +#pragma prefast(push) +#pragma prefast(suppress:26000, "RemainingName->FileName.Buffer = FileName.Buffer + (RelatedNameLength + SeparatorLength); FileName.MaximumLength < (RelatedNameLength + SeparatorLength + RemainingNameLength).") + if (FsRtlDoesNameContainWildCards( &RemainingName->FileName )) { +#pragma prefast(pop) + + return STATUS_OBJECT_NAME_INVALID; + } + + // + // For the open by file Id case we verify the name really contains + // a 64 bit value. + // + + } else { + + // + // Check for validity of the buffer. + // + + if (FileName->Length != sizeof( FILE_ID )) { + + return STATUS_INVALID_PARAMETER; + } + } + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME ); + + // + // If we are in the retry path then the full name is already in the + // file object name. If this is a case-sensitive operation then + // we need to upcase the name from the end of any related file name already stored + // there. + // + + } else { + + // + // Assume there is no relative name. + // + + RemainingName->FileName = *FileName; + RemainingName->VersionString.Length = 0; + + // + // Nothing to do if the name length is zero. + // + + if (RemainingName->FileName.Length != 0) { + + // + // If there is a relative name then we need to walk past it. + // + + if (RelatedTypeOfOpen != UnopenedFileObject) { + + // + // Nothing to walk past if the RelatedCcb is opened by FileId. + // + + + if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) { + + // + // Related file name is a proper prefix of the full name. + // We step over the related name and if we are then + // pointing at a separator character we step over that. + // + + RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer, + RelatedFileName->Length, + PWCHAR ); + + RemainingName->FileName.Length -= RelatedFileName->Length; + } + } + + // + // If we are pointing at a separator character then step past that. + // + + if (RemainingName->FileName.Length != 0) { + + if (*(RemainingName->FileName.Buffer) == L'\\') { + + RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer, + sizeof( WCHAR ), + PWCHAR ); + + RemainingName->FileName.Length -= sizeof( WCHAR ); + } + } + } + + // + // Upcase the name if necessary. + // + + if (IgnoreCase && (RemainingName->FileName.Length != 0)) { + + CdUpcaseName( IrpContext, + RemainingName, + RemainingName ); + } + } + +#pragma prefast(push) +#pragma prefast(suppress:26030, "RemainingName->FileName.Buffer = FileName.Buffer + (RelatedNameLength + SeparatorLength); FileName.MaximumLength < (RelatedNameLength + SeparatorLength + RemainingNameLength).") + return STATUS_SUCCESS; +#pragma prefast(pop) +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +_Acquires_exclusive_lock_((*CurrentFcb)->FcbNonpaged->FcbResource) +NTSTATUS +CdOpenByFileId ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PVCB Vcb, + _Inout_ PFCB *CurrentFcb + ) + +/*++ + +Routine Description: + + This routine is called to open a file by the FileId. The file Id is in + the FileObject name buffer and has been verified to be 64 bits. + + We extract the Id number and then check to see whether we are opening a + file or directory and compare that with the create options. If this + generates no error then optimistically look up the Fcb in the Fcb Table. + + If we don't find the Fcb then we need to carefully verify there is a file + at this offset. First check whether the Parent Fcb is in the table. If + not then lookup the parent at the path table offset given by file ID. + + If found then build the Fcb from this entry and store the new Fcb in the + tree. + + We know have the parent Fcb. Do a directory scan to find the dirent at + the given offset in this stream. This must point to the first entry + of a valid file. + + Finally we call our worker routine to complete the open on this Fcb. + +Arguments: + + IrpSp - Stack location within the create Irp. + + Vcb - Vcb for this volume. + + CurrentFcb - Address to store the Fcb for this open. We only store the + CurrentFcb here when we have acquired it so our caller knows to + free or deallocate it. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + NTSTATUS Status = STATUS_ACCESS_DENIED; + + BOOLEAN UnlockVcb = FALSE; + BOOLEAN Found; + + ULONG StreamOffset; + + NODE_TYPE_CODE NodeTypeCode; + TYPE_OF_OPEN TypeOfOpen; + + FILE_ENUM_CONTEXT FileContext; + BOOLEAN CleanupFileContext = FALSE; + + COMPOUND_PATH_ENTRY CompoundPathEntry = {0}; + BOOLEAN CleanupCompoundPathEntry = FALSE; + + FILE_ID FileId; + FILE_ID ParentFileId; + + PFCB NextFcb; + + PAGED_CODE(); + + // + // Extract the FileId from the FileObject. + // + + RtlCopyMemory( &FileId, IrpSp->FileObject->FileName.Buffer, sizeof( FILE_ID )); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Go ahead and figure out the TypeOfOpen and NodeType. We can + // get these from the input FileId. + // + + if (CdFidIsDirectory( FileId )) { + + TypeOfOpen = UserDirectoryOpen; + NodeTypeCode = CDFS_NTC_FCB_INDEX; + + // + // If the offset isn't zero then the file Id is bad. + // + + if (CdQueryFidDirentOffset( FileId ) != 0) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + } else { + + TypeOfOpen = UserFileOpen; + NodeTypeCode = CDFS_NTC_FCB_DATA; + } + + // + // Acquire the Vcb and check if there is already an Fcb. + // If not we will need to carefully verify the Fcb. + // We will post the request if we don't find the Fcb and this + // request can't wait. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + NextFcb = CdLookupFcbTable( IrpContext, Vcb, FileId ); + + if (NextFcb == NULL) { + + // + // Get the path table offset from the file id. + // + + StreamOffset = CdQueryFidPathTableOffset( FileId ); + + // + // Build the parent FileId for this and try looking it + // up in the PathTable. + // + + CdSetFidDirentOffset( ParentFileId, 0 ); + CdSetFidPathTableOffset( ParentFileId, StreamOffset ); + CdFidSetDirectory( ParentFileId ); + + NextFcb = CdLookupFcbTable( IrpContext, Vcb, ParentFileId ); + + // + // If not present then walk through the PathTable to this point. + // + + if (NextFcb == NULL) { + + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + + // + // Check that the path table offset lies within the path + // table. + // + + if (StreamOffset > Vcb->PathTableFcb->FileSize.LowPart) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CleanupCompoundPathEntry = TRUE; + + // + // Start at the first entry in the PathTable. + // + + CdLookupPathEntry( IrpContext, + Vcb->PathTableFcb->StreamOffset, + 1, + TRUE, + &CompoundPathEntry ); + + // + // Continue looking until we have passed our target offset. + // + + while (TRUE) { + + // + // Move to the next entry. + // + + Found = CdLookupNextPathEntry( IrpContext, + &CompoundPathEntry.PathContext, + &CompoundPathEntry.PathEntry ); + + // + // If we didn't find the entry or are beyond it then the + // input Id is invalid. + // + + if (!Found || + (CompoundPathEntry.PathEntry.PathTableOffset > StreamOffset)) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + } + + // + // If the FileId specified a directory then we have found + // the entry. Make sure our caller wanted to open a directory. + // + + if ((TypeOfOpen == UserDirectoryOpen) && + FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { + + try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + // + // Lock the Vcb and create the Fcb if necessary. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + NextFcb = CdCreateFcb( IrpContext, ParentFileId, NodeTypeCode, &Found ); + + // + // It's possible that someone got in here ahead of us. + // + + if (!Found) { + + CdInitializeFcbFromPathEntry( IrpContext, + NextFcb, + NULL, + &CompoundPathEntry.PathEntry ); + } + + // + // If the user wanted to open a directory then we have found + // it. Store this Fcb into the CurrentFcb and skip the + // directory scan. + // + + if (TypeOfOpen == UserDirectoryOpen) { + + *CurrentFcb = NextFcb; + NextFcb = NULL; + } + } + + // + // Perform the directory scan if we don't already have our target. + // + + if (NextFcb != NULL) { + + // + // Acquire the parent. We currently own the Vcb lock so + // do this without waiting first. + // + + if (!CdAcquireFcbExclusive( IrpContext, + NextFcb, + TRUE )) { + + NextFcb->FcbReference += 1; + CdUnlockVcb( IrpContext, Vcb ); + + CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); + + CdLockVcb( IrpContext, Vcb ); + NextFcb->FcbReference -= 1; + CdUnlockVcb( IrpContext, Vcb ); + + } else { + + CdUnlockVcb( IrpContext, Vcb ); + } + + UnlockVcb = FALSE; + + // + // Set up the CurrentFcb pointers. We know there was + // no previous parent in this case. + // + + *CurrentFcb = NextFcb; + + // + // Calculate the offset in the stream. + // + + StreamOffset = CdQueryFidDirentOffset( FileId ); + + // + // Create the stream file if it doesn't exist. This will update + // the Fcb with the size from the self entry. + // + + CdVerifyOrCreateDirStreamFile( IrpContext, NextFcb); + + // + // If our offset is beyond the end of the directory then the + // FileId is invalid. + // + + if (StreamOffset > NextFcb->FileSize.LowPart) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Otherwise position ourselves at the self entry and walk + // through dirent by dirent until this location is found. + // + + CdInitializeFileContext( IrpContext, &FileContext ); + CdLookupInitialFileDirent( IrpContext, + NextFcb, + &FileContext, + NextFcb->StreamOffset ); + + CleanupFileContext = TRUE; + + while (TRUE) { + + // + // Move to the first entry of the next file. + // + + Found = CdLookupNextInitialFileDirent( IrpContext, + NextFcb, + &FileContext ); + + // + // If we didn't find the entry or are beyond it then the + // input Id is invalid. + // + + if (!Found || + (FileContext.InitialDirent->Dirent.DirentOffset > StreamOffset)) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + } + + // + // This better not be a directory. Directory FileIds must + // refer to the self entry for directories. + // + + if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags, + CD_ATTRIBUTE_DIRECTORY )) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Check that our caller wanted to open a file. + // + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + try_return( Status = STATUS_NOT_A_DIRECTORY ); + } + + // + // Otherwise we want to collect all of the dirents for this file + // and create an Fcb with this. + // + + CdLookupLastFileDirent( IrpContext, NextFcb, &FileContext ); + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + NextFcb = CdCreateFcb( IrpContext, FileId, NodeTypeCode, &Found ); + + // + // It's possible that someone has since created this Fcb since we + // first checked. If so then can simply use this. Otherwise + // we need to initialize a new Fcb and attach it to our parent + // and insert it into the Fcb Table. + // + + if (!Found) { + + CdInitializeFcbFromFileContext( IrpContext, + NextFcb, + *CurrentFcb, + &FileContext ); + } + } + + // + // We have the Fcb. Check that the type of the file is compatible with + // the desired type of file to open. + // + + } else { + + if (FlagOn( NextFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) { + + try_return( Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + } else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + try_return( Status = STATUS_NOT_A_DIRECTORY ); + } + } + + // + // If we have a the previous Fcb and have inserted the next Fcb into + // the Fcb Table. It is safe to release the current Fcb if present + // since it is referenced through the child Fcb. + // + + if (*CurrentFcb != NULL) { + + CdReleaseFcb( IrpContext, *CurrentFcb ); + } + + // + // We now know the Fcb and currently hold the Vcb lock. + // Try to acquire this Fcb without waiting. Otherwise we + // need to reference it, drop the Vcb, acquire the Fcb and + // then dereference the Fcb. + // + + if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) { + + NextFcb->FcbReference += 1; + + CdUnlockVcb( IrpContext, Vcb ); + + CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); + + CdLockVcb( IrpContext, Vcb ); + NextFcb->FcbReference -= 1; + CdUnlockVcb( IrpContext, Vcb ); + + } else { + + CdUnlockVcb( IrpContext, Vcb ); + } + + UnlockVcb = FALSE; + + // + // Move to this Fcb. + // + + *CurrentFcb = NextFcb; + + // Lock object is acquired using internal state + _Analysis_suppress_lock_checking_(NextFcb->FcbNonpaged->FcbResource); + + // + // Check the requested access on this Fcb. + // + + if (!CdIllegalFcbAccess( IrpContext, + TypeOfOpen, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { + + // + // Call our worker routine to complete the open. + // + + Status = CdCompleteFcbOpen( IrpContext, + IrpSp, + Vcb, + CurrentFcb, + TypeOfOpen, + CCB_FLAG_OPEN_BY_ID, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + + } + + try_exit: NOTHING; + } finally { + + if (UnlockVcb) { + + CdUnlockVcb( IrpContext, Vcb ); + } + + if (CleanupFileContext) { + + CdCleanupFileContext( IrpContext, &FileContext ); + } + + if (CleanupCompoundPathEntry) { + + CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); + } + } + + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdOpenExistingFcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFCB *CurrentFcb, + _In_ TYPE_OF_OPEN TypeOfOpen, + _In_ BOOLEAN IgnoreCase, + _In_opt_ PCCB RelatedCcb + ) + +/*++ + +Routine Description: + + This routine is called to open an Fcb which is already in the Fcb table. + We will verify the access to the file and then call our worker routine + to perform the final operations. + +Arguments: + + IrpSp - Pointer to the stack location for this open. + + CurrentFcb - Address of Fcb to open. We will clear this if the Fcb + is released here. + + TypeOfOpen - Indicates whether we are opening a file, directory or volume. + + IgnoreCase - Indicates if this open is case-insensitive. + + RelatedCcb - Ccb for related file object if relative open. We use + this when setting the Ccb flags for this open. It will tell + us whether the name currently in the file object is relative or + absolute. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + ULONG CcbFlags = 0; + + NTSTATUS Status = STATUS_ACCESS_DENIED; + + PAGED_CODE(); + + // + // Check that the desired access is legal. + // + + if (!CdIllegalFcbAccess( IrpContext, + TypeOfOpen, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { + + // + // Set the Ignore case. + // + + if (IgnoreCase) { + + SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE ); + } + + // + // Check the related Ccb to see if this was an OpenByFileId and + // whether there was a version. + // + + if (ARGUMENT_PRESENT( RelatedCcb )) { + + SetFlag( CcbFlags, FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_WITH_VERSION )); + + + if (FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) { + + SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID ); + } + } + + // + // Call our worker routine to complete the open. + // + + Status = CdCompleteFcbOpen( IrpContext, + IrpSp, + (*CurrentFcb)->Vcb, + CurrentFcb, + TypeOfOpen, + CcbFlags, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + } + + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +_Acquires_lock_((*CurrentFcb)->FcbNonpaged->FcbResource) +NTSTATUS +CdOpenDirectoryFromPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PVCB Vcb, + _Inout_ PFCB *CurrentFcb, + _In_ PCD_NAME DirName, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ShortNameMatch, + _In_ PPATH_ENTRY PathEntry, + _In_ BOOLEAN PerformUserOpen, + _In_opt_ PCCB RelatedCcb + ) + +/*++ + +Routine Description: + + This routine is called to open a directory where the directory was found + in the path table. This routine is called in the case where this is the + file to open for the user and where this is an intermediate node in the + full path to open. + + We first check that the desired access is legal for a directory. Then we + construct the FileId for this and do a check to see if it is the Fcb + Table. It is always possible that either it was created since or simply + wasn't in the prefix table at the time of the prefix table search. + Initialize the Fcb and store into the FcbTable if not present. + + Next we will add this to the prefix table of our parent if needed. + + Once we know that the new Fcb has been initialized then we move our pointer + in the tree down to this position. + + This routine does not own the Vcb lock on entry. We must be sure to release + it on exit. + +Arguments: + + IrpSp - Stack location for this request. + + Vcb - Vcb for this volume. + + CurrentFcb - On input this is the parent of the Fcb to open. On output we + store the Fcb for the file being opened. + + DirName - This is always the exact name used to reach this file. + + IgnoreCase - Indicates the type of case match for the open. + + ShortNameMatch - Indicates if we are opening via the short name. + + PathEntry - Path entry for the entry found. + + PerformUserOpen - TRUE if we are to open this for a user, FALSE otherwise. + + RelatedCcb - RelatedCcb for relative file object used to make this open. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + ULONG CcbFlags = 0; + FILE_ID FileId; + + BOOLEAN UnlockVcb = FALSE; + BOOLEAN FcbExisted; + + PFCB NextFcb; + PFCB ParentFcb = NULL; + + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Check for illegal access to this file. + // + + if (PerformUserOpen && + CdIllegalFcbAccess( IrpContext, + UserDirectoryOpen, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { + + return STATUS_ACCESS_DENIED; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Check the related Ccb to see if this was an OpenByFileId. + // + + if (ARGUMENT_PRESENT( RelatedCcb ) && + FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) { + + CcbFlags = CCB_FLAG_OPEN_RELATIVE_BY_ID; + } + + if (IgnoreCase) { + + SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE ); + } + + // + // Build the file Id for this file. + // + + FileId.QuadPart = 0; + CdSetFidPathTableOffset( FileId, PathEntry->PathTableOffset ); + CdFidSetDirectory( FileId ); + + // + // Lock the Vcb so we can examine the Fcb Table. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + // + // Get the Fcb for this directory. + // + + NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_INDEX, &FcbExisted ); + + // + // If the Fcb was created here then initialize from the values in the + // path table entry. + // + + if (!FcbExisted) { + + CdInitializeFcbFromPathEntry( IrpContext, NextFcb, *CurrentFcb, PathEntry ); + } + + // + // Now try to acquire the new Fcb without waiting. We will reference + // the Fcb and retry with wait if unsuccessful. + // + + if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) { + + NextFcb->FcbReference += 1; + + CdUnlockVcb( IrpContext, Vcb ); + + CdReleaseFcb( IrpContext, *CurrentFcb ); + CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); + CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE ); + + CdLockVcb( IrpContext, Vcb ); + NextFcb->FcbReference -= 1; + CdUnlockVcb( IrpContext, Vcb ); + + } else { + + // + // Unlock the Vcb and move down to this new Fcb. Remember that we still + // own the parent however. + // + + CdUnlockVcb( IrpContext, Vcb ); + } + + UnlockVcb = FALSE; + + ParentFcb = *CurrentFcb; + *CurrentFcb = NextFcb; + + // Lock object is acquired using internal state + _Analysis_suppress_lock_checking_(NextFcb->FcbNonpaged->FcbResource); + + // + // Store this name into the prefix table for the parent. + // + + if (ShortNameMatch) { + + // + // Make sure the exact case is always in the tree. + // + + CdInsertPrefix( IrpContext, + NextFcb, + DirName, + FALSE, + TRUE, + ParentFcb ); + + if (IgnoreCase) { + + CdInsertPrefix( IrpContext, + NextFcb, + DirName, + TRUE, + TRUE, + ParentFcb ); + } + + } else { + + // + // Make sure the exact case is always in the tree. + // + + CdInsertPrefix( IrpContext, + NextFcb, + &PathEntry->CdDirName, + FALSE, + FALSE, + ParentFcb ); + + if (IgnoreCase) { + + CdInsertPrefix( IrpContext, + NextFcb, + &PathEntry->CdCaseDirName, + TRUE, + FALSE, + ParentFcb ); + } + } + + // + // Release the parent Fcb at this point. + // + + CdReleaseFcb( IrpContext, ParentFcb ); + ParentFcb = NULL; + + // + // Call our worker routine to complete the open. + // + + if (PerformUserOpen) { + + Status = CdCompleteFcbOpen( IrpContext, + IrpSp, + Vcb, + CurrentFcb, + UserDirectoryOpen, + CcbFlags, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + } + + } finally { + + // + // Unlock the Vcb if held. + // + + if (UnlockVcb) { + + CdUnlockVcb( IrpContext, Vcb ); + } + + // + // Release the parent if held. + // + + if (ParentFcb != NULL) { + + CdReleaseFcb( IrpContext, ParentFcb ); + } + } + + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdOpenFileFromFileContext ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PVCB Vcb, + _Inout_ PFCB *CurrentFcb, + _In_ PCD_NAME FileName, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ShortNameMatch, + _In_ PFILE_ENUM_CONTEXT FileContext, + _In_opt_ PCCB RelatedCcb + ) + +/*++ + +Routine Description: + + This routine is called to open a file where the file was found in a directory scan. + This should only be for a file in the case since we will find the directories in the + path table. + + We first check that the desired access is legal for this file. Then we + construct the FileId for this and do a check to see if it is the Fcb + Table. It is always possible that either it was created since or simply + wasn't in the prefix table at the time of the prefix table search. + Initialize the Fcb and store into the FcbTable if not present. + + Next we will add this to the prefix table of our parent if needed. + + Once we know that the new Fcb has been initialized then we move our pointer + in the tree down to this position. + + This routine does not own the Vcb lock on entry. We must be sure to release + it on exit. + +Arguments: + + IrpSp - Stack location for this request. + + Vcb - Vcb for the current volume. + + CurrentFcb - On input this is the parent of the Fcb to open. On output we + store the Fcb for the file being opened. + + FileName - This is always the exact name used to reach this file. + + IgnoreCase - Indicates the type of case of CaseName above. + + ShortNameMatch - Indicates if we are opening via the short name. + + FileContext - This is the context used to find the file. + + RelatedCcb - RelatedCcb for relative file object used to make this open. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + ULONG CcbFlags = 0; + FILE_ID FileId; + + BOOLEAN UnlockVcb = FALSE; + BOOLEAN FcbExisted; + + PFCB NextFcb; + PFCB ParentFcb = NULL; + + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Check for illegal access to this file. + // + + if (CdIllegalFcbAccess( IrpContext, + UserFileOpen, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) { + + return STATUS_ACCESS_DENIED; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Check if a version number was used to open this file. + // + + if (FileName->VersionString.Length != 0) { + + SetFlag( CcbFlags, CCB_FLAG_OPEN_WITH_VERSION ); + } + + // + // Check the related Ccb to see if this was an OpenByFileId. + // + + if (ARGUMENT_PRESENT( RelatedCcb ) && + FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) { + + SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID ); + } + + if (IgnoreCase) { + + SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE ); + } + + // + // Build the file Id for this file. We can use the path table offset from the + // parent and the directory offset from the dirent. + // + + CdSetFidPathTableOffset( FileId, CdQueryFidPathTableOffset( (*CurrentFcb)->FileId )); + CdSetFidDirentOffset( FileId, FileContext->InitialDirent->Dirent.DirentOffset ); + + // + // Lock the Vcb so we can examine the Fcb Table. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + // + // Get the Fcb for this file. + // + + NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_DATA, &FcbExisted ); + + // + // If the Fcb was created here then initialize from the values in the + // dirent. + // + + if (!FcbExisted) { + + CdInitializeFcbFromFileContext( IrpContext, + NextFcb, + *CurrentFcb, + FileContext ); + } + + // + // Now try to acquire the new Fcb without waiting. We will reference + // the Fcb and retry with wait if unsuccessful. + // + + if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) { + + NextFcb->FcbReference += 1; + + CdUnlockVcb( IrpContext, Vcb ); + + CdReleaseFcb( IrpContext, *CurrentFcb ); + CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE ); + CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE ); + + CdLockVcb( IrpContext, Vcb ); + NextFcb->FcbReference -= 1; + CdUnlockVcb( IrpContext, Vcb ); + + } else { + + // + // Unlock the Vcb and move down to this new Fcb. Remember that we still + // own the parent however. + // + + CdUnlockVcb( IrpContext, Vcb ); + } + + UnlockVcb = FALSE; + + ParentFcb = *CurrentFcb; + *CurrentFcb = NextFcb; + + // + // Store this name into the prefix table for the parent. + // + + + if (ShortNameMatch) { + + // + // Make sure the exact case is always in the tree. + // + + CdInsertPrefix( IrpContext, + NextFcb, + FileName, + FALSE, + TRUE, + ParentFcb ); + + if (IgnoreCase) { + + CdInsertPrefix( IrpContext, + NextFcb, + FileName, + TRUE, + TRUE, + ParentFcb ); + } + + // + // Insert this into the prefix table if we found this without + // using a version string. + // + + } else if (FileName->VersionString.Length == 0) { + + // + // Make sure the exact case is always in the tree. + // + + CdInsertPrefix( IrpContext, + NextFcb, + &FileContext->InitialDirent->Dirent.CdFileName, + FALSE, + FALSE, + ParentFcb ); + + if (IgnoreCase) { + + CdInsertPrefix( IrpContext, + NextFcb, + &FileContext->InitialDirent->Dirent.CdCaseFileName, + TRUE, + FALSE, + ParentFcb ); + } + } + + // + // Release the parent Fcb at this point. + // + + _Analysis_assume_same_lock_(ParentFcb->FcbNonpaged->FcbResource, NextFcb->FcbNonpaged->FcbResource); + CdReleaseFcb( IrpContext, ParentFcb ); + ParentFcb = NULL; + + // + // Call our worker routine to complete the open. + // + + Status = CdCompleteFcbOpen( IrpContext, + IrpSp, + Vcb, + CurrentFcb, + UserFileOpen, + CcbFlags, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + + } finally { + + // + // Unlock the Vcb if held. + // + + if (UnlockVcb) { + + CdUnlockVcb( IrpContext, Vcb ); + } + + // + // Release the parent if held. + // + + if (ParentFcb != NULL) { + + CdReleaseFcb( IrpContext, ParentFcb ); + } + } + + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCompleteFcbOpen ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PVCB Vcb, + _Inout_ PFCB *CurrentFcb, + _In_ TYPE_OF_OPEN TypeOfOpen, + _In_ ULONG UserCcbFlags, + _In_ ACCESS_MASK DesiredAccess + ) + +/*++ + +Routine Description: + + This is the worker routine which takes an existing Fcb and completes + the open. We will do any necessary oplock checks and sharing checks. + Finally we will create the Ccb and update the file object and any + file object flags. + +Arguments: + + IrpSp - Stack location for the current request. + + Vcb - Vcb for the current volume. + + CurrentFcb - Address of pointer to Fcb to open. We clear this field if + we release the resource for this file. + + TypeOfOpen - Type of open for this request. + + UserCcbFlags - Flags to OR into the Ccb flags. + + DesiredAccess - Desired access for this open. + +Return Value: + + NTSTATUS - STATUS_SUCCESS if we complete this request, STATUS_PENDING if + the oplock package takes the Irp or SHARING_VIOLATION if there is a + sharing check conflict. + +--*/ + +{ + NTSTATUS Status; + NTSTATUS OplockStatus = STATUS_SUCCESS; + ULONG Information = FILE_OPENED; + + BOOLEAN LockVolume = FALSE; + + PFCB Fcb = *CurrentFcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Expand maximum allowed to something sensible for share access checking + // + + if (MAXIMUM_ALLOWED == DesiredAccess) { + + DesiredAccess = FILE_ALL_ACCESS & ~((TypeOfOpen != UserVolumeOpen ? + (FILE_WRITE_ATTRIBUTES | + FILE_WRITE_DATA | + FILE_WRITE_EA | + FILE_ADD_FILE | + FILE_ADD_SUBDIRECTORY | + FILE_APPEND_DATA) : 0) | + FILE_DELETE_CHILD | + DELETE | + WRITE_DAC ); + } + + // + // If this a volume open and the user wants to lock the volume then + // purge and lock the volume. + // + + if ((TypeOfOpen <= UserVolumeOpen) && + !FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_READ )) { + + // + // If there are open handles then fail this immediately. + // + + if (Vcb->VcbCleanup != 0) { + + return STATUS_SHARING_VIOLATION; + } + + // + // If we can't wait then force this to be posted. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + LockVolume = TRUE; + + // + // Purge the volume and make sure all of the user references + // are gone. + // + + Status = CdPurgeVolume( IrpContext, Vcb, FALSE ); + + if (Status != STATUS_SUCCESS) { + + return Status; + } + + // + // Now force all of the delayed close operations to go away. + // + + CdFspClose( Vcb ); + + if (Vcb->VcbUserReference > CDFS_RESIDUAL_USER_REFERENCE) { + + return STATUS_SHARING_VIOLATION; + } + } + + // + // If the Fcb already existed then we need to check the oplocks and + // the share access. + // + + if (Fcb->FcbCleanup != 0) { + + // + // If this is a user file open then check whether there are any + // batch oplock. + // + + if (TypeOfOpen == UserFileOpen) { + + // + // Store the address of the Fcb for a possible teardown into + // the IrpContext. We will release this in the call to + // prepost the Irp. + // + + IrpContext->TeardownFcb = CurrentFcb; + + if (FsRtlCurrentBatchOplock( CdGetFcbOplock(Fcb) )) { + + // + // We remember if a batch oplock break is underway for the + // case where the sharing check fails. + // + + Information = FILE_OPBATCH_BREAK_UNDERWAY; + + OplockStatus = FsRtlCheckOplock( CdGetFcbOplock(Fcb), + IrpContext->Irp, + IrpContext, + CdOplockComplete, + CdPrePostIrp ); + + if (OplockStatus == STATUS_PENDING) { + + return STATUS_PENDING; + } + } + + // + // Check the share access before breaking any exclusive oplocks. + // + + Status = IoCheckShareAccess( DesiredAccess, + IrpSp->Parameters.Create.ShareAccess, + IrpSp->FileObject, + &Fcb->ShareAccess, + FALSE ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + + // + // Now check that we can continue based on the oplock state of the + // file. + // + + OplockStatus = FsRtlCheckOplock( CdGetFcbOplock(Fcb), + IrpContext->Irp, + IrpContext, + CdOplockComplete, + CdPrePostIrp ); + + if (OplockStatus == STATUS_PENDING) { + + return STATUS_PENDING; + } + + IrpContext->TeardownFcb = NULL; + + // + // Otherwise just do the sharing check. + // + + } else { + + Status = IoCheckShareAccess( DesiredAccess, + IrpSp->Parameters.Create.ShareAccess, + IrpSp->FileObject, + &Fcb->ShareAccess, + FALSE ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + } + } + + // + // Create the Ccb now. + // + + Ccb = CdCreateCcb( IrpContext, Fcb, UserCcbFlags ); + + // + // Update the share access. + // + + if (Fcb->FcbCleanup == 0) { + + IoSetShareAccess( DesiredAccess, + IrpSp->Parameters.Create.ShareAccess, + IrpSp->FileObject, + &Fcb->ShareAccess ); + + } else { + + IoUpdateShareAccess( IrpSp->FileObject, &Fcb->ShareAccess ); + } + + // + // Set the file object type. + // + + CdSetFileObject( IrpContext, IrpSp->FileObject, TypeOfOpen, Fcb, Ccb ); + + // + // Set the appropriate cache flags for a user file object. + // + + if (TypeOfOpen == UserFileOpen) { + + if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_INTERMEDIATE_BUFFERING )) { + + SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ); + + } else { + + SetFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED ); + } + } + else if (TypeOfOpen == UserVolumeOpen) { + + SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ); + } + + // + // Update the open and cleanup counts. Check the fast io state here. + // + + CdLockVcb( IrpContext, Vcb ); + + CdIncrementCleanupCounts( IrpContext, Fcb ); + CdIncrementReferenceCounts( IrpContext, Fcb, 1, 1 ); + + if (LockVolume) { + + Vcb->VolumeLockFileObject = IrpSp->FileObject; + SetFlag( Vcb->VcbState, VCB_STATE_LOCKED ); + } + + CdUnlockVcb( IrpContext, Vcb ); + + CdLockFcb( IrpContext, Fcb ); + + if (TypeOfOpen == UserFileOpen) { + + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + + } else { + + Fcb->IsFastIoPossible = FastIoIsNotPossible; + } + + CdUnlockFcb( IrpContext, Fcb ); + + // + // Show that we opened the file. + // + + IrpContext->Irp->IoStatus.Information = Information; + + // + // Point to the section object pointer in the non-paged Fcb. + // + + IrpSp->FileObject->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject; + return OplockStatus; +} + + + + + diff --git a/filesys/cdfs/devctrl.c b/filesys/cdfs/devctrl.c new file mode 100644 index 00000000..ce120f56 --- /dev/null +++ b/filesys/cdfs/devctrl.c @@ -0,0 +1,197 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DevCtrl.c + +Abstract: + + This module implements the File System Device Control routines for Cdfs + called by the dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_DEVCTRL) + +// +// Local support routines +// + +// Tell prefast this is a completion routine +IO_COMPLETION_ROUTINE CdDevCtrlCompletionRoutine; + +NTSTATUS +CdDevCtrlCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonDevControl) +#endif + + +NTSTATUS +CdCommonDevControl ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ + +{ + NTSTATUS Status; + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + PIO_STACK_LOCATION IrpSp; + PIO_STACK_LOCATION NextIrpSp; + + PAGED_CODE(); + + // + // Extract and decode the file object. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + TypeOfOpen = CdDecodeFileObject( IrpContext, + IrpSp->FileObject, + &Fcb, + &Ccb ); + + // + // The only type of opens we accept are user volume opens. + // + + if (TypeOfOpen != UserVolumeOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) { + + // + // Verify the Vcb in this case to detect if the volume has changed. + // + + CdVerifyVcb( IrpContext, Fcb->Vcb ); + + // + // Handle the case of the disk type ourselves. + // + + } else if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_DISK_TYPE) { + + // + // Verify the Vcb in this case to detect if the volume has changed. + // + + CdVerifyVcb( IrpContext, Fcb->Vcb ); + + // + // Check the size of the output buffer. + // + + if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof( CDROM_DISK_DATA )) { + + CdCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Copy the data from the Vcb. + // + + ((PCDROM_DISK_DATA) Irp->AssociatedIrp.SystemBuffer)->DiskData = Fcb->Vcb->DiskFlags; + + Irp->IoStatus.Information = sizeof( CDROM_DISK_DATA ); + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Get the next stack location, and copy over the stack parameter + // information. + // + + NextIrpSp = IoGetNextIrpStackLocation( Irp ); + + *NextIrpSp = *IrpSp; + + // + // Set up the completion routine + // + + IoSetCompletionRoutine( Irp, + CdDevCtrlCompletionRoutine, + NULL, + TRUE, + TRUE, + TRUE ); + + // + // Send the request. + // + + Status = IoCallDriver( IrpContext->Vcb->TargetDeviceObject, Irp ); + + // + // Cleanup our Irp Context. The driver has completed the Irp. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdDevCtrlCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) + +{ + // + // Add the hack-o-ramma to fix formats. + // + + if (Irp->PendingReturned) { + + IoMarkIrpPending( Irp ); + } + + return STATUS_SUCCESS; + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Contxt ); +} + + diff --git a/filesys/cdfs/deviosup.c b/filesys/cdfs/deviosup.c new file mode 100644 index 00000000..a6a69c64 --- /dev/null +++ b/filesys/cdfs/deviosup.c @@ -0,0 +1,4172 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DevIoSup.c + +Abstract: + + This module implements the low lever disk read/write support for Cdfs. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_DEVIOSUP) + +// +// Local structure definitions +// + +// +// An array of these structures is passed to CdMultipleAsync describing +// a set of runs to execute in parallel. +// + +typedef struct _IO_RUN { + + // + // Disk offset to read from and number of bytes to read. These + // must be a multiple of 2048 and the disk offset is also a + // multiple of 2048. + // + + LONGLONG DiskOffset; + ULONG DiskByteCount; + + // + // Current position in user buffer. This is the final destination for + // this portion of the Io transfer. + // + + PVOID UserBuffer; + + // + // Buffer to perform the transfer to. If this is the same as the + // user buffer above then we are using the user's buffer. Otherwise + // we either allocated a temporary buffer or are using a different portion + // of the user's buffer. + // + // TransferBuffer - Read full sectors into this location. This can + // be a pointer into the user's buffer at the exact location the + // data should go. It can also be an earlier point in the user's + // buffer if the complete I/O doesn't start on a sector boundary. + // It may also be a pointer into an allocated buffer. + // + // TransferByteCount - Count of bytes to transfer to user's buffer. A + // value of zero indicates that we did do the transfer into the + // user's buffer directly. + // + // TransferBufferOffset - Offset in this buffer to begin the transfer + // to the user's buffer. + // + + PVOID TransferBuffer; + ULONG TransferByteCount; + ULONG TransferBufferOffset; + + // + // This is the Mdl describing the locked pages in memory. It may + // be allocated to describe the allocated buffer. Or it may be + // the Mdl in the originating Irp. The MdlOffset is the offset of + // the current buffer from the beginning of the buffer described by + // the Mdl below. If the TransferMdl is not the same as the Mdl + // in the user's Irp then we know we have allocated it. + // + + PMDL TransferMdl; + PVOID TransferVirtualAddress; + + // + // Associated Irp used to perform the Io. + // + + PIRP SavedIrp; + +} IO_RUN; +typedef IO_RUN *PIO_RUN; + +#define MAX_PARALLEL_IOS 5 + +// +// Local support routines +// + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +CdPrepareBuffers ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp, + _In_ PFCB Fcb, + _In_reads_bytes_(ByteCount) PVOID UserBuffer, + _In_ ULONG UserBufferOffset, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount, + _Out_ PIO_RUN IoRuns, + _Out_ PULONG RunCount, + _Out_ PULONG ThisByteCount + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdPrepareXABuffers ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp, + _In_ PFCB Fcb, + _In_reads_bytes_(ByteCount) PVOID UserBuffer, + _In_ ULONG UserBufferOffset, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount, + _Out_ PIO_RUN IoRuns, + _Out_ PULONG RunCount, + _Out_ PULONG ThisByteCount + ); + +BOOLEAN +CdFinishBuffers ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PIO_RUN IoRuns, + _In_ ULONG RunCount, + _In_ BOOLEAN FinalCleanup, + _In_ BOOLEAN SaveXABuffer + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdMultipleAsync ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ ULONG RunCount, + _Inout_ PIO_RUN IoRuns + ); + +VOID +CdMultipleXAAsync ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG RunCount, + _Inout_ PIO_RUN IoRuns, + _In_ PRAW_READ_INFO RawReads, + _In_ TRACK_MODE_TYPE TrackMode + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdSingleAsync ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_RUN Run, + _In_ PFCB Fcb + ); + +VOID +CdWaitSync ( + _In_ PIRP_CONTEXT IrpContext + ); + +// Tell prefast this is a completion routine. +IO_COMPLETION_ROUTINE CdMultiSyncCompletionRoutine; + +// Tell prefast this is a completion routine +IO_COMPLETION_ROUTINE CdMultiAsyncCompletionRoutine; + +// Tell prefast this is a completion routine +IO_COMPLETION_ROUTINE CdSingleSyncCompletionRoutine; + +// Tell prefast this is a completion routine +IO_COMPLETION_ROUTINE CdSingleAsyncCompletionRoutine; + +_When_(SafeNodeType(Fcb) != CDFS_NTC_FCB_PATH_TABLE && StartingOffset == 0, _At_(ByteCount, _In_range_(>=, CdAudioDirentSize + sizeof(RAW_DIRENT)))) +_When_(SafeNodeType(Fcb) != CDFS_NTC_FCB_PATH_TABLE && StartingOffset != 0, _At_(ByteCount, _In_range_(>=, CdAudioDirentSize + SECTOR_SIZE))) +VOID +CdReadAudioSystemFile ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG StartingOffset, + _In_ _In_range_(>=, CdAudioDirentSize) ULONG ByteCount, + _Out_writes_bytes_(ByteCount) PVOID SystemBuffer + ); + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +CdReadDirDataThroughCache ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_RUN Run + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCreateUserMdl) +#pragma alloc_text(PAGE, CdMultipleAsync) +#pragma alloc_text(PAGE, CdMultipleXAAsync) +#pragma alloc_text(PAGE, CdNonCachedRead) +#pragma alloc_text(PAGE, CdNonCachedXARead) +#pragma alloc_text(PAGE, CdVolumeDasdWrite) +#pragma alloc_text(PAGE, CdFinishBuffers) +#pragma alloc_text(PAGE, CdPerformDevIoCtrl) +#pragma alloc_text(PAGE, CdPerformDevIoCtrlEx) +#pragma alloc_text(PAGE, CdPrepareBuffers) +#pragma alloc_text(PAGE, CdPrepareXABuffers) +#pragma alloc_text(PAGE, CdReadAudioSystemFile) +#pragma alloc_text(PAGE, CdReadSectors) +#pragma alloc_text(PAGE, CdSingleAsync) +#pragma alloc_text(PAGE, CdWaitSync) +#pragma alloc_text(PAGE, CdReadDirDataThroughCache) +#pragma alloc_text(PAGE, CdFreeDirCache) +#pragma alloc_text(PAGE, CdLbnToMmSsFf) +#pragma alloc_text(PAGE, CdHijackIrpAndFlushDevice) +#endif + + +VOID +CdLbnToMmSsFf ( + _In_ ULONG Blocks, + _Out_writes_(3) PUCHAR Msf + ) + +/*++ + +Routine Description: + + Convert Lbn to MSF format. + +Arguments: + + Msf - on output, set to 0xMmSsFf representation of blocks. + +--*/ + +{ + PAGED_CODE(); + + Blocks += 150; // Lbn 0 == 00:02:00, 1sec == 75 frames. + + Msf[0] = (UCHAR)(Blocks % 75); // Frames + Blocks /= 75; // -> Seconds + Msf[1] = (UCHAR)(Blocks % 60); // Seconds + Blocks /= 60; // -> Minutes + Msf[2] = (UCHAR)Blocks; // Minutes +} + + +__inline +TRACK_MODE_TYPE +CdFileTrackMode ( + _In_ PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine converts FCB XA file type flags to the track mode + used by the device drivers. + +Arguments: + + Fcb - Fcb representing the file to read. + +Return Value: + + TrackMode of the file represented by the Fcb. + +--*/ +{ + NT_ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_MODE2FORM2_FILE | + FCB_STATE_MODE2_FILE | + FCB_STATE_DA_FILE )); + + if (FlagOn( Fcb->FcbState, FCB_STATE_MODE2FORM2_FILE )) { + + return XAForm2; + + } else if (FlagOn( Fcb->FcbState, FCB_STATE_DA_FILE )) { + + return CDDA; + + } + + // + // FCB_STATE_MODE2_FILE + // + + return YellowMode2; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdNonCachedRead ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine performs the non-cached reads to 'cooked' sectors (2048 bytes + per sector). This is done by performing the following in a loop. + + Fill in the IoRuns array for the next block of Io. + Send the Io to the device. + Perform any cleanup on the Io runs array. + + We will not do async Io to any request that generates non-aligned Io. + Also we will not perform async Io if it will exceed the size of our + IoRuns array. These should be the unusual cases but we will raise + or return CANT_WAIT in this routine if we detect this case. + +Arguments: + + Fcb - Fcb representing the file to read. + + StartingOffset - Logical offset in the file to read from. + + ByteCount - Number of bytes to read. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + IO_RUN IoRuns[MAX_PARALLEL_IOS]; + ULONG RunCount = 0; + ULONG CleanupRunCount = 0; + + PVOID UserBuffer; + ULONG UserBufferOffset = 0; + LONGLONG CurrentOffset = StartingOffset; + ULONG RemainingByteCount = ByteCount; + ULONG ThisByteCount; + + BOOLEAN Unaligned; + BOOLEAN FlushIoBuffers = FALSE; + BOOLEAN FirstPass = TRUE; + + PAGED_CODE(); + + // + // We want to make sure the user's buffer is locked in all cases. + // + + if (IrpContext->Irp->MdlAddress == NULL) { + + CdCreateUserMdl( IrpContext, ByteCount, TRUE, IoWriteAccess ); + } + + CdMapUserBuffer( IrpContext, &UserBuffer); + + // + // Special case the root directory and path table for a music volume. + // + + if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_AUDIO_DISK ) && + ((SafeNodeType( Fcb ) == CDFS_NTC_FCB_INDEX) || + (SafeNodeType( Fcb ) == CDFS_NTC_FCB_PATH_TABLE))) { + + CdReadAudioSystemFile( IrpContext, + Fcb, + StartingOffset, + ByteCount, + UserBuffer ); + + return STATUS_SUCCESS; + } + + // + // If we're going to use the sector cache for this request, then + // mark the request waitable. + // + + if ((SafeNodeType( Fcb) == CDFS_NTC_FCB_INDEX) && + (NULL != Fcb->Vcb->SectorCacheBuffer) && + (VcbMounted == IrpContext->Vcb->VcbCondition)) { + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + KeInitializeEvent( &IrpContext->IoContext->SyncEvent, + NotificationEvent, + FALSE ); + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + } + + // + // Use a try-finally to perform the final cleanup. + // + + try { + + // + // Loop while there are more bytes to transfer. + // + + do { + + // + // Call prepare buffers to set up the next entries + // in the IoRuns array. Remember if there are any + // unaligned entries. This routine will raise CANT_WAIT + // if there are unaligned entries for an async request. + // + + RtlZeroMemory( IoRuns, sizeof( IoRuns )); + + Unaligned = CdPrepareBuffers( IrpContext, + IrpContext->Irp, + Fcb, + UserBuffer, + UserBufferOffset, + CurrentOffset, + RemainingByteCount, + IoRuns, + &CleanupRunCount, + &ThisByteCount ); + + + RunCount = CleanupRunCount; + + // + // If this is an async request and there aren't enough entries + // in the Io array then post the request. + // + + if ((ThisByteCount < RemainingByteCount) && + !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // If the entire Io is contained in a single run then + // we can pass the Io down to the driver. Send the driver down + // and wait on the result if this is synchronous. + // + + if ((RunCount == 1) && !Unaligned && FirstPass) { + + CdSingleAsync( IrpContext,&IoRuns[0], Fcb ); + + // + // No cleanup needed for the IoRuns array here. + // + + CleanupRunCount = 0; + + // + // Wait if we are synchronous, otherwise return + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdWaitSync( IrpContext ); + + Status = IrpContext->Irp->IoStatus.Status; + + // + // Our completion routine will free the Io context but + // we do want to return STATUS_PENDING. + // + + } else { + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + Status = STATUS_PENDING; + } + + try_return( NOTHING ); + } + + // + // Otherwise we will perform multiple Io to read in the data. + // + + CdMultipleAsync( IrpContext, Fcb, RunCount, IoRuns ); + + // + // If this is a synchronous request then perform any necessary + // post-processing. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + // + // Wait for the request to complete. + // + + CdWaitSync( IrpContext ); + + Status = IrpContext->Irp->IoStatus.Status; + + // + // Exit this loop if there is an error. + // + + if (!NT_SUCCESS( Status )) { + + try_return( NOTHING ); + } + + // + // Perform post read operations on the IoRuns if + // necessary. + // + + if (Unaligned && + CdFinishBuffers( IrpContext, IoRuns, RunCount, FALSE, FALSE )) { + + FlushIoBuffers = TRUE; + } + + CleanupRunCount = 0; + + // + // Exit this loop if there are no more bytes to transfer + // or we have any error. + // + + RemainingByteCount -= ThisByteCount; + CurrentOffset += ThisByteCount; + UserBuffer = Add2Ptr( UserBuffer, ThisByteCount, PVOID ); + UserBufferOffset += ThisByteCount; + + // + // Otherwise this is an asynchronous request. Always return + // STATUS_PENDING. + // + + } else { + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + CleanupRunCount = 0; + try_return( Status = STATUS_PENDING ); + break; + } + + FirstPass = FALSE; + } while (RemainingByteCount != 0); + + // + // Flush the hardware cache if we performed any copy operations. + // + + if (FlushIoBuffers) { + + KeFlushIoBuffers( IrpContext->Irp->MdlAddress, TRUE, FALSE ); + } + + try_exit: NOTHING; + } finally { + + // + // Perform final cleanup on the IoRuns if necessary. + // + + if (CleanupRunCount != 0) { + + CdFinishBuffers( IrpContext, IoRuns, CleanupRunCount, TRUE, FALSE ); + } + } + + return Status; +} + + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdNonCachedXARead ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine performs the non-cached reads for 'raw' sectors (2352 bytes + per sector). We also prepend a hard-coded RIFF header of 44 bytes to the file. + All of this is already reflected in the file size. + + We start by checking whether to prepend any portion of the RIFF header. Then we check + if the last raw sector read was from the beginning portion of this file, deallocating + that buffer if necessary. Finally we do the following in a loop. + + Fill the IoRuns array for the next block of Io. + Send the Io to the device driver. + Perform any cleanup necessary on the IoRuns array. + + We will not do any async request in this path. The request would have been + posted to a worker thread before getting to this point. + +Arguments: + + Fcb - Fcb representing the file to read. + + StartingOffset - Logical offset in the file to read from. + + ByteCount - Number of bytes to read. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + RIFF_HEADER LocalRiffHeader; + PRIFF_HEADER RiffHeader; + + RAW_READ_INFO RawReads[MAX_PARALLEL_IOS]; + IO_RUN IoRuns[MAX_PARALLEL_IOS]; + ULONG RunCount = 0; + ULONG CleanupRunCount = 0; + + PVOID UserBuffer; + ULONG UserBufferOffset = 0; + LONGLONG CurrentOffset = StartingOffset; + ULONG RemainingByteCount = ByteCount; + ULONG ThisByteCount = 0; + ULONG Address = 0; + + BOOLEAN TryingYellowbookMode2 = FALSE; + + TRACK_MODE_TYPE TrackMode; + + PAGED_CODE(); + + // + // We want to make sure the user's buffer is locked in all cases. + // + + if (IrpContext->Irp->MdlAddress == NULL) { + + CdCreateUserMdl( IrpContext, ByteCount, TRUE, IoWriteAccess ); + } + + // + // The byte count was rounded up to a logical sector boundary. It has + // nothing to do with the raw sectors on disk. Limit the remaining + // byte count to file size. + // + + if (CurrentOffset + RemainingByteCount > Fcb->FileSize.QuadPart) { + + RemainingByteCount = (ULONG) (Fcb->FileSize.QuadPart - CurrentOffset); + } + + CdMapUserBuffer( IrpContext, &UserBuffer); + + // + // Use a try-finally to perform the final cleanup. + // + + try { + + // + // If the initial offset lies within the RIFF header then copy the + // necessary bytes to the user's buffer. + // + + if (CurrentOffset < sizeof( RIFF_HEADER )) { + + // + // Copy the appropriate RIFF header. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_DA_FILE )) { + + // + // Create the pseudo entries for a music disk. + // + + if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_AUDIO_DISK )) { + + PAUDIO_PLAY_HEADER AudioPlayHeader; + PTRACK_DATA TrackData; + + AudioPlayHeader = (PAUDIO_PLAY_HEADER) &LocalRiffHeader; + TrackData = &Fcb->Vcb->CdromToc->TrackData[Fcb->XAFileNumber]; + + // + // Copy the data header into our local buffer. + // + + RtlCopyMemory( AudioPlayHeader, + CdAudioPlayHeader, + sizeof( AUDIO_PLAY_HEADER )); + + // + // Copy the serial number into the Id field. Also + // the track number in the TOC. + // + + AudioPlayHeader->DiskID = Fcb->Vcb->Vpb->SerialNumber; + AudioPlayHeader->TrackNumber = TrackData->TrackNumber; + + // + // One frame == One sector. + // One second == 75 frames (winds up being a 44.1khz sample) + // + // Note: LBN 0 == 0:2:0 (MSF) + // + + // + // Fill in the address (both MSF and Lbn format) and length fields. + // + + SwapCopyUchar4( &Address, TrackData->Address); + CdLbnToMmSsFf( Address, AudioPlayHeader->TrackAddress); + + SwapCopyUchar4( &AudioPlayHeader->StartingSector, TrackData->Address); + + // + // Go to the next track and find the starting point. + // + + TrackData = &Fcb->Vcb->CdromToc->TrackData[Fcb->XAFileNumber + 1]; + + SwapCopyUchar4( &AudioPlayHeader->SectorCount, TrackData->Address); + + // + // Now compute the difference. If there is an error then use + // a length of zero. + // + + if (AudioPlayHeader->SectorCount < AudioPlayHeader->StartingSector) { + + AudioPlayHeader->SectorCount = 0; + + } else { + + AudioPlayHeader->SectorCount -= AudioPlayHeader->StartingSector; + } + + // + // Use the sector count to determine the MSF length. Bias by 150 to make + // it an "lbn" since the conversion routine corrects for Lbn 0 == 0:2:0; + // + + Address = AudioPlayHeader->SectorCount - 150; + CdLbnToMmSsFf( Address, AudioPlayHeader->TrackLength); + + ThisByteCount = sizeof( RIFF_HEADER ) - (ULONG) CurrentOffset; + + RtlCopyMemory( UserBuffer, + Add2Ptr( AudioPlayHeader, + sizeof( RIFF_HEADER ) - ThisByteCount, + PCHAR ), + ThisByteCount ); + + // + // CD-XA CDDA + // + + } else { + + // + // The WAVE header format is actually much closer to an audio play + // header in format but we only need to modify the filesize fields. + // + + RiffHeader = &LocalRiffHeader; + + // + // Copy the data header into our local buffer and add the file size to it. + // + + RtlCopyMemory( RiffHeader, + CdXAAudioPhileHeader, + sizeof( RIFF_HEADER )); + + RiffHeader->ChunkSize += Fcb->FileSize.LowPart; + RiffHeader->RawSectors += Fcb->FileSize.LowPart; + + ThisByteCount = sizeof( RIFF_HEADER ) - (ULONG) CurrentOffset; + RtlCopyMemory( UserBuffer, + Add2Ptr( RiffHeader, + sizeof( RIFF_HEADER ) - ThisByteCount, + PCHAR ), + ThisByteCount ); + } + + // + // CD-XA non-audio + // + + } else { + + NT_ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_MODE2_FILE | FCB_STATE_MODE2FORM2_FILE )); + + RiffHeader = &LocalRiffHeader; + + // + // Copy the data header into our local buffer and add the file size to it. + // + + RtlCopyMemory( RiffHeader, + CdXAFileHeader, + sizeof( RIFF_HEADER )); + + RiffHeader->ChunkSize += Fcb->FileSize.LowPart; + RiffHeader->RawSectors += Fcb->FileSize.LowPart; + + RiffHeader->Attributes = (USHORT) Fcb->XAAttributes; + RiffHeader->FileNumber = (UCHAR) Fcb->XAFileNumber; + + ThisByteCount = sizeof( RIFF_HEADER ) - (ULONG) CurrentOffset; + RtlCopyMemory( UserBuffer, + Add2Ptr( RiffHeader, + sizeof( RIFF_HEADER ) - ThisByteCount, + PCHAR ), + ThisByteCount ); + } + + // + // Adjust the starting offset and byte count to reflect that + // we copied over the RIFF bytes. + // + + UserBuffer = Add2Ptr( UserBuffer, ThisByteCount, PVOID ); + UserBufferOffset += ThisByteCount; + CurrentOffset += ThisByteCount; + RemainingByteCount -= ThisByteCount; + } + + // + // Set up the appropriate trackmode + // + + TrackMode = CdFileTrackMode(Fcb); + + // + // Loop while there are more bytes to transfer. + // + + while (RemainingByteCount != 0) { + + // + // Call prepare buffers to set up the next entries + // in the IoRuns array. Remember if there are any + // unaligned entries. If we're just retrying the previous + // runs with a different track mode, then don't do anything here. + // + + if (!TryingYellowbookMode2) { + + RtlZeroMemory( IoRuns, sizeof( IoRuns )); + RtlZeroMemory( RawReads, sizeof( RawReads )); + + CdPrepareXABuffers( IrpContext, + IrpContext->Irp, + Fcb, + UserBuffer, + UserBufferOffset, + CurrentOffset, + RemainingByteCount, + IoRuns, + &CleanupRunCount, + &ThisByteCount ); + } + + // + // Perform multiple Io to read in the data. Note that + // there may be no Io to do if we were able to use an + // existing buffer from the Vcb. + // + + if (CleanupRunCount != 0) { + + RunCount = CleanupRunCount; + + CdMultipleXAAsync( IrpContext, + RunCount, + IoRuns, + RawReads, + TrackMode ); + // + // Wait for the request to complete. + // + + CdWaitSync( IrpContext ); + + Status = IrpContext->Irp->IoStatus.Status; + + // + // Exit this loop if there is an error. + // + + if (!NT_SUCCESS( Status )) { + + if (!TryingYellowbookMode2 && + FlagOn( Fcb->FcbState, FCB_STATE_MODE2FORM2_FILE )) { + + // + // There are wacky cases where someone has mastered as CD-XA + // but the sectors they claim are Mode2Form2 are really, according + // to ATAPI devices, Yellowbook Mode2. We will try once more + // with these. Kodak PHOTO-CD has been observed to do this. + // + + TryingYellowbookMode2 = TRUE; + TrackMode = YellowMode2; + + // + // Clear our 'cumulative' error status value + // + + IrpContext->IoContext->Status = STATUS_SUCCESS; + + continue; + } + + try_return( NOTHING ); + } + + CleanupRunCount = 0; + + if (TryingYellowbookMode2) { + + // + // We succesfully got data when we tried switching the trackmode, + // so change the state of the FCB to remember that. + // + + SetFlag( Fcb->FcbState, FCB_STATE_MODE2_FILE ); + ClearFlag( Fcb->FcbState, FCB_STATE_MODE2FORM2_FILE ); + + TryingYellowbookMode2 = FALSE; + } + + // + // Perform post read operations on the IoRuns if + // necessary. + // + + CdFinishBuffers( IrpContext, IoRuns, RunCount, FALSE, TRUE ); + } + + // + // Adjust our loop variants. + // + + RemainingByteCount -= ThisByteCount; + CurrentOffset += ThisByteCount; + UserBuffer = Add2Ptr( UserBuffer, ThisByteCount, PVOID ); + UserBufferOffset += ThisByteCount; + } + + // + // Always flush the hardware cache. + // + + KeFlushIoBuffers( IrpContext->Irp->MdlAddress, TRUE, FALSE ); + + try_exit: NOTHING; + } finally { + + // + // Perform final cleanup on the IoRuns if necessary. + // + + if (CleanupRunCount != 0) { + + CdFinishBuffers( IrpContext, IoRuns, CleanupRunCount, TRUE, FALSE ); + } + } + + return Status; +} + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdVolumeDasdWrite ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine performs the non-cached writes to 'cooked' sectors (2048 bytes + per sector). This is done by filling the IoRun for the desired request + and send it down to the device. + +Arguments: + + Fcb - Fcb representing the file to read. + + StartingOffset - Logical offset in the file to read from. + + ByteCount - Number of bytes to read. + +Return Value: + + NTSTATUS - Status indicating the result of the operation. + +--*/ + +{ + NTSTATUS Status; + IO_RUN IoRun; + + PAGED_CODE(); + + // + // We want to make sure the user's buffer is locked in all cases. + // + + CdLockUserBuffer( IrpContext, ByteCount, IoReadAccess ); + + // + // The entire Io can be contained in a single run, just pass + // the Io down to the driver. Send the driver down + // and wait on the result if this is synchronous. + // + + RtlZeroMemory( &IoRun, sizeof( IoRun ) ); + + IoRun.DiskOffset = StartingOffset; + IoRun.DiskByteCount = ByteCount; + + CdSingleAsync( IrpContext, &IoRun, Fcb ); + + // + // Wait if we are synchronous, otherwise return + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdWaitSync( IrpContext ); + + Status = IrpContext->Irp->IoStatus.Status; + + // + // Our completion routine will free the Io context but + // we do want to return STATUS_PENDING. + // + + } else { + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + Status = STATUS_PENDING; + } + + return Status; +} + + + +BOOLEAN +CdReadSectors ( + _In_ PIRP_CONTEXT IrpContext, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount, + _In_ BOOLEAN ReturnError, + _Out_writes_bytes_(ByteCount) PVOID Buffer, + _In_ PDEVICE_OBJECT TargetDeviceObject + ) + +/*++ + +Routine Description: + + This routine is called to transfer sectors from the disk to a + specified buffer. It is used for mount and volume verify operations. + + This routine is synchronous, it will not return until the operation + is complete or until the operation fails. + + The routine allocates an IRP and then passes this IRP to a lower + level driver. Errors may occur in the allocation of this IRP or + in the operation of the lower driver. + +Arguments: + + StartingOffset - Logical offset on the disk to start the read. This + must be on a sector boundary, no check is made here. + + ByteCount - Number of bytes to read. This is an integral number of + 2K sectors, no check is made here to confirm this. + + ReturnError - Indicates whether we should return TRUE or FALSE + to indicate an error or raise an error condition. This only applies + to the result of the IO. Any other error may cause a raise. + + Buffer - Buffer to transfer the disk data into. + + TargetDeviceObject - The device object for the volume to be read. + +Return Value: + + BOOLEAN - Depending on 'RaiseOnError' flag above. TRUE if operation + succeeded, FALSE otherwise. + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + PIRP Irp; + + PAGED_CODE(); + + // + // Initialize the event. + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // Attempt to allocate the IRP. If unsuccessful, raise + // STATUS_INSUFFICIENT_RESOURCES. + // + + Irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ, + TargetDeviceObject, + Buffer, + ByteCount, + (PLARGE_INTEGER) &StartingOffset, + &Event, + &IrpContext->Irp->IoStatus ); + + if (Irp == NULL) { + + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Ignore the change line (verify) for mount and verify requests + // + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + + // + // Send the request down to the driver. If an error occurs return + // it to the caller. + // + + Status = IoCallDriver( TargetDeviceObject, Irp ); + + // + // If the status was STATUS_PENDING then wait on the event. + // + + if (Status == STATUS_PENDING) { + + Status = KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + // + // On a successful wait pull the status out of the IoStatus block. + // + + if (NT_SUCCESS( Status )) { + + Status = IrpContext->Irp->IoStatus.Status; + } + } + + // + // Check whether we should raise in the error case. + // + + if (!NT_SUCCESS( Status )) { + + if (!ReturnError) { + + CdNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // We don't raise, but return FALSE to indicate an error. + // + + return FALSE; + + // + // The operation completed successfully. + // + + } else { + + return TRUE; + } +} + + +NTSTATUS +CdCreateUserMdl ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG BufferLength, + _In_ BOOLEAN RaiseOnError, + _In_ LOCK_OPERATION Operation + ) + +/*++ + +Routine Description: + + This routine locks the specified buffer for read access (we only write into + the buffer). The file system requires this routine since it does not + ask the I/O system to lock its buffers for direct I/O. This routine + may only be called from the Fsd while still in the user context. + + This routine is only called if there is not already an Mdl. + +Arguments: + + BufferLength - Length of user buffer. + + RaiseOnError - Indicates if our caller wants this routine to raise on + an error condition. + + Operation - IoWriteAccess or IoReadAccess + +Return Value: + + NTSTATUS - Status from this routine. Error status only returned if + RaiseOnError is FALSE. + +--*/ + +{ + NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES; + PMDL Mdl; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Operation ); + UNREFERENCED_PARAMETER( IrpContext ); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( IrpContext->Irp ); + NT_ASSERT( IrpContext->Irp->MdlAddress == NULL ); + + // + // Allocate the Mdl, and Raise if we fail. + // + + Mdl = IoAllocateMdl( IrpContext->Irp->UserBuffer, + BufferLength, + FALSE, + FALSE, + IrpContext->Irp ); + + if (Mdl != NULL) { + + // + // Now probe the buffer described by the Irp. If we get an exception, + // deallocate the Mdl and return the appropriate "expected" status. + // + + try { + + MmProbeAndLockPages( Mdl, IrpContext->Irp->RequestorMode, IoWriteAccess ); + + Status = STATUS_SUCCESS; + +#pragma warning(suppress: 6320) + } except(EXCEPTION_EXECUTE_HANDLER) { + + Status = GetExceptionCode(); + + IoFreeMdl( Mdl ); + IrpContext->Irp->MdlAddress = NULL; + + if (!FsRtlIsNtstatusExpected( Status )) { + + Status = STATUS_INVALID_USER_BUFFER; + } + } + } + + // + // Check if we are to raise or return + // + + if (Status != STATUS_SUCCESS) { + + if (RaiseOnError) { + + CdRaiseStatus( IrpContext, Status ); + } + } + + // + // Return the status code. + // + + return Status; +} + + +NTSTATUS +CdPerformDevIoCtrlEx ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG IoControlCode, + _In_ PDEVICE_OBJECT Device, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _In_ BOOLEAN InternalDeviceIoControl, + _In_ BOOLEAN OverrideVerify, + _Out_opt_ PIO_STATUS_BLOCK Iosb + ) + +/*++ + +Routine Description: + + This routine is called to perform DevIoCtrl functions internally within + the filesystem. We take the status from the driver and return it to our + caller. + +Arguments: + + IoControlCode - Code to send to driver. + + Device - This is the device to send the request to. + + OutPutBuffer - Pointer to output buffer. + + OutputBufferLength - Length of output buffer above. + + InternalDeviceIoControl - Indicates if this is an internal or external + Io control code. + + OverrideVerify - Indicates if we should tell the driver not to return + STATUS_VERIFY_REQUIRED for mount and verify. + + Iosb - If specified, we return the results of the operation here. + +Return Value: + + NTSTATUS - Status returned by next lower driver. + +--*/ + +{ + NTSTATUS Status; + PIRP Irp; + KEVENT Event; + IO_STATUS_BLOCK LocalIosb; + PIO_STATUS_BLOCK IosbToUse = &LocalIosb; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Check if the user gave us an Iosb. + // + + if (ARGUMENT_PRESENT( Iosb )) { + + IosbToUse = Iosb; + } + + IosbToUse->Status = 0; + IosbToUse->Information = 0; + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + Irp = IoBuildDeviceIoControlRequest( IoControlCode, + Device, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength, + InternalDeviceIoControl, + &Event, + IosbToUse ); + + if (Irp == NULL) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (OverrideVerify) { + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + } + + Status = IoCallDriver( Device, Irp ); + + // + // We check for device not ready by first checking Status + // and then if status pending was returned, the Iosb status + // value. + // + + if (Status == STATUS_PENDING) { + + (VOID) KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL ); + + Status = IosbToUse->Status; + } + + NT_ASSERT( !(OverrideVerify && (STATUS_VERIFY_REQUIRED == Status))); + + return Status; +} + + +NTSTATUS +FASTCALL +CdPerformDevIoCtrl ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG IoControlCode, + _In_ PDEVICE_OBJECT Device, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _In_ BOOLEAN InternalDeviceIoControl, + _In_ BOOLEAN OverrideVerify, + _Out_opt_ PIO_STATUS_BLOCK Iosb + ) +{ + PAGED_CODE(); + + return CdPerformDevIoCtrlEx( IrpContext, + IoControlCode, + Device, + NULL, + 0, + OutputBuffer, + OutputBufferLength, + InternalDeviceIoControl, + OverrideVerify, + Iosb); +} + + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +CdPrepareBuffers ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp, + _In_ PFCB Fcb, + _In_reads_bytes_(ByteCount) PVOID UserBuffer, + _In_ ULONG UserBufferOffset, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount, + _Out_ PIO_RUN IoRuns, + _Out_ PULONG RunCount, + _Out_ PULONG ThisByteCount + ) + +/*++ + +Routine Description: + + This routine is the worker routine which looks up each run of an IO + request and stores an entry for it in the IoRuns array. If the run + begins on an unaligned disk boundary then we will allocate a buffer + and Mdl for the unaligned portion and put it in the IoRuns entry. + + This routine will raise CANT_WAIT if an unaligned transfer is encountered + and this request can't wait. + +Arguments: + + Irp - Originating Irp for this request. + + Fcb - This is the Fcb for this data stream. It may be a file, directory, + path table or the volume file. + + UserBuffer - Current position in the user's buffer. + + UserBufferOffset - Offset from the start of the original user buffer. + + StartingOffset - Offset in the stream to begin the read. + + ByteCount - Number of bytes to read. We will fill the IoRuns array up + to this point. We will stop early if we exceed the maximum number + of parallel Ios we support. + + IoRuns - Pointer to the IoRuns array. The entire array is zeroes when + this routine is called. + + RunCount - Number of entries in the IoRuns array filled here. + + ThisByteCount - Number of bytes described by the IoRun entries. Will + not exceed the ByteCount passed in. + +Return Value: + + BOOLEAN - TRUE if one of the entries in an unaligned buffer (provided + this is synchronous). FALSE otherwise. + +--*/ + +{ + BOOLEAN FoundUnaligned = FALSE; + PIO_RUN ThisIoRun = IoRuns; + + // + // Following indicate where we are in the current transfer. Current + // position in the file and number of bytes yet to transfer from + // this position. + // + + ULONG RemainingByteCount = ByteCount; + LONGLONG CurrentFileOffset = StartingOffset; + + // + // Following indicate the state of the user's buffer. We have + // the destination of the next transfer and its offset in the + // buffer. We also have the next available position in the buffer + // available for a scratch buffer. We will align this up to a sector + // boundary. + // + + PVOID CurrentUserBuffer = UserBuffer; + ULONG CurrentUserBufferOffset = UserBufferOffset; + + // + // The following is the next contiguous bytes on the disk to + // transfer. Read from the allocation package. + // + + LONGLONG DiskOffset = 0; + ULONG CurrentByteCount = RemainingByteCount; + + PAGED_CODE(); + + // + // Initialize the RunCount and ByteCount. + // + + *RunCount = 0; + *ThisByteCount = 0; + + // + // Loop while there are more bytes to process or there are + // available entries in the IoRun array. + // + + while (TRUE) { + + *RunCount += 1; + + // + // Initialize the current position in the IoRuns array. + // Find the user's buffer for this portion of the transfer. + // + + ThisIoRun->UserBuffer = CurrentUserBuffer; + + // + // Find the allocation information for the current offset in the + // stream. + // + + CdLookupAllocation( IrpContext, + Fcb, + CurrentFileOffset, + &DiskOffset, + &CurrentByteCount ); + + // + // Limit ourselves to the data requested. + // + + if (CurrentByteCount > RemainingByteCount) { + + CurrentByteCount = RemainingByteCount; + } + + // + // Handle the case where this is an unaligned transfer. The + // following must all be true for this to be an aligned transfer. + // + // Disk offset on a 2048 byte boundary (Start of transfer) + // + // Byte count is a multiple of 2048 (Length of transfer) + // + // If the ByteCount is at least one sector then do the + // unaligned transfer only for the tail. We can use the + // user's buffer for the aligned portion. + // + + if (FlagOn( (ULONG) DiskOffset, SECTOR_MASK ) || + (FlagOn( (ULONG) CurrentByteCount, SECTOR_MASK ) && + (CurrentByteCount < SECTOR_SIZE))) { + + NT_ASSERT( SafeNodeType(Fcb) != CDFS_NTC_FCB_INDEX); + + // + // If we can't wait then raise. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Remember the offset and the number of bytes out of + // the transfer buffer to copy into the user's buffer. + // We will truncate the current read to end on a sector + // boundary. + // + + ThisIoRun->TransferBufferOffset = SectorOffset( DiskOffset ); + + // + // Make sure this transfer ends on a sector boundary. + // + + ThisIoRun->DiskOffset = LlSectorTruncate( DiskOffset ); + + // + // We need to allocate an auxilary buffer for the next sector. + // Read up to a page containing the partial data. + // + + ThisIoRun->DiskByteCount = SectorAlign( ThisIoRun->TransferBufferOffset + CurrentByteCount ); + + if (ThisIoRun->DiskByteCount > PAGE_SIZE) { + + ThisIoRun->DiskByteCount = PAGE_SIZE; + } + + if (ThisIoRun->TransferBufferOffset + CurrentByteCount > ThisIoRun->DiskByteCount) { + + CurrentByteCount = ThisIoRun->DiskByteCount - ThisIoRun->TransferBufferOffset; + } + + ThisIoRun->TransferByteCount = CurrentByteCount; + + // + // Allocate a buffer for the non-aligned transfer. + // + + ThisIoRun->TransferBuffer = FsRtlAllocatePoolWithTag( CdNonPagedPool, PAGE_SIZE, TAG_IO_BUFFER ); + + // + // Allocate and build the Mdl to describe this buffer. + // + + ThisIoRun->TransferMdl = IoAllocateMdl( ThisIoRun->TransferBuffer, + PAGE_SIZE, + FALSE, + FALSE, + NULL ); + + ThisIoRun->TransferVirtualAddress = ThisIoRun->TransferBuffer; + + if (ThisIoRun->TransferMdl == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + MmBuildMdlForNonPagedPool( ThisIoRun->TransferMdl ); + + // + // Remember we found an unaligned transfer. + // + + FoundUnaligned = TRUE; + + // + // Otherwise we use the buffer and Mdl from the original request. + // + + } else { + + // + // Truncate the read length to a sector-aligned value. We know + // the length must be at least one sector or we wouldn't be + // here now. + // + + CurrentByteCount = SectorTruncate( CurrentByteCount ); + + // + // Read these sectors from the disk. + // + + ThisIoRun->DiskOffset = DiskOffset; + ThisIoRun->DiskByteCount = CurrentByteCount; + + // + // Use the user's buffer and Mdl as our transfer buffer + // and Mdl. + // + + ThisIoRun->TransferBuffer = CurrentUserBuffer; + ThisIoRun->TransferMdl = Irp->MdlAddress; + ThisIoRun->TransferVirtualAddress = Add2Ptr( Irp->UserBuffer, + CurrentUserBufferOffset, + PVOID ); + } + + // + // Update our position in the transfer and the RunCount and + // ByteCount for the user. + // + + RemainingByteCount -= CurrentByteCount; + + // + // Break out if no more positions in the IoRuns array or + // we have all of the bytes accounted for. + // + + *ThisByteCount += CurrentByteCount; + + if ((RemainingByteCount == 0) || (*RunCount == MAX_PARALLEL_IOS)) { + + break; + } + + // + // Update our pointers for the user's buffer. + // + + ThisIoRun += 1; + CurrentUserBuffer = Add2Ptr( CurrentUserBuffer, CurrentByteCount, PVOID ); + CurrentUserBufferOffset += CurrentByteCount; + CurrentFileOffset += CurrentByteCount; + } + + return FoundUnaligned; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdPrepareXABuffers ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp, + _In_ PFCB Fcb, + _In_reads_bytes_(ByteCount) PVOID UserBuffer, + _In_ ULONG UserBufferOffset, + _In_ LONGLONG StartingOffset, + _In_ ULONG ByteCount, + _Out_ PIO_RUN IoRuns, + _Out_ PULONG RunCount, + _Out_ PULONG ThisByteCount + ) + +/*++ + +Routine Description: + + This routine is the worker routine which looks up the individual runs + of an IO request and stores an entry for it in the IoRuns array. The + worker routine is for XA files where we need to convert the raw offset + in the file to logical cooked sectors. We store one raw sector in + the Vcb. If the current read is to that sector then we can simply copy + whatever bytes are needed from that sector. + +Arguments: + + Irp - Originating Irp for this request. + + Fcb - This is the Fcb for this data stream. It must be a data stream. + + UserBuffer - Current position in the user's buffer. + + UserBufferOffset - Offset of this buffer from the beginning of the user's + buffer for the original request. + + StartingOffset - Offset in the stream to begin the read. + + ByteCount - Number of bytes to read. We will fill the IoRuns array up + to this point. We will stop early if we exceed the maximum number + of parallel Ios we support. + + IoRuns - Pointer to the IoRuns array. The entire array is zeroes when + this routine is called. + + RunCount - Number of entries in the IoRuns array filled here. + + ThisByteCount - Number of bytes described by the IoRun entries. Will + not exceed the ByteCount passed in. + +Return Value: + + None + +--*/ + +{ + PIO_RUN ThisIoRun = IoRuns; + BOOLEAN PerformedCopy; + + // + // The following deal with where we are in the range of raw sectors. + // Note that we will bias the input file offset by the RIFF header + // to deal directly with the raw sectors. + // + + ULONG RawSectorOffset; + ULONG RemainingRawByteCount = ByteCount; + LONGLONG CurrentRawOffset = StartingOffset - sizeof( RIFF_HEADER ); + + // + // The following is the offset into the cooked sectors for the file. + // + + LONGLONG CurrentCookedOffset; + ULONG RemainingCookedByteCount; + + // + // Following indicate the state of the user's buffer. We have + // the destination of the next transfer and its offset in the + // buffer. We also have the next available position in the buffer + // available for a scratch buffer. + // + + PVOID CurrentUserBuffer = UserBuffer; + ULONG CurrentUserBufferOffset = UserBufferOffset; + + // + // The following is the next contiguous bytes on the disk to + // transfer. These are represented by cooked byte offset and length. + // We also compute the number of raw bytes in the current transfer. + // + + LONGLONG DiskOffset = 0; + ULONG CurrentCookedByteCount = 0; + ULONG CurrentRawByteCount; + + PAGED_CODE(); + + // + // We need to maintain our position as we walk through the sectors on the disk. + // We keep separate values for the cooked offset as well as the raw offset. + // These are initialized on sector boundaries and we move through these + // the file sector-by-sector. + // + // Try to do 32-bit math. + // + + if (((PLARGE_INTEGER) &CurrentRawOffset)->HighPart == 0) { + + // + // Prefix/fast: Note that the following are safe since we only + // take this path for 32bit offsets. + // + + CurrentRawOffset = (LONGLONG) ((ULONG) CurrentRawOffset / RAW_SECTOR_SIZE); + +#pragma prefast( suppress: __WARNING_RESULTOFSHIFTCASTTOLARGERSIZE, "This is fine beacuse raw sector size > sector shift" ) + CurrentCookedOffset = (LONGLONG) ((ULONG) CurrentRawOffset << SECTOR_SHIFT ); + + CurrentRawOffset = (LONGLONG) ((ULONG) CurrentRawOffset * RAW_SECTOR_SIZE); + + // + // Otherwise we need to do 64-bit math (sigh). + // + + } else { + + CurrentRawOffset /= RAW_SECTOR_SIZE; + + CurrentCookedOffset = CurrentRawOffset << SECTOR_SHIFT; + + CurrentRawOffset *= RAW_SECTOR_SIZE; + } + + // + // Now compute the full number of sectors to be read. Count all of the raw + // sectors that need to be read and convert to cooked bytes. + // + + RawSectorOffset = (ULONG) ( StartingOffset - CurrentRawOffset) - sizeof( RIFF_HEADER ); + CurrentRawByteCount = (RawSectorOffset + RemainingRawByteCount + RAW_SECTOR_SIZE - 1) / RAW_SECTOR_SIZE; + + RemainingCookedByteCount = CurrentRawByteCount << SECTOR_SHIFT; + + // + // Initialize the RunCount and ByteCount. + // + + *RunCount = 0; + *ThisByteCount = 0; + + // + // Loop while there are more bytes to process or there are + // available entries in the IoRun array. + // + + while (TRUE) { + + PerformedCopy = FALSE; + *RunCount += 1; + + // + // Initialize the current position in the IoRuns array. Find the + // eventual destination in the user's buffer for this portion of the transfer. + // + + ThisIoRun->UserBuffer = CurrentUserBuffer; + + // + // Find the allocation information for the current offset in the + // stream. + // + + CdLookupAllocation( IrpContext, + Fcb, + CurrentCookedOffset, + &DiskOffset, + &CurrentCookedByteCount ); + // + // Maybe we got lucky and this is the same sector as in the + // Vcb. + // + + if (DiskOffset == Fcb->Vcb->XADiskOffset) { + + // + // We will perform safe synchronization. Check again that + // this is the correct sector. + // + + CdLockVcb( IrpContext, Fcb->Vcb ); + + if ((DiskOffset == Fcb->Vcb->XADiskOffset) && + (Fcb->Vcb->XASector != NULL)) { + + // + // Copy any bytes we can from the current sector. + // + + CurrentRawByteCount = RAW_SECTOR_SIZE - RawSectorOffset; + + // + // Check whether we don't go to the end of the sector. + // + + if (CurrentRawByteCount > RemainingRawByteCount) { + + CurrentRawByteCount = RemainingRawByteCount; + } + + RtlCopyMemory( CurrentUserBuffer, + Add2Ptr( Fcb->Vcb->XASector, RawSectorOffset, PCHAR ), + CurrentRawByteCount ); + + CdUnlockVcb( IrpContext, Fcb->Vcb ); + + // + // Adjust the run count and pointer in the IoRuns array + // to show that we didn't use a position. + // + + *RunCount -= 1; + ThisIoRun -= 1; + + // + // Remember that we performed a copy operation. + // + + PerformedCopy = TRUE; + + CurrentCookedByteCount = SECTOR_SIZE; + + } else { + + // + // The safe test showed no available buffer. Drop down to common code to + // perform the Io. + // + + CdUnlockVcb( IrpContext, Fcb->Vcb ); + } + } + + // + // No work in this pass if we did a copy operation. + // + + if (!PerformedCopy) { + + // + // Limit ourselves by the number of remaining cooked bytes. + // + + if (CurrentCookedByteCount > RemainingCookedByteCount) { + + CurrentCookedByteCount = RemainingCookedByteCount; + } + + ThisIoRun->DiskOffset = DiskOffset; + ThisIoRun->TransferBufferOffset = RawSectorOffset; + + // + // We will always need to perform copy operations for XA files. + // We allocate an auxillary buffer to read the start of the + // transfer. Then we can use a range of the user's buffer to + // perform the next range of the transfer. Finally we may + // need to allocate a buffer for the tail of the transfer. + // + // We can use the user's buffer (at the current scratch buffer) if the + // following are true: + // + // If we are to store the beginning of the raw sector in the user's buffer. + // The current scratch buffer precedes the destination in the user's buffer + // (and hence also lies within it) + // There are enough bytes remaining in the buffer for at least one + // raw sector. + // + + if ((RawSectorOffset == 0) && + (RemainingRawByteCount >= RAW_SECTOR_SIZE)) { + + // + // We can use the scratch buffer. We must ensure we don't send down reads + // greater than the device can handle, since the driver is unable to split + // raw requests. + // + + if (CurrentCookedByteCount <= Fcb->Vcb->MaximumTransferRawSectors * SECTOR_SIZE) { + + CurrentRawByteCount = (SectorAlign( CurrentCookedByteCount) >> SECTOR_SHIFT) * RAW_SECTOR_SIZE; + + } else { + + CurrentCookedByteCount = Fcb->Vcb->MaximumTransferRawSectors * SECTOR_SIZE; + CurrentRawByteCount = Fcb->Vcb->MaximumTransferRawSectors * RAW_SECTOR_SIZE; + } + + // + // Now make sure we are within the page transfer limit. + // + + while (ADDRESS_AND_SIZE_TO_SPAN_PAGES(CurrentUserBuffer, RawSectorAlign( CurrentRawByteCount)) > + Fcb->Vcb->MaximumPhysicalPages ) { + + CurrentRawByteCount -= RAW_SECTOR_SIZE; + CurrentCookedByteCount -= SECTOR_SIZE; + } + + // + // Trim the number of bytes to read if it won't fit into the current buffer. Take + // account of the fact that we must read in whole raw sector multiples. + // + + while (RawSectorAlign( CurrentRawByteCount) > RemainingRawByteCount) { + + CurrentRawByteCount -= RAW_SECTOR_SIZE; + CurrentCookedByteCount -= SECTOR_SIZE; + } + + // + // Now trim the maximum number of raw bytes to the remaining bytes. + // + + if (CurrentRawByteCount > RemainingRawByteCount) { + + CurrentRawByteCount = RemainingRawByteCount; + } + + // + // Update the IO run array. We point to the scratch buffer as + // well as the buffer and Mdl in the original Irp. + // + + ThisIoRun->DiskByteCount = SectorAlign( CurrentCookedByteCount); + + // + // Point to the user's buffer and Mdl for this transfer. + // + + ThisIoRun->TransferBuffer = CurrentUserBuffer; + ThisIoRun->TransferMdl = Irp->MdlAddress; + ThisIoRun->TransferVirtualAddress = Add2Ptr( Irp->UserBuffer, + CurrentUserBufferOffset, + PVOID); + + } else { + + // + // We need to determine the number of bytes to transfer and the + // offset into this page to begin the transfer. + // + // We will transfer only one raw sector. + // + + ThisIoRun->DiskByteCount = SECTOR_SIZE; + + CurrentCookedByteCount = SECTOR_SIZE; + + ThisIoRun->TransferByteCount = RAW_SECTOR_SIZE - RawSectorOffset; + ThisIoRun->TransferBufferOffset = RawSectorOffset; + + if (ThisIoRun->TransferByteCount > RemainingRawByteCount) { + + ThisIoRun->TransferByteCount = RemainingRawByteCount; + } + + CurrentRawByteCount = ThisIoRun->TransferByteCount; + + // + // We need to allocate an auxillary buffer. We will allocate + // a single page. Then we will build an Mdl to describe the buffer. + // + + ThisIoRun->TransferBuffer = FsRtlAllocatePoolWithTag( CdNonPagedPool, PAGE_SIZE, TAG_IO_BUFFER ); + + // + // Allocate and build the Mdl to describe this buffer. + // + + ThisIoRun->TransferMdl = IoAllocateMdl( ThisIoRun->TransferBuffer, + PAGE_SIZE, + FALSE, + FALSE, + NULL ); + + ThisIoRun->TransferVirtualAddress = ThisIoRun->TransferBuffer; + + if (ThisIoRun->TransferMdl == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + MmBuildMdlForNonPagedPool( ThisIoRun->TransferMdl ); + } + } + + // + // Update the byte count for our caller. + // + + RemainingRawByteCount -= CurrentRawByteCount; + *ThisByteCount += CurrentRawByteCount; + + // + // Break out if no more positions in the IoRuns array or + // we have all of the bytes accounted for. + // + + if ((RemainingRawByteCount == 0) || (*RunCount == MAX_PARALLEL_IOS)) { + + break; + } + + // + // Update our local pointers to allow for the current range of bytes. + // + + ThisIoRun += 1; + + CurrentUserBuffer = Add2Ptr( CurrentUserBuffer, CurrentRawByteCount, PVOID ); + CurrentUserBufferOffset += CurrentRawByteCount; + + RawSectorOffset = 0; + + CurrentCookedOffset += CurrentCookedByteCount; + RemainingCookedByteCount -= CurrentCookedByteCount; + } + + return; +} + + +// +// Local support routine +// + +BOOLEAN +CdFinishBuffers ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PIO_RUN IoRuns, + _In_ ULONG RunCount, + _In_ BOOLEAN FinalCleanup, + _In_ BOOLEAN SaveXABuffer + ) + +/*++ + +Routine Description: + + This routine is called to perform any data transferred required for + unaligned Io or to perform the final cleanup of the IoRuns array. + + In all cases this is where we will deallocate any buffer and mdl + allocated to perform the unaligned transfer. If this is not the + final cleanup then we also transfer the bytes to the user buffer + and flush the hardware cache. + + We walk backwards through the run array because we may be shifting data + in the user's buffer. Typical case is where we allocated a buffer for + the first part of a read and then used the user's buffer for the + next section (but stored it at the beginning of the buffer. + +Arguments: + + IoRuns - Pointer to the IoRuns array. + + RunCount - Number of entries in the IoRuns array filled here. + + FinalCleanup - Indicates if we should be deallocating temporary buffers + (TRUE) or transferring bytes for a unaligned transfers and + deallocating the buffers (FALSE). Flush the system cache if + transferring data. + + SaveXABuffer - TRUE if we should try to save an XA buffer, FALSE otherwise + +Return Value: + + BOOLEAN - TRUE if this request needs the Io buffers to be flushed, FALSE otherwise. + +--*/ + +{ + BOOLEAN FlushIoBuffers = FALSE; + + ULONG RemainingEntries = RunCount; + PIO_RUN ThisIoRun = &IoRuns[RunCount - 1]; + PVCB Vcb; + + PAGED_CODE(); + + // + // Walk through each entry in the IoRun array. + // + + while (RemainingEntries != 0) { + + // + // We only need to deal with the case of an unaligned transfer. + // + + if (ThisIoRun->TransferByteCount != 0) { + + // + // If not the final cleanup then transfer the data to the + // user's buffer and remember that we will need to flush + // the user's buffer to memory. + // + + if (!FinalCleanup) { + + RtlCopyMemory( ThisIoRun->UserBuffer, + Add2Ptr( ThisIoRun->TransferBuffer, + ThisIoRun->TransferBufferOffset, + PVOID ), + ThisIoRun->TransferByteCount ); + + FlushIoBuffers = TRUE; + } + + // + // Free any Mdl we may have allocated. If the Mdl isn't + // present then we must have failed during the allocation + // phase. + // + + if (ThisIoRun->TransferMdl != IrpContext->Irp->MdlAddress) { + + if (ThisIoRun->TransferMdl != NULL) { + + IoFreeMdl( ThisIoRun->TransferMdl ); + } + + // + // Now free any buffer we may have allocated. If the Mdl + // doesn't match the original Mdl then free the buffer. + // + + if (ThisIoRun->TransferBuffer != NULL) { + + // + // If this is the final buffer for an XA read then store this buffer + // into the Vcb so that we will have it when reading any remaining + // portion of this buffer. + // + + if (SaveXABuffer) { + + Vcb = IrpContext->Vcb; + + CdLockVcb( IrpContext, Vcb ); + + if (Vcb->XASector != NULL) { + + CdFreePool( &Vcb->XASector ); + } + + Vcb->XASector = ThisIoRun->TransferBuffer; + Vcb->XADiskOffset = ThisIoRun->DiskOffset; + + SaveXABuffer = FALSE; + + CdUnlockVcb( IrpContext, Vcb ); + + // + // Otherwise just free the buffer. + // + + } else { + + CdFreePool( &ThisIoRun->TransferBuffer ); + } + } + } + } + + // + // Now handle the case where we failed in the process + // of allocating associated Irps and Mdls. + // + + if (ThisIoRun->SavedIrp != NULL) { + + if (ThisIoRun->SavedIrp->MdlAddress != NULL) { + + IoFreeMdl( ThisIoRun->SavedIrp->MdlAddress ); + } + + IoFreeIrp( ThisIoRun->SavedIrp ); + } + + // + // Move to the previous IoRun entry. + // + + ThisIoRun -= 1; + RemainingEntries -= 1; + } + + // + // If we copied any data then flush the Io buffers. + // + + return FlushIoBuffers; +} + +// Tell prefast this is a completion routine. +IO_COMPLETION_ROUTINE CdSyncCompletionRoutine; + +NTSTATUS +CdSyncCompletionRoutine ( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Contxt + ) + +/*++ + +Routine Description: + + Completion routine for synchronizing back to dispatch. + +Arguments: + + Contxt - pointer to KEVENT. + +Return Value: + + STATUS_MORE_PROCESSING_REQUIRED + +--*/ + +{ + PKEVENT Event = (PKEVENT)Contxt; + _Analysis_assume_(Contxt != NULL); + + UNREFERENCED_PARAMETER( Irp ); + UNREFERENCED_PARAMETER( DeviceObject ); + + KeSetEvent( Event, 0, FALSE ); + + // + // We don't want IO to get our IRP and free it. + // + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdFreeDirCache ( + _In_ PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + Safely frees the sector cache buffer. + +Arguments: + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + if (NULL != IrpContext->Vcb->SectorCacheBuffer) { + + CdAcquireCacheForUpdate( IrpContext); + CdFreePool( &IrpContext->Vcb->SectorCacheBuffer); + CdReleaseCache( IrpContext); + } +} + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +CdReadDirDataThroughCache ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_RUN Run + ) + +/*++ + +Routine Description: + + Reads blocks through the sector cache. If the data is present, then it + is copied from memory. If not present, one of the cache chunks will be + replaced with a chunk containing the requested region, and the data + copied from there. + + Only intended for reading *directory* blocks, for the purpose of pre-caching + directory information, by reading a chunk of blocks which hopefully contains + other directory blocks, rather than just the (usually) single block requested. + +Arguments: + + Run - description of extent required, and buffer to read into. + +Return Value: + + None. Raises on error. + +--*/ + +{ + PVCB Vcb = IrpContext->Vcb; + ULONG Lbn = SectorsFromLlBytes( Run->DiskOffset); + ULONG Remaining = SectorsFromBytes( Run->DiskByteCount); + PUCHAR UserBuffer = Run->TransferBuffer; + + NTSTATUS Status; + ULONG Found; + ULONG BufferSectorOffset; + ULONG StartBlock; + ULONG EndBlock; + ULONG Blocks; + + PIO_STACK_LOCATION IrpSp; + IO_STATUS_BLOCK Iosb; + + PTRACK_DATA TrackData; + +#if DBG + BOOLEAN JustRead = FALSE; +#endif + + ULONG Index; + PCD_SECTOR_CACHE_CHUNK Buffer; + BOOLEAN Result = FALSE; + + PAGED_CODE(); + + CdAcquireCacheForRead( IrpContext); + + try { + + // + // Check the cache hasn't gone away due to volume verify failure (which + // is the *only* reason it'll go away). If this is the case we raise + // the same error any I/O would return if the cache weren't here. + // + + if (NULL == Vcb->SectorCacheBuffer) { + + CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED); + } + + while (Remaining) { + + Buffer = NULL; + + // + // Look to see if any portion is currently cached. + // + + for (Index = 0; Index < CD_SEC_CACHE_CHUNKS; Index++) { + + if ((Vcb->SecCacheChunks[ Index].BaseLbn != -1) && + (Vcb->SecCacheChunks[ Index].BaseLbn <= Lbn) && + ((Vcb->SecCacheChunks[ Index].BaseLbn + CD_SEC_CHUNK_BLOCKS) > Lbn)) { + + Buffer = &Vcb->SecCacheChunks[ Index]; + break; + } + } + + // + // If we found any, copy it out and continue. + // + + if (NULL != Buffer) { + + BufferSectorOffset = Lbn - Buffer->BaseLbn; + Found = Min( CD_SEC_CHUNK_BLOCKS - BufferSectorOffset, Remaining); + + RtlCopyMemory( UserBuffer, + Buffer->Buffer + BytesFromSectors( BufferSectorOffset), + BytesFromSectors( Found)); + + Remaining -= Found; + UserBuffer += BytesFromSectors( Found); + Lbn += Found; +#if DBG + // + // Update stats. Don't count a hit if we've just read the data in. + // + + if (!JustRead) { + + InterlockedIncrement( (LONG*)&Vcb->SecCacheHits); + } + + JustRead = FALSE; +#endif + continue; + } + + // + // Missed the cache, so we need to read a new chunk. Take the cache + // resource exclusive while we do so. + // + + CdReleaseCache( IrpContext); + CdAcquireCacheForUpdate( IrpContext); +#if DBG + Vcb->SecCacheMisses += 1; +#endif + // + // Select the chunk to replace and calculate the start block of the + // chunk to cache. We cache blocks which start on Lbns aligned on + // multiples of chunk size, treating block 16 (VRS start) as block + // zero. + // + + Buffer = &Vcb->SecCacheChunks[ Vcb->SecCacheLRUChunkIndex]; + + StartBlock = Lbn - ((Lbn - 16) % CD_SEC_CHUNK_BLOCKS); + + // + // Make sure we don't try and read past end of the last track. + // + + TrackData = &Vcb->CdromToc->TrackData[(Vcb->CdromToc->LastTrack - Vcb->CdromToc->FirstTrack + 1)]; + + SwapCopyUchar4( &EndBlock, &TrackData->Address ); + + Blocks = EndBlock - StartBlock; + + if (Blocks > CD_SEC_CHUNK_BLOCKS) { + + Blocks = CD_SEC_CHUNK_BLOCKS; + } + + if ((0 == Blocks) || (Lbn < 16)) { + + CdRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER); + } + + // + // Now build / send the read request. + // + + IoReuseIrp( Vcb->SectorCacheIrp, STATUS_SUCCESS); + + KeClearEvent( &Vcb->SectorCacheEvent); + Vcb->SectorCacheIrp->Tail.Overlay.Thread = PsGetCurrentThread(); + + // + // Get a pointer to the stack location of the first driver which will be + // invoked. This is where the function codes and the parameters are set. + // + + IrpSp = IoGetNextIrpStackLocation( Vcb->SectorCacheIrp); + IrpSp->MajorFunction = (UCHAR) IRP_MJ_READ; + + // + // Build an MDL to describe the buffer. + // + + IoAllocateMdl( Buffer->Buffer, + BytesFromSectors( Blocks), + FALSE, + FALSE, + Vcb->SectorCacheIrp); + + if (NULL == Vcb->SectorCacheIrp->MdlAddress) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES); + } + + // + // We're reading/writing into the block cache (paged pool). Lock the + // pages and update the MDL with physical page information. + // + + try { + + MmProbeAndLockPages( Vcb->SectorCacheIrp->MdlAddress, + KernelMode, + (LOCK_OPERATION) IoWriteAccess ); + } +#pragma warning(suppress: 6320) + except(EXCEPTION_EXECUTE_HANDLER) { + + IoFreeMdl( Vcb->SectorCacheIrp->MdlAddress ); + Vcb->SectorCacheIrp->MdlAddress = NULL; + } + + if (NULL == Vcb->SectorCacheIrp->MdlAddress) { + + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Reset the BaseLbn as we can't trust this Buffer's data until the request + // is successfully completed. + // + + Buffer->BaseLbn = (ULONG)-1; + + IrpSp->Parameters.Read.Length = BytesFromSectors( Blocks); + IrpSp->Parameters.Read.ByteOffset.QuadPart = LlBytesFromSectors( StartBlock); + + IoSetCompletionRoutine( Vcb->SectorCacheIrp, + CdSyncCompletionRoutine, + &Vcb->SectorCacheEvent, + TRUE, + TRUE, + TRUE ); + + Vcb->SectorCacheIrp->UserIosb = &Iosb; + + Status = IoCallDriver( Vcb->TargetDeviceObject, Vcb->SectorCacheIrp ); + + if (STATUS_PENDING == Status) { + + + (VOID)KeWaitForSingleObject( &Vcb->SectorCacheEvent, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Vcb->SectorCacheIrp->IoStatus.Status; + } + + Vcb->SectorCacheIrp->UserIosb = NULL; + + // + // Unlock the pages and free the MDL. + // + + MmUnlockPages( Vcb->SectorCacheIrp->MdlAddress ); + IoFreeMdl( Vcb->SectorCacheIrp->MdlAddress ); + Vcb->SectorCacheIrp->MdlAddress = NULL; + + if (!NT_SUCCESS( Status )) { + + try_leave( Status ); + } + + // + // Update the buffer information, and drop the cache resource to shared + // to allow in reads. + // + + Buffer->BaseLbn = StartBlock; + Vcb->SecCacheLRUChunkIndex = (Vcb->SecCacheLRUChunkIndex + 1) % CD_SEC_CACHE_CHUNKS; + + CdConvertCacheToShared( IrpContext); +#if DBG + JustRead = TRUE; +#endif + } + + Result = TRUE; + } + finally { + + CdReleaseCache( IrpContext); + } + + return Result; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdMultipleAsync ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ ULONG RunCount, + _Inout_ PIO_RUN IoRuns + ) + +/*++ + +Routine Description: + + This routine first does the initial setup required of a Master IRP that is + going to be completed using associated IRPs. This routine should not + be used if only one async request is needed, instead the single read + async routines should be called. + + A context parameter is initialized, to serve as a communications area + between here and the common completion routine. + + Next this routine reads or writes one or more contiguous sectors from + a device asynchronously, and is used if there are multiple reads for a + master IRP. A completion routine is used to synchronize with the + completion of all of the I/O requests started by calls to this routine. + + Also, prior to calling this routine the caller must initialize the + IoStatus field in the Context, with the correct success status and byte + count which are expected if all of the parallel transfers complete + successfully. After return this status will be unchanged if all requests + were, in fact, successful. However, if one or more errors occur, the + IoStatus will be modified to reflect the error status and byte count + from the first run (by Vbo) which encountered an error. I/O status + from all subsequent runs will not be indicated. + +Arguments: + + RunCount - Supplies the number of multiple async requests + that will be issued against the master irp. + + IoRuns - Supplies an array containing the Offset and ByteCount for the + separate requests. + +Return Value: + + None. + +--*/ + +{ + PIO_COMPLETION_ROUTINE CompletionRoutine; + PIO_STACK_LOCATION IrpSp; + PMDL Mdl; + PIRP Irp; + PIRP MasterIrp; + ULONG UnwindRunCount; + BOOLEAN UseSectorCache; + + PAGED_CODE(); + + // + // Set up things according to whether this is truely async. + // + + CompletionRoutine = CdMultiSyncCompletionRoutine; + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CompletionRoutine = CdMultiAsyncCompletionRoutine; + } + + // + // For directories, use the sector cache. + // + + if ((SafeNodeType( Fcb) == CDFS_NTC_FCB_INDEX) && + (NULL != Fcb->Vcb->SectorCacheBuffer) && + (VcbMounted == IrpContext->Vcb->VcbCondition)) { + + UseSectorCache = TRUE; + } + else { + + UseSectorCache = FALSE; + } + + // + // Initialize some local variables. + // + + MasterIrp = IrpContext->Irp; + + // + // Itterate through the runs, doing everything that can fail. + // We let the cleanup in CdFinishBuffers clean up on error. + // + + for (UnwindRunCount = 0; + UnwindRunCount < RunCount; + UnwindRunCount += 1) { + + if (UseSectorCache) { + + if (!CdReadDirDataThroughCache( IrpContext, &IoRuns[ UnwindRunCount])) { + + // + // Turn off using directory cache and restart all over again. + // + + UseSectorCache = FALSE; + UnwindRunCount = 0; + } + + continue; + } + + // + // Create an associated IRP, making sure there is one stack entry for + // us, as well. + // + + IoRuns[UnwindRunCount].SavedIrp = + Irp = IoMakeAssociatedIrp( MasterIrp, (CCHAR)(IrpContext->Vcb->TargetDeviceObject->StackSize + 1) ); + + if (Irp == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Allocate and build a partial Mdl for the request. + // + + Mdl = IoAllocateMdl( IoRuns[UnwindRunCount].TransferVirtualAddress, + IoRuns[UnwindRunCount].DiskByteCount, + FALSE, + FALSE, + Irp ); + + if (Mdl == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + IoBuildPartialMdl( IoRuns[UnwindRunCount].TransferMdl, + Mdl, + IoRuns[UnwindRunCount].TransferVirtualAddress, + IoRuns[UnwindRunCount].DiskByteCount ); + + // + // Get the first IRP stack location in the associated Irp + // + + IoSetNextIrpStackLocation( Irp ); + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Setup the Stack location to describe our read. + // + + IrpSp->MajorFunction = IRP_MJ_READ; + IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].DiskByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].DiskOffset; + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( Irp, + CompletionRoutine, + IrpContext->IoContext, + TRUE, + TRUE, + TRUE ); + + // + // Setup the next IRP stack location in the associated Irp for the disk + // driver beneath us. + // + + IrpSp = IoGetNextIrpStackLocation( Irp ); + + // + // Setup the Stack location to do a read from the disk driver. + // + + IrpSp->MajorFunction = IRP_MJ_READ; + IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].DiskByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].DiskOffset; + } + + // + // If we used the cache, we're done. + // + + if (UseSectorCache) { + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + IrpContext->Irp->IoStatus.Status = STATUS_SUCCESS; + KeSetEvent( &IrpContext->IoContext->SyncEvent, 0, FALSE ); + } + + return; + } + + // + // We only need to set the associated IRP count in the master irp to + // make it a master IRP. But we set the count to one more than our + // caller requested, because we do not want the I/O system to complete + // the I/O. We also set our own count. + // + + IrpContext->IoContext->IrpCount = RunCount; + IrpContext->IoContext->MasterIrp = MasterIrp; + + // + // We set the count in the master Irp to 1 since typically we + // will clean up the associated irps ourselves. Setting this to one + // means completing the last associated Irp with SUCCESS (in the async + // case) will complete the master irp. + // + + MasterIrp->AssociatedIrp.IrpCount = 1; + + // + // If we (FS) acquired locks, transition the lock owners to an object, since + // when we return this thread could go away before request completion, and + // the resource package may otherwise try to boost priority, etc. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ) && + FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL )) { + + NT_ASSERT( IrpContext->IoContext->ResourceThreadId == (ERESOURCE_THREAD)PsGetCurrentThread() ); + + IrpContext->IoContext->ResourceThreadId = ((ULONG_PTR)IrpContext->IoContext) | 3; + + ExSetResourceOwnerPointer( IrpContext->IoContext->Resource, + (PVOID)IrpContext->IoContext->ResourceThreadId ); + } + + // + // Now that all the dangerous work is done, issue the Io requests + // + + for (UnwindRunCount = 0; + UnwindRunCount < RunCount; + UnwindRunCount++) { + + Irp = IoRuns[UnwindRunCount].SavedIrp; + IoRuns[UnwindRunCount].SavedIrp = NULL; + + if (NULL != Irp) { + + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be caught by our completion routines + // and dealt with as a normal IO error. + // + + (VOID) IoCallDriver( IrpContext->Vcb->TargetDeviceObject, Irp ); + } + } +} + + +// +// Local support routine +// + +VOID +CdMultipleXAAsync ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG RunCount, + _Inout_ PIO_RUN IoRuns, + _In_ PRAW_READ_INFO RawReads, + _In_ TRACK_MODE_TYPE TrackMode + ) + +/*++ + +Routine Description: + + This routine first does the initial setup required of a Master IRP that is + going to be completed using associated IRPs. This routine is used to generate + the associated Irps used to read raw sectors from the disk. + + A context parameter is initialized, to serve as a communications area + between here and the common completion routine. + + Next this routine reads or writes one or more contiguous sectors from + a device asynchronously, and is used if there are multiple reads for a + master IRP. A completion routine is used to synchronize with the + completion of all of the I/O requests started by calls to this routine. + + Also, prior to calling this routine the caller must initialize the + IoStatus field in the Context, with the correct success status and byte + count which are expected if all of the parallel transfers complete + successfully. After return this status will be unchanged if all requests + were, in fact, successful. However, if one or more errors occur, the + IoStatus will be modified to reflect the error status and byte count + from the first run (by Vbo) which encountered an error. I/O status + from all subsequent runs will not be indicated. + +Arguments: + + RunCount - Supplies the number of multiple async requests + that will be issued against the master irp. + + IoRuns - Supplies an array containing the Offset and ByteCount for the + separate requests. + + RawReads - Supplies an array of structures to store in the Irps passed to the + device driver to perform the low-level Io. + + TrackMode - Supplies the recording mode of sectors in these IoRuns + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + PMDL Mdl; + PIRP Irp; + PIRP MasterIrp; + ULONG UnwindRunCount; + ULONG RawByteCount; + + PIO_RUN ThisIoRun = IoRuns; + PRAW_READ_INFO ThisRawRead = RawReads; + + PAGED_CODE(); + + // + // Initialize some local variables. + // + + MasterIrp = IrpContext->Irp; + + // + // Itterate through the runs, doing everything that can fail. + // We let the cleanup in CdFinishBuffers clean up on error. + // + + for (UnwindRunCount = 0; + UnwindRunCount < RunCount; + UnwindRunCount += 1, ThisIoRun += 1, ThisRawRead += 1) { + + // + // Create an associated IRP, making sure there is one stack entry for + // us, as well. + // + + ThisIoRun->SavedIrp = + Irp = IoMakeAssociatedIrp( MasterIrp, (CCHAR)(IrpContext->Vcb->TargetDeviceObject->StackSize + 1) ); + + if (Irp == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Should have been passed a byte count of at least one sector, and + // must be a multiple of sector size + // + + NT_ASSERT( ThisIoRun->DiskByteCount && !SectorOffset(ThisIoRun->DiskByteCount)); + + RawByteCount = SectorsFromBytes( ThisIoRun->DiskByteCount) * RAW_SECTOR_SIZE; + + // + // Allocate and build a partial Mdl for the request. + // + + Mdl = IoAllocateMdl( ThisIoRun->TransferVirtualAddress, + RawByteCount, + FALSE, + FALSE, + Irp ); + + if (Mdl == NULL) { + + IrpContext->Irp->IoStatus.Information = 0; + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + IoBuildPartialMdl( ThisIoRun->TransferMdl, + Mdl, + ThisIoRun->TransferVirtualAddress, + RawByteCount); + // + // Get the first IRP stack location in the associated Irp + // + + IoSetNextIrpStackLocation( Irp ); + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Setup the Stack location to describe our read (using cooked values) + // These values won't be used for the raw read in any case. + // + + IrpSp->MajorFunction = IRP_MJ_READ; + IrpSp->Parameters.Read.Length = ThisIoRun->DiskByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = ThisIoRun->DiskOffset; + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( Irp, + CdMultiSyncCompletionRoutine, + IrpContext->IoContext, + TRUE, + TRUE, + TRUE ); + + // + // Setup the next IRP stack location in the associated Irp for the disk + // driver beneath us. + // + + IrpSp = IoGetNextIrpStackLocation( Irp ); + + // + // Setup the stack location to do a read of raw sectors at this location. + // Note that the storage stack always reads multiples of whole XA sectors. + // + + ThisRawRead->DiskOffset.QuadPart = ThisIoRun->DiskOffset; + ThisRawRead->SectorCount = ThisIoRun->DiskByteCount >> SECTOR_SHIFT; + ThisRawRead->TrackMode = TrackMode; + + IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; + + IrpSp->Parameters.DeviceIoControl.OutputBufferLength = ThisRawRead->SectorCount * RAW_SECTOR_SIZE; + Irp->UserBuffer = ThisIoRun->TransferVirtualAddress; + + IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof( RAW_READ_INFO ); + IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = ThisRawRead; + + IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_CDROM_RAW_READ; + } + + // + // We only need to set the associated IRP count in the master irp to + // make it a master IRP. But we set the count to one more than our + // caller requested, because we do not want the I/O system to complete + // the I/O. We also set our own count. + // + + IrpContext->IoContext->IrpCount = RunCount; + IrpContext->IoContext->MasterIrp = MasterIrp; + + // + // We set the count in the master Irp to 1 since typically we + // will clean up the associated irps ourselves. Setting this to one + // means completing the last associated Irp with SUCCESS (in the async + // case) will complete the master irp. + // + + MasterIrp->AssociatedIrp.IrpCount = 1; + + // + // Now that all the dangerous work is done, issue the Io requests + // + + for (UnwindRunCount = 0; + UnwindRunCount < RunCount; + UnwindRunCount++) { + + Irp = IoRuns[UnwindRunCount].SavedIrp; + IoRuns[UnwindRunCount].SavedIrp = NULL; + + // + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be caught by our completion routines + // and dealt with as a normal IO error. + // + + (VOID) IoCallDriver( IrpContext->Vcb->TargetDeviceObject, Irp ); + } + + return; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdSingleAsync ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_RUN Run, + _In_ PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine reads one or more contiguous sectors from a device + asynchronously, and is used if there is only one read necessary to + complete the IRP. It implements the read by simply filling + in the next stack frame in the Irp, and passing it on. The transfer + occurs to the single buffer originally specified in the user request. + +Arguments: + + ByteOffset - Supplies the starting Logical Byte Offset to begin reading from + + ByteCount - Supplies the number of bytes to read from the device + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + PIO_COMPLETION_ROUTINE CompletionRoutine; + + PAGED_CODE(); + + // + // For directories, look in the sector cache, + // + + if ((SafeNodeType( Fcb) == CDFS_NTC_FCB_INDEX) && + (NULL != Fcb->Vcb->SectorCacheBuffer) && + (VcbMounted == IrpContext->Vcb->VcbCondition)) { + + if (CdReadDirDataThroughCache( IrpContext, Run )) { + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + IrpContext->Irp->IoStatus.Status = STATUS_SUCCESS; + KeSetEvent( &IrpContext->IoContext->SyncEvent, 0, FALSE ); + } + + return; + } + } + + // + // Set up things according to whether this is truely async. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CompletionRoutine = CdSingleSyncCompletionRoutine; + + } else { + + CompletionRoutine = CdSingleAsyncCompletionRoutine; + + // + // If we (FS) acquired locks, transition the lock owners to an object, since + // when we return this thread could go away before request completion, and + // the resource package may otherwise try to boost priority, etc. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL )) { + + NT_ASSERT( IrpContext->IoContext->ResourceThreadId == (ERESOURCE_THREAD)PsGetCurrentThread() ); + + IrpContext->IoContext->ResourceThreadId = ((ULONG_PTR)IrpContext->IoContext) | 3; + + ExSetResourceOwnerPointer( IrpContext->IoContext->Resource, + (PVOID)IrpContext->IoContext->ResourceThreadId ); + } + } + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( IrpContext->Irp, + CompletionRoutine, + IrpContext->IoContext, + TRUE, + TRUE, + TRUE ); + + // + // Setup the next IRP stack location in the associated Irp for the disk + // driver beneath us. + // + + IrpSp = IoGetNextIrpStackLocation( IrpContext->Irp ); + + // + // Setup the Stack location to do a read from the disk driver. + // + + IrpSp->MajorFunction = IrpContext->MajorFunction; + IrpSp->Parameters.Read.Length = Run->DiskByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = Run->DiskOffset; + + // + // Issue the Io request + // + + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be caught by our completion routines + // and dealt with as a normal IO error. + // + + (VOID)IoCallDriver( IrpContext->Vcb->TargetDeviceObject, IrpContext->Irp ); +} + + +// +// Local support routine +// + +VOID +CdWaitSync ( + _In_ PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine waits for one or more previously started I/O requests + from the above routines, by simply waiting on the event. + +Arguments: + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + + (VOID)KeWaitForSingleObject( &IrpContext->IoContext->SyncEvent, + Executive, + KernelMode, + FALSE, + NULL ); + + KeClearEvent( &IrpContext->IoContext->SyncEvent ); +} + + +// +// Local support routine +// + +NTSTATUS +CdMultiSyncCompletionRoutine ( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) + +/*++ + +Routine Description: + + This is the completion routine for all synchronous reads + started via CdMultipleAsynch. + + The completion routine has has the following responsibilities: + + If the individual request was completed with an error, then + this completion routine must see if this is the first error + and remember the error status in the Context. + + If the IrpCount goes to 1, then it sets the event in the Context + parameter to signal the caller that all of the asynch requests + are done. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the associated Irp which is being completed. (This + Irp will no longer be accessible after this routine returns.) + + Context - The context parameter which was specified for all of + the multiple asynch I/O requests for this MasterIrp. + +Return Value: + + The routine returns STATUS_MORE_PROCESSING_REQUIRED so that we can + immediately complete the Master Irp without being in a race condition + with the IoCompleteRequest thread trying to decrement the IrpCount in + the Master Irp. + +--*/ + +{ + PCD_IO_CONTEXT IoContext = Context; + _Analysis_assume_(Context != NULL); + + AssertVerifyDeviceIrp( Irp ); + + // + // If we got an error (or verify required), remember it in the Irp + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + InterlockedExchange( &IoContext->Status, Irp->IoStatus.Status ); + IoContext->MasterIrp->IoStatus.Information = 0; + } + + // + // We must do this here since IoCompleteRequest won't get a chance + // on this associated Irp. + // + + IoFreeMdl( Irp->MdlAddress ); + IoFreeIrp( Irp ); + + if (InterlockedDecrement( &IoContext->IrpCount ) == 0) { + + // + // Update the Master Irp with any error status from the associated Irps. + // + + IoContext->MasterIrp->IoStatus.Status = IoContext->Status; + KeSetEvent( &IoContext->SyncEvent, 0, FALSE ); + } + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +// +// Local support routine +// + +NTSTATUS +CdMultiAsyncCompletionRoutine ( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) + +/*++ + +Routine Description: + + This is the completion routine for all asynchronous reads + started via CdMultipleAsynch. + + The completion routine has has the following responsibilities: + + If the individual request was completed with an error, then + this completion routine must see if this is the first error + and remember the error status in the Context. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the associated Irp which is being completed. (This + Irp will no longer be accessible after this routine returns.) + + Context - The context parameter which was specified for all of + the multiple asynch I/O requests for this MasterIrp. + +Return Value: + + Currently always returns STATUS_SUCCESS. + +--*/ + +{ + PCD_IO_CONTEXT IoContext = Context; + _Analysis_assume_(Context != NULL); + AssertVerifyDeviceIrp( Irp ); + + // + // If we got an error (or verify required), remember it in the Irp + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + InterlockedExchange( &IoContext->Status, Irp->IoStatus.Status ); + } + + // + // Decrement IrpCount and see if it goes to zero. + // + + if (InterlockedDecrement( &IoContext->IrpCount ) == 0) { + + // + // Mark the master Irp pending + // + + IoMarkIrpPending( IoContext->MasterIrp ); + + // + // Update the Master Irp with any error status from the associated Irps. + // + + IoContext->MasterIrp->IoStatus.Status = IoContext->Status; + + // + // Update the information field with the correct value. + // + + IoContext->MasterIrp->IoStatus.Information = 0; + + if (NT_SUCCESS( IoContext->MasterIrp->IoStatus.Status )) { + + IoContext->MasterIrp->IoStatus.Information = IoContext->RequestedByteCount; + } + + // + // Now release the resource + // + + _Analysis_assume_lock_held_(*IoContext->Resource); + ExReleaseResourceForThreadLite( IoContext->Resource, IoContext->ResourceThreadId ); + + // + // and finally, free the context record. + // + + CdFreeIoContext( IoContext ); + + // + // Return success in this case. + // + + return STATUS_SUCCESS; + + } else { + + // + // We need to cleanup the associated Irp and its Mdl. + // + + IoFreeMdl( Irp->MdlAddress ); + IoFreeIrp( Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + UNREFERENCED_PARAMETER( DeviceObject ); +} + + +// +// Local support routine +// + +NTSTATUS +CdSingleSyncCompletionRoutine ( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads started via CdSingleAsynch. + + The completion routine has has the following responsibilities: + + It sets the event in the Context parameter to signal the caller + that all of the asynch requests are done. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the Irp for this request. (This Irp will no longer + be accessible after this routine returns.) + + Context - The context parameter which was specified in the call to + CdSingleAsynch. + +Return Value: + + The routine returns STATUS_MORE_PROCESSING_REQUIRED so that we can + immediately complete the Master Irp without being in a race condition + with the IoCompleteRequest thread trying to decrement the IrpCount in + the Master Irp. + +--*/ + +{ + _Analysis_assume_(Context != NULL); + + UNREFERENCED_PARAMETER( DeviceObject ); + + AssertVerifyDeviceIrp( Irp ); + + // + // Store the correct information field into the Irp. + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + Irp->IoStatus.Information = 0; + } + + KeSetEvent( &((PCD_IO_CONTEXT)Context)->SyncEvent, 0, FALSE ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +// +// Local support routine +// + +NTSTATUS +CdSingleAsyncCompletionRoutine ( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + PVOID Context + ) + +/*++ + +Routine Description: + + This is the completion routine for all asynchronous reads + started via CdSingleAsynch. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the Irp for this request. (This Irp will no longer + be accessible after this routine returns.) + + Context - The context parameter which was specified in the call to + CdSingleAsynch. + +Return Value: + + Currently always returns STATUS_SUCCESS. + +--*/ + +{ + PCD_IO_CONTEXT IoContext = Context; + + _Analysis_assume_(IoContext != NULL); + AssertVerifyDeviceIrp( Irp ); + + // + // Update the information field with the correct value for bytes read. + // + + Irp->IoStatus.Information = 0; + + if (NT_SUCCESS( Irp->IoStatus.Status )) { + + Irp->IoStatus.Information = IoContext->RequestedByteCount; + } + + // + // Mark the Irp pending + // + + IoMarkIrpPending( Irp ); + + // + // Now release the resource + // + + ExReleaseResourceForThreadLite( IoContext->Resource, IoContext->ResourceThreadId ); + + // + // and finally, free the context record. + // + + CdFreeIoContext( IoContext ); + return STATUS_SUCCESS; + + UNREFERENCED_PARAMETER( DeviceObject ); +} + + +// +// Local support routine +// + +_When_(SafeNodeType(Fcb) != CDFS_NTC_FCB_PATH_TABLE && StartingOffset == 0, _At_(ByteCount, _In_range_(>=, CdAudioDirentSize + sizeof(RAW_DIRENT)))) +_When_(SafeNodeType(Fcb) != CDFS_NTC_FCB_PATH_TABLE && StartingOffset != 0, _At_(ByteCount, _In_range_(>=, CdAudioDirentSize + SECTOR_SIZE))) +VOID +CdReadAudioSystemFile ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG StartingOffset, + _In_ _In_range_(>=, CdAudioDirentSize) ULONG ByteCount, + _Out_writes_bytes_(ByteCount) PVOID SystemBuffer + ) + +/*++ + +Routine Description: + + This routine is called to read the pseudo root directory and path + table for a music disk. We build the individual elements on the + stack and copy into the cache buffer. + +Arguments: + + Fcb - Fcb representing the file to read. + + StartingOffset - Logical offset in the file to read from. + + ByteCount - Number of bytes to read. + + SystemBuffer - Pointer to buffer to fill in. This will always be page + aligned. + +Return Value: + + None. + +--*/ + +{ + PRAW_PATH_ISO RawPath; + PRAW_DIRENT RawDirent; + + ULONG CurrentTrack; + ULONG SectorOffset; + ULONG EntryCount; + UCHAR TrackOnes; + UCHAR TrackTens; + PTRACK_DATA ThisTrack; + + LONGLONG CurrentOffset; + + PVOID CurrentSector; + + PSYSTEM_USE_XA SystemUse; + + ULONG BytesToCopy; + + UCHAR LocalBuffer[FIELD_OFFSET( RAW_DIRENT, FileId ) + 12]; + + PAGED_CODE(); + + // + // If this is the path table then we just need a single entry. + // + + if (SafeNodeType( Fcb ) == CDFS_NTC_FCB_PATH_TABLE) { + + // + // Sanity check that the offset is zero. + // + + NT_ASSERT( StartingOffset == 0 ); + + // + // Store a pseudo path entry in our local buffer. + // + + RawPath = (PRAW_PATH_ISO) LocalBuffer; + + RtlZeroMemory( RawPath, sizeof( LocalBuffer )); + + RawPath->DirIdLen = 1; + RawPath->ParentNum = 1; + RawPath->DirId[0] = '\0'; + + // + // Now copy to the user's buffer. + // + + BytesToCopy = FIELD_OFFSET( RAW_PATH_ISO, DirId ) + 2; + + if (BytesToCopy > ByteCount) { + + BytesToCopy = ByteCount; + } + + RtlCopyMemory( SystemBuffer, + RawPath, + BytesToCopy ); + + // + // We need to deal with the multiple sector case for the root directory. + // + + } else { + + // + // Initialize the first track to return to our caller. + // + + CurrentTrack = 0; + + // + // If the offset is zero then store the entries for the self and parent + // entries. + // + + if (StartingOffset == 0) { + + RawDirent = SystemBuffer; + + // + // Clear all of the fields initially. + // + + RtlZeroMemory( RawDirent, FIELD_OFFSET( RAW_DIRENT, FileId )); + + // + // Now fill in the interesting fields. + // + + RawDirent->DirLen = FIELD_OFFSET( RAW_DIRENT, FileId ) + 1; + RawDirent->FileIdLen = 1; + RawDirent->FileId[0] = '\0'; + SetFlag( RawDirent->FlagsISO, CD_ATTRIBUTE_DIRECTORY ); + + // + // Set the time stamp to be Jan 1, 1995 + // + + RawDirent->RecordTime[0] = 95; + RawDirent->RecordTime[1] = 1; + RawDirent->RecordTime[2] = 1; + + SectorOffset = RawDirent->DirLen; + + RawDirent = Add2Ptr( RawDirent, SectorOffset, PRAW_DIRENT ); + + // + // Clear all of the fields initially. + // + + RtlZeroMemory( RawDirent, FIELD_OFFSET( RAW_DIRENT, FileId )); + + // + // Now fill in the interesting fields. + // + + RawDirent->DirLen = FIELD_OFFSET( RAW_DIRENT, FileId ) + 1; + RawDirent->FileIdLen = 1; + RawDirent->FileId[0] = '\1'; + SetFlag( RawDirent->FlagsISO, CD_ATTRIBUTE_DIRECTORY ); + + // + // Set the time stamp to be Jan 1, 1995 + // + + RawDirent->RecordTime[0] = 95; + RawDirent->RecordTime[1] = 1; + RawDirent->RecordTime[2] = 1; + + SectorOffset += RawDirent->DirLen; + EntryCount = 2; + + // + // Otherwise compute the starting track to write to the buffer. + // + + } else { + + // + // Count the tracks in each preceding sector. + // + + CurrentOffset = 0; + + do { + + CurrentTrack += CdAudioDirentsPerSector; + CurrentOffset += SECTOR_SIZE; + + } while (CurrentOffset < StartingOffset); + + // + // Bias the track count to reflect the two default entries. + // + + CurrentTrack -= 2; + + SectorOffset = 0; + EntryCount = 0; + } + + // + // We now know the first track to return as well as where we are in + // the current sector. We will walk through sector by sector adding + // the entries for the separate tracks in the TOC. We will zero + // any sectors or partial sectors without data. + // + + CurrentSector = SystemBuffer; + BytesToCopy = SECTOR_SIZE; + + // + // Loop for each sector. + // + + do { + + // + // Add entries until we reach our threshold for each sector. + // + + do { + + // + // If we are beyond the entries in the TOC then exit. + // + + if (CurrentTrack >= IrpContext->Vcb->TrackCount) { + + break; + } + + ThisTrack = &IrpContext->Vcb->CdromToc->TrackData[CurrentTrack]; + + // + // Point to the current position in the buffer. + // + + RawDirent = Add2Ptr( CurrentSector, SectorOffset, PRAW_DIRENT ); + + // + // Clear all of the fields initially. + // + + RtlZeroMemory( RawDirent, CdAudioDirentSize ); + + // + // Now fill in the interesting fields. + // + + RawDirent->DirLen = (UCHAR) CdAudioDirentSize; + RawDirent->FileIdLen = CdAudioFileNameLength; + + RtlCopyMemory( RawDirent->FileId, + CdAudioFileName, + CdAudioFileNameLength ); + + // + // Set the time stamp to be Jan 1, 1995 00:00 + // + + RawDirent->RecordTime[0] = 95; + RawDirent->RecordTime[1] = 1; + RawDirent->RecordTime[2] = 1; + + // + // Put the track number into the file name. + // + + TrackTens = TrackOnes = ThisTrack->TrackNumber; + + TrackOnes = (TrackOnes % 10) + '0'; + + TrackTens /= 10; + TrackTens = (TrackTens % 10) + '0'; + + RawDirent->FileId[AUDIO_NAME_TENS_OFFSET] = TrackTens; + RawDirent->FileId[AUDIO_NAME_ONES_OFFSET] = TrackOnes; + + SystemUse = Add2Ptr( RawDirent, CdAudioSystemUseOffset, PSYSTEM_USE_XA ); + + SystemUse->Attributes = SYSTEM_USE_XA_DA; + SystemUse->Signature = SYSTEM_XA_SIGNATURE; + + // + // Store the track number as the file number. + // + + SystemUse->FileNumber = (UCHAR) CurrentTrack; + + EntryCount += 1; + SectorOffset += CdAudioDirentSize; + CurrentTrack += 1; + + } while (EntryCount < CdAudioDirentsPerSector); + + // + // Zero the remaining portion of this buffer. + // + + RtlZeroMemory( Add2Ptr( CurrentSector, SectorOffset, PVOID ), + SECTOR_SIZE - SectorOffset ); + + // + // Prepare for the next sector. + // + + EntryCount = 0; + BytesToCopy += SECTOR_SIZE; + SectorOffset = 0; + CurrentSector = Add2Ptr( CurrentSector, SECTOR_SIZE, PVOID ); + + } while (BytesToCopy <= ByteCount); + } + + return; +} + + +NTSTATUS +CdHijackIrpAndFlushDevice ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _In_ PDEVICE_OBJECT TargetDeviceObject + ) + +/*++ + +Routine Description: + + This routine is called when we need to send a flush to a device but + we don't have a flush Irp. What this routine does is make a copy + of its current Irp stack location, but changes the Irp Major code + to a IRP_MJ_FLUSH_BUFFERS amd then send it down, but cut it off at + the knees in the completion routine, fix it up and return to the + user as if nothing had happened. + +Arguments: + + Irp - The Irp to hijack + + TargetDeviceObject - The device to send the request to. + +Return Value: + + NTSTATUS - The Status from the flush in case anybody cares. + +--*/ + +{ + KEVENT Event; + NTSTATUS Status; + PIO_STACK_LOCATION NextIrpSp; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Get the next stack location, and copy over the stack location + // + + NextIrpSp = IoGetNextIrpStackLocation( Irp ); + + *NextIrpSp = *IoGetCurrentIrpStackLocation( Irp ); + + NextIrpSp->MajorFunction = IRP_MJ_FLUSH_BUFFERS; + NextIrpSp->MinorFunction = 0; + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + IoSetCompletionRoutine( Irp, + CdSyncCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request. + // + + Status = IoCallDriver( TargetDeviceObject, Irp ); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // If the driver doesn't support flushes, return SUCCESS. + // + + if (Status == STATUS_INVALID_DEVICE_REQUEST) { + + Status = STATUS_SUCCESS; + } + + Irp->IoStatus.Status = 0; + Irp->IoStatus.Information = 0; + + return Status; +} + + diff --git a/filesys/cdfs/dirctrl.c b/filesys/cdfs/dirctrl.c new file mode 100644 index 00000000..71cfccb6 --- /dev/null +++ b/filesys/cdfs/dirctrl.c @@ -0,0 +1,1544 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DirCtrl.c + +Abstract: + + This module implements the File Directory Control routines for Cdfs called + by the Fsd/Fsp dispatch drivers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_DIRCTRL) + +// +// Local support routines +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdQueryDirectory ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PFCB Fcb, + _In_ PCCB Ccb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdNotifyChangeDirectory ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PCCB Ccb + ); + +VOID +CdInitializeEnumeration ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PFCB Fcb, + _Inout_ PCCB Ccb, + _Inout_ PFILE_ENUM_CONTEXT FileContext, + _Out_ PBOOLEAN ReturnNextEntry, + _Out_ PBOOLEAN ReturnSingleEntry, + _Out_ PBOOLEAN InitialQuery + ); + +BOOLEAN +CdEnumerateIndex ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PCCB Ccb, + _Inout_ PFILE_ENUM_CONTEXT FileContext, + _In_ BOOLEAN ReturnNextEntry + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonDirControl) +#pragma alloc_text(PAGE, CdEnumerateIndex) +#pragma alloc_text(PAGE, CdInitializeEnumeration) +#pragma alloc_text(PAGE, CdNotifyChangeDirectory) +#pragma alloc_text(PAGE, CdQueryDirectory) +#endif + + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonDirControl ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the entry point for the directory control operations. These + are directory enumerations and directory notify calls. We verify the + user's handle is for a directory and then call the appropriate routine. + +Arguments: + + Irp - Irp for this request. + +Return Value: + + NTSTATUS - Status returned from the lower level routines. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Decode the user file object and fail this request if it is not + // a user directory. + // + + if (CdDecodeFileObject( IrpContext, + IrpSp->FileObject, + &Fcb, + &Ccb ) != UserDirectoryOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // We know this is a directory control so we'll case on the + // minor function, and call a internal worker routine to complete + // the irp. + // + + switch (IrpSp->MinorFunction) { + + case IRP_MN_QUERY_DIRECTORY: + + Status = CdQueryDirectory( IrpContext, Irp, IrpSp, Fcb, Ccb ); + break; + + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: + + Status = CdNotifyChangeDirectory( IrpContext, Irp, IrpSp, Ccb ); + break; + + default: + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return Status; +} + + +// +// Local support routines +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdQueryDirectory ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PFCB Fcb, + _In_ PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine performs the query directory operation. It is responsible + for either completing of enqueuing the input Irp. We store the state of the + search in the Ccb. + +Arguments: + + Irp - Supplies the Irp to process + + IrpSp - Stack location for this Irp. + + Fcb - Fcb for this directory. + + Ccb - Ccb for this directory open. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG Information = 0; + + ULONG LastEntry = 0; + ULONG NextEntry = 0; + + ULONG FileNameBytes; + ULONG SeparatorBytes; + ULONG VersionStringBytes; + + FILE_ENUM_CONTEXT FileContext; + PDIRENT ThisDirent = NULL; + BOOLEAN InitialQuery; + BOOLEAN ReturnNextEntry = FALSE; + BOOLEAN ReturnSingleEntry; + BOOLEAN Found; + BOOLEAN DoCcbUpdate = FALSE; + + PCHAR UserBuffer; + ULONG BytesRemainingInBuffer; + + ULONG BaseLength; + + PFILE_BOTH_DIR_INFORMATION DirInfo = NULL; + PFILE_NAMES_INFORMATION NamesInfo; + PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo; + PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo; + + PAGED_CODE(); + + // + // Check if we support this search mode. Also remember the size of the base part of + // each of these structures. + // + + switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) { + + case FileDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, + FileName[0] ); + break; + + case FileFullDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, + FileName[0] ); + break; + + case FileIdFullDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION, + FileName[0] ); + break; + + case FileNamesInformation: + + BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, + FileName[0] ); + break; + + case FileBothDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, + FileName[0] ); + break; + + case FileIdBothDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, + FileName[0] ); + break; + + default: + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_INFO_CLASS ); + return STATUS_INVALID_INFO_CLASS; + } + + // + // Get the user buffer. + // + + CdMapUserBuffer( IrpContext, &UserBuffer); + + // + // Initialize our search context. + // + + CdInitializeFileContext( IrpContext, &FileContext ); + + // + // Acquire the directory. + // + + CdAcquireFileShared( IrpContext, Fcb ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify the Fcb is still good. + // + + CdVerifyFcbOperation( IrpContext, Fcb ); + + // + // Start by getting the initial state for the enumeration. This will set up the Ccb with + // the initial search parameters and let us know the starting offset in the directory + // to search. + // + + CdInitializeEnumeration( IrpContext, + IrpSp, + Fcb, + Ccb, + &FileContext, + &ReturnNextEntry, + &ReturnSingleEntry, + &InitialQuery ); + + // + // The current dirent is stored in the InitialDirent field. We capture + // this here so that we have a valid restart point even if we don't + // find a single entry. + // + + ThisDirent = &FileContext.InitialDirent->Dirent; + + // + // At this point we are about to enter our query loop. We have + // determined the index into the directory file to begin the + // search. LastEntry and NextEntry are used to index into the user + // buffer. LastEntry is the last entry we've added, NextEntry is + // current one we're working on. If NextEntry is non-zero, then + // at least one entry was added. + // + + while (TRUE) { + + // + // If the user had requested only a single match and we have + // returned that, then we stop at this point. We update the Ccb with + // the status based on the last entry returned. + // + + if ((NextEntry != 0) && ReturnSingleEntry) { + + DoCcbUpdate = TRUE; + try_leave( Status ); + } + + // + // We try to locate the next matching dirent. Our search if based on a starting + // dirent offset, whether we should return the current or next entry, whether + // we should be doing a short name search and finally whether we should be + // checking for a version match. + // + + Found = CdEnumerateIndex( IrpContext, Ccb, &FileContext, ReturnNextEntry ); + + // + // Initialize the value for the next search. + // + + ReturnNextEntry = TRUE; + + // + // If we didn't receive a dirent, then we are at the end of the + // directory. If we have returned any files, we exit with + // success, otherwise we return STATUS_NO_MORE_FILES. + // + + if (!Found) { + + if (NextEntry == 0) { + + Status = STATUS_NO_MORE_FILES; + + if (InitialQuery) { + + Status = STATUS_NO_SUCH_FILE; + } + } + + DoCcbUpdate = TRUE; + try_leave( Status ); + } + + // + // Remember the dirent for the file we just found. + // + + ThisDirent = &FileContext.InitialDirent->Dirent; + + // + // Here are the rules concerning filling up the buffer: + // + // 1. The Io system garentees that there will always be + // enough room for at least one base record. + // + // 2. If the full first record (including file name) cannot + // fit, as much of the name as possible is copied and + // STATUS_BUFFER_OVERFLOW is returned. + // + // 3. If a subsequent record cannot completely fit into the + // buffer, none of it (as in 0 bytes) is copied, and + // STATUS_SUCCESS is returned. A subsequent query will + // pick up with this record. + // + + // + // Let's compute the number of bytes we need to transfer the current entry. + // + + SeparatorBytes = + VersionStringBytes = 0; + + // + // We can look directly at the dirent that we found. + // + + FileNameBytes = ThisDirent->CdFileName.FileName.Length; + + // + // Compute the number of bytes for the version string if + // we will return this. Allow directories with illegal ";". + // + + if (((Ccb->SearchExpression.VersionString.Length != 0) || + (FlagOn(ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY))) && + (ThisDirent->CdFileName.VersionString.Length != 0)) { + + SeparatorBytes = 2; + + VersionStringBytes = ThisDirent->CdFileName.VersionString.Length; + } + + // + // If the slot for the next entry would be beyond the length of the + // user's buffer just exit (we know we've returned at least one entry + // already). This will happen when we align the pointer past the end. + // + + if (NextEntry > IrpSp->Parameters.QueryDirectory.Length) { + + ReturnNextEntry = FALSE; + DoCcbUpdate = TRUE; + try_leave( Status = STATUS_SUCCESS ); + } + + // + // Compute the number of bytes remaining in the buffer. Round this + // down to a WCHAR boundary so we can copy full characters. + // + + BytesRemainingInBuffer = IrpSp->Parameters.QueryDirectory.Length - NextEntry; + ClearFlag( BytesRemainingInBuffer, 1 ); + + // + // If this won't fit and we have returned a previous entry then just + // return STATUS_SUCCESS. + // + + if ((BaseLength + FileNameBytes + SeparatorBytes + VersionStringBytes) > BytesRemainingInBuffer) { + + // + // If we already found an entry then just exit. + // + + if (NextEntry != 0) { + + ReturnNextEntry = FALSE; + DoCcbUpdate = TRUE; + try_leave( Status = STATUS_SUCCESS ); + } + + // + // Don't even try to return the version string if it doesn't all fit. + // Reduce the FileNameBytes to just fit in the buffer. + // + + if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) { + + FileNameBytes = BytesRemainingInBuffer - BaseLength; + } + + // + // Don't return any version string bytes. + // + + VersionStringBytes = + SeparatorBytes = 0; + + // + // Use a status code of STATUS_BUFFER_OVERFLOW. Also set + // ReturnSingleEntry so that we will exit the loop at the top. + // + + Status = STATUS_BUFFER_OVERFLOW; + ReturnSingleEntry = TRUE; + } + + // + // Protect access to the user buffer with an exception handler. + // Since (at our request) IO doesn't buffer these requests, we have + // to guard against a user messing with the page protection and other + // such trickery. + // + + try { + + // + // Zero and initialize the base part of the current entry. + // + + RtlZeroMemory( Add2Ptr( UserBuffer, NextEntry, PVOID ), + BaseLength ); + + // + // Now we have an entry to return to our caller. + // We'll case on the type of information requested and fill up + // the user buffer if everything fits. + // + + switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) { + + case FileBothDirectoryInformation: + case FileFullDirectoryInformation: + case FileIdBothDirectoryInformation: + case FileIdFullDirectoryInformation: + case FileDirectoryInformation: + + DirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_BOTH_DIR_INFORMATION ); + + // + // Use the create time for all the time stamps. + // + + CdConvertCdTimeToNtTime( IrpContext, + FileContext.InitialDirent->Dirent.CdTime, + &DirInfo->CreationTime ); + + DirInfo->LastWriteTime = DirInfo->ChangeTime = DirInfo->CreationTime; + + // + // Set the attributes and sizes separately for directories and + // files. + // + + if (FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { + + DirInfo->EndOfFile.QuadPart = DirInfo->AllocationSize.QuadPart = 0; + + SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY); + + } else { + + DirInfo->EndOfFile.QuadPart = FileContext.FileSize; + DirInfo->AllocationSize.QuadPart = LlSectorAlign( FileContext.FileSize ); + + SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_READONLY); + } + + if (FlagOn( ThisDirent->DirentFlags, + CD_ATTRIBUTE_HIDDEN )) { + + SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_HIDDEN ); + } + + DirInfo->FileIndex = ThisDirent->DirentOffset; + + DirInfo->FileNameLength = FileNameBytes + SeparatorBytes + VersionStringBytes; + + break; + + case FileNamesInformation: + + NamesInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_NAMES_INFORMATION ); + + NamesInfo->FileIndex = ThisDirent->DirentOffset; + + NamesInfo->FileNameLength = FileNameBytes + SeparatorBytes + VersionStringBytes; + + break; + } + + // + // Fill in the FileId + // + + switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) { + + case FileIdBothDirectoryInformation: + + IdBothDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_BOTH_DIR_INFORMATION ); + CdSetFidFromParentAndDirent( IdBothDirInfo->FileId, Fcb, ThisDirent ); + break; + + case FileIdFullDirectoryInformation: + + IdFullDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_FULL_DIR_INFORMATION ); + CdSetFidFromParentAndDirent( IdFullDirInfo->FileId, Fcb, ThisDirent ); + break; + + default: + break; + } + + // + // Now copy as much of the name as possible. We also may have a version + // string to copy. + // + + if (FileNameBytes != 0) { + + // + // This is a Unicode name, we can copy the bytes directly. + // + + RtlCopyMemory( Add2Ptr( UserBuffer, NextEntry + BaseLength, PVOID ), + ThisDirent->CdFileName.FileName.Buffer, + FileNameBytes ); + + if (SeparatorBytes != 0) { + + *(Add2Ptr( UserBuffer, + NextEntry + BaseLength + FileNameBytes, + PWCHAR )) = L';'; + + if (VersionStringBytes != 0) { + + RtlCopyMemory( Add2Ptr( UserBuffer, + NextEntry + BaseLength + FileNameBytes + sizeof( WCHAR ), + PVOID ), + ThisDirent->CdFileName.VersionString.Buffer, + VersionStringBytes ); + } + } + } + + // + // Fill in the short name if we got STATUS_SUCCESS. The short name + // may already be in the file context. Otherwise we will check + // whether the long name is 8.3. Special case the self and parent + // directory names. + // + + if ((Status == STATUS_SUCCESS) && + (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation || + IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation) && + (Ccb->SearchExpression.VersionString.Length == 0) && + !FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) { + + // + // If we already have the short name then copy into the user's buffer. + // + + if (FileContext.ShortName.FileName.Length != 0) { + + RtlCopyMemory( DirInfo->ShortName, + FileContext.ShortName.FileName.Buffer, + FileContext.ShortName.FileName.Length ); + + DirInfo->ShortNameLength = (CCHAR) FileContext.ShortName.FileName.Length; + + // + // If the short name length is currently zero then check if + // the long name is not 8.3. We can copy the short name in + // unicode form directly into the caller's buffer. + // + + } else { + + if (!CdIs8dot3Name( IrpContext, + ThisDirent->CdFileName.FileName )) { + + CdGenerate8dot3Name( IrpContext, + &ThisDirent->CdCaseFileName.FileName, + ThisDirent->DirentOffset, + DirInfo->ShortName, + &FileContext.ShortName.FileName.Length ); + + DirInfo->ShortNameLength = (CCHAR) FileContext.ShortName.FileName.Length; + } + } + + } + + // + // Sum the total number of bytes for the information field. + // + + FileNameBytes += SeparatorBytes + VersionStringBytes; + + // + // Update the information with the number of bytes stored in the + // buffer. We quad-align the existing buffer to add any necessary + // pad bytes. + // + + Information = NextEntry + BaseLength + FileNameBytes; + + // + // Go back to the previous entry and fill in the update to this entry. + // + + *(Add2Ptr( UserBuffer, LastEntry, PULONG )) = NextEntry - LastEntry; + + // + // Set up our variables for the next dirent. + // + + InitialQuery = FALSE; + + LastEntry = NextEntry; + NextEntry = QuadAlign( Information ); + +#pragma warning(suppress: 6320) + } except (EXCEPTION_EXECUTE_HANDLER) { + + // + // We had a problem filling in the user's buffer, so stop and + // fail this request. This is the only reason any exception + // would have occured at this level. + // + + Information = 0; + try_leave( Status = GetExceptionCode()); + } + } + + DoCcbUpdate = TRUE; + + } finally { + + // + // Cleanup our search context - *before* aquiring the FCB mutex exclusive, + // else can block on threads in cdcreateinternalstream/purge which + // hold the FCB but are waiting for all maps in this stream to be released. + // + + CdCleanupFileContext( IrpContext, &FileContext ); + + // + // Now we can safely aqure the FCB mutex if we need to. + // + + if (DoCcbUpdate && !NT_ERROR( Status )) { + + // + // Update the Ccb to show the current state of the enumeration. + // + + CdLockFcb( IrpContext, Fcb ); + + Ccb->CurrentDirentOffset = ThisDirent->DirentOffset; + + ClearFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT ); + + if (ReturnNextEntry) { + + SetFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT ); + } + + CdUnlockFcb( IrpContext, Fcb ); + } + + // + // Release the Fcb. + // + + CdReleaseFile( IrpContext, Fcb ); + } + + // + // Complete the request here. + // + + Irp->IoStatus.Information = Information; + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routines +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdNotifyChangeDirectory ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine performs the notify change directory operation. It is + responsible for either completing of enqueuing the input Irp. Although there + will never be a notify signalled on a CDROM disk we still support this call. + + We have already checked that this is not an OpenById handle. + +Arguments: + + Irp - Supplies the Irp to process + + IrpSp - Io stack location for this request. + + Ccb - Handle to the directory being watched. + +Return Value: + + NTSTATUS - STATUS_PENDING, any other error will raise. + +--*/ + +{ + PAGED_CODE(); + + // + // Always set the wait bit in the IrpContext so the initial wait can't fail. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Acquire the Vcb shared. + // + + CdAcquireVcbShared( IrpContext, IrpContext->Vcb, FALSE ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify the Vcb. + // + + CdVerifyVcb( IrpContext, IrpContext->Vcb ); + + // + // Call the Fsrtl package to process the request. We cast the + // unicode strings to ansi strings as the dir notify package + // only deals with memory matching. + // + + FsRtlNotifyFullChangeDirectory( IrpContext->Vcb->NotifySync, + &IrpContext->Vcb->DirNotifyList, + Ccb, + (PSTRING) &IrpSp->FileObject->FileName, + BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE ), + FALSE, + IrpSp->Parameters.NotifyDirectory.CompletionFilter, + Irp, + NULL, + NULL ); + + } finally { + + // + // Release the Vcb. + // + + CdReleaseVcb( IrpContext, IrpContext->Vcb ); + } + + // + // Cleanup the IrpContext. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + return STATUS_PENDING; +} + + +// +// Local support routine +// + +VOID +CdInitializeEnumeration ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _In_ PFCB Fcb, + _Inout_ PCCB Ccb, + _Inout_ PFILE_ENUM_CONTEXT FileContext, + _Out_ PBOOLEAN ReturnNextEntry, + _Out_ PBOOLEAN ReturnSingleEntry, + _Out_ PBOOLEAN InitialQuery + ) + +/*++ + +Routine Description: + + This routine is called to initialize the enumeration variables and structures. + We look at the state of a previous enumeration from the Ccb as well as any + input values from the user. On exit we will position the FileContext at + a file in the directory and let the caller know whether this entry or the + next entry should be returned. + +Arguments: + + IrpSp - Irp stack location for this request. + + Fcb - Fcb for this directory. + + Ccb - Ccb for the directory handle. + + FileContext - FileContext to use for this enumeration. + + ReturnNextEntry - Address to store whether we should return the entry at + the FileContext position or the next entry. + + ReturnSingleEntry - Address to store whether we should only return + a single entry. + + InitialQuery - Address to store whether this is the first enumeration + query on this handle. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + + PUNICODE_STRING FileName; + CD_NAME WildCardName; + CD_NAME SearchExpression; + + ULONG CcbFlags; + + ULONG DirentOffset; + ULONG LastDirentOffset; + BOOLEAN KnownOffset; + + BOOLEAN Found; + + PAGED_CODE(); + + // + // If the user has specified that the scan be restarted, and has specicified + // a new query pattern, reinitialize the CCB. + // + + if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) { + + CdLockFcb( IrpContext, Fcb ); + + FileName = (PUNICODE_STRING) IrpSp->Parameters.QueryDirectory.FileName; + if (FileName && FileName->Length > 0) { + + if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) { + + CdFreePool( &Ccb->SearchExpression.FileName.Buffer ); + } + + ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL); + ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED); + ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD); + } + + CdUnlockFcb( IrpContext, Fcb ); + } + + // + // If this is the initial query then build a search expression from the input + // file name. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) { + + FileName = IrpSp->Parameters.QueryDirectory.FileName; + + CcbFlags = 0; + + // + // If the filename is not specified or is a single '*' then we will + // match all names. + // + + if ((FileName == NULL) || + (FileName->Buffer == NULL) || + (FileName->Length == 0) || + ((FileName->Length == sizeof( WCHAR )) && + (FileName->Buffer[0] == L'*'))) { + + SetFlag( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL ); + RtlZeroMemory( &SearchExpression, sizeof( SearchExpression )); + + // + // Otherwise build the CdName from the name in the stack location. + // This involves building both the name and version portions and + // checking for wild card characters. We also upcase the string if + // this is a case-insensitive search. + // + + } else { + + // + // Create a CdName to check for wild cards. + // + + WildCardName.FileName = *FileName; + + CdConvertNameToCdName( IrpContext, &WildCardName ); + + // + // The name better have at least one character. + // + + if (WildCardName.FileName.Length == 0) { + + CdRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER ); + } + + // + // Check for wildcards in the separate components. + // + + if (FsRtlDoesNameContainWildCards( &WildCardName.FileName)) { + + SetFlag( CcbFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD ); + } + + if ((WildCardName.VersionString.Length != 0) && + (FsRtlDoesNameContainWildCards( &WildCardName.VersionString ))) { + + SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD ); + + // + // Check if this is a wild card only and match all version + // strings. + // + + if ((WildCardName.VersionString.Length == sizeof( WCHAR )) && + (WildCardName.VersionString.Buffer[0] == L'*')) { + + SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL ); + } + } + + // + // Now create the search expression to store in the Ccb. + // + + SearchExpression.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, + FileName->Length, + TAG_ENUM_EXPRESSION ); + + SearchExpression.FileName.MaximumLength = FileName->Length; + + // + // Either copy the name directly or perform the upcase. + // + + if (FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )) { + + Status = RtlUpcaseUnicodeString( (PUNICODE_STRING) &SearchExpression.FileName, + FileName, + FALSE ); + + // + // This should never fail. + // + __analysis_assert( Status == STATUS_SUCCESS ); + NT_ASSERT( Status == STATUS_SUCCESS ); + + } else { + + RtlCopyMemory( SearchExpression.FileName.Buffer, + FileName->Buffer, + FileName->Length ); + } + + // + // Now split into the separate name and version components. + // + + SearchExpression.FileName.Length = WildCardName.FileName.Length; + SearchExpression.VersionString.Length = WildCardName.VersionString.Length; + SearchExpression.VersionString.MaximumLength = WildCardName.VersionString.MaximumLength; + + SearchExpression.VersionString.Buffer = Add2Ptr( SearchExpression.FileName.Buffer, + SearchExpression.FileName.Length + sizeof( WCHAR ), + PWCHAR ); + } + + // + // But we do not want to return the constant "." and ".." entries for + // the root directory, for consistency with the rest of Microsoft's + // filesystems. + // + + if (Fcb == Fcb->Vcb->RootIndexFcb) { + + SetFlag( CcbFlags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY ); + } + + // + // Now lock the Fcb in order to update the Ccb with the inital + // enumeration values. + // + + CdLockFcb( IrpContext, Fcb ); + + // + // Check again that this is the initial search. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) { + + // + // Update the values in the Ccb. + // + + Ccb->CurrentDirentOffset = Fcb->StreamOffset; + Ccb->SearchExpression = SearchExpression; + + // + // Set the appropriate flags in the Ccb. + // + + SetFlag( Ccb->Flags, CcbFlags | CCB_FLAG_ENUM_INITIALIZED ); + + // + // Otherwise cleanup any buffer allocated here. + // + + } else { + + if (!FlagOn( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL )) { + + CdFreePool( &SearchExpression.FileName.Buffer ); + } + } + + // + // Otherwise lock the Fcb so we can read the current enumeration values. + // + + } else { + + CdLockFcb( IrpContext, Fcb ); + } + + // + // Capture the current state of the enumeration. + // + // If the user specified an index then use his offset. We always + // return the next entry in this case. + // + + if (FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED )) { + + KnownOffset = FALSE; + DirentOffset = IrpSp->Parameters.QueryDirectory.FileIndex; + *ReturnNextEntry = TRUE; + + // + // If we are restarting the scan then go from the self entry. + // + + } else if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) { + + KnownOffset = TRUE; + DirentOffset = Fcb->StreamOffset; + *ReturnNextEntry = FALSE; + + // + // Otherwise use the values from the Ccb. + // + + } else { + + KnownOffset = TRUE; + DirentOffset = Ccb->CurrentDirentOffset; + *ReturnNextEntry = BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT ); + } + + // + // Unlock the Fcb. + // + + CdUnlockFcb( IrpContext, Fcb ); + + // + // We have the starting offset in the directory and whether to return + // that entry or the next. If we are at the beginning of the directory + // and are returning that entry, then tell our caller this is the + // initial query. + // + + *InitialQuery = FALSE; + + if ((DirentOffset == Fcb->StreamOffset) && + !(*ReturnNextEntry)) { + + *InitialQuery = TRUE; + } + + // + // If there is no file object then create it now. + // + + CdVerifyOrCreateDirStreamFile( IrpContext, Fcb); + + // + // Determine the offset in the stream to position the FileContext and + // whether this offset is known to be a file offset. + // + // If this offset is known to be safe then go ahead and position the + // file context. This handles the cases where the offset is the beginning + // of the stream, the offset is from a previous search or this is the + // initial query. + // + + if (KnownOffset) { + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, DirentOffset ); + + // + // Otherwise we walk through the directory from the beginning until + // we reach the entry which contains this offset. + // + + } else { + + LastDirentOffset = Fcb->StreamOffset; + Found = TRUE; + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset ); + + // + // If the requested offset is prior to the beginning offset in the stream + // then don't return the next entry. + // + + if (DirentOffset < LastDirentOffset) { + + *ReturnNextEntry = FALSE; + + // + // Else look for the last entry which ends past the desired index. + // + + } else { + + // + // Keep walking through the directory until we run out of + // entries or we find an entry which ends beyond the input + // index value. + // + + do { + + // + // If we have passed the index value then exit. + // + + if (FileContext->InitialDirent->Dirent.DirentOffset > DirentOffset) { + + Found = FALSE; + break; + } + + // + // Remember the current position in case we need to go back. + // + + LastDirentOffset = FileContext->InitialDirent->Dirent.DirentOffset; + + // + // Exit if the next entry is beyond the desired index value. + // + + if (LastDirentOffset + FileContext->InitialDirent->Dirent.DirentLength > DirentOffset) { + + break; + } + + Found = CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext ); + + } while (Found); + + // + // If we didn't find the entry then go back to the last known entry. + // This can happen if the index lies in the unused range at the + // end of a sector. + // + + if (!Found) { + + CdCleanupFileContext( IrpContext, FileContext ); + CdInitializeFileContext( IrpContext, FileContext ); + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset ); + } + } + } + + // + // Only update the dirent name if we will need it for some reason. + // Don't update this name if we are returning the next entry and + // the search string has a version component. + // + + FileContext->ShortName.FileName.Length = 0; + + if (!(*ReturnNextEntry) || + (Ccb->SearchExpression.VersionString.Length == 0)) { + + // + // Update the name in the dirent into filename and version components. + // + + CdUpdateDirentName( IrpContext, + &FileContext->InitialDirent->Dirent, + FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )); + } + + // + // Look at the flag in the IrpSp indicating whether to return just + // one entry. + // + + *ReturnSingleEntry = FALSE; + + if (FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )) { + + *ReturnSingleEntry = TRUE; + } + + return; +} + + +// +// Local support routine +// + +BOOLEAN +CdEnumerateIndex ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PCCB Ccb, + _Inout_ PFILE_ENUM_CONTEXT FileContext, + _In_ BOOLEAN ReturnNextEntry + ) + +/*++ + +Routine Description: + + This routine is the worker routine for index enumeration. We are positioned + at some dirent in the directory and will either return the first match + at that point or look to the next entry. The Ccb contains details about + the type of matching to do. If the user didn't specify a version in + his search string then we only return the first version of a sequence + of files with versions. We also don't return any associated files. + +Arguments: + + Ccb - Ccb for this directory handle. + + FileContext - File context already positioned at some entry in the directory. + + ReturnNextEntry - Indicates if we are returning this entry or should start + with the next entry. + +Return Value: + + BOOLEAN - TRUE if next entry is found, FALSE otherwise. + +--*/ + +{ + PDIRENT PreviousDirent = NULL; + PDIRENT ThisDirent = &FileContext->InitialDirent->Dirent; + + BOOLEAN Found = FALSE; + + PAGED_CODE(); + + // + // Loop until we find a match or exaust the directory. + // + + while (TRUE) { + + // + // Move to the next entry unless we want to consider the current + // entry. + // + + if (ReturnNextEntry) { + + if (!CdLookupNextInitialFileDirent( IrpContext, Ccb->Fcb, FileContext )) { + + break; + } + + PreviousDirent = ThisDirent; + ThisDirent = &FileContext->InitialDirent->Dirent; + + CdUpdateDirentName( IrpContext, ThisDirent, FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )); + + } else { + + ReturnNextEntry = TRUE; + } + + // + // Don't bother if we have a constant entry and are ignoring them. + // + + if (FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ) && + FlagOn( Ccb->Flags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY )) { + + continue; + } + + // + // Look at the current entry if it is not an associated file + // and the name doesn't match the previous file if the version + // name is not part of the search. + // + + if (!FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) { + + // + // Check if this entry matches the previous entry except + // for version number and whether we should return the + // entry in that case. Go directly to the name comparison + // if: + // + // There is no previous entry. + // The search expression has a version component. + // The name length doesn't match the length of the previous entry. + // The base name strings don't match. + // + + if ((PreviousDirent == NULL) || + (Ccb->SearchExpression.VersionString.Length != 0) || + (PreviousDirent->CdCaseFileName.FileName.Length != ThisDirent->CdCaseFileName.FileName.Length) || + FlagOn( PreviousDirent->DirentFlags, CD_ATTRIBUTE_ASSOC ) || + !RtlEqualMemory( PreviousDirent->CdCaseFileName.FileName.Buffer, + ThisDirent->CdCaseFileName.FileName.Buffer, + ThisDirent->CdCaseFileName.FileName.Length )) { + + // + // If we match all names then return to our caller. + // + + if (FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) { + + FileContext->ShortName.FileName.Length = 0; + Found = TRUE; + break; + } + + // + // Check if the long name matches the search expression. + // + + if (CdIsNameInExpression( IrpContext, + &ThisDirent->CdCaseFileName, + &Ccb->SearchExpression, + Ccb->Flags, + TRUE )) { + + // + // Let our caller know we found an entry. + // + + Found = TRUE; + FileContext->ShortName.FileName.Length = 0; + break; + } + + // + // The long name didn't match so we need to check for a + // possible short name match. There is no match if the + // long name is 8dot3 or the search expression has a + // version component. Special case the self and parent + // entries. + // + + if ((Ccb->SearchExpression.VersionString.Length == 0) && + !FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ) && + !CdIs8dot3Name( IrpContext, + ThisDirent->CdFileName.FileName )) { + + CdGenerate8dot3Name( IrpContext, + &ThisDirent->CdCaseFileName.FileName, + ThisDirent->DirentOffset, + FileContext->ShortName.FileName.Buffer, + &FileContext->ShortName.FileName.Length ); + + // + // Check if this name matches. + // + + if (CdIsNameInExpression( IrpContext, + &FileContext->ShortName, + &Ccb->SearchExpression, + Ccb->Flags, + FALSE )) { + + // + // Let our caller know we found an entry. + // + + Found = TRUE; + break; + } + } + } + } + } + + // + // If we found the entry then make sure we walk through all of the + // file dirents. + // + + if (Found) { + + CdLookupLastFileDirent( IrpContext, Ccb->Fcb, FileContext ); + } + + return Found; +} + + diff --git a/filesys/cdfs/dirsup.c b/filesys/cdfs/dirsup.c new file mode 100644 index 00000000..b1667d3d --- /dev/null +++ b/filesys/cdfs/dirsup.c @@ -0,0 +1,1870 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DirSup.c + +Abstract: + + This module implements the dirent support routines for Cdfs. + + Directories on a CD consist of a number of contiguous sectors on + the disk. File descriptors consist of one or more directory entries + (dirents) within a directory. Files may contain version numbers. If + present all like-named files will be ordered contiguously in the + directory by decreasing version numbers. We will only return the + first of these on a directory query unless the user explicitly + asks for version numbers. Finally dirents will not span sector + boundaries. Unused bytes at the end of a sector will be zero + filled. + + Directory sector: Offset + 2048 + +---------------------------------------------------------------+ + | | | | | | | + | foo;4 | foo;4 | foo;3 | hat | zebra | Zero| + | | | | | | Fill| + | | final | single | | | | + | | extent | extent | | | | + +---------------------------------------------------------------+ + + Dirent operations: + + - Position scan at known offset in directory. Dirent at this + offset must exist and is valid. Used when scanning a directory + from the beginning when the self entry is known to be valid. + Used when positioning at the first dirent for an open + file to scan the allocation information. Used when resuming + a directory enumeration from a valid directory entry. + + - Position scan at known offset in directory. Dirent is known to + start at this position but must be checked for validity. + Used to read the self-directory entry. + + - Move to the next dirent within a directory. + + - Given a known starting dirent, collect all the dirents for + that file. Scan will finish positioned at the last dirent + for the file. We will accumulate the extent lengths to + find the size of the file. + + - Given a known starting dirent, position the scan for the first + dirent of the following file. Used when not interested in + all of the details for the current file and are looking for + the next file. + + - Update a common dirent structure with the details of the on-disk + structure. This is used to smooth out the differences + + - Build the filename (name and version strings) out of the stream + of bytes in the file name on disk. For Joliet disks we will have + to convert to little endian. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_DIRSUP) + +// +// Local macros +// + +// +// PRAW_DIRENT +// CdRawDirent ( +// _In_ PIRP_CONTEXT IrpContext, +// _In_ PDIR_ENUM_CONTEXT DirContext +// ); +// + +#define CdRawDirent(IC,DC) \ + Add2Ptr( (DC)->Sector, (DC)->SectorOffset, PRAW_DIRENT ) + +// +// Local support routines +// + +ULONG +CdCheckRawDirentBounds ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PDIRENT_ENUM_CONTEXT DirContext + ); + +XA_EXTENT_TYPE +CdCheckForXAExtent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PRAW_DIRENT RawDirent, + _Inout_ PDIRENT Dirent + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCheckForXAExtent) +#pragma alloc_text(PAGE, CdCheckRawDirentBounds) +#pragma alloc_text(PAGE, CdCleanupFileContext) +#pragma alloc_text(PAGE, CdFindFile) +#pragma alloc_text(PAGE, CdFindDirectory) +#pragma alloc_text(PAGE, CdFindFileByShortName) +#pragma alloc_text(PAGE, CdLookupDirent) +#pragma alloc_text(PAGE, CdLookupLastFileDirent) +#pragma alloc_text(PAGE, CdLookupNextDirent) +#pragma alloc_text(PAGE, CdLookupNextInitialFileDirent) +#pragma alloc_text(PAGE, CdUpdateDirentFromRawDirent) +#pragma alloc_text(PAGE, CdUpdateDirentName) +#endif + + +VOID +CdLookupDirent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ ULONG DirentOffset, + _Out_ PDIRENT_ENUM_CONTEXT DirContext + ) + +/*++ + +Routine Description: + + This routine is called to initiate a walk through a directory. We will + position ourselves in the directory at offset DirentOffset. We know that + a dirent begins at this boundary but may have to verify the dirent bounds. + We will call this routine when looking up the first entry of a known + file or verifying the self entry of a directory. + +Arguments: + + Fcb - Fcb for the directory being traversed. + + DirentOffset - This is our target point in the directory. We will map the + page containing this entry and possibly verify the dirent bounds at + this location. + + DirContext - This is the dirent context for this scan. We update it with + the location of the dirent we found. This structure has been initialized + outside of this call. + +Return Value: + + None. + +--*/ + +{ + LONGLONG BaseOffset; + + PAGED_CODE(); + + // + // Initialize the offset of the first dirent we want to map. + // + + DirContext->BaseOffset = SectorTruncate( DirentOffset ); + BaseOffset = DirContext->BaseOffset; + + DirContext->DataLength = SECTOR_SIZE; + + DirContext->SectorOffset = SectorOffset( DirentOffset ); + + // + // Truncate the data length if we are at the end of the file. + // + + if (DirContext->DataLength > (Fcb->FileSize.QuadPart - BaseOffset)) { + + DirContext->DataLength = (ULONG) (Fcb->FileSize.QuadPart - BaseOffset); + } + + // + // Now map the data at this offset. + // + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &BaseOffset, + DirContext->DataLength, + TRUE, + &DirContext->Bcb, + &DirContext->Sector ); + + // + // Verify the dirent bounds. + // + + DirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext, + DirContext ); + + return; +} + + +BOOLEAN +CdLookupNextDirent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PDIRENT_ENUM_CONTEXT CurrentDirContext, + _Inout_ PDIRENT_ENUM_CONTEXT NextDirContext + ) + +/*++ + +Routine Description: + + This routine is called to find the next dirent in the directory. The + current position is given and we look for the next. We leave the context + for the starting position untouched and update the context for the + dirent we found. The target context may already be initialized so we + may already have the sector in memory. + + This routine will position the enumeration context for the next dirent and + verify the dirent bounds. + + NOTE - This routine can be called with CurrentDirContext and NextDirContext + pointing to the same enumeration context. + +Arguments: + + Fcb - Fcb for the directory being traversed. + + CurrentDirContext - This is the dirent context for this scan. We update + it with the location of the dirent we found. This is currently + pointing to a dirent location. The dirent bounds at this location + have already been verified. + + NextDirContext - This is the dirent context to update with the dirent we + find. This may already point to a dirent so we need to check if + we are in the same sector and unmap any buffer as necessary. + + This dirent is left in an indeterminant state if we don't find a dirent. + +Return Value: + + BOOLEAN - TRUE if we find a location for the next dirent, FALSE otherwise. + This routine can cause a raise if the directory is corrupt. + +--*/ + +{ + LONGLONG CurrentBaseOffset = CurrentDirContext->BaseOffset; + ULONG TempUlong; + + BOOLEAN FoundDirent = FALSE; + + PAGED_CODE(); + + // + // Check if a different sector is mapped. If so then move our target + // enumeration context to the same sector. + // + + if ((CurrentDirContext->BaseOffset != NextDirContext->BaseOffset) || + (NextDirContext->Bcb == NULL)) { + + // + // Unpin the current target Bcb and map the next sector. + // + + CdUnpinData( IrpContext, &NextDirContext->Bcb ); + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &CurrentBaseOffset, + CurrentDirContext->DataLength, + TRUE, + &NextDirContext->Bcb, + &NextDirContext->Sector ); + + // + // Copy the data length and sector offset. + // + + NextDirContext->DataLength = CurrentDirContext->DataLength; + NextDirContext->BaseOffset = CurrentDirContext->BaseOffset; + } + + // + // Now move to the same offset in the sector. + // + + NextDirContext->SectorOffset = CurrentDirContext->SectorOffset; + + // + // If the value is zero then unmap the current sector and set up + // the base offset to the beginning of the next sector. + // + + if (CurrentDirContext->NextDirentOffset == 0) { + + CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength; + + // + // Unmap the current sector. We test the value of the Bcb in the + // loop below to see if we need to read in another sector. + // + + CdUnpinData( IrpContext, &NextDirContext->Bcb ); + + // + // There is another possible dirent in the current sector. Update the + // enumeration context to reflect this. + // + + } else { + + NextDirContext->SectorOffset += CurrentDirContext->NextDirentOffset; + } + + // + // Now loop until we find the next possible dirent or walk off the directory. + // + + while (TRUE) { + + // + // If we don't currently have a sector mapped then map the + // directory at the current offset. + // + + if (NextDirContext->Bcb == NULL) { + + TempUlong = SECTOR_SIZE; + + if (TempUlong > (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset)) { + + TempUlong = (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset); + + // + // If the length is zero then there is no dirent. + // + + if (TempUlong == 0) { + + break; + } + } + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &CurrentBaseOffset, + TempUlong, + TRUE, + &NextDirContext->Bcb, + &NextDirContext->Sector ); + + NextDirContext->BaseOffset = (ULONG) CurrentBaseOffset; + NextDirContext->SectorOffset = 0; + NextDirContext->DataLength = TempUlong; + } + + // + // The CDFS spec allows for sectors in a directory to contain all zeroes. + // In this case we need to move to the next sector. So look at the + // current potential dirent for a zero length. Move to the next + // dirent if length is zero. + // + + if (*((PCHAR) CdRawDirent( IrpContext, NextDirContext )) != 0) { + + FoundDirent = TRUE; + break; + } + + CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength; + CdUnpinData( IrpContext, &NextDirContext->Bcb ); + } + + // + // Check the dirent bounds if we found a dirent. + // + + if (FoundDirent) { + + NextDirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext, + NextDirContext ); + } + + return FoundDirent; +} + + +_At_(Dirent->CdTime, _Post_notnull_) +VOID +CdUpdateDirentFromRawDirent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PDIRENT_ENUM_CONTEXT DirContext, + _Inout_ PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine is called to safely copy the data from the dirent on disk + to the in-memory dirent. The fields on disk are unaligned so we + need to safely copy them to our structure. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + DirContext - Enumeration context for the raw disk dirent. + + Dirent - In-memory dirent to update. + +Return Value: + + None. + +--*/ + +{ + PRAW_DIRENT RawDirent = CdRawDirent( IrpContext, DirContext ); + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Fcb ); + + // + // Clear all of the current state flags except the flag indicating that + // we allocated a name string. + // + + ClearFlag( Dirent->Flags, DIRENT_FLAG_NOT_PERSISTENT ); + + // + // The dirent offset is the sum of the start of the sector and the + // sector offset. + // + + Dirent->DirentOffset = DirContext->BaseOffset + DirContext->SectorOffset; + + // + // Copy the dirent length from the raw dirent. + // + + Dirent->DirentLength = RawDirent->DirLen; + + // + // The starting offset on disk is computed by finding the starting + // logical block and stepping over the Xar block. + // + + CopyUchar4( &Dirent->StartingOffset, RawDirent->FileLoc ); + + Dirent->StartingOffset += RawDirent->XarLen; + + // + // Do a safe copy to get the data length. + // + + CopyUchar4( &Dirent->DataLength, RawDirent->DataLen ); + + // + // Save a pointer to the time stamps. + // + + Dirent->CdTime = (PCHAR)RawDirent->RecordTime; + + // + // Copy the dirent flags. + // + + Dirent->DirentFlags = CdRawDirentFlags( IrpContext, RawDirent ); + + // + // For both the file unit and interleave skip we want to take the + // logical block count. + // + + Dirent->FileUnitSize = + Dirent->InterleaveGapSize = 0; + + if (RawDirent->IntLeaveSize != 0) { + + Dirent->FileUnitSize = RawDirent->IntLeaveSize; + Dirent->InterleaveGapSize = RawDirent->IntLeaveSkip; + } + + // + // Get the name length and remember a pointer to the start of the + // name string. We don't do any processing on the name at this + // point. + // + // Check that the name length is non-zero. + // + + if (RawDirent->FileIdLen == 0) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + Dirent->FileNameLen = RawDirent->FileIdLen; + Dirent->FileName = (PCHAR)RawDirent->FileId; + + // + // If there are any remaining bytes at the end of the dirent then + // there may be a system use area. We protect ourselves from + // disks which don't pad the dirent entries correctly by using + // a fudge factor of one. All system use areas must have a length + // greater than one. Don't bother with the system use area + // if this is a directory. + // + + Dirent->XAAttributes = 0; + Dirent->XAFileNumber = 0; + Dirent->ExtentType = Form1Data; + Dirent->SystemUseOffset = 0; + + if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY ) && + (Dirent->DirentLength > ((FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen) + 1))) { + + Dirent->SystemUseOffset = WordAlign( FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen ); + } + + return; +} + + +VOID +CdUpdateDirentName ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PDIRENT Dirent, + _In_ ULONG IgnoreCase + ) + +/*++ + +Routine Description: + + This routine is called to update the name in the dirent with the name + from the disk. We will look for the special case of the self and + parent entries and also construct the Unicode name for the Joliet disk + in order to work around the BigEndian on-disk structure. + +Arguments: + + Dirent - Pointer to the in-memory dirent structure. + + IgnoreCase - TRUE if we should build the upcased version. Otherwise we + use the exact case name. + +Return Value: + + None. + +--*/ + +{ + UCHAR DirectoryValue; + ULONG Length; + + NTSTATUS Status; + + PAGED_CODE(); + + // + // Check if this is a self or parent entry. There is no version number + // in these cases. We use a fixed string for these. + // + // Self-Entry - Length is 1, value is 0. + // Parent-Entry - Length is 1, value is 1. + // + + if ((Dirent->FileNameLen == 1) && + FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { + + DirectoryValue = *((PCHAR) Dirent->FileName); + + if ((DirectoryValue == 0) || (DirectoryValue == 1)) { + + // + // We should not have allocated a name by the time we see these cases. + // If we have, this means that the image is in violation of ISO 9660 7.6.2, + // which states that the ./.. entries must be the first two in the directory. + // + + if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Now use one of the hard coded directory names. + // + + Dirent->CdFileName.FileName = CdUnicodeDirectoryNames[DirectoryValue]; + + // + // Show that there is no version number. + // + + Dirent->CdFileName.VersionString.Length = 0; + + // + // The case name is the same as the exact name. + // + + Dirent->CdCaseFileName = Dirent->CdFileName; + + // + // Mark this as a constant value entry. + // + + SetFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ); + + // + // Return now. + // + + return; + } + } + + // + // Mark this as a non-constant value entry. + // + + ClearFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ); + + // + // Compute how large a buffer we will need. If this is an ignore + // case operation then we will want a double size buffer. If the disk is not + // a Joliet disk then we might need two bytes for each byte in the name. + // + + Length = Dirent->FileNameLen; + + if (IgnoreCase) { + + Length *= 2; + } + + if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + Length *= sizeof( WCHAR ); + } + + // + // Now decide if we need to allocate a new buffer. We will if + // this name won't fit in the embedded name buffer and it is + // larger than the current allocated buffer. We always use the + // allocated buffer if present. + // + // If we haven't allocated a buffer then use the embedded buffer if the data + // will fit. This is the typical case. + // + + if (!FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ) && + (Length <= sizeof( Dirent->NameBuffer ))) { + + Dirent->CdFileName.FileName.MaximumLength = sizeof( Dirent->NameBuffer ); + Dirent->CdFileName.FileName.Buffer = Dirent->NameBuffer; + + } else { + + // + // We need to use an allocated buffer. Check if the current buffer + // is large enough. + // + + if (Length > Dirent->CdFileName.FileName.MaximumLength) { + + // + // Free any allocated buffer. + // + + if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) { + + CdFreePool( &Dirent->CdFileName.FileName.Buffer ); + ClearFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ); + } + + Dirent->CdFileName.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, + Length, + TAG_DIRENT_NAME ); + + SetFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ); + + Dirent->CdFileName.FileName.MaximumLength = (USHORT) Length; + } + } + + // + // We now have a buffer for the name. We need to either convert the on-disk bigendian + // to little endian or covert the name to Unicode. + // + + if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + Status = RtlOemToUnicodeN( Dirent->CdFileName.FileName.Buffer, + Dirent->CdFileName.FileName.MaximumLength, + &Length, + Dirent->FileName, + Dirent->FileNameLen ); + + __analysis_assert( Status == STATUS_SUCCESS ); + NT_ASSERT( Status == STATUS_SUCCESS ); + Dirent->CdFileName.FileName.Length = (USHORT) Length; + + } else { + + // + // Convert this string to little endian. + // + + CdConvertBigToLittleEndian( IrpContext, + Dirent->FileName, + Dirent->FileNameLen, + (PCHAR) Dirent->CdFileName.FileName.Buffer ); + + Dirent->CdFileName.FileName.Length = (USHORT) Dirent->FileNameLen; + } + + // + // Split the name into name and version strings. + // + + CdConvertNameToCdName( IrpContext, + &Dirent->CdFileName ); + + // + // The name length better be non-zero. + // + + if (Dirent->CdFileName.FileName.Length == 0) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If the filename ends with a period then back up one character. + // + + if (Dirent->CdFileName.FileName.Buffer[(Dirent->CdFileName.FileName.Length - sizeof( WCHAR )) / 2] == L'.') { + + // + // Slide the version string down. + // + + if (Dirent->CdFileName.VersionString.Length != 0) { + + PWCHAR NewVersion; + + // + // Start from the position currently containing the separator. + // + + NewVersion = Add2Ptr( Dirent->CdFileName.FileName.Buffer, + Dirent->CdFileName.FileName.Length, + PWCHAR ); + + // + // Now overwrite the period. + // + + RtlMoveMemory( NewVersion - 1, + NewVersion, + Dirent->CdFileName.VersionString.Length + sizeof( WCHAR )); + + // + // Now point to the new version string. + // + + Dirent->CdFileName.VersionString.Buffer = NewVersion; + } + + // + // Shrink the filename length. + // + + Dirent->CdFileName.FileName.Length -= sizeof( WCHAR ); + } + + if (!CdIsLegalName( IrpContext, &Dirent->CdFileName.FileName )) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If this an exact case operation then use the filename exactly. + // + + if (!IgnoreCase) { + + Dirent->CdCaseFileName = Dirent->CdFileName; + + // + // Otherwise perform our upcase operation. We already have guaranteed the buffers are + // there. + // + + } else { + + Dirent->CdCaseFileName.FileName.Buffer = Add2Ptr( Dirent->CdFileName.FileName.Buffer, + Dirent->CdFileName.FileName.MaximumLength / 2, + PWCHAR); + + Dirent->CdCaseFileName.FileName.MaximumLength = Dirent->CdFileName.FileName.MaximumLength / 2; + + CdUpcaseName( IrpContext, + &Dirent->CdFileName, + &Dirent->CdCaseFileName ); + } + + return; +} + + +_Success_(return != FALSE) BOOLEAN +CdFindFile ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PCD_NAME Name, + _In_ BOOLEAN IgnoreCase, + _Inout_ PFILE_ENUM_CONTEXT FileContext, + _Out_ PCD_NAME *MatchingName + ) + +/*++ + +Routine Description: + + This routine is called to search a dirctory for a file matching the input + name. This name has been upcased at this point if this a case-insensitive + search. The name has been separated into separate name and version strings. + We look for an exact match in the name and only consider the version if + there is a version specified in the search name. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + Name - Name to search for. + + IgnoreCase - Indicates the case of the search. + + FileContext - File context to use for the search. This has already been + initialized. + + MatchingName - Pointer to buffer containing matching name. We need this + in case we don't match the name in the directory but match the + short name instead. + +Return Value: + + BOOLEAN - TRUE if matching entry is found, FALSE otherwise. + +--*/ + +{ + PDIRENT Dirent; + ULONG ShortNameDirentOffset; + + BOOLEAN Found = FALSE; + + PAGED_CODE(); + + // + // Make sure there is a stream file for this Fcb. + // + + CdVerifyOrCreateDirStreamFile( IrpContext, Fcb); + + // + // Check to see whether we need to check for a possible short name. + // + + ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &Name->FileName ); + + // + // Position ourselves at the first entry. + // + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); + + // + // Loop while there are more entries in this directory. + // + + do { + + Dirent = &FileContext->InitialDirent->Dirent; + + // + // We only consider files which don't have the associated bit set. + // We also only look for files. All directories would already + // have been found. + // + + if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC | CD_ATTRIBUTE_DIRECTORY )) { + + // + // Update the name in the current dirent. + // + + CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); + + // + // Don't bother with constant entries. + // + + if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) { + + continue; + } + + // + // Now check whether we have a name match. + // We exit the loop if we have a match. + // + + if (CdIsNameInExpression( IrpContext, + &Dirent->CdCaseFileName, + Name, + 0, + TRUE )) { + + *MatchingName = &Dirent->CdCaseFileName; + Found = TRUE; + break; + } + + // + // The names didn't match. If the input name is a possible short + // name and we are at the correct offset in the directory then + // check if the short names match. + // + + if (((Dirent->DirentOffset >> SHORT_NAME_SHIFT) == ShortNameDirentOffset) && + (Name->VersionString.Length == 0) && + !CdIs8dot3Name( IrpContext, + Dirent->CdFileName.FileName )) { + + // + // Create the short name and check for a match. + // + + CdGenerate8dot3Name( IrpContext, + &Dirent->CdCaseFileName.FileName, + Dirent->DirentOffset, + FileContext->ShortName.FileName.Buffer, + &FileContext->ShortName.FileName.Length ); + + // + // Now check whether we have a name match. + // We exit the loop if we have a match. + // + + if (CdIsNameInExpression( IrpContext, + &FileContext->ShortName, + Name, + 0, + FALSE )) { + + *MatchingName = &FileContext->ShortName, + Found = TRUE; + break; + } + } + } + + // + // Go to the next initial dirent for a file. + // + + } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); + + // + // If we find the file then collect all of the dirents. + // + + if (Found) { + + CdLookupLastFileDirent( IrpContext, Fcb, FileContext ); + + } + + return Found; +} + + +BOOLEAN +CdFindDirectory ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PCD_NAME Name, + _In_ BOOLEAN IgnoreCase, + _Inout_ PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to search a dirctory for a directory matching the input + name. This name has been upcased at this point if this a case-insensitive + search. We look for an exact match in the name and do not look for shortname + equivalents. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + Name - Name to search for. + + IgnoreCase - Indicates the case of the search. + + FileContext - File context to use for the search. This has already been + initialized. + +Return Value: + + BOOLEAN - TRUE if matching entry is found, FALSE otherwise. + +--*/ + +{ + PDIRENT Dirent; + + BOOLEAN Found = FALSE; + + PAGED_CODE(); + + // + // Make sure there is a stream file for this Fcb. + // + + CdVerifyOrCreateDirStreamFile( IrpContext, Fcb); + + // + // Position ourselves at the first entry. + // + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); + + // + // Loop while there are more entries in this directory. + // + + do { + + Dirent = &FileContext->InitialDirent->Dirent; + + // + // We only look for directories. Directories cannot have the + // associated bit set. + // + + if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { + + // + // Update the name in the current dirent. + // + + CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); + + // + // Don't bother with constant entries. + // + + if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) { + + continue; + } + + // + // Now check whether we have a name match. + // We exit the loop if we have a match. + // + + if (CdIsNameInExpression( IrpContext, + &Dirent->CdCaseFileName, + Name, + 0, + TRUE )) { + + Found = TRUE; + break; + } + } + + // + // Go to the next initial dirent. + // + + } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); + + return Found; +} + + +_At_(FileContext->ShortName.FileName.MaximumLength, _In_range_(>=, BYTE_COUNT_8_DOT_3)) +BOOLEAN +CdFindFileByShortName ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PCD_NAME Name, + _In_ BOOLEAN IgnoreCase, + _In_ ULONG ShortNameDirentOffset, + _Inout_ PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to find the file name entry whose short name + is defined by the input DirentOffset. The dirent offset here is + multiplied by 32 and we look for the dirent begins in this 32 byte offset in + directory. The minimum dirent length is 34 so we are guaranteed that only + one dirent can begin in each 32 byte block in the directory. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + Name - Name we are trying to match. We know this contains the tilde + character followed by decimal characters. + + IgnoreCase - Indicates whether we need to upcase the long name and + generated short name. + + ShortNameDirentOffset - This is the shifted value for the offset of the + name in the directory. + + FileContext - This is the initialized file context to use for the search. + +Return Value: + + BOOLEAN - TRUE if a matching name was found, FALSE otherwise. + +--*/ + +{ + BOOLEAN Found = FALSE; + PDIRENT Dirent; + + ULONG ThisShortNameDirentOffset; + + PAGED_CODE(); + + // + // Make sure there is a stream file for this Fcb. + // + + CdVerifyOrCreateDirStreamFile( IrpContext, Fcb); + + // + // Position ourselves at the start of the directory and update + // + // + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); + + // + // Loop until we have found the entry or are beyond this dirent. + // + + do { + + // + // Compute the short name dirent offset for the current dirent. + // + + Dirent = &FileContext->InitialDirent->Dirent; + ThisShortNameDirentOffset = Dirent->DirentOffset >> SHORT_NAME_SHIFT; + + // + // If beyond the target then exit. + // + + if (ThisShortNameDirentOffset > ShortNameDirentOffset) { + + break; + } + + // + // If equal to the target then check if we have a name match. + // We will either match or fail here. + // + + if (ThisShortNameDirentOffset == ShortNameDirentOffset) { + + // + // If this is an associated file then get out. + // + + if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) { + + break; + } + + // + // Update the name in the dirent and check if it is not + // an 8.3 name. + // + + CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); + + if (CdIs8dot3Name( IrpContext, + Dirent->CdFileName.FileName )) { + + break; + } + + // + // Generate the 8.3 name see if it matches our input name. + // + + CdGenerate8dot3Name( IrpContext, + &Dirent->CdCaseFileName.FileName, + Dirent->DirentOffset, + FileContext->ShortName.FileName.Buffer, + &FileContext->ShortName.FileName.Length ); + + // + // Check if this name matches. + // + + if (CdIsNameInExpression( IrpContext, + Name, + &FileContext->ShortName, + 0, + FALSE )) { + + // + // Let our caller know we found an entry. + // + + Found = TRUE; + } + + // + // Break out of the loop. + // + + break; + } + + // + // Continue until there are no more entries. + // + + } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); + + // + // If we find the file then collect all of the dirents. + // + + if (Found) { + + CdLookupLastFileDirent( IrpContext, Fcb, FileContext ); + + } + + return Found; +} + + +BOOLEAN +CdLookupNextInitialFileDirent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Inout_ PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to walk through the directory until we find the + first possible dirent for file. We are positioned at some point described + by the FileContext. We will walk through any remaing dirents for the + current file until we find the first dirent for some subsequent file. + + We can be called when we have found just one dirent for a file or all + of them. We first check the CurrentDirContext. In the typical + single-extent case this is unused. Then we look to the InitialDirContext + which must be initialized. + + This routine will save the initial DirContext to the PriorDirContext and + clean up any existing DirContext for the Prior or Current positions in + the enumeration context. + +Arguments: + + Fcb - This is the directory to scan. + + FileContext - This is the file enumeration context. It is currently pointing + at some file in the directory. + +Return Value: + +--*/ + +{ + PRAW_DIRENT RawDirent; + + PDIRENT_ENUM_CONTEXT CurrentDirContext; + PDIRENT_ENUM_CONTEXT TargetDirContext; + PCOMPOUND_DIRENT TempDirent; + + BOOLEAN FoundDirent = FALSE; + BOOLEAN FoundLastDirent; + + PAGED_CODE(); + + // + // Start by saving the initial dirent of the current file as the + // previous file. + // + + TempDirent = FileContext->PriorDirent; + FileContext->PriorDirent = FileContext->InitialDirent; + FileContext->InitialDirent = TempDirent; + + // + // We will use the initial dirent of the prior file unless the + // previous search returned multiple extents. + // + + CurrentDirContext = &FileContext->PriorDirent->DirContext; + + if (FlagOn( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS )) { + + CurrentDirContext = &FileContext->CurrentDirent->DirContext; + } + + // + // Clear all of the flags and file size for the next file. + // + + FileContext->Flags = 0; + FileContext->FileSize = 0; + + FileContext->ShortName.FileName.Length = 0; + + // + // We always want to store the result into the updated initial dirent + // context. + // + + TargetDirContext = &FileContext->InitialDirent->DirContext; + + // + // Loop until we find the first dirent after the last dirent of the + // current file. We may not be at the last dirent for the current file yet + // so we may walk forward looking for the last and then find the + // initial dirent for the next file after that. + // + + while (TRUE) { + + // + // Remember if the last dirent we visited was the last dirent for + // a file. + // + + RawDirent = CdRawDirent( IrpContext, CurrentDirContext ); + + FoundLastDirent = !FlagOn( CdRawDirentFlags( IrpContext, RawDirent ), CD_ATTRIBUTE_MULTI ); + + // + // Try to find another dirent. + // + + FoundDirent = CdLookupNextDirent( IrpContext, + Fcb, + CurrentDirContext, + TargetDirContext ); + + // + // Exit the loop if no entry found. + // + + if (!FoundDirent) { + + break; + + } + + // + // Update the in-memory dirent. + // + + CdUpdateDirentFromRawDirent( IrpContext, + Fcb, + TargetDirContext, + &FileContext->InitialDirent->Dirent ); + + // + // Exit the loop if we had the end for the previous file. + // + + if (FoundLastDirent) { + + break; + } + + // + // Always use a single dirent from this point on. + // + + CurrentDirContext = TargetDirContext; + } + + return FoundDirent; +} + + +VOID +CdLookupLastFileDirent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called when we've found the matching initial dirent for + a file. Now we want to find all of the dirents for a file as well as + compute the running total for the file size. + + We also go out to the system use area and check whether this is an + XA sector. In that case we will compute the real file size. + + The dirent in the initial compound dirent has been updated from the + raw dirent when this routine is called. + +Arguments: + + Fcb - Directory containing the entries for the file. + + FileContext - Enumeration context for this search. It currently points + to the first dirent of the file and the in-memory dirent has been + updated. + +Return Value: + + None. This routine may raise STATUS_FILE_CORRUPT. + +--*/ + +{ + XA_EXTENT_TYPE ExtentType = Form1Data; + PCOMPOUND_DIRENT CurrentCompoundDirent; + PDIRENT CurrentDirent = NULL; + + BOOLEAN FirstPass = TRUE; + BOOLEAN FoundDirent; + + PAGED_CODE(); + + // + // The current dirent to look at is the initial dirent for the file. + // + + CurrentCompoundDirent = FileContext->InitialDirent; + + // + // Loop until we reach the last dirent for the file. + // + + while (TRUE) { + + CurrentDirent = &CurrentCompoundDirent->Dirent; + + // + // Check if this extent has XA sectors. + // + + if ((CurrentDirent->SystemUseOffset != 0) && + FlagOn( Fcb->Vcb->VcbState, VCB_STATE_CDXA ) && + CdCheckForXAExtent( IrpContext, + CdRawDirent( IrpContext, &CurrentCompoundDirent->DirContext ), + CurrentDirent )) { + + // + // Any previous dirent must describe XA sectors as well. + // + + if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If there are XA sectors then the data on the disk must + // be correctly aligned on sectors and be an integral number of + // sectors. Only an issue if the logical block size is not + // 2048. + // + + if (Fcb->Vcb->BlockSize != SECTOR_SIZE) { + + // + // We will do the following checks. + // + // Data must start on a sector boundary. + // Data length must be integral number of sectors. + // + + if ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->StartingOffset ) != 0) || + (SectorBlockOffset( Fcb->Vcb, CurrentDirent->DataLength ) != 0)) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If interleaved then both the file unit and interleave + // gap must be integral number of sectors. + // + + if ((CurrentDirent->FileUnitSize != 0) && + ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->FileUnitSize ) != 0) || + (SectorBlockOffset( Fcb->Vcb, CurrentDirent->InterleaveGapSize ) != 0))) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + } + + // + // If this is the first dirent then add the bytes for the RIFF + // header. + // + + if (FirstPass) { + + FileContext->FileSize = sizeof( RIFF_HEADER ); + } + + // + // Add the size of the mode2-form2 sector for each sector + // we have here. + // + + FileContext->FileSize += Int32x32To64( CurrentDirent->DataLength >> SECTOR_SHIFT, + XA_SECTOR_SIZE); + + } else { + + // + // This extent does not have XA sectors. Any previous dirent + // better not have XA sectors. + // + + if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Add these bytes to the file size. + // + + FileContext->FileSize += CurrentDirent->DataLength; + } + + // + // If we are at the last dirent then exit. + // + + if (!FlagOn( CurrentDirent->DirentFlags, CD_ATTRIBUTE_MULTI )) { + + break; + } + + // + // Remember the extent type of the current extent. + // + + ExtentType = CurrentDirent->ExtentType; + + // + // Look for the next dirent of the file. + // + + FoundDirent = CdLookupNextDirent( IrpContext, + Fcb, + &CurrentCompoundDirent->DirContext, + &FileContext->CurrentDirent->DirContext ); + + // + // If we didn't find the entry then this is a corrupt directory. + // + + if (!FoundDirent) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Remember the dirent we just found. + // + + CurrentCompoundDirent = FileContext->CurrentDirent; + FirstPass = FALSE; + + // + // Look up all of the dirent information for the given dirent. + // + + CdUpdateDirentFromRawDirent( IrpContext, + Fcb, + &CurrentCompoundDirent->DirContext, + &CurrentCompoundDirent->Dirent ); + + // + // Set flag to show there were multiple extents. + // + + SetFlag( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS ); + } + + return; +} + + +VOID +CdCleanupFileContext ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to cleanup the enumeration context for a file + search in a directory. We will unpin any remaining Bcbs and free + any allocated buffers. + +Arguments: + + FileContext - Enumeration context for the file search. + +Return Value: + + None. + +--*/ + +{ + PCOMPOUND_DIRENT CurrentCompoundDirent; + ULONG Count = 2; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Cleanup the individual compound dirents. + // + + do { + + CurrentCompoundDirent = &FileContext->Dirents[ Count ]; + CdCleanupDirContext( IrpContext, &CurrentCompoundDirent->DirContext ); + CdCleanupDirent( IrpContext, &CurrentCompoundDirent->Dirent ); + + } while (Count--); + + return; +} + + +// +// Local support routine +// + +ULONG +CdCheckRawDirentBounds ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PDIRENT_ENUM_CONTEXT DirContext + ) + +/*++ + +Routine Description: + + This routine takes a Dirent enumeration context and computes the offset + to the next dirent. A non-zero value indicates the offset within this + sector. A zero value indicates to move to the next sector. If the + current dirent does not fit within the sector then we will raise + STATUS_CORRUPT. + +Arguments: + + DirContext - Enumeration context indicating the current position in + the sector. + +Return Value: + + ULONG - Offset to the next dirent in this sector or zero if the + next dirent is in the next sector. + + This routine will raise on a dirent which does not fit into the + described data buffer. + +--*/ + +{ + ULONG NextDirentOffset; + PRAW_DIRENT RawDirent; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // We should always have at least a byte still available in the + // current buffer. + // + + NT_ASSERT( (DirContext->DataLength - DirContext->SectorOffset) >= 1 ); + + // + // Get a pointer to the current dirent. + // + + RawDirent = CdRawDirent( IrpContext, DirContext ); + + // + // If the dirent length is non-zero then look at the current dirent. + // + + if (RawDirent->DirLen != 0) { + + // + // Check the following bound for the dirent length. + // + // - Fits in the available bytes in the sector. + // - Is at least the minimal dirent size. + // - Is large enough to hold the file name. + // + + if ((RawDirent->DirLen > (DirContext->DataLength - DirContext->SectorOffset)) || + (RawDirent->DirLen < MIN_RAW_DIRENT_LEN) || + (RawDirent->DirLen < (MIN_RAW_DIRENT_LEN - 1 + RawDirent->FileIdLen))) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Copy the dirent length field. + // + + NextDirentOffset = RawDirent->DirLen; + + // + // If we are exactly at the next sector then tell our caller by + // returning zero. + // + + if (NextDirentOffset == (DirContext->DataLength - DirContext->SectorOffset)) { + + NextDirentOffset = 0; + } + + } else { + + NextDirentOffset = 0; + } + + return NextDirentOffset; +} + + +// +// Local support routine +// + +XA_EXTENT_TYPE +CdCheckForXAExtent ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PRAW_DIRENT RawDirent, + _Inout_ PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine is called to scan through the system use area to test if + the current dirent has the XA bit set. The bit in the in-memory + dirent will be set as appropriate. + +Arguments: + + RawDirent - Pointer to the on-disk dirent. + + Dirent - Pointer to the in-memory dirent. We will update this with the + appropriate XA flag. + +Return Value: + + XA_EXTENT_TYPE - Type of physical extent for this on disk dirent. + +--*/ + +{ + XA_EXTENT_TYPE ExtentType = Form1Data; + PSYSTEM_USE_XA SystemUseArea; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Check if there is enough space for the XA system use area. + // + + if (Dirent->DirentLength - Dirent->SystemUseOffset >= sizeof( SYSTEM_USE_XA )) { + + SystemUseArea = Add2Ptr( RawDirent, Dirent->SystemUseOffset, PSYSTEM_USE_XA ); + + // + // Check for a valid signature. + // + + if (SystemUseArea->Signature == SYSTEM_XA_SIGNATURE) { + + // + // Check for an audio track. + // + + if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_DA )) { + + ExtentType = CDAudio; + + } else if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_FORM2 )) { + + // + // Check for XA data. Note that a number of discs (video CDs) + // have files marked as type XA Mode 2 Form 1 (2048 bytes of + // user data), but actually record these sectors as Mode2 Form 2 + // (2352). We will fail to read these files, since for M2F1, + // a normal read CD command is issued (as per SCSI specs). + // + + ExtentType = Mode2Form2Data; + } + + Dirent->XAAttributes = SystemUseArea->Attributes; + Dirent->XAFileNumber = SystemUseArea->FileNumber; + } + } + + Dirent->ExtentType = ExtentType; + return ExtentType; +} + + + diff --git a/filesys/cdfs/fieldoff.c b/filesys/cdfs/fieldoff.c new file mode 100644 index 00000000..0aecc392 --- /dev/null +++ b/filesys/cdfs/fieldoff.c @@ -0,0 +1,476 @@ +#include "CdProcs.h" +#include + +#define doit(a,b) { printf("%s %04lx %4lx %s\n", #a, FIELD_OFFSET(a,b), sizeof(d.b), #b); } + +VOID +__cdecl +main (argc, argv) + int argc; + char *argv[]; +{ + printf(" \n\n"); + { + CD_MCB d; + doit( CD_MCB, MaximumEntryCount ); + doit( CD_MCB, CurrentEntryCount ); + doit( CD_MCB, McbArray ); + } + printf("\n"); + { + CD_MCB_ENTRY d; + doit( CD_MCB_ENTRY, DiskOffset ); + doit( CD_MCB_ENTRY, ByteCount ); + doit( CD_MCB_ENTRY, FileOffset ); + doit( CD_MCB_ENTRY, DataBlockByteCount ); + doit( CD_MCB_ENTRY, TotalBlockByteCount ); + } + printf("\n"); + { + CD_NAME d; + doit( CD_NAME, FileName ); + doit( CD_NAME, VersionString ); + } + printf("\n"); + { + NAME_LINK d; + doit( NAME_LINK, Links ); + doit( NAME_LINK, FileName ); + } + printf("\n"); + { + PREFIX_ENTRY d; + doit( PREFIX_ENTRY, Fcb ); + doit( PREFIX_ENTRY, PrefixFlags ); + doit( PREFIX_ENTRY, ExactCaseName ); + doit( PREFIX_ENTRY, IgnoreCaseName ); + doit( PREFIX_ENTRY, FileNameBuffer ); + } + printf("\n"); + { + CD_DATA d; + doit( CD_DATA, NodeTypeCode ); + doit( CD_DATA, NodeByteSize ); + doit( CD_DATA, DriverObject ); + doit( CD_DATA, VcbQueue ); + doit( CD_DATA, IrpContextDepth ); + doit( CD_DATA, IrpContextMaxDepth ); + doit( CD_DATA, IrpContextList ); + doit( CD_DATA, FileSystemDeviceObject ); + doit( CD_DATA, AsyncCloseQueue ); + doit( CD_DATA, AsyncCloseCount ); + doit( CD_DATA, FspCloseActive ); + doit( CD_DATA, ReduceDelayedClose ); + doit( CD_DATA, PadUshort ); + doit( CD_DATA, DelayedCloseQueue ); + doit( CD_DATA, DelayedCloseCount ); + doit( CD_DATA, MinDelayedCloseCount ); + doit( CD_DATA, MaxDelayedCloseCount ); + doit( CD_DATA, CdDataLockThread ); + doit( CD_DATA, CdDataMutex ); + doit( CD_DATA, DataResource ); + doit( CD_DATA, CacheManagerCallbacks ); + doit( CD_DATA, CacheManagerVolumeCallbacks ); + doit( CD_DATA, CloseItem ); + } + printf("\n"); + { + VCB d; + doit( VCB, NodeTypeCode ); + doit( VCB, NodeByteSize ); + doit( VCB, Vpb ); + doit( VCB, TargetDeviceObject ); + doit( VCB, VolumeLockFileObject ); + doit( VCB, VcbLinks ); + doit( VCB, VcbState ); + doit( VCB, VcbCondition ); + doit( VCB, VcbCleanup ); + doit( VCB, VcbReference ); + doit( VCB, VcbUserReference ); + doit( VCB, VolumeDasdFcb ); + doit( VCB, RootIndexFcb ); + doit( VCB, PathTableFcb ); + doit( VCB, BaseSector ); + doit( VCB, VdSectorOffset ); + doit( VCB, PrimaryVdSectorOffset ); + doit( VCB, XASector ); + doit( VCB, XADiskOffset ); + doit( VCB, VcbResource ); + doit( VCB, FileResource ); + doit( VCB, VcbMutex ); + doit( VCB, VcbLockThread ); + doit( VCB, NotifySync ); + doit( VCB, DirNotifyList ); + doit( VCB, BlockSize ); + doit( VCB, BlockToSectorShift ); + doit( VCB, BlockToByteShift ); + doit( VCB, BlocksPerSector ); + doit( VCB, BlockMask ); + doit( VCB, BlockInverseMask ); + doit( VCB, FcbTable ); + doit( VCB, CdromToc ); + doit( VCB, TocLength ); + doit( VCB, TrackCount ); + doit( VCB, DiskFlags ); + doit( VCB, BlockFactor ); + } + printf("\n"); + { + VOLUME_DEVICE_OBJECT d; + doit( VOLUME_DEVICE_OBJECT, DeviceObject ); + doit( VOLUME_DEVICE_OBJECT, PostedRequestCount ); + doit( VOLUME_DEVICE_OBJECT, OverflowQueueCount ); + doit( VOLUME_DEVICE_OBJECT, OverflowQueue ); + doit( VOLUME_DEVICE_OBJECT, OverflowQueueSpinLock ); + doit( VOLUME_DEVICE_OBJECT, Vcb ); + } + printf("\n"); + { + FCB_DATA d; + doit( FCB_DATA, FileLock ); + } + printf("\n"); + { + FCB_INDEX d; + doit( FCB_INDEX, FileObject ); + doit( FCB_INDEX, StreamOffset ); + doit( FCB_INDEX, FcbQueue ); + doit( FCB_INDEX, Ordinal ); + doit( FCB_INDEX, ChildPathTableOffset ); + doit( FCB_INDEX, ChildOrdinal ); + doit( FCB_INDEX, ExactCaseRoot ); + doit( FCB_INDEX, IgnoreCaseRoot ); + } + printf("\n"); + { + FCB_NONPAGED d; + doit( FCB_NONPAGED, NodeTypeCode ); + doit( FCB_NONPAGED, NodeByteSize ); + doit( FCB_NONPAGED, SegmentObject ); + doit( FCB_NONPAGED, FcbResource ); + doit( FCB_NONPAGED, FcbMutex ); + } + printf("\n"); + { + FCB d; + doit( FCB, Header ); + doit( FCB, Vcb ); + doit( FCB, ParentFcb ); + doit( FCB, FcbLinks ); + doit( FCB, FileId ); + doit( FCB, FcbCleanup ); + doit( FCB, FcbReference ); + doit( FCB, FcbUserReference ); + doit( FCB, FcbState ); + doit( FCB, FileAttributes ); + doit( FCB, XAAttributes ); + doit( FCB, XAFileNumber ); + doit( FCB, FcbLockThread ); + doit( FCB, FcbLockCount ); + doit( FCB, FcbNonpaged ); + doit( FCB, ShareAccess ); + doit( FCB, McbEntry ); + doit( FCB, Mcb ); + doit( FCB, ShortNamePrefix ); + doit( FCB, FileNamePrefix ); + doit( FCB, CreationTime ); + doit( FCB, FcbType ); + } + printf("\n"); + { + CCB d; + doit( CCB, NodeTypeCode ); + doit( CCB, NodeByteSize ); + doit( CCB, Flags ); + doit( CCB, Fcb ); + doit( CCB, CurrentDirentOffset ); + doit( CCB, SearchExpression ); + } + printf("\n"); + { + IRP_CONTEXT d; + doit( IRP_CONTEXT, NodeTypeCode ); + doit( IRP_CONTEXT, NodeByteSize ); + doit( IRP_CONTEXT, Irp ); + doit( IRP_CONTEXT, Vcb ); + doit( IRP_CONTEXT, ExceptionStatus ); + doit( IRP_CONTEXT, Flags ); + doit( IRP_CONTEXT, RealDevice ); + doit( IRP_CONTEXT, IoContext ); + doit( IRP_CONTEXT, TeardownFcb ); + doit( IRP_CONTEXT, TopLevel ); + doit( IRP_CONTEXT, MajorFunction ); + doit( IRP_CONTEXT, MinorFunction ); + doit( IRP_CONTEXT, ThreadContext ); + doit( IRP_CONTEXT, WorkQueueItem ); + } + printf("\n"); + { + IRP_CONTEXT_LITE d; + doit( IRP_CONTEXT_LITE, NodeTypeCode ); + doit( IRP_CONTEXT_LITE, NodeByteSize ); + doit( IRP_CONTEXT_LITE, Fcb ); + doit( IRP_CONTEXT_LITE, DelayedCloseLinks ); + doit( IRP_CONTEXT_LITE, UserReference ); + doit( IRP_CONTEXT_LITE, RealDevice ); + } + printf("\n"); + { + CD_IO_CONTEXT d; + doit( CD_IO_CONTEXT, IrpCount ); + doit( CD_IO_CONTEXT, MasterIrp ); + doit( CD_IO_CONTEXT, Status ); + doit( CD_IO_CONTEXT, AllocatedContext ); + doit( CD_IO_CONTEXT, Resource ); + doit( CD_IO_CONTEXT, ResourceThreadId ); + doit( CD_IO_CONTEXT, SyncEvent ); + } + printf("\n"); + { + THREAD_CONTEXT d; + doit( THREAD_CONTEXT, Cdfs ); + doit( THREAD_CONTEXT, SavedTopLevelIrp ); + doit( THREAD_CONTEXT, TopLevelIrpContext ); + } + printf("\n"); + { + PATH_ENUM_CONTEXT d; + doit( PATH_ENUM_CONTEXT, Data ); + doit( PATH_ENUM_CONTEXT, BaseOffset ); + doit( PATH_ENUM_CONTEXT, DataLength ); + doit( PATH_ENUM_CONTEXT, Bcb ); + doit( PATH_ENUM_CONTEXT, DataOffset ); + doit( PATH_ENUM_CONTEXT, AllocatedData ); + doit( PATH_ENUM_CONTEXT, LastDataBlock ); + } + printf("\n"); + { + PATH_ENTRY d; + doit( PATH_ENTRY, Ordinal ); + doit( PATH_ENTRY, PathTableOffset ); + doit( PATH_ENTRY, DiskOffset ); + doit( PATH_ENTRY, PathEntryLength ); + doit( PATH_ENTRY, ParentOrdinal ); + doit( PATH_ENTRY, DirNameLen ); + doit( PATH_ENTRY, DirName ); + doit( PATH_ENTRY, Flags ); + doit( PATH_ENTRY, CdDirName ); + doit( PATH_ENTRY, CdCaseDirName ); + doit( PATH_ENTRY, NameBuffer ); + } + printf("\n"); + { + COMPOUND_PATH_ENTRY d; + doit( COMPOUND_PATH_ENTRY, PathContext ); + doit( COMPOUND_PATH_ENTRY, PathEntry ); + } + printf("\n"); + { + DIRENT_ENUM_CONTEXT d; + doit( DIRENT_ENUM_CONTEXT, Sector ); + doit( DIRENT_ENUM_CONTEXT, BaseOffset ); + doit( DIRENT_ENUM_CONTEXT, DataLength ); + doit( DIRENT_ENUM_CONTEXT, Bcb ); + doit( DIRENT_ENUM_CONTEXT, SectorOffset ); + doit( DIRENT_ENUM_CONTEXT, NextDirentOffset ); + } + printf("\n"); + { + DIRENT d; + doit( DIRENT, DirentOffset ); + doit( DIRENT, DirentLength ); + doit( DIRENT, StartingOffset ); + doit( DIRENT, DataLength ); + doit( DIRENT, CdTime ); + doit( DIRENT, DirentFlags ); + doit( DIRENT, Flags ); + doit( DIRENT, FileUnitSize ); + doit( DIRENT, InterleaveGapSize ); + doit( DIRENT, SystemUseOffset ); + doit( DIRENT, XAAttributes ); + doit( DIRENT, XAFileNumber ); + doit( DIRENT, FileNameLen ); + doit( DIRENT, FileName ); + doit( DIRENT, CdFileName ); + doit( DIRENT, CdCaseFileName ); + doit( DIRENT, ExtentType ); + doit( DIRENT, NameBuffer ); + } + printf("\n"); + { + COMPOUND_DIRENT d; + doit( COMPOUND_DIRENT, DirContext ); + doit( COMPOUND_DIRENT, Dirent ); + } + printf("\n"); + { + FILE_ENUM_CONTEXT d; + doit( FILE_ENUM_CONTEXT, PriorDirent ); + doit( FILE_ENUM_CONTEXT, InitialDirent ); + doit( FILE_ENUM_CONTEXT, CurrentDirent ); + doit( FILE_ENUM_CONTEXT, Flags ); + doit( FILE_ENUM_CONTEXT, FileSize ); + doit( FILE_ENUM_CONTEXT, ShortName ); + doit( FILE_ENUM_CONTEXT, ShortNameBuffer ); + doit( FILE_ENUM_CONTEXT, Dirents ); + } + printf("\n"); + { + RIFF_HEADER d; + doit( RIFF_HEADER, ChunkId ); + doit( RIFF_HEADER, ChunkSize ); + doit( RIFF_HEADER, SignatureCDXA ); + doit( RIFF_HEADER, SignatureFMT ); + doit( RIFF_HEADER, XAChunkSize ); + doit( RIFF_HEADER, OwnerId ); + doit( RIFF_HEADER, Attributes ); + doit( RIFF_HEADER, SignatureXA ); + doit( RIFF_HEADER, FileNumber ); + doit( RIFF_HEADER, Reserved ); + doit( RIFF_HEADER, SignatureData ); + doit( RIFF_HEADER, RawSectors ); + } + printf("\n"); + { + AUDIO_PLAY_HEADER d; + doit( AUDIO_PLAY_HEADER, Chunk ); + doit( AUDIO_PLAY_HEADER, ChunkSize ); + doit( AUDIO_PLAY_HEADER, SignatureCDDA ); + doit( AUDIO_PLAY_HEADER, SignatureFMT ); + doit( AUDIO_PLAY_HEADER, FMTChunkSize ); + doit( AUDIO_PLAY_HEADER, FormatTag ); + doit( AUDIO_PLAY_HEADER, TrackNumber ); + doit( AUDIO_PLAY_HEADER, DiskID ); + doit( AUDIO_PLAY_HEADER, StartingSector ); + doit( AUDIO_PLAY_HEADER, SectorCount ); + doit( AUDIO_PLAY_HEADER, TrackAddress ); + doit( AUDIO_PLAY_HEADER, TrackLength ); + } + printf("\n"); + { + RAW_ISO_VD d; + doit( RAW_ISO_VD, DescType ); + doit( RAW_ISO_VD, StandardId ); + doit( RAW_ISO_VD, Version ); + doit( RAW_ISO_VD, VolumeFlags ); + doit( RAW_ISO_VD, SystemId ); + doit( RAW_ISO_VD, VolumeId ); + doit( RAW_ISO_VD, Reserved ); + doit( RAW_ISO_VD, VolSpaceI ); + doit( RAW_ISO_VD, VolSpaceM ); + doit( RAW_ISO_VD, CharSet ); + doit( RAW_ISO_VD, VolSetSizeI ); + doit( RAW_ISO_VD, VolSetSizeM ); + doit( RAW_ISO_VD, VolSeqNumI ); + doit( RAW_ISO_VD, VolSeqNumM ); + doit( RAW_ISO_VD, LogicalBlkSzI ); + doit( RAW_ISO_VD, LogicalBlkSzM ); + doit( RAW_ISO_VD, PathTableSzI ); + doit( RAW_ISO_VD, PathTableSzM ); + doit( RAW_ISO_VD, PathTabLocI ); + doit( RAW_ISO_VD, PathTabLocM ); + doit( RAW_ISO_VD, RootDe ); + doit( RAW_ISO_VD, VolSetId ); + doit( RAW_ISO_VD, PublId ); + doit( RAW_ISO_VD, PreparerId ); + doit( RAW_ISO_VD, AppId ); + doit( RAW_ISO_VD, Copyright ); + doit( RAW_ISO_VD, Abstract ); + doit( RAW_ISO_VD, Bibliograph ); + doit( RAW_ISO_VD, CreateDate ); + doit( RAW_ISO_VD, ModDate ); + doit( RAW_ISO_VD, ExpireDate ); + doit( RAW_ISO_VD, EffectDate ); + doit( RAW_ISO_VD, FileStructVer ); + doit( RAW_ISO_VD, Reserved3 ); + doit( RAW_ISO_VD, ResApp ); + doit( RAW_ISO_VD, Reserved4 ); + } + printf("\n"); + { + RAW_HSG_VD d; + doit( RAW_HSG_VD, BlkNumI ); + doit( RAW_HSG_VD, BlkNumM ); + doit( RAW_HSG_VD, DescType ); + doit( RAW_HSG_VD, StandardId ); + doit( RAW_HSG_VD, Version ); + doit( RAW_HSG_VD, VolumeFlags ); + doit( RAW_HSG_VD, SystemId ); + doit( RAW_HSG_VD, VolumeId ); + doit( RAW_HSG_VD, Reserved ); + doit( RAW_HSG_VD, VolSpaceI ); + doit( RAW_HSG_VD, VolSpaceM ); + doit( RAW_HSG_VD, CharSet ); + doit( RAW_HSG_VD, VolSetSizeI ); + doit( RAW_HSG_VD, VolSetSizeM ); + doit( RAW_HSG_VD, VolSeqNumI ); + doit( RAW_HSG_VD, VolSeqNumM ); + doit( RAW_HSG_VD, LogicalBlkSzI ); + doit( RAW_HSG_VD, LogicalBlkSzM ); + doit( RAW_HSG_VD, PathTableSzI ); + doit( RAW_HSG_VD, PathTableSzM ); + doit( RAW_HSG_VD, PathTabLocI ); + doit( RAW_HSG_VD, PathTabLocM ); + doit( RAW_HSG_VD, RootDe ); + doit( RAW_HSG_VD, VolSetId ); + doit( RAW_HSG_VD, PublId ); + doit( RAW_HSG_VD, PreparerId ); + doit( RAW_HSG_VD, AppId ); + doit( RAW_HSG_VD, Copyright ); + doit( RAW_HSG_VD, Abstract ); + doit( RAW_HSG_VD, CreateDate ); + doit( RAW_HSG_VD, ModDate ); + doit( RAW_HSG_VD, ExpireDate ); + doit( RAW_HSG_VD, EffectDate ); + doit( RAW_HSG_VD, FileStructVer ); + doit( RAW_HSG_VD, Reserved3 ); + doit( RAW_HSG_VD, ResApp ); + doit( RAW_HSG_VD, Reserved4 ); + } + printf("\n"); + { + RAW_DIRENT d; + doit( RAW_DIRENT, DirLen ); + doit( RAW_DIRENT, XarLen ); + doit( RAW_DIRENT, FileLoc ); + doit( RAW_DIRENT, FileLocMot ); + doit( RAW_DIRENT, DataLen ); + doit( RAW_DIRENT, DataLenMot ); + doit( RAW_DIRENT, RecordTime ); + doit( RAW_DIRENT, FlagsHSG ); + doit( RAW_DIRENT, FlagsISO ); + doit( RAW_DIRENT, IntLeaveSize ); + doit( RAW_DIRENT, IntLeaveSkip ); + doit( RAW_DIRENT, Vssn ); + doit( RAW_DIRENT, VssnMot ); + doit( RAW_DIRENT, FileIdLen ); + doit( RAW_DIRENT, FileId ); + } + printf("\n"); + { + RAW_PATH_ISO d; + doit( RAW_PATH_ISO, DirIdLen ); + doit( RAW_PATH_ISO, XarLen ); + doit( RAW_PATH_ISO, DirLoc ); + doit( RAW_PATH_ISO, ParentNum ); + doit( RAW_PATH_ISO, DirId ); + } + printf("\n"); + { + RAW_PATH_HSG d; + doit( RAW_PATH_HSG, DirLoc ); + doit( RAW_PATH_HSG, XarLen ); + doit( RAW_PATH_HSG, DirIdLen ); + doit( RAW_PATH_HSG, ParentNum ); + doit( RAW_PATH_HSG, DirId ); + } + printf("\n"); + { + SYSTEM_USE_XA d; + doit( SYSTEM_USE_XA, OwnerId ); + doit( SYSTEM_USE_XA, Attributes ); + doit( SYSTEM_USE_XA, Signature ); + doit( SYSTEM_USE_XA, FileNumber ); + doit( SYSTEM_USE_XA, Reserved ); + } +} diff --git a/filesys/cdfs/fileinfo.c b/filesys/cdfs/fileinfo.c new file mode 100644 index 00000000..cd9f1af3 --- /dev/null +++ b/filesys/cdfs/fileinfo.c @@ -0,0 +1,1528 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FileInfo.c + +Abstract: + + This module implements the File Information routines for Cdfs called by + the Fsd/Fsp dispatch drivers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_FILEINFO) + +// +// Local support routines +// + +VOID +CdQueryBasicInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Out_ PFILE_BASIC_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +VOID +CdQueryStandardInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Out_ PFILE_STANDARD_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +VOID +CdQueryInternalInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Out_ PFILE_INTERNAL_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +VOID +CdQueryEaInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Out_ PFILE_EA_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +VOID +CdQueryPositionInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFILE_OBJECT FileObject, + _Out_ PFILE_POSITION_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +NTSTATUS +CdQueryNameInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFILE_OBJECT FileObject, + _Out_ PFILE_NAME_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdQueryAlternateNameInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PCCB Ccb, + _Out_ PFILE_NAME_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +VOID +CdQueryNetworkInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Out_ PFILE_NETWORK_OPEN_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonQueryInfo) +#pragma alloc_text(PAGE, CdCommonSetInfo) +#pragma alloc_text(PAGE, CdFastQueryBasicInfo) +#pragma alloc_text(PAGE, CdFastQueryStdInfo) +#pragma alloc_text(PAGE, CdFastQueryNetworkInfo) +#pragma alloc_text(PAGE, CdQueryAlternateNameInfo) +#pragma alloc_text(PAGE, CdQueryBasicInfo) +#pragma alloc_text(PAGE, CdQueryEaInfo) +#pragma alloc_text(PAGE, CdQueryInternalInfo) +#pragma alloc_text(PAGE, CdQueryNameInfo) +#pragma alloc_text(PAGE, CdQueryNetworkInfo) +#pragma alloc_text(PAGE, CdQueryPositionInfo) +#pragma alloc_text(PAGE, CdQueryStandardInfo) +#endif + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonQueryInfo ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for query file information called by both the + fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp to process. + +Return Value: + + NTSTATUS - The return status for this operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + ULONG Length; + FILE_INFORMATION_CLASS FileInformationClass; + PFILE_ALL_INFORMATION Buffer; + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN ReleaseFcb = FALSE; + + PAGED_CODE(); + + // + // Reference our input parameters to make things easier + // + + Length = IrpSp->Parameters.QueryFile.Length; + FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Decode the file object + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // We only support query on file and directory handles. + // + + switch (TypeOfOpen) { + + case UserDirectoryOpen : + case UserFileOpen : + + // + // Acquire shared access to this file. NOTE that this could be + // a recursive acquire, if we already preacquired in + // CdAcquireForCreateSection(). + // + + CdAcquireFileShared( IrpContext, Fcb ); + ReleaseFcb = TRUE; + + // + // Make sure we have the correct sizes for a directory. + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED )) { + + NT_ASSERT( TypeOfOpen == UserDirectoryOpen ); + CdVerifyOrCreateDirStreamFile( IrpContext, Fcb); + } + + // + // Make sure the Fcb is in a usable condition. This will raise + // an error condition if the volume is unusable + // + + CdVerifyFcbOperation( IrpContext, Fcb ); + + // + // Based on the information class we'll do different + // actions. Each of hte procedures that we're calling fills + // up the output buffer, if possible. They will raise the + // status STATUS_BUFFER_OVERFLOW for an insufficient buffer. + // This is considered a somewhat unusual case and is handled + // more cleanly with the exception mechanism rather than + // testing a return status value for each call. + // + + switch (FileInformationClass) { + + case FileAllInformation: + + // + // We don't allow this operation on a file opened by file Id. + // + + if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_ID )) { + + Status = STATUS_INVALID_PARAMETER; + break; + } + + // + // In this case go ahead and call the individual routines to + // fill in the buffer. Only the name routine will + // pointer to the output buffer and then call the + // individual routines to fill in the buffer. + // + + Length -= (sizeof( FILE_ACCESS_INFORMATION ) + + sizeof( FILE_MODE_INFORMATION ) + + sizeof( FILE_ALIGNMENT_INFORMATION )); + + CdQueryBasicInfo( IrpContext, Fcb, &Buffer->BasicInformation, &Length ); + CdQueryStandardInfo( IrpContext, Fcb, &Buffer->StandardInformation, &Length ); + CdQueryInternalInfo( IrpContext, Fcb, &Buffer->InternalInformation, &Length ); + CdQueryEaInfo( IrpContext, Fcb, &Buffer->EaInformation, &Length ); + CdQueryPositionInfo( IrpContext, IrpSp->FileObject, &Buffer->PositionInformation, &Length ); + Status = CdQueryNameInfo( IrpContext, IrpSp->FileObject, &Buffer->NameInformation, &Length ); + + break; + + case FileBasicInformation: + + CdQueryBasicInfo( IrpContext, Fcb, (PFILE_BASIC_INFORMATION) Buffer, &Length ); + break; + + case FileStandardInformation: + + CdQueryStandardInfo( IrpContext, Fcb, (PFILE_STANDARD_INFORMATION) Buffer, &Length ); + break; + + case FileInternalInformation: + + CdQueryInternalInfo( IrpContext, Fcb, (PFILE_INTERNAL_INFORMATION) Buffer, &Length ); + break; + + case FileEaInformation: + + CdQueryEaInfo( IrpContext, Fcb, (PFILE_EA_INFORMATION) Buffer, &Length ); + break; + + case FilePositionInformation: + + CdQueryPositionInfo( IrpContext, IrpSp->FileObject, (PFILE_POSITION_INFORMATION) Buffer, &Length ); + break; + + case FileNameInformation: + + // + // We don't allow this operation on a file opened by file Id. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_ID )) { + + Status = CdQueryNameInfo( IrpContext, IrpSp->FileObject, (PFILE_NAME_INFORMATION) Buffer, &Length ); + + } else { + + Status = STATUS_INVALID_PARAMETER; + } + + break; + + case FileAlternateNameInformation: + + if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_ID )) { + + Status = CdQueryAlternateNameInfo( IrpContext, Fcb, Ccb, (PFILE_NAME_INFORMATION) Buffer, &Length ); + + } else { + + Status = STATUS_INVALID_PARAMETER; + } + + break; + + case FileNetworkOpenInformation: + + CdQueryNetworkInfo( IrpContext, Fcb, (PFILE_NETWORK_OPEN_INFORMATION) Buffer, &Length ); + break; + + default : + + Status = STATUS_INVALID_PARAMETER; + } + + break; + + default : + + Status = STATUS_INVALID_PARAMETER; + } + + // + // Set the information field to the number of bytes actually filled in + // and then complete the request + // + + Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length; + + } finally { + + // + // Release the file. + // + + if (ReleaseFcb) { + + CdReleaseFile( IrpContext, Fcb ); + } + } + + // + // Complete the request if we didn't raise. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonSetInfo ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for set file information called by both the + fsd and fsp threads. We only support operations which set the file position. + +Arguments: + + Irp - Supplies the Irp to process. + +Return Value: + + NTSTATUS - The return status for this operation. + +--*/ + +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PFILE_POSITION_INFORMATION Buffer; + + PAGED_CODE(); + + // + // Decode the file object + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + // + // We only support a SetPositionInformation on a user file. + // + + if ((TypeOfOpen != UserFileOpen) || + (IrpSp->Parameters.QueryFile.FileInformationClass != FilePositionInformation)) { + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // Acquire shared access to this file. + // + + CdAcquireFileShared( IrpContext, Fcb ); + + try { + + // + // Make sure the Fcb is in a usable condition. This + // will raise an error condition if the fcb is unusable + // + + CdVerifyFcbOperation( IrpContext, Fcb ); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Check if the file does not use intermediate buffering. If it + // does not use intermediate buffering then the new position we're + // supplied must be aligned properly for the device + // + + if (FlagOn( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) && + ((Buffer->CurrentByteOffset.LowPart & Fcb->Vcb->BlockMask) != 0)) { + + try_return( NOTHING ); + } + + // + // The input parameter is fine so set the current byte offset and + // complete the request + // + + // + // Lock the Fcb to provide synchronization. + // + + CdLockFcb( IrpContext, Fcb ); + IrpSp->FileObject->CurrentByteOffset = Buffer->CurrentByteOffset; + CdUnlockFcb( IrpContext, Fcb ); + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + } finally { + + CdReleaseFile( IrpContext, Fcb ); + } + + // + // Complete the request if there was no raise. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +_Function_class_(FAST_IO_QUERY_BASIC_INFO) +_IRQL_requires_same_ +_Success_(return != FALSE) +BOOLEAN +CdFastQueryBasicInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_BASIC_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine is for the fast query call for basic file information. + +Arguments: + + FileObject - Supplies the file object used in this operation + + Wait - Indicates if we are allowed to wait for the information + + Buffer - Supplies the output buffer to receive the basic information + + IoStatus - Receives the final status of the operation + +Return Value: + + BOOLEAN - TRUE if the operation succeeded and FALSE if the caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Result = FALSE; + TYPE_OF_OPEN TypeOfOpen; + + PFCB Fcb; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + + ASSERT_FILE_OBJECT( FileObject ); + + FsRtlEnterFileSystem(); + + // + // Decode the file object to find the type of open and the data + // structures. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + // + // We only support this request on user file or directory objects. + // + + if ((TypeOfOpen != UserFileOpen) && + ((TypeOfOpen != UserDirectoryOpen) || !FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED))) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Acquire the file shared to access the Fcb. + // + + if (!ExAcquireResourceSharedLite( Fcb->Resource, Wait )) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Only deal with 'good' Fcb's. + // + + if (CdVerifyFcbOperation( NULL, Fcb )) { + + // + // Fill in the input buffer from the Fcb fields. + // + + Buffer->CreationTime.QuadPart = + Buffer->LastWriteTime.QuadPart = + Buffer->ChangeTime.QuadPart = Fcb->CreationTime; + + Buffer->LastAccessTime.QuadPart = 0; + + Buffer->FileAttributes = Fcb->FileAttributes; + + // + // Update the IoStatus block with the size of this data. + // + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof( FILE_BASIC_INFORMATION ); + + Result = TRUE; + } + + } finally { + + ExReleaseResourceLite( Fcb->Resource ); + + FsRtlExitFileSystem(); + } + + return Result; +} + + +_Function_class_(FAST_IO_QUERY_STANDARD_INFO) +_IRQL_requires_same_ +_Success_(return != FALSE) +BOOLEAN +CdFastQueryStdInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_STANDARD_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine is for the fast query call for standard file information. + +Arguments: + + FileObject - Supplies the file object used in this operation + + Wait - Indicates if we are allowed to wait for the information + + Buffer - Supplies the output buffer to receive the basic information + + IoStatus - Receives the final status of the operation + +Return Value: + + BOOLEAN - TRUE if the operation succeeded and FALSE if the caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Result = FALSE; + TYPE_OF_OPEN TypeOfOpen; + + PFCB Fcb; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + + ASSERT_FILE_OBJECT( FileObject ); + + FsRtlEnterFileSystem(); + + // + // Decode the file object to find the type of open and the data + // structures. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + // + // We only support this request on initialized user file or directory objects. + // + + if ((TypeOfOpen != UserFileOpen) && + ((TypeOfOpen != UserDirectoryOpen) || !FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED ))) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Acquire the file shared to access the Fcb. + // + + if (!ExAcquireResourceSharedLite( Fcb->Resource, Wait )) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Only deal with 'good' Fcb's. + // + + if (CdVerifyFcbOperation( NULL, Fcb )) { + + // + // Check whether this is a directory. + // + + if (FlagOn( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + Buffer->AllocationSize.QuadPart = + Buffer->EndOfFile.QuadPart = 0; + + Buffer->Directory = TRUE; + + } else { + + Buffer->AllocationSize.QuadPart = Fcb->AllocationSize.QuadPart; + Buffer->EndOfFile.QuadPart = Fcb->FileSize.QuadPart; + + Buffer->Directory = FALSE; + } + + Buffer->NumberOfLinks = 1; + Buffer->DeletePending = FALSE; + + // + // Update the IoStatus block with the size of this data. + // + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof( FILE_STANDARD_INFORMATION ); + + Result = TRUE; + } + + } finally { + + ExReleaseResourceLite( Fcb->Resource ); + + FsRtlExitFileSystem(); + } + + return Result; +} + + +_Function_class_(FAST_IO_QUERY_NETWORK_OPEN_INFO) +_IRQL_requires_same_ +_Success_(return != FALSE) +BOOLEAN +CdFastQueryNetworkInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_NETWORK_OPEN_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine is for the fast query call for network file information. + +Arguments: + + FileObject - Supplies the file object used in this operation + + Wait - Indicates if we are allowed to wait for the information + + Buffer - Supplies the output buffer to receive the basic information + + IoStatus - Receives the final status of the operation + +Return Value: + + BOOLEAN - TRUE if the operation succeeded and FALSE if the caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Result = FALSE; + TYPE_OF_OPEN TypeOfOpen; + + PFCB Fcb; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + + ASSERT_FILE_OBJECT( FileObject ); + + FsRtlEnterFileSystem(); + + // + // Decode the file object to find the type of open and the data + // structures. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + // + // We only support this request on user file or directory objects. + // + + if ((TypeOfOpen != UserFileOpen) && + ((TypeOfOpen != UserDirectoryOpen) || !FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED))) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Acquire the file shared to access the Fcb. + // + + if (!ExAcquireResourceSharedLite( Fcb->Resource, Wait )) { + + FsRtlExitFileSystem(); + return FALSE; + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Only deal with 'good' Fcb's. + // + + if (CdVerifyFcbOperation( NULL, Fcb )) { + + // + // Fill in the input buffer from the Fcb fields. + // + + Buffer->CreationTime.QuadPart = + Buffer->LastWriteTime.QuadPart = + Buffer->ChangeTime.QuadPart = Fcb->CreationTime; + + Buffer->LastAccessTime.QuadPart = 0; + + Buffer->FileAttributes = Fcb->FileAttributes; + + // + // Check whether this is a directory. + // + + if (FlagOn( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + Buffer->AllocationSize.QuadPart = + Buffer->EndOfFile.QuadPart = 0; + + } else { + + Buffer->AllocationSize.QuadPart = Fcb->AllocationSize.QuadPart; + Buffer->EndOfFile.QuadPart = Fcb->FileSize.QuadPart; + } + + // + // Update the IoStatus block with the size of this data. + // + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof( FILE_NETWORK_OPEN_INFORMATION ); + + Result = TRUE; + } + + } finally { + + ExReleaseResourceLite( Fcb->Resource ); + + FsRtlExitFileSystem(); + } + + return Result; +} + + +// +// Local support routine +// + +VOID +CdQueryBasicInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Out_ PFILE_BASIC_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + + Description: + + This routine performs the query basic information function for Cdfs + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // We only support creation, last modify and last write times on Cdfs. + // + + Buffer->LastWriteTime.QuadPart = + Buffer->CreationTime.QuadPart = + Buffer->ChangeTime.QuadPart = Fcb->CreationTime; + + Buffer->LastAccessTime.QuadPart = 0; + + Buffer->FileAttributes = Fcb->FileAttributes; + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_BASIC_INFORMATION ); + + return; +} + + +// +// Local support routine +// + +VOID +CdQueryStandardInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Out_ PFILE_STANDARD_INFORMATION Buffer, + _Inout_ PULONG Length + ) +/*++ + +Routine Description: + + This routine performs the query standard information function for cdfs. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // There is only one link and delete is never pending on a Cdrom file. + // + + Buffer->NumberOfLinks = 1; + Buffer->DeletePending = FALSE; + + // + // We get the sizes from the header. Return a size of zero + // for all directories. + // + + if (FlagOn( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + Buffer->AllocationSize.QuadPart = + Buffer->EndOfFile.QuadPart = 0; + + Buffer->Directory = TRUE; + + } else { + + Buffer->AllocationSize.QuadPart = Fcb->AllocationSize.QuadPart; + Buffer->EndOfFile.QuadPart = Fcb->FileSize.QuadPart; + + Buffer->Directory = FALSE; + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_STANDARD_INFORMATION ); + + return; +} + + +// +// Local support routine +// + +VOID +CdQueryInternalInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Out_ PFILE_INTERNAL_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query internal information function for cdfs. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Index number is the file Id number in the Fcb. + // + + Buffer->IndexNumber = Fcb->FileId; + *Length -= sizeof( FILE_INTERNAL_INFORMATION ); + + return; +} + + +// +// Local support routine +// + +VOID +CdQueryEaInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Out_ PFILE_EA_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query Ea information function for cdfs. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + UNREFERENCED_PARAMETER( Fcb ); + + // + // No Ea's on Cdfs volumes. + // + + Buffer->EaSize = 0; + *Length -= sizeof( FILE_EA_INFORMATION ); + + return; +} + + +// +// Local support routine +// + +VOID +CdQueryPositionInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFILE_OBJECT FileObject, + _Out_ PFILE_POSITION_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query position information function for cdfs. + +Arguments: + + FileObject - Supplies the File object being queried + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Get the current position found in the file object. + // + + Buffer->CurrentByteOffset = FileObject->CurrentByteOffset; + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_POSITION_INFORMATION ); + + return; +} + + +// +// Local support routine +// + +NTSTATUS +CdQueryNameInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFILE_OBJECT FileObject, + _Out_ PFILE_NAME_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query name information function for cdfs. + +Arguments: + + FileObject - Supplies the file object containing the name. + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + NTSTATUS - STATUS_BUFFER_OVERFLOW if the entire name can't be copied. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG LengthToCopy; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + NT_ASSERT(*Length >= sizeof(ULONG)); + + // + // Simply copy the name in the file object to the user's buffer. + // + + // + // Place the size of the filename in the user's buffer and reduce the remaining + // size to match. + // + + Buffer->FileNameLength = LengthToCopy = FileObject->FileName.Length; + *Length -= sizeof(ULONG); + + if (LengthToCopy > *Length) { + + LengthToCopy = *Length; + Status = STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory( Buffer->FileName, FileObject->FileName.Buffer, LengthToCopy ); + + // + // Reduce the available bytes by the amount stored into this buffer. In the overflow + // case, this simply drops to zero. The returned filenamelength will indicate to the + // caller how much space is required. + // + + *Length -= LengthToCopy; + + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdQueryAlternateNameInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ PCCB Ccb, + _Out_ PFILE_NAME_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query alternate name information function. + We lookup the dirent for this file and then check if there is a + short name. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified. + + Ccb - Ccb for this open handle. + + Buffer - Supplies a pointer to the buffer where the information is to + be returned. + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + NTSTATUS - STATUS_SUCCESS if the whole name would fit into the user buffer, + STATUS_OBJECT_NAME_NOT_FOUND if we can't return the name, + STATUS_BUFFER_OVERFLOW otherwise. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + DIRENT_ENUM_CONTEXT DirContext = {0}; + DIRENT Dirent = {0}; + + PUNICODE_STRING NameToUse; + ULONG DirentOffset; + + COMPOUND_PATH_ENTRY CompoundPathEntry = {0}; + FILE_ENUM_CONTEXT FileContext; + + PFCB ParentFcb = NULL; + BOOLEAN ReleaseParentFcb = FALSE; + + BOOLEAN CleanupFileLookup = FALSE; + BOOLEAN CleanupDirectoryLookup = FALSE; + + WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / 2 ]; + USHORT ShortNameLength; + + PAGED_CODE(); + + // + // Initialize the buffer length to zero. + // + + Buffer->FileNameLength = 0; + + // + // If this is the root or this file was opened using a version number then + // there is no short name. + // + + if ((Fcb == Fcb->Vcb->RootIndexFcb) || + FlagOn( Ccb->Flags, CCB_FLAG_OPEN_WITH_VERSION)) { + + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + // + // Use a try-finally to cleanup the structures. + // + + try { + + ParentFcb = Fcb->ParentFcb; + CdAcquireFileShared( IrpContext, ParentFcb ); + ReleaseParentFcb = TRUE; + + CdVerifyOrCreateDirStreamFile( IrpContext, ParentFcb); + + if (CdFidIsDirectory( Fcb->FileId)) { + + // + // Fcb is for a directory, so we need to dig the dirent from the parent. In + // order to do this we need to get the name of the directory from its pathtable + // entry and then search in the parent for a matching dirent. + // + // This could be optimized somewhat. + // + + CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CdInitializeFileContext( IrpContext, &FileContext ); + + CleanupDirectoryLookup = TRUE; + + CdLookupPathEntry( IrpContext, + CdQueryFidPathTableOffset( Fcb->FileId ), + Fcb->Ordinal, + FALSE, + &CompoundPathEntry ); + + CdUpdatePathEntryName( IrpContext, &CompoundPathEntry.PathEntry, TRUE ); + + if (!CdFindDirectory( IrpContext, + ParentFcb, + &CompoundPathEntry.PathEntry.CdCaseDirName, + TRUE, + &FileContext )) { + + // + // If we failed to find the child directory by name in the parent + // something is quite wrong with this disc. + // + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + NameToUse = &FileContext.InitialDirent->Dirent.CdCaseFileName.FileName; + DirentOffset = FileContext.InitialDirent->Dirent.DirentOffset; + + } else { + + // + // Initialize the search dirent structures. + // + + CdInitializeDirContext( IrpContext, &DirContext ); + CdInitializeDirent( IrpContext, &Dirent ); + + CleanupFileLookup = TRUE; + + CdLookupDirent( IrpContext, + ParentFcb, + CdQueryFidDirentOffset( Fcb->FileId ), + &DirContext ); + + CdUpdateDirentFromRawDirent( IrpContext, + ParentFcb, + &DirContext, + &Dirent ); + + // + // Now update the dirent name. + // + + CdUpdateDirentName( IrpContext, &Dirent, TRUE ); + + NameToUse = &Dirent.CdCaseFileName.FileName; + DirentOffset = Dirent.DirentOffset; + } + + // + // If the name is 8.3 then fail this request. + // + + if (CdIs8dot3Name( IrpContext, + *NameToUse )) { + + + try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND ); + } + + CdGenerate8dot3Name( IrpContext, + NameToUse, + DirentOffset, + ShortNameBuffer, + &ShortNameLength ); + + // + // We now have the short name. We have left it in Unicode form so copy it directly. + // + + Buffer->FileNameLength = ShortNameLength; + + if (Buffer->FileNameLength + sizeof( ULONG ) > *Length) { + + Buffer->FileNameLength = *Length - sizeof( ULONG ); + Status = STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory( Buffer->FileName, ShortNameBuffer, Buffer->FileNameLength ); + + try_exit: NOTHING; + } finally { + + if (CleanupFileLookup) { + + CdCleanupDirContext( IrpContext, &DirContext ); + CdCleanupDirent( IrpContext, &Dirent ); + + } else if (CleanupDirectoryLookup) { + + CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry ); + CdCleanupFileContext( IrpContext, &FileContext ); + } + + if (ReleaseParentFcb) { + + CdReleaseFile( IrpContext, ParentFcb ); + } + } + + // + // Reduce the available bytes by the amount stored into this buffer. + // + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + + *Length -= sizeof( ULONG ) + Buffer->FileNameLength; + } + + return Status; +} + + +// +// Local support routine +// + +VOID +CdQueryNetworkInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _Out_ PFILE_NETWORK_OPEN_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + + Description: + + This routine performs the query network open information function for Cdfs + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // We only support creation, last modify and last write times on Cdfs. + // + + Buffer->LastWriteTime.QuadPart = + Buffer->CreationTime.QuadPart = + Buffer->ChangeTime.QuadPart = Fcb->CreationTime; + + Buffer->LastAccessTime.QuadPart = 0; + + Buffer->FileAttributes = Fcb->FileAttributes; + + // + // We get the sizes from the header. Return a size of zero + // for all directories. + // + + if (FlagOn( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) { + + Buffer->AllocationSize.QuadPart = + Buffer->EndOfFile.QuadPart = 0; + + } else { + + Buffer->AllocationSize.QuadPart = Fcb->AllocationSize.QuadPart; + Buffer->EndOfFile.QuadPart = Fcb->FileSize.QuadPart; + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION ); + + return; +} + diff --git a/filesys/cdfs/filobsup.c b/filesys/cdfs/filobsup.c new file mode 100644 index 00000000..1354bc11 --- /dev/null +++ b/filesys/cdfs/filobsup.c @@ -0,0 +1,245 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FilObSup.c + +Abstract: + + This module implements the Cdfs File object support routines. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_FILOBSUP) + +// +// Local constants. +// + +#define TYPE_OF_OPEN_MASK (0x00000007) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdDecodeFileObject) +#pragma alloc_text(PAGE, CdFastDecodeFileObject) +#pragma alloc_text(PAGE, CdSetFileObject) +#endif + + +_When_(TypeOfOpen == UnopenedFileObject, _At_(Fcb, _In_opt_)) +_When_(TypeOfOpen != UnopenedFileObject, _At_(Fcb, _In_)) +VOID +CdSetFileObject ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _In_ TYPE_OF_OPEN TypeOfOpen, + PFCB Fcb, + _In_opt_ PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine will initialize the FileObject context fields based on the + input type and data structures. + +Arguments: + + FileObject - Supplies the file object pointer being initialized. + + TypeOfOpen - Sets the type of open. + + Fcb - Fcb for this file object. Ignored for UnopenedFileObject. + + Ccb - Ccb for the handle corresponding to this file object. Will not + be present for stream file objects. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // We only have values 0 to 7 available so make sure we didn't + // inadvertantly add a new type. + // + + NT_ASSERTMSG( "FileObject types exceed available bits\n", BeyondValidType <= 8 ); + + // + // Setting a file object to type UnopenedFileObject means just + // clearing all of the context fields. All the other input + // + + if (TypeOfOpen == UnopenedFileObject) { + + FileObject->FsContext = + FileObject->FsContext2 = NULL; + + return; + } + + // + // Check that the 3 low-order bits of the Ccb are clear. + // + + NT_ASSERTMSG( "Ccb is not quad-aligned\n", !FlagOn( ((ULONG_PTR) Ccb), TYPE_OF_OPEN_MASK )); + + // + // We will or the type of open into the low order bits of FsContext2 + // along with the Ccb value. + // The Fcb is stored into the FsContext field. + // + + FileObject->FsContext = Fcb; + FileObject->FsContext2 = Ccb; + +#pragma warning( suppress: 4213 ) + SetFlag( ((ULONG_PTR) FileObject->FsContext2), TypeOfOpen ); + + // + // Set the Vpb field in the file object. + // + + FileObject->Vpb = Fcb->Vcb->Vpb; + + return; +} + + +_When_(return == UnopenedFileObject, _At_(*Fcb, _Post_null_)) +_When_(return != UnopenedFileObject, _At_(Fcb, _Outptr_)) +_When_(return == UnopenedFileObject, _At_(*Ccb, _Post_null_)) +_When_(return != UnopenedFileObject, _At_(Ccb, _Outptr_)) +TYPE_OF_OPEN +CdDecodeFileObject ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFILE_OBJECT FileObject, + PFCB *Fcb, + PCCB *Ccb + ) + +/*++ + +Routine Description: + + This routine takes a file object and extracts the Fcb and Ccb (possibly NULL) + and returns the type of open. + +Arguments: + + FileObject - Supplies the file object pointer being initialized. + + Fcb - Address to store the Fcb contained in the file object. + + Ccb - Address to store the Ccb contained in the file object. + +Return Value: + + TYPE_OF_OPEN - Indicates the type of file object. + +--*/ + +{ + TYPE_OF_OPEN TypeOfOpen; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // If this is an unopened file object then return NULL for the + // Fcb/Ccb. Don't trust any other values in the file object. + // + + TypeOfOpen = (TYPE_OF_OPEN) FlagOn( (ULONG_PTR) FileObject->FsContext2, + TYPE_OF_OPEN_MASK ); + + if (TypeOfOpen == UnopenedFileObject) { + + *Fcb = NULL; + *Ccb = NULL; + + } else { + + // + // The Fcb is pointed to by the FsContext field. The Ccb is in + // FsContext2 (after clearing the low three bits). The low three + // bits are the file object type. + // + + *Fcb = FileObject->FsContext; + *Ccb = FileObject->FsContext2; + +#pragma warning( suppress: 4213 ) + ClearFlag( (ULONG_PTR) *Ccb, TYPE_OF_OPEN_MASK ); + } + + // + // Now return the type of open. + // + + return TypeOfOpen; +} + + +TYPE_OF_OPEN +CdFastDecodeFileObject ( + _In_ PFILE_OBJECT FileObject, + _Out_ PFCB *Fcb + ) + +/*++ + +Routine Description: + + This procedure takes a pointer to a file object, that has already been + opened by Cdfs and does a quick decode operation. It will only return + a non null value if the file object is a user file open + +Arguments: + + FileObject - Supplies the file object pointer being interrogated + + Fcb - Address to store Fcb if this is a user file object. NULL + otherwise. + +Return Value: + + TYPE_OF_OPEN - type of open of this file object. + +--*/ + +{ + PAGED_CODE(); + + ASSERT_FILE_OBJECT( FileObject ); + + // + // The Fcb is in the FsContext field. The type of open is in the low + // bits of the Ccb. + // + + *Fcb = FileObject->FsContext; + + return (TYPE_OF_OPEN) + FlagOn( (ULONG_PTR) FileObject->FsContext2, TYPE_OF_OPEN_MASK ); +} + + + diff --git a/filesys/cdfs/fsctrl.c b/filesys/cdfs/fsctrl.c new file mode 100644 index 00000000..8ff03dbd --- /dev/null +++ b/filesys/cdfs/fsctrl.c @@ -0,0 +1,3440 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FsCtrl.c + +Abstract: + + This module implements the File System Control routines for Cdfs called + by the Fsd/Fsp dispatch drivers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_FSCTRL) + +// +// Local constants +// + +BOOLEAN CdDisable = FALSE; +BOOLEAN CdNoJoliet = FALSE; + +// +// Local support routines +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdUserFsctl ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +VOID +CdReMountOldVcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB OldVcb, + _Inout_ PVCB NewVcb, + _In_ PDEVICE_OBJECT DeviceObjectWeTalkTo + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdMountVolume ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdVerifyVolume ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdOplockRequest ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdLockVolume ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdUnlockVolume ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdDismountVolume ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +NTSTATUS +CdIsVolumeDirty ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +NTSTATUS +CdIsVolumeMounted ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +NTSTATUS +CdIsPathnameValid ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdInvalidateVolumes ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +NTSTATUS +CdAllowExtendedDasdIo ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdScanForDismountedVcb ( + _Inout_ PIRP_CONTEXT IrpContext + ); + +_Success_(return != FALSE) +BOOLEAN +CdFindPrimaryVd ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _Out_writes_bytes_(SECTOR_SIZE) PCHAR RawIsoVd, + _In_ ULONG BlockFactor, + _In_ BOOLEAN ReturnOnError, + _In_ BOOLEAN VerifyVolume + ); + +_Success_(return != FALSE) BOOLEAN +CdIsRemount ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_ PVCB *OldVcb + ); + +VOID +CdFindActiveVolDescriptor ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Inout_updates_bytes_(ROUND_TO_PAGES( SECTOR_SIZE )) PCHAR RawIsoVd, + _In_ BOOLEAN VerifyVolume + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonFsControl) +#pragma alloc_text(PAGE, CdDismountVolume) +#pragma alloc_text(PAGE, CdFindActiveVolDescriptor) +#pragma alloc_text(PAGE, CdFindPrimaryVd) +#pragma alloc_text(PAGE, CdIsPathnameValid) +#pragma alloc_text(PAGE, CdIsRemount) +#pragma alloc_text(PAGE, CdIsVolumeDirty) +#pragma alloc_text(PAGE, CdIsVolumeMounted) +#pragma alloc_text(PAGE, CdLockVolume) +#pragma alloc_text(PAGE, CdMountVolume) +#pragma alloc_text(PAGE, CdOplockRequest) +#pragma alloc_text(PAGE, CdAllowExtendedDasdIo) +#pragma alloc_text(PAGE, CdScanForDismountedVcb) +#pragma alloc_text(PAGE, CdUnlockVolume) +#pragma alloc_text(PAGE, CdUserFsctl) +#pragma alloc_text(PAGE, CdVerifyVolume) +#endif + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(Vcb->VcbResource) +NTSTATUS +CdLockVolumeInternal ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _In_opt_ PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + This routine performs the actual lock volume operation. It will be called + by anyone wishing to try to protect the volume for a long duration. PNP + operations are such a user. + + The volume must be held exclusive by the caller. + +Arguments: + + Vcb - The volume being locked. + + FileObject - File corresponding to the handle locking the volume. If this + is not specified, a system lock is assumed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KIRQL SavedIrql; + NTSTATUS FinalStatus = (FileObject? STATUS_ACCESS_DENIED: STATUS_DEVICE_BUSY); + ULONG RemainingUserReferences = (FileObject? 1: 0); + + // + // The cleanup count for the volume only reflects the fileobject that + // will lock the volume. Otherwise, we must fail the request. + // + // Since the only cleanup is for the provided fileobject, we will try + // to get rid of all of the other user references. If there is only one + // remaining after the purge then we can allow the volume to be locked. + // + + CdPurgeVolume( IrpContext, Vcb, FALSE ); + + // + // Now back out of our synchronization and wait for the lazy writer + // to finish off any lazy closes that could have been outstanding. + // + // Since we purged, we know that the lazy writer will issue all + // possible lazy closes in the next tick - if we hadn't, an otherwise + // unopened file with a large amount of dirty data could have hung + // around for a while as the data trickled out to the disk. + // + // This is even more important now since we send notification to + // alert other folks that this style of check is about to happen so + // that they can close their handles. We don't want to enter a fast + // race with the lazy writer tearing down his references to the file. + // + + CdReleaseVcb( IrpContext, Vcb ); + + Status = CcWaitForCurrentLazyWriterActivity(); + + // + // This is intentional. If we were able to get the Vcb before, just + // wait for it and take advantage of knowing that it is OK to leave + // the flag up. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + + CdFspClose( Vcb ); + + // + // If the volume is already explicitly locked then fail. We use the + // Vpb locked flag as an 'explicit lock' flag in the same way as Fat. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (!FlagOn( Vcb->Vpb->Flags, VPB_LOCKED ) && + (Vcb->VcbCleanup == RemainingUserReferences) && + (Vcb->VcbUserReference == CDFS_RESIDUAL_USER_REFERENCE + RemainingUserReferences)) { + + SetFlag( Vcb->VcbState, VCB_STATE_LOCKED ); + SetFlag( Vcb->Vpb->Flags, VPB_LOCKED); + Vcb->VolumeLockFileObject = FileObject; + FinalStatus = STATUS_SUCCESS; + } + + IoReleaseVpbSpinLock( SavedIrql ); + + return FinalStatus; +} + + +NTSTATUS +CdUnlockVolumeInternal ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _In_opt_ PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + This routine performs the actual unlock volume operation. + + The volume must be held exclusive by the caller. + +Arguments: + + Vcb - The volume being locked. + + FileObject - File corresponding to the handle locking the volume. If this + is not specified, a system lock is assumed. + +Return Value: + + NTSTATUS - The return status for the operation + + Attempting to remove a system lock that did not exist is OK. + +--*/ + +{ + NTSTATUS Status = STATUS_NOT_LOCKED; + KIRQL SavedIrql; + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Note that we check the VPB_LOCKED flag here rather than the Vcb + // lock flag. The Vpb flag is only set for an explicit lock request, not + // for the implicit lock obtained on a volume open with zero share mode. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) && + (FileObject == Vcb->VolumeLockFileObject)) { + + ClearFlag( Vcb->VcbState, VCB_STATE_LOCKED ); + ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED); + Vcb->VolumeLockFileObject = NULL; + Status = STATUS_SUCCESS; + } + + IoReleaseVpbSpinLock( SavedIrql ); + + return Status; +} + + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonFsControl ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing FileSystem control operations called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PAGED_CODE(); + + // + // We know this is a file system control so we'll case on the + // minor function, and call a internal worker routine to complete + // the irp. + // + + switch (IrpSp->MinorFunction) { + + case IRP_MN_USER_FS_REQUEST: + + Status = CdUserFsctl( IrpContext, Irp ); + break; + + case IRP_MN_MOUNT_VOLUME: + + Status = CdMountVolume( IrpContext, Irp ); + break; + + case IRP_MN_VERIFY_VOLUME: + + Status = CdVerifyVolume( IrpContext, Irp ); + break; + + default: + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdUserFsctl ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + This is the common routine for implementing the user's requests made + through NtFsControlFile. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PAGED_CODE(); + + // + // Case on the control code. + // + + switch ( IrpSp->Parameters.FileSystemControl.FsControlCode ) { + + case FSCTL_REQUEST_OPLOCK_LEVEL_1 : + case FSCTL_REQUEST_OPLOCK_LEVEL_2 : + case FSCTL_REQUEST_BATCH_OPLOCK : + case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE : + case FSCTL_OPBATCH_ACK_CLOSE_PENDING : + case FSCTL_OPLOCK_BREAK_NOTIFY : + case FSCTL_OPLOCK_BREAK_ACK_NO_2 : + case FSCTL_REQUEST_FILTER_OPLOCK : + + Status = CdOplockRequest( IrpContext, Irp ); + break; + + case FSCTL_LOCK_VOLUME : + + Status = CdLockVolume( IrpContext, Irp ); + break; + + case FSCTL_UNLOCK_VOLUME : + + Status = CdUnlockVolume( IrpContext, Irp ); + break; + + case FSCTL_DISMOUNT_VOLUME : + + Status = CdDismountVolume( IrpContext, Irp ); + break; + + case FSCTL_IS_VOLUME_DIRTY : + + Status = CdIsVolumeDirty( IrpContext, Irp ); + break; + + case FSCTL_IS_VOLUME_MOUNTED : + + Status = CdIsVolumeMounted( IrpContext, Irp ); + break; + + case FSCTL_IS_PATHNAME_VALID : + + Status = CdIsPathnameValid( IrpContext, Irp ); + break; + + case FSCTL_INVALIDATE_VOLUMES : + + Status = CdInvalidateVolumes( IrpContext, Irp ); + break; + + case FSCTL_ALLOW_EXTENDED_DASD_IO: + + Status = CdAllowExtendedDasdIo( IrpContext, Irp ); + break; + + // + // We don't support any of the known or unknown requests. + // + + default: + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return Status; +} + + +VOID +CdReMountOldVcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB OldVcb, + _Inout_ PVCB NewVcb, + _In_ PDEVICE_OBJECT DeviceObjectWeTalkTo + ) +{ + KIRQL SavedIrql; + ULONG Index; + PUCHAR Buffer; + + UNREFERENCED_PARAMETER( IrpContext ); + + ObDereferenceObject( OldVcb->TargetDeviceObject ); + + IoAcquireVpbSpinLock( &SavedIrql ); + +#pragma prefast(suppress: 28175, "this is a filesystem driver, touching the vpb is allowed") + NewVcb->Vpb->RealDevice->Vpb = OldVcb->Vpb; + + OldVcb->Vpb->RealDevice = NewVcb->Vpb->RealDevice; + OldVcb->TargetDeviceObject = DeviceObjectWeTalkTo; + + CdUpdateVcbCondition( OldVcb, VcbMounted); + CdUpdateMediaChangeCount( OldVcb, NewVcb->MediaChangeCount); + + ClearFlag( OldVcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE); + + Buffer = OldVcb->SectorCacheBuffer = NewVcb->SectorCacheBuffer; + NewVcb->SectorCacheBuffer = NULL; + + if (NULL != Buffer) { + + for (Index = 0; Index < CD_SEC_CACHE_CHUNKS; Index++) { + + OldVcb->SecCacheChunks[ Index].Buffer = Buffer; + OldVcb->SecCacheChunks[ Index].BaseLbn = (ULONG)-1; + + Buffer += CD_SEC_CHUNK_BLOCKS * SECTOR_SIZE; + } + } + + IoReleaseVpbSpinLock( SavedIrql ); +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdMountVolume ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the mount volume operation. It is responsible for + either completing of enqueuing the input Irp. + + Its job is to verify that the volume denoted in the IRP is a Cdrom volume, + and create the VCB and root DCB structures. The algorithm it + uses is essentially as follows: + + 1. Create a new Vcb Structure, and initialize it enough to do I/O + through the on-disk volume descriptors. + + 2. Read the disk and check if it is a Cdrom volume. + + 3. If it is not a Cdrom volume then delete the Vcb and + complete the IRP back with an appropriate status. + + 4. Check if the volume was previously mounted and if it was then do a + remount operation. This involves deleting the VCB, hook in the + old VCB, and complete the IRP. + + 5. Otherwise create a Vcb and root DCB for each valid volume descriptor. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PVOLUME_DEVICE_OBJECT VolDo = NULL; + PVCB Vcb = NULL; + PVCB OldVcb; + UCHAR StackSize; + + BOOLEAN FoundPvd = FALSE; + BOOLEAN SetDoVerifyOnFail; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + PDEVICE_OBJECT DeviceObjectWeTalkTo = IrpSp->Parameters.MountVolume.DeviceObject; + PVPB Vpb = IrpSp->Parameters.MountVolume.Vpb; + + PFILE_OBJECT FileObjectToNotify = NULL; + + ULONG BlockFactor; + DISK_GEOMETRY DiskGeometry; + + IO_SCSI_CAPABILITIES Capabilities; + + IO_STATUS_BLOCK Iosb; + + PCHAR RawIsoVd = NULL; + + PCDROM_TOC_LARGE CdromToc = NULL; + ULONG TocLength = 0; + ULONG TocTrackCount = 0; + ULONG TocDiskFlags = 0; + ULONG MediaChangeCount = 0; + +#ifdef CDFS_TELEMETRY_DATA + GUID VolumeGuid; +#endif + + PAGED_CODE(); + + // + // Check that we are talking to a Cdrom device. This request should + // always be waitable. + // + + NT_ASSERT( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ); + NT_ASSERT( FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )); + +#ifdef CDFS_TELEMETRY_DATA + // + // We don't want a bogus VolumeGuid to show up in our telemetry + // + + RtlZeroMemory( &VolumeGuid, sizeof(GUID) ); + +#endif + + // + // Update the real device in the IrpContext from the Vpb. There was no available + // file object when the IrpContext was created. + // + + IrpContext->RealDevice = Vpb->RealDevice; + + SetDoVerifyOnFail = CdRealDevNeedsVerify( IrpContext->RealDevice); + + // + // Check if we have disabled the mount process. + // + + if (CdDisable) { + + CdCompleteRequest( IrpContext, Irp, STATUS_UNRECOGNIZED_VOLUME ); + return STATUS_UNRECOGNIZED_VOLUME; + } + + // + // If we've shutdown disallow further mounts. + // + + if (FlagOn( CdData.Flags, CD_FLAGS_SHUTDOWN )) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SYSTEM_SHUTDOWN ); + return STATUS_SYSTEM_SHUTDOWN; + } + + // + // Do a CheckVerify here to lift the MediaChange ticker from the driver + // + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_CHECK_VERIFY, + DeviceObjectWeTalkTo, + &MediaChangeCount, + sizeof(ULONG), + FALSE, + TRUE, + &Iosb ); + + if (!NT_SUCCESS( Status )) { + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + MediaChangeCount = 0; + } + + // + // Now let's make Jeff delirious and call to get the disk geometry. This + // will fix the case where the first change line is swallowed. + // + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_GET_DRIVE_GEOMETRY, + DeviceObjectWeTalkTo, + &DiskGeometry, + sizeof( DISK_GEOMETRY ), + FALSE, + TRUE, + NULL ); + + // + // Return insufficient sources to our caller. + // + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // Now check the block factor for addressing the volume descriptors. + // If the call for the disk geometry failed then assume there is one + // block per sector. + // + + BlockFactor = 1; + + if (NT_SUCCESS( Status ) && + (DiskGeometry.BytesPerSector != 0) && + (DiskGeometry.BytesPerSector < SECTOR_SIZE)) { + + BlockFactor = SECTOR_SIZE / DiskGeometry.BytesPerSector; + } + + // + // Acquire the global resource to do mount operations. + // + + CdAcquireCdData( IrpContext ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Allocate a buffer to query the TOC. + // + + CdromToc = FsRtlAllocatePoolWithTag( CdPagedPool, + sizeof( CDROM_TOC_LARGE ), + TAG_CDROM_TOC ); + + RtlZeroMemory( CdromToc, sizeof( CDROM_TOC_LARGE )); + + // + // Do a quick check to see if there any Vcb's which can be removed. + // + + CdScanForDismountedVcb( IrpContext ); + + // + // Get our device object and alignment requirement. + // + + Status = IoCreateDevice( CdData.DriverObject, + sizeof( VOLUME_DEVICE_OBJECT ) - sizeof( DEVICE_OBJECT ), + NULL, + FILE_DEVICE_CD_ROM_FILE_SYSTEM, + 0, + FALSE, + (PDEVICE_OBJECT *) &VolDo ); + + if (!NT_SUCCESS( Status )) { try_leave( Status ); } + + // + // Our alignment requirement is the larger of the processor alignment requirement + // already in the volume device object and that in the DeviceObjectWeTalkTo + // + + if (DeviceObjectWeTalkTo->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) { + + VolDo->DeviceObject.AlignmentRequirement = DeviceObjectWeTalkTo->AlignmentRequirement; + } + + // + // We must initialize the stack size in our device object before + // the following reads, because the I/O system has not done it yet. + // + + ((PDEVICE_OBJECT) VolDo)->StackSize = (CCHAR) (DeviceObjectWeTalkTo->StackSize + 1); + StackSize = ((PDEVICE_OBJECT) VolDo)->StackSize; + + ClearFlag( VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING ); + + // + // Initialize the overflow queue for the volume + // + + VolDo->OverflowQueueCount = 0; + InitializeListHead( &VolDo->OverflowQueue ); + + VolDo->PostedRequestCount = 0; + KeInitializeSpinLock( &VolDo->OverflowQueueSpinLock ); + + // + // Let's query for the Toc now and handle any error we get from this operation. + // + + Status = CdProcessToc( IrpContext, + DeviceObjectWeTalkTo, + CdromToc, + &TocLength, + &TocTrackCount, + &TocDiskFlags ); + + // + // If we failed to read the TOC, then bail out. Probably blank media. + // + + if (Status != STATUS_SUCCESS) { + + try_leave( Status ); + } + + // + // Now before we can initialize the Vcb we need to set up the + // device object field in the VPB to point to our new volume device + // object. + // + + Vpb->DeviceObject = (PDEVICE_OBJECT) VolDo; + + // + // Initialize the Vcb. This routine will raise on an allocation + // failure. + // + + CdInitializeVcb( IrpContext, + &VolDo->Vcb, + DeviceObjectWeTalkTo, + Vpb, + CdromToc, + TocLength, + TocTrackCount, + TocDiskFlags, + BlockFactor, + MediaChangeCount ); + + // + // Show that we initialized the Vcb and can cleanup with the Vcb. + // + + Vcb = &VolDo->Vcb; + VolDo = NULL; + Vpb = NULL; + CdromToc = NULL; + +#ifdef CDFS_TELEMETRY_DATA + + // + // Initialize the volume guid. + // + + if (NT_SUCCESS( IoVolumeDeviceToGuid( Vcb->TargetDeviceObject, &VolumeGuid ))) { + + // + // We got a GUID, set it in the Telemetry structure + // + + RtlCopyMemory( &CdTelemetryData.VolumeGuid, &VolumeGuid, sizeof(GUID) ); + } + +#endif // CDFS_TELEMETRY_DATA + + // Lock object is acquired and released using internal state + _Analysis_suppress_lock_checking_(Vcb->VcbResource); + + // + // Store the Vcb in the IrpContext as we didn't have one before. + // + + IrpContext->Vcb = Vcb; + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Let's reference the Vpb to make sure we are the one to + // have the last dereference. + // + + Vcb->Vpb->ReferenceCount += 1; + + // + // Clear the verify bit for the start of mount. + // + + CdMarkRealDevVerifyOk( Vcb->Vpb->RealDevice); + + if (!FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK)) { + + // + // Allocate a buffer to read in the volume descriptors. We allocate a full + // page to make sure we don't hit any alignment problems. + // + + RawIsoVd = FsRtlAllocatePoolWithTag( CdNonPagedPool, + ROUND_TO_PAGES( SECTOR_SIZE ), + TAG_VOL_DESC ); + + // + // Try to find the primary volume descriptor. + // + + FoundPvd = CdFindPrimaryVd( IrpContext, + Vcb, + RawIsoVd, + BlockFactor, + TRUE, + FALSE ); + + if (!FoundPvd) { + + // + // We failed to find a valid VD in the data track, but there were also + // audio tracks on this disc, so we'll try to mount it as an audio CD. + // Since we're always last in the mount order, we won't be preventing + // any other FS from trying to mount the data track. However if the + // data track was at the start of the disc, then we abort, to avoid + // having to filter it from our synthesised directory listing later. We + // already filtered off any data track at the end. + // + + if (!(TocDiskFlags & CDROM_DISK_AUDIO_TRACK) || + BooleanFlagOn( Vcb->CdromToc->TrackData[0].Control, TOC_DATA_TRACK)) { + + try_leave( Status = STATUS_UNRECOGNIZED_VOLUME); + } + + SetFlag( Vcb->VcbState, VCB_STATE_AUDIO_DISK | VCB_STATE_CDXA ); + + CdFreePool( &RawIsoVd ); + RawIsoVd = NULL; + } + } + + // + // Look and see if there is a secondary volume descriptor we want to + // use. + // + + if (FoundPvd) { + + // + // Store the primary volume descriptor in the second half of + // RawIsoVd. Then if our search for a secondary fails we can + // recover this immediately. + // + + RtlCopyMemory( Add2Ptr( RawIsoVd, SECTOR_SIZE, PVOID ), + RawIsoVd, + SECTOR_SIZE ); + + // + // We have the initial volume descriptor. Locate a secondary + // volume descriptor if present. + // + + CdFindActiveVolDescriptor( IrpContext, + Vcb, + RawIsoVd, + FALSE); + } + + // + // Allocate a block cache to speed directory operations. We can't + // use the cache if there is any chance the volume has link blocks + // in the data area (i.e. was packet written and then finalized to + // Joliet/9660). So we simply only allow the cache to operate on + // media with a single track - since we're really targetting pressed + // installation media here. We can't be more precise, since D/CD-ROM + // drives don't support READ_TRACK_INFO, which is the only way for + // certain to know whether or not a track was packet written. + // + + if (!FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK) && + ((Vcb->CdromToc->LastTrack - Vcb->CdromToc->FirstTrack) == 0)) { + + ULONG Index; + PUCHAR Buffer; + + Buffer = + Vcb->SectorCacheBuffer = FsRtlAllocatePool( CdPagedPool, + CD_SEC_CACHE_CHUNKS * + CD_SEC_CHUNK_BLOCKS * + SECTOR_SIZE); + + for (Index = 0; Index < (ULONG)CD_SEC_CACHE_CHUNKS; Index++) { + + Vcb->SecCacheChunks[ Index].Buffer = Buffer; + Vcb->SecCacheChunks[ Index].BaseLbn = (ULONG)-1; + + Buffer += CD_SEC_CHUNK_BLOCKS * SECTOR_SIZE; + } + + Vcb->SectorCacheIrp = IoAllocateIrp( StackSize, FALSE); + + if (Vcb->SectorCacheIrp == NULL) { + + try_leave( Status = STATUS_INSUFFICIENT_RESOURCES ); + } + + IoInitializeIrp( Vcb->SectorCacheIrp, + IoSizeOfIrp( StackSize), + (CCHAR)StackSize); + + KeInitializeEvent( &Vcb->SectorCacheEvent, SynchronizationEvent, FALSE); + ExInitializeResourceLite( &Vcb->SectorCacheResource); + } + + // + // Check if this is a remount operation. If so then clean up + // the data structures passed in and created here. + // + + if (CdIsRemount( IrpContext, Vcb, &OldVcb )) { + + NT_ASSERT( NULL != OldVcb->SwapVpb ); + + // + // Link the old Vcb to point to the new device object that we + // should be talking to, dereferencing the previous. Call a + // nonpaged routine to do this since we take the Vpb spinlock. + // + + CdReMountOldVcb( IrpContext, + OldVcb, + Vcb, + DeviceObjectWeTalkTo); + + // + // See if we will need to provide notification of the remount. This is the readonly + // filesystem's form of dismount/mount notification - we promise that whenever a + // volume is "dismounted", that a mount notification will occur when it is revalidated. + // Note that we do not send mount on normal remounts - that would duplicate the media + // arrival notification of the device driver. + // + + if (FlagOn( OldVcb->VcbState, VCB_STATE_NOTIFY_REMOUNT )) { + + ClearFlag( OldVcb->VcbState, VCB_STATE_NOTIFY_REMOUNT ); + + FileObjectToNotify = OldVcb->RootIndexFcb->FileObject; + ObReferenceObject( FileObjectToNotify ); + } + + try_leave( Status = STATUS_SUCCESS ); + } + + // + // This is a new mount. Go ahead and initialize the + // Vcb from the volume descriptor. + // + + CdUpdateVcbFromVolDescriptor( IrpContext, + Vcb, + RawIsoVd ); + + // + // Drop an extra reference on the root dir file so we'll be able to send + // notification. + // + + if (Vcb->RootIndexFcb) { + + FileObjectToNotify = Vcb->RootIndexFcb->FileObject; + ObReferenceObject( FileObjectToNotify ); + } + + // + // Now check the maximum transfer limits on the device in case we + // get raw reads on this volume. + // + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_SCSI_GET_CAPABILITIES, + DeviceObjectWeTalkTo, + &Capabilities, + sizeof( IO_SCSI_CAPABILITIES ), + FALSE, + TRUE, + NULL ); + + if (NT_SUCCESS(Status)) { + + Vcb->MaximumTransferRawSectors = Capabilities.MaximumTransferLength / RAW_SECTOR_SIZE; + Vcb->MaximumPhysicalPages = Capabilities.MaximumPhysicalPages; + + } else { + + // + // This should never happen, but we can safely assume 64k and 16 pages. + // + + Vcb->MaximumTransferRawSectors = (64 * 1024) / RAW_SECTOR_SIZE; + Vcb->MaximumPhysicalPages = 16; + } + + // + // The new mount is complete. Remove the additional references on this + // Vcb and the device we are mounted on top of. + // + + Vcb->VcbReference -= CDFS_RESIDUAL_REFERENCE; + NT_ASSERT( Vcb->VcbReference == CDFS_RESIDUAL_REFERENCE ); + + ObDereferenceObject( Vcb->TargetDeviceObject ); + + CdUpdateVcbCondition( Vcb, VcbMounted); + + CdReleaseVcb( IrpContext, Vcb ); + Vcb = NULL; + + Status = STATUS_SUCCESS; + + } finally { + + // + // Free the TOC buffer if not in the Vcb. + // + + if (CdromToc != NULL) { + + CdFreePool( &CdromToc ); + } + + // + // Free the sector buffer if allocated. + // + + if (RawIsoVd != NULL) { + + CdFreePool( &RawIsoVd ); + } + + // + // If we are not mounting the device, then set the verify bit again. + // + + if ((AbnormalTermination() || (Status != STATUS_SUCCESS)) && + SetDoVerifyOnFail) { + + CdMarkRealDevForVerify( IrpContext->RealDevice); + } + + // + // If we didn't complete the mount then cleanup any remaining structures. + // + + if (Vpb != NULL) { Vpb->DeviceObject = NULL; } + + if (Vcb != NULL) { + + // + // Make sure there is no Vcb in the IrpContext since it could go away + // + + IrpContext->Vcb = NULL; + + Vcb->VcbReference -= CDFS_RESIDUAL_REFERENCE; + + if (CdDismountVcb( IrpContext, Vcb )) { + + CdReleaseVcb( IrpContext, Vcb ); + } + + } else if (VolDo != NULL) { + + IoDeleteDevice( (PDEVICE_OBJECT) VolDo ); + } + + // + // Release the global resource. + // + + CdReleaseCdData( IrpContext ); + } + + // + // Now send mount notification. + // + + if (FileObjectToNotify) { + + FsRtlNotifyVolumeEvent( FileObjectToNotify, FSRTL_VOLUME_MOUNT ); + ObDereferenceObject( FileObjectToNotify ); + } + +#ifdef CDFS_TELEMETRY_DATA + + // + // Send Telemetry + // + + RtlCopyMemory( &VolumeGuid, &CdTelemetryData.VolumeGuid, sizeof(GUID) ); + CdTelemetryMountSafe(&VolumeGuid, STATUS_SUCCESS, Vcb); + +#endif + + // + // Complete the request if no exception. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdVerifyVolume ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the verify volume operation. It is responsible for + either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + PVPB Vpb = IrpSp->Parameters.VerifyVolume.Vpb; + PVCB Vcb = &((PVOLUME_DEVICE_OBJECT) IrpSp->Parameters.VerifyVolume.DeviceObject)->Vcb; + + PCHAR RawIsoVd = NULL; + + PCDROM_TOC_LARGE CdromToc = NULL; + ULONG TocLength = 0; + ULONG TocTrackCount = 0; + ULONG TocDiskFlags = 0; + + ULONG MediaChangeCount = Vcb->MediaChangeCount; + + PFILE_OBJECT FileObjectToNotify = NULL; + + BOOLEAN ReturnError; + BOOLEAN ReleaseVcb = FALSE; + + IO_STATUS_BLOCK Iosb; + + STRING AnsiLabel; + UNICODE_STRING UnicodeLabel; + + WCHAR VolumeLabel[ VOLUME_ID_LENGTH ]; + ULONG VolumeLabelLength; + + ULONG Index; + + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // We check that we are talking to a Cdrom device. + // + + NT_ASSERT( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ); + NT_ASSERT( FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )); + + // + // Update the real device in the IrpContext from the Vpb. There was no available + // file object when the IrpContext was created. + // + + IrpContext->RealDevice = Vpb->RealDevice; + + // + // Acquire the global resource to synchronise against mounts and teardown, + // finally clause releases. + // + + CdAcquireCdData( IrpContext ); + + try { + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + ReleaseVcb = TRUE; + + // + // Check to see if the volume is eligible for verification. + // + + if ((Vcb->VcbCondition == VcbInvalid) || + (Vcb->VcbCondition == VcbDismountInProgress)) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // Verify that there is a disk here. + // + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_CHECK_VERIFY, + Vcb->TargetDeviceObject, + &MediaChangeCount, + sizeof(ULONG), + FALSE, + TRUE, + &Iosb ); + + if (!NT_SUCCESS( Status )) { + + // + // If we will allow a raw mount then return WRONG_VOLUME to + // allow the volume to be mounted by raw. + // + + if (FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT )) { + + Status = STATUS_WRONG_VOLUME; + } + + try_return( Status ); + } + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + MediaChangeCount = 0; + } + + // + // Verify that the device actually saw a change. If the driver does not + // support the MCC, then we must verify the volume in any case. + // + + if (MediaChangeCount == 0 || + (Vcb->MediaChangeCount != MediaChangeCount)) { + + // + // Allocate a buffer to query the TOC. + // + + CdromToc = FsRtlAllocatePoolWithTag( CdPagedPool, + sizeof( CDROM_TOC_LARGE ), + TAG_CDROM_TOC ); + + RtlZeroMemory( CdromToc, sizeof( CDROM_TOC_LARGE )); + + // + // Let's query for the Toc now and handle any error we get from this operation. + // + + Status = CdProcessToc( IrpContext, + Vcb->TargetDeviceObject, + CdromToc, + &TocLength, + &TocTrackCount, + &TocDiskFlags ); + + // + // If we failed to read the TOC, then give up now. Drives will fail + // a TOC read on, for example, erased CD-RW media. + // + + if (Status != STATUS_SUCCESS) { + + // + // For any errors other than no media and not ready, commute the + // status to ensure that the current VPB is kicked off the device + // below - there is probably blank media in the drive, since we got + // further than the check verify. + // + + if (!CdIsRawDevice( IrpContext, Status )) { + + Status = STATUS_WRONG_VOLUME; + } + + try_return( Status ); + + // + // We got a TOC. Verify that it matches the previous Toc. + // + + } else if ((Vcb->TocLength != TocLength) || + (Vcb->TrackCount != TocTrackCount) || + (Vcb->DiskFlags != TocDiskFlags) || + !RtlEqualMemory( CdromToc, + Vcb->CdromToc, + TocLength )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // If the disk to verify is an audio disk then we already have a + // match. Otherwise we need to check the volume descriptor. + // + + if (!FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) { + + // + // Allocate a buffer for the sector buffer. + // + + RawIsoVd = FsRtlAllocatePoolWithTag( CdNonPagedPool, + ROUND_TO_PAGES( 2 * SECTOR_SIZE ), + TAG_VOL_DESC ); + + // + // Read the primary volume descriptor for this volume. If we + // get an io error and this verify was a the result of DASD open, + // commute the Io error to STATUS_WRONG_VOLUME. Note that if we currently + // expect a music disk then this request should fail. + // + + ReturnError = FALSE; + + if (FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT )) { + + ReturnError = TRUE; + } + + if (!CdFindPrimaryVd( IrpContext, + Vcb, + RawIsoVd, + Vcb->BlockFactor, + ReturnError, + TRUE )) { + + // + // If the previous Vcb did not represent a raw disk + // then show this volume was dismounted. + // + + try_return( Status = STATUS_WRONG_VOLUME ); + + } + else { + + // + // Look for a supplementary VD. + // + // Store the primary volume descriptor in the second half of + // RawIsoVd. Then if our search for a secondary fails we can + // recover this immediately. + // + + RtlCopyMemory( Add2Ptr( RawIsoVd, SECTOR_SIZE, PVOID ), + RawIsoVd, + SECTOR_SIZE ); + + // + // We have the initial volume descriptor. Locate a secondary + // volume descriptor if present. + // + + CdFindActiveVolDescriptor( IrpContext, + Vcb, + RawIsoVd, + TRUE); + // + // Compare the serial numbers. If they don't match, set the + // status to wrong volume. + // + + if (Vpb->SerialNumber != CdSerial32( RawIsoVd, SECTOR_SIZE )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // Verify the volume labels. + // + + if (!FlagOn( Vcb->VcbState, VCB_STATE_JOLIET )) { + + // + // Compute the length of the volume name + // + + AnsiLabel.Buffer = (PCHAR)CdRvdVolId( RawIsoVd, Vcb->VcbState ); + AnsiLabel.MaximumLength = AnsiLabel.Length = (ULONG)VOLUME_ID_LENGTH; + + UnicodeLabel.MaximumLength = VOLUME_ID_LENGTH * sizeof( WCHAR ); + UnicodeLabel.Buffer = VolumeLabel; + + // + // Convert this to unicode. If we get any error then use a name + // length of zero. + // + + VolumeLabelLength = 0; + + if (NT_SUCCESS( RtlOemStringToCountedUnicodeString( &UnicodeLabel, + &AnsiLabel, + FALSE ))) { + + VolumeLabelLength = UnicodeLabel.Length; + } + + // + // We need to convert from big-endian to little endian. + // + + } else { + + CdConvertBigToLittleEndian( IrpContext, + (PCHAR) CdRvdVolId( RawIsoVd, Vcb->VcbState ), + VOLUME_ID_LENGTH, + (PCHAR) VolumeLabel ); + + VolumeLabelLength = VOLUME_ID_LENGTH; + } + + // + // Strip the trailing spaces or zeroes from the name. + // + + Index = VolumeLabelLength / sizeof( WCHAR ); + + while (Index > 0) { + + if ((VolumeLabel[ Index - 1 ] != L'\0') && + (VolumeLabel[ Index - 1 ] != L' ')) { + + break; + } + + Index -= 1; + } + + // + // Now set the final length for the name. + // + + VolumeLabelLength = (USHORT) (Index * sizeof( WCHAR )); + + // + // Now check that the label matches. + // + if ((Vpb->VolumeLabelLength != VolumeLabelLength) || + !RtlEqualMemory( Vpb->VolumeLabel, + VolumeLabel, + VolumeLabelLength )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + } + } + } + + // + // The volume is OK, clear the verify bit. + // + + CdUpdateVcbCondition( Vcb, VcbMounted); + + CdMarkRealDevVerifyOk( Vpb->RealDevice); + + // + // See if we will need to provide notification of the remount. This is the readonly + // filesystem's form of dismount/mount notification. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_NOTIFY_REMOUNT )) { + + ClearFlag( Vcb->VcbState, VCB_STATE_NOTIFY_REMOUNT ); + + FileObjectToNotify = Vcb->RootIndexFcb->FileObject; + ObReferenceObject( FileObjectToNotify ); + } + + try_exit: NOTHING; + + // + // Update the media change count to note that we have verified the volume + // at this value - regardless of the outcome. + // + + CdUpdateMediaChangeCount( Vcb, MediaChangeCount); + + // + // If the volume was already unmounted, nothing more to do. + // + + if (Vcb->VcbCondition == VcbNotMounted) { + + Status = STATUS_WRONG_VOLUME; + + // + // If we got the wrong volume then free any remaining XA sector in + // the current Vcb. Also mark the Vcb as not mounted. + // + + } else if ((Vcb->VcbCondition == VcbMounted) && (Status == STATUS_WRONG_VOLUME)) { + + CdUpdateVcbCondition( Vcb, VcbNotMounted); + + if (Vcb->XASector != NULL) { + + CdFreePool( &Vcb->XASector ); + Vcb->XASector = 0; + Vcb->XADiskOffset = 0; + } + + CdFreeDirCache( IrpContext); + + // + // Now, if there are no user handles to the volume, try to spark + // teardown by purging the volume. + // + + if (Vcb->VcbCleanup == 0) { + + if (NT_SUCCESS( CdPurgeVolume( IrpContext, Vcb, FALSE ))) { + + ReleaseVcb = CdCheckForDismount( IrpContext, Vcb, FALSE ); + } + } + } + + } + finally { + + // + // Free the TOC buffer if allocated. + // + + if (CdromToc != NULL) { + + CdFreePool( &CdromToc ); + } + + if (RawIsoVd != NULL) { + + CdFreePool( &RawIsoVd ); + } + + if (ReleaseVcb) { + + CdReleaseVcb( IrpContext, Vcb ); + } + else { + _Analysis_assume_lock_not_held_(Vcb->VcbResource); + } + + CdReleaseCdData( IrpContext ); + } + + // + // Now send mount notification. + // + + if (FileObjectToNotify) { + + FsRtlNotifyVolumeEvent( FileObjectToNotify, FSRTL_VOLUME_MOUNT ); + ObDereferenceObject( FileObjectToNotify ); + } + + // + // Complete the request if no exception. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdOplockRequest ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine to handle oplock requests made via the + NtFsControlFile call. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PFCB Fcb; + PCCB Ccb; + + ULONG OplockCount = 0; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PAGED_CODE(); + + // + // We only permit oplock requests on files. + // + + if (CdDecodeFileObject( IrpContext, + IrpSp->FileObject, + &Fcb, + &Ccb ) != UserFileOpen ) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Make this a waitable Irpcontext so we don't fail to acquire + // the resources. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST ); + + // + // Switch on the function control code. We grab the Fcb exclusively + // for oplock requests, shared for oplock break acknowledgement. + // + + switch (IrpSp->Parameters.FileSystemControl.FsControlCode) { + + case FSCTL_REQUEST_OPLOCK_LEVEL_1 : + case FSCTL_REQUEST_OPLOCK_LEVEL_2 : + case FSCTL_REQUEST_BATCH_OPLOCK : + case FSCTL_REQUEST_FILTER_OPLOCK : + + CdAcquireFcbExclusive( IrpContext, Fcb, FALSE ); + + if (IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) { + + if (Fcb->FileLock != NULL) { + +#if (NTDDI_VERSION >= NTDDI_WIN7) + OplockCount = (ULONG) FsRtlAreThereCurrentOrInProgressFileLocks( Fcb->FileLock ); +#else + OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( Fcb->FileLock ); +#endif + } + + } else { + + OplockCount = Fcb->FcbCleanup; + } + + break; + + case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: + case FSCTL_OPBATCH_ACK_CLOSE_PENDING: + case FSCTL_OPLOCK_BREAK_NOTIFY: + case FSCTL_OPLOCK_BREAK_ACK_NO_2: + + CdAcquireFcbShared( IrpContext, Fcb, FALSE ); + break; + + default: + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Use a try finally to free the Fcb. + // + + try { + + // + // Verify the Fcb. + // + + CdVerifyFcbOperation( IrpContext, Fcb ); + + // + // Call the FsRtl routine to grant/acknowledge oplock. + // + + Status = FsRtlOplockFsctrl( CdGetFcbOplock(Fcb), + Irp, + OplockCount ); + + // + // Set the flag indicating if Fast I/O is possible + // + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + + // + // The oplock package will complete the Irp. + // + + Irp = NULL; + + } finally { + + // + // Release all of our resources + // + + CdReleaseFcb( IrpContext, Fcb ); + } + + // + // Complete the request if there was no exception. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdLockVolume ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the lock volume operation. It is responsible for + either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + return STATUS_INVALID_PARAMETER; + } + + // + // Send our notification so that folks that like to hold handles on + // volumes can get out of the way. + // + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK ); + + // + // Acquire exclusive access to the Vcb. + // + + Vcb = Fcb->Vcb; + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + try { + + // + // Verify the Vcb. + // + + CdVerifyVcb( IrpContext, Vcb ); + + Status = CdLockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject ); + + } finally { + + // + // Release the Vcb. + // + + CdReleaseVcb( IrpContext, Vcb ); + + if (AbnormalTermination() || !NT_SUCCESS( Status )) { + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED ); + } + } + + // + // Complete the request if there haven't been any exceptions. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdUnlockVolume ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the unlock volume operation. It is responsible for + either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen ) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Acquire exclusive access to the Vcb. + // + + Vcb = Fcb->Vcb; + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // We won't check for a valid Vcb for this request. An unlock will always + // succeed on a locked volume. + // + + Status = CdUnlockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject ); + + // + // Release all of our resources + // + + CdReleaseVcb( IrpContext, Vcb ); + + // + // Send notification that the volume is avaliable. + // + + if (NT_SUCCESS( Status )) { + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_UNLOCK ); + } + + // + // Complete the request if there haven't been any exceptions. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdDismountVolume ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the dismount volume operation. It is responsible for + either completing of enqueuing the input Irp. We only dismount a volume which + has been locked. The intent here is that someone has locked the volume (they are the + only remaining handle). We set the verify bit here and the user will close his handle. + We will dismount a volume with no user's handles in the verify path. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen ) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + Vcb = Fcb->Vcb; + + // + // Send dismount notification. + // + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT ); + + // + // Make this request waitable. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Acquire exclusive access to the Vcb, and take the global resource to + // sync. against mounts, verifies etc. + // + + CdAcquireCdData( IrpContext ); + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Mark the volume as needs to be verified, but only do it if + // the vcb is locked by this handle and the volume is currently mounted. + // + + if (Vcb->VcbCondition != VcbMounted) { + + Status = STATUS_VOLUME_DISMOUNTED; + + } else { + + // + // Invalidate the volume right now. + // + // The intent here is to make every subsequent operation + // on the volume fail and grease the rails toward dismount. + // By definition there is no going back from a SURPRISE. + // + + CdLockVcb( IrpContext, Vcb ); + + if (Vcb->VcbCondition != VcbDismountInProgress) { + + CdUpdateVcbCondition( Vcb, VcbInvalid ); + } + + SetFlag( Vcb->VcbState, VCB_STATE_DISMOUNTED ); + + CdUnlockVcb( IrpContext, Vcb ); + + + // + // Set flag to tell the close path that we want to force dismount + // the volume when this handle is closed. + // + + SetFlag( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE); + + Status = STATUS_SUCCESS; + } + + // + // Release all of our resources + // + + CdReleaseVcb( IrpContext, Vcb ); + CdReleaseCdData( IrpContext ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + FsRtlDismountComplete( Vcb->TargetDeviceObject, Status ); + +#endif + + // + // Complete the request if there haven't been any exceptions. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdIsVolumeDirty ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine determines if a volume is currently dirty. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + PULONG VolumeState; + + PAGED_CODE(); + + // + // Get the current stack location and extract the output + // buffer information. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Get a pointer to the output buffer. + // + + if (Irp->AssociatedIrp.SystemBuffer != NULL) { + + VolumeState = Irp->AssociatedIrp.SystemBuffer; + + } else { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER ); + return STATUS_INVALID_USER_BUFFER; + } + + // + // Make sure the output buffer is large enough and then initialize + // the answer to be that the volume isn't dirty. + // + + if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + *VolumeState = 0; + + // + // Decode the file object + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + if (TypeOfOpen != UserVolumeOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + if (Fcb->Vcb->VcbCondition != VcbMounted) { + + CdCompleteRequest( IrpContext, Irp, STATUS_VOLUME_DISMOUNTED ); + return STATUS_VOLUME_DISMOUNTED; + } + + // + // Now set up to return the clean state. CDs obviously can never be dirty + // but we want to make sure we have enforced the full semantics of this call. + // + + Irp->IoStatus.Information = sizeof( ULONG ); + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdIsVolumeMounted ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine determines if a volume is currently mounted. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Decode the file object. + // + + CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + if (Fcb != NULL) { + + // + // Disable PopUps, we want to return any error. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS ); + + // + // Verify the Vcb. This will raise in the error condition. + // + + CdVerifyVcb( IrpContext, Fcb->Vcb ); + } + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdIsPathnameValid ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine determines if pathname is a valid CDFS pathname. + We always succeed this request. + +Arguments: + + Irp - Supplies the Irp to process. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdInvalidateVolumes ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine searches for all the volumes mounted on the same real device + of the current DASD handle, and marks them all bad. The only operation + that can be done on such handles is cleanup and close. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + KIRQL SavedIrql; + + BOOLEAN UnlockVcb = FALSE; + + LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0}; + + HANDLE Handle; + + PVCB Vcb; + + PLIST_ENTRY Links; + + PFILE_OBJECT FileToMarkBad; + PDEVICE_OBJECT DeviceToMarkBad; + + // + // We only allow the invalidate call to come in on our file system devices. + // + + if (IrpSp->DeviceObject != CdData.FileSystemDeviceObject) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Check for the correct security access. + // The caller must have the SeTcbPrivilege. + // + + if (!SeSinglePrivilegeCheck( TcbPrivilege, Irp->RequestorMode )) { + + CdCompleteRequest( IrpContext, Irp, STATUS_PRIVILEGE_NOT_HELD ); + + return STATUS_PRIVILEGE_NOT_HELD; + } + + // + // Try to get a pointer to the device object from the handle passed in. + // + +#if defined(_WIN64) && BUILD_WOW64_ENABLED + + if (IoIs32bitProcess( Irp )) { + + if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof( UINT32 )) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + Handle = (HANDLE) LongToHandle( *((PUINT32) Irp->AssociatedIrp.SystemBuffer) ); + + } else + +#endif + + { + if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof( HANDLE )) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + Handle = *((PHANDLE) Irp->AssociatedIrp.SystemBuffer); + } + + Status = ObReferenceObjectByHandle( Handle, + 0, + *IoFileObjectType, + KernelMode, + &FileToMarkBad, + NULL ); + + if (!NT_SUCCESS(Status)) { + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // Grab the DeviceObject from the FileObject. + // + + DeviceToMarkBad = FileToMarkBad->DeviceObject; + + // + // We only needed the device object involved, not a reference to the file. + // + + ObDereferenceObject( FileToMarkBad ); + + // + // Make sure this request can wait. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST ); + + // + // Synchronise with pnp/mount/verify paths. + // + + CdAcquireCdData( IrpContext ); + + // + // Nothing can go wrong now. + // + + // + // Now walk through all the mounted Vcb's looking for candidates to + // mark invalid. + // + // On volumes we mark invalid, check for dismount possibility (which is + // why we have to get the next link so early). + // + + Links = CdData.VcbQueue.Flink; + + while (Links != &CdData.VcbQueue) { + + Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks); + + Links = Links->Flink; + + // + // If we get a match, mark the volume Bad, and also check to + // see if the volume should go away. + // + + CdLockVcb( IrpContext, Vcb ); + + if (Vcb->Vpb->RealDevice == DeviceToMarkBad) { + + // + // Take the VPB spinlock, and look to see if this volume is the + // one currently mounted on the actual device. If it is, pull it + // off immediately. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + +#pragma prefast(suppress: 28175, "this is a filesystem driver, touching the vpb is allowed") + if (DeviceToMarkBad->Vpb == Vcb->Vpb) { + + PVPB NewVpb = Vcb->SwapVpb; + + NT_ASSERT( FlagOn( Vcb->Vpb->Flags, VPB_MOUNTED)); + NT_ASSERT( NULL != NewVpb); + + RtlZeroMemory( NewVpb, sizeof( VPB ) ); + + NewVpb->Type = IO_TYPE_VPB; + NewVpb->Size = sizeof( VPB ); + NewVpb->RealDevice = DeviceToMarkBad; + +#pragma prefast(push) +#pragma prefast(disable: 28175, "this is a filesystem driver, touching the vpb is allowed") + NewVpb->Flags = FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_REMOVE_PENDING ); + DeviceToMarkBad->Vpb = NewVpb; +#pragma prefast(pop) + + Vcb->SwapVpb = NULL; + } + + IoReleaseVpbSpinLock( SavedIrql ); + + if (Vcb->VcbCondition != VcbDismountInProgress) { + + CdUpdateVcbCondition( Vcb, VcbInvalid); + } + + CdUnlockVcb( IrpContext, Vcb ); + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE); + + CdPurgeVolume( IrpContext, Vcb, FALSE ); + + UnlockVcb = CdCheckForDismount( IrpContext, Vcb, FALSE ); + + // + // prefast: if UnlockVcb is false, then the VCB was already deleted, so we better not touch the Vcb. + // tell Prefast something nice so it stops complaining about us leaking it. + // + + __analysis_assert( UnlockVcb == TRUE ); + + if (UnlockVcb) { + + CdReleaseVcb( IrpContext, Vcb); + } + + } else { + + CdUnlockVcb( IrpContext, Vcb ); + } + } + + CdReleaseCdData( IrpContext ); + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdAllowExtendedDasdIo ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine marks the CCB to indicate that the handle + may be used to read past the end of the volume file. The + handle must be a dasd handle. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen ) { + + Status = STATUS_INVALID_PARAMETER; + } + else { + + SetFlag( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO ); + Status = STATUS_SUCCESS; + } + + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdScanForDismountedVcb ( + _Inout_ PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine walks through the list of Vcb's looking for any which may + now be deleted. They may have been left on the list because there were + outstanding references. + +Arguments: + +Return Value: + + None + +--*/ + +{ + PVCB Vcb; + PLIST_ENTRY Links; + + PAGED_CODE(); + + // + // Walk through all of the Vcb's attached to the global data. + // + + Links = CdData.VcbQueue.Flink; + + while (Links != &CdData.VcbQueue) { + + Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks ); + + // + // Move to the next link now since the current Vcb may be deleted. + // + + Links = Links->Flink; + + // + // If dismount is already underway then check if this Vcb can + // go away. + // + + if ((Vcb->VcbCondition == VcbDismountInProgress) || + (Vcb->VcbCondition == VcbInvalid) || + ((Vcb->VcbCondition == VcbNotMounted) && (Vcb->VcbReference <= CDFS_RESIDUAL_REFERENCE))) { + + CdCheckForDismount( IrpContext, Vcb, FALSE ); + } + } + + return; +} + + +// +// Local support routine +// +_Success_(return != FALSE) +BOOLEAN +CdFindPrimaryVd ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _Out_writes_bytes_(SECTOR_SIZE) PCHAR RawIsoVd, + _In_ ULONG BlockFactor, + _In_ BOOLEAN ReturnOnError, + _In_ BOOLEAN VerifyVolume + ) + +/*++ + +Routine Description: + + This routine is called to walk through the volume descriptors looking + for a primary volume descriptor. When/if a primary is found a 32-bit + serial number is generated and stored into the Vpb. We also store the + location of the primary volume descriptor in the Vcb. + +Arguments: + + Vcb - Pointer to the VCB for the volume. + + RawIsoVd - Pointer to a sector buffer which will contain the primary + volume descriptor on exit, if successful. + + BlockFactor - Block factor used by the current device for the TableOfContents. + + ReturnOnError - Indicates that we should raise on I/O errors rather than + returning a FALSE value. + + VerifyVolume - Indicates if we were called from the verify path. We + do a few things different in this path. We don't update the Vcb in + the verify path. + +Return Value: + + BOOLEAN - TRUE if a valid primary volume descriptor found, FALSE + otherwise. + +--*/ + +{ + NTSTATUS Status; + ULONG ThisPass = 1; + BOOLEAN FoundVd = FALSE; + + ULONG BaseSector; + ULONG SectorOffset; + + PCDROM_TOC_LARGE CdromToc; + + ULONG VolumeFlags; + + PAGED_CODE(); + + // + // If there are no data tracks, don't even bother hunting for descriptors. + // + // This explicitly breaks various non-BlueBook compliant CDs that scribble + // an ISO filesystem on media claiming only audio tracks. Since these + // disks can cause serious problems in some CDROM units, fail fast. I admit + // that it is possible that someone can still record the descriptors in the + // audio track, record a data track (but fail to record descriptors there) + // and still have the disk work. As this form of error worked in NT 4.0, and + // since these disks really do exist, I don't want to change them. + // + // If we wished to support all such media (we don't), it would be neccesary + // to clear this flag on finding ISO or HSG descriptors below. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_AUDIO_DISK)) { + + return FALSE; + } + + // + // We will make at most two passes through the volume descriptor sequence. + // + // On the first pass we will query for the last session. Using this + // as a starting offset we will attempt to mount the volume. On any failure + // we will go to the second pass and try without using any multi-session + // information. + // + // On the second pass we will start offset from sector zero. + // + + while (!FoundVd && (ThisPass <= 2)) { + + // + // If we aren't at pass 1 then we start at sector 0. Otherwise we + // try to look up the multi-session information. + // + + BaseSector = 0; + + if (ThisPass == 1) { + + CdromToc = NULL; + + // + // Check for whether this device supports XA and multi-session. + // + + try { + + // + // Allocate a buffer for the last session information. + // + + CdromToc = FsRtlAllocatePoolWithTag( CdPagedPool, + sizeof( CDROM_TOC_LARGE ), + TAG_CDROM_TOC ); + + RtlZeroMemory( CdromToc, sizeof( CDROM_TOC_LARGE )); + + // + // Query the last session information from the driver. + // + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_GET_LAST_SESSION, + Vcb->TargetDeviceObject, + CdromToc, + sizeof( CDROM_TOC_LARGE ), + FALSE, + TRUE, + NULL ); + + // + // Raise an exception if there was an allocation failure. + // + + if (Status == STATUS_INSUFFICIENT_RESOURCES) { + + CdRaiseStatus( IrpContext, Status ); + } + + // + // We don't handle any errors yet. We will hit that below + // as we try to scan the disk. If we have last session information + // then modify the base sector. + // + + if (NT_SUCCESS( Status ) && + (CdromToc->FirstTrack != CdromToc->LastTrack)) { + + PCHAR Source, Dest; + ULONG Count; + + Count = 4; + + // + // The track address is BigEndian, we need to flip the bytes. + // + + Source = (PCHAR) &CdromToc->TrackData[0].Address[3]; + Dest = (PCHAR) &BaseSector; + + do { + + *Dest++ = *Source--; + + } while (--Count); + + // + // Now adjust the base sector by the block factor of the + // device. + // + + BaseSector /= BlockFactor; + + // + // Make this look like the second pass since we are only using the + // first session. No reason to retry on error. + // + + } else { + + ThisPass += 1; + } + + } finally { + + if (CdromToc != NULL) { CdFreePool( &CdromToc ); } + } + } + + // + // Compute the starting sector offset from the start of the session. + // + + SectorOffset = FIRST_VD_SECTOR; + + // + // Start by assuming we have neither Hsg or Iso volumes. + // + + VolumeFlags = 0; + + // + // Loop until either error encountered, primary volume descriptor is + // found or a terminal volume descriptor is found. + // + + while (TRUE) { + + // + // Attempt to read the desired sector. Exit directly if operation + // not completed. + // + // If this is pass 1 we will ignore errors in read sectors and just + // go to the next pass. + // + + if (!CdReadSectors( IrpContext, + LlBytesFromSectors( BaseSector + SectorOffset ), + SECTOR_SIZE, + (BOOLEAN) ((ThisPass == 1) || ReturnOnError), + RawIsoVd, + Vcb->TargetDeviceObject )) { + + break; + } + + // + // Check if either an ISO or HSG volume. + // + + if (RtlEqualMemory( CdIsoId, + CdRvdId( RawIsoVd, VCB_STATE_ISO ), + VOL_ID_LEN )) { + + SetFlag( VolumeFlags, VCB_STATE_ISO ); + + } else if (RtlEqualMemory( CdHsgId, + CdRvdId( RawIsoVd, VCB_STATE_HSG ), + VOL_ID_LEN )) { + + SetFlag( VolumeFlags, VCB_STATE_HSG ); + + // + // We have neither so break out of the loop. + // + + } else { + + break; + } + + // + // Break out if the version number is incorrect or this is + // a terminator. + // + + if ((CdRvdVersion( RawIsoVd, VolumeFlags ) != VERSION_1) || + (CdRvdDescType( RawIsoVd, VolumeFlags ) == VD_TERMINATOR)) { + + break; + } + + // + // If this is a primary volume descriptor then our search is over. + // + + if (CdRvdDescType( RawIsoVd, VolumeFlags ) == VD_PRIMARY) { + + // + // If we are not in the verify path then initialize the + // fields in the Vcb with basic information from this + // descriptor. + // + + if (!VerifyVolume) { + + // + // Set the flag for the volume type. + // + + SetFlag( Vcb->VcbState, VolumeFlags ); + + // + // Store the base sector and sector offset for the + // primary volume descriptor. + // + + Vcb->BaseSector = BaseSector; + Vcb->VdSectorOffset = SectorOffset; + Vcb->PrimaryVdSectorOffset = SectorOffset; + } + + FoundVd = TRUE; + break; + } + + // + // Indicate that we're at the next sector. + // + + SectorOffset += 1; + } + + ThisPass += 1; + } + + return FoundVd; +} + + +// +// Local support routine +// + +_Success_(return != FALSE) BOOLEAN +CdIsRemount ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_ PVCB *OldVcb + ) +/*++ + +Routine Description: + + This routine walks through the links of the Vcb chain in the global + data structure. The remount condition is met when the following + conditions are all met: + + If the new Vcb is a device only Mvcb and there is a previous + device only Mvcb. + + Otherwise following conditions must be matched. + + 1 - The 32 serial in the current VPB matches that in a previous + VPB. + + 2 - The volume label in the Vpb matches that in the previous + Vpb. + + 3 - The system pointer to the real device object in the current + VPB matches that in the same previous VPB. + + 4 - Finally the previous Vcb cannot be invalid or have a dismount + underway. + + If a VPB is found which matches these conditions, then the address of + the VCB for that VPB is returned via the pointer Vcb. + + Skip over the current Vcb. + +Arguments: + + Vcb - This is the Vcb we are checking for a remount. + + OldVcb - A pointer to the address to store the address for the Vcb + for the volume if this is a remount. (This is a pointer to + a pointer) + +Return Value: + + BOOLEAN - TRUE if this is in fact a remount, FALSE otherwise. + +--*/ + +{ + PLIST_ENTRY Link; + + PVPB Vpb = Vcb->Vpb; + PVPB OldVpb; + + BOOLEAN Remount = FALSE; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Check whether we are looking for a device only Mvcb. + // + + for (Link = CdData.VcbQueue.Flink; + Link != &CdData.VcbQueue; + Link = Link->Flink) { + + *OldVcb = CONTAINING_RECORD( Link, VCB, VcbLinks ); + + // + // Skip ourselves. + // + + if (Vcb == *OldVcb) { continue; } + + // + // Look at the Vpb and state of the previous Vcb. + // + + OldVpb = (*OldVcb)->Vpb; + + if ((OldVpb != Vpb) && + (OldVpb->RealDevice == Vpb->RealDevice) && + ((*OldVcb)->VcbCondition == VcbNotMounted)) { + + // + // If the current disk is a raw disk then it can match a previous music or + // raw disk. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK)) { + + if (FlagOn( (*OldVcb)->VcbState, VCB_STATE_AUDIO_DISK )) { + + // + // If we have both TOC then fail the remount if the lengths + // are different or they don't match. + // + + if ((Vcb->TocLength != (*OldVcb)->TocLength) || + ((Vcb->TocLength != 0) && + !RtlEqualMemory( Vcb->CdromToc, + (*OldVcb)->CdromToc, + Vcb->TocLength ))) { + + continue; + } + + Remount = TRUE; + break; + } + + // + // The current disk is not a raw disk. Go ahead and compare + // serial numbers, volume label and TOC. + // + + } + else if ((OldVpb->SerialNumber == Vpb->SerialNumber) && + (Vcb->TocLength == (*OldVcb)->TocLength) && + ((Vcb->TocLength == 0) || RtlEqualMemory( Vcb->CdromToc, + (*OldVcb)->CdromToc, + Vcb->TocLength )) && + (Vpb->VolumeLabelLength == OldVpb->VolumeLabelLength) && + (RtlEqualMemory( OldVpb->VolumeLabel, + Vpb->VolumeLabel, + Vpb->VolumeLabelLength ))) { + // + // Remember the old Vcb. Then set the return value to + // TRUE and break. + // + + Remount = TRUE; + break; + } + } + } + + return Remount; +} + + +// +// Local support routine +// + +VOID +CdFindActiveVolDescriptor ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Inout_updates_bytes_(ROUND_TO_PAGES( SECTOR_SIZE )) PCHAR RawIsoVd, + _In_ BOOLEAN VerifyVolume + ) + +/*++ + +Routine Description: + + This routine is called to search for a valid secondary volume descriptor that + we will support. Right now we only support Joliet escape sequences for + the secondary descriptor. + + If we don't find the secondary descriptor then we will reread the primary. + + This routine will update the serial number and volume label in the Vpb. + +Arguments: + + Vcb - This is the Vcb for the volume being mounted. + + RawIsoVd - Sector buffer used to read the volume descriptors from the disks, but + on input should contain the PVD (ISO) in the SECOND 'sector' of the + buffer. + + VerifyVolume - indicates we are being called by the verify path, and should + not modify the Vcb fields. + +Return Value: + + None + +--*/ + +{ + BOOLEAN FoundSecondaryVd = FALSE; + ULONG SectorOffset = FIRST_VD_SECTOR; + + ULONG Length; + + ULONG Index; + + PAGED_CODE(); + + // + // We only look for secondary volume descriptors on an Iso disk. + // + + if ((FlagOn( Vcb->VcbState, VCB_STATE_ISO) || VerifyVolume) && !CdNoJoliet) { + + // + // Scan the volume descriptors from the beginning looking for a valid + // secondary or a terminator. + // + + SectorOffset = FIRST_VD_SECTOR; + + while (TRUE) { + + // + // Read the next sector. We should never have an error in this + // path. + // + + CdReadSectors( IrpContext, + LlBytesFromSectors( Vcb->BaseSector + SectorOffset ), + SECTOR_SIZE, + FALSE, + RawIsoVd, + Vcb->TargetDeviceObject ); + + // + // Break out if the version number or standard Id is incorrect. + // Also break out if this is a terminator. + // + + if (!RtlEqualMemory( CdIsoId, CdRvdId( RawIsoVd, VCB_STATE_JOLIET ), VOL_ID_LEN ) || + (CdRvdVersion( RawIsoVd, VCB_STATE_JOLIET ) != VERSION_1) || + (CdRvdDescType( RawIsoVd, VCB_STATE_JOLIET ) == VD_TERMINATOR)) { + + break; + } + + // + // We have a match if this is a secondary descriptor with a matching + // escape sequence. + // + + if ((CdRvdDescType( RawIsoVd, VCB_STATE_JOLIET ) == VD_SECONDARY) && + (RtlEqualMemory( CdRvdEsc( RawIsoVd, VCB_STATE_JOLIET ), + CdJolietEscape[0], + ESC_SEQ_LEN ) || + RtlEqualMemory( CdRvdEsc( RawIsoVd, VCB_STATE_JOLIET ), + CdJolietEscape[1], + ESC_SEQ_LEN ) || + RtlEqualMemory( CdRvdEsc( RawIsoVd, VCB_STATE_JOLIET ), + CdJolietEscape[2], + ESC_SEQ_LEN ))) { + + if (!VerifyVolume) { + + // + // Update the Vcb with the new volume descriptor. + // + + ClearFlag( Vcb->VcbState, VCB_STATE_ISO ); + SetFlag( Vcb->VcbState, VCB_STATE_JOLIET ); + + Vcb->VdSectorOffset = SectorOffset; + } + + FoundSecondaryVd = TRUE; + break; + } + + // + // Otherwise move on to the next sector. + // + + SectorOffset += 1; + } + + // + // If we didn't find the secondary then recover the original volume + // descriptor stored in the second half of the RawIsoVd. + // + + if (!FoundSecondaryVd) { + + RtlCopyMemory( RawIsoVd, + Add2Ptr( RawIsoVd, SECTOR_SIZE, PVOID ), + SECTOR_SIZE ); + } + } + + // + // If we're in the verify path, our work is done, since we don't want + // to update any Vcb/Vpb values. + // + + if (VerifyVolume) { + + return; + } + + // + // Compute the serial number and volume label from the volume descriptor. + // + + Vcb->Vpb->SerialNumber = CdSerial32( RawIsoVd, SECTOR_SIZE ); + + // + // Make sure the CD label will fit in the Vpb. + // + + NT_ASSERT( VOLUME_ID_LENGTH * sizeof( WCHAR ) <= MAXIMUM_VOLUME_LABEL_LENGTH ); + + // + // If this is not a Unicode label we must convert it to unicode. + // + + if (!FlagOn( Vcb->VcbState, VCB_STATE_JOLIET )) { + + // + // Convert the label to unicode. If we get any error then use a name + // length of zero. + // + + Vcb->Vpb->VolumeLabelLength = 0; + + if (NT_SUCCESS( RtlOemToUnicodeN( &Vcb->Vpb->VolumeLabel[0], + MAXIMUM_VOLUME_LABEL_LENGTH, + &Length, + (PCH)CdRvdVolId( RawIsoVd, Vcb->VcbState ), + VOLUME_ID_LENGTH ))) { + + Vcb->Vpb->VolumeLabelLength = (USHORT) Length; + } + + // + // We need to convert from big-endian to little endian. + // + + } else { + + CdConvertBigToLittleEndian( IrpContext, + (PCHAR) CdRvdVolId( RawIsoVd, Vcb->VcbState ), + VOLUME_ID_LENGTH, + (PCHAR) Vcb->Vpb->VolumeLabel ); + + Vcb->Vpb->VolumeLabelLength = VOLUME_ID_LENGTH * sizeof( WCHAR ); + } + + // + // Strip the trailing spaces or zeroes from the name. + // + + Index = Vcb->Vpb->VolumeLabelLength / sizeof( WCHAR ); + + while (Index > 0) { + + if ((Vcb->Vpb->VolumeLabel[ Index - 1 ] != L'\0') && + (Vcb->Vpb->VolumeLabel[ Index - 1 ] != L' ')) { + + break; + } + + Index -= 1; + } + + // + // Now set the final length for the name. + // + + Vcb->Vpb->VolumeLabelLength = (USHORT) (Index * sizeof( WCHAR )); +} + + + diff --git a/filesys/cdfs/fspdisp.c b/filesys/cdfs/fspdisp.c new file mode 100644 index 00000000..b38548d6 --- /dev/null +++ b/filesys/cdfs/fspdisp.c @@ -0,0 +1,280 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FspDisp.c + +Abstract: + + This module implements the main dispatch procedure/thread for the Cdfs + Fsp + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_FSPDISP) + + +VOID +CdFspDispatch ( + _In_ PVOID Context + ) + +/*++ + +Routine Description: + + This is the main FSP thread routine that is executed to receive + and dispatch IRP requests. Each FSP thread begins its execution here. + There is one thread created at system initialization time and subsequent + threads created as needed. + +Arguments: + + IrpContext - IrpContext for a request to process. + +Return Value: + + None + +--*/ + +{ + THREAD_CONTEXT ThreadContext = {0}; + PIRP_CONTEXT IrpContext = Context; + NTSTATUS Status; + + PIRP Irp = IrpContext->Irp; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PVOLUME_DEVICE_OBJECT VolDo = NULL; + + // + // If this request has an associated volume device object, remember it. + // + + if (IrpSp->FileObject != NULL) { + + VolDo = CONTAINING_RECORD( IrpSp->DeviceObject, + VOLUME_DEVICE_OBJECT, + DeviceObject ); + } + + // + // Now case on the function code. For each major function code, + // either call the appropriate worker routine. This routine that + // we call is responsible for completing the IRP, and not us. + // That way the routine can complete the IRP and then continue + // post processing as required. For example, a read can be + // satisfied right away and then read can be done. + // + // We'll do all of the work within an exception handler that + // will be invoked if ever some underlying operation gets into + // trouble. + // + + while ( TRUE ) { + + // + // Set all the flags indicating we are in the Fsp. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FSP_FLAGS ); + + FsRtlEnterFileSystem(); + + CdSetThreadContext( IrpContext, &ThreadContext ); + + while (TRUE) { + + try { + + // + // Reinitialize for the next try at completing this + // request. + // + + Status = + IrpContext->ExceptionStatus = STATUS_SUCCESS; + + // + // Initialize the Io status field in the Irp. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + // + // Case on the major irp code. + // + + switch (IrpContext->MajorFunction) { + + case IRP_MJ_CREATE : + + CdCommonCreate( IrpContext, Irp ); + break; + + case IRP_MJ_CLOSE : + + NT_ASSERT( FALSE ); + break; + + case IRP_MJ_READ : + + CdCommonRead( IrpContext, Irp ); + break; + + case IRP_MJ_QUERY_INFORMATION : + + CdCommonQueryInfo( IrpContext, Irp ); + break; + + case IRP_MJ_SET_INFORMATION : + + CdCommonSetInfo( IrpContext, Irp ); + break; + + case IRP_MJ_QUERY_VOLUME_INFORMATION : + + CdCommonQueryVolInfo( IrpContext, Irp ); + break; + + case IRP_MJ_DIRECTORY_CONTROL : + + CdCommonDirControl( IrpContext, Irp ); + break; + + case IRP_MJ_FILE_SYSTEM_CONTROL : + + CdCommonFsControl( IrpContext, Irp ); + break; + + case IRP_MJ_DEVICE_CONTROL : + + CdCommonDevControl( IrpContext, Irp ); + break; + + case IRP_MJ_LOCK_CONTROL : + + CdCommonLockControl( IrpContext, Irp ); + break; + + case IRP_MJ_CLEANUP : + + CdCommonCleanup( IrpContext, Irp ); + break; + + case IRP_MJ_PNP : + + NT_ASSERT( FALSE ); + CdCommonPnp( IrpContext, Irp ); + break; + + default : + + Status = STATUS_INVALID_DEVICE_REQUEST; + CdCompleteRequest( IrpContext, Irp, Status ); + } + + } except( CdExceptionFilter( IrpContext, GetExceptionInformation() )) { + + Status = CdProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + // + // Break out of the loop if we didn't get CANT_WAIT. + // + + if (Status != STATUS_CANT_WAIT) { break; } + + // + // We are retrying this request. Cleanup the IrpContext for the retry. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); + CdCleanupIrpContext( IrpContext, FALSE ); + } + + FsRtlExitFileSystem(); + + // + // If there are any entries on this volume's overflow queue, service + // them. + // + + if (VolDo != NULL) { + + KIRQL SavedIrql; + PVOID Entry = NULL; + + // + // We have a volume device object so see if there is any work + // left to do in its overflow queue. + // + + KeAcquireSpinLock( &VolDo->OverflowQueueSpinLock, &SavedIrql ); + + if (VolDo->OverflowQueueCount > 0) { + + // + // There is overflow work to do in this volume so we'll + // decrement the Overflow count, dequeue the IRP, and release + // the Event + // + + VolDo->OverflowQueueCount -= 1; + + Entry = RemoveHeadList( &VolDo->OverflowQueue ); + + } else { + + VolDo->PostedRequestCount -= 1; + + Entry = NULL; + } + + KeReleaseSpinLock( &VolDo->OverflowQueueSpinLock, SavedIrql ); + + // + // There wasn't an entry, break out of the loop and return to + // the Ex Worker thread. + // + + if (Entry == NULL) { + + break; + } + + // + // Extract the IrpContext , Irp, set wait to TRUE, and loop. + // + + IrpContext = CONTAINING_RECORD( Entry, + IRP_CONTEXT, + WorkQueueItem.List ); + + Irp = IrpContext->Irp; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + __analysis_assert( IrpSp != 0 ); + + continue; + } + + break; + } + + return; +} + + + + diff --git a/filesys/cdfs/lockctrl.c b/filesys/cdfs/lockctrl.c new file mode 100644 index 00000000..edf0d07a --- /dev/null +++ b/filesys/cdfs/lockctrl.c @@ -0,0 +1,683 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + LockCtrl.c + +Abstract: + + This module implements the Lock Control routines for Cdfs called + by the Fsd/Fsp dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_LOCKCTRL) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonLockControl) +#pragma alloc_text(PAGE, CdFastLock) +#pragma alloc_text(PAGE, CdFastUnlockAll) +#pragma alloc_text(PAGE, CdFastUnlockAllByKey) +#pragma alloc_text(PAGE, CdFastUnlockSingle) +#endif + + +NTSTATUS +CdCommonLockControl ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for Lock Control called by both the fsd and fsp + threads. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Extract and decode the type of file object we're being asked to process + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + // + // If the file is not a user file open then we reject the request + // as an invalid parameter + // + + if (TypeOfOpen != UserFileOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // We check whether we can proceed based on the state of the file oplocks. + // This call might post the irp for us. + // + + Status = FsRtlCheckOplock( CdGetFcbOplock(Fcb), + Irp, + IrpContext, + CdOplockComplete, + NULL ); + + // + // If we don't get success then the oplock package completed the request. + // + + if (Status != STATUS_SUCCESS) { + + return Status; + } + + // + // Verify the Fcb. + // + + CdVerifyFcbOperation( IrpContext, Fcb ); + + // + // If we don't have a file lock, then get one now. + // + + if (Fcb->FileLock == NULL) { CdCreateFileLock( IrpContext, Fcb, TRUE ); } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request + // + + Status = FsRtlProcessFileLock( Fcb->FileLock, Irp, NULL ); + + // + // Set the flag indicating if Fast I/O is possible + // + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + + // + // Complete the request. + // + + CdCompleteRequest( IrpContext, NULL, Status ); + return Status; +} + + +BOOLEAN +CdFastLock ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PLARGE_INTEGER Length, + _In_ PEPROCESS ProcessId, + _In_ ULONG Key, + _In_ BOOLEAN FailImmediately, + _In_ BOOLEAN ExclusiveLock, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast lock call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + FileOffset - Supplies the file offset used in this operation + + Length - Supplies the length used in this operation + + ProcessId - Supplies the process ID used in this operation + + Key - Supplies the key used in this operation + + FailImmediately - Indicates if the request should fail immediately + if the lock cannot be granted. + + ExclusiveLock - Indicates if this is a request for an exclusive or + shared lock + + IoStatus - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + + PFCB Fcb; + TYPE_OF_OPEN TypeOfOpen; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + + ASSERT_FILE_OBJECT( FileObject ); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and + // make sure that is is only a user file open. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + if (TypeOfOpen != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + return TRUE; + } + + // + // Only deal with 'good' Fcb's. + // + + if (!CdVerifyFcbOperation( NULL, Fcb )) { + + return FALSE; + } + + FsRtlEnterFileSystem(); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( CdGetFcbOplock(Fcb) )) { + + try_return( NOTHING ); + } + + // + // If we don't have a file lock, then get one now. + // + + if ((Fcb->FileLock == NULL) && !CdCreateFileLock( NULL, Fcb, FALSE )) { + + try_return( NOTHING ); + } + + // + // Now call the FsRtl routine to perform the lock request. + // + +#pragma prefast(suppress: 28159, "prefast thinks this is an obsolete routine, but it is ok for CDFS to use it") + if ((Results = FsRtlFastLock( Fcb->FileLock, + FileObject, + FileOffset, + Length, + ProcessId, + Key, + FailImmediately, + ExclusiveLock, + IoStatus, + NULL, + FALSE )) != FALSE) { + + // + // Set the flag indicating if Fast I/O is questionable. We + // only change this flag if the current state is possible. + // Retest again after synchronizing on the header. + // + + if (Fcb->IsFastIoPossible == FastIoIsPossible) { + + CdLockFcb( NULL, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( NULL, Fcb ); + } + } + + try_exit: NOTHING; + } finally { + + FsRtlExitFileSystem(); + } + + return Results; +} + + +BOOLEAN +CdFastUnlockSingle ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PLARGE_INTEGER Length, + _In_ PEPROCESS ProcessId, + _In_ ULONG Key, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast unlock single call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + FileOffset - Supplies the file offset used in this operation + + Length - Supplies the length used in this operation + + ProcessId - Supplies the process ID used in this operation + + Key - Supplies the key used in this operation + + Status - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and + // make sure that is is only a user file open. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + if (TypeOfOpen != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + return TRUE; + } + + // + // Only deal with 'good' Fcb's. + // + + if (!CdVerifyFcbOperation( NULL, Fcb )) { + + return FALSE; + } + + // + // If there is no lock then return immediately. + // + + if (Fcb->FileLock == NULL) { + + IoStatus->Status = STATUS_RANGE_NOT_LOCKED; + return TRUE; + } + + FsRtlEnterFileSystem(); + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( CdGetFcbOplock(Fcb) )) { + + try_return( NOTHING ); + } + + // + // If we don't have a file lock, then get one now. + // + + if ((Fcb->FileLock == NULL) && !CdCreateFileLock( NULL, Fcb, FALSE )) { + + try_return( NOTHING ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request. The call will always succeed. + // + + Results = TRUE; + IoStatus->Status = FsRtlFastUnlockSingle( Fcb->FileLock, + FileObject, + FileOffset, + Length, + ProcessId, + Key, + NULL, + FALSE ); + + // + // Set the flag indicating if Fast I/O is possible. We are + // only concerned if there are no longer any filelocks on this + // file. + // + + if (!FsRtlAreThereCurrentFileLocks( Fcb->FileLock ) && + (Fcb->IsFastIoPossible != FastIoIsPossible)) { + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + } + + try_exit: NOTHING; + } finally { + + FsRtlExitFileSystem(); + } + + return Results; +} + + +BOOLEAN +CdFastUnlockAll ( + _In_ PFILE_OBJECT FileObject, + _In_ PEPROCESS ProcessId, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast unlock all call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + ProcessId - Supplies the process ID used in this operation + + Status - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and + // make sure that is is only a user file open. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + if (TypeOfOpen != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + return TRUE; + } + + // + // Only deal with 'good' Fcb's. + // + + if (!CdVerifyFcbOperation( NULL, Fcb )) { + + return FALSE; + } + + // + // If there is no lock then return immediately. + // + + if (Fcb->FileLock == NULL) { + + IoStatus->Status = STATUS_RANGE_NOT_LOCKED; + return TRUE; + } + + FsRtlEnterFileSystem(); + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( CdGetFcbOplock(Fcb) )) { + + try_return( NOTHING ); + } + + // + // If we don't have a file lock, then get one now. + // + + if ((Fcb->FileLock == NULL) && !CdCreateFileLock( NULL, Fcb, FALSE )) { + + try_return( NOTHING ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request. The call will always succeed. + // + + Results = TRUE; + IoStatus->Status = FsRtlFastUnlockAll( Fcb->FileLock, + FileObject, + ProcessId, + NULL ); + + + // + // Set the flag indicating if Fast I/O is possible + // + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + + try_exit: NOTHING; + } finally { + + FsRtlExitFileSystem(); + } + + return Results; +} + + +BOOLEAN +CdFastUnlockAllByKey ( + _In_ PFILE_OBJECT FileObject, + _In_ PVOID ProcessId, + _In_ ULONG Key, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast unlock all by key call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + ProcessId - Supplies the process ID used in this operation + + Key - Supplies the key used in this operation + + Status - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and + // make sure that is is only a user file open. + // + + TypeOfOpen = CdFastDecodeFileObject( FileObject, &Fcb ); + + if (TypeOfOpen != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + return TRUE; + } + + // + // Only deal with 'good' Fcb's. + // + + if (!CdVerifyFcbOperation( NULL, Fcb )) { + + return FALSE; + } + + // + // If there is no lock then return immediately. + // + + if (Fcb->FileLock == NULL) { + + IoStatus->Status = STATUS_RANGE_NOT_LOCKED; + return TRUE; + } + + FsRtlEnterFileSystem(); + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( CdGetFcbOplock(Fcb) )) { + + try_return( NOTHING ); + } + + // + // If we don't have a file lock, then get one now. + // + + if ((Fcb->FileLock == NULL) && !CdCreateFileLock( NULL, Fcb, FALSE )) { + + try_return( NOTHING ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request. The call will always succeed. + // + + Results = TRUE; + IoStatus->Status = FsRtlFastUnlockAllByKey( Fcb->FileLock, + FileObject, + ProcessId, + Key, + NULL ); + + + // + // Set the flag indicating if Fast I/O is possible + // + + CdLockFcb( IrpContext, Fcb ); + Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb ); + CdUnlockFcb( IrpContext, Fcb ); + + try_exit: NOTHING; + } finally { + + FsRtlExitFileSystem(); + } + + return Results; +} + + + diff --git a/filesys/cdfs/namesup.c b/filesys/cdfs/namesup.c new file mode 100644 index 00000000..dc3ed319 --- /dev/null +++ b/filesys/cdfs/namesup.c @@ -0,0 +1,1127 @@ +/*++ + +Copyright (c) 1991-2000 Microsoft Corporation + +Module Name: + + NameSup.c + +Abstract: + + This module implements the Cdfs Name support routines + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_NAMESUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdConvertBigToLittleEndian) +#pragma alloc_text(PAGE, CdConvertNameToCdName) +#pragma alloc_text(PAGE, CdDissectName) +#pragma alloc_text(PAGE, CdGenerate8dot3Name) +#pragma alloc_text(PAGE, CdFullCompareNames) +#pragma alloc_text(PAGE, CdIsLegalName) +#pragma alloc_text(PAGE, CdIs8dot3Name) +#pragma alloc_text(PAGE, CdIsNameInExpression) +#pragma alloc_text(PAGE, CdShortNameDirentOffset) +#pragma alloc_text(PAGE, CdUpcaseName) +#endif + + +_Post_satisfies_(_Old_(CdName->FileName.Length) >= + CdName->FileName.Length + CdName->VersionString.Length) +VOID +CdConvertNameToCdName ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PCD_NAME CdName + ) + +/*++ + +Routine Description: + + This routine is called to convert a string of bytes into a CdName. + + The full name is already in the CdName structure in the FileName field. + We split this into the filename and version strings. + +Arguments: + + CdName - Pointer to CdName structure to update. + +Return Value: + + None. + +--*/ + +{ + ULONG NameLength = 0; + PWCHAR CurrentCharacter = CdName->FileName.Buffer; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Look for a separator character. + // + + while ((NameLength < CdName->FileName.Length) && + (*CurrentCharacter != L';')) { + + CurrentCharacter += 1; + NameLength += 2; + } + + // + // If there is at least one more character after a possible separator then it + // and all following characters are part of the version string. + // + + CdName->VersionString.Length = 0; + if (NameLength + sizeof( WCHAR ) < CdName->FileName.Length) { + + CdName->VersionString.MaximumLength = + CdName->VersionString.Length = (USHORT) (CdName->FileName.Length - NameLength - sizeof( WCHAR )); + CdName->VersionString.Buffer = Add2Ptr( CdName->FileName.Buffer, + NameLength + sizeof( WCHAR ), + PWCHAR ); + } + + // + // Now update the filename portion of the name. + // + + CdName->FileName.Length = (USHORT) NameLength; + + return; +} + + +VOID +CdConvertBigToLittleEndian ( + _In_ PIRP_CONTEXT IrpContext, + _In_reads_bytes_(ByteCount) PCHAR BigEndian, + _In_ ULONG ByteCount, + _Out_writes_bytes_(ByteCount) PCHAR LittleEndian + ) + +/*++ + +Routine Description: + + This routine is called to convert a unicode string in big endian to + little endian. We start by copying all of the source bytes except + the first. This will put the low order bytes in the correct position. + We then copy each high order byte in its correct position. + +Arguments: + + BigEndian - Pointer to the string of big endian characters. + + ByteCount - Number of unicode characters in this string. + + LittleEndian - Pointer to array to store the little endian characters. + +Return Value: + + None. + +--*/ + +{ + ULONG RemainingByteCount = ByteCount; + + PCHAR Source = BigEndian; + PCHAR Destination = LittleEndian; + + PAGED_CODE(); + + // + // If the byte count isn't an even number then the disk is corrupt. + // + + if (FlagOn( ByteCount, 1 )) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + // + // Start by copy the low-order bytes into the correct position. Do + // this by skipping the first byte in the BigEndian string. + // + + RtlCopyMemory( Destination, + Source + 1, + RemainingByteCount - 1 ); + + // + // Now move the high-order bytes into position. + // + + Destination += 1; + + while (RemainingByteCount != 0) { + +#pragma prefast(push) +#pragma prefast(suppress:26014, "RemainingByteCount is even") + *Destination = *Source; +#pragma prefast(pop) + + Source += 2; + Destination += 2; + + RemainingByteCount -= 2; + } + + return; +} + + +VOID +CdUpcaseName ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PCD_NAME Name, + _Inout_ PCD_NAME UpcaseName + ) + +/*++ + +Routine Description: + + This routine is called to upcase a CdName structure. We will do both + the filename and version strings. + +Arguments: + + Name - This is the mixed case version of the name. + + UpcaseName - This is the destination for the upcase operation. + +Return Value: + + None. This routine will raise all errors. + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // If the name structures are different then initialize the different components. + // + + if (Name != UpcaseName) { + + // + // Initialize the version string portion of the name. + // + + UpcaseName->VersionString.Length = 0; + + if (Name->VersionString.Length != 0) { + + UpcaseName->VersionString.MaximumLength = + UpcaseName->VersionString.Length = Name->VersionString.Length; + + // + // Initially set the buffer to point to where we need to insert + // the separator. + // + + UpcaseName->VersionString.Buffer = Add2Ptr( UpcaseName->FileName.Buffer, + Name->FileName.Length, + PWCHAR ); + + // + // We are currently pointing to the location to store the separator. + // Store the ';' and then move to the next character to + // copy the data. + // + +#pragma prefast( suppress:26015, "CD_NAME structures have two UNICODE_STRING structures pointing to the same allocation. there is no way to tell prefast this is the case and that the allocation is always big enough."); + *(UpcaseName->VersionString.Buffer) = L';'; + + UpcaseName->VersionString.Buffer += 1; + } + } + + // + // Upcase the string using the correct upcase routine. + // + + Status = RtlUpcaseUnicodeString( &UpcaseName->FileName, + &Name->FileName, + FALSE ); + + // + // This should never fail. + // + + NT_ASSERT( Status == STATUS_SUCCESS ); + __analysis_assert( Status == STATUS_SUCCESS ); + + if (Name->VersionString.Length != 0) { + + Status = RtlUpcaseUnicodeString( &UpcaseName->VersionString, + &Name->VersionString, + FALSE ); + + // + // This should never fail. + // + + NT_ASSERT( Status == STATUS_SUCCESS ); + __analysis_assert( Status == STATUS_SUCCESS ); + } + + return; +} + + +VOID +CdDissectName ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PUNICODE_STRING RemainingName, + _Out_ PUNICODE_STRING FinalName + ) + +/*++ + +Routine Description: + + This routine is called to strip off leading components of the name strings. We search + for either the end of the string or separating characters. The input remaining + name strings should have neither a trailing or leading backslash. + +Arguments: + + RemainingName - Remaining name. + + FinalName - Location to store next component of name. + +Return Value: + + None. + +--*/ + +{ + ULONG NameLength; + PWCHAR NextWchar; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Find the offset of the next component separators. + // + + for (NameLength = 0, NextWchar = RemainingName->Buffer; + (NameLength < RemainingName->Length) && (*NextWchar != L'\\'); + NameLength += sizeof( WCHAR) , NextWchar += 1); + + // + // Adjust all the strings by this amount. + // + + FinalName->Buffer = RemainingName->Buffer; + + FinalName->MaximumLength = FinalName->Length = (USHORT) NameLength; + + // + // If this is the last component then set the RemainingName lengths to zero. + // + + if (NameLength == RemainingName->Length) { + + RemainingName->Length = 0; + + // + // Otherwise we adjust the string by this amount plus the separating character. + // + + } else { + + RemainingName->MaximumLength -= (USHORT) (NameLength + sizeof( WCHAR )); + RemainingName->Length -= (USHORT) (NameLength + sizeof( WCHAR )); + RemainingName->Buffer = Add2Ptr( RemainingName->Buffer, + NameLength + sizeof( WCHAR ), + PWCHAR ); + } + + return; +} + + +BOOLEAN +CdIsLegalName ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PUNICODE_STRING FileName + ) + +/*++ + +Routine Description: + + This routine checks if the name is a legal ISO 9660 name. + +Arguments: + + FileName - String of bytes containing the name. + +Return Value: + + BOOLEAN - TRUE if this name is a legal, FALSE otherwise. + +--*/ + +{ + PWCHAR Wchar; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Check if name corresponds to a legal file name. + // + + for (Wchar = FileName->Buffer; + Wchar < Add2Ptr( FileName->Buffer, FileName->Length, PWCHAR ); + Wchar++) { + + if ((*Wchar < 0xff) && + !FsRtlIsAnsiCharacterLegalHpfs( *Wchar, FALSE ) && + (*Wchar != L'"') && + (*Wchar != L'<') && + (*Wchar != L'>') && + (*Wchar != L'|')) { + + return FALSE; + } + } + + return TRUE; +} + + +BOOLEAN +CdIs8dot3Name ( + _In_ PIRP_CONTEXT IrpContext, + _In_ UNICODE_STRING FileName + ) + +/*++ + +Routine Description: + + This routine checks if the name follows the 8.3 name conventions. We check for + the name length and whether the characters are valid. + +Arguments: + + FileName - String of bytes containing the name. + +Return Value: + + BOOLEAN - TRUE if this name is a legal 8.3 name, FALSE otherwise. + +--*/ + +{ + CHAR DbcsNameBuffer[ BYTE_COUNT_8_DOT_3 ]; + STRING DbcsName = {0}; + + PWCHAR NextWchar; + ULONG Count; + + ULONG DotCount = 0; + BOOLEAN LastCharDot = FALSE; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // The length must be less than 24 bytes. + // + + NT_ASSERT( FileName.Length != 0 ); + if (FileName.Length > BYTE_COUNT_8_DOT_3) { + + return FALSE; + } + + // + // Walk though and check for a space character. + // + + NextWchar = FileName.Buffer; + Count = 0; + + do { + + // + // No spaces allowed. + // + + if (*NextWchar == L' ') { return FALSE; } + + if (*NextWchar == L'.') { + + // + // Not an 8.3 name if more than 1 dot or more than 8 characters + // remaining. (It is legal for the dot to be in the ninth + // position) + // + + if ((DotCount > 0) || + (Count > 8 * sizeof( WCHAR ))) { + + return FALSE; + } + + DotCount += 1; + LastCharDot = TRUE; + + } else { + + LastCharDot = FALSE; + } + + Count += 2; + NextWchar += 1; + + } while (Count < FileName.Length); + + // + // Go ahead and truncate the dot if at the end. + // + + if (LastCharDot) { + + FileName.Length -= sizeof( WCHAR ); + } + + // + // Create an Oem name to use to check for a valid short name. + // + + DbcsName.MaximumLength = BYTE_COUNT_8_DOT_3; + DbcsName.Buffer = DbcsNameBuffer; + + if (!NT_SUCCESS( RtlUnicodeStringToCountedOemString( &DbcsName, + &FileName, + FALSE ))) { + + return FALSE; + } + + // + // We have now initialized the Oem string. Call the FsRtl package to check for a + // valid FAT name. + // + + return FsRtlIsFatDbcsLegal( DbcsName, FALSE, FALSE, FALSE ); +} + + +VOID +CdGenerate8dot3Name ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PUNICODE_STRING FileName, + _In_ ULONG DirentOffset, + _Out_writes_bytes_to_(BYTE_COUNT_8_DOT_3, *ShortByteCount) PWCHAR ShortFileName, + _Out_ PUSHORT ShortByteCount + ) + +/*++ + +Routine Description: + + This routine is called to generate a short name from the given long name. We will + generate a short name from the given long name. + + We go through the following steps to make this conversion. + + 1 - Generate the generic short name. This will also be in unicode format. + + 2 - Build the string representation of the dirent offset. + + 3 - Build the biased short name string by combining the generic short name with + the dirent offset string. + + 4 - Copy the final unicode string back to our caller's buffer. + +Arguments: + + FileName - String of bytes containing the name. + + DirentOffset - Offset in the directory for this filename. We incorporate the offset into + the short name by dividing this by 32 and prepending a tilde character to the + digit character. We then append this to the base of the generated short name. + + ShortFileName - Pointer to the buffer to store the short name into. + + ShortByteCount - Address to store the number of bytes in the short name. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + + UNICODE_STRING ShortName; + UNICODE_STRING BiasedShortName; + WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ] = {0}; + WCHAR BiasedShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ]; + + GENERATE_NAME_CONTEXT NameContext; + + ULONG BiasedDirentOffset; + + ULONG MaximumBaseBytes; + ULONG BaseNameOffset; + + PWCHAR NextWchar; + WCHAR ThisWchar; + USHORT Length; + + BOOLEAN FoundTilde = FALSE; + + OEM_STRING OemName = {0}; + USHORT OemNameOffset = 0; + BOOLEAN OverflowBuffer = FALSE; + + PAGED_CODE(); + + // + // Initialize the short string to use the input buffer. + // + + ShortName.Buffer = ShortNameBuffer; + ShortName.MaximumLength = BYTE_COUNT_8_DOT_3; + + // + // Initialize the name context. + // + + RtlZeroMemory( &NameContext, sizeof( GENERATE_NAME_CONTEXT )); + + // + // We now have the unicode name for the input string. Go ahead and generate + // the short name. + // + + RtlGenerate8dot3Name( FileName, TRUE, &NameContext, &ShortName ); + + // + // We now have the generic short name. We want incorporate the dirent offset + // into the name in order to reduce the chance of name conflicts. We will use + // a tilde character followed by a character representation of the dirent offset. + // This will be the hexadecimal representation of the dirent offset in the directory. + // It is actuall this offset divided by 32 since we don't need the full + // granularity. + // + + BiasedDirentOffset = DirentOffset >> SHORT_NAME_SHIFT; + + // + // Point to a local buffer to store the offset string. We start + // at the end of the buffer and work backwards. + // + + NextWchar = Add2Ptr( BiasedShortNameBuffer, + BYTE_COUNT_8_DOT_3, + PWCHAR ); + + BiasedShortName.MaximumLength = BYTE_COUNT_8_DOT_3; + + // + // Generate an OEM version of the string so that we can check for double + // byte characters. + // + + Status = RtlUnicodeStringToOemString(&OemName, &ShortName, TRUE); + + // + // If this failed, bail out. Don't expect any problems other than no mem. + // + + if (!NT_SUCCESS( Status)) { + + NT_ASSERT( STATUS_INSUFFICIENT_RESOURCES == Status); + CdRaiseStatus( IrpContext, Status); + } + + Length = 0; + + // + // Now add the characters for the dirent offset. We need to start + // from the least significant digit and work backwards. + // + + do { + + NextWchar -= 1; + + ThisWchar = (WCHAR) (BiasedDirentOffset & 0x0000000f); + + // + // Store in the next character. Bias against either '0' or 'A' + // + + if (ThisWchar <= 9) { + + *NextWchar = ThisWchar + L'0'; + + } else { + + *NextWchar = ThisWchar + L'A' - 0xA; + } + + Length += sizeof( WCHAR ); + + // + // Shift out the low 4 bits of the offset. + // + + BiasedDirentOffset >>= 4; + + } while (BiasedDirentOffset != 0); + + // + // Now store in the tilde character. + // + + NextWchar -= 1; + *NextWchar = L'~'; + Length += sizeof( WCHAR ); + + // + // Set the length of this string. + // + + BiasedShortName.Length = Length; + BiasedShortName.Buffer = NextWchar; + + // + // Figure out the maximum number of characters we can copy of the base + // name. We subract the number of characters in the dirent string from 8. + // We will copy this many characters or stop when we reach a '.' character + // or a '~' character in the name. + // + + MaximumBaseBytes = 16 - Length; + + BaseNameOffset = 0; + + // + // Keep copying from the base name until we hit a '.', '~' or the end of + // the short name. + // + + NextWchar = ShortFileName; + Length = 0; + + while ((BaseNameOffset < ShortName.Length) && + (ShortName.Buffer[BaseNameOffset / 2] != L'.')) { + + // + // Remember if we found a tilde character in the short name, + // so we don't copy it or anything following it. + // + + if (ShortName.Buffer[BaseNameOffset / 2] == L'~') { + + FoundTilde = TRUE; + } + + // + // We need to consider the DBCS code page, because Unicode characters + // may use 2 bytes as DBCS characters. + // + +#pragma prefast(push) +#pragma prefast(suppress:26014, "OemNameOffset <= BaseNameOffset throughout this loop; OemName buffer previously allocated based on ShortName's length.") + if (FsRtlIsLeadDbcsCharacter(OemName.Buffer[OemNameOffset])) { +#pragma prefast(pop) + + OemNameOffset += 2; + + if ((OemNameOffset + (BiasedShortName.Length / sizeof(WCHAR))) > 8) { + + OverflowBuffer = TRUE; + } + } + else { + + OemNameOffset++; + } + + // + // Only copy the bytes if we still have space for the dirent string. + // + + if (!FoundTilde && !OverflowBuffer && (BaseNameOffset < MaximumBaseBytes)) { + + *NextWchar = ShortName.Buffer[BaseNameOffset / 2]; + Length += sizeof( WCHAR ); + NextWchar += 1; + } + + BaseNameOffset += 2; + } + + RtlFreeOemString(&OemName); + + // + // Now copy the dirent string into the biased name buffer. + // + +#pragma prefast(push) + RtlCopyMemory( NextWchar, + BiasedShortName.Buffer, + BiasedShortName.Length ); +#pragma prefast(pop) + + Length += BiasedShortName.Length; + NextWchar += (BiasedShortName.Length / sizeof( WCHAR )); + + // + // Now copy any remaining bytes over to the biased short name. + // + + if (BaseNameOffset != ShortName.Length) { + + RtlCopyMemory( NextWchar, + &ShortName.Buffer[BaseNameOffset / 2], + ShortName.Length - BaseNameOffset ); + + Length += (ShortName.Length - (USHORT) BaseNameOffset); + } + + // + // The final short name is stored in the user's buffer. + // + + *ShortByteCount = Length; +} + + +BOOLEAN +CdIsNameInExpression ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PCD_NAME CurrentName, + _In_ PCD_NAME SearchExpression, + _In_ ULONG WildcardFlags, + _In_ BOOLEAN CheckVersion + ) + +/*++ + +Routine Description: + + This routine will compare two CdName strings. We assume that if this + is to be a case-insensitive search then they are already upcased. + + We compare the filename portions of the name and if they match we + compare the version strings if requested. + +Arguments: + + CurrentName - Filename from the disk. + + SearchExpression - Filename expression to use for match. + + WildcardFlags - Flags field which indicates which parts of the + search expression might have wildcards. These flags are the + same as in the Ccb flags field. + + CheckVersion - Indicates whether we should check both the name and the + version strings or just the name. + +Return Value: + + BOOLEAN - TRUE if the expressions match, FALSE otherwise. + +--*/ + +{ + BOOLEAN Match = TRUE; + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // If there are wildcards in the expression then we call the + // appropriate FsRtlRoutine. + // + + if (FlagOn( WildcardFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD )) { + + Match = FsRtlIsNameInExpression( &SearchExpression->FileName, + &CurrentName->FileName, + FALSE, + NULL ); + + // + // Otherwise do a direct memory comparison for the name string. + // + + } else { + + if ((CurrentName->FileName.Length != SearchExpression->FileName.Length) || + (!RtlEqualMemory( CurrentName->FileName.Buffer, + SearchExpression->FileName.Buffer, + CurrentName->FileName.Length ))) { + + Match = FALSE; + } + } + + // + // Check the version numbers if requested by the user and we have a + // match on the name and the version number is present. + // + + if (Match && CheckVersion && SearchExpression->VersionString.Length && + !FlagOn( WildcardFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL )) { + + // + // If there are wildcards in the expression then call the + // appropriate search expression. + // + + if (FlagOn( WildcardFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD )) { + + Match = FsRtlIsNameInExpression( &SearchExpression->VersionString, + &CurrentName->VersionString, + FALSE, + NULL ); + + // + // Otherwise do a direct memory comparison for the name string. + // + + } else { + + if ((CurrentName->VersionString.Length != SearchExpression->VersionString.Length) || + (!RtlEqualMemory( CurrentName->VersionString.Buffer, + SearchExpression->VersionString.Buffer, + CurrentName->VersionString.Length ))) { + + Match = FALSE; + } + } + } + + return Match; +} + + +ULONG +CdShortNameDirentOffset ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PUNICODE_STRING Name + ) + +/*++ + +Routine Description: + + This routine is called to examine a name to see if the dirent offset string is contained. + This consists of a tilde character followed by the offset represented as a hexadecimal + characters. We don't do any other checks to see if this is a short name. We + catch that later outside this routine. + +Arguments: + + Name - This is the CdName to examine. + +Return Value: + + ULONG - MAXULONG if there is no valid dirent offset string embedded, otherwise the + convert the value to numeric form. + +--*/ + +{ + ULONG ResultOffset = MAXULONG; + ULONG RemainingByteCount = Name->Length; + + BOOLEAN FoundTilde = FALSE; + + PWCHAR NextWchar; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Walk through the name until we either reach the end of the name + // or find a tilde character. + // + + for (NextWchar = Name->Buffer; + RemainingByteCount != 0; + NextWchar += 1, RemainingByteCount -= sizeof( WCHAR )) { + + // + // Check if this is a dot. Stop constructing any string if + // we found a dot. + // + + if (*NextWchar == L'.') { + + break; + } + + // + // If we already found a tilde then check this character as a + // valid character. It must be a digit or A to F. + // + + if (FoundTilde) { + + if ((*NextWchar < L'0') || + (*NextWchar > L'F') || + ((*NextWchar > L'9') && (*NextWchar < 'A'))) { + + ResultOffset = MAXULONG; + break; + } + + // + // Shift the result by 4 bits and add in this new character. + // + + ResultOffset <<= 4; + + if (*NextWchar < L'A') { + + ResultOffset += *NextWchar - L'0'; + + } else { + + ResultOffset += (*NextWchar - L'A') + 10; + } + + continue; + } + + // + // If this is a tilde then start building the dirent string. + // + + if (*NextWchar == L'~') { + + FoundTilde = TRUE; + ResultOffset = 0; + } + } + + return ResultOffset; +} + + +// +// Local support routine +// + +FSRTL_COMPARISON_RESULT +CdFullCompareNames ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PUNICODE_STRING NameA, + _In_ PUNICODE_STRING NameB + ) + +/*++ + +Routine Description: + + This function compares two names as fast as possible. Note that since + this comparison is case sensitive we can do a direct memory comparison. + +Arguments: + + NameA & NameB - The names to compare. + +Return Value: + + COMPARISON - returns + + LessThan if NameA < NameB lexicalgraphically, + GreaterThan if NameA > NameB lexicalgraphically, + EqualTo if NameA is equal to NameB + +--*/ + +{ + SIZE_T i; + ULONG MinLength = NameA->Length; + FSRTL_COMPARISON_RESULT Result = LessThan; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Figure out the minimum of the two lengths + // + + if (NameA->Length > NameB->Length) { + + MinLength = NameB->Length; + Result = GreaterThan; + + } else if (NameA->Length == NameB->Length) { + + Result = EqualTo; + } + + // + // Loop through looking at all of the characters in both strings + // testing for equalilty, less than, and greater than + // + + i = RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinLength ); + + if (i < MinLength) { + + // + // We know the offset of the first character which is different. + // + + return ((NameA->Buffer[ i / 2 ] < NameB->Buffer[ i / 2 ]) ? + LessThan : + GreaterThan); + } + + // + // The names match up to the length of the shorter string. + // The shorter string lexically appears first. + // + + return Result; +} + + + diff --git a/filesys/cdfs/nodetype.h b/filesys/cdfs/nodetype.h new file mode 100644 index 00000000..41e2eefa --- /dev/null +++ b/filesys/cdfs/nodetype.h @@ -0,0 +1,106 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + NodeType.h + +Abstract: + + This module defines all of the node type codes used in this development + shell. Every major data structure in the file system is assigned a node + type code that is. This code is the first CSHORT in the structure and is + followed by a CSHORT containing the size, in bytes, of the structure. + + +--*/ + +#ifndef _CDNODETYPE_ +#define _CDNODETYPE_ + +typedef USHORT NODE_TYPE_CODE; +typedef NODE_TYPE_CODE *PNODE_TYPE_CODE; + +#define NTC_UNDEFINED ((NODE_TYPE_CODE)0x0000) + +#define CDFS_NTC_DATA_HEADER ((NODE_TYPE_CODE)0x0301) +#define CDFS_NTC_VCB ((NODE_TYPE_CODE)0x0302) +#define CDFS_NTC_FCB_PATH_TABLE ((NODE_TYPE_CODE)0x0303) +#define CDFS_NTC_FCB_INDEX ((NODE_TYPE_CODE)0x0304) +#define CDFS_NTC_FCB_DATA ((NODE_TYPE_CODE)0x0305) +#define CDFS_NTC_FCB_NONPAGED ((NODE_TYPE_CODE)0x0306) +#define CDFS_NTC_CCB ((NODE_TYPE_CODE)0x0307) +#define CDFS_NTC_IRP_CONTEXT ((NODE_TYPE_CODE)0x0308) +#define CDFS_NTC_IRP_CONTEXT_LITE ((NODE_TYPE_CODE)0x0309) + +typedef CSHORT NODE_BYTE_SIZE; + +// +// So all records start with +// +// typedef struct _RECORD_NAME { +// NODE_TYPE_CODE NodeTypeCode; +// NODE_BYTE_SIZE NodeByteSize; +// : +// } RECORD_NAME; +// typedef RECORD_NAME *PRECORD_NAME; +// + +#ifndef NodeType +#define NodeType(P) ((P) != NULL ? (*((PNODE_TYPE_CODE)(P))) : NTC_UNDEFINED) +#endif +#ifndef SafeNodeType +#define SafeNodeType(Ptr) (*((PNODE_TYPE_CODE)(Ptr))) +#endif + +// +// The following definitions are used to generate meaningful blue bugcheck +// screens. On a bugcheck the file system can output 4 ulongs of useful +// information. The first ulong will have encoded in it a source file id +// (in the high word) and the line number of the bugcheck (in the low word). +// The other values can be whatever the caller of the bugcheck routine deems +// necessary. +// +// Each individual file that calls bugcheck needs to have defined at the +// start of the file a constant called BugCheckFileId with one of the +// CDFS_BUG_CHECK_ values defined below and then use CdBugCheck to bugcheck +// the system. +// + +#define CDFS_BUG_CHECK_ACCHKSUP (0x00010000) +#define CDFS_BUG_CHECK_ALLOCSUP (0x00020000) +#define CDFS_BUG_CHECK_CACHESUP (0x00030000) +#define CDFS_BUG_CHECK_CDDATA (0x00040000) +#define CDFS_BUG_CHECK_CDINIT (0x00050000) +#define CDFS_BUG_CHECK_CLEANUP (0x00060000) +#define CDFS_BUG_CHECK_CLOSE (0x00070000) +#define CDFS_BUG_CHECK_CREATE (0x00080000) +#define CDFS_BUG_CHECK_DEVCTRL (0x00090000) +#define CDFS_BUG_CHECK_DEVIOSUP (0x000a0000) +#define CDFS_BUG_CHECK_DIRCTRL (0x000b0000) +#define CDFS_BUG_CHECK_DIRSUP (0x000c0000) +#define CDFS_BUG_CHECK_FILEINFO (0x000d0000) +#define CDFS_BUG_CHECK_FILOBSUP (0x000e0000) +#define CDFS_BUG_CHECK_FSCTRL (0x000f0000) +#define CDFS_BUG_CHECK_FSPDISP (0x00100000) +#define CDFS_BUG_CHECK_LOCKCTRL (0x00110000) +#define CDFS_BUG_CHECK_NAMESUP (0x00120000) +#define CDFS_BUG_CHECK_PATHSUP (0x00130000) +#define CDFS_BUG_CHECK_PNP (0x00140000) +#define CDFS_BUG_CHECK_PREFXSUP (0x00150000) +#define CDFS_BUG_CHECK_READ (0x00160000) +#define CDFS_BUG_CHECK_WRITE (0x00170000) +#define CDFS_BUG_CHECK_RESRCSUP (0x00180000) +#define CDFS_BUG_CHECK_STRUCSUP (0x00190000) +#define CDFS_BUG_CHECK_TIMESUP (0x001a0000) +#define CDFS_BUG_CHECK_VERFYSUP (0x001b0000) +#define CDFS_BUG_CHECK_VOLINFO (0x001c0000) +#define CDFS_BUG_CHECK_WORKQUE (0x001d0000) +#define CDFS_BUG_CHECK_SHUTDOWN (0x001e0000) + + +#define CdBugCheck(A,B,C) { KeBugCheckEx(CDFS_FILE_SYSTEM, BugCheckFileId | __LINE__, A, B, C ); } + +#endif // _NODETYPE_ + diff --git a/filesys/cdfs/pathsup.c b/filesys/cdfs/pathsup.c new file mode 100644 index 00000000..bd5d1dcf --- /dev/null +++ b/filesys/cdfs/pathsup.c @@ -0,0 +1,1001 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + PathSup.c + +Abstract: + + This module implements the Path Table support routines for Cdfs. + + The path table on a CDROM is a condensed summary of the entire + directory structure. It is stored on a number of contiguous sectors + on the disk. Each directory on the disk has an entry in the path + table. The entries are aligned on USHORT boundaries and MAY span + sector boundaries. The entries are stored as a breadth-first search. + + The first entry in the table contains the entry for the root. The + next entries will consist of the contents of the root directory. The + next entries will consist of the all the directories at the next level + of the tree. The children of a given directory will be grouped together. + + The directories are assigned ordinal numbers based on their position in + the path table. The root dirctory is assigned ordinal value 1. + + Path table sectors: + + Ordinal 1 2 3 4 5 6 + +-----------+ + | Spanning | + | Sectors | + +----------------------------+ +------------------------+ + | | | | | | | | | + DirName | \ | a | b |c| | c | d | e | + | | | | | | | | | + Parent #| 1 | 1 | 1 | | | 2 | 2 | 3 | + +----------------------------+ +------------------------+ + + Directory Tree: + + \ (root) + + / \ + / \ + a b + + / \ \ + / \ \ + c d e + + Path Table Entries: + + - Position scan at known offset in the path table. Path Entry at + this offset must exist and is known to be valid. Used when + scanning for the children of a given directory. + + - Position scan at known offset in the path table. Path Entry is + known to start at this location but the bounds must be checked + for validity. + + - Move to next path entry in the table. + + - Update a common path entry structure with the details of the + on-disk structure. This is used to smooth out the differences + in the on-disk structures. + + - Update the filename in the in-memory path entry with the bytes + off the disk. For Joliet disks we will have + to convert to little endian. We assume that directories + don't have version numbers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_PATHSUP) + +// +// Local macros +// + +// +// PRAW_PATH_ENTRY +// CdRawPathEntry ( +// _In_ PIRP_CONTEXT IrpContext, +// _In_ PPATH_ENUM_CONTEXT PathContext +// ); +// + +#define CdRawPathEntry(IC, PC) \ + Add2Ptr( (PC)->Data, (PC)->DataOffset, PRAW_PATH_ENTRY ) + +// +// Local support routines +// + +VOID +CdMapPathTableBlock ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG BaseOffset, + _Inout_ PPATH_ENUM_CONTEXT PathContext + ); + +_Success_(return != FALSE) +BOOLEAN +CdUpdatePathEntryFromRawPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG Ordinal, + _In_ BOOLEAN VerifyBounds, + _In_ PPATH_ENUM_CONTEXT PathContext, + _Out_ PPATH_ENTRY PathEntry + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdFindPathEntry) +#pragma alloc_text(PAGE, CdLookupPathEntry) +#pragma alloc_text(PAGE, CdLookupNextPathEntry) +#pragma alloc_text(PAGE, CdMapPathTableBlock) +#pragma alloc_text(PAGE, CdUpdatePathEntryFromRawPathEntry) +#pragma alloc_text(PAGE, CdUpdatePathEntryName) +#endif + + +VOID +CdLookupPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG PathEntryOffset, + _In_ ULONG Ordinal, + _In_ BOOLEAN VerifyBounds, + _Inout_ PCOMPOUND_PATH_ENTRY CompoundPathEntry + ) + +/*++ + +Routine Description: + + This routine is called to initiate a walk through a path table. We are + looking for a path table entry at location PathEntryOffset. + +Arguments: + + PathEntryOffset - This is our target point in the Path Table. We know that + a path entry must begin at this point although we may have to verify + the bounds. + + Ordinal - Ordinal number for the directory at the PathEntryOffset above. + + VerifyBounds - Indicates whether we need to check the validity of + this entry. + + CompoundPathEntry - PathEnumeration context and in-memory path entry. This + has been initialized outside of this call. + +Return Value: + + None. + +--*/ + +{ + PPATH_ENUM_CONTEXT PathContext = &CompoundPathEntry->PathContext; + LONGLONG CurrentBaseOffset; + + PAGED_CODE(); + + // + // Compute the starting base and starting path table offset. + // + + CurrentBaseOffset = SectorTruncate( PathEntryOffset ); + + // + // Map the next block in the Path Table. + // + + CdMapPathTableBlock( IrpContext, + IrpContext->Vcb->PathTableFcb, + CurrentBaseOffset, + PathContext ); + + // + // Set up our current offset into the Path Context. + // + + PathContext->DataOffset = PathEntryOffset - PathContext->BaseOffset; + + // + // Update the in-memory structure for this path entry. + // + + (VOID) CdUpdatePathEntryFromRawPathEntry( IrpContext, + Ordinal, + VerifyBounds, + &CompoundPathEntry->PathContext, + &CompoundPathEntry->PathEntry ); +} + + +BOOLEAN +CdLookupNextPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PPATH_ENUM_CONTEXT PathContext, + _Inout_ PPATH_ENTRY PathEntry + ) + +/*++ + +Routine Description: + + This routine is called to move to the next path table entry. We know + the offset and the length of the current entry. We start by computing + the offset of the next entry and determine if it is contained in the + table. Then we check to see if we need to move to the next sector in + the path table. We always map two sectors at a time so we don't + have to deal with any path entries which span sectors. We move to + the next sector if we are in the second sector of the current mapped + data block. + + We look up the next entry and update the path entry structure with + the values out of the raw sector but don't update the CdName structure. + +Arguments: + + PathContext - Enumeration context for this scan of the path table. + + PathEntry - In-memory representation of the on-disk path table entry. + +Return Value: + + BOOLEAN - TRUE if another entry is found, FALSE otherwise. + This routine may raise on error. + +--*/ + +{ + LONGLONG CurrentBaseOffset; + + PAGED_CODE(); + + // + // Get the offset of the next path entry within the current + // data block. + // + + PathContext->DataOffset += PathEntry->PathEntryLength; + + // + // If we are in the last data block then check if we are beyond the + // end of the file. + // + + if (PathContext->LastDataBlock) { + + if (PathContext->DataOffset >= PathContext->DataLength) { + + return FALSE; + } + + // + // If we are not in the last data block of the path table and + // this offset is in the second sector then move to the next + // data block. + // + + } else if (PathContext->DataOffset >= SECTOR_SIZE) { + + CurrentBaseOffset = PathContext->BaseOffset + SECTOR_SIZE; + + CdMapPathTableBlock( IrpContext, + IrpContext->Vcb->PathTableFcb, + CurrentBaseOffset, + PathContext ); + + // + // Set up our current offset into the Path Context. + // + + PathContext->DataOffset -= SECTOR_SIZE; + } + + // + // Now update the path entry with the values from the on-disk + // structure. + // + + return CdUpdatePathEntryFromRawPathEntry( IrpContext, + PathEntry->Ordinal + 1, + TRUE, + PathContext, + PathEntry ); +} + +_Success_(return != FALSE) +BOOLEAN +CdFindPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB ParentFcb, + _In_ PCD_NAME DirName, + _In_ BOOLEAN IgnoreCase, + _Inout_ PCOMPOUND_PATH_ENTRY CompoundPathEntry + ) + +/*++ + +Routine Description: + + This routine will walk through the path table looking for a matching entry for DirName + among the child directories of the ParentFcb. + +Arguments: + + ParentFcb - This is the directory we are examining. We know the ordinal and path table + offset for this directory in the path table. If this is the first scan for this + Fcb we will update the first child offset for this directory in the path table. + + DirName - This is the name we are searching for. This name will not contain wildcard + characters. The name will also not have a version string. + + IgnoreCase - Indicates if this search is exact or ignore case. + + CompoundPathEntry - Complete path table enumeration structure. We will have initialized + it for the search on entry. This will be positioned at the matching name if found. + +Return Value: + + BOOLEAN - TRUE if matching entry found, FALSE otherwise. + +--*/ + +{ + BOOLEAN Found = FALSE; + BOOLEAN UpdateChildOffset = TRUE; + + ULONG StartingOffset; + ULONG StartingOrdinal; + + PAGED_CODE(); + + // + // Position ourselves at either the first child or at the directory itself. + // Lock the Fcb to get this value and remember whether to update with the first + // child. + // + + StartingOffset = CdQueryFidPathTableOffset( ParentFcb->FileId ); + StartingOrdinal = ParentFcb->Ordinal; + + // + // ISO 9660 9.4.4 restricts the backpointer from child to parent in a + // pathtable entry to 16bits. Although we internally store ordinals + // as 32bit values, it is impossible to search for the children of a + // directory whose ordinal value is greater than MAXUSHORT. Media that + // could induce such a search is illegal. + // + // Note that it is not illegal to have more than MAXUSHORT directories. + // + + if (ParentFcb->Ordinal > MAXUSHORT) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + CdLockFcb( IrpContext, ParentFcb ); + + if (ParentFcb->ChildPathTableOffset != 0) { + + StartingOffset = ParentFcb->ChildPathTableOffset; + StartingOrdinal = ParentFcb->ChildOrdinal; + UpdateChildOffset = FALSE; + + } else if (ParentFcb == ParentFcb->Vcb->RootIndexFcb) { + + UpdateChildOffset = FALSE; + } + + CdUnlockFcb( IrpContext, ParentFcb ); + + CdLookupPathEntry( IrpContext, StartingOffset, StartingOrdinal, FALSE, CompoundPathEntry ); + + // + // Loop until we find a match or are beyond the children for this directory. + // + + do { + + // + // If we are beyond this directory then return FALSE. + // + + if (CompoundPathEntry->PathEntry.ParentOrdinal > ParentFcb->Ordinal) { + + // + // Update the Fcb with the offsets for the children in the path table. + // + + if (UpdateChildOffset) { + + CdLockFcb( IrpContext, ParentFcb ); + + ParentFcb->ChildPathTableOffset = StartingOffset; + ParentFcb->ChildOrdinal = StartingOrdinal; + + CdUnlockFcb( IrpContext, ParentFcb ); + } + + break; + } + + // + // If we are within the children of this directory then check for a match. + // + + if (CompoundPathEntry->PathEntry.ParentOrdinal == ParentFcb->Ordinal) { + + // + // Update the child offset if not yet done. + // + + if (UpdateChildOffset) { + + CdLockFcb( IrpContext, ParentFcb ); + + ParentFcb->ChildPathTableOffset = CompoundPathEntry->PathEntry.PathTableOffset; + ParentFcb->ChildOrdinal = CompoundPathEntry->PathEntry.Ordinal; + + CdUnlockFcb( IrpContext, ParentFcb ); + + UpdateChildOffset = FALSE; + } + + // + // Update the name in the path entry. + // + + CdUpdatePathEntryName( IrpContext, &CompoundPathEntry->PathEntry, IgnoreCase ); + + // + // Now compare the names for an exact match. + // + + if (CdIsNameInExpression( IrpContext, + &CompoundPathEntry->PathEntry.CdCaseDirName, + DirName, + 0, + FALSE )) { + + // + // Let our caller know we have a match. + // + + Found = TRUE; + break; + } + } + + // + // Go to the next entry in the path table. Remember the current position + // in the event we update the Fcb. + // + + StartingOffset = CompoundPathEntry->PathEntry.PathTableOffset; + StartingOrdinal = CompoundPathEntry->PathEntry.Ordinal; + + } while (CdLookupNextPathEntry( IrpContext, + &CompoundPathEntry->PathContext, + &CompoundPathEntry->PathEntry )); + + return Found; +} + + +// +// Local support routine +// + +VOID +CdMapPathTableBlock ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ LONGLONG BaseOffset, + _Inout_ PPATH_ENUM_CONTEXT PathContext + ) + +/*++ + +Routine Description: + + This routine is called to map (or allocate and copy) the next + data block in the path table. We check if the next block will + span a view boundary and allocate an auxilary buffer in that case. + +Arguments: + + Fcb - This is the Fcb for the Path Table. + + BaseOffset - Offset of the first sector to map. This will be on a + sector boundary. + + PathContext - Enumeration context to update in this routine. + +Return Value: + + None. + +--*/ + +{ + ULONG CurrentLength; + ULONG SectorSize; + ULONG DataOffset; + ULONG PassCount; + PVOID Sector; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Map the new block and set the enumeration context to this + // point. Allocate an auxilary buffer if necessary. + // + + CurrentLength = 2 * SECTOR_SIZE; + + if (CurrentLength >= (ULONG) (Fcb->FileSize.QuadPart - BaseOffset)) { + + CurrentLength = (ULONG) (Fcb->FileSize.QuadPart - BaseOffset); + + // + // We know this is the last data block for this + // path table. + // + + PathContext->LastDataBlock = TRUE; + } + + // + // Set context values. + // + + PathContext->BaseOffset = (ULONG) BaseOffset; + PathContext->DataLength = CurrentLength; + + // + // Drop the previous sector's mapping + // + + CdUnpinData( IrpContext, &PathContext->Bcb ); + + // + // Check if spanning a view section. The following must + // be true before we take this step. + // + // Data length is more than one sector. + // Starting offset must be one sector before the + // cache manager VACB boundary. + // + + if ((CurrentLength > SECTOR_SIZE) && + (FlagOn( ((ULONG) BaseOffset), VACB_MAPPING_MASK ) == LAST_VACB_SECTOR_OFFSET )) { + + // + // Map each sector individually and store into an auxilary + // buffer. + // + + SectorSize = SECTOR_SIZE; + DataOffset = 0; + PassCount = 2; + + PathContext->Data = FsRtlAllocatePoolWithTag( CdPagedPool, + CurrentLength, + TAG_SPANNING_PATH_TABLE ); + PathContext->AllocatedData = TRUE; + + while (PassCount--) { + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &BaseOffset, + SectorSize, + TRUE, + &PathContext->Bcb, + &Sector ); + + RtlCopyMemory( Add2Ptr( PathContext->Data, DataOffset, PVOID ), + Sector, + SectorSize ); + + CdUnpinData( IrpContext, &PathContext->Bcb ); + + BaseOffset += SECTOR_SIZE; + SectorSize = CurrentLength - SECTOR_SIZE; + DataOffset = SECTOR_SIZE; + } + + // + // Otherwise we can just map the data into the cache. + // + + } else { + + // + // There is a slight chance that we have allocated an + // auxilary buffer on the previous sector. + // + + if (PathContext->AllocatedData) { + + CdFreePool( &PathContext->Data ); + PathContext->AllocatedData = FALSE; + } + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &BaseOffset, + CurrentLength, + TRUE, + &PathContext->Bcb, + &PathContext->Data ); + } + + return; +} + + +// +// Local support routine +// +_Success_(return != FALSE) +BOOLEAN +CdUpdatePathEntryFromRawPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _In_ ULONG Ordinal, + _In_ BOOLEAN VerifyBounds, + _In_ PPATH_ENUM_CONTEXT PathContext, + _Out_ PPATH_ENTRY PathEntry + ) + +/*++ + +Routine Description: + + This routine is called to update the in-memory Path Entry from the on-disk + path entry. We also do a careful check of the bounds if requested and we + are in the last data block of the path table. + +Arguments: + + Ordinal - Ordinal number for this directory. + + VerifyBounds - Check that the current raw Path Entry actually fits + within the data block. + + PathContext - Current path table enumeration context. + + PathEntry - Pointer to the in-memory path entry structure. + +Return Value: + + TRUE if updated ok, + FALSE if we've hit the end of the pathtable - zero name length && PT size is a multiple + of blocksize. This is a workaround for some Video CDs. Win 9x works around this. + + This routine may raise. + +--*/ + +{ + PRAW_PATH_ENTRY RawPathEntry = CdRawPathEntry( IrpContext, PathContext ); + ULONG RemainingDataLength; + + PAGED_CODE(); + + // + // Check for a name length of zero. This is the first byte of the record, + // and there must be at least one byte remaining in the buffer else we + // wouldn't be here (caller would have spotted buffer end). + // + + PathEntry->DirNameLen = CdRawPathIdLen( IrpContext, RawPathEntry ); + + if (0 == PathEntry->DirNameLen) { + + // + // If we are in the last block, and the path table size (ie last block) is a + // multiple of block size, then we will consider this the end of the path table + // rather than raising an error. Workaround for NTI Cd Maker video CDs which + // round path table length to blocksize multiple. In all other cases we consider + // a zero length name to be corruption. + // + + if ( PathContext->LastDataBlock && + (0 == BlockOffset( IrpContext->Vcb, PathContext->DataLength))) { + + return FALSE; + } + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + // + // Check if we should verify the path entry. If we are not in the last + // data block then there is nothing to check. + // + + if (PathContext->LastDataBlock && VerifyBounds) { + + // + // Quick check to see if the maximum size is still available. This + // will handle most cases and we don't need to access any of the + // fields. + // + + RemainingDataLength = PathContext->DataLength - PathContext->DataOffset; + + if (RemainingDataLength < sizeof( RAW_PATH_ENTRY )) { + + // + // Make sure the remaining bytes hold the path table entries. + // Do the following checks. + // + // - A minimal path table entry will fit (and then check) + // - This path table entry (with dir name) will fit. + // + + if ((RemainingDataLength < MIN_RAW_PATH_ENTRY_LEN) || + (RemainingDataLength < (ULONG) (CdRawPathIdLen( IrpContext, RawPathEntry ) + MIN_RAW_PATH_ENTRY_LEN - 1))) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + } + } + + // + // The ordinal number of this directory is passed in. + // Compute the path table offset of this entry. + // + + PathEntry->Ordinal = Ordinal; + PathEntry->PathTableOffset = PathContext->BaseOffset + PathContext->DataOffset; + + // + // We know we can safely access all of the fields of the raw path table at + // this point. + + // + // Bias the disk offset by the number of logical blocks + // + + CopyUchar4( &PathEntry->DiskOffset, CdRawPathLoc( IrpContext, RawPathEntry )); + + PathEntry->DiskOffset += CdRawPathXar( IrpContext, RawPathEntry ); + + CopyUchar2( &PathEntry->ParentOrdinal, &RawPathEntry->ParentNum ); + + PathEntry->PathEntryLength = PathEntry->DirNameLen + MIN_RAW_PATH_ENTRY_LEN - 1; + + // + // Align the path entry length on a ushort boundary. + // + + PathEntry->PathEntryLength = WordAlign( PathEntry->PathEntryLength ); + + PathEntry->DirName = (PCHAR)RawPathEntry->DirId; + + return TRUE; +} + + +// +// Local support routine +// + +VOID +CdUpdatePathEntryName ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PPATH_ENTRY PathEntry, + _In_ BOOLEAN IgnoreCase + ) + +/*++ + +Routine Description: + + This routine will store the directory name into the CdName in the + path entry. If this is a Joliet name then we will make sure we have + an allocated buffer and need to convert from big endian to little + endian. We also correctly update the case name. If this operation is ignore + case then we need an auxilary buffer for the name. + + For an Ansi disk we can use the name from the disk for the exact case. We only + need to allocate a buffer for the ignore case name. The on-disk representation of + a Unicode name is useless for us. In this case we will need a name buffer for + both names. We store a buffer in the PathEntry which can hold two 8.3 unicode + names. This means we will almost never need to allocate a buffer in the Ansi case + (we only need one buffer and already have 48 characters). + +Arguments: + + PathEntry - Pointer to a path entry structure. We have already updated + this path entry with the values from the raw path entry. + +Return Value: + + None. + +--*/ + +{ + ULONG Length; + NTSTATUS Status; + + PAGED_CODE(); + + // + // Check if this is a self entry. We use a fixed string for this. + // + // Self-Entry - Length is 1, value is 0. + // + + if ((*PathEntry->DirName == 0) && + (PathEntry->DirNameLen == 1)) { + + // + // There should be no allocated buffers. + // + + NT_ASSERT( !FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER )); + + // + // Now use one of the hard coded directory names. + // + + PathEntry->CdDirName.FileName = CdUnicodeDirectoryNames[0]; + + // + // Show that there is no version number. + // + + PathEntry->CdDirName.VersionString.Length = 0; + + // + // The case name is identical. + // + + PathEntry->CdCaseDirName = PathEntry->CdDirName; + + // + // Return now. + // + + return; + } + + // + // Compute how large a buffer we will need. If this is an ignore + // case operation then we will want a double size buffer. If the disk is not + // a Joliet disk then we might need two bytes for each byte in the name. + // + + Length = PathEntry->DirNameLen; + + if (IgnoreCase) { + + Length *= 2; + } + + if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + Length *= sizeof( WCHAR ); + } + + // + // Now decide if we need to allocate a new buffer. We will if + // this name won't fit in the embedded name buffer and it is + // larger than the current allocated buffer. We always use the + // allocated buffer if present. + // + // If we haven't allocated a buffer then use the embedded buffer if the data + // will fit. This is the typical case. + // + + if (!FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ) && + (Length <= sizeof( PathEntry->NameBuffer ))) { + + PathEntry->CdDirName.FileName.MaximumLength = sizeof( PathEntry->NameBuffer ); + PathEntry->CdDirName.FileName.Buffer = PathEntry->NameBuffer; + + } else { + + // + // We need to use an allocated buffer. Check if the current buffer + // is large enough. + // + + if (Length > PathEntry->CdDirName.FileName.MaximumLength) { + + // + // Free any allocated buffer. + // + + if (FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER )) { + + CdFreePool( &PathEntry->CdDirName.FileName.Buffer ); + ClearFlag( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ); + } + + PathEntry->CdDirName.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, + Length, + TAG_PATH_ENTRY_NAME ); + + SetFlag( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ); + + PathEntry->CdDirName.FileName.MaximumLength = (USHORT) Length; + } + } + + // + // We now have a buffer for the name. We need to either convert the on-disk bigendian + // to little endian or covert the name to Unicode. + // + + if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + Status = RtlOemToUnicodeN( PathEntry->CdDirName.FileName.Buffer, + PathEntry->CdDirName.FileName.MaximumLength, + &Length, + PathEntry->DirName, + PathEntry->DirNameLen ); + + NT_ASSERT( Status == STATUS_SUCCESS ); + __analysis_assert( Status == STATUS_SUCCESS ); + PathEntry->CdDirName.FileName.Length = (USHORT) Length; + + } else { + + // + // Convert this string to little endian. + // + + CdConvertBigToLittleEndian( IrpContext, + PathEntry->DirName, + PathEntry->DirNameLen, + (PCHAR) PathEntry->CdDirName.FileName.Buffer ); + + PathEntry->CdDirName.FileName.Length = (USHORT) PathEntry->DirNameLen; + } + + // + // There is no version string. + // + + PathEntry->CdDirName.VersionString.Length = + PathEntry->CdCaseDirName.VersionString.Length = 0; + + // + // If the name string ends with a period then knock off the last + // character. + // + + if (PathEntry->CdDirName.FileName.Buffer[(PathEntry->CdDirName.FileName.Length - sizeof( WCHAR )) / 2] == L'.') { + + // + // Shrink the filename length. + // + + PathEntry->CdDirName.FileName.Length -= sizeof( WCHAR ); + } + + // + // Update the case name buffer if necessary. If this is an exact case + // operation then just copy the exact case string. + // + + if (IgnoreCase) { + + PathEntry->CdCaseDirName.FileName.Buffer = Add2Ptr( PathEntry->CdDirName.FileName.Buffer, + PathEntry->CdDirName.FileName.MaximumLength / 2, + PWCHAR); + + PathEntry->CdCaseDirName.FileName.MaximumLength = PathEntry->CdDirName.FileName.MaximumLength / 2; + + CdUpcaseName( IrpContext, + &PathEntry->CdDirName, + &PathEntry->CdCaseDirName ); + + } else { + + PathEntry->CdCaseDirName = PathEntry->CdDirName; + } + + return; +} + + diff --git a/filesys/cdfs/pnp.c b/filesys/cdfs/pnp.c new file mode 100644 index 00000000..b3237df3 --- /dev/null +++ b/filesys/cdfs/pnp.c @@ -0,0 +1,833 @@ +/*++ + +Copyright (c) 1997-2000 Microsoft Corporation + +Module Name: + + Pnp.c + +Abstract: + + This module implements the Plug and Play routines for CDFS called by + the dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_PNP) + +_Requires_lock_held_(_Global_critical_region_) +_Releases_nonreentrant_lock_(CdData.DataResource) +NTSTATUS +CdPnpQueryRemove ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _Inout_ PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +_Releases_nonreentrant_lock_(CdData.DataResource) +NTSTATUS +CdPnpRemove ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _Inout_ PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +_Releases_nonreentrant_lock_(CdData.DataResource) +NTSTATUS +CdPnpSurpriseRemove ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _Inout_ PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +_Releases_nonreentrant_lock_(CdData.DataResource) +NTSTATUS +CdPnpCancelRemove ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _Inout_ PVCB Vcb + ); + +// Tell prefast this is a completion routine. +IO_COMPLETION_ROUTINE CdPnpCompletionRoutine; + +NTSTATUS +CdPnpCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonPnp) +#pragma alloc_text(PAGE, CdPnpCancelRemove) +#pragma alloc_text(PAGE, CdPnpQueryRemove) +#pragma alloc_text(PAGE, CdPnpRemove) +#pragma alloc_text(PAGE, CdPnpSurpriseRemove) +#endif + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonPnp ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing PnP operations called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN PassThrough = FALSE; + + PIO_STACK_LOCATION IrpSp; + + PVOLUME_DEVICE_OBJECT OurDeviceObject; + PVCB Vcb; + + PAGED_CODE(); + + // Global lock object is acquired based on internal book-keeping + _Analysis_suppress_lock_checking_(CdData.DataResource); + + // + // Get the current Irp stack location. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Find our Vcb. This is tricky since we have no file object in the Irp. + // + + OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject; + + // + // IO holds a handle reference on our VDO and holds the device lock, which + // syncs us against mounts/verifies. However we hold no reference on the + // volume, which may already have been torn down (and the Vpb freed), for + // example by a force dismount. Check for this condition. We must hold this + // lock until the pnp worker functions take additional locks/refs on the Vcb. + // + + CdAcquireCdData( IrpContext); + + // + // Make sure this device object really is big enough to be a volume device + // object. If it isn't, we need to get out before we try to reference some + // field that takes us past the end of an ordinary device object. + // + +#pragma prefast(suppress: 28175, "this is a filesystem driver, touching the size member is allowed") + if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) || + NodeType( &OurDeviceObject->Vcb ) != CDFS_NTC_VCB) { + + // + // We were called with something we don't understand. + // + + Status = STATUS_INVALID_PARAMETER; + CdReleaseCdData( IrpContext); + CdCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // Force all PnP operations to be synchronous. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + Vcb = &OurDeviceObject->Vcb; + + // + // Check that the Vcb hasn't already been deleted. If so, just pass the + // request through to the driver below, we don't need to do anything. + // + + if (NULL == Vcb->Vpb) { + + PassThrough = TRUE; + } + else { + + // + // Case on the minor code. + // + + switch ( IrpSp->MinorFunction ) { + + case IRP_MN_QUERY_REMOVE_DEVICE: + + Status = CdPnpQueryRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_SURPRISE_REMOVAL: + + Status = CdPnpSurpriseRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_REMOVE_DEVICE: + + Status = CdPnpRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_CANCEL_REMOVE_DEVICE: + + Status = CdPnpCancelRemove( IrpContext, Irp, Vcb ); + break; + + default: + + PassThrough = TRUE; + break; + } + } + + if (PassThrough) { + + CdReleaseCdData( IrpContext); + + // + // Just pass the IRP on. As we do not need to be in the + // way on return, ellide ourselves out of the stack. + // + + IoSkipCurrentIrpStackLocation( Irp ); + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + // + // Cleanup our Irp Context. The driver has completed the Irp. + // + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + } + + return Status; +} + +_Requires_lock_held_(_Global_critical_region_) +_Releases_nonreentrant_lock_(CdData.DataResource) +NTSTATUS +CdPnpQueryRemove ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _Inout_ PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP query remove operation. The filesystem + is responsible for answering whether there are any reasons it sees + that the volume can not go away (and the device removed). Initiation + of the dismount begins when we answer yes to this question. + + Query will be followed by a Cancel or Remove. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being queried. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + BOOLEAN VcbPresent = TRUE; + + PAGED_CODE(); + + ASSERT_EXCLUSIVE_CDDATA; + + // + // Having said yes to a QUERY, any communication with the + // underlying storage stack is undefined (and may block) + // until the bounding CANCEL or REMOVE is sent. + // + // Acquire the global resource so that we can try to vaporize the volume, + // and the vcb resource itself. + // + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Drop a reference on the Vcb to keep it around after we drop the locks. + // + + CdLockVcb( IrpContext, Vcb); + Vcb->VcbReference += 1; + CdUnlockVcb( IrpContext, Vcb); + + CdReleaseCdData( IrpContext); + + Status = CdLockVolumeInternal( IrpContext, Vcb, NULL ); + + // + // Reacquire the global lock, which means dropping the Vcb resource. + // + + CdReleaseVcb( IrpContext, Vcb ); + + CdAcquireCdData( IrpContext ); + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Remove our extra reference. + // + + CdLockVcb( IrpContext, Vcb); + Vcb->VcbReference -= 1; + CdUnlockVcb( IrpContext, Vcb); + + if (NT_SUCCESS( Status )) { + + // + // We need to pass this down before starting the dismount, which + // could disconnect us immediately from the stack. + // + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + IoSetCompletionRoutine( Irp, + CdPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // Now if no one below us failed already, initiate the dismount + // on this volume, make it go away. PnP needs to see our internal + // streams close and drop their references to the target device. + // + // Since we were able to lock the volume, we are guaranteed to + // move this volume into dismount state and disconnect it from + // the underlying storage stack. The force on our part is actually + // unnecesary, though complete. + // + // What is not strictly guaranteed, though, is that the closes + // for the metadata streams take effect synchronously underneath + // of this call. This would leave references on the target device + // even though we are disconnected! + // + + if (NT_SUCCESS( Status )) { + + VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE ); + + NT_ASSERT( !VcbPresent || Vcb->VcbCondition == VcbDismountInProgress ); + } + + // + // Note: Normally everything will complete and the internal streams will + // vaporise. However there is some code in the system which drops additional + // references on fileobjects, including our internal stream file objects, + // for (WMI) tracing purposes. If that happens to run concurrently with our + // teardown, our internal streams will not vaporise until those references + // are removed. So it's possible that the volume still remains at this + // point. The pnp query remove will fail due to our references on the device. + // To be cleaner we will return an error here. We could pend the pnp + // IRP until the volume goes away, but since we don't know when that will + // be, and this is a very rare case, we'll just fail the query. + // + // The reason this is the case is that handles/fileobjects place a reference + // on the device objects they overly. In the filesystem case, these references + // are on our target devices. PnP correcly thinks that if references remain + // on the device objects in the stack that someone has a handle, and that this + // counts as a reason to not succeed the query - even though every interrogated + // driver thinks that it is OK. + // + + if (NT_SUCCESS( Status) && VcbPresent && (Vcb->VcbReference != 0)) { + + Status = STATUS_DEVICE_BUSY; + } + } + + // + // Release the Vcb if it could still remain. + // + + if (VcbPresent) { + + CdReleaseVcb( IrpContext, Vcb ); + } + else { + _Analysis_assume_lock_not_held_(Vcb->VcbResource); + } + + CdReleaseCdData( IrpContext ); + + // + // Cleanup our IrpContext and complete the IRP if neccesary. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + +_Requires_lock_held_(_Global_critical_region_) +_Releases_nonreentrant_lock_(CdData.DataResource) +NTSTATUS +CdPnpRemove ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _Inout_ PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP remove operation. This is our notification + that the underlying storage device for the volume we have is gone, and + an excellent indication that the volume will never reappear. The filesystem + is responsible for initiation or completion the dismount. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being removed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + BOOLEAN VcbPresent = TRUE; + + PAGED_CODE(); + + ASSERT_EXCLUSIVE_CDDATA; + + // + // REMOVE - a storage device is now gone. We either got + // QUERY'd and said yes OR got a SURPRISE OR a storage + // stack failed to spin back up from a sleep/stop state + // (the only case in which this will be the first warning). + // + // Note that it is entirely unlikely that we will be around + // for a REMOVE in the first two cases, as we try to intiate + // dismount. + // + + // + // Acquire the global resource so that we can try to vaporize + // the volume, and the vcb resource itself. + // + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // The device will be going away. Remove our lock and find + // out if we ever had one in the first place. + // + + Status = CdUnlockVolumeInternal( IrpContext, Vcb, NULL ); + + // + // If the volume had not been locked, we must invalidate the + // volume to ensure it goes away properly. The remove will + // succeed. + // + + if (!NT_SUCCESS( Status )) { + + CdLockVcb( IrpContext, Vcb ); + + if (Vcb->VcbCondition != VcbDismountInProgress) { + + CdUpdateVcbCondition( Vcb, VcbInvalid); + } + + CdUnlockVcb( IrpContext, Vcb ); + + Status = STATUS_SUCCESS; + } + + // + // We need to pass this down before starting the dismount, which + // could disconnect us immediately from the stack. + // + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + IoSetCompletionRoutine( Irp, + CdPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // Now make our dismount happen. This may not vaporize the + // Vcb, of course, since there could be any number of handles + // outstanding if we were not preceeded by a QUERY. + // + // PnP will take care of disconnecting this stack if we + // couldn't get off of it immediately. + // + + + VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE ); + + // + // Release the Vcb if it could still remain. + // + + if (VcbPresent) { + + CdReleaseVcb( IrpContext, Vcb ); + } + else { + _Analysis_assume_lock_not_held_(Vcb->VcbResource); + } + + CdReleaseCdData( IrpContext ); + + // + // Cleanup our IrpContext and complete the IRP. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + +_Requires_lock_held_(_Global_critical_region_) +_Releases_nonreentrant_lock_(CdData.DataResource) +NTSTATUS +CdPnpSurpriseRemove ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _Inout_ PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP surprise remove operation. This is another + type of notification that the underlying storage device for the volume we + have is gone, and is excellent indication that the volume will never reappear. + The filesystem is responsible for initiation or completion the dismount. + + For the most part, only "real" drivers care about the distinction of a + surprise remove, which is a result of our noticing that a user (usually) + physically reached into the machine and pulled something out. + + Surprise will be followed by a Remove when all references have been shut down. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being removed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + BOOLEAN VcbPresent = TRUE; + + PAGED_CODE(); + + ASSERT_EXCLUSIVE_CDDATA; + + // + // SURPRISE - a device was physically yanked away without + // any warning. This means external forces. + // + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Invalidate the volume right now. + // + // The intent here is to make every subsequent operation + // on the volume fail and grease the rails toward dismount. + // By definition there is no going back from a SURPRISE. + // + + CdLockVcb( IrpContext, Vcb ); + + if (Vcb->VcbCondition != VcbDismountInProgress) { + + CdUpdateVcbCondition( Vcb, VcbInvalid); + } + + CdUnlockVcb( IrpContext, Vcb ); + + // + // We need to pass this down before starting the dismount, which + // could disconnect us immediately from the stack. + // + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + IoSetCompletionRoutine( Irp, + CdPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // Now make our dismount happen. This may not vaporize the + // Vcb, of course, since there could be any number of handles + // outstanding since this is an out of band notification. + // + + + VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE ); + + // + // Release the Vcb if it could still remain. + // + + if (VcbPresent) { + + CdReleaseVcb( IrpContext, Vcb ); + } + else { + _Analysis_assume_lock_not_held_(Vcb->VcbResource); + } + + CdReleaseCdData( IrpContext ); + + // + // Cleanup our IrpContext and complete the IRP. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + +_Requires_lock_held_(_Global_critical_region_) +_Releases_nonreentrant_lock_(CdData.DataResource) +NTSTATUS +CdPnpCancelRemove ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _Inout_ PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP cancel remove operation. This is our + notification that a previously proposed remove (query) was eventually + vetoed by a component. The filesystem is responsible for cleaning up + and getting ready for more IO. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being removed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + ASSERT_EXCLUSIVE_CDDATA; + + // + // CANCEL - a previous QUERY has been rescinded as a result + // of someone vetoing. Since PnP cannot figure out who may + // have gotten the QUERY (think about it: stacked drivers), + // we must expect to deal with getting a CANCEL without having + // seen the QUERY. + // + // For CDFS, this is quite easy. In fact, we can't get a + // CANCEL if the underlying drivers succeeded the QUERY since + // we disconnect the Vpb on our dismount initiation. This is + // actually pretty important because if PnP could get to us + // after the disconnect we'd be thoroughly unsynchronized + // with respect to the Vcb getting torn apart - merely referencing + // the volume device object is insufficient to keep us intact. + // + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + CdReleaseCdData( IrpContext); + + // + // Unlock the volume. This is benign if we never had seen + // a QUERY. + // + + (VOID) CdUnlockVolumeInternal( IrpContext, Vcb, NULL ); + + CdReleaseVcb( IrpContext, Vcb ); + + // + // Send the request. The underlying driver will complete the + // IRP. Since we don't need to be in the way, simply ellide + // ourselves out of the IRP stack. + // + + IoSkipCurrentIrpStackLocation( Irp ); + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdPnpCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) +{ + PKEVENT Event = (PKEVENT) Contxt; + _Analysis_assume_(Contxt != NULL); + + KeSetEvent( Event, 0, FALSE ); + + return STATUS_MORE_PROCESSING_REQUIRED; + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); + UNREFERENCED_PARAMETER( Contxt ); +} + + diff --git a/filesys/cdfs/prefxsup.c b/filesys/cdfs/prefxsup.c new file mode 100644 index 00000000..6a7b6c59 --- /dev/null +++ b/filesys/cdfs/prefxsup.c @@ -0,0 +1,715 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + PrefxSup.c + +Abstract: + + This module implements the Cdfs Prefix support routines + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_PREFXSUP) + +// +// Local support routines. +// + +PNAME_LINK +CdFindNameLink ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PRTL_SPLAY_LINKS *RootNode, + _In_ PUNICODE_STRING Name + ); + +BOOLEAN +CdInsertNameLink ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PRTL_SPLAY_LINKS *RootNode, + _In_ PNAME_LINK NameLink + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdFindNameLink) +#pragma alloc_text(PAGE, CdFindPrefix) +#pragma alloc_text(PAGE, CdInsertNameLink) +#pragma alloc_text(PAGE, CdInsertPrefix) +#pragma alloc_text(PAGE, CdRemovePrefix) +#endif + + +VOID +CdInsertPrefix ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ PCD_NAME Name, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ShortNameMatch, + _Inout_ PFCB ParentFcb + ) + +/*++ + +Routine Description: + + This routine inserts the names in the given Lcb into the links for the + parent. + +Arguments: + + Fcb - This is the Fcb whose name is being inserted into the tree. + + Name - This is the name for the component. The IgnoreCase flag tells + us which entry this belongs to. + + IgnoreCase - Indicates if we should insert the case-insensitive name. + + ShortNameMatch - Indicates if this is the short name. + + ParentFcb - This is the ParentFcb. The prefix tree is attached to this. + +Return Value: + + None. + +--*/ + +{ + ULONG PrefixFlags; + PNAME_LINK NameLink; + PPREFIX_ENTRY PrefixEntry; + PRTL_SPLAY_LINKS *TreeRoot; + + PWCHAR NameBuffer; + + PAGED_CODE(); + + // + // Check if we need to allocate a prefix entry for the short name. + // If we can't allocate one then fail quietly. We don't have to + // insert the name. + // + + PrefixEntry = &Fcb->FileNamePrefix; + + if (ShortNameMatch) { + + if (Fcb->ShortNamePrefix == NULL) { + + Fcb->ShortNamePrefix = ExAllocatePoolWithTag( CdPagedPool, + sizeof( PREFIX_ENTRY ), + TAG_PREFIX_ENTRY ); + + if (Fcb->ShortNamePrefix == NULL) { return; } + + RtlZeroMemory( Fcb->ShortNamePrefix, sizeof( PREFIX_ENTRY )); + } + + PrefixEntry = Fcb->ShortNamePrefix; + } + + // + // Capture the local variables for the separate cases. + // + + if (IgnoreCase) { + + PrefixFlags = PREFIX_FLAG_IGNORE_CASE_IN_TREE; + NameLink = &PrefixEntry->IgnoreCaseName; + TreeRoot = &ParentFcb->IgnoreCaseRoot; + + } else { + + PrefixFlags = PREFIX_FLAG_EXACT_CASE_IN_TREE; + NameLink = &PrefixEntry->ExactCaseName; + TreeRoot = &ParentFcb->ExactCaseRoot; + } + + // + // If neither name is in the tree then check whether we have a buffer for this + // name + // + + if (!FlagOn( PrefixEntry->PrefixFlags, + PREFIX_FLAG_EXACT_CASE_IN_TREE | PREFIX_FLAG_IGNORE_CASE_IN_TREE )) { + + // + // Allocate a new buffer if the embedded buffer is too small. + // + + NameBuffer = PrefixEntry->FileNameBuffer; + + if (Name->FileName.Length > BYTE_COUNT_EMBEDDED_NAME) { + + NameBuffer = ExAllocatePoolWithTag( CdPagedPool, + Name->FileName.Length * 2, + TAG_PREFIX_NAME ); + + // + // Exit if no name buffer. + // + + if (NameBuffer == NULL) { return; } + } + + // + // Split the buffer and fill in the separate components. + // + + PrefixEntry->ExactCaseName.FileName.Buffer = NameBuffer; + PrefixEntry->IgnoreCaseName.FileName.Buffer = Add2Ptr( NameBuffer, + Name->FileName.Length, + PWCHAR ); + + PrefixEntry->IgnoreCaseName.FileName.MaximumLength = + PrefixEntry->IgnoreCaseName.FileName.Length = + PrefixEntry->ExactCaseName.FileName.MaximumLength = + PrefixEntry->ExactCaseName.FileName.Length = Name->FileName.Length; + } + + // + // Only insert the name if not already present. + // + + if (!FlagOn( PrefixEntry->PrefixFlags, PrefixFlags )) { + + // + // Initialize the name in the prefix entry. + // + + RtlCopyMemory( NameLink->FileName.Buffer, + Name->FileName.Buffer, + Name->FileName.Length ); + + CdInsertNameLink( IrpContext, + TreeRoot, + NameLink ); + + PrefixEntry->Fcb = Fcb; + SetFlag( PrefixEntry->PrefixFlags, PrefixFlags ); + } + + return; +} + + +VOID +CdRemovePrefix ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called to remove all of the previx entries of a + given Fcb from its parent Fcb. + +Arguments: + + Fcb - Fcb whose entries are to be removed. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Start with the short name prefix entry. + // + + if (Fcb->ShortNamePrefix != NULL) { + + if (FlagOn( Fcb->ShortNamePrefix->PrefixFlags, PREFIX_FLAG_IGNORE_CASE_IN_TREE )) { + + Fcb->ParentFcb->IgnoreCaseRoot = RtlDelete( &Fcb->ShortNamePrefix->IgnoreCaseName.Links ); + } + + if (FlagOn( Fcb->ShortNamePrefix->PrefixFlags, PREFIX_FLAG_EXACT_CASE_IN_TREE )) { + + Fcb->ParentFcb->ExactCaseRoot = RtlDelete( &Fcb->ShortNamePrefix->ExactCaseName.Links ); + } + + ClearFlag( Fcb->ShortNamePrefix->PrefixFlags, + PREFIX_FLAG_IGNORE_CASE_IN_TREE | PREFIX_FLAG_EXACT_CASE_IN_TREE ); + } + + // + // Now do the long name prefix entries. + // + + if (FlagOn( Fcb->FileNamePrefix.PrefixFlags, PREFIX_FLAG_IGNORE_CASE_IN_TREE )) { + + Fcb->ParentFcb->IgnoreCaseRoot = RtlDelete( &Fcb->FileNamePrefix.IgnoreCaseName.Links ); + } + + if (FlagOn( Fcb->FileNamePrefix.PrefixFlags, PREFIX_FLAG_EXACT_CASE_IN_TREE )) { + + Fcb->ParentFcb->ExactCaseRoot = RtlDelete( &Fcb->FileNamePrefix.ExactCaseName.Links ); + } + + ClearFlag( Fcb->FileNamePrefix.PrefixFlags, + PREFIX_FLAG_IGNORE_CASE_IN_TREE | PREFIX_FLAG_EXACT_CASE_IN_TREE ); + + // + // Deallocate any buffer we may have allocated. + // + + if ((Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer != (PWCHAR) &Fcb->FileNamePrefix.FileNameBuffer) && + (Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer != NULL)) { + + CdFreePool( &Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer ); + Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer = NULL; + } + + return; +} + + + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdFindPrefix ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB *CurrentFcb, + _Inout_ PUNICODE_STRING RemainingName, + _In_ BOOLEAN IgnoreCase + ) + +/*++ + +Routine Description: + + This routine begins from the given CurrentFcb and walks through all of + components of the name looking for the longest match in the prefix + splay trees. The search is relative to the starting Fcb so the + full name may not begin with a '\'. On return this routine will + update Current Fcb with the lowest point it has travelled in the + tree. It will also hold only that resource on return and it must + hold that resource. + +Arguments: + + CurrentFcb - Address to store the lowest Fcb we find on this search. + On return we will have acquired this Fcb. On entry this is the + Fcb to examine. + + RemainingName - Supplies a buffer to store the exact case of the name being + searched for. Initially will contain the upcase name based on the + IgnoreCase flag. + + IgnoreCase - Indicates if we are doing a case-insensitive compare. + +Return Value: + + None + +--*/ + +{ + UNICODE_STRING LocalRemainingName; + + UNICODE_STRING FinalName; + + PNAME_LINK NameLink; + PPREFIX_ENTRY PrefixEntry; + + PAGED_CODE(); + + // + // Make a local copy of the input strings. + // + + LocalRemainingName = *RemainingName; + + // + // Loop until we find the longest matching prefix. + // + + while (TRUE) { + + // + // If there are no characters left or we are not at an IndexFcb then + // return immediately. + // + + if ((LocalRemainingName.Length == 0) || + (SafeNodeType( *CurrentFcb ) != CDFS_NTC_FCB_INDEX)) { + + return; + } + + // + // Split off the next component from the name. + // + + CdDissectName( IrpContext, + &LocalRemainingName, + &FinalName ); + + // + // Check if this name is in the splay tree for this Scb. + // + + if (IgnoreCase) { + + NameLink = CdFindNameLink( IrpContext, + &(*CurrentFcb)->IgnoreCaseRoot, + &FinalName ); + + // + // Get the prefix entry from this NameLink. Don't access any + // fields within it until we verify we have a name link. + // + + PrefixEntry = (PPREFIX_ENTRY) CONTAINING_RECORD( NameLink, + PREFIX_ENTRY, + IgnoreCaseName ); + + } else { + + NameLink = CdFindNameLink( IrpContext, + &(*CurrentFcb)->ExactCaseRoot, + &FinalName ); + + PrefixEntry = (PPREFIX_ENTRY) CONTAINING_RECORD( NameLink, + PREFIX_ENTRY, + ExactCaseName ); + } + + // + // If we didn't find a match then exit. + // + + if (NameLink == NULL) { return; } + + // + // If this is a case-insensitive match then copy the exact case of the name into + // the input buffer. + // + + if (IgnoreCase) { + + RtlCopyMemory( FinalName.Buffer, + PrefixEntry->ExactCaseName.FileName.Buffer, + PrefixEntry->ExactCaseName.FileName.Length ); + } + + // + // Update the caller's remaining name string to reflect the fact that we found + // a match. + // + + *RemainingName = LocalRemainingName; + + // + // Move down to the next component in the tree. Acquire without waiting. + // If this fails then lock the Fcb to reference this Fcb and then drop + // the parent and acquire the child. + // + + if (!CdAcquireFcbExclusive( IrpContext, PrefixEntry->Fcb, TRUE )) { + + // + // If we can't wait then raise CANT_WAIT. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + CdLockVcb( IrpContext, IrpContext->Vcb ); + PrefixEntry->Fcb->FcbReference += 1; + CdUnlockVcb( IrpContext, IrpContext->Vcb ); + + CdReleaseFcb( IrpContext, *CurrentFcb ); + CdAcquireFcbExclusive( IrpContext, PrefixEntry->Fcb, FALSE ); + + CdLockVcb( IrpContext, IrpContext->Vcb ); + PrefixEntry->Fcb->FcbReference -= 1; + CdUnlockVcb( IrpContext, IrpContext->Vcb ); + + } else { + + CdReleaseFcb( IrpContext, *CurrentFcb ); + } + + *CurrentFcb = PrefixEntry->Fcb; + } +} + + +// +// Local support routine +// + +PNAME_LINK +CdFindNameLink ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PRTL_SPLAY_LINKS *RootNode, + _In_ PUNICODE_STRING Name + ) + +/*++ + +Routine Description: + + This routine searches through a splay link tree looking for a match for the + input name. If we find the corresponding name we will rebalance the + tree. + +Arguments: + + RootNode - Supplies the parent to search. + + Name - This is the name to search for. Note if we are doing a case + insensitive search the name would have been upcased already. + +Return Value: + + PNAME_LINK - The name link found or NULL if there is no match. + +--*/ + +{ + FSRTL_COMPARISON_RESULT Comparison; + PNAME_LINK Node; + PRTL_SPLAY_LINKS Links; + + PAGED_CODE(); + + Links = *RootNode; + + while (Links != NULL) { + + Node = CONTAINING_RECORD( Links, NAME_LINK, Links ); + + // + // Compare the prefix in the tree with the full name + // + + Comparison = CdFullCompareNames( IrpContext, &Node->FileName, Name ); + + // + // See if they don't match + // + + if (Comparison == GreaterThan) { + + // + // The prefix is greater than the full name + // so we go down the left child + // + + Links = RtlLeftChild( Links ); + + // + // And continue searching down this tree + // + + } else if (Comparison == LessThan) { + + // + // The prefix is less than the full name + // so we go down the right child + // + + Links = RtlRightChild( Links ); + + // + // And continue searching down this tree + // + + } else { + + // + // We found it. + // + // Splay the tree and save the new root. + // + + *RootNode = RtlSplay( Links ); + + return Node; + } + } + + // + // We didn't find the Link. + // + + return NULL; +} + + +// +// Local support routine +// + +BOOLEAN +CdInsertNameLink ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PRTL_SPLAY_LINKS *RootNode, + _In_ PNAME_LINK NameLink + ) + +/*++ + +Routine Description: + + This routine will insert a name in the splay tree pointed to + by RootNode. + + The name could already exist in this tree for a case-insensitive tree. + In that case we simply return FALSE and do nothing. + +Arguments: + + RootNode - Supplies a pointer to the table. + + NameLink - Contains the new link to enter. + +Return Value: + + BOOLEAN - TRUE if the name is inserted, FALSE otherwise. + +--*/ + +{ + FSRTL_COMPARISON_RESULT Comparison; + PNAME_LINK Node; + + PAGED_CODE(); + + RtlInitializeSplayLinks( &NameLink->Links ); + + // + // If we are the first entry in the tree, just become the root. + // + + if (*RootNode == NULL) { + + *RootNode = &NameLink->Links; + + return TRUE; + } + + Node = CONTAINING_RECORD( *RootNode, NAME_LINK, Links ); + + while (TRUE) { + + // + // Compare the prefix in the tree with the prefix we want + // to insert. + // + + Comparison = CdFullCompareNames( IrpContext, &Node->FileName, &NameLink->FileName ); + + // + // If we found the entry, return immediately. + // + + if (Comparison == EqualTo) { return FALSE; } + + // + // If the tree prefix is greater than the new prefix then + // we go down the left subtree + // + + if (Comparison == GreaterThan) { + + // + // We want to go down the left subtree, first check to see + // if we have a left subtree + // + + if (RtlLeftChild( &Node->Links ) == NULL) { + + // + // there isn't a left child so we insert ourselves as the + // new left child + // + + RtlInsertAsLeftChild( &Node->Links, &NameLink->Links ); + + // + // and exit the while loop + // + + break; + + } else { + + // + // there is a left child so simply go down that path, and + // go back to the top of the loop + // + + Node = CONTAINING_RECORD( RtlLeftChild( &Node->Links ), + NAME_LINK, + Links ); + } + + } else { + + // + // The tree prefix is either less than or a proper prefix + // of the new string. We treat both cases as less than when + // we do insert. So we want to go down the right subtree, + // first check to see if we have a right subtree + // + + if (RtlRightChild( &Node->Links ) == NULL) { + + // + // These isn't a right child so we insert ourselves as the + // new right child + // + + RtlInsertAsRightChild( &Node->Links, &NameLink->Links ); + + // + // and exit the while loop + // + + break; + + } else { + + // + // there is a right child so simply go down that path, and + // go back to the top of the loop + // + + Node = CONTAINING_RECORD( RtlRightChild( &Node->Links ), + NAME_LINK, + Links ); + } + } + } + + return TRUE; +} + + + + + diff --git a/filesys/cdfs/read.c b/filesys/cdfs/read.c new file mode 100644 index 00000000..43e8f9ce --- /dev/null +++ b/filesys/cdfs/read.c @@ -0,0 +1,566 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Read.c + +Abstract: + + This module implements the File Read routine for Read called by the + Fsd/Fsp dispatch drivers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_READ) + +// +// VOID +// SafeZeroMemory ( +// _Out_ PUCHAR At, +// _In_ ULONG ByteCount +// ); +// + +// +// This macro just puts a nice little try-except around RtlZeroMemory +// + +#define SafeZeroMemory(IC,AT,BYTE_COUNT) { \ + try { \ + RtlZeroMemory( (AT), (BYTE_COUNT) ); \ +__pragma(warning(suppress: 6320)) \ + } except( EXCEPTION_EXECUTE_HANDLER ) { \ + CdRaiseStatus( IC, STATUS_INVALID_USER_BUFFER ); \ + } \ +} + +// +// Read ahead amount used for normal data files +// + +#define READ_AHEAD_GRANULARITY (0x10000) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonRead) +#endif + + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonRead ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common entry point for NtReadFile calls. For synchronous requests, + CommonRead will complete the request in the current thread. If not + synchronous the request will be passed to the Fsp if there is a need to + block. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The result of this operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN Wait; + ULONG PagingIo; + ULONG SynchronousIo; + ULONG NonCachedIo; + PVOID UserBuffer; + + LONGLONG StartingOffset; + LONGLONG ByteRange; + ULONG ByteCount; + ULONG ReadByteCount; + ULONG OriginalByteCount; + + PVOID SystemBuffer; + + BOOLEAN ReleaseFile = TRUE; + + CD_IO_CONTEXT LocalIoContext; + + PAGED_CODE(); + + // + // If this is a zero length read then return SUCCESS immediately. + // + + if (IrpSp->Parameters.Read.Length == 0) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Decode the file object and verify we support read on this. It + // must be a user file, stream file or volume file (for a data disk). + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + // Internal lock object is acquired if return status is STATUS_PENDING + _Analysis_suppress_lock_checking_(Fcb->Resource); + + if ((TypeOfOpen == UnopenedFileObject) || + (TypeOfOpen == UserDirectoryOpen)) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Examine our input parameters to determine if this is noncached and/or + // a paging io operation. + // + + Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + PagingIo = FlagOn( Irp->Flags, IRP_PAGING_IO ); + NonCachedIo = FlagOn( Irp->Flags, IRP_NOCACHE ); + SynchronousIo = FlagOn( IrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO ); + + + // + // Extract the range of the Io. + // + + StartingOffset = IrpSp->Parameters.Read.ByteOffset.QuadPart; + OriginalByteCount = ByteCount = IrpSp->Parameters.Read.Length; + + ByteRange = StartingOffset + ByteCount; + + // + // Make sure that Dasd access is always non-cached. + // + + if (TypeOfOpen == UserVolumeOpen) { + + NonCachedIo = TRUE; + } + + // + // Acquire the file shared to perform the read. If we are doing paging IO, + // it may be the case that we would have a deadlock imminent because we may + // block on shared access, so starve out any exclusive waiters. This requires + // a degree of caution - we believe that any paging IO bursts will recede and + // allow the exclusive waiter in. + // + + if (PagingIo) { + + CdAcquireFileSharedStarveExclusive( IrpContext, Fcb ); + + } else { + + CdAcquireFileShared( IrpContext, Fcb ); + } + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify the Fcb. Allow reads if this is a DASD handle that is + // dismounting the volume. + // + + if ((TypeOfOpen != UserVolumeOpen) || (NULL == Ccb) || + !FlagOn( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE)) { + + CdVerifyFcbOperation( IrpContext, Fcb ); + } + + // + // If this is a non-cached then check whether we need to post this + // request if this thread can't block. + // + + if (!Wait && NonCachedIo) { + + // + // XA requests must always be waitable. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_RAWSECTOR_MASK )) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST ); + try_return( Status = STATUS_CANT_WAIT ); + } + } + + // + // If this is a user request then verify the oplock and filelock state. + // + + if (TypeOfOpen == UserFileOpen) { + + // + // We check whether we can proceed + // based on the state of the file oplocks. + // + + Status = FsRtlCheckOplock( CdGetFcbOplock(Fcb), + Irp, + IrpContext, + CdOplockComplete, + CdPrePostIrp ); + + // + // If the result is not STATUS_SUCCESS then the Irp was completed + // elsewhere. + // + + if (Status != STATUS_SUCCESS) { + + Irp = NULL; + IrpContext = NULL; + + try_return( NOTHING ); + } + + if (!PagingIo && + (Fcb->FileLock != NULL) && + !FsRtlCheckLockForReadAccess( Fcb->FileLock, Irp )) { + + try_return( Status = STATUS_FILE_LOCK_CONFLICT ); + } + } + + // + // Check request beyond end of file if this is not a read on a volume + // handle marked for extended DASD IO. + // + + if ((TypeOfOpen != UserVolumeOpen) || + (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO ))) { + + // + // Complete the request if it begins beyond the end of file. + // + + if (StartingOffset >= Fcb->FileSize.QuadPart) { + + try_return( Status = STATUS_END_OF_FILE ); + } + + // + // Truncate the read if it extends beyond the end of the file. + // + + if (ByteRange > Fcb->FileSize.QuadPart) { + + ByteCount = (ULONG) (Fcb->FileSize.QuadPart - StartingOffset); + ByteRange = Fcb->FileSize.QuadPart; + } + } + + // + // Handle the non-cached read first. + // + + if (NonCachedIo) { + + // + // If we have an unaligned transfer then post this request if + // we can't wait. Unaligned means that the starting offset + // is not on a sector boundary or the read is not integral + // sectors. + // + + ReadByteCount = BlockAlign( Fcb->Vcb, ByteCount ); + + if (SectorOffset( StartingOffset ) || + SectorOffset( ReadByteCount ) || + (ReadByteCount > OriginalByteCount)) { + + if (!Wait) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Make sure we don't overwrite the buffer. + // + + ReadByteCount = ByteCount; + } + + // + // Initialize the IoContext for the read. + // If there is a context pointer, we need to make sure it was + // allocated and not a stale stack pointer. + // + + if (IrpContext->IoContext == NULL || + !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO )) { + + // + // If we can wait, use the context on the stack. Otherwise + // we need to allocate one. + // + + if (Wait) { + + IrpContext->IoContext = &LocalIoContext; + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + + } else { + + IrpContext->IoContext = CdAllocateIoContext(); + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + } + } + + RtlZeroMemory( IrpContext->IoContext, sizeof( CD_IO_CONTEXT )); + + // + // Store whether we allocated this context structure in the structure + // itself. + // + + IrpContext->IoContext->AllocatedContext = + BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + + if (Wait) { + + KeInitializeEvent( &IrpContext->IoContext->SyncEvent, + NotificationEvent, + FALSE ); + + } else { + + IrpContext->IoContext->ResourceThreadId = ExGetCurrentResourceThread(); + IrpContext->IoContext->Resource = Fcb->Resource; + IrpContext->IoContext->RequestedByteCount = ByteCount; + } + + Irp->IoStatus.Information = ReadByteCount; + + // + // Call one of the NonCacheIo routines to perform the actual + // read. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_RAWSECTOR_MASK )) { + + Status = CdNonCachedXARead( IrpContext, Fcb, StartingOffset, ReadByteCount ); + + } else { + + Status = CdNonCachedRead( IrpContext, Fcb, StartingOffset, ReadByteCount ); + } + + // + // Don't complete this request now if STATUS_PENDING was returned. + // + + if (Status == STATUS_PENDING) { + + Irp = NULL; + ReleaseFile = FALSE; + + // + // Test is we should zero part of the buffer or update the + // synchronous file position. + // + + } else { + + // + // Convert any unknown error code to IO_ERROR. + // + + if (!NT_SUCCESS( Status )) { + + // + // Set the information field to zero. + // + + Irp->IoStatus.Information = 0; + + // + // Raise if this is a user induced error. + // + + if (IoIsErrorUserInduced( Status )) { + + CdRaiseStatus( IrpContext, Status ); + } + + Status = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR ); + + // + // Check if there is any portion of the user's buffer to zero. + // + + } else if (ReadByteCount != ByteCount) { + + CdMapUserBuffer( IrpContext, &UserBuffer); + + SafeZeroMemory( IrpContext, + Add2Ptr( UserBuffer, + ByteCount, + PVOID ), + ReadByteCount - ByteCount ); + + Irp->IoStatus.Information = ByteCount; + } + + // + // Update the file position if this is a synchronous request. + // + + if (SynchronousIo && !PagingIo && NT_SUCCESS( Status )) { + + IrpSp->FileObject->CurrentByteOffset.QuadPart = ByteRange; + } + } + + try_return( NOTHING ); + } + + // + // Handle the cached case. Start by initializing the private + // cache map. + // + + if (IrpSp->FileObject->PrivateCacheMap == NULL) { + + // + // Now initialize the cache map. + // + + CcInitializeCacheMap( IrpSp->FileObject, + (PCC_FILE_SIZES) &Fcb->AllocationSize, + FALSE, + &CdData.CacheManagerCallbacks, + Fcb ); + + CcSetReadAheadGranularity( IrpSp->FileObject, READ_AHEAD_GRANULARITY ); + } + + // + // Read from the cache if this is not an Mdl read. + // + + if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL )) { + + // + // If we are in the Fsp now because we had to wait earlier, + // we must map the user buffer, otherwise we can use the + // user's buffer directly. + // + + CdMapUserBuffer( IrpContext, &SystemBuffer ); + + // + // Now try to do the copy. + // + + if (!CcCopyRead( IrpSp->FileObject, + (PLARGE_INTEGER) &StartingOffset, + ByteCount, + Wait, + SystemBuffer, + &Irp->IoStatus )) { + + try_return( Status = STATUS_CANT_WAIT ); + } + + // + // If the call didn't succeed, raise the error status + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + CdNormalizeAndRaiseStatus( IrpContext, Irp->IoStatus.Status ); + } + + // + // Otherwise perform the MdlRead operation. + // + + } else { + + CcMdlRead( IrpSp->FileObject, + (PLARGE_INTEGER) &StartingOffset, + ByteCount, + &Irp->MdlAddress, + &Irp->IoStatus ); + + Status = Irp->IoStatus.Status; + } + + // + // Update the current file position in the user file object. + // + + if (SynchronousIo && !PagingIo && NT_SUCCESS( Status )) { + + IrpSp->FileObject->CurrentByteOffset.QuadPart = ByteRange; + } + + try_exit: NOTHING; + } finally { + + // + // Release the Fcb. + // + + if (ReleaseFile) { + + CdReleaseFile( IrpContext, Fcb ); + } + } + + // + // Post the request if we got CANT_WAIT. + // + + if (Status == STATUS_CANT_WAIT) { + + Status = CdFsdPostRequest( IrpContext, Irp ); + + // + // Otherwise complete the request. + // + + } else { + + CdCompleteRequest( IrpContext, Irp, Status ); + } + + return Status; +} + + + diff --git a/filesys/cdfs/resrcsup.c b/filesys/cdfs/resrcsup.c new file mode 100644 index 00000000..86b2b91a --- /dev/null +++ b/filesys/cdfs/resrcsup.c @@ -0,0 +1,389 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + ResrcSup.c + +Abstract: + + This module implements the Cdfs Resource acquisition routines + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_RESRCSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdAcquireForCache) +#pragma alloc_text(PAGE, CdFilterCallbackAcquireForCreateSection) +#pragma alloc_text(PAGE, CdAcquireResource) +#pragma alloc_text(PAGE, CdNoopAcquire) +#pragma alloc_text(PAGE, CdNoopRelease) +#pragma alloc_text(PAGE, CdReleaseForCreateSection) +#pragma alloc_text(PAGE, CdReleaseFromCache) +#endif + + + +_Requires_lock_held_(_Global_critical_region_) +_When_(Type == AcquireExclusive && return != FALSE, _Acquires_exclusive_lock_(*Resource)) +_When_(Type == AcquireShared && return != FALSE, _Acquires_shared_lock_(*Resource)) +_When_(Type == AcquireSharedStarveExclusive && return != FALSE, _Acquires_shared_lock_(*Resource)) +_When_(IgnoreWait == FALSE, _Post_satisfies_(return == TRUE)) +BOOLEAN +CdAcquireResource ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PERESOURCE Resource, + _In_ BOOLEAN IgnoreWait, + _In_ TYPE_OF_ACQUIRE Type + ) + +/*++ + +Routine Description: + + This is the single routine used to acquire file system resources. It + looks at the IgnoreWait flag to determine whether to try to acquire the + resource without waiting. Returning TRUE/FALSE to indicate success or + failure. Otherwise it is driven by the WAIT flag in the IrpContext and + will raise CANT_WAIT on a failure. + +Arguments: + + Resource - This is the resource to try and acquire. + + IgnoreWait - If TRUE then this routine will not wait to acquire the + resource and will return a boolean indicating whether the resource was + acquired. Otherwise we use the flag in the IrpContext and raise + if the resource is not acquired. + + Type - Indicates how we should try to get the resource. + +Return Value: + + BOOLEAN - TRUE if the resource is acquired. FALSE if not acquired and + IgnoreWait is specified. Otherwise we raise CANT_WAIT. + +--*/ + +{ + BOOLEAN Wait = FALSE; + BOOLEAN Acquired; + PAGED_CODE(); + + // + // We look first at the IgnoreWait flag, next at the flag in the Irp + // Context to decide how to acquire this resource. + // + + if (!IgnoreWait && FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + Wait = TRUE; + } + + // + // Attempt to acquire the resource either shared or exclusively. + // + + switch (Type) { + case AcquireExclusive: + +#pragma prefast( suppress:28137, "prefast believes Wait should be a constant, but this is ok for CDFS" ) + Acquired = ExAcquireResourceExclusiveLite( Resource, Wait ); + break; + + case AcquireShared: + + Acquired = ExAcquireResourceSharedLite( Resource, Wait ); + break; + + case AcquireSharedStarveExclusive: + + Acquired = ExAcquireSharedStarveExclusive( Resource, Wait ); + break; + + default: + Acquired = FALSE; + NT_ASSERT( FALSE ); + } + + // + // If not acquired and the user didn't specifiy IgnoreWait then + // raise CANT_WAIT. + // + + if (!Acquired && !IgnoreWait) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + return Acquired; +} + + + +_Requires_lock_held_(_Global_critical_region_) +_When_(return!=0, _Acquires_shared_lock_(*Fcb->Resource)) +BOOLEAN +CdAcquireForCache ( + _Inout_ PFCB Fcb, + _In_ BOOLEAN Wait + ) + +/*++ + +Routine Description: + + The address of this routine is specified when creating a CacheMap for + a file. It is subsequently called by the Lazy Writer for synchronization. + +Arguments: + + Fcb - The pointer supplied as context to the cache initialization + routine. + + Wait - TRUE if the caller is willing to block. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + NT_ASSERT(IoGetTopLevelIrp() == NULL); + IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + return ExAcquireResourceSharedLite( Fcb->Resource, Wait ); +} + + +_Requires_lock_held_(_Global_critical_region_) +_Releases_lock_(*Fcb->Resource) +VOID +CdReleaseFromCache ( + _Inout_ PFCB Fcb + ) + +/*++ + +Routine Description: + + The address of this routine is specified when creating a CacheMap for + a virtual file. It is subsequently called by the Lazy Writer to release + a resource acquired above. + +Arguments: + + Fcb - The pointer supplied as context to the cache initialization + routine. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + NT_ASSERT(IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + IoSetTopLevelIrp( NULL ); + + ExReleaseResourceLite( Fcb->Resource ); +} + + +BOOLEAN +CdNoopAcquire ( + _In_ PVOID Fcb, + _In_ BOOLEAN Wait + ) + +/*++ + +Routine Description: + + This routine does nothing. + +Arguments: + + Fcb - The Fcb/Vcb which was specified as a context parameter for this + routine. + + Wait - TRUE if the caller is willing to block. + +Return Value: + + TRUE + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Fcb ); + UNREFERENCED_PARAMETER( Wait ); + + return TRUE; +} + + +VOID +CdNoopRelease ( + _In_ PVOID Fcb + ) + +/*++ + +Routine Description: + + This routine does nothing. + +Arguments: + + Fcb - The Fcb/Vcb which was specified as a context parameter for this + routine. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Fcb ); +} + + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdFilterCallbackAcquireForCreateSection ( + _In_ PFS_FILTER_CALLBACK_DATA CallbackData, + _Unreferenced_parameter_ PVOID *CompletionContext + ) + +/*++ + +Routine Description: + + This is the callback routine for MM to use to acquire the file exclusively. + +Arguments: + + FS_FILTER_CALLBACK_DATA - Filter based callback data that provides the file object we + want to acquire. + + CompletionContext - Ignored. + +Return Value: + + On success we return STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY. + + If SyncType is SyncTypeCreateSection, we return a status that indicates there are no + writers to this file. + +--*/ + +{ + PFILE_OBJECT FileObject; + + + PAGED_CODE(); + + NT_ASSERT( CallbackData->Operation == FS_FILTER_ACQUIRE_FOR_SECTION_SYNCHRONIZATION ); + NT_ASSERT( CallbackData->SizeOfFsFilterCallbackData == sizeof(FS_FILTER_CALLBACK_DATA) ); + + // + // Get the file object from the callback data. + // + + FileObject = CallbackData->FileObject; + + // + // Get the Fcb resource exclusively. + // + + ExAcquireResourceExclusiveLite( &((PFCB) FileObject->FsContext)->FcbNonpaged->FcbResource, + TRUE ); + + // + // Take the File resource shared. We need this later on when MM calls + // QueryStandardInfo to get the file size. + // + // If we don't use StarveExclusive, then we can get wedged behind an + // exclusive waiter who is waiting on someone else holding it shared in the + // read->initializecachemap path (which calls createsection) who is in turn + // waiting on us to finish the create section. + // + + ExAcquireSharedStarveExclusive( ((PFCB) FileObject->FsContext)->Resource, + TRUE ); + + // + // CDFS is a read-only file system, so we can always indicate no writers. + // We only do this for create section synchronization. For others we + // return the generic success STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY. + // + + if (CallbackData->Parameters.AcquireForSectionSynchronization.SyncType == SyncTypeCreateSection) { + + return STATUS_FILE_LOCKED_WITH_ONLY_READERS; + + } else { + + return STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY; + } + + UNREFERENCED_PARAMETER( CompletionContext ); +} + + +_Function_class_(FAST_IO_RELEASE_FILE) +_Requires_lock_held_(_Global_critical_region_) +VOID +CdReleaseForCreateSection ( + _In_ PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + This is the callback routine for MM to use to release a file acquired with + the AcquireForCreateSection call above. + +Arguments: + + FileObject - File object for a Cdfs stream. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Release the resources. + // + + ExReleaseResourceLite( &((PFCB) FileObject->FsContext)->FcbNonpaged->FcbResource ); + ExReleaseResourceLite( ((PFCB) FileObject->FsContext)->Resource); +} + diff --git a/filesys/cdfs/shutdown.c b/filesys/cdfs/shutdown.c new file mode 100644 index 00000000..ca42635a --- /dev/null +++ b/filesys/cdfs/shutdown.c @@ -0,0 +1,175 @@ +/*++ + +Copyright (c) 1997-2006 Microsoft Corporation + +Module Name: + + Shutdown.c + +Abstract: + + This module implements the shutdown routine for CDFS called by + the dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_SHUTDOWN) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonShutdown) +#endif + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonShutdown ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for handling shutdown operation called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + KEVENT Event; + PLIST_ENTRY Links; + PVCB Vcb; + PIRP NewIrp; + IO_STATUS_BLOCK Iosb; + BOOLEAN VcbPresent; + NTSTATUS Status; + + PAGED_CODE(); + + // + // Make sure we don't get any pop-ups. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS ); + + // + // Initialize an event for doing calls down to + // our target device objects. + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // Indicate that shutdown has started. + // + + SetFlag( CdData.Flags, CD_FLAGS_SHUTDOWN ); + + // + // Get everyone else out of the way + // + + CdAcquireCdData( IrpContext ); + + // + // Now walk through all the mounted Vcb's and shutdown the target + // device objects. + // + + Links = CdData.VcbQueue.Flink; + + while (Links != &CdData.VcbQueue) { + + Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks ); + + // + // Move to the next link now since the current Vcb may be deleted. + // + + Links = Links->Flink; + + // + // If we have already been called before for this volume + // (and yes this does happen), skip this volume as no writes + // have been allowed since the first shutdown. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_SHUTDOWN ) || + (Vcb->VcbCondition != VcbMounted)) { + + continue; + } + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + CdPurgeVolume( IrpContext, Vcb, FALSE ); + + // + // Build an irp for this volume stack - our own irp is probably too small and + // each stack may have a different stack size. + // + + NewIrp = IoBuildSynchronousFsdRequest( IRP_MJ_SHUTDOWN, + Vcb->TargetDeviceObject, + NULL, + 0, + NULL, + &Event, + &Iosb ); + + if (NewIrp != NULL) { + + Status = IoCallDriver( Vcb->TargetDeviceObject, NewIrp ); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + } + + KeClearEvent( &Event ); + } + + SetFlag( Vcb->VcbState, VCB_STATE_SHUTDOWN ); + + // + // Attempt to punch the volume down. + // + + VcbPresent = CdCheckForDismount( IrpContext, Vcb, FALSE ); + + if (VcbPresent) { + + CdReleaseVcb( IrpContext, Vcb ); + } + } + + + CdReleaseCdData( IrpContext ); + + IoUnregisterFileSystem( CdData.FileSystemDeviceObject ); + IoDeleteDevice( CdData.FileSystemDeviceObject ); + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + diff --git a/filesys/cdfs/strucsup.c b/filesys/cdfs/strucsup.c new file mode 100644 index 00000000..5f354ac2 --- /dev/null +++ b/filesys/cdfs/strucsup.c @@ -0,0 +1,2828 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + StrucSup.c + +Abstract: + + This module implements the Cdfs in-memory data structure manipulation + routines + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_STRUCSUP) + +// +// Local macros +// + +// +// PFCB +// CdAllocateFcbData ( +// _In_ PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdDeallocateFcbData ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// +// PFCB +// CdAllocateFcbIndex ( +// _In_ PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdDeallocateFcbIndex ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB Fcb +// ); +// +// PFCB_NONPAGED +// CdAllocateFcbNonpaged ( +// _In_ PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdDeallocateFcbNonpaged ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PFCB_NONPAGED FcbNonpaged +// ); +// +// PCCB +// CdAllocateCcb ( +// _In_ PIRP_CONTEXT IrpContext +// ); +// +// VOID +// CdDeallocateCcb ( +// _In_ PIRP_CONTEXT IrpContext, +// _Inout_ PCCB Ccb +// ); +// + +#define CdAllocateFcbData(IC) \ + FsRtlAllocatePoolWithTag( CdPagedPool, SIZEOF_FCB_DATA, TAG_FCB_DATA ) + +#define CdDeallocateFcbData(IC,F) \ + CdFreePool( &(F) ) + +#define CdAllocateFcbIndex(IC) \ + FsRtlAllocatePoolWithTag( CdPagedPool, SIZEOF_FCB_INDEX, TAG_FCB_INDEX ) + +#define CdDeallocateFcbIndex(IC,F) \ + CdFreePool( &(F) ) + +#define CdAllocateFcbNonpaged(IC) \ + ExAllocatePoolWithTag( CdNonPagedPool, sizeof( FCB_NONPAGED ), TAG_FCB_NONPAGED ) + +#define CdDeallocateFcbNonpaged(IC,FNP) \ + CdFreePool( &(FNP) ) + +#define CdAllocateCcb(IC) \ + FsRtlAllocatePoolWithTag( CdPagedPool, sizeof( CCB ), TAG_CCB ) + +#define CdDeallocateCcb(IC,C) \ + CdFreePool( &(C) ) + +// +// Local structures +// + +typedef struct _FCB_TABLE_ELEMENT { + + FILE_ID FileId; + PFCB Fcb; + +} FCB_TABLE_ELEMENT, *PFCB_TABLE_ELEMENT; + +// +// Local macros +// + +// +// VOID +// CdInsertFcbTable ( +// _In_ PIRP_CONTEXT IrpContext, +// _In_ PFCB Fcb +// ); +// +// VOID +// CdDeleteFcbTable ( +// _In_ PIRP_CONTEXT IrpContext, +// _In_ PFCB Fcb +// ); +// + + +#define CdInsertFcbTable(IC,F) { \ + FCB_TABLE_ELEMENT _Key; \ + _Key.Fcb = (F); \ + _Key.FileId = (F)->FileId; \ + RtlInsertElementGenericTable( &(F)->Vcb->FcbTable, \ + &_Key, \ + sizeof( FCB_TABLE_ELEMENT ), \ + NULL ); \ +} + +#define CdDeleteFcbTable(IC,F) { \ + FCB_TABLE_ELEMENT _Key; \ + _Key.FileId = (F)->FileId; \ + RtlDeleteElementGenericTable( &(F)->Vcb->FcbTable, &_Key ); \ +} + +// +// Local support routines +// + +VOID +CdDeleteFcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb + ); + +PFCB_NONPAGED +CdCreateFcbNonpaged ( + _In_ PIRP_CONTEXT IrpContext + ); + +VOID +CdDeleteFcbNonpaged ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB_NONPAGED FcbNonpaged + ); + +// Inform prefast that this is a compare routine. +RTL_GENERIC_COMPARE_ROUTINE CdFcbTableCompare; + +RTL_GENERIC_COMPARE_RESULTS +CdFcbTableCompare ( + _In_ PRTL_GENERIC_TABLE FcbTable, + _In_ PVOID Fid1, + _In_ PVOID Fid2 + ); + +// Inform prefast that this is an alloc reoutine. +RTL_GENERIC_ALLOCATE_ROUTINE CdAllocateFcbTable; + +PVOID +CdAllocateFcbTable ( + _In_ PRTL_GENERIC_TABLE FcbTable, + _In_ CLONG ByteSize + ); + +// Inform prefast that this is a free reoutine. +RTL_GENERIC_FREE_ROUTINE CdDeallocateFcbTable; + +VOID +CdDeallocateFcbTable ( + _In_ PRTL_GENERIC_TABLE FcbTable, + _In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Buffer + ); + +ULONG +CdTocSerial ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PCDROM_TOC_LARGE CdromToc + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdAllocateFcbTable) +#pragma alloc_text(PAGE, CdCleanupIrpContext) +#pragma alloc_text(PAGE, CdCreateCcb) +#pragma alloc_text(PAGE, CdCreateFcb) +#pragma alloc_text(PAGE, CdCreateFcbNonpaged) +#pragma alloc_text(PAGE, CdCreateFileLock) +#pragma alloc_text(PAGE, CdCreateIrpContext) +#pragma alloc_text(PAGE, CdDeallocateFcbTable) +#pragma alloc_text(PAGE, CdDeleteCcb) +#pragma alloc_text(PAGE, CdDeleteFcb) +#pragma alloc_text(PAGE, CdDeleteFcbNonpaged) +#pragma alloc_text(PAGE, CdDeleteFileLock) +#pragma alloc_text(PAGE, CdDeleteVcb) +#pragma alloc_text(PAGE, CdFcbTableCompare) +#pragma alloc_text(PAGE, CdGetNextFcb) +#pragma alloc_text(PAGE, CdInitializeFcbFromFileContext) +#pragma alloc_text(PAGE, CdInitializeFcbFromPathEntry) +#pragma alloc_text(PAGE, CdInitializeStackIrpContext) +#pragma alloc_text(PAGE, CdInitializeVcb) +#pragma alloc_text(PAGE, CdLookupFcbTable) +#pragma alloc_text(PAGE, CdProcessToc) +#pragma alloc_text(PAGE, CdTeardownStructures) +#pragma alloc_text(PAGE, CdTocSerial) +#pragma alloc_text(PAGE, CdUpdateVcbFromVolDescriptor) +#endif + +// +// Some static names for volume streams +// + +UNICODE_STRING CdInternalStreamNames[] = { + { 24, 24, L"$PATH_TABLE$"}, + { 2, 2, L"\\"} +}; + + +VOID +CdInitializeVcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _In_ __drv_aliasesMem PDEVICE_OBJECT TargetDeviceObject, + _In_ __drv_aliasesMem PVPB Vpb, + _In_ __drv_aliasesMem PCDROM_TOC_LARGE CdromToc, + _In_ ULONG TocLength, + _In_ ULONG TocTrackCount, + _In_ ULONG TocDiskFlags, + _In_ ULONG BlockFactor, + _In_ ULONG MediaChangeCount + ) + +/*++ + +Routine Description: + + This routine initializes and inserts a new Vcb record into the in-memory + data structure. The Vcb record "hangs" off the end of the Volume device + object and must be allocated by our caller. + +Arguments: + + Vcb - Supplies the address of the Vcb record being initialized. + + TargetDeviceObject - Supplies the address of the target device object to + associate with the Vcb record. + + Vpb - Supplies the address of the Vpb to associate with the Vcb record. + + CdromToc - Buffer to hold table of contents. NULL if TOC command not + supported. + + TocLength - Byte count length of TOC. We use this as the TOC length to + return on a user query. + + TocTrackCount - Count of tracks in TOC. Used to create pseudo files for + audio disks. + + TocDiskFlags - Flag field to indicate the type of tracks on the disk. + + BlockFactor - Used to decode any multi-session information. + + MediaChangeCount - Initial media change count of the target device + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // We start by first zeroing out all of the VCB, this will guarantee + // that any stale data is wiped clean. + // + + RtlZeroMemory( Vcb, sizeof( VCB )); + + // + // Set the proper node type code and node byte size. + // + + Vcb->NodeTypeCode = CDFS_NTC_VCB; + Vcb->NodeByteSize = sizeof( VCB ); + + // + // Initialize the DirNotify structs. FsRtlNotifyInitializeSync can raise. + // + + InitializeListHead( &Vcb->DirNotifyList ); + FsRtlNotifyInitializeSync( &Vcb->NotifySync ); + + // + // Pick up a VPB right now so we know we can pull this filesystem stack + // off of the storage stack on demand. This can raise - if it does, + // uninitialize the notify structures before returning. + // + + try { + + Vcb->SwapVpb = FsRtlAllocatePoolWithTag( CdNonPagedPool, + sizeof( VPB ), + TAG_VPB ); + } + finally { + + if (AbnormalTermination()) { + + FsRtlNotifyUninitializeSync( &Vcb->NotifySync ); + } + } + + // + // Nothing beyond this point should raise. + // + + RtlZeroMemory( Vcb->SwapVpb, sizeof( VPB ) ); + + // + // Initialize the resource variable for the Vcb and files. + // + + ExInitializeResourceLite( &Vcb->VcbResource ); + ExInitializeResourceLite( &Vcb->FileResource ); + ExInitializeFastMutex( &Vcb->VcbMutex ); + + // + // Insert this Vcb record on the CdData.VcbQueue. + // + + InsertHeadList( &CdData.VcbQueue, &Vcb->VcbLinks ); + + // + // Set the Target Device Object and Vpb fields, referencing the + // Target device for the mount. + // + + ObReferenceObject( TargetDeviceObject ); + Vcb->TargetDeviceObject = TargetDeviceObject; + Vcb->Vpb = Vpb; + + // + // Set the removable media flag based on the real device's + // characteristics + // + + if (FlagOn( Vpb->RealDevice->Characteristics, FILE_REMOVABLE_MEDIA )) { + + SetFlag( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA ); + } + + // + // Initialize the generic Fcb Table. + // + + RtlInitializeGenericTable( &Vcb->FcbTable, + (PRTL_GENERIC_COMPARE_ROUTINE) CdFcbTableCompare, + (PRTL_GENERIC_ALLOCATE_ROUTINE) CdAllocateFcbTable, + (PRTL_GENERIC_FREE_ROUTINE) CdDeallocateFcbTable, + NULL ); + + // + // Show that we have a mount in progress. + // + + CdUpdateVcbCondition( Vcb, VcbMountInProgress); + + // + // Refererence the Vcb for two reasons. The first is a reference + // that prevents the Vcb from going away on the last close unless + // dismount has already occurred. The second is to make sure + // we don't go into the dismount path on any error during mount + // until we get to the Mount cleanup. + // + + Vcb->VcbReference = 1 + CDFS_RESIDUAL_REFERENCE; + + // + // Update the TOC information in the Vcb. + // + + Vcb->CdromToc = CdromToc; + Vcb->TocLength = TocLength; + Vcb->TrackCount = TocTrackCount; + Vcb->DiskFlags = TocDiskFlags; + + // + // If this disk contains audio tracks only then set the audio flag. + // + + if (TocDiskFlags == CDROM_DISK_AUDIO_TRACK) { + + SetFlag( Vcb->VcbState, VCB_STATE_AUDIO_DISK | VCB_STATE_CDXA ); + } + + // + // Set the block factor. + // + + Vcb->BlockFactor = BlockFactor; + + // + // Set the media change count on the device + // + + CdUpdateMediaChangeCount( Vcb, MediaChangeCount); +} + + +VOID +CdUpdateVcbFromVolDescriptor ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _In_reads_bytes_opt_(SECTOR_SIZE) PCHAR RawIsoVd + ) + +/*++ + +Routine Description: + + This routine is called to perform the final initialization of a Vcb from the + volume descriptor on the disk. + +Arguments: + + Vcb - Vcb for the volume being mounted. We have already set the flags for the + type of descriptor. + + RawIsoVd - If specified this is the volume descriptor to use to mount the + volume. Not specified for a raw disk. + +Return Value: + + None + +--*/ + +{ + ULONG StartingBlock; + ULONG ByteCount; + + LONGLONG FileId = 0; + + PRAW_DIRENT RawDirent; + PATH_ENTRY PathEntry; + PCD_MCB_ENTRY McbEntry; + + BOOLEAN UnlockVcb = FALSE; + + PAGED_CODE(); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Copy the block size and compute the various block masks. + // Block size must not be larger than the sector size. We will + // use a default of the CD physical sector size if we are not + // on a data-full disc. + // + // This must always be set. + // + + Vcb->BlockSize = ( ARGUMENT_PRESENT( RawIsoVd ) ? + CdRvdBlkSz( RawIsoVd, Vcb->VcbState ) : + SECTOR_SIZE ); + + // + // We no longer accept media where blocksize != sector size. + // + + if (Vcb->BlockSize != SECTOR_SIZE) { + + CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR ); + } + + Vcb->BlocksPerSector = SECTOR_SIZE / Vcb->BlockSize; + Vcb->BlockMask = Vcb->BlockSize - 1; + Vcb->BlockInverseMask = ~Vcb->BlockMask; + + Vcb->BlockToSectorShift = 0; + Vcb->BlockToByteShift = SECTOR_SHIFT; + + // + // If there is a volume descriptor then do the internal Fcb's and + // other Vcb fields. + // + + if (ARGUMENT_PRESENT( RawIsoVd )) { + + // + // Create the path table Fcb and refererence it and the Vcb. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + Vcb->PathTableFcb = CdCreateFcb( IrpContext, + *((PFILE_ID) &FileId), + CDFS_NTC_FCB_PATH_TABLE, + NULL ); + + CdIncrementReferenceCounts( IrpContext, Vcb->PathTableFcb, 1, 1 ); + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + + // + // Compute the stream offset and size of this path table. + // + + StartingBlock = CdRvdPtLoc( RawIsoVd, Vcb->VcbState ); + + ByteCount = CdRvdPtSz( RawIsoVd, Vcb->VcbState ); + + Vcb->PathTableFcb->StreamOffset = BytesFromBlocks( Vcb, + SectorBlockOffset( Vcb, StartingBlock )); + + Vcb->PathTableFcb->FileSize.QuadPart = (LONGLONG) (Vcb->PathTableFcb->StreamOffset + + ByteCount); + + Vcb->PathTableFcb->ValidDataLength.QuadPart = Vcb->PathTableFcb->FileSize.QuadPart; + + Vcb->PathTableFcb->AllocationSize.QuadPart = LlSectorAlign( Vcb->PathTableFcb->FileSize.QuadPart ); + + // + // Now add the mapping information. + // + + CdLockFcb( IrpContext, Vcb->PathTableFcb ); + + CdAddInitialAllocation( IrpContext, + Vcb->PathTableFcb, + StartingBlock, + Vcb->PathTableFcb->AllocationSize.QuadPart ); + + CdUnlockFcb( IrpContext, Vcb->PathTableFcb ); + + // + // Point to the file resource. + // + + Vcb->PathTableFcb->Resource = &Vcb->FileResource; + + // + // Mark the Fcb as initialized and create the stream file for this. + // + + SetFlag( Vcb->PathTableFcb->FcbState, FCB_STATE_INITIALIZED ); + + CdCreateInternalStream( IrpContext, Vcb, Vcb->PathTableFcb, &CdInternalStreamNames[0]); + + // + // Create the root index and reference it in the Vcb. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + Vcb->RootIndexFcb = CdCreateFcb( IrpContext, + *((PFILE_ID) &FileId), + CDFS_NTC_FCB_INDEX, + NULL ); + + CdIncrementReferenceCounts( IrpContext, Vcb->RootIndexFcb, 1, 1 ); + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + + // + // Create the File id by hand for this Fcb. + // + + CdSetFidPathTableOffset( Vcb->RootIndexFcb->FileId, Vcb->PathTableFcb->StreamOffset ); + CdFidSetDirectory( Vcb->RootIndexFcb->FileId ); + + // + // Create a pseudo path table entry so we can call the initialization + // routine for the directory. + // + + RawDirent = (PRAW_DIRENT) CdRvdDirent( RawIsoVd, Vcb->VcbState ); + + CopyUchar4( &PathEntry.DiskOffset, RawDirent->FileLoc ); + + PathEntry.DiskOffset += RawDirent->XarLen; + PathEntry.Ordinal = 1; + PathEntry.PathTableOffset = Vcb->PathTableFcb->StreamOffset; + + CdInitializeFcbFromPathEntry( IrpContext, + Vcb->RootIndexFcb, + NULL, + &PathEntry ); + + // + // Create the stream file for the root directory. + // + + CdCreateInternalStream( IrpContext, Vcb, Vcb->RootIndexFcb, &CdInternalStreamNames[1] ); + + // + // Now do the volume dasd Fcb. Create this and reference it in the + // Vcb. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + Vcb->VolumeDasdFcb = CdCreateFcb( IrpContext, + *((PFILE_ID) &FileId), + CDFS_NTC_FCB_DATA, + NULL ); + + CdIncrementReferenceCounts( IrpContext, Vcb->VolumeDasdFcb, 1, 1 ); + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + + // + // The file size is the full disk. + // + + StartingBlock = CdRvdVolSz( RawIsoVd, Vcb->VcbState ); + + Vcb->VolumeDasdFcb->FileSize.QuadPart = LlBytesFromBlocks( Vcb, StartingBlock ); + + Vcb->VolumeDasdFcb->AllocationSize.QuadPart = + Vcb->VolumeDasdFcb->ValidDataLength.QuadPart = Vcb->VolumeDasdFcb->FileSize.QuadPart; + + // + // Now add the extent representing the volume 'by hand'. + // + + CdLockFcb( IrpContext, Vcb->VolumeDasdFcb ); + + McbEntry = Vcb->VolumeDasdFcb->Mcb.McbArray; + + McbEntry->FileOffset = + McbEntry->DiskOffset = 0; + + McbEntry->ByteCount = Vcb->VolumeDasdFcb->AllocationSize.QuadPart; + + McbEntry->DataBlockByteCount = + McbEntry->TotalBlockByteCount = McbEntry->ByteCount; + + Vcb->VolumeDasdFcb->Mcb.CurrentEntryCount = 1; + + CdUnlockFcb( IrpContext, Vcb->VolumeDasdFcb ); + + // + // Point to the file resource. + // + + Vcb->VolumeDasdFcb->Resource = &Vcb->FileResource; + + Vcb->VolumeDasdFcb->FileAttributes = FILE_ATTRIBUTE_READONLY; + + // + // Mark the Fcb as initialized. + // + + SetFlag( Vcb->VolumeDasdFcb->FcbState, FCB_STATE_INITIALIZED ); + + // + // Check and see if this is an XA disk. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_ISO | VCB_STATE_JOLIET) + && RtlEqualMemory( CdXaId, + Add2Ptr( RawIsoVd, 0x400, PCHAR ), + 8 )) { + + SetFlag( Vcb->VcbState, VCB_STATE_CDXA ); + } + + // + // If this is a music disk then we want to mock this disk to make it + // look like ISO disk. We will create a pseudo root directory in + // that case. + // + + } else if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) { + + ULONG RootDirectorySize; + + // + // Create the path table Fcb and refererence it and the Vcb. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + Vcb->PathTableFcb = CdCreateFcb( IrpContext, + *((PFILE_ID) &FileId), + CDFS_NTC_FCB_PATH_TABLE, + NULL ); + + CdIncrementReferenceCounts( IrpContext, Vcb->PathTableFcb, 1, 1 ); + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + + // + // We only create a pseudo entry for the root. + // + + Vcb->PathTableFcb->FileSize.QuadPart = (LONGLONG) (FIELD_OFFSET( RAW_PATH_ISO, DirId ) + 2); + + Vcb->PathTableFcb->ValidDataLength.QuadPart = Vcb->PathTableFcb->FileSize.QuadPart; + + Vcb->PathTableFcb->AllocationSize.QuadPart = LlSectorAlign( Vcb->PathTableFcb->FileSize.QuadPart ); + + // + // Point to the file resource. + // + + Vcb->PathTableFcb->Resource = &Vcb->FileResource; + + // + // Mark the Fcb as initialized and create the stream file for this. + // + + SetFlag( Vcb->PathTableFcb->FcbState, FCB_STATE_INITIALIZED ); + + CdCreateInternalStream( IrpContext, Vcb, Vcb->PathTableFcb, &CdInternalStreamNames[0]); + + // + // Create the root index and reference it in the Vcb. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + Vcb->RootIndexFcb = CdCreateFcb( IrpContext, + *((PFILE_ID) &FileId), + CDFS_NTC_FCB_INDEX, + NULL ); + + CdIncrementReferenceCounts( IrpContext, Vcb->RootIndexFcb, 1, 1 ); + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + + // + // Create the File id by hand for this Fcb. + // + + CdSetFidPathTableOffset( Vcb->RootIndexFcb->FileId, Vcb->PathTableFcb->StreamOffset ); + CdFidSetDirectory( Vcb->RootIndexFcb->FileId ); + + // + // Create a pseudo path table entry so we can call the initialization + // routine for the directory. + // + + RtlZeroMemory( &PathEntry, sizeof( PATH_ENTRY )); + + + PathEntry.Ordinal = 1; + PathEntry.PathTableOffset = Vcb->PathTableFcb->StreamOffset; + + CdInitializeFcbFromPathEntry( IrpContext, + Vcb->RootIndexFcb, + NULL, + &PathEntry ); + + // + // Set the sizes by hand for this Fcb. It should have an entry for each track plus an + // entry for the root and parent. + // + + RootDirectorySize = (Vcb->TrackCount + 2) * CdAudioDirentSize; + RootDirectorySize = SectorAlign( RootDirectorySize ); + + Vcb->RootIndexFcb->AllocationSize.QuadPart = + Vcb->RootIndexFcb->ValidDataLength.QuadPart = + Vcb->RootIndexFcb->FileSize.QuadPart = RootDirectorySize; + + SetFlag( Vcb->RootIndexFcb->FcbState, FCB_STATE_INITIALIZED ); + + // + // Create the stream file for the root directory. + // + + CdCreateInternalStream( IrpContext, Vcb, Vcb->RootIndexFcb, &CdInternalStreamNames[1] ); + + // + // Now do the volume dasd Fcb. Create this and reference it in the + // Vcb. + // + + CdLockVcb( IrpContext, Vcb ); + UnlockVcb = TRUE; + + Vcb->VolumeDasdFcb = CdCreateFcb( IrpContext, + *((PFILE_ID) &FileId), + CDFS_NTC_FCB_DATA, + NULL ); + + CdIncrementReferenceCounts( IrpContext, Vcb->VolumeDasdFcb, 1, 1 ); + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + + // + // We won't allow raw reads on this Fcb so leave the size at + // zero. + // + + // + // Point to the file resource. + // + + Vcb->VolumeDasdFcb->Resource = &Vcb->FileResource; + + Vcb->VolumeDasdFcb->FileAttributes = FILE_ATTRIBUTE_READONLY; + + // + // Mark the Fcb as initialized. + // + + SetFlag( Vcb->VolumeDasdFcb->FcbState, FCB_STATE_INITIALIZED ); + + // + // We will store a hard-coded name in the Vpb and use the toc as + // the serial number. + // + + Vcb->Vpb->VolumeLabelLength = CdAudioLabelLength; + + RtlCopyMemory( Vcb->Vpb->VolumeLabel, + CdAudioLabel, + CdAudioLabelLength ); + + // + // Find the serial number for the audio disk. + // + + Vcb->Vpb->SerialNumber = CdTocSerial( IrpContext, Vcb->CdromToc ); + + // + // Set the ISO bit so we know how to treat the names. + // + + SetFlag( Vcb->VcbState, VCB_STATE_ISO ); + } + + } finally { + + if (UnlockVcb) { CdUnlockVcb( IrpContext, Vcb ); } + } +} + + +VOID +CdDeleteVcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine is called to delete a Vcb which failed mount or has been + dismounted. The dismount code should have already removed all of the + open Fcb's. We do nothing here but clean up other auxilary structures. + +Arguments: + + Vcb - Vcb to delete. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + ASSERT_EXCLUSIVE_CDDATA; + ASSERT_EXCLUSIVE_VCB( Vcb ); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Chuck the backpocket Vpb we kept just in case. + // + + CdFreePool( &Vcb->SwapVpb ); + + // + // If there is a Vpb then we must delete it ourselves. + // + + CdFreePool( &Vcb->Vpb ); + + // + // Dereference our target if we haven't already done so. + // + + if (Vcb->TargetDeviceObject != NULL) { + + ObDereferenceObject( Vcb->TargetDeviceObject ); + } + + // + // Delete the XA Sector and sector cache buffer if allocated. + // + + CdFreePool( &Vcb->XASector ); + CdFreePool( &Vcb->SectorCacheBuffer); + + if (Vcb->SectorCacheIrp != NULL) { + + IoFreeIrp( Vcb->SectorCacheIrp); + Vcb->SectorCacheIrp = NULL; + + ExDeleteResourceLite( &Vcb->SectorCacheResource); + } + + // + // Remove this entry from the global queue. + // + + RemoveEntryList( &Vcb->VcbLinks ); + + // + // Delete the Vcb and File resources. + // + + ExDeleteResourceLite( &Vcb->VcbResource ); + ExDeleteResourceLite( &Vcb->FileResource ); + + // + // Delete the TOC if present. + // + + CdFreePool( &Vcb->CdromToc ); + + // + // Uninitialize the notify structures. + // + + if (Vcb->NotifySync != NULL) { + + FsRtlNotifyUninitializeSync( &Vcb->NotifySync ); + } + + // + // Now delete the volume device object. + // +#pragma prefast( suppress: __WARNING_BUFFER_UNDERFLOW, "This is ok, the Vcb is embedded in our volume device object, and that is what we are really deleting." ) + IoDeleteDevice( (PDEVICE_OBJECT) CONTAINING_RECORD( Vcb, + VOLUME_DEVICE_OBJECT, + Vcb )); + + return; +} + + +PFCB +CdCreateFcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ FILE_ID FileId, + _In_ NODE_TYPE_CODE NodeTypeCode, + _Out_opt_ PBOOLEAN FcbExisted + ) + +/*++ + +Routine Description: + + This routine is called to find the Fcb for the given FileId. We will + look this up first in the Fcb table and if not found we will create + an Fcb. We don't initialize it or insert it into the FcbTable in this + routine. + + This routine is called while the Vcb is locked. + +Arguments: + + FileId - This is the Id for the target Fcb. + + NodeTypeCode - Node type for this Fcb if we need to create. + + FcbExisted - If specified, we store whether the Fcb existed. + +Return Value: + + PFCB - The Fcb found in the table or created if needed. + +--*/ + +{ + PFCB NewFcb; + BOOLEAN LocalFcbExisted; + + PAGED_CODE(); + + // + // Use the local boolean if one was not passed in. + // + + if (!ARGUMENT_PRESENT( FcbExisted )) { + + FcbExisted = &LocalFcbExisted; + } + + // + // Maybe this is already in the table. + // + + NewFcb = CdLookupFcbTable( IrpContext, IrpContext->Vcb, FileId ); + + // + // If not then create the Fcb is requested by our caller. + // + + if (NewFcb == NULL) { + + // + // Allocate and initialize the structure depending on the + // type code. + // + + switch (NodeTypeCode) { + + case CDFS_NTC_FCB_PATH_TABLE: + case CDFS_NTC_FCB_INDEX: + + NewFcb = CdAllocateFcbIndex( IrpContext ); + + RtlZeroMemory( NewFcb, SIZEOF_FCB_INDEX ); + + NewFcb->NodeByteSize = SIZEOF_FCB_INDEX; + + InitializeListHead( &NewFcb->FcbQueue ); + + break; + + case CDFS_NTC_FCB_DATA : + + NewFcb = CdAllocateFcbData( IrpContext ); + + RtlZeroMemory( NewFcb, SIZEOF_FCB_DATA ); + + NewFcb->NodeByteSize = SIZEOF_FCB_DATA; + + break; + + default: + +#pragma prefast( suppress: __WARNING_USE_OTHER_FUNCTION, "This is a bug." ) + CdBugCheck( 0, 0, 0 ); + } + + // + // Now do the common initialization. + // + + NewFcb->NodeTypeCode = NodeTypeCode; + + NewFcb->Vcb = IrpContext->Vcb; + NewFcb->FileId = FileId; + + CdInitializeMcb( IrpContext, NewFcb ); + + // + // Now create the non-paged section object. + // + + NewFcb->FcbNonpaged = CdCreateFcbNonpaged( IrpContext ); + + // + // Deallocate the Fcb and raise if the allocation failed. + // + + if (NewFcb->FcbNonpaged == NULL) { + + CdFreePool( &NewFcb ); + + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + *FcbExisted = FALSE; + + // + // Initialize Advanced FCB Header fields + // + + ExInitializeFastMutex( &NewFcb->FcbNonpaged->AdvancedFcbHeaderMutex ); + FsRtlSetupAdvancedHeader( &NewFcb->Header, + &NewFcb->FcbNonpaged->AdvancedFcbHeaderMutex ); + + if (NodeTypeCode == CDFS_NTC_FCB_DATA) { + + FsRtlInitializeOplock( CdGetFcbOplock(NewFcb) ); + } + + } else { + + *FcbExisted = TRUE; + } + + return NewFcb; +} + + +VOID +CdInitializeFcbFromPathEntry ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_opt_ PFCB ParentFcb, + _In_ PPATH_ENTRY PathEntry + ) + +/*++ + +Routine Description: + + This routine is called to initialize an Fcb for a directory from + the path entry. Since we only have a starting point for the directory, + not the length, we can only speculate on the sizes. + + The general initialization is performed in CdCreateFcb. + +Arguments: + + Fcb - Newly created Fcb for this stream. + + ParentFcb - Parent Fcb for this stream. It may not be present. + + PathEntry - PathEntry for this Fcb in the Path Table. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Fill in the Index specific fields of the Fcb. + // + + Fcb->StreamOffset = BytesFromBlocks( Fcb->Vcb, + SectorBlockOffset( Fcb->Vcb, PathEntry->DiskOffset )); + + Fcb->Ordinal = PathEntry->Ordinal; + + // + // Initialize the common header in the Fcb. The node type is already + // present. + // + + Fcb->Resource = &Fcb->Vcb->FileResource; + + // + // Always set the sizes to one sector until we read the self-entry. + // + + Fcb->AllocationSize.QuadPart = + Fcb->FileSize.QuadPart = + Fcb->ValidDataLength.QuadPart = SECTOR_SIZE; + + CdAddInitialAllocation( IrpContext, + Fcb, + PathEntry->DiskOffset, + SECTOR_SIZE ); + // + // State flags for this Fcb. + // + + SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY ); + + // + // Link into the other in-memory structures and into the Fcb table. + // + + if (ParentFcb != NULL) { + + Fcb->ParentFcb = ParentFcb; + + InsertTailList( &ParentFcb->FcbQueue, &Fcb->FcbLinks ); + + CdIncrementReferenceCounts( IrpContext, ParentFcb, 1, 1 ); + } + + CdInsertFcbTable( IrpContext, Fcb ); + SetFlag( Fcb->FcbState, FCB_STATE_IN_FCB_TABLE ); + + return; +} + + +VOID +CdInitializeFcbFromFileContext ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ PFCB ParentFcb, + _In_ PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to initialize an Fcb for a file from + the file context. We have looked up all of the dirents for this + stream and have the full file size. We will load the all of the allocation + for the file into the Mcb now. + + The general initialization is performed in CdCreateFcb. + +Arguments: + + Fcb - Newly created Fcb for this stream. + + ParentFcb - Parent Fcb for this stream. + + FileContext - FileContext for the file. + +Return Value: + + None + +--*/ + +{ + PDIRENT ThisDirent = &FileContext->InitialDirent->Dirent; + PCOMPOUND_DIRENT CurrentCompoundDirent; + + LONGLONG CurrentFileOffset; + ULONG CurrentMcbEntryOffset; + + PAGED_CODE(); + + // + // Use a try-finally to facilitate cleanup. + // + + CdLockFcb( IrpContext, Fcb ); + + try { + + // + // Initialize the common header in the Fcb. The node type is already + // present. + // + + Fcb->Resource = &IrpContext->Vcb->FileResource; + + // + // Allocation occurs in block-sized units. + // + + Fcb->FileSize.QuadPart = + Fcb->ValidDataLength.QuadPart = FileContext->FileSize; + + Fcb->AllocationSize.QuadPart = LlBlockAlign( Fcb->Vcb, FileContext->FileSize ); + + // + // Set the flags from the dirent. We always start with the read-only bit. + // + + SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_READONLY ); + if (FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_HIDDEN )) { + + SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_HIDDEN ); + } + + // + // Convert the time to NT time. + // + + CdConvertCdTimeToNtTime( IrpContext, + ThisDirent->CdTime, + (PLARGE_INTEGER) &Fcb->CreationTime ); + + // + // Set the flag indicating the type of extent. + // + + if (ThisDirent->ExtentType != Form1Data) { + + if (ThisDirent->ExtentType == Mode2Form2Data) { + + SetFlag( Fcb->FcbState, FCB_STATE_MODE2FORM2_FILE ); + + } else { + + SetFlag( Fcb->FcbState, FCB_STATE_DA_FILE ); + } + + Fcb->XAAttributes = ThisDirent->XAAttributes; + Fcb->XAFileNumber = ThisDirent->XAFileNumber; + } + + // + // Read through all of the dirents for the file until we find the last + // and add the allocation into the Mcb. + // + + CurrentCompoundDirent = FileContext->InitialDirent; + CurrentFileOffset = 0; + CurrentMcbEntryOffset = 0; + + while (TRUE) { + + CdAddAllocationFromDirent( IrpContext, + Fcb, + CurrentMcbEntryOffset, + CurrentFileOffset, + &CurrentCompoundDirent->Dirent ); + + // + // Break out if we are at the last dirent. + // + + if (!FlagOn( CurrentCompoundDirent->Dirent.DirentFlags, CD_ATTRIBUTE_MULTI )) { + + break; + } + + CurrentFileOffset += CurrentCompoundDirent->Dirent.DataLength; + CurrentMcbEntryOffset += 1; + + // + // We better be able to find the next dirent. + // + + if (!CdLookupNextDirent( IrpContext, + ParentFcb, + &CurrentCompoundDirent->DirContext, + &FileContext->CurrentDirent->DirContext )) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + CurrentCompoundDirent = FileContext->CurrentDirent; + + CdUpdateDirentFromRawDirent( IrpContext, + ParentFcb, + &CurrentCompoundDirent->DirContext, + &CurrentCompoundDirent->Dirent ); + } + + // + // Show that the Fcb is initialized. + // + + SetFlag( Fcb->FcbState, FCB_STATE_INITIALIZED ); + + // + // Link into the other in-memory structures and into the Fcb table. + // + + Fcb->ParentFcb = ParentFcb; + + InsertTailList( &ParentFcb->FcbQueue, &Fcb->FcbLinks ); + + CdIncrementReferenceCounts( IrpContext, ParentFcb, 1, 1 ); + + CdInsertFcbTable( IrpContext, Fcb ); + SetFlag( Fcb->FcbState, FCB_STATE_IN_FCB_TABLE ); + + } finally { + + CdUnlockFcb( IrpContext, Fcb ); + } + + return; +} + + +PCCB +CdCreateCcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb, + _In_ ULONG Flags + ) + +/*++ + +Routine Description: + + This routine is called to allocate and initialize the Ccb structure. + +Arguments: + + Fcb - This is the Fcb for the file being opened. + + Flags - User flags to set in this Ccb. + +Return Value: + + PCCB - Pointer to the created Ccb. + +--*/ + +{ + PCCB NewCcb; + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Allocate and initialize the structure. + // + + NewCcb = CdAllocateCcb( IrpContext ); + + RtlZeroMemory( NewCcb, sizeof( CCB )); + + // + // Set the proper node type code and node byte size + // + + NewCcb->NodeTypeCode = CDFS_NTC_CCB; + NewCcb->NodeByteSize = sizeof( CCB ); + + // + // Set the initial value for the flags and Fcb + // + + NewCcb->Flags = Flags; + NewCcb->Fcb = Fcb; + + return NewCcb; +} + + +VOID +CdDeleteCcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ __drv_freesMem( Pool ) PCCB Ccb + ) +/*++ + +Routine Description: + + This routine is called to cleanup and deallocate a Ccb structure. + +Arguments: + + Ccb - This is the Ccb to delete. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + if (Ccb->SearchExpression.FileName.Buffer != NULL) { + + CdFreePool( &Ccb->SearchExpression.FileName.Buffer ); + } + + CdDeallocateCcb( IrpContext, Ccb ); + return; +} + + +_When_(RaiseOnError || return, _At_(Fcb->FileLock, _Post_notnull_)) +_When_(RaiseOnError, _At_(IrpContext, _Pre_notnull_)) +BOOLEAN +CdCreateFileLock ( + _In_opt_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ BOOLEAN RaiseOnError + ) + +/*++ + +Routine Description: + + This routine is called when we want to attach a file lock structure to the + given Fcb. It is possible the file lock is already attached. + + This routine is sometimes called from the fast path and sometimes in the + Irp-based path. We don't want to raise in the fast path, just return FALSE. + +Arguments: + + Fcb - This is the Fcb to create the file lock for. + + RaiseOnError - If TRUE, we will raise on an allocation failure. Otherwise we + return FALSE on an allocation failure. + +Return Value: + + BOOLEAN - TRUE if the Fcb has a filelock, FALSE otherwise. + +--*/ + +{ + BOOLEAN Result = TRUE; + PFILE_LOCK FileLock; + + PAGED_CODE(); + + // + // Lock the Fcb and check if there is really any work to do. + // + + CdLockFcb( IrpContext, Fcb ); + + if (Fcb->FileLock != NULL) { + + CdUnlockFcb( IrpContext, Fcb ); + return TRUE; + } + + Fcb->FileLock = FileLock = + FsRtlAllocateFileLock( NULL, NULL ); + + CdUnlockFcb( IrpContext, Fcb ); + + // + // Return or raise as appropriate. + // + + if (FileLock == NULL) { + + if (RaiseOnError) { + + NT_ASSERT( ARGUMENT_PRESENT( IrpContext )); + + CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + Result = FALSE; + } + + return Result; +} + + +_Ret_valid_ PIRP_CONTEXT +CdCreateIrpContext ( + _In_ PIRP Irp, + _In_ BOOLEAN Wait + ) + +/*++ + +Routine Description: + + This routine is called to initialize an IrpContext for the current + CDFS request. We allocate the structure and then initialize it from + the given Irp. + +Arguments: + + Irp - Irp for this request. + + Wait - TRUE if this request is synchronous, FALSE otherwise. + +Return Value: + + PIRP_CONTEXT - Allocated IrpContext. + +--*/ + +{ + PIRP_CONTEXT NewIrpContext = NULL; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PAGED_CODE(); + + // + // The only operations a filesystem device object should ever receive + // are create/teardown of fsdo handles and operations which do not + // occur in the context of fileobjects (i.e., mount). + // + + if (IrpSp->DeviceObject == CdData.FileSystemDeviceObject) { + + if (IrpSp->FileObject != NULL && + IrpSp->MajorFunction != IRP_MJ_CREATE && + IrpSp->MajorFunction != IRP_MJ_CLEANUP && + IrpSp->MajorFunction != IRP_MJ_CLOSE) { + + ExRaiseStatus( STATUS_INVALID_DEVICE_REQUEST ); + } + + NT_ASSERT( IrpSp->FileObject != NULL || + + (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST && + IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_INVALIDATE_VOLUMES) || + + (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + IrpSp->MinorFunction == IRP_MN_MOUNT_VOLUME ) || + + IrpSp->MajorFunction == IRP_MJ_SHUTDOWN ); + } + + // + // Look in our lookaside list for an IrpContext. + // + + if (CdData.IrpContextDepth) { + + CdLockCdData(); + NewIrpContext = (PIRP_CONTEXT) PopEntryList( &CdData.IrpContextList ); + if (NewIrpContext != NULL) { + + CdData.IrpContextDepth--; + } + + CdUnlockCdData(); + } + + if (NewIrpContext == NULL) { + + // + // We didn't get it from our private list so allocate it from pool. + // + + NewIrpContext = FsRtlAllocatePoolWithTag( CdNonPagedPool, sizeof( IRP_CONTEXT ), TAG_IRP_CONTEXT ); + } + + RtlZeroMemory( NewIrpContext, sizeof( IRP_CONTEXT )); + + // + // Set the proper node type code and node byte size + // + + NewIrpContext->NodeTypeCode = CDFS_NTC_IRP_CONTEXT; + NewIrpContext->NodeByteSize = sizeof( IRP_CONTEXT ); + + // + // Set the originating Irp field + // + + NewIrpContext->Irp = Irp; + + // + // Copy RealDevice for workque algorithms. We will update this in the Mount or + // Verify since they have no file objects to use here. + // + + if (IrpSp->FileObject != NULL) { + + NewIrpContext->RealDevice = IrpSp->FileObject->DeviceObject; + } + + // + // Locate the volume device object and Vcb that we are trying to access. + // This may be our filesystem device object. In that case don't initialize + // the Vcb field. + // + + if (IrpSp->DeviceObject != CdData.FileSystemDeviceObject) { + + NewIrpContext->Vcb = &((PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject)->Vcb; + + } + + // + // Major/Minor Function codes + // + + NewIrpContext->MajorFunction = IrpSp->MajorFunction; + NewIrpContext->MinorFunction = IrpSp->MinorFunction; + + // + // Set the wait parameter + // + + if (Wait) { + + SetFlag( NewIrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + } else { + + SetFlag( NewIrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST ); + } + + // + // return and tell the caller + // + + return NewIrpContext; +} + + +VOID +CdCleanupIrpContext ( + _In_ PIRP_CONTEXT IrpContext, + _In_ BOOLEAN Post + ) + +/*++ + +Routine Description: + + This routine is called to cleanup and possibly deallocate the Irp Context. + If the request is being posted or this Irp Context is possibly on the + stack then we only cleanup any auxilary structures. + +Arguments: + + Post - TRUE if we are posting this request, FALSE if we are deleting + or retrying this in the current thread. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + // + // If we aren't doing more processing then deallocate this as appropriate. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING)) { + + // + // If this context is the top level CDFS context then we need to + // restore the top level thread context. + // + + if (IrpContext->ThreadContext != NULL) { + + CdRestoreThreadContext( IrpContext ); + } + + // + // Deallocate the Io context if allocated. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO )) { + + CdFreeIoContext( IrpContext->IoContext ); + } + + // + // Deallocate the IrpContext if not from the stack. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ON_STACK )) { + + if (CdData.IrpContextDepth < CdData.IrpContextMaxDepth) { + + CdLockCdData(); + + PushEntryList( &CdData.IrpContextList, (PSINGLE_LIST_ENTRY) IrpContext ); + CdData.IrpContextDepth++; + + CdUnlockCdData(); + + } else { + + // + // We couldn't add this to our lookaside list so free it to + // pool. + // + + CdFreePool( &IrpContext ); + } + } + + // + // Clear the appropriate flags. + // + + } else if (Post) { + + // + // If this context is the top level CDFS context then we need to + // restore the top level thread context. + // + + if (IrpContext->ThreadContext != NULL) { + + CdRestoreThreadContext( IrpContext ); + } + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAGS_CLEAR_ON_POST ); + + } else { + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAGS_CLEAR_ON_RETRY ); + } + + return; +} + + +VOID +CdInitializeStackIrpContext ( + _Out_ PIRP_CONTEXT IrpContext, + _In_ PIRP_CONTEXT_LITE IrpContextLite + ) + +/*++ + +Routine Description: + + This routine is called to initialize an IrpContext for the current + CDFS request. The IrpContext is on the stack and we need to initialize + it for the current request. The request is a close operation. + +Arguments: + + IrpContext - IrpContext to initialize. + + IrpContextLite - Structure containing the details of this request. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Zero and then initialize the structure. + // + + RtlZeroMemory( IrpContext, sizeof( IRP_CONTEXT )); + + // + // Set the proper node type code and node byte size + // + + IrpContext->NodeTypeCode = CDFS_NTC_IRP_CONTEXT; + IrpContext->NodeByteSize = sizeof( IRP_CONTEXT ); + + // + // Note that this is from the stack. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ON_STACK ); + + // + // Copy RealDevice for workque algorithms. + // + + IrpContext->RealDevice = IrpContextLite->RealDevice; + + // + // The Vcb is found in the Fcb. + // + + IrpContext->Vcb = IrpContextLite->Fcb->Vcb; + + // + // Major/Minor Function codes + // + + IrpContext->MajorFunction = IRP_MJ_CLOSE; + + // + // Set the wait parameter + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + return; +} + + + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdTeardownStructures ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB StartingFcb, + _Out_ PBOOLEAN RemovedStartingFcb + ) + +/*++ + +Routine Description: + + This routine is used to walk from some starting point in the Fcb tree towards + the root. It will remove the Fcb and continue walking up the tree until + it finds a point where we can't remove an Fcb. + + We look at the following fields in the Fcb to determine whether we can + remove this. + + 1 - Handle count must be zero. + 2 - If directory then only the only reference can be for a stream file. + 3 - Reference count must either be zero or go to zero here. + + We return immediately if we are recursively entering this routine. + +Arguments: + + StartingFcb - This is the Fcb node in the tree to begin with. This Fcb + must currently be acquired exclusively. + + RemovedStartingFcb - Address to store whether we removed the starting Fcb. + +Return Value: + + None + +--*/ + +{ + PVCB Vcb = StartingFcb->Vcb; + PFCB CurrentFcb = StartingFcb; + BOOLEAN AcquiredCurrentFcb = FALSE; + PFCB ParentFcb; + + PAGED_CODE(); + + *RemovedStartingFcb = FALSE; + + // + // If this is a recursive call to TearDownStructures we return immediately + // doing no operation. + // + + if (FlagOn( IrpContext->TopLevel->Flags, IRP_CONTEXT_FLAG_IN_TEARDOWN )) { + + return; + } + + SetFlag( IrpContext->TopLevel->Flags, IRP_CONTEXT_FLAG_IN_TEARDOWN ); + + // + // Use a try-finally to safely clear the top-level field. + // + + try { + + // + // Loop until we find an Fcb we can't remove. + // + + do { + + // + // See if there is an internal stream we should delete. + // Only do this if it is the last reference on the Fcb. + // + + if ((SafeNodeType( CurrentFcb ) != CDFS_NTC_FCB_DATA) && + (CurrentFcb->FcbUserReference == 0) && + (CurrentFcb->FileObject != NULL)) { + + // + // Go ahead and delete the stream file object. + // + + CdDeleteInternalStream( IrpContext, CurrentFcb ); + } + + // + // If the reference count is non-zero then break. + // + + if (CurrentFcb->FcbReference != 0) { + + break; + } + + // + // It looks like we have a candidate for removal here. We + // will need to acquire the parent, if present, in order to + // remove this from the parent prefix table. + // + + ParentFcb = CurrentFcb->ParentFcb; + + if (ParentFcb != NULL) { + + CdAcquireFcbExclusive( IrpContext, ParentFcb, FALSE ); + } + + // + // Now lock the vcb. + // + + CdLockVcb( IrpContext, Vcb ); + + // + // Final check to see if the reference count is still zero. + // + + if (CurrentFcb->FcbReference != 0) { + + CdUnlockVcb( IrpContext, Vcb ); + + if (ParentFcb != NULL) { + + CdReleaseFcb( IrpContext, ParentFcb ); + } + + break; + } + + // + // If there is a parent then do the necessary cleanup for the parent. + // + + if (ParentFcb != NULL) { + + CdRemovePrefix( IrpContext, CurrentFcb ); + RemoveEntryList( &CurrentFcb->FcbLinks ); + + CdDecrementReferenceCounts( IrpContext, ParentFcb, 1, 1 ); + } + + if (FlagOn( CurrentFcb->FcbState, FCB_STATE_IN_FCB_TABLE )) { + + CdDeleteFcbTable( IrpContext, CurrentFcb ); + ClearFlag( CurrentFcb->FcbState, FCB_STATE_IN_FCB_TABLE ); + + } + + // + // Unlock the Vcb but hold the parent in order to walk up + // the tree. + // + + CdUnlockVcb( IrpContext, Vcb ); + CdDeleteFcb( IrpContext, CurrentFcb ); + + // + // Move to the parent Fcb. + // + + CurrentFcb = ParentFcb; + AcquiredCurrentFcb = TRUE; + + } while (CurrentFcb != NULL); + + } finally { + + // + // Release the current Fcb if we have acquired it. + // + + if (AcquiredCurrentFcb && (CurrentFcb != NULL)) { + + CdReleaseFcb( IrpContext, CurrentFcb ); + } + + // + // Clear the teardown flag. + // + + ClearFlag( IrpContext->TopLevel->Flags, IRP_CONTEXT_FLAG_IN_TEARDOWN ); + } + + *RemovedStartingFcb = (CurrentFcb != StartingFcb); + return; +} + + +PFCB +CdLookupFcbTable ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _In_ FILE_ID FileId + ) + +/*++ + +Routine Description: + + This routine will look through the Fcb table looking for a matching + entry. + +Arguments: + + Vcb - Vcb for this volume. + + FileId - This is the key value to use for the search. + +Return Value: + + PFCB - A pointer to the matching entry or NULL otherwise. + +--*/ + +{ + FCB_TABLE_ELEMENT Key; + PFCB_TABLE_ELEMENT Hit; + PFCB ReturnFcb = NULL; + + PAGED_CODE(); + + Key.FileId = FileId; + + Hit = (PFCB_TABLE_ELEMENT) RtlLookupElementGenericTable( &Vcb->FcbTable, &Key ); + + if (Hit != NULL) { + + ReturnFcb = Hit->Fcb; + } + + return ReturnFcb; + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +PFCB +CdGetNextFcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _In_ PVOID *RestartKey + ) + +/*++ + +Routine Description: + + This routine will enumerate through all of the Fcb's in the Fcb table. + +Arguments: + + Vcb - Vcb for this volume. + + RestartKey - This value is used by the table package to maintain + its position in the enumeration. It is initialized to NULL + for the first search. + +Return Value: + + PFCB - A pointer to the next fcb or NULL if the enumeration is + completed + +--*/ + +{ + PFCB Fcb; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + Fcb = (PFCB) RtlEnumerateGenericTableWithoutSplaying( &Vcb->FcbTable, RestartKey ); + + if (Fcb != NULL) { + + Fcb = ((PFCB_TABLE_ELEMENT)(Fcb))->Fcb; + } + + return Fcb; +} + + +NTSTATUS +CdProcessToc ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PDEVICE_OBJECT TargetDeviceObject, + _In_ PCDROM_TOC_LARGE CdromToc, + _Inout_ PULONG Length, + _Out_ PULONG TrackCount, + _Inout_ PULONG DiskFlags + ) + +/*++ + +Routine Description: + + This routine is called to verify and process the TOC for this disk. + We hide a data track for a CD+ volume. + +Arguments: + + TargetDeviceObject - Device object to send TOC request to. + + CdromToc - Pointer to TOC structure. + + Length - On input this is the length of the TOC. On return is the TOC + length we will show to the user. + + TrackCount - This is the count of tracks for the TOC. We use this + when creating a pseudo directory for a music disk. + + DiskFlags - We return flags indicating what we know about this disk. + +Return Value: + + NTSTATUS - The result of trying to read the TOC. + +--*/ + +{ + NTSTATUS Status; + IO_STATUS_BLOCK Iosb; + CDROM_READ_TOC_EX Command; + + ULONG CurrentTrack; + ULONG LocalTrackCount; + ULONG LocalTocLength; + ULONG Address = 0; + BOOLEAN UseReadToc = FALSE; + + union { + + UCHAR BigEndian[2]; + USHORT Length; + + } BiasedTocLength; + + PTRACK_DATA Track; + + PAGED_CODE(); + + // + // Zero the command block. This conveniently corresponds to an + // LBA mode READ_TOC request. + // + + RtlZeroMemory( &Command, sizeof( Command)); + +RetryReadToc: + + // + // Go ahead and read the table of contents + // + + Status = CdPerformDevIoCtrlEx( IrpContext, + UseReadToc ? IOCTL_CDROM_READ_TOC : IOCTL_CDROM_READ_TOC_EX, + TargetDeviceObject, + &Command, + sizeof( Command ), + CdromToc, + sizeof( CDROM_TOC_LARGE ), + FALSE, + TRUE, + &Iosb ); + + // + // Nothing to process if this request fails. + // + + if (!NT_SUCCESS( Status )) { + + // + // If the underlying device does not support READ_TOC_EX, try the old method. + // + + if (!UseReadToc && + ((Status == STATUS_INVALID_DEVICE_REQUEST) || + (Status == STATUS_INVALID_PARAMETER))) { + + UseReadToc = TRUE; + goto RetryReadToc; + } + + return Status; + } + + // + // Get the number of tracks and stated size of this structure. + // + + CurrentTrack = 0; + LocalTrackCount = CdromToc->LastTrack - CdromToc->FirstTrack + 1; + LocalTocLength = PtrOffset( CdromToc, &CdromToc->TrackData[LocalTrackCount + 1] ); + + // + // Get out if there is an immediate problem with the TOC. + // + + if ((LocalTocLength > Iosb.Information) || + (CdromToc->FirstTrack > CdromToc->LastTrack)) { + + Status = STATUS_DISK_CORRUPT_ERROR; + return Status; + } + + // + // Walk through the individual tracks. Stop at the first data track after + // any lead-in audio tracks. + // + + do { + + // + // Get the next track. + // + + Track = &CdromToc->TrackData[CurrentTrack]; + + // + // If this is a data track then check if we have only seen audio tracks + // to this point. + // + + if (FlagOn( Track->Control, TOC_DATA_TRACK )) { + + // + // If we have only seen audio tracks then assume this is a + // CD+ disk. Hide the current data track and only return + // the previous audio tracks. Set the disk type to be mixed + // data/audio. + // + + if (FlagOn( *DiskFlags, CDROM_DISK_AUDIO_TRACK ) && + !FlagOn( *DiskFlags, CDROM_DISK_DATA_TRACK )) { + + // + // Remove one track from the TOC. + // + + CdromToc->LastTrack -= 1; + + // + // Knock 2.5 minutes off the current track to hide the final leadin. + // 2.5 min = 150 sec = (x 75) 11250 frames (sectors). + // + + SwapCopyUchar4( &Address, &Track->Address); + Address -= 11250; + SwapCopyUchar4( &Track->Address, &Address); + + Track->TrackNumber = TOC_LAST_TRACK; + + // + // Set the disk type to mixed data/audio. + // + + SetFlag( *DiskFlags, CDROM_DISK_DATA_TRACK ); + + break; + } + + // + // Set the flag to indicate data tracks present. + // + + SetFlag( *DiskFlags, CDROM_DISK_DATA_TRACK ); + + // + // If this is a audio track then set the flag indicating audio + // tracks. + // + + } else { + + SetFlag( *DiskFlags, CDROM_DISK_AUDIO_TRACK ); + } + + // + // Set our index for the next track. + // + + CurrentTrack += 1; + + } while (CurrentTrack < LocalTrackCount); + + // + // Set the length to point just past the last track we looked at. + // + + *TrackCount = CurrentTrack; + *Length = PtrOffset( CdromToc, &CdromToc->TrackData[CurrentTrack + 1] ); + BiasedTocLength.Length = (USHORT) *Length - 2; + + CdromToc->Length[0] = BiasedTocLength.BigEndian[1]; + CdromToc->Length[1] = BiasedTocLength.BigEndian[0]; + + return Status; +} + + +// +// Local support routine +// + +VOID +CdDeleteFcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called to cleanup and deallocate an Fcb. We know there + are no references remaining. We cleanup any auxilary structures and + deallocate this Fcb. + +Arguments: + + Fcb - This is the Fcb to deallcoate. + +Return Value: + + None + +--*/ + +{ + PVCB Vcb = NULL; + PAGED_CODE(); + + // + // Sanity check the counts. + // + + NT_ASSERT( Fcb->FcbCleanup == 0 ); + NT_ASSERT( Fcb->FcbReference == 0 ); + + // + // Release any Filter Context structures associated with this FCB + // + + FsRtlTeardownPerStreamContexts( &Fcb->Header ); + + // + // Start with the common structures. + // + + CdUninitializeMcb( IrpContext, Fcb ); + + CdDeleteFcbNonpaged( IrpContext, Fcb->FcbNonpaged ); + + // + // Check if we need to deallocate the prefix name buffer. + // + + if ((Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer != (PWCHAR) Fcb->FileNamePrefix.FileNameBuffer) && + (Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer != NULL)) { + + CdFreePool( &Fcb->FileNamePrefix.ExactCaseName.FileName.Buffer ); + } + + // + // Now look at the short name prefix. + // + + if (Fcb->ShortNamePrefix != NULL) { + + CdFreePool( &Fcb->ShortNamePrefix ); + } + + // + // Now do the type specific structures. + // + + switch (Fcb->NodeTypeCode) { + + case CDFS_NTC_FCB_PATH_TABLE: + case CDFS_NTC_FCB_INDEX: + + NT_ASSERT( Fcb->FileObject == NULL ); + NT_ASSERT( IsListEmpty( &Fcb->FcbQueue )); + + if (Fcb == Fcb->Vcb->RootIndexFcb) { + + Vcb = Fcb->Vcb; + Vcb->RootIndexFcb = NULL; + + } else if (Fcb == Fcb->Vcb->PathTableFcb) { + + Vcb = Fcb->Vcb; + Vcb->PathTableFcb = NULL; + } + + CdDeallocateFcbIndex( IrpContext, Fcb ); + break; + + case CDFS_NTC_FCB_DATA : + + if (Fcb->FileLock != NULL) { + + FsRtlFreeFileLock( Fcb->FileLock ); + } + + FsRtlUninitializeOplock( CdGetFcbOplock(Fcb) ); + + if (Fcb == Fcb->Vcb->VolumeDasdFcb) { + + Vcb = Fcb->Vcb; + Vcb->VolumeDasdFcb = NULL; + } + + CdDeallocateFcbData( IrpContext, Fcb ); + } + + // + // Decrement the Vcb reference count if this is a system + // Fcb. + // + + if (Vcb != NULL) { + + InterlockedDecrement( (LONG*)&Vcb->VcbReference ); + InterlockedDecrement( (LONG*)&Vcb->VcbUserReference ); + } + + return; +} + + +// +// Local support routine +// + +PFCB_NONPAGED +CdCreateFcbNonpaged ( + _In_ PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine is called to create and initialize the non-paged portion + of an Fcb. + +Arguments: + +Return Value: + + PFCB_NONPAGED - Pointer to the created nonpaged Fcb. NULL if not created. + +--*/ + +{ + PFCB_NONPAGED FcbNonpaged; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Allocate the non-paged pool and initialize the various + // synchronization objects. + // + + FcbNonpaged = CdAllocateFcbNonpaged( IrpContext ); + + if (FcbNonpaged != NULL) { + + RtlZeroMemory( FcbNonpaged, sizeof( FCB_NONPAGED )); + + FcbNonpaged->NodeTypeCode = CDFS_NTC_FCB_NONPAGED; + FcbNonpaged->NodeByteSize = sizeof( FCB_NONPAGED ); + + ExInitializeResourceLite( &FcbNonpaged->FcbResource ); + ExInitializeFastMutex( &FcbNonpaged->FcbMutex ); + } + + return FcbNonpaged; +} + + +// +// Local support routine +// + +VOID +CdDeleteFcbNonpaged ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB_NONPAGED FcbNonpaged + ) + +/*++ + +Routine Description: + + This routine is called to cleanup the non-paged portion of an Fcb. + +Arguments: + + FcbNonpaged - Structure to clean up. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + ExDeleteResourceLite( &FcbNonpaged->FcbResource ); + + CdDeallocateFcbNonpaged( IrpContext, FcbNonpaged ); + + return; +} + + +// +// Local support routine +// + +RTL_GENERIC_COMPARE_RESULTS +CdFcbTableCompare ( + _In_ PRTL_GENERIC_TABLE FcbTable, + _In_ PVOID Fid1, + _In_ PVOID Fid2 + ) + +/*++ + +Routine Description: + + This routine is the Cdfs compare routine called by the generic table package. + If will compare the two File Id values and return a comparison result. + +Arguments: + + FcbTable - This is the table being searched. + + Fid1 - First key value. + + Fid2 - Second key value. + +Return Value: + + RTL_GENERIC_COMPARE_RESULTS - The results of comparing the two + input structures + +--*/ + +{ + FILE_ID Id1, Id2; + PAGED_CODE(); + + Id1 = *((FILE_ID UNALIGNED *) Fid1); + Id2 = *((FILE_ID UNALIGNED *) Fid2); + + if (Id1.QuadPart < Id2.QuadPart) { + + return GenericLessThan; + + } else if (Id1.QuadPart > Id2.QuadPart) { + + return GenericGreaterThan; + + } else { + + return GenericEqual; + } + + UNREFERENCED_PARAMETER( FcbTable ); +} + + +// +// Local support routine +// + +PVOID +CdAllocateFcbTable ( + _In_ PRTL_GENERIC_TABLE FcbTable, + _In_ CLONG ByteSize + ) + +/*++ + +Routine Description: + + This is a generic table support routine to allocate memory + +Arguments: + + FcbTable - Supplies the generic table being used + + ByteSize - Supplies the number of bytes to allocate + +Return Value: + + PVOID - Returns a pointer to the allocated data + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( FcbTable ); + + return( FsRtlAllocatePoolWithTag( CdPagedPool, ByteSize, TAG_FCB_TABLE )); +} + + +// +// Local support routine +// +VOID +CdDeallocateFcbTable ( + _In_ PRTL_GENERIC_TABLE FcbTable, + _In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Buffer + ) +/*++ + +Routine Description: + + This is a generic table support routine that deallocates memory + +Arguments: + + FcbTable - Supplies the generic table being used + + Buffer - Supplies the buffer being deallocated + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + CdFreePool( &Buffer ); + + UNREFERENCED_PARAMETER( FcbTable ); +} + + +// +// Local support routine +// + +ULONG +CdTocSerial ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PCDROM_TOC_LARGE CdromToc + ) + +/*++ + +Routine Description: + + This routine is called to generate a serial number for an audio disk. + The number is based on the starting positions of the tracks. + The following algorithm is used. + + If the number of tracks is <= 2 then initialize the serial number to the + leadout block number. + + Then add the starting address of each track (use 0x00mmssff format). + +Arguments: + + CdromToc - Valid table of contents to use for track information. + +Return Value: + + ULONG - 32 bit serial number based on TOC. + +--*/ + +{ + ULONG SerialNumber = 0; + PTRACK_DATA ThisTrack; + PTRACK_DATA LastTrack; + ULONG Address; + ULONG MsfAddress = 0; // satisfy PREFIX + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Check if there are two tracks or fewer. + // + + LastTrack = &CdromToc->TrackData[ CdromToc->LastTrack - CdromToc->FirstTrack + 1]; + ThisTrack = &CdromToc->TrackData[0]; + + if (CdromToc->LastTrack - CdromToc->FirstTrack <= 1) { + + SwapCopyUchar4( &Address, LastTrack->Address); + CdLbnToMmSsFf( Address, (PUCHAR)&SerialNumber); + } + else { + + // + // Add the starting offset of each track and add to the serial number. + // + + while (ThisTrack != LastTrack) { + + SwapCopyUchar4( &Address, ThisTrack->Address); + CdLbnToMmSsFf( Address, (PUCHAR)&MsfAddress); + + SerialNumber += MsfAddress; + ThisTrack += 1; + } + } + + return SerialNumber; +} + + diff --git a/filesys/cdfs/verfysup.c b/filesys/cdfs/verfysup.c new file mode 100644 index 00000000..015b367b --- /dev/null +++ b/filesys/cdfs/verfysup.c @@ -0,0 +1,984 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + VerfySup.c + +Abstract: + + This module implements the Cdfs Verification routines. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_VERFYSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdVerifyFcbOperation) +#pragma alloc_text(PAGE, CdVerifyVcb) +#endif + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdPerformVerify ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp, + _In_ PDEVICE_OBJECT DeviceToVerify + ) + +/*++ + +Routine Description: + + This routines performs an IoVerifyVolume operation and takes the + appropriate action. If the verify is successful then we send the originating + Irp off to an Ex Worker Thread. This routine is called from the exception handler. + + No file system resources are held when this routine is called. + +Arguments: + + Irp - The irp to send off after all is well and done. + + Device - The real device needing verification. + +Return Value: + + None. + +--*/ + +{ + PVCB Vcb; + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp; + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // Check if this Irp has a status of Verify required and if it does + // then call the I/O system to do a verify. + // + // Skip the IoVerifyVolume if this is a mount or verify request + // itself. Trying a recursive mount will cause a deadlock with + // the DeviceObject->DeviceLock. + // + + if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && + ((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) || + (IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME))) { + + return CdFsdPostRequest( IrpContext, Irp ); + } + + // + // Extract a pointer to the Vcb from the VolumeDeviceObject. + // Note that since we have specifically excluded mount, + // requests, we know that IrpSp->DeviceObject is indeed a + // volume device object. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + Vcb = &CONTAINING_RECORD( IrpSp->DeviceObject, + VOLUME_DEVICE_OBJECT, + DeviceObject )->Vcb; + try { + + // + // Send down the verify FSCTL. Note that this is sent to the + // currently mounted volume, which may not be this one. + // + // We will allow Raw to mount this volume if we were doing a + // an absolute DASD open. + // + + Status = IoVerifyVolume( DeviceToVerify, CdOperationIsDasdOpen( IrpContext)); + + // + // Acquire the Vcb so we're working with a stable VcbCondition. + // + + CdAcquireVcbShared( IrpContext, Vcb, FALSE); + + // + // If the verify operation completed it will return + // either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. + // + // If CdVerifyVolume encountered an error during + // processing, it will return that error. If we got + // STATUS_WRONG_VOLUME from the verify, and our volume + // is now mounted, commute the status to STATUS_SUCCESS. + // + + if ((Status == STATUS_WRONG_VOLUME) && + (Vcb->VcbCondition == VcbMounted)) { + + Status = STATUS_SUCCESS; + } + else if ((STATUS_SUCCESS == Status) && (Vcb->VcbCondition != VcbMounted)) { + + // + // If the verify succeeded, but our volume is not mounted, + // then some other volume is on the device. + // + + Status = STATUS_WRONG_VOLUME; + } + + // + // Do a quick unprotected check here. The routine will do + // a safe check. After here we can release the resource. + // Note that if the volume really went away, we will be taking + // the Reparse path. + // + + // + // If the device might need to go away then call our dismount routine. + // + + if (((Vcb->VcbCondition == VcbNotMounted) || + (Vcb->VcbCondition == VcbInvalid) || + (Vcb->VcbCondition == VcbDismountInProgress)) && + (Vcb->VcbReference <= CDFS_RESIDUAL_REFERENCE)) { + + CdReleaseVcb( IrpContext, Vcb); + + CdAcquireCdData( IrpContext ); + CdCheckForDismount( IrpContext, Vcb, FALSE ); + CdReleaseCdData( IrpContext ); + } + else { + + CdReleaseVcb( IrpContext, Vcb); + } + + // + // If this is a create and the verify succeeded then complete the + // request with a REPARSE status. + // + + if ((IrpContext->MajorFunction == IRP_MJ_CREATE) && + (IrpSp->FileObject->RelatedFileObject == NULL) && + ((Status == STATUS_SUCCESS) || (Status == STATUS_WRONG_VOLUME))) { + + Irp->IoStatus.Information = IO_REMOUNT; + + CdCompleteRequest( IrpContext, Irp, STATUS_REPARSE ); + Status = STATUS_REPARSE; + Irp = NULL; + IrpContext = NULL; + + // + // If there is still an error to process then call the Io system + // for a popup. + // + + } else if ((Irp != NULL) && !NT_SUCCESS( Status )) { + + // + // Fill in the device object if required. + // + + if (IoIsErrorUserInduced( Status ) ) { + + IoSetHardErrorOrVerifyDevice( Irp, DeviceToVerify ); + } + + CdNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // If there is still an Irp, send it off to an Ex Worker thread. + // + + if (IrpContext != NULL) { + + Status = CdFsdPostRequest( IrpContext, Irp ); + } + + } except(CdExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the verify or raised + // an error ourselves. So we'll abort the I/O request with + // the error status that we get back from the execption code. + // + + Status = CdProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + return Status; +} + + + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +CdCheckForDismount ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb, + _In_ BOOLEAN Force + ) + +/*++ + +Routine Description: + + This routine is called to check if a volume is ready for dismount. This + occurs when only file system references are left on the volume. + + If the dismount is not currently underway and the user reference count + has gone to zero then we can begin the dismount. + + If the dismount is in progress and there are no references left on the + volume (we check the Vpb for outstanding references as well to catch + any create calls dispatched to the file system) then we can delete + the Vcb. + +Arguments: + + Vcb - Vcb for the volume to try to dismount. + + Force - Whether we will force this volume to be dismounted. + +Return Value: + + BOOLEAN - True if the Vcb was not gone by the time this function finished, + False if it was deleted. + + This is only a trustworthy indication to the caller if it had the vcb + exclusive itself. + +--*/ + +{ + BOOLEAN UnlockVcb = TRUE; + BOOLEAN VcbPresent = TRUE; + KIRQL SavedIrql; + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_VCB( Vcb ); + + ASSERT_EXCLUSIVE_CDDATA; + + // + // Acquire and lock this Vcb to check the dismount state. + // + + CdAcquireVcbExclusive( IrpContext, Vcb, FALSE ); + + // + // Lets get rid of any pending closes for this volume. + // + + CdFspClose( Vcb ); + + CdLockVcb( IrpContext, Vcb ); + + // + // If the dismount is not already underway then check if the + // user reference count has gone to zero or we are being forced + // to disconnect. If so start the teardown on the Vcb. + // + + if (Vcb->VcbCondition != VcbDismountInProgress) { + + if (Vcb->VcbUserReference <= CDFS_RESIDUAL_USER_REFERENCE || Force) { + + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + VcbPresent = CdDismountVcb( IrpContext, Vcb ); + } + + // + // If the teardown is underway and there are absolutely no references + // remaining then delete the Vcb. References here include the + // references in the Vcb and Vpb. + // + + } else if (Vcb->VcbReference == 0) { + + IoAcquireVpbSpinLock( &SavedIrql ); + + // + // If there are no file objects and no reference counts in the + // Vpb we can delete the Vcb. Don't forget that we have the + // last reference in the Vpb. + // + + if (Vcb->Vpb->ReferenceCount == 1) { + + IoReleaseVpbSpinLock( SavedIrql ); + CdUnlockVcb( IrpContext, Vcb ); + UnlockVcb = FALSE; + CdDeleteVcb( IrpContext, Vcb ); + VcbPresent = FALSE; + + } else { + + IoReleaseVpbSpinLock( SavedIrql ); + } + } + + // + // Unlock the Vcb if still held. + // + + if (UnlockVcb) { + + CdUnlockVcb( IrpContext, Vcb ); + } + + // + // Release any resources still acquired. + // + + if (VcbPresent) { + + CdReleaseVcb( IrpContext, Vcb ); + } + else { + _Analysis_assume_lock_not_held_(Vcb->VcbResource); + } + + return VcbPresent; +} + + +BOOLEAN +CdMarkDevForVerifyIfVcbMounted ( + _Inout_ PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine checks to see if the specified Vcb is currently mounted on + the device or not. If it is, it sets the verify flag on the device, if + not then the state is noted in the Vcb. + +Arguments: + + Vcb - This is the volume to check. + +Return Value: + + TRUE if the device has been marked for verify here, FALSE otherwise. + +--*/ + +{ + BOOLEAN Marked = FALSE; + KIRQL SavedIrql; + + IoAcquireVpbSpinLock( &SavedIrql ); + +#pragma prefast(suppress: 28175, "this is a filesystem driver, touching the vpb is allowed") + if (Vcb->Vpb->RealDevice->Vpb == Vcb->Vpb) { + + CdMarkRealDevForVerify( Vcb->Vpb->RealDevice); + Marked = TRUE; + } + else { + + // + // Flag this to avoid the VPB spinlock in future passes. + // + + SetFlag( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE); + } + + IoReleaseVpbSpinLock( SavedIrql ); + + return Marked; +} + + +VOID +CdVerifyVcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine checks that the current Vcb is valid and currently mounted + on the device. It will raise on an error condition. + + We check whether the volume needs verification and the current state + of the Vcb. + +Arguments: + + Vcb - This is the volume to verify. + +Return Value: + + None + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + IO_STATUS_BLOCK Iosb; + ULONG MediaChangeCount = 0; + BOOLEAN ForceVerify = FALSE; + BOOLEAN DevMarkedForVerify; + + PAGED_CODE(); + + // + // Fail immediately if the volume is in the progress of being dismounted + // or has been marked invalid. + // + + if ((Vcb->VcbCondition == VcbInvalid) || + ((Vcb->VcbCondition == VcbDismountInProgress) && + (IrpContext->MajorFunction != IRP_MJ_CREATE))) { + + if (FlagOn( Vcb->VcbState, VCB_STATE_DISMOUNTED )) { + + CdRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED ); + + } else { + + CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + } + + // + // Capture the real device verify state. + // + + DevMarkedForVerify = CdRealDevNeedsVerify( Vcb->Vpb->RealDevice); + + if (FlagOn( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA ) && !DevMarkedForVerify) { + + // + // If the media is removable and the verify volume flag in the + // device object is not set then we want to ping the device + // to see if it needs to be verified. + // + + if (Vcb->VcbCondition != VcbMountInProgress) { + + Status = CdPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_CHECK_VERIFY, + Vcb->TargetDeviceObject, + &MediaChangeCount, + sizeof(ULONG), + FALSE, + FALSE, + &Iosb ); + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + MediaChangeCount = 0; + } + + // + // There are four cases when we want to do a verify. These are the + // first three. + // + // 1. We are mounted, and the device has become empty + // 2. The device has returned verify required (=> DO_VERIFY_VOL flag is + // set, but could be due to hardware condition) + // 3. Media change count doesn't match the one in the Vcb + // + + if (((Vcb->VcbCondition == VcbMounted) && + CdIsRawDevice( IrpContext, Status )) + || + (Status == STATUS_VERIFY_REQUIRED) + || + (NT_SUCCESS(Status) && + (Vcb->MediaChangeCount != MediaChangeCount))) { + + // + // If we are currently the volume on the device then it is our + // responsibility to set the verify flag. If we're not on the device, + // then we shouldn't touch the flag. + // + + if (!FlagOn( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE) && + !DevMarkedForVerify) { + + DevMarkedForVerify = CdMarkDevForVerifyIfVcbMounted( Vcb); + } + + ForceVerify = TRUE; + + // + // NOTE that we no longer update the media change count here. We + // do so only when we've actually completed a verify at a particular + // change count value. + // + } + } + + // + // This is the 4th verify case. + // + // We ALWAYS force CREATE requests on unmounted volumes through the + // verify path. These requests could have been in limbo between + // IoCheckMountedVpb and us when a verify/mount took place and caused + // a completely different fs/volume to be mounted. In this case the + // checks above may not have caught the condition, since we may already + // have verified (wrong volume) and decided that we have nothing to do. + // We want the requests to be re routed to the currently mounted volume, + // since they were directed at the 'drive', not our volume. + // + + if (NT_SUCCESS( Status) && !ForceVerify && !DevMarkedForVerify && + (IrpContext->MajorFunction == IRP_MJ_CREATE)) { + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->Irp); + + ForceVerify = (IrpSp->FileObject->RelatedFileObject == NULL) && + ((Vcb->VcbCondition == VcbDismountInProgress) || + (Vcb->VcbCondition == VcbNotMounted)); + + // + // Note that we don't touch the device verify flag here. It required + // it would have been caught and set by the first set of checks. + // + } + } + + // + // Raise the verify / error if neccessary. + // + + if (ForceVerify || DevMarkedForVerify || !NT_SUCCESS( Status)) { + + IoSetHardErrorOrVerifyDevice( IrpContext->Irp, + Vcb->Vpb->RealDevice ); + + CdRaiseStatus( IrpContext, (ForceVerify || DevMarkedForVerify) + ? STATUS_VERIFY_REQUIRED + : Status); + } + + // + // Based on the condition of the Vcb we'll either return to our + // caller or raise an error condition + // + + switch (Vcb->VcbCondition) { + + case VcbNotMounted: + + IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); + + CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME ); + break; + + case VcbInvalid: + case VcbDismountInProgress: + + if (FlagOn( Vcb->VcbState, VCB_STATE_DISMOUNTED )) { + + CdRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED ); + + } else { + + CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + break; + } +} + + +BOOLEAN +CdVerifyFcbOperation ( + _In_opt_ PIRP_CONTEXT IrpContext, + _In_ PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called to verify that the state of the Fcb is valid + to allow the current operation to continue. We use the state of the + Vcb, target device and type of operation to determine this. + +Arguments: + + IrpContext - IrpContext for the request. If not present then we + were called from the fast IO path. + + Fcb - Fcb to perform the request on. + +Return Value: + + BOOLEAN - TRUE if the request can continue, FALSE otherwise. + +--*/ + +{ + PVCB Vcb = Fcb->Vcb; + PDEVICE_OBJECT RealDevice = Vcb->Vpb->RealDevice; + PIRP Irp; + + PAGED_CODE(); + + // + // Check that the fileobject has not been cleaned up. + // + + if ( ARGUMENT_PRESENT( IrpContext )) { + + PFILE_OBJECT FileObject; + + Irp = IrpContext->Irp; + FileObject = IoGetCurrentIrpStackLocation( Irp)->FileObject; + + if ( FileObject && FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE)) { + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Following FAT, we allow certain operations even on cleaned up + // file objects. Everything else, we fail. + // + + if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) || + (IrpSp->MajorFunction == IRP_MJ_CLOSE ) || + (IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) || + ( (IrpSp->MajorFunction == IRP_MJ_READ) && + FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) { + + NOTHING; + + } else { + + CdRaiseStatus( IrpContext, STATUS_FILE_CLOSED ); + } + } + } + + // + // Fail immediately if the volume is in the progress of being dismounted + // or has been marked invalid. + // + + if ((Vcb->VcbCondition == VcbInvalid) || + (Vcb->VcbCondition == VcbDismountInProgress)) { + + if (ARGUMENT_PRESENT( IrpContext )) { + + if (FlagOn( Vcb->VcbState, VCB_STATE_DISMOUNTED )) { + + CdRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED ); + + } else { + + CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + } + + return FALSE; + } + + // + // Always fail if the volume needs to be verified. + // + + if (CdRealDevNeedsVerify( RealDevice)) { + + if (ARGUMENT_PRESENT( IrpContext )) { + + IoSetHardErrorOrVerifyDevice( IrpContext->Irp, + RealDevice ); + + CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED ); + } + + return FALSE; + + // + // + // All operations are allowed on mounted. + // + + } else if ((Vcb->VcbCondition == VcbMounted) || + (Vcb->VcbCondition == VcbMountInProgress)) { + + return TRUE; + + // + // Fail all requests for fast Io on other Vcb conditions. + // + + } else if (!ARGUMENT_PRESENT( IrpContext )) { + + return FALSE; + + // + // The remaining case is VcbNotMounted. + // Mark the device to be verified and raise WRONG_VOLUME. + // + + } else if (Vcb->VcbCondition == VcbNotMounted) { + + IoSetHardErrorOrVerifyDevice( IrpContext->Irp, RealDevice ); + CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME ); + +// return FALSE; // unreachable code + } + + return TRUE; +} + + + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +CdDismountVcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine is called when all of the user references to a volume are + gone. We will initiate all of the teardown any system resources. + + If all of the references to this volume are gone at the end of this routine + then we will complete the teardown of this Vcb and mark the current Vpb + as not mounted. Otherwise we will allocated a new Vpb for this device + and keep the current Vpb attached to the Vcb. + +Arguments: + + Vcb - Vcb for the volume to dismount. + +Return Value: + + BOOLEAN - TRUE if we didn't delete the Vcb, FALSE otherwise. + +--*/ + +{ + PVPB OldVpb; + BOOLEAN VcbPresent = TRUE; + KIRQL SavedIrql; + + BOOLEAN FinalReference; + + ASSERT_EXCLUSIVE_CDDATA; + ASSERT_EXCLUSIVE_VCB( Vcb ); + + CdLockVcb( IrpContext, Vcb ); + + // + // We should only take this path once. + // + + NT_ASSERT( Vcb->VcbCondition != VcbDismountInProgress ); + + // + // Mark the Vcb as DismountInProgress. + // + + Vcb->VcbCondition = VcbDismountInProgress; + + if (Vcb->XASector != NULL) { + + CdFreePool( &Vcb->XASector ); + Vcb->XASector = 0; + Vcb->XADiskOffset = 0; + } + + // + // Remove our reference to the internal Fcb's. The Fcb's will then + // be removed in the purge path below. + // + + if (Vcb->RootIndexFcb != NULL) { + + Vcb->RootIndexFcb->FcbReference -= 1; + Vcb->RootIndexFcb->FcbUserReference -= 1; + } + + if (Vcb->PathTableFcb != NULL) { + + Vcb->PathTableFcb->FcbReference -= 1; + Vcb->PathTableFcb->FcbUserReference -= 1; + } + + if (Vcb->VolumeDasdFcb != NULL) { + + Vcb->VolumeDasdFcb->FcbReference -= 1; + Vcb->VolumeDasdFcb->FcbUserReference -= 1; + } + + CdUnlockVcb( IrpContext, Vcb ); + + // + // Purge the volume. + // + + CdPurgeVolume( IrpContext, Vcb, TRUE ); + + // + // Empty the delayed and async close queues. + // + + CdFspClose( Vcb ); + + OldVpb = Vcb->Vpb; + + // + // Remove the mount volume reference. + // + + CdLockVcb( IrpContext, Vcb ); + Vcb->VcbReference -= 1; + + // + // Acquire the Vpb spinlock to check for Vpb references. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + // + // Remember if this is the last reference on this Vcb. We incremented + // the count on the Vpb earlier so we get one last crack it. If our + // reference has gone to zero but the vpb reference count is greater + // than zero then the Io system will be responsible for deleting the + // Vpb. + // + + FinalReference = (BOOLEAN) ((Vcb->VcbReference == 0) && + (OldVpb->ReferenceCount == 1)); + + // + // There is a reference count in the Vpb and in the Vcb. We have + // incremented the reference count in the Vpb to make sure that + // we have last crack at it. If this is a failed mount then we + // want to return the Vpb to the IO system to use for the next + // mount request. + // + +#pragma prefast(suppress: 28175, "this is a filesystem driver, touching the vpb is allowed") + if (OldVpb->RealDevice->Vpb == OldVpb) { + + // + // If not the final reference then swap out the Vpb. We must + // preserve the REMOVE_PENDING flag so that the device is + // not remounted in the middle of a PnP remove operation. + // + + if (!FinalReference) { + + NT_ASSERT( Vcb->SwapVpb != NULL ); + + Vcb->SwapVpb->Type = IO_TYPE_VPB; + Vcb->SwapVpb->Size = sizeof( VPB ); + +#pragma prefast(push) +#pragma prefast(disable: 28175, "this is a filesystem driver, touching the vpb is allowed") + Vcb->SwapVpb->RealDevice = OldVpb->RealDevice; + Vcb->SwapVpb->RealDevice->Vpb = Vcb->SwapVpb; +#pragma prefast(pop) + + Vcb->SwapVpb->Flags = FlagOn( OldVpb->Flags, VPB_REMOVE_PENDING ); + + IoReleaseVpbSpinLock( SavedIrql ); + + // + // Indicate we used up the swap. + // + + Vcb->SwapVpb = NULL; + + CdUnlockVcb( IrpContext, Vcb ); + + // + // We want to leave the Vpb for the IO system. Mark it + // as being not mounted. Go ahead and delete the Vcb as + // well. + // + + } else { + + // + // Make sure to remove the last reference on the Vpb. + // + + OldVpb->ReferenceCount -= 1; + + OldVpb->DeviceObject = NULL; + ClearFlag( Vcb->Vpb->Flags, VPB_MOUNTED ); + ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED ); + + // + // Clear the Vpb flag so we know not to delete it. + // + + Vcb->Vpb = NULL; + + IoReleaseVpbSpinLock( SavedIrql ); + CdUnlockVcb( IrpContext, Vcb ); + CdDeleteVcb( IrpContext, Vcb ); + VcbPresent = FALSE; + } + + // + // Someone has already swapped in a new Vpb. If this is the final reference + // then the file system is responsible for deleting the Vpb. + // + + } else if (FinalReference) { + + // + // Make sure to remove the last reference on the Vpb. + // + + OldVpb->ReferenceCount -= 1; + + IoReleaseVpbSpinLock( SavedIrql ); + CdUnlockVcb( IrpContext, Vcb ); + CdDeleteVcb( IrpContext, Vcb ); + VcbPresent = FALSE; + + // + // The current Vpb is no longer the Vpb for the device (the IO system + // has already allocated a new one). We leave our reference in the + // Vpb and will be responsible for deleting it at a later time. + // + + } else { + + IoReleaseVpbSpinLock( SavedIrql ); + CdUnlockVcb( IrpContext, Vcb ); + } + + // + // Let our caller know whether the Vcb is still present. + // + + return VcbPresent; +} + diff --git a/filesys/cdfs/volinfo.c b/filesys/cdfs/volinfo.c new file mode 100644 index 00000000..6640fd8a --- /dev/null +++ b/filesys/cdfs/volinfo.c @@ -0,0 +1,598 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + VolInfo.c + +Abstract: + + This module implements the volume information routines for Cdfs called by + the dispatch driver. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_VOLINFO) + +// +// Local support routines +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdQueryFsVolumeInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_ PFILE_FS_VOLUME_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +NTSTATUS +CdQueryFsSizeInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_ PFILE_FS_SIZE_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +NTSTATUS +CdQueryFsDeviceInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_ PFILE_FS_DEVICE_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +NTSTATUS +CdQueryFsAttributeInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_ PFILE_FS_ATTRIBUTE_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +NTSTATUS +CdQueryFsSectorSizeInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_writes_bytes_(*Length) PFILE_FS_SECTOR_SIZE_INFORMATION Buffer, + _Inout_ PULONG Length + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonQueryVolInfo) +#pragma alloc_text(PAGE, CdQueryFsAttributeInfo) +#pragma alloc_text(PAGE, CdQueryFsDeviceInfo) +#pragma alloc_text(PAGE, CdQueryFsSizeInfo) +#pragma alloc_text(PAGE, CdQueryFsVolumeInfo) +#pragma alloc_text(PAGE, CdQueryFsSectorSizeInfo) +#endif + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonQueryVolInfo ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for querying volume information called by both + the fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + ULONG Length; + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Reference our input parameters to make things easier + // + + Length = IrpSp->Parameters.QueryVolume.Length; + + // + // Decode the file object and fail if this an unopened file object. + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + if (TypeOfOpen == UnopenedFileObject) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Acquire the Vcb for this volume. + // + + CdAcquireVcbShared( IrpContext, Fcb->Vcb, FALSE ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify the Vcb. + // + + CdVerifyVcb( IrpContext, Fcb->Vcb ); + + // + // Based on the information class we'll do different actions. Each + // of the procedures that we're calling fills up the output buffer + // if possible and returns true if it successfully filled the buffer + // and false if it couldn't wait for any I/O to complete. + // + + switch (IrpSp->Parameters.QueryVolume.FsInformationClass) { + + case FileFsSizeInformation: + + Status = CdQueryFsSizeInfo( IrpContext, Fcb->Vcb, Irp->AssociatedIrp.SystemBuffer, &Length ); + break; + + case FileFsVolumeInformation: + + Status = CdQueryFsVolumeInfo( IrpContext, Fcb->Vcb, Irp->AssociatedIrp.SystemBuffer, &Length ); + break; + + case FileFsDeviceInformation: + + Status = CdQueryFsDeviceInfo( IrpContext, Fcb->Vcb, Irp->AssociatedIrp.SystemBuffer, &Length ); + break; + + case FileFsAttributeInformation: + + Status = CdQueryFsAttributeInfo( IrpContext, Fcb->Vcb, Irp->AssociatedIrp.SystemBuffer, &Length ); + break; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + case FileFsSectorSizeInformation: + + Status = CdQueryFsSectorSizeInfo( IrpContext, Fcb->Vcb, Irp->AssociatedIrp.SystemBuffer, &Length ); + break; +#endif + } + + // + // Set the information field to the number of bytes actually filled in + // + + Irp->IoStatus.Information = IrpSp->Parameters.QueryVolume.Length - Length; + + } finally { + + // + // Release the Vcb. + // + + CdReleaseVcb( IrpContext, Fcb->Vcb ); + } + + // + // Complete the request if we didn't raise. + // + + CdCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdQueryFsVolumeInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_ PFILE_FS_VOLUME_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume info call + +Arguments: + + Vcb - Vcb for this volume. + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + ULONG BytesToCopy; + + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Fill in the data from the Vcb. + // + + Buffer->VolumeCreationTime = *((PLARGE_INTEGER) &Vcb->VolumeDasdFcb->CreationTime); + Buffer->VolumeSerialNumber = Vcb->Vpb->SerialNumber; + + Buffer->SupportsObjects = FALSE; + + *Length -= FIELD_OFFSET( FILE_FS_VOLUME_INFORMATION, VolumeLabel[0] ); + + // + // Check if the buffer we're given is long enough + // + + if (*Length >= (ULONG) Vcb->Vpb->VolumeLabelLength) { + + BytesToCopy = Vcb->Vpb->VolumeLabelLength; + + } else { + + BytesToCopy = *Length; + + Status = STATUS_BUFFER_OVERFLOW; + } + + // + // Copy over what we can of the volume label, and adjust *Length + // + + Buffer->VolumeLabelLength = BytesToCopy; + + if (BytesToCopy) { + + RtlCopyMemory( &Buffer->VolumeLabel[0], + &Vcb->Vpb->VolumeLabel[0], + BytesToCopy ); + } + + *Length -= BytesToCopy; + + // + // Set our status and return to our caller + // + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +CdQueryFsSizeInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_ PFILE_FS_SIZE_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume size call. + +Arguments: + + Vcb - Vcb for this volume. + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Fill in the output buffer. + // + + Buffer->TotalAllocationUnits.QuadPart = LlSectorsFromBytes( Vcb->VolumeDasdFcb->AllocationSize.QuadPart ); + + Buffer->AvailableAllocationUnits.QuadPart = 0; + Buffer->SectorsPerAllocationUnit = 1; + Buffer->BytesPerSector = SECTOR_SIZE; + + // + // Adjust the length variable + // + + *Length -= sizeof( FILE_FS_SIZE_INFORMATION ); + + // + // And return success to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdQueryFsDeviceInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_ PFILE_FS_DEVICE_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume device call. + +Arguments: + + Vcb - Vcb for this volume. + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Update the output buffer. + // + + Buffer->Characteristics = Vcb->TargetDeviceObject->Characteristics; + Buffer->DeviceType = FILE_DEVICE_CD_ROM; + + // + // Adjust the length variable + // + + *Length -= sizeof( FILE_FS_DEVICE_INFORMATION ); + + // + // And return success to our caller + // + + return STATUS_SUCCESS; +} + + +// +// Local support routine +// + +NTSTATUS +CdQueryFsAttributeInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_ PFILE_FS_ATTRIBUTE_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume attribute call. + +Arguments: + + Vcb - Vcb for this volume. + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + ULONG BytesToCopy; + + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Vcb ); + + // + // Fill out the fixed portion of the buffer. + // + + Buffer->FileSystemAttributes = FILE_CASE_SENSITIVE_SEARCH | + FILE_READ_ONLY_VOLUME | + FILE_SUPPORTS_OPEN_BY_FILE_ID; + + if (FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + SetFlag( Buffer->FileSystemAttributes, FILE_UNICODE_ON_DISK ); + + Buffer->MaximumComponentNameLength = 110; + + } else { + + Buffer->MaximumComponentNameLength = 221; + } + + *Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ); + + // + // Make sure we can copy full unicode characters. + // + + ClearFlag( *Length, 1 ); + + // + // Determine how much of the file system name will fit. + // + + if (*Length >= 8) { + + BytesToCopy = 8; + + } else { + + BytesToCopy = *Length; + Status = STATUS_BUFFER_OVERFLOW; + } + + *Length -= BytesToCopy; + + // + // Do the file system name. + // + + Buffer->FileSystemNameLength = BytesToCopy; + + RtlCopyMemory( &Buffer->FileSystemName[0], L"CDFS", BytesToCopy ); + + // + // And return to our caller + // + + return Status; +} + +#if (NTDDI_VERSION >= NTDDI_WIN8) + +NTSTATUS +CdQueryFsSectorSizeInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PVCB Vcb, + _Out_writes_bytes_(*Length) PFILE_FS_SECTOR_SIZE_INFORMATION Buffer, + _Inout_ PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query sector size information call + This operation will work on any handle and requires no privilege. + +Arguments: + + Vcb - Supplies the Vcb being queried + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return receives the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Sufficient buffer size is guaranteed by the I/O manager or the + // originating kernel mode driver. + // + + ASSERT( *Length >= sizeof( FILE_FS_SECTOR_SIZE_INFORMATION )); + _Analysis_assume_( *Length >= sizeof( FILE_FS_SECTOR_SIZE_INFORMATION )); + + // + // Retrieve the sector size information + // + + Status = FsRtlGetSectorSizeInformation( Vcb->Vpb->RealDevice, + Buffer ); + + // + // Adjust the length variable + // + + if (NT_SUCCESS( Status )) { + + *Length -= sizeof( FILE_FS_SECTOR_SIZE_INFORMATION ); + } + + return Status; +} + +#endif + diff --git a/filesys/cdfs/workque.c b/filesys/cdfs/workque.c new file mode 100644 index 00000000..ee548826 --- /dev/null +++ b/filesys/cdfs/workque.c @@ -0,0 +1,416 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + WorkQue.c + +Abstract: + + This module implements the Work queue routines for the Cdfs File + system. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_WORKQUE) + +// +// The following constant is the maximum number of ExWorkerThreads that we +// will allow to be servicing a particular target device at any one time. +// + +#define FSP_PER_DEVICE_THRESHOLD (2) + +// +// Local support routines +// + +VOID +CdAddToWorkque ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdFsdPostRequest) +#pragma alloc_text(PAGE, CdOplockComplete) +#pragma alloc_text(PAGE, CdPrePostIrp) +#endif + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdFsdPostRequest ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine enqueues the request packet specified by IrpContext to the + work queue associated with the FileSystemDeviceObject. This is a FSD + routine. + +Arguments: + + IrpContext - Pointer to the IrpContext to be queued to the Fsp. + + Irp - I/O Request Packet. + +Return Value: + + STATUS_PENDING + +--*/ + +{ + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // Posting is a three step operation. First lock down any buffers + // in the Irp. Next cleanup the IrpContext for the post and finally + // add this to a workque. + // + + CdPrePostIrp( IrpContext, Irp ); + + CdAddToWorkque( IrpContext, Irp ); + + // + // And return to our caller + // + + return STATUS_PENDING; +} + + + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdPrePostIrp ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs any neccessary work before STATUS_PENDING is + returned with the Fsd thread. This routine is called within the + filesystem and by the oplock package. + +Arguments: + + Context - Pointer to the IrpContext to be queued to the Fsp + + Irp - I/O Request Packet. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + BOOLEAN RemovedFcb; + + PAGED_CODE(); + + ASSERT_IRP_CONTEXT( IrpContext ); + ASSERT_IRP( Irp ); + + // + // Case on the type of the operation. + // + + switch (IrpContext->MajorFunction) { + + case IRP_MJ_CREATE : + + // + // If called from the oplock package then there is an + // Fcb to possibly teardown. We will call the teardown + // routine and release the Fcb if still present. The cleanup + // code in create will know not to release this Fcb because + // we will clear the pointer. + // + + if ((IrpContext->TeardownFcb != NULL) && + *(IrpContext->TeardownFcb) != NULL) { + + CdTeardownStructures( IrpContext, *(IrpContext->TeardownFcb), &RemovedFcb ); + + if (!RemovedFcb) { + + _Analysis_assume_lock_held_((*IrpContext->TeardownFcb)->FcbNonpaged->FcbResource); + CdReleaseFcb( IrpContext, *(IrpContext->TeardownFcb) ); + } + + *(IrpContext->TeardownFcb) = NULL; + IrpContext->TeardownFcb = NULL; + } + + break; + + // + // We need to lock the user's buffer, unless this is an MDL read/write, + // in which case there is no user buffer. + // + + case IRP_MJ_READ : + + if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL )) { + + CdLockUserBuffer( IrpContext, IrpSp->Parameters.Read.Length, IoWriteAccess ); + } + + break; + + case IRP_MJ_WRITE : + + if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL )) { + + CdLockUserBuffer( IrpContext, IrpSp->Parameters.Read.Length, IoReadAccess ); + } + + break; + + // + // We also need to check whether this is a query file operation. + // + + case IRP_MJ_DIRECTORY_CONTROL : + + if (IrpContext->MinorFunction == IRP_MN_QUERY_DIRECTORY) { + + CdLockUserBuffer( IrpContext, IrpSp->Parameters.QueryDirectory.Length, IoWriteAccess ); + } + + break; + } + + // + // Cleanup the IrpContext for the post. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); + CdCleanupIrpContext( IrpContext, TRUE ); + + // + // Mark the Irp to show that we've already returned pending to the user. + // + + IoMarkIrpPending( Irp ); + + return; +} + + + +_Requires_lock_held_(_Global_critical_region_) +VOID +CdOplockComplete ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the oplock package when an oplock break has + completed, allowing an Irp to resume execution. If the status in + the Irp is STATUS_SUCCESS, then we queue the Irp to the Fsp queue. + Otherwise we complete the Irp with the status in the Irp. + + If we are completing due to an error then check if there is any + cleanup to do. + +Arguments: + + Irp - I/O Request Packet. + +Return Value: + + None. + +--*/ + +{ + BOOLEAN RemovedFcb; + + PAGED_CODE(); + + // + // Check on the return value in the Irp. If success then we + // are to post this request. + // + + if (Irp->IoStatus.Status == STATUS_SUCCESS) { + + // + // Check if there is any cleanup work to do. + // + + switch (IrpContext->MajorFunction) { + + case IRP_MJ_CREATE : + + // + // If called from the oplock package then there is an + // Fcb to possibly teardown. We will call the teardown + // routine and release the Fcb if still present. The cleanup + // code in create will know not to release this Fcb because + // we will clear the pointer. + // + + if (IrpContext->TeardownFcb != NULL) { + + CdTeardownStructures( IrpContext, *(IrpContext->TeardownFcb), &RemovedFcb ); + + if (!RemovedFcb) { + + _Analysis_assume_lock_held_((*IrpContext->TeardownFcb)->FcbNonpaged->FcbResource); + CdReleaseFcb( IrpContext, *(IrpContext->TeardownFcb) ); + } + + *(IrpContext->TeardownFcb) = NULL; + IrpContext->TeardownFcb = NULL; + } + + break; + } + + // + // Insert the Irp context in the workqueue. + // + + CdAddToWorkque( IrpContext, Irp ); + + // + // Otherwise complete the request. + // + + } else { + + CdCompleteRequest( IrpContext, Irp, Irp->IoStatus.Status ); + } + + return; +} + + +// +// Local support routine +// + +VOID +CdAddToWorkque ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called to acually store the posted Irp to the Fsp + workque. + +Arguments: + + IrpContext - Pointer to the IrpContext to be queued to the Fsp + + Irp - I/O Request Packet. + +Return Value: + + None. + +--*/ + +{ + PVOLUME_DEVICE_OBJECT Vdo; + KIRQL SavedIrql; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Check if this request has an associated file object, and thus volume + // device object. + // + + if (IrpSp->FileObject != NULL) { + + + Vdo = CONTAINING_RECORD( IrpSp->DeviceObject, + VOLUME_DEVICE_OBJECT, + DeviceObject ); + + // + // Check to see if this request should be sent to the overflow + // queue. If not, then send it off to an exworker thread. + // + + KeAcquireSpinLock( &Vdo->OverflowQueueSpinLock, &SavedIrql ); + + if (Vdo->PostedRequestCount > FSP_PER_DEVICE_THRESHOLD) { + + // + // We cannot currently respond to this IRP so we'll just enqueue it + // to the overflow queue on the volume. + // + + InsertTailList( &Vdo->OverflowQueue, + &IrpContext->WorkQueueItem.List ); + + Vdo->OverflowQueueCount += 1; + + KeReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql ); + + return; + + } else { + + // + // We are going to send this Irp to an ex worker thread so up + // the count. + // + + Vdo->PostedRequestCount += 1; + + KeReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql ); + } + } + + // + // Send it off..... + // + +#pragma prefast(suppress:28155, "the function prototype is correct") + ExInitializeWorkItem( &IrpContext->WorkQueueItem, + CdFspDispatch, + IrpContext ); + +#pragma prefast(suppress: 28159, "prefast believes this routine is obsolete, but it is ok for CDFS to continue using it") + ExQueueWorkItem( &IrpContext->WorkQueueItem, CriticalWorkQueue ); + + return; +} + + + diff --git a/filesys/cdfs/write.c b/filesys/cdfs/write.c new file mode 100644 index 00000000..36a41a7c --- /dev/null +++ b/filesys/cdfs/write.c @@ -0,0 +1,381 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Write.c + +Abstract: + + This module implements the File Write routine for Write called by the + Fsd/Fsp dispatch drivers. + + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_WRITE) + +// +// VOID +// SafeZeroMemory ( +// _Out_ PUCHAR At, +// _In_ ULONG ByteCount +// ); +// + +// +// This macro just puts a nice little try-except around RtlZeroMemory +// + +#define SafeZeroMemory(IC,AT,BYTE_COUNT) { \ + try { \ + RtlZeroMemory( (AT), (BYTE_COUNT) ); \ +__pragma(warning(suppress: 6320)) \ + } except( EXCEPTION_EXECUTE_HANDLER ) { \ + CdRaiseStatus( IC, STATUS_INVALID_USER_BUFFER ); \ + } \ +} + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCommonWrite) +#endif + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +CdCommonWrite ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common entry point for NtWriteFile calls. For synchronous requests, + CommonWrite will complete the request in the current thread. If not + synchronous the request will be passed to the Fsp if there is a need to + block. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The result of this operation. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + TYPE_OF_OPEN TypeOfOpen; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN Wait; + ULONG SynchronousIo; + PVOID UserBuffer; + + LONGLONG StartingOffset; + LONGLONG ByteRange; + ULONG ByteCount; + ULONG WriteByteCount; + ULONG OriginalByteCount; + + BOOLEAN ReleaseFile = TRUE; + + CD_IO_CONTEXT LocalIoContext; + + PAGED_CODE(); + + // + // If this is a zero length write then return SUCCESS immediately. + // + + if (IrpSp->Parameters.Write.Length == 0) { + + CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Decode the file object and verify we support write on this. It + // must be a volume file. + // + + TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ); + + // Internal lock object is acquired if return status is STATUS_PENDING + _Analysis_suppress_lock_checking_(Fcb->Resource); + + if (TypeOfOpen != UserVolumeOpen) { + + CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Examine our input parameters to determine if this is noncached and/or + // a paging io operation. + // + + Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + SynchronousIo = FlagOn( IrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO ); + + + // + // Extract the range of the Io. + // + + StartingOffset = IrpSp->Parameters.Write.ByteOffset.QuadPart; + OriginalByteCount = ByteCount = IrpSp->Parameters.Write.Length; + + ByteRange = StartingOffset + ByteCount; + + // + // Acquire the file shared to perform the write. + // + + CdAcquireFileShared( IrpContext, Fcb ); + + // + // Use a try-finally to facilitate cleanup. + // + + try { + + // + // Verify the Fcb. Allow writes if this is a DASD handle that is + // dismounting the volume. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE )) { + + CdVerifyFcbOperation( IrpContext, Fcb ); + } + + if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO )) { + + // + // Complete the request if it begins beyond the end of file. + // + + if (StartingOffset >= Fcb->FileSize.QuadPart) { + + try_return( Status = STATUS_END_OF_FILE ); + } + + // + // Truncate the write if it extends beyond the end of the file. + // + + if (ByteRange > Fcb->FileSize.QuadPart) { + + ByteCount = (ULONG) (Fcb->FileSize.QuadPart - StartingOffset); + ByteRange = Fcb->FileSize.QuadPart; + } + } + + // + // If we have an unaligned transfer then post this request if + // we can't wait. Unaligned means that the starting offset + // is not on a sector boundary or the write is not integral + // sectors. + // + + WriteByteCount = BlockAlign( Fcb->Vcb, ByteCount ); + + if (SectorOffset( StartingOffset ) || + SectorOffset( WriteByteCount ) || + (WriteByteCount > OriginalByteCount)) { + + if (!Wait) { + + CdRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Make sure we don't overwrite the buffer. + // + + WriteByteCount = ByteCount; + } + + // + // Initialize the IoContext for the write. + // If there is a context pointer, we need to make sure it was + // allocated and not a stale stack pointer. + // + + if (IrpContext->IoContext == NULL || + !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO )) { + + // + // If we can wait, use the context on the stack. Otherwise + // we need to allocate one. + // + + if (Wait) { + + IrpContext->IoContext = &LocalIoContext; + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + + } else { + + IrpContext->IoContext = CdAllocateIoContext(); + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + } + } + + RtlZeroMemory( IrpContext->IoContext, sizeof( CD_IO_CONTEXT ) ); + + // + // Store whether we allocated this context structure in the structure + // itself. + // + + IrpContext->IoContext->AllocatedContext = + BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO ); + + if (Wait) { + + KeInitializeEvent( &IrpContext->IoContext->SyncEvent, + NotificationEvent, + FALSE ); + + } else { + + IrpContext->IoContext->ResourceThreadId = ExGetCurrentResourceThread(); + IrpContext->IoContext->Resource = Fcb->Resource; + IrpContext->IoContext->RequestedByteCount = ByteCount; + } + + Irp->IoStatus.Information = WriteByteCount; + + // + // Set the FO_MODIFIED flag here to trigger a verify when this + // handle is closed. Note that we can err on the conservative + // side with no problem, i.e. if we accidently do an extra + // verify there is no problem. + // + + SetFlag( IrpSp->FileObject->Flags, FO_FILE_MODIFIED ); + + // + // Dasd access is always non-cached. Call the Dasd write routine to + // perform the actual write. + // + + Status = CdVolumeDasdWrite( IrpContext, Fcb, StartingOffset, WriteByteCount ); + + // + // Don't complete this request now if STATUS_PENDING was returned. + // + + if (Status == STATUS_PENDING) { + + Irp = NULL; + ReleaseFile = FALSE; + + // + // Test is we should zero part of the buffer or update the + // synchronous file position. + // + + } else { + + // + // Convert any unknown error code to IO_ERROR. + // + + if (!NT_SUCCESS( Status )) { + + // + // Set the information field to zero. + // + + Irp->IoStatus.Information = 0; + + // + // Raise if this is a user induced error. + // + + if (IoIsErrorUserInduced( Status )) { + + CdRaiseStatus( IrpContext, Status ); + } + + Status = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR ); + + // + // Check if there is any portion of the user's buffer to zero. + // + + } else if (WriteByteCount != ByteCount) { + + CdMapUserBuffer( IrpContext, &UserBuffer ); + + SafeZeroMemory( IrpContext, + Add2Ptr( UserBuffer, + ByteCount, + PVOID ), + WriteByteCount - ByteCount ); + + Irp->IoStatus.Information = ByteCount; + } + + // + // Update the file position if this is a synchronous request. + // + + if (SynchronousIo && NT_SUCCESS( Status )) { + + IrpSp->FileObject->CurrentByteOffset.QuadPart = ByteRange; + } + } + + try_exit: NOTHING; + } finally { + + // + // Release the Fcb. + // + + if (ReleaseFile) { + + CdReleaseFile( IrpContext, Fcb ); + } + } + + // + // Post the request if we got CANT_WAIT. + // + + if (Status == STATUS_CANT_WAIT) { + + Status = CdFsdPostRequest( IrpContext, Irp ); + + // + // Otherwise complete the request. + // + + } else { + + CdCompleteRequest( IrpContext, Irp, Status ); + } + + return Status; +} + + diff --git a/filesys/miniFilter/MetadataManager/DataStore.c b/filesys/miniFilter/MetadataManager/DataStore.c new file mode 100644 index 00000000..736e5088 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/DataStore.c @@ -0,0 +1,1082 @@ +/*++ + +Copyright (c) 2002 - 2003 Microsoft Corporation + +Module Name: + + datastore.c + +Abstract: + + This module contains routines that provide support for storage and + retrieval of the filter metadata manager filter metadata. + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FmmOpenMetadata) +#pragma alloc_text(PAGE, FmmCloseMetadata) +#pragma alloc_text(PAGE, FmmReleaseMetadataFileReferences) +#pragma alloc_text(PAGE, FmmReacquireMetadataFileReferences) +#pragma alloc_text(PAGE, FmmSetMetadataOpenTriggerFileObject) +#pragma alloc_text(PAGE, FmmBeginFileSystemOperation) +#pragma alloc_text(PAGE, FmmEndFileSystemOperation) +#endif + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +FmmOpenMetadata ( + _In_ PFMM_INSTANCE_CONTEXT InstanceContext, + _In_ BOOLEAN CreateIfNotPresent + ) +/*++ + +Routine Description: + + This routine opens or creates the Fmm metadata on the specified instance. + +Arguments: + + InstanceContext - Supplies the instance context for this instance. + CreateIfNotPresent - Supplies if the directory entry must be created if it is not present + +Return Value: + + Returns the status of this operation. + +Note: + + The caller must hold the instance context resource exclusive when this routine is called. + +--*/ +{ + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK ioStatus; + UNICODE_STRING fileName; + NTSTATUS status; + ULONG length; + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Opening metadata file ... (Volume = %p, CreateIfNotPresent = %X)\n", + InstanceContext->Volume, + CreateIfNotPresent) ); + + status = STATUS_SUCCESS; + fileName.Buffer = NULL; + + // + // Get the volume name and construct the full metadata filename. + // + + + length = FMM_DEFAULT_VOLUME_NAME_LENGTH + FMM_METADATA_FILE_NAME_LENGTH; + +#pragma warning(push) +#pragma warning(disable:4127) // Conditional expression is constant + while (TRUE) { + +#pragma warning(pop) + + fileName.MaximumLength = (USHORT)length; + + status = FmmAllocateUnicodeString( &fileName ); + + if (!NT_SUCCESS( status )) { + + goto FmmOpenMetadataCleanup; + } + + status = FltGetVolumeName( InstanceContext->Volume, &fileName, &length ); + + if (NT_SUCCESS( status )) { + + status = RtlAppendUnicodeToString( &fileName, FMM_METADATA_FILE_NAME ); + + if (NT_SUCCESS( status )) { + + break; + } + } else { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to get volume name (Volume = %p, Status = 0x%x)\n", + InstanceContext->Volume, + status) ); + } + + + if (status != STATUS_BUFFER_TOO_SMALL) { + + goto FmmOpenMetadataCleanup;; + } + + // + // Free the filename buffer since a bigger one will be allocated + // above + // + + FmmFreeUnicodeString( &fileName ); + + length += FMM_METADATA_FILE_NAME_LENGTH; + } + + + // + // Initialize the object attributes and open the file. + // + + InitializeObjectAttributes( &objectAttributes, + &fileName, + OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + + + +RetryFltCreateFile: + + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Calling FltCreateFile for metadata file %wZ (Volume = %p, Status = 0x%x)\n", + &fileName, + InstanceContext->Volume, + status) ); + + + // + // Mark the beginning of a file system operation + // + + FmmBeginFileSystemOperation( InstanceContext ); + + status = FltCreateFile( Globals.Filter, + InstanceContext->Instance, + &InstanceContext->MetadataHandle, + FILE_ALL_ACCESS, + &objectAttributes, + &ioStatus, + (PLARGE_INTEGER) NULL, + FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, + FILE_SHARE_READ, + (CreateIfNotPresent ? FILE_OPEN_IF : FILE_OPEN), + 0L, + NULL, + 0L, + 0 ); + + // + // Mark the end of a file system operation + // + + FmmEndFileSystemOperation( InstanceContext ); + + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Fmm]: FltCreateFile failure for metadata file %wZ (Volume = %p, Status = 0x%x)\n", + &fileName, + InstanceContext->Volume, + status) ); + + if (CreateIfNotPresent && (status == STATUS_OBJECT_PATH_NOT_FOUND)) { + + // + // We need to create the metadata file and the creation failed + // because the SystemVolumeInformation folder does not exist. + // So, create the folder and try again. + // + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Creating SystemVolumeInformation folder for metadata file %wZ (Volume = %p, Status = 0x%x)\n", + &fileName, + InstanceContext->Volume, + status) ); + + + // + // Mark the beginning of a file system operation + // + + FmmBeginFileSystemOperation( InstanceContext ); + + status = FltCreateSystemVolumeInformationFolder( InstanceContext->Instance ); + + // + // Mark the end of a file system operation + // + + FmmEndFileSystemOperation( InstanceContext ); + + + + if (NT_SUCCESS( status )) { + + // + // We have sucessfully created the SystemVolumeInformation folder + // Try to create the metadata file again + // + + goto RetryFltCreateFile; + } else { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Fmm]: FltCreateSystemVolumeInformationFolder failure for metadata file %wZ (Volume = %p, Status = 0x%x)\n", + &fileName, + InstanceContext->Volume, + status) ); + } + } + + goto FmmOpenMetadataCleanup; + } + + // + // Retrieve the FileObject from the handle created + // + + status = ObReferenceObjectByHandle( InstanceContext->MetadataHandle, + STANDARD_RIGHTS_REQUIRED, + *IoFileObjectType, + KernelMode, + &InstanceContext->MetadataFileObject, + NULL ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Fmm]: Failure to get file object from handle for metadata file %wZ (Volume = %p, Status = 0x%x)\n", + &fileName, + InstanceContext->Volume, + status) ); + + goto FmmOpenMetadataCleanup; + } + + if (ioStatus.Information == FILE_CREATED) { + + // + // New metadata was created + // + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Created new metadata file %wZ (Volume = %p, Status = 0x%x)\n", + &fileName, + InstanceContext->Volume, + status) ); + + // + // The filter may want to do some initialization on the newly created + // metadata file here like adding a header to the file + // + + } + else { + + // + // Existing metadata was opened + // + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Opened existing metadata file %wZ (Volume = %p, Status = 0x%x)\n", + &fileName, + InstanceContext->Volume, + status) ); + + // + // The filter may want to do some sanity checks on the metadata file here + // like validating the header of the file + // + + } + + // + // Here the filter may read the metadata contents and initialize + // its in memory data structures with the data from the metadata + // file + // + + +FmmOpenMetadataCleanup: + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to open metadata (Volume = %p, Status = 0x%x)\n", + InstanceContext->Volume, + status) ); + + // + // CLose the handle and dereference the file object + // + + if (InstanceContext->MetadataHandle) { + + + // + // Mark the beginning of a file system operation + // + + FmmBeginFileSystemOperation( InstanceContext ); + + FltClose( InstanceContext->MetadataHandle ); + + // + // Mark the end of a file system operation + // + + FmmEndFileSystemOperation( InstanceContext ); + + InstanceContext->MetadataHandle = NULL; + + if (InstanceContext->MetadataFileObject) { + + ObDereferenceObject( InstanceContext->MetadataFileObject ); + InstanceContext->MetadataFileObject = NULL; + } + } + } else { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Metadata successfully opened (Volume = %p)\n", + InstanceContext->Volume) ); + + // + // Set flags to indicate successful open of filter metadata + // + + SetFlag( InstanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED ); + + } + + if (fileName.Buffer != NULL) { + + FmmFreeUnicodeString( &fileName ); + } + + return status; +} + + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +FmmCloseMetadata ( + _In_ PFMM_INSTANCE_CONTEXT InstanceContext + ) +/*++ + +Routine Description: + + This routine closes the filters handle to the metadata file. + +Arguments: + + InstanceContext - Instance context for this instance. + +Return Value: + + Void. + +Note: + + The caller must hold the instance context resource when this routine is called. + + +--*/ +{ + PAGED_CODE(); + + FLT_ASSERT( InstanceContext->MetadataHandle ); + FLT_ASSERT( InstanceContext->MetadataFileObject ); + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Closing metadata file ... (Volume = %p)\n", + InstanceContext->Volume ) ); + + // + // Dereference the file object and close the file handle. + // + + ObDereferenceObject( InstanceContext->MetadataFileObject ); + + InstanceContext->MetadataFileObject = NULL; + + + // + // Mark the beginning of a file system operation + // + + FmmBeginFileSystemOperation( InstanceContext ); + + FltClose( InstanceContext->MetadataHandle ); + + // + // Mark the end of a file system operation + // + + FmmEndFileSystemOperation( InstanceContext ); + + + InstanceContext->MetadataHandle = NULL; + + // + // Reset flag to indicate filter metadata is closed + // + + ClearFlag( InstanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED ); + +} + +NTSTATUS +FmmReleaseMetadataFileReferences ( + _Inout_ PFLT_CALLBACK_DATA Cbd + ) +/*++ + +Routine Description: + + This routine releases all references to the metadata file on the specified instance. + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + +Return Value: + + Status + +Note: + + This routine takes care of the synchronization needed to access the metadata + file object and handle + + This routine will also set the MetadataOpenTriggerFileObject in the instance context + to the file object of the volume that triggered the release of the metadata file + references. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PFMM_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + // + // Get the instance context + // + + status = FltGetInstanceContext( Cbd->Iopb->TargetInstance, + &instanceContext ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmReleaseMetadataFileReferences -> Failed to get instance context.\n") ); + + goto FmmReleaseMetadataFileReferencesCleanup; + } + + // + // Acquire exclusive access to the instance context + // + + FmmAcquireResourceExclusive( &instanceContext->MetadataResource ); + + if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION)) { + + // + // If this instance context is in a transition state, it implies that + // the instance context lock has been released while sending an operation + // down to the file system. The reason for doing so is to prevent a potential + // deadlock if an underlying filter sends an IO to the top of the filter + // stack while we are holding the resource + // + // We have managed to acquire this resource in this state of transition. + // It would be incorrect to use or modify the instance context in any way + // in this situation. So we simply let go. + // + + status = STATUS_FILE_LOCK_CONFLICT; + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmReleaseMetadataFileReferences -> Failed to get exclusive access to instance context since it is in a state of transition.\n") ); + } else { + + // + // Close the metadata file if it is open + // + + if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmReleaseMetadataFileReferences -> Releasing references to metadata handle and file object (InstanceContext = %p VolumeFileObject = %p)\n", + instanceContext, + Cbd->Iopb->TargetFileObject) ); + + // + // Close the metadata file object + // + + FmmCloseMetadata( instanceContext ); + + // + // Save the volume file object for which we are releasing our references + // + + instanceContext->MetadataOpenTriggerFileObject = Cbd->Iopb->TargetFileObject; + } else { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmReleaseMetadataFileReferences -> Exit without attempting to release references to metadata handle and file object (InstanceContext = %p, VolumeFileObject = %p, MetadataOpenTriggerFileObject = %p, MetadataAlreadyOpen = 0x%x)\n", + instanceContext, + Cbd->Iopb->TargetFileObject, + instanceContext->MetadataOpenTriggerFileObject, + FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) ); + } + } + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &instanceContext->MetadataResource ); + + +FmmReleaseMetadataFileReferencesCleanup: + + // + // Release the references we have acquired + // + + if (instanceContext != NULL) { + + FltReleaseContext( instanceContext ); + } + + + return status; +} + + +NTSTATUS +FmmReacquireMetadataFileReferences ( + _Inout_ PFLT_CALLBACK_DATA Cbd + ) +/*++ + +Routine Description: + + This routine re-acquires references to the metadata file on the specified instance. + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + +Return Value: + + Status + +Note: + + This routine takes care of the synchronization needed to access the metadata + file object and handle + + + This routine will also NULL the MetadataOpenTriggerFileObject in the instance context + if it was successfully able to open the metadata file references. + + +--*/ +{ + + NTSTATUS status = STATUS_SUCCESS; + PFMM_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + // + // Get the instance context + // + + status = FltGetInstanceContext( Cbd->Iopb->TargetInstance, + &instanceContext ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmReacquireMetadataFileReferences -> Failed to get instance context.\n") ); + + goto FmmReacquireMetadataFileReferencesCleanup; + } + + // + // Acquire exclusive access to the instance context + // + + FmmAcquireResourceExclusive( &instanceContext->MetadataResource ); + + if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION)) { + + // + // If this instance context is in a transition state, it implies that + // the instance context lock has been released while sending an operation + // down to the file system. The reason for doing so is to prevent a potential + // deadlock if an underlying filter sends an IO to the top of the filter + // stack while we are holding the resource + // + // We have managed to acquire this resource in this state of transition. + // It would be incorrect to use or modify the instance context in any way + // in this situation. So we simply let go. + // + + status = STATUS_FILE_LOCK_CONFLICT; + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmReacquireMetadataFileReferences -> Failed to get exclusive access to instance context since it is in a state of transition.\n") ); + } else { + + // + // Re-open the metadata only if the trigger file object match the file object that + // caused this function to be called + // + + if (instanceContext->MetadataOpenTriggerFileObject == Cbd->Iopb->TargetFileObject) { + + // + // Open the filter metadata file (do not read the file since we already have + // stuff in memory and do not create if the file does not exist + // + + if (!FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmReacquireMetadataFileReferences -> Re-acquiring references to metadata handle and file object (InstanceContext = %p, VolumeFileObject = %p)\n", + instanceContext, + Cbd->Iopb->TargetFileObject) ); + + status = FmmOpenMetadata( instanceContext, + FALSE ); + + // + // Reset the trigger file object since the volume open failed. + // + + instanceContext->MetadataOpenTriggerFileObject = NULL; + + } else { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmReacquireMetadataFileReferences -> Exit without attempting to re-acquire references to metadata handle and file object (InstanceContext = %p, VolumeFileObject = %p, MetadataOpenTriggerFileObject = %p, MetadataAlreadyOpen = 0x%x)\n", + instanceContext, + Cbd->Iopb->TargetFileObject, + instanceContext->MetadataOpenTriggerFileObject, + FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) ); + } + } else { + + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmReacquireMetadataFileReferences -> Exit without attempting to re-acquire references to metadata handle and file object (InstanceContext = %p, VolumeFileObject = %p, MetadataOpenTriggerFileObject = %p, MetadataAlreadyOpen = 0x%x)\n", + instanceContext, + Cbd->Iopb->TargetFileObject, + instanceContext->MetadataOpenTriggerFileObject, + FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) ); + } + } + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &instanceContext->MetadataResource ); + + +FmmReacquireMetadataFileReferencesCleanup: + + // + // Release the references we have acquired + // + + if (instanceContext != NULL) { + + FltReleaseContext( instanceContext ); + } + + + return status;; + +} + + + +NTSTATUS +FmmSetMetadataOpenTriggerFileObject ( + _Inout_ PFLT_CALLBACK_DATA Cbd + ) +/*++ + +Routine Description: + + This routine sets the MetadataOpenTriggerFileObject in the instance context + to the file object of the volume that triggered the release of the metadata file + references. + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + +Return Value: + + Status + +Note: + + This routine takes care of the synchronization needed to access the metadata + file object and handle + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PFMM_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + // + // Get the instance context + // + + status = FltGetInstanceContext( Cbd->Iopb->TargetInstance, + &instanceContext ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmSetMetadataOpenTriggerFileObject -> Failed to get instance context.\n") ); + + goto FmmSetMetadataOpenTriggerFileObjectCleanup; + } + + // + // Acquire exclusive access to the instance context + // + + FmmAcquireResourceExclusive( &instanceContext->MetadataResource ); + + if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION)) { + + // + // If this instance context is in a transition state, it implies that + // the instance context lock has been released while sending an operation + // down to the file system. The reason for doing so is to prevent a potential + // deadlock if an underlying filter sends an IO to the top of the filter + // stack while we are holding the resource + // + // We have managed to acquire this resource in this state of transition. + // It would be incorrect to use or modify the instance context in any way + // in this situation. So we simply let go. + // + + status = STATUS_FILE_LOCK_CONFLICT; + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmSetMetadataOpenTriggerFileObject -> Failed to get exclusive access to instance context since it is in a state of transition.\n") ); + } else { + + DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmSetMetadataOpenTriggerFileObject -> Setting MetadataOpenTriggerFileObject to %p (OldValue = %p).\n", + Cbd->Iopb->TargetFileObject, + instanceContext->MetadataOpenTriggerFileObject) ); + + + // + // Save the volume file object as the trigger file object + // + + FLT_ASSERT((instanceContext->MetadataOpenTriggerFileObject == NULL) || + (instanceContext->MetadataOpenTriggerFileObject == Cbd->Iopb->TargetFileObject)); + + instanceContext->MetadataOpenTriggerFileObject = Cbd->Iopb->TargetFileObject; + } + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &instanceContext->MetadataResource ); + + +FmmSetMetadataOpenTriggerFileObjectCleanup: + + // + // Release the references we have acquired + // + + if (instanceContext != NULL) { + + FltReleaseContext( instanceContext ); + } + + + return status;; +} + +_Releases_lock_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_Releases_lock_(InstanceContext->MetadataResource) +_IRQL_requires_max_(APC_LEVEL) +FORCEINLINE +VOID +FmmBeginFileSystemOperation ( + IN PFMM_INSTANCE_CONTEXT InstanceContext + ) +/*++ + +Routine Description: + + This routine must be called before the filter performs a file system operation + if it is holding an exclusive lock to the instance context resource at the + time it needs to perform the file system operation + +Arguments: + + InstanceContext - Supplies the instance context for this instance. + +Return Value: + + Returns the status of this operation. + +Note: + + The caller must hold the instance context resource exclusive when this routine is called. + +--*/ +{ + PAGED_CODE(); + + // + // Release the instance context lock before sending an operation down to the + // file system. The reason for doing so is to prevent a potential deadlock if + // an underlying filter sends an IO to the top of the filter stack while we + // are holding the resource + // + // Before we release the lock we mark the instance context to indicate it is + // in a transition state. Any other thread that finds the instance context in a + // transition state will not use or modify the instance context + // + // This thread can however continue to use/modify the instance context since it + // is guaranteed exclusive access. Other threads that see the instance context + // in a transition state will not use or modify the context + // + + FLT_ASSERT( !FlagOn( InstanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION ) ); + + SetFlag( InstanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION ); + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &InstanceContext->MetadataResource ); + +} + + +_Acquires_lock_(_Global_critical_region_) +_Requires_lock_not_held_(InstanceContext->MetadataResource) +_Acquires_exclusive_lock_(InstanceContext->MetadataResource) +_IRQL_requires_max_(APC_LEVEL) +FORCEINLINE +VOID +FmmEndFileSystemOperation ( + IN PFMM_INSTANCE_CONTEXT InstanceContext + ) +/*++ + +Routine Description: + + This routine must be called after the filter performs a file system operation + if it was holding an exclusive lock to the instance context resource at the + time it needed to perform the file system operation + +Arguments: + + InstanceContext - Supplies the instance context for this instance. + +Return Value: + + Returns the status of this operation. + +Note: + + The caller will hold the instance context resource exclusive when this routine returns. + +--*/ +{ + PAGED_CODE(); + + // + // Acquire exclusive access to the instance context + // + + FmmAcquireResourceExclusive( &InstanceContext->MetadataResource ); + + + // + // Sanity - nothing should have changed this flag while we dropped the resource + // because all other threads will not use or modify the instance context while + // this flag is set + // + + FLT_ASSERT( FlagOn( InstanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION ) ); + + + // + // Reset the flag to indicate that the instance context is no longer in + // a transition state + // + + ClearFlag( InstanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION ); + +} + + + +#if VERIFY_METADATA_OPENED + +NTSTATUS +FmmIsMetadataOpen ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _Out_ BOOLEAN* MetadataOpen + ) +/*++ + +Routine Description: + + This routine returns if the metadata file is open on the specified instance. + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + MetadataOpen - Returns if the metadata file is open + +Return Value: + + Status + +Note: + + This routine takes care of the synchronization needed to access the metadata + file object and handle + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PFMM_INSTANCE_CONTEXT instanceContext = NULL; + + + // + // Get the instance context + // + + status = FltGetInstanceContext( Cbd->Iopb->TargetInstance, + &instanceContext ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmIsMetadataOpen -> Failed to get instance context.\n") ); + + goto FmmIsMetadataOpenCleanup; + } + + // + // Acquire exclusive access to the instance context + // + + FmmAcquireResourceShared( &instanceContext->MetadataResource ); + + if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION)) { + + // + // If this instance context is in a transition state, it implies that + // the instance context lock has been released while sending an operation + // down to the file system. The reason for doing so is to prevent a potential + // deadlock if an underlying filter sends an IO to the top of the filter + // stack while we are holding the resource + // + // We have managed to acquire this resource in this state of transition. + // It would be incorrect to use or modify the instance context in any way + // in this situation. So we simply let go. + // + + status = STATUS_FILE_LOCK_CONFLICT; + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmIsMetadataOpen -> Failed to get exclusive access to instance context since it is in a state of transition.\n") ); + } else { + + // + // Return if the metadata is opened + // + + *MetadataOpen = BooleanFlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED ); + + // + // Sanity - verify that this flag is reflecting the correct state of the metadata file + // + + FLT_ASSERT ( (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED ) && + (instanceContext->MetadataFileObject != NULL) && + (instanceContext->MetadataHandle != NULL)) || + (!FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED ) && + (instanceContext->MetadataFileObject == NULL) && + (instanceContext->MetadataHandle == NULL)) ); + + } + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &instanceContext->MetadataResource ); + + +FmmIsMetadataOpenCleanup: + + // + // Release the references we have acquired + // + + if (instanceContext != NULL) { + + FltReleaseContext( instanceContext ); + } + + + return status; +} + + +#endif + + diff --git a/filesys/miniFilter/MetadataManager/MetadataManager.rc b/filesys/miniFilter/MetadataManager/MetadataManager.rc new file mode 100644 index 00000000..850a65e4 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/MetadataManager.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Metadata Management File System Filter Driver Sample" +#define VER_INTERNALNAME_STR "FMM.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/MetadataManager/MetadataManager.sln b/filesys/miniFilter/MetadataManager/MetadataManager.sln new file mode 100644 index 00000000..9af2fc43 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/MetadataManager.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmm", "fmm.vcxproj", "{A95B7D4F-B926-4E1F-A051-E66091E08D3A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|Win32.ActiveCfg = Debug|Win32 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|Win32.Build.0 = Debug|Win32 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|Win32.ActiveCfg = Release|Win32 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|Win32.Build.0 = Release|Win32 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|x64.ActiveCfg = Debug|x64 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|x64.Build.0 = Debug|x64 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|x64.ActiveCfg = Release|x64 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/MetadataManager/MetadataManagerInit.c b/filesys/miniFilter/MetadataManager/MetadataManagerInit.c new file mode 100644 index 00000000..222159f7 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/MetadataManagerInit.c @@ -0,0 +1,865 @@ +/*++ + +Copyright (c) 1999 - 2003 Microsoft Corporation + +Module Name: + + MetadataManagerInit.c + +Abstract: + + This is the main module of the kernel mode filter driver implementing + filter metadata management. + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + +// +// Global variables +// + +FMM_GLOBAL_DATA Globals; + + +// +// Local constants +// + +#define FMM_UNSUPPORTED_DEVICE_CHARACS FILE_FLOPPY_DISKETTE | \ + FILE_READ_ONLY_DEVICE | \ + FILE_VIRTUAL_VOLUME + +// +// Local function prototypes +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +FmmUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +VOID +FmmContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +NTSTATUS +FmmInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +NTSTATUS +FmmInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +VOID +FmmInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +FmmInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +#if DBG + +VOID +FmmInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ); + +#endif + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) + +#if DBG +#pragma alloc_text(INIT, FmmInitializeDebugLevel) +#endif + +#pragma alloc_text(PAGE, FmmUnload) +#pragma alloc_text(PAGE, FmmContextCleanup) +#pragma alloc_text(PAGE, FmmInstanceSetup) +#pragma alloc_text(PAGE, FmmInstanceQueryTeardown) +#pragma alloc_text(PAGE, FmmInstanceTeardownStart) +#pragma alloc_text(PAGE, FmmInstanceTeardownComplete) +#endif + + +// +// If we need to verify that the metadata file is indeed open whenever +// a create suceeds on the volume, then we need to monitor all creates +// not just DASD creates. + +// If that is not the case, then we are better off telling filter manager +// to show us only DASD creates. That way we can avoid the performance +// penalty of being called for all creates when we only have use for DASD +// creates. +// + + +#if VERIFY_METADATA_OPENED + +#define OPERATION_REGISTRATION_FLAGS_FOR_CREATE (FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO) + +#else + +#define OPERATION_REGISTRATION_FLAGS_FOR_CREATE (FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO | FLTFL_OPERATION_REGISTRATION_SKIP_NON_DASD_IO) + +#endif + + + +// +// Filters callback routines +// + +FLT_OPERATION_REGISTRATION Callbacks[] = { + + { IRP_MJ_CREATE, + OPERATION_REGISTRATION_FLAGS_FOR_CREATE, + FmmPreCreate, + FmmPostCreate }, + + { IRP_MJ_CLEANUP, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO | FLTFL_OPERATION_REGISTRATION_SKIP_NON_DASD_IO, + FmmPreCleanup, + FmmPostCleanup }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO | FLTFL_OPERATION_REGISTRATION_SKIP_NON_DASD_IO, + FmmPreFSControl, + FmmPostFSControl }, + + { IRP_MJ_DEVICE_CONTROL, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + FmmPreDeviceControl, + FmmPostDeviceControl }, + + { IRP_MJ_SHUTDOWN, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + FmmPreShutdown, + NULL }, + + { IRP_MJ_PNP, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + FmmPrePnp, + FmmPostPnp }, + + { IRP_MJ_OPERATION_END } +}; + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_INSTANCE_CONTEXT, + 0, + FmmContextCleanup, + FMM_INSTANCE_CONTEXT_SIZE, + FMM_INSTANCE_CONTEXT_TAG }, + + { FLT_CONTEXT_END } +}; + +// +// Filters registration data structure +// + +FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + ContextRegistration, // Context + Callbacks, // Operation callbacks + FmmUnload, // Filters unload routine + FmmInstanceSetup, // InstanceSetup routine + FmmInstanceQueryTeardown, // InstanceQueryTeardown routine + FmmInstanceTeardownStart, // InstanceTeardownStart routine + FmmInstanceTeardownComplete, // InstanceTeardownComplete routine + NULL, NULL, NULL // Unused naming support callbacks +}; + +// +// Filter driver initialization and unload routines +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this filter driver. It registers + itself with the filter manager and initializes all its global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS status; + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + + RtlZeroMemory( &Globals, sizeof( Globals ) ); + +#if DBG + + // + // Initialize global debug level + // + + FmmInitializeDebugLevel( RegistryPath ); + +#else + + UNREFERENCED_PARAMETER( RegistryPath ); + +#endif + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Fmm]: Driver being loaded\n") ); + + + + // + // Register with the filter manager + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // Start filtering I/O + // + + status = FltStartFiltering( Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( Globals.Filter ); + } + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Fmm]: Driver loaded complete (Status = 0x%08X)\n", + status) ); + + return status; +} + +#if DBG + +VOID +FmmInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to read the filter DebugLevel parameter from + the registry. This value will be found in the registry location + indicated by the RegistryPath passed in. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + None. + +--*/ +{ + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey; + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING valueName; + UCHAR buffer[sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + sizeof( LONG )]; + + Globals.DebugLevel = DEBUG_TRACE_ERROR; + + // + // Open the desired registry key + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (NT_SUCCESS( status )) { + + // + // Read the DebugFlags value from the registry. + // + + RtlInitUnicodeString( &valueName, L"DebugLevel" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.DebugLevel = *((PULONG) &(((PKEY_VALUE_PARTIAL_INFORMATION) buffer)->Data)); + } + + // + // Close the registry entry + // + + ZwClose( driverRegKey ); + + } + +} + +#endif + +NTSTATUS +FmmUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this filter driver. This is called + when the minifilter is about to be unloaded. We can fail this unload + request if this is not a mandatory unloaded indicated by the Flags + parameter. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Fmm]: Unloading driver\n") ); + + + FltUnregisterFilter( Globals.Filter ); + Globals.Filter = NULL; + + return STATUS_SUCCESS; +} + +VOID +FmmContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +{ + PFMM_INSTANCE_CONTEXT instanceContext; + + PAGED_CODE(); + + switch(ContextType) { + + case FLT_INSTANCE_CONTEXT: + + instanceContext = Context; + + DebugTrace( DEBUG_TRACE_INFO, + ("[Fmm]: Cleaning up instance context for volume (Context = %p)\n", + instanceContext) ); + + ExDeleteResourceLite( &instanceContext->MetadataResource ); + + break; + + } + + DebugTrace( DEBUG_TRACE_INFO, + ("[Fmm]: Context cleanup complete.\n") ); + +} + +// +// Instance setup/teardown routines. +// + +NTSTATUS +FmmInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. This + gives us a chance to decide if we need to attach to this volume or not. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + PFMM_INSTANCE_CONTEXT instanceContext = NULL; + PDEVICE_OBJECT diskDeviceObject; + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER( VolumeDeviceType ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance setup started (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + // + // Check if the file system mounted is ntfs or fat + // + // The sample picks NTFS, FAT and ReFS as examples. The metadata + // handling demostrated in the sample can be applied + // to any file system + // + + if (VolumeFilesystemType != FLT_FSTYPE_NTFS && VolumeFilesystemType != FLT_FSTYPE_FAT && VolumeFilesystemType != FLT_FSTYPE_REFS) { + + // + // An unknown file system is mounted which we do not care + // + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Unsupported file system mounted (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + status = STATUS_NOT_SUPPORTED; + goto FmmInstanceSetupCleanup; + } + + // + // Get the disk device object and make sure it is a disk device type and does not + // have any of the device characteristics we do not support. + // + // The sample picks the device characteristics to demonstrate how to access and + // check the device characteristics in order to make a decision to attach. The + // metadata handling demostrated in the sample is not limited to the + // characteristics we have used in the sample. + // + + status = FltGetDiskDeviceObject( FltObjects->Volume, &diskDeviceObject ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to get device object (Volume = %p, Status = 0x%08X)\n", + FltObjects->Volume, + status) ); + goto FmmInstanceSetupCleanup; + } + + if (diskDeviceObject->DeviceType != FILE_DEVICE_DISK || + FlagOn( diskDeviceObject->Characteristics, FMM_UNSUPPORTED_DEVICE_CHARACS )) { + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Unsupported device type or device characteristics (Volume = %p, Instance = %p DiskDeviceObjectDeviceTYpe = 0x%x, DiskDeviceObjectCharacteristics = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + diskDeviceObject->DeviceType, + diskDeviceObject->Characteristics) ); + + ObDereferenceObject( diskDeviceObject ); + status = STATUS_NOT_SUPPORTED; + goto FmmInstanceSetupCleanup; + } + + ObDereferenceObject( diskDeviceObject ); + + // + // Allocate and initialize the context for this volume + // + + status = FltAllocateContext( FltObjects->Filter, + FLT_INSTANCE_CONTEXT, + FMM_INSTANCE_CONTEXT_SIZE, + NonPagedPool, + &instanceContext ); + + if( !NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to allocate instance context (Volume = %p, Instance = %p, Status = 0x%08X)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + + goto FmmInstanceSetupCleanup; + } + + FLT_ASSERT( instanceContext != NULL ); + + RtlZeroMemory( instanceContext, FMM_INSTANCE_CONTEXT_SIZE ); + + instanceContext->Flags = 0; + instanceContext->Instance = FltObjects->Instance; + instanceContext->FilesystemType = VolumeFilesystemType; + instanceContext->Volume = FltObjects->Volume; + ExInitializeResourceLite( &instanceContext->MetadataResource ); + + + // + // Set the instance context. + // + + status = FltSetInstanceContext( FltObjects->Instance, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + instanceContext, + NULL ); + + if( !NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to set instance context (Volume = %p, Instance = %p, Status = 0x%08X)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + goto FmmInstanceSetupCleanup; + } + + // + // Acquire exclusive access to the instance context + // + + FmmAcquireResourceExclusive( &instanceContext->MetadataResource ); + + // + // Sanity - the instance context cannot be in a transition state during instance setup + // + + FLT_ASSERT( !FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION) ); + + // + // Open the filter metadata on disk + // + // The sample will attach to volume if it finds its metadata file on the volume. + // If this is a manual attachment then the sample filter will create its metadata + // file and attach to the volume. + // + + status = FmmOpenMetadata( instanceContext, + BooleanFlagOn( Flags, FLTFL_INSTANCE_SETUP_MANUAL_ATTACHMENT ) ); + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &instanceContext->MetadataResource ); + + if (!NT_SUCCESS( status )) { + + goto FmmInstanceSetupCleanup; + } + + +FmmInstanceSetupCleanup: + + // + // If FltAllocateContext suceeded then we MUST release the context, + // irrespective of whether FltSetInstanceContext suceeded or not. + // + // FltAllocateContext increments the ref count by one. + // A successful FltSetInstanceContext increments the ref count by one + // and also associates the context with the file system object + // + // FltReleaseContext decrements the ref count by one. + // + // When FltSetInstanceContext succeeds, calling FltReleaseContext will + // leave the context with a ref count of 1 corresponding to the internal + // reference to the context from the file system structures + // + // When FltSetInstanceContext fails, calling FltReleaseContext will + // leave the context with a ref count of 0 which is correct since + // there is no reference to the context from the file system structures + // + + if ( instanceContext != NULL ) { + + FltReleaseContext( instanceContext ); + } + + + if (NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance setup complete (Volume = %p, Instance = %p). Filter will attach to the volume.\n", + FltObjects->Volume, + FltObjects->Instance) ); + } else { + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance setup complete (Volume = %p, Instance = %p). Filter will not attach to the volume.\n", + FltObjects->Volume, + FltObjects->Instance) ); + } + + // + // If this is an automatic attachment (mount, load, etc) and we are not + // attaching to this volume because we do not support attaching to this + // volume, then simply return STATUS_FLT_DO_NOT_ATTACH. If we return + // anything else fltmgr logs an event log indicating failure to attach. + // Since this failure to attach is not really an error, we do not want + // this failure to be logged as an error in the event log. For all other + // error codes besides the ones we consider "normal", if is ok for fltmgr + // to actually log the failure to attach. + // + // If this is a manual attach attempt that we have failed then we want to + // give the user a clear indication of why the attachment failed. Hence in + // this case, we will not override the error status with STATUS_FLT_DO_NOT_ATTACH + // irrespective of the cause of the failure to attach + // + + if (status == STATUS_NOT_SUPPORTED && + !FlagOn( Flags, FLTFL_INSTANCE_SETUP_MANUAL_ATTACHMENT )) { + + status = STATUS_FLT_DO_NOT_ATTACH; + } + + return status; +} + + +NTSTATUS +FmmInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach thereby giving us a + chance to fail that detach request. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Returns the status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance query teardown started (Instance = %p)\n", + FltObjects->Instance) ); + + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance query teadown ended (Instance = %p)\n", + FltObjects->Instance) ); + return STATUS_SUCCESS; +} + + +VOID +FmmInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the start of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance teardown start started (Instance = %p)\n", + FltObjects->Instance) ); + + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance teardown start ended (Instance = %p)\n", + FltObjects->Instance) ); +} + + +VOID +FmmInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the end of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + PFMM_INSTANCE_CONTEXT instanceContext; + NTSTATUS status; + + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance teardown complete started (Instance = %p)\n", + FltObjects->Instance) ); + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (NT_SUCCESS( status )) { + + // + // Acquire exclusive access to the instance context + // + + FmmAcquireResourceExclusive( &instanceContext->MetadataResource ); + + // + // Sanity - the instance context cannot be in a transition state during instance teardown complete + // + + FLT_ASSERT( !FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION) ); + + + if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) { + + // + // Close the metadata file + // + + FmmCloseMetadata( instanceContext ); + } + + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &instanceContext->MetadataResource ); + + FltReleaseContext( instanceContext ); + } + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance teardown complete ended (Instance = %p)\n", + FltObjects->Instance) ); +} + diff --git a/filesys/miniFilter/MetadataManager/MetadataManagerProc.h b/filesys/miniFilter/MetadataManager/MetadataManagerProc.h new file mode 100644 index 00000000..bee71cd7 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/MetadataManagerProc.h @@ -0,0 +1,255 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + MetadataManagerProc.h + +Abstract: + + This is the header file defining the functions of the kernel mode + filter driver implementing filter metadata management. + + +Environment: + + Kernel mode + + +--*/ + +#define MAKE_RESOURCE_OWNER(X) (((ERESOURCE_THREAD)(X)) | 0x3) + +// +// Functions implemented in operations.c +// + +FLT_PREOP_CALLBACK_STATUS +FmmPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +FmmPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +FmmPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +FmmPostCleanup ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + + +FLT_PREOP_CALLBACK_STATUS +FmmPreFSControl ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +FmmPostFSControl ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +FmmPreDeviceControl ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +FmmPostDeviceControl ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +FmmPreShutdown ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +FmmPrePnp ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +FmmPostPnp ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +// +// Functions implemented in datastore.c +// + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +FmmOpenMetadata ( + _In_ PFMM_INSTANCE_CONTEXT InstanceContext, + _In_ BOOLEAN CreateIfNotPresent + ); + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +FmmCloseMetadata ( + _In_ PFMM_INSTANCE_CONTEXT InstanceContext + ); + +NTSTATUS +FmmReleaseMetadataFileReferences ( + _Inout_ PFLT_CALLBACK_DATA Cbd + ); + +NTSTATUS +FmmReacquireMetadataFileReferences ( + _Inout_ PFLT_CALLBACK_DATA Cbd + ); + +NTSTATUS +FmmSetMetadataOpenTriggerFileObject ( + _Inout_ PFLT_CALLBACK_DATA Cbd + ); + +_Releases_lock_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_Releases_lock_(InstanceContext->MetadataResource) +_IRQL_requires_max_(APC_LEVEL) +VOID +FmmBeginFileSystemOperation ( + IN PFMM_INSTANCE_CONTEXT InstanceContext + ); + +_Acquires_lock_(_Global_critical_region_) +_Requires_lock_not_held_(InstanceContext->MetadataResource) +_Acquires_exclusive_lock_(InstanceContext->MetadataResource) +_IRQL_requires_max_(APC_LEVEL) +VOID +FmmEndFileSystemOperation ( + IN PFMM_INSTANCE_CONTEXT InstanceContext + ); + + +#if VERIFY_METADATA_OPENED + +NTSTATUS +FmmIsMetadataOpen ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _Out_ BOOLEAN* MetadataOpen + ); + +#endif + +// +// Functions implemented in support.c +// + +NTSTATUS +FmmAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +VOID +FmmFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +BOOLEAN +FmmTargetIsVolumeOpen ( + _In_ PFLT_CALLBACK_DATA Cbd + ); + +NTSTATUS +FmmIsImplicitVolumeLock( + _In_ PFLT_CALLBACK_DATA Cbd, + _Out_ PBOOLEAN IsLock + ); + +// +// Lock primitives +// + +_Acquires_lock_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +FORCEINLINE +VOID +FmmAcquireResourceExclusive ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_exclusive_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + !ExIsResourceAcquiredSharedLite(Resource)); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); +} + +_Acquires_lock_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +FORCEINLINE +VOID +FmmAcquireResourceShared ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_shared_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceSharedLite( Resource, TRUE ); +} + +_Releases_lock_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +FORCEINLINE +VOID +FmmReleaseResource ( + _Inout_ _Requires_lock_held_(*_Curr_) _Releases_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + ExIsResourceAcquiredSharedLite(Resource)); + + ExReleaseResourceLite(Resource); + KeLeaveCriticalRegion(); +} + + + diff --git a/filesys/miniFilter/MetadataManager/MetadataManagerStruc.h b/filesys/miniFilter/MetadataManager/MetadataManagerStruc.h new file mode 100644 index 00000000..edce9c75 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/MetadataManagerStruc.h @@ -0,0 +1,197 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + MetadataManagerStruct.h + +Abstract: + + This is the header file defining the data structures used by the kernel mode + filter driver implementing filter metadata manager. + + +Environment: + + Kernel mode + + +--*/ + +// +// If this is 1, then the filter will validate that the metadata file is indeed open +// whenever a create suceeds on the volume +// + +#define VERIFY_METADATA_OPENED 0 + + +// +// Memory Pool Tags +// + +#define FMM_STRING_TAG 'tSmF' +#define FMM_INSTANCE_CONTEXT_TAG 'cImF' + + +// +// Filter metadata management filter global data +// + +typedef struct _FMM_GLOBAL_DATA { + + // + // Handle to minifilter returned from FltRegisterFilter() + // + + PFLT_FILTER Filter; + + +#if DBG + + // + // Field to control nature of debug output + // + + ULONG DebugLevel; +#endif + +} FMM_GLOBAL_DATA, *PFMM_GLOBAL_DATA; + +extern FMM_GLOBAL_DATA Globals; + + + + +// +// Instance context flags and data structure +// + +// +// Indicates that the instance context resource has been released +// before performing a file system operation that could potentially +// cause the resource to be re-acquired and deadlock the system +// + +#define INSTANCE_CONTEXT_F_TRANSITION 0x00000001 + + +// +// Indicates if the filter has opened the metadata file and +// holds a reference to the metadata file object for the +// volume +// + +#define INSTANCE_CONTEXT_F_METADATA_OPENED 0x00000002 + + +typedef struct _FMM_INSTANCE_CONTEXT { + + // + // Flags for this instance - defined as INSTANCE_CONTEXT_F_XXX + // + + ULONG Flags; + + // + // Instance for this context. + // + + PFLT_INSTANCE Instance; + + // + // File System Type for this instance. + // + + FLT_FILESYSTEM_TYPE FilesystemType; + + // + // Volume associated with this instance. + // + + PFLT_VOLUME Volume; + + // + // Resource for synchronizing access to the metadata file. + // This recource may also be overloaded to control access to in-memory + // structures that hang off the instance context of the volume. + // + + ERESOURCE MetadataResource; + + // + // Handle of the metadata file. + // + + HANDLE MetadataHandle; + + // + // File object of the metadata file. + // + + PFILE_OBJECT MetadataFileObject; + + // + // The file object on cleanup or cancel removal of which we need to re-open + // our metadata file. This is basically the file object on which we received + // an explicit or implicit lock or a pnp query removal that caused us to + // drop the references to our metadata file + // + + PFILE_OBJECT MetadataOpenTriggerFileObject; + +} FMM_INSTANCE_CONTEXT, *PFMM_INSTANCE_CONTEXT; + +#define FMM_INSTANCE_CONTEXT_SIZE sizeof( FMM_INSTANCE_CONTEXT ) + + +// +// Name of the metadata file for this filter. +// In this sample, we put the metadata file in the SystemVolumeInformation +// folder so as to demonstrate creation of this folder if it does not +// exist +// + +#define FMM_METADATA_FILE_NAME L"\\System Volume Information\\FilterMetadata.md" +#define FMM_METADATA_FILE_NAME_LENGTH (sizeof( FMM_METADATA_FILE_NAME ) - sizeof( WCHAR )) + +// +// Default length of the volume name. +// + +#define FMM_DEFAULT_VOLUME_NAME_LENGTH 64 + + +// +// Debug helper functions +// + +#if DBG + + +#define DEBUG_TRACE_ERROR 0x00000001 // Errors - whenever we return a failure code +#define DEBUG_TRACE_LOAD_UNLOAD 0x00000002 // Loading/unloading of the filter +#define DEBUG_TRACE_INSTANCES 0x00000004 // Attach / detatch of instances + +#define DEBUG_TRACE_METADATA_OPERATIONS 0x00000008 // Operation to access / modify in memory metadata + +#define DEBUG_TRACE_ALL_IO 0x00000010 // All IO operations tracked by this filter + +#define DEBUG_TRACE_INFO 0x00000020 // Misc. information + +#define DEBUG_TRACE_ALL 0xFFFFFFFF // All flags + + +#define DebugTrace(Level, Data) \ + if ((Level) & Globals.DebugLevel) { \ + DbgPrint Data; \ + } + + +#else + +#define DebugTrace(Level, Data) {NOTHING;} + +#endif + diff --git a/filesys/miniFilter/MetadataManager/ReadMe.md b/filesys/miniFilter/MetadataManager/ReadMe.md new file mode 100644 index 00000000..52230ee1 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/ReadMe.md @@ -0,0 +1,21 @@ +Metadata Manager File System Minifilter Driver +============================================== + +The Metadata Manager minifilter sample serves as an example if you want to use files for storing metadata that corresponds to your minifilters. The implementation of this sample depicts scenarios in which modifications to the file might have to be blocked or the minifilter might be required to close the file temporarily. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The Metadata Manager minifilter opens a file when it is first loaded. After that, the minifilter monitors open, close, file control, device control, and Plug and Play (PnP) operations to identify scenarios in which it should close its metadata file or block all writes to it. Applications such as chkdsk obtain implicit or explicit exclusive locks on the volume, and the metadata minifilter demonstrates how to maintain a metadata file without interfering with such lock acquisitions. + +The minifilter identifies implicit locks when it sees a non-shared write open request on a volume object. In this scenario, the minifilter closes its metadata file and sets a trigger that corresponds to the volume in its instance object. Later, each close operation is examined to identify if the implicit lock on the volume is being released and, if so, a re-open of the minifilter's metadata file is triggered. + +Similarly, the minifilter might close its metadata file if it sees an explicit FSCTL\_DISMOUNT\_VOLUME or FSCTL\_LOCK\_VOLUME file-system control operation. The file is later opened when the minifilter observes the FSCTL\_UNLOCK\_VOLUME control operation. The IRP\_MN\_QUERY\_REMOVE\_DEVICE PnP request can also cause the minifilter to close its metadata file, and the IRP\_MN\_SURPRISE\_REMOVAL PnP request will cause it to detach. + +The metadata minifilter also handles the case when a snapshot of its volume object is being taken. In this scenario, the minifilter acquires a shared exclusive lock on the metadata resource object while calling the callback that corresponds to the pre-device control operation for IOCTL\_VOLSNAP\_FLUSH\_AND\_HOLD\_WRITES. The lock is later released in the callback that corresponds to the post-device control operation for IOCTL\_VOLSNAP\_FLUSH\_AND\_HOLD\_WRITES. The lock is acquired to prevent any modifications on the metadata file while the snapshot is being taken. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/MetadataManager/fmm.inf b/filesys/miniFilter/MetadataManager/fmm.inf new file mode 100644 index 00000000..f859f9b0 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/fmm.inf @@ -0,0 +1,96 @@ +;;; +;;; Metadata Management File System Filter Driver Sample +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.1 +CatalogFile = fmm.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 0 ;SERVICE_BOOT_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"DebugLevel",0x00010001,0x00000001 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +fmm.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Metadata Management File System Filter Driver Sample" +ServiceName = "FMM" +DriverName = "fmm" +DiskId1 = "FMM Device Installation Disk" + +;Instances specific information. +DefaultInstance = "FMM" +Instance1.Name = "FMM" +Instance1.Altitude = "370060" +Instance1.Flags = 0x0 diff --git a/filesys/miniFilter/MetadataManager/fmm.vcxproj b/filesys/miniFilter/MetadataManager/fmm.vcxproj new file mode 100644 index 00000000..c2da0192 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/fmm.vcxproj @@ -0,0 +1,183 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A95B7D4F-B926-4E1F-A051-E66091E08D3A} + $(MSBuildProjectName) + Debug + Win32 + {DD166882-E368-4576-B537-05B15D55B977} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + fmm + + + fmm + + + fmm + + + fmm + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters b/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters new file mode 100644 index 00000000..b76f8eb2 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {3AEFB0E9-101A-43AB-8E44-D384CB8D17F8} + + + h;hpp;hxx;hm;inl;inc;xsd + {58BAA0C3-0CF4-438F-A011-53C6310A7E5E} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {168C91BC-EA8F-4E36-9315-77ACF64E6FA7} + + + inf;inv;inx;mof;mc; + {95E307D5-2257-4A2D-AA47-58C291F7AF68} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/MetadataManager/operations.c b/filesys/miniFilter/MetadataManager/operations.c new file mode 100644 index 00000000..dc9b7c19 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/operations.c @@ -0,0 +1,1356 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + operations.c + +Abstract: + + This is the i/o operations module of the kernel mode filter driver implementing + filter metadata management. + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + +// +// Missing error code on Win2k +// + +#if (WINVER==0x0500) +#ifndef STATUS_INVALID_DEVICE_OBJECT_PARAMETER +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER ((NTSTATUS)0xC0000369L) +#endif +#endif + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FmmPreCreate) +#pragma alloc_text(PAGE, FmmPostCreate) +#pragma alloc_text(PAGE, FmmPreCleanup) +#pragma alloc_text(PAGE, FmmPostCleanup) +#pragma alloc_text(PAGE, FmmPreFSControl) +#pragma alloc_text(PAGE, FmmPostFSControl) +#pragma alloc_text(PAGE, FmmPreDeviceControl) +#pragma alloc_text(NONPAGED, FmmPostDeviceControl) +#pragma alloc_text(PAGE, FmmPreShutdown) +#pragma alloc_text(PAGE, FmmPrePnp) +#pragma alloc_text(PAGE, FmmPostPnp) + +#endif + +FLT_PREOP_CALLBACK_STATUS +FmmPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + NTSTATUS status; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + BOOLEAN isImpliedLock = FALSE; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPreCreate -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + + // + // Initialize defaults + // + + status = STATUS_SUCCESS; + callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; // pass through - default is no post op callback + + // + // Sanity check to ensure that the volume detection logic works + // + // If the filename length is 0 and the related file object is NULL, + // the the FO_VOLUME_OPEN flag must be set + // + + FLT_ASSERT( (!(Cbd->Iopb->TargetFileObject->FileName.Length == 0 && + Cbd->Iopb->TargetFileObject->RelatedFileObject == NULL)) || + FlagOn( Cbd->Iopb->TargetFileObject->Flags, FO_VOLUME_OPEN ) ); + + if (FmmTargetIsVolumeOpen( Cbd )) { + + // + // Check for implicit volume locks (primarily used by autochk) + // + + status = FmmIsImplicitVolumeLock( Cbd, &isImpliedLock ); + + FLT_ASSERT( NT_SUCCESS( status ) ); + + if (isImpliedLock) { + + // + // This is an implicit volume lock + // + + // + // Give up the metadata file handle and the metadata file object + // + + status = FmmReleaseMetadataFileReferences( Cbd ); + + if ( NT_SUCCESS( status )) { + + // + // Continue with the lock/dismount - we need to check if the + // lock operation suceeded in the post-op + // + + callbackStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK; + } else { + + // + // Fail the lock/dismount + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to release metadata file references with status 0x%x for a volume lock/dismount\n", + status) ); + + // + // Since this operation has failed, FmmPreCreateCleanup will + // update Cbd->IoStatus.Status with the status code and + // complete the operation by returning FLT_PREOP_COMPLETE + // + + } + + } + + // + // We do not need to process volume opens any further + // + + goto FmmPreCreateCleanup; + } + + +#if VERIFY_METADATA_OPENED + + // + // For all non-volume opens, check if the metadata is open in the post-op + // + + callbackStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK; + +#endif + + + // + // Here the filter can do any further processing it may want to do + // in the PreCreate Callback + // + + + +FmmPreCreateCleanup: + + + // + // If any operation has failed then complete and fail the call + // + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Fmm]: FmmPreCreate -> Failed with status 0x%x \n", + status) ); + + Cbd->IoStatus.Status = status; + callbackStatus = FLT_PREOP_COMPLETE; + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPreCreate -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return callbackStatus; + +} + + +FLT_POSTOP_CALLBACK_STATUS +FmmPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +{ + NTSTATUS status; + BOOLEAN isImpliedLock = FALSE; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CbdContext ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPostCreate -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Initialize defaults + // + + status = STATUS_SUCCESS; + + if (!FlagOn(Flags,FLTFL_POST_OPERATION_DRAINING) && + FmmTargetIsVolumeOpen( Cbd )) { + + // + // Check for implicit volume locks (primarily used by autochk) + // + + status = FmmIsImplicitVolumeLock( Cbd, &isImpliedLock ); + + FLT_ASSERT( NT_SUCCESS( status ) ); + + if (isImpliedLock) { + + // + // This is an implicit volume lock + // + + if (!NT_SUCCESS( Cbd->IoStatus.Status )) { + + // + // The lock failed - reaquire our references to the metadata file + // handle and the metadata file object + // + + status = FmmReacquireMetadataFileReferences( Cbd ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to re-open metadata with status 0x%x after a failed lock with status 0x%x\n", + status, + Cbd->IoStatus.Status) ); + + // + // Sanity - we are now in a bad state. The lock has failed + // but we have not been able to re-acquire references to + // our metadata file + // + // It is always possible to fail with STATUS_INSUFFICIENT_RESOURCES + // so we should ignore that. + // + // It is also possible to fail if the instance context was in a transition state + // so we should ignore STATUS_FILE_LOCK_CONFLICT too. + // + + FLT_ASSERT( (status == STATUS_INSUFFICIENT_RESOURCES) || + (status == STATUS_FILE_LOCK_CONFLICT) ); + } + + } else { + + // + // The lock operation suceeded - update the + // MetadataOpenTriggerFileObject in the instance context to + // the File Object that performed the lock operation. This + // is so we can recognize an implicit unlock at close time. + // + // You may have noticed that we set the + // MetadataOpenTriggerFileObject in pre-create and may be + // wondering why we set it again in this case. This is to + // support a lower filter doing a recursive lock operation + // from the top of the stack. + // + + status = FmmSetMetadataOpenTriggerFileObject( Cbd ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to update MetadataOpenTriggerFileObject in the instance context with status 0x%x after a successful lock.\n", + status) ); + + // + // Sanity - we are now in a bad state. We have failed to set the TriggerFileObject + // We may not be able to detect an unlock operation on which we need to + // re-acquire our metadata file references + // + + FLT_ASSERT( status == STATUS_FILE_LOCK_CONFLICT ); + + } + } + } + + // + // We do not need to process volume opens any further + // + + goto FmmPostCreateCleanup; + } + +#if VERIFY_METADATA_OPENED + + // + // For all successful non-volume opens, check if the metadata is open + // + + if (NT_SUCCESS( Cbd->IoStatus.Status )) { + + BOOLEAN metadataOpen; + + status = FmmIsMetadataOpen( Cbd, &metadataOpen ); + + if (NT_SUCCESS( status) && !metadataOpen) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Fmm]: FmmPostCreate -> Create successful but metadata not open \n") ); + } + + FLT_ASSERT( ((NT_SUCCESS( status) && metadataOpen) || + (status == STATUS_FILE_LOCK_CONFLICT)) ); + } + +#endif + + // + // Here the filter can do any further processing it may want to do + // in the PostCreate Callback + // + +FmmPostCreateCleanup: + + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Fmm]: FmmPostCreate -> Failed with status 0x%x \n", + status) ); + + // + // It does not make sense to fail in the the post op, since the operation has completed + // + } + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPostCreate -> Exit (Cbd = %p, FileObject = %p, Status = 0x%08X)\n", + Cbd, + FltObjects->FileObject, + Cbd->IoStatus.Status) ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +FmmPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + + UNREFERENCED_PARAMETER( Cbd ); + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPreCleanup -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPreCleanup -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return FLT_PREOP_SYNCHRONIZE; +} + + +FLT_POSTOP_CALLBACK_STATUS +FmmPostCleanup ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +{ + NTSTATUS status; + + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + // + // The pre-operation callback will return FLT_PREOP_SYNCHRONIZE if it needs a + // post operation callback. In this case, the Filter Manager will call the + // minifilter's post-operation callback in the context of the pre-operation + // thread, at IRQL <= APC_LEVEL. This allows the post-operation code to be + // pagable and also allows it to access paged data + // + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPostCleanup -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Initialize defaults + // + + status = STATUS_SUCCESS; + + + if (!FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) && + FmmTargetIsVolumeOpen( Cbd )) { + + if (NT_SUCCESS( Cbd->IoStatus.Status )) { + + // + // A close on a volume handle could be an unlock if a lock was + // previously called on this handle. Check if this was a close + // on a volume handle on which a lock was previously successful. + // If so, re-acquire the references to our metadata file + // + + status = FmmReacquireMetadataFileReferences( Cbd ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to re-open metadata with status 0x%x after a successful unlock.\n", + status) ); + + // + // Sanity - we are now in a bad state. The volume was unlocked + // but we have not been able to re-acquire references to + // our metadata file + // + // Ntfs dismounts and remounts the volume after an unlock. So we ignore + // failures to open the metadata with STATUS_INVALID_DEVICE_OBJECT_PARAMETER + // or STATUS_FILE_INVALID because the volume should have been remounted and + // the metadata file should have been opened on the newly mounted instance + // of that volume + // + // Note however, that if this is an implicit lock (used by autoXXX.exe) then + // ntfs will not automatically dismount the volume. It relies on the application + // to restart the system if it has made any changes to the volume. If the + // application has not made any changes then ntfs will simply continue on + // after the unlock without dismounting the volume. Hence we cannot assume + // that ntfs always dismounts the volume. We need to try to re-acquire + // a handle to our metadata file and ignore failure with error + // STATUS_INVALID_DEVICE_OBJECT_PARAMETER or STATUS_NO_MEDIA_IN_DEVICE which + // indicate that the volume has been dismounted + // + // Also it is always possible to fail with STATUS_INSUFFICIENT_RESOURCES + // so we should ignore that as well. + // + // It is also possible to fail if the instance context was in a transition state + // so we should ignore STATUS_FILE_LOCK_CONFLICT too. + // + + FLT_ASSERT( (status == STATUS_INVALID_DEVICE_OBJECT_PARAMETER) || + (status == STATUS_NO_MEDIA_IN_DEVICE) || + (status == STATUS_INSUFFICIENT_RESOURCES) || + (status == STATUS_FILE_LOCK_CONFLICT) || + (status == STATUS_FILE_INVALID) ); + + // + // There is little use updating the return status since it already has a + // failure code from the failed dismount + // + } + } + + // + // We don't need to process a volume CleanUp any further + // + + goto FmmPostCleanupCleanup; + } + + + // + // Here the filter can do any further processing it may want to do + // in the PostCleanUp Callback + // + +FmmPostCleanupCleanup: + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Fmm]: FmmPostCleanup -> Failed with status 0x%x \n", + status) ); + + // + // It does not make sense to fail in the the post op, since the operation has completed + // + + } + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPostCleanup -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +FmmPreFSControl ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + + NTSTATUS status; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPreFsCtl -> Enter (FsControlCode = 0x%x, Cbd = %p, FileObject = %p)\n", + Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode, + Cbd, + FltObjects->FileObject) ); + + + // + // default to no post-op callback + // + + callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + + + if (Cbd->Iopb->MinorFunction != IRP_MN_USER_FS_REQUEST) { + + goto FmmPreFSControlCleanup; + } + + + switch (Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode) { + + // + // System FSCTLs that we are interested in + // + + case FSCTL_DISMOUNT_VOLUME: + case FSCTL_LOCK_VOLUME: + + + if (FmmTargetIsVolumeOpen( Cbd )) { + + // + // Give up the metadata file handle and the metadata file object + // + + status = FmmReleaseMetadataFileReferences( Cbd ); + + if ( NT_SUCCESS( status )) { + + // + // Continue with the lock/dismount - we need to check if the + // lock operation suceeded in the post-op + // + + callbackStatus = FLT_PREOP_SYNCHRONIZE; + } else { + + // + // Fail the lock/dismount + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to release metadata file references with status 0x%x for a volume lock/dismount\n", + status) ); + + Cbd->IoStatus.Status = status; + callbackStatus = FLT_PREOP_COMPLETE; + } + } + break; + + case FSCTL_UNLOCK_VOLUME: + + // + // We need to handle unlock in the post-op + // + + callbackStatus = FLT_PREOP_SYNCHRONIZE; + break; + + } + +FmmPreFSControlCleanup: + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPreFsCtl -> Exit (FsControlCode = 0x%x, Cbd = %p, FileObject = %p)\n", + Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode, + Cbd, + FltObjects->FileObject) ); + + return callbackStatus; + +} + + +FLT_POSTOP_CALLBACK_STATUS +FmmPostFSControl ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +{ + + NTSTATUS status; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPostFsCtl -> Enter (FsControlCode = 0x%x, Cbd = %p, FileObject = %p)\n", + Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode, + Cbd, + FltObjects->FileObject) ); + + + if (!FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING )) { + + switch (Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode) { + + // + // System FSCTLs that we are interested in + // + + case FSCTL_DISMOUNT_VOLUME: + + if (FmmTargetIsVolumeOpen( Cbd )) { + + if (NT_SUCCESS( Cbd->IoStatus.Status )) { + + // + // Dismount succeeded - teardown our instance because its no longer valid. + // If we do not tear down this instance, it will stay around until the + // last handle for that volume is closed. This will cause the instance + // enumeration APIs to see multiple instances + // + + status = FltDetachVolume( Globals.Filter, FltObjects->Volume, NULL ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to detach instance with status 0x%x after a volume dismount\n", + status) ); + + // + // Doesn't make sense to update the status code in the post-op with a + // failure code since the operation has already been performed by the + // file system + // + } + + } else { + + // + // The dismount failed - reaquire our references to the metadata file + // handle and the metadata file object + // + + status = FmmReacquireMetadataFileReferences( Cbd ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to re-open metadata with status 0x%x after a failed dismount with status 0x%x\n", + status, + Cbd->IoStatus.Status) ); + + // + // Sanity - we are now in a bad state. The dismount has failed + // but we have not been able to re-acquire references to + // our metadata file + // + // It is always possible to fail with STATUS_INSUFFICIENT_RESOURCES + // so we should ignore that. + // + // It is also possible to fail if the instance context was in a transition state + // so we should ignore STATUS_FILE_LOCK_CONFLICT too. + // + + FLT_ASSERT( (status == STATUS_INSUFFICIENT_RESOURCES) || + (status == STATUS_FILE_LOCK_CONFLICT) ); + + // + // There is little use updating the return status since it already has a + // failure code from the failed dismount + // + } + } + } + + break; + + case FSCTL_LOCK_VOLUME: + + + if (FmmTargetIsVolumeOpen( Cbd )) { + + if (!NT_SUCCESS( Cbd->IoStatus.Status )) { + + // + // The lock failed - reaquired our references to the metadata file + // handle and the metadata file object + // + + status = FmmReacquireMetadataFileReferences( Cbd ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to re-open metadata with status 0x%x after a failed lock with status 0x%x\n", + status, + Cbd->IoStatus.Status) ); + + // + // Sanity - we are now in a bad state. The lock has failed + // but we have not been able to re-acquire references to + // our metadata file + // + // It is always possible to fail with STATUS_INSUFFICIENT_RESOURCES + // so we should ignore that. + // + // It is also possible to fail if the instance context was in a transition state + // so we should ignore STATUS_FILE_LOCK_CONFLICT too. + // + + FLT_ASSERT( (status == STATUS_INSUFFICIENT_RESOURCES) || + (status == STATUS_FILE_LOCK_CONFLICT) ); + + // + // There is little use updating the return status since it already has a + // failure code from the failed lock + // + + } + } else { + + // + // The lock operation suceeded - update the MetadataOpenTriggerFileObject in the + // instance context to the File Object on the lock operation suceeded because this + // the file object on close/unlock of which, we need to reacquire our metadata file + // references + // + + status = FmmSetMetadataOpenTriggerFileObject( Cbd ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to update MetadataOpenTriggerFileObject in the instance context with status 0x%x after a successful lock.\n", + status) ); + + // + // Sanity - we are now in a bad state. We have failed to + // set the TriggerFileObject We may not be able to detect + // an unlock operation on which we need to re-acquire our + // metadata file references + // + + FLT_ASSERT( status == STATUS_FILE_LOCK_CONFLICT ); + + // + // Doesn't make sense to update the status code in the + // post-op with a failure code since the operation has + // already been performed by the file system + // + } + + } + } + + break; + + case FSCTL_UNLOCK_VOLUME: + + if (NT_SUCCESS( Cbd->IoStatus.Status )) { + + // + // The unlock suceeded - reaquired our references to the metadata file + // handle and the metadata file object + // + + status = FmmReacquireMetadataFileReferences( Cbd ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to re-open metadata with status 0x%x after a successful unlock.\n", + status) ); + + // + // Sanity - we are now in a bad state. The volume was unlocked + // but we have not been able to re-acquire references to + // our metadata file + // + // Ntfs dismounts and remounts the volume after an unlock. So we ignore + // failures to open the metadata with STATUS_INVALID_DEVICE_OBJECT_PARAMETER + // because the volume should have been remounted and the metadata file + // should have been opened on the newly mounted instance of that volume + // + // Note however, that if this is an implicit lock (used by autoXXX.exe) then + // ntfs will not automatically dismount the volume. It relies on the application + // to restart the system if it has made any changes to the volume. If the + // application has not made any changes then ntfs will simply continue on + // after the unlock without dismounting the volume. Hence we cannot assume + // that ntfs always dismounts the volume. We need to try to re-acquire + // a handle to our metadata file and ignore failure with error + // STATUS_INVALID_DEVICE_OBJECT_PARAMETER which indicates that the + // volume has dismounted + // + // Also it is always possible to fail with STATUS_INSUFFICIENT_RESOURCES + // so we should ignore that as well. + // + // It is also possible to fail if the instance context was in a transition state + // so we should ignore STATUS_FILE_LOCK_CONFLICT too. + // + + FLT_ASSERT( (status == STATUS_INVALID_DEVICE_OBJECT_PARAMETER) || + (status == STATUS_INSUFFICIENT_RESOURCES) || + (status == STATUS_FILE_LOCK_CONFLICT) ); + + // + // There is little use updating the return status since it already has a + // failure code from the failed dismount + // + } + } + + + break; + + } + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPostFsCtl -> Exit (FsControlCode = 0x%x, Cbd = %p, FileObject = %p)\n", + Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode, + Cbd, + FltObjects->FileObject) ); + + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +FmmPreDeviceControl ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + + NTSTATUS status = STATUS_SUCCESS; + FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + PFMM_INSTANCE_CONTEXT instanceContext = NULL; + + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + *CompletionContext = NULL; + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPreDeviceControl -> Enter (IoControlCode = 0x%x, Cbd = %p, FileObject = %p)\n", + Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode, + Cbd, + FltObjects->FileObject) ); + + + switch (Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode) { + + // + // System IOCTLs that we are interested in + // + + case IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES: + + // + // We want the snapshot to have a consistent image + // of our metadata file that is in sync with the state + // of the volume + // + + // + // Get the instance context + // + + status = FltGetInstanceContext( Cbd->Iopb->TargetInstance, + &instanceContext ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to get instance context in FmmPreDeviceControl.\n") ); + + goto FmmPreDeviceControlCleanup; + } + + + // + // Here the filter must flush any portion of its metadata + // that it has not flushed to disk + // + // If the filter is using mapped cache buffers to read/write + // its metadata then the File System will take care of all the + // flushing. The filter just needs to ensure that it does not + // write to any of its mapped cache buffers while the FS is + // trying to flush changes out to disk. + // + + // + // After this point, the filter should not be sending any updates + // to its metadata file on disk until the post-op callback for + // IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES + // + // The filter would do this by marking its instance context in some way + // (say, by setting a flag) to indicate to other threads that they should + // not try to update the metadata file on the disk + // + + // + // Do not release the instance context but instead pass it to the PostOp + // The PostOp routine would need to unmark the instance context in some way + // to indicate that it is now ok to update the metadata file on the disk. + // + // Since we do not want to fail this unmarking because we cannot acquire the + // instance context in the post-op, it is better to pass the instance context from + // PreOp to PostOp + // + + *CompletionContext = instanceContext; + + // + // Force a post-op so we may undo our marking and release the instance context + // + + callbackStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK; + + break; + + } + +FmmPreDeviceControlCleanup: + + + // + // If any operation has failed then complete and fail the call + // + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Fmm]: FmmPreDeviceControl -> Failed with status 0x%x \n", + status) ); + + // + // We are not having a post-op since the pre-op failed + // Release the instance context + // + + if (instanceContext != NULL) { + + FltReleaseContext( instanceContext ); + *CompletionContext = NULL; + } + + Cbd->IoStatus.Status = status; + callbackStatus = FLT_PREOP_COMPLETE; + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPreDeviceControl -> Exit (IoControlCode = 0x%x, Cbd = %p, FileObject = %p)\n", + Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode, + Cbd, + FltObjects->FileObject) ); + + return callbackStatus; + +} + + +FLT_POSTOP_CALLBACK_STATUS +FmmPostDeviceControl ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +{ + + PFMM_INSTANCE_CONTEXT instanceContext = NULL; + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( FltObjects ); + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPostDeviceControl -> Enter (IoControlCode = 0x%x, Cbd = %p, FileObject = %p)\n", + Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode, + Cbd, + FltObjects->FileObject) ); + + // + // We need to do this even if we are draining + // + + switch (Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode) { + + // + // System IOCTLs that we are interested in + // + + case IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES: + + // + // Assign the instance context + // + + instanceContext = (PFMM_INSTANCE_CONTEXT) CbdContext; + + // + // Sanity + // + + FLT_ASSERT( instanceContext != NULL ); + + + // + // At this point, it is ok for the filter to send updates to its metadata + // file on disk + // + // The filter would do this by unmarking its instance context (say by, + // resetting the flag that it set in the PreOp) to indicate to other threads + // that it is now ok to update the metadata file on the disk + // + + + // + // Release the instance context + // + + FltReleaseContext( instanceContext ); + + break; + + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPostDeviceControl -> Exit (IoControlCode = 0x%x, Cbd = %p, FileObject = %p)\n", + Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode, + Cbd, + FltObjects->FileObject) ); + + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + + +FLT_PREOP_CALLBACK_STATUS +FmmPreShutdown ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Cbd ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPreShutdown -> Enter (Cbd = %p, FileObject = %p, Volume = %p)\n", + Cbd, + FltObjects->FileObject, + FltObjects->Volume) ); + + status = FltDetachVolume( Globals.Filter, FltObjects->Volume, NULL ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to detach instance with status 0x%x on system shutdown\n", + status) ); + + // + // Doesn't really make sense to fail a shutdown, even if this operation failed + // + + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPreShutdown -> Exit (Cbd = %p, FileObject = %p, Volume = %p)\n", + Cbd, + FltObjects->FileObject, + FltObjects->Volume) ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +FLT_PREOP_CALLBACK_STATUS +FmmPrePnp ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/* + +Routine Description: + + This routine handles the pre-processing of all PNP operations received + on this instance. It handles query, cancel and suprise device removal. + For query device removal we have to close all open file handles we hold + so that the base file system can correctly response to these PNP requests. + +Arguments: + + Cbd - Pointer to the FLT_CALLBACK_DATA structure containing all the relevant + parameters for this operation. + + FltObject - Pointer to the FLT_RELATED_OBJECTS data structure containing, + opaque handles to this filter, instance and its associated volume. + + CompletionContext - Not used. + +Return Value: + + FLT_PREOP_SUCCESS_NO_CALLBACK as we are done with our processing and are + not interested in a post-operartion callback. + +*/ +{ + NTSTATUS status; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPrePnp -> Enter (Cbd = %p, FileObject = %p, Volume = %p)\n", + Cbd, + FltObjects->FileObject, + FltObjects->Volume) ); + + // + // default to no post op callback + // + + callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + + switch (Cbd->Iopb->MinorFunction) { + + case IRP_MN_QUERY_REMOVE_DEVICE: + + // + // Give up the metadata file handle and the metadata file object + // + + status = FmmReleaseMetadataFileReferences( Cbd ); + + if (!NT_SUCCESS( status )) { + + // + // Fail the query removal + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to release metadata file references with status 0x%x for query removal\n", + status) ); + + Cbd->IoStatus.Status = status; + callbackStatus = FLT_PREOP_COMPLETE; + } + + break; + + case IRP_MN_CANCEL_REMOVE_DEVICE: + + // + // We need to pass this notification through to the file system + // so he start allowing IO to the volume. We file for a post op + // so that we can reacquire our resources. We must return + // FLT_PREOP_SYNCRONIZE because we need to be below DPC inorder + // to reopen our metadata file. + // + + callbackStatus = FLT_PREOP_SYNCHRONIZE; + + break; + + case IRP_MN_SURPRISE_REMOVAL: + + // + // Teardown our instance because its no longer valid. + // + + status = FltDetachVolume( Globals.Filter, FltObjects->Volume, NULL ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to detach instance with status 0x%x after a surprise removal\n", + status) ); + + } + + break; + + default: + + // + // Pass all PNP minor codes we don't care about. + // + + break; + } + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Fmm]: FmmPrePnp -> Exit (Cbd = %p, FileObject = %p, Volume = %p)\n", + Cbd, + FltObjects->FileObject, + FltObjects->Volume) ); + + return callbackStatus; +} + +FLT_POSTOP_CALLBACK_STATUS +FmmPostPnp ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/* + +Routine Description: + + This routine handles the post-processing of IRP_MN_CANCEL_REMOVE_DEVICE. + We reacquire our references to the metadata file handle and the metadata + file object. + +*/ +{ + + + NTSTATUS status; + + UNREFERENCED_PARAMETER( CbdContext ); + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + // + // Sanity - we should only have a post operation for IRP_MN_CANCEL_DEVICE. + // + + FLT_ASSERT( Cbd->Iopb->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE ); + + // + // Sanity - IRP_MN_CANCEL_DEVICE cannot fail. + // + + FLT_ASSERT( Cbd->IoStatus.Status == STATUS_SUCCESS ); + + if( FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) ) { + + // + // We are draining. This means that we should not reacquire our + // resources because the IRP may not have completed. + // + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // The device removal was cancelled - reaquire our references to the + // metadata file handle and the metadata file object + // + + status = FmmReacquireMetadataFileReferences( Cbd ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: Failed to re-open metadata with status 0x%x after cancel device removal.\n", + status) ); + + // + // Sanity - we are now in a bad state. The removal has been + // cancelled but we have not been able to re-acquire references + // to our metadata file + // + // It is always possible to fail with STATUS_INSUFFICIENT_RESOURCES + // so we should ignore that. + // + // It is also possible to fail if the instance context was in a transition state + // so we should ignore STATUS_FILE_LOCK_CONFLICT too. + // + + FLT_ASSERT( (status == STATUS_INSUFFICIENT_RESOURCES) || + (status == STATUS_FILE_LOCK_CONFLICT) ); + + } + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + diff --git a/filesys/miniFilter/MetadataManager/pch.h b/filesys/miniFilter/MetadataManager/pch.h new file mode 100644 index 00000000..ed7047d8 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/pch.h @@ -0,0 +1,47 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + pch.h + +Abstract: + + This module includes all the headers which need to be + precompiled & are included by all the source files in this + project + + +Environment: + + Kernel mode + + +--*/ + +#ifndef __FMM_PCH_H__ +#define __FMM_PCH_H__ + +// +// Enabled warnings +// + +#pragma warning(error:4100) // Enable-Unreferenced formal parameter +#pragma warning(error:4101) // Enable-Unreferenced local variable +#pragma warning(error:4061) // Eenable-missing enumeration in switch statement +#pragma warning(error:4505) // Enable-identify dead functions + +// +// Includes +// + +#include +#include +#include +#include "MetadataManagerStruc.h" +#include "MetadataManagerProc.h" + + +#endif __FMM_PCH_H__ + diff --git a/filesys/miniFilter/MetadataManager/support.c b/filesys/miniFilter/MetadataManager/support.c new file mode 100644 index 00000000..fdfc22cf --- /dev/null +++ b/filesys/miniFilter/MetadataManager/support.c @@ -0,0 +1,244 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + operations.c + +Abstract: + + This is the support routines module of the kernel mode filter driver implementing + filter metadata management. + + +Environment: + + Kernel mode + + +--*/ + + + +#include "pch.h" + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FmmAllocateUnicodeString) +#pragma alloc_text(PAGE, FmmFreeUnicodeString) +#pragma alloc_text(PAGE, FmmTargetIsVolumeOpen) +#pragma alloc_text(PAGE, FmmIsImplicitVolumeLock) +#endif + +// +// Support Routines +// + +NTSTATUS +FmmAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This routine allocates a unicode string + +Arguments: + + String - supplies the size of the string to be allocated in the MaximumLength field + return the unicode string + +Return Value: + + STATUS_SUCCESS - success + STATUS_INSUFFICIENT_RESOURCES - failure + +--*/ +{ + PAGED_CODE(); + + String->Buffer = ExAllocatePoolWithTag( PagedPool, + String->MaximumLength, + FMM_STRING_TAG ); + + if (String->Buffer == NULL) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to allocate unicode string of size 0x%x\n", + String->MaximumLength) ); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + String->Length = 0; + + return STATUS_SUCCESS; +} + +VOID +FmmFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This routine frees a unicode string + +Arguments: + + String - supplies the string to be freed + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + ExFreePoolWithTag( String->Buffer, + FMM_STRING_TAG ); + + String->Length = String->MaximumLength = 0; + String->Buffer = NULL; +} + + +BOOLEAN +FmmTargetIsVolumeOpen ( + _In_ PFLT_CALLBACK_DATA Cbd + ) +/*++ + +Routine Description: + + This routine returns if the target object in this callback datastructure + is a volume. If the file object is NULL then assume this is NOT a volume + file object + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + +Return Value: + + TRUE - target is a volume + FALSE - target is not a volume + +--*/ +{ + PAGED_CODE(); + + if ((Cbd->Iopb->TargetFileObject != NULL) && + FlagOn( Cbd->Iopb->TargetFileObject->Flags, FO_VOLUME_OPEN )) { + + return TRUE; + } else { + + return FALSE; + } +} + +NTSTATUS +FmmIsImplicitVolumeLock( + _In_ PFLT_CALLBACK_DATA Cbd, + _Out_ PBOOLEAN IsLock + ) +/*++ + +Routine Description: + + This routine determines if an open is a implcit volume lock. + +Arguments + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + + IsLock - Supplies a pointer to a user allocated boolean + which is used to tell the user wheather the + operation is an implied volume lock. +Return Value: + + Returns STATUS_SUCCESS if the the function determined wheather or not + the operation was a volume lock. On STATUS_SUCCESS it is safe to check + IsLock to get the answer. Otherwise, the check failed and we dont know + if it is a lock or not. STATUS_INVALID_PARAMETER indicates that the + volume's file system type is unrecognized by the check function. This is + an error code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PFMM_INSTANCE_CONTEXT instanceContext = NULL; + USHORT shareAccess; + ACCESS_MASK prevAccess; + + PAGED_CODE(); + + // + // Get the instance context so we know + // which file system we are attached to. + // + + status = FltGetInstanceContext( Cbd->Iopb->TargetInstance, + &instanceContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS, + ("[Fmm]: FmmIsImplicitVolumeLock -> Failed to get instance context.\n") ); + goto FmmIsImplicitVolumeLockCleanup; + } + + FLT_ASSERT( instanceContext != NULL ); + + // + // Now check to see if the open is an implied volume lock + // on this filesystem. + // + + shareAccess = Cbd->Iopb->Parameters.Create.ShareAccess; + prevAccess = Cbd->Iopb->Parameters.Create.SecurityContext->DesiredAccess; + + switch (instanceContext->FilesystemType) { + + case FLT_FSTYPE_REFS: + *IsLock = ((!BooleanFlagOn( shareAccess, FILE_SHARE_WRITE | FILE_SHARE_DELETE)) && + (BooleanFlagOn( prevAccess,(FILE_WRITE_DATA | FILE_APPEND_DATA) ))); + status = STATUS_SUCCESS; + break; + + case FLT_FSTYPE_NTFS: + *IsLock = ((!BooleanFlagOn( shareAccess, FILE_SHARE_WRITE | FILE_SHARE_DELETE)) && + (BooleanFlagOn( prevAccess,(FILE_WRITE_DATA | FILE_APPEND_DATA) ))); + status = STATUS_SUCCESS; + break; + + case FLT_FSTYPE_FAT: + *IsLock = (!BooleanFlagOn( shareAccess, FILE_SHARE_WRITE | FILE_SHARE_DELETE)); + status = STATUS_SUCCESS; + break; + + default: + status = STATUS_INVALID_PARAMETER; + break; + } + +FmmIsImplicitVolumeLockCleanup: + + if (instanceContext != NULL ) { + + FltReleaseContext( instanceContext ); + } + + return status; +} + diff --git a/filesys/miniFilter/avscan/ReadMe.md b/filesys/miniFilter/avscan/ReadMe.md new file mode 100644 index 00000000..f17c27c2 --- /dev/null +++ b/filesys/miniFilter/avscan/ReadMe.md @@ -0,0 +1,8 @@ +AvScan File System Minifilter Driver +==================================== + +The AvScan minifilter is a transaction-aware file scanner. This is an example for developers who intend to write filters that examine data in files. Typically, anti-virus products fall into this category. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + diff --git a/filesys/miniFilter/avscan/avscan.inf b/filesys/miniFilter/avscan/avscan.inf new file mode 100644 index 00000000..e536395e --- /dev/null +++ b/filesys/miniFilter/avscan/avscan.inf @@ -0,0 +1,106 @@ +;;; +;;; AvScan +;;; +;;; +;;; Copyright (c) Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ContentScreener" ;This is determined by the work this filter driver does +ClassGuid = {3e3f0674-c83c-4558-bb26-9820e1eba5c5} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2011,1.0.0.1 +CatalogFile = avscan.cat + + +[DestinationDirs] +DefaultDestDir = 12 +AvScan.DriverFiles = 12 ;%windir%\system32\drivers +AvScan.UserFiles = 10,FltMgr ;%windir%\FltMgr + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = AvScan.DriverFiles, AvScan.UserFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,AvScan.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = AvScan.DriverFiles, AvScan.UserFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[AvScan.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Content Screener" +AddReg = AvScan.AddRegistry + +; +; Registry Modifications +; + +[AvScan.AddRegistry] +HKR,,"DebugFlags",0x00010001,0xc +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% +HKR,,"LocalScanTimeout",0x00010001,%LocalScanTimeout% +HKR,,"NetworkScanTimeout",0x00010001,%NetworkScanTimeout% +HKR,,"SupportedFeatures",0x00010001,0x3 + +; +; Copy Files +; + +[AvScan.DriverFiles] +%DriverName%.sys + +[AvScan.UserFiles] +%UserAppName%.exe + +[SourceDisksFiles] +avscan.sys = 1,, +avscan.exe = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Anti-virus Mini-Filter Driver" +ServiceName = "avscan" +DriverName = "avscan" +UserAppName = "avscan" +DiskId1 = "Anti-virus Device Installation Disk" +LocalScanTimeout = "30000" +NetworkScanTimeout = "60000" + +;Instances specific information. +DefaultInstance = "avscan Instance" +Instance1.Name = "avscan Instance" +Instance1.Altitude = "265010" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/avscan/avscan.sln b/filesys/miniFilter/avscan/avscan.sln new file mode 100644 index 00000000..0fc17fad --- /dev/null +++ b/filesys/miniFilter/avscan/avscan.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{9B4F0964-C698-49FE-B160-251A3A483399}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{4A27FF26-F700-4D97-9F45-F1CBD7C78513}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "filter\avscan.vcxproj", "{9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "user\avscan.vcxproj", "{23D46B81-CF8D-48E5-BF28-3679E6106D7F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|Win32.ActiveCfg = Debug|Win32 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|Win32.Build.0 = Debug|Win32 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|Win32.ActiveCfg = Release|Win32 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|Win32.Build.0 = Release|Win32 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|x64.ActiveCfg = Debug|x64 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|x64.Build.0 = Debug|x64 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|x64.ActiveCfg = Release|x64 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|x64.Build.0 = Release|x64 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|Win32.ActiveCfg = Debug|Win32 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|Win32.Build.0 = Debug|Win32 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|Win32.ActiveCfg = Release|Win32 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|Win32.Build.0 = Release|Win32 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|x64.ActiveCfg = Debug|x64 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|x64.Build.0 = Debug|x64 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|x64.ActiveCfg = Release|x64 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A} = {9B4F0964-C698-49FE-B160-251A3A483399} + {23D46B81-CF8D-48E5-BF28-3679E6106D7F} = {4A27FF26-F700-4D97-9F45-F1CBD7C78513} + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/avscan/filter/avscan.c b/filesys/miniFilter/avscan/filter/avscan.c new file mode 100644 index 00000000..e9cab600 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/avscan.c @@ -0,0 +1,3152 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + avscan.c + +Abstract: + + This is the main module of the avscan mini-filter driver. + This filter demonstrates how to implement a transaction-aware + anti-virus filter. + + Av prefix denotes "Anti-virus" module. + +Environment: + + Kernel mode + +--*/ + +#include +#include "avscan.h" + +/************************************************************************* + Local Function Prototypes +*************************************************************************/ + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +AvSetConfiguration ( + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +AvInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +VOID +AvInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Unreferenced_parameter_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +AvInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +NTSTATUS +AvUnload ( + _Unreferenced_parameter_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +AvInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +AvPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +AvPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +AvPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +AvPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +AvPreFsControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +NTSTATUS +AvKtmNotificationCallback ( + _Unreferenced_parameter_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ); + +NTSTATUS +AvScanAbortCallbackAsync ( + _Unreferenced_parameter_ PFLT_INSTANCE Instance, + _In_ PFLT_CONTEXT Context, + _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data + ); + +// +// Local routines +// + +BOOLEAN +AvOperationsModifyingFile ( + _In_ PFLT_CALLBACK_DATA Data + ); + +NTSTATUS +AvQueryTransactionOutcome( + _In_ PKTRANSACTION Transaction, + _Out_ PULONG TxOutcome + ); + +NTSTATUS +AvProcessPreviousTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext + ); + +NTSTATUS +AvProcessTransactionOutcome ( + _Inout_ PAV_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG TransactionOutcome + ); + +NTSTATUS +AvLoadFileStateFromCache ( + _In_ PFLT_INSTANCE Instance, + _In_ PAV_FILE_REFERENCE FileId, + _Out_ LONG volatile* State, + _Out_ PLONGLONG VolumeRevision, + _Out_ PLONGLONG CacheRevision, + _Out_ PLONGLONG FileRevision + ); + +NTSTATUS +AvSyncCache ( + _In_ PFLT_INSTANCE Instance, + _In_ PAV_STREAM_CONTEXT StreamContext + ); + +BOOLEAN +AvIsPrefetchEcpPresent ( + _In_ PFLT_FILTER Filter, + _In_ PFLT_CALLBACK_DATA Data + ); + +BOOLEAN +AvIsStreamAlternate ( + _Inout_ PFLT_CALLBACK_DATA Data + ); + +NTSTATUS +AvScan ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ AV_SCAN_MODE ScanMode, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _Inout_ PAV_STREAM_CONTEXT StreamContext + ); + +VOID +AvDoCancelScanAndRelease ( + _In_ PAV_SCAN_CONTEXT ScanContext, + _In_ PAV_SECTION_CONTEXT SectionContext + ); + +NTSTATUS +AvSendUnloadingToUser ( + VOID + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, AvSetConfiguration) +#pragma alloc_text(PAGE, AvUnload) +#pragma alloc_text(PAGE, AvInstanceQueryTeardown) +#pragma alloc_text(PAGE, AvInstanceSetup) +#pragma alloc_text(PAGE, AvInstanceTeardownStart) +#pragma alloc_text(PAGE, AvInstanceTeardownComplete) +#pragma alloc_text(PAGE, AvPreCreate) +#pragma alloc_text(PAGE, AvPostCreate) +#pragma alloc_text(PAGE, AvPreFsControl) +#pragma alloc_text(PAGE, AvPreCleanup) +#pragma alloc_text(PAGE, AvKtmNotificationCallback) +#pragma alloc_text(PAGE, AvScanAbortCallbackAsync) +#pragma alloc_text(PAGE, AvOperationsModifyingFile) +#pragma alloc_text(PAGE, AvQueryTransactionOutcome) +#pragma alloc_text(PAGE, AvProcessPreviousTransaction) +#pragma alloc_text(PAGE, AvProcessTransactionOutcome) +#pragma alloc_text(PAGE, AvLoadFileStateFromCache) +#pragma alloc_text(PAGE, AvSyncCache) +#pragma alloc_text(PAGE, AvIsPrefetchEcpPresent) +#pragma alloc_text(PAGE, AvIsStreamAlternate) +#pragma alloc_text(PAGE, AvScan) +#pragma alloc_text(PAGE, AvDoCancelScanAndRelease) +#pragma alloc_text(PAGE, AvSendAbortToUser) +#pragma alloc_text(PAGE, AvSendUnloadingToUser) +#endif + +// +// operation registration +// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_CREATE, + 0, + AvPreCreate, + AvPostCreate }, + + { IRP_MJ_CLEANUP, + 0, + AvPreCleanup, + NULL }, + + { IRP_MJ_WRITE, + 0, + AvPreOperationCallback, + NULL }, + + { IRP_MJ_SET_INFORMATION, + 0, + AvPreOperationCallback, + NULL }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + AvPreFsControl, + NULL }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Context registraction construct defined in context.c +// + +extern const FLT_CONTEXT_REGISTRATION ContextRegistration[]; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + ContextRegistration, // Context + Callbacks, // Operation callbacks + + AvUnload, // MiniFilterUnload + + AvInstanceSetup, // InstanceSetup + AvInstanceQueryTeardown, // InstanceQueryTeardown + AvInstanceTeardownStart, // InstanceTeardownStart + AvInstanceTeardownComplete, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // NormalizeNameComponentCallback + NULL, // NormalizeContextCleanupCallback + AvKtmNotificationCallback, // TransactionNotificationCallback + NULL, // NormalizeNameComponentExCallback + AvScanAbortCallbackAsync // SectionNotificationCallback +}; + + + +NTSTATUS +AvInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. This + gives us a chance to decide if we need to attach to this volume or not. + + If this routine is not defined in the registration structure, automatic + instances are alwasys created. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + NTSTATUS status; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + BOOLEAN isOnCsv = FALSE; + + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvInstanceSetup: Entered\n") ); + + // + // Don't attach to network volumes. + // + + if (VolumeDeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) { + + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Determine if the filter is attaching to the hidden NTFS volume + // that corresponds to a CSV volume. If so do not attach. Note + // that it would be feasible for the filter to attach to this + // volume as part of a distrubuted filter implementation but that + // is beyond the scope of this sample. + // + + if (VolumeFilesystemType == FLT_FSTYPE_NTFS) { + isOnCsv = AvIsVolumeOnCsvDisk( FltObjects->Volume ); + if (isOnCsv) { + + return STATUS_FLT_DO_NOT_ATTACH; + } + } + + status = FltAllocateContext( Globals.Filter, + FLT_INSTANCE_CONTEXT, + AV_INSTANCE_CONTEXT_SIZE, + NonPagedPoolNx, + &instanceContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvInstanceSetup: allocate instance context failed. status = 0x%x\n", status) ); + + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Setup instance context + // + + RtlZeroMemory(instanceContext, AV_INSTANCE_CONTEXT_SIZE); + instanceContext->Volume = FltObjects->Volume; + instanceContext->Instance = FltObjects->Instance; + instanceContext->VolumeFSType = VolumeFilesystemType; + instanceContext->IsOnCsvMDS = isOnCsv; + + // + // There will be a file state cache table for each NTFS volume instance. + // As for other file systems, file id is not unique, and thus we do + // not have cache for other kinds of file systems. Since the cache + // table is not mandatory to implement an anti-virus filter, we + // only have the volatile cache for NTFS, CSVFS and REFS. + // + // It is worth mentioning that the table is potentially very large. + // We use an AVL tree to improve insertion and query times. We do not + // set an upper bound for the size of the tree which is not optimal. + // Consider limiting the size of the tree for a production filter. + // + + if (FS_SUPPORTS_FILE_STATE_CACHE( VolumeFilesystemType )) { + + // + // Initialize file state cache in the instance context. + // + + ExInitializeResourceLite( &instanceContext->Resource ); + + RtlInitializeGenericTable( &instanceContext->FileStateCacheTable, + (PRTL_GENERIC_COMPARE_ROUTINE) AvCompareEntry, + (PRTL_GENERIC_ALLOCATE_ROUTINE) AvAllocateGenericTableEntry, + (PRTL_GENERIC_FREE_ROUTINE) AvFreeGenericTableEntry, + NULL ); + } + + status = FltSetInstanceContext( FltObjects->Instance, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + instanceContext, + NULL ); + + // + // In all cases, we need to release the instance context at this time. + // If we hit an error, it will get freed now. + // + + FltReleaseContext( instanceContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvInstanceSetup: set instance context failed. status = 0x%x\n", status) ); + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Register this instance as a datascan filter. If this call + // fails the underlying filesystem does not support using + // the filter manager datascan API. Currently only the + // the namedpipe and mailslot file systems are unsupported. + // + + status = FltRegisterForDataScan( FltObjects->Instance ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvInstanceSetup: FltRegisterForDataScan failed. status = 0x%x\n", status) ); + return STATUS_FLT_DO_NOT_ATTACH; + + } + + return STATUS_SUCCESS; +} + +NTSTATUS +AvInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach thereby giving us a + chance to fail that detach request. + + If this routine is not defined in the registration structure, explicit + detach requests via FltDetachVolume or FilterDetach will always be + failed. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Returns the status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvInstanceQueryTeardown: Entered\n") ); + + return STATUS_SUCCESS; +} + +VOID +AvInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Unreferenced_parameter_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the start of instance teardown. + If we have cache table, we have to clean up the table at this point. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + NTSTATUS status; + PLIST_ENTRY scan; + PLIST_ENTRY next; + PAV_SCAN_CONTEXT scanCtx = NULL; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvInstanceTeardownStart: Entered\n") ); + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvInstanceTeardownStart: FltGetInstanceContext failed. status = 0x%x\n", status) ); + return; + } + + // + // Search the scan context from the global list. + // + + AvAcquireResourceExclusive( &Globals.ScanCtxListLock ); + + LIST_FOR_EACH_SAFE( scan, next, &Globals.ScanCtxListHead ) { + + scanCtx = CONTAINING_RECORD( scan, AV_SCAN_CONTEXT, List ); + + if (scanCtx->FilterInstance != FltObjects->Instance) { + + continue; + } + + // + // Notify the user scan thread to abort the scan. + // + status = AvSendAbortToUser(scanCtx->ScanThreadId, + scanCtx->ScanId); + + + // + // If we fail to send message to the user, then we + // do the cancel and cleanup by ourself; otherwise, + // the listening thread will call back to cleanup and + // I/O request thred will tear down the scan context. + // + + if (!NT_SUCCESS( status ) || status == STATUS_TIMEOUT) { + + AvFinalizeScanAndSection(scanCtx); + } + } + + AvReleaseResource( &Globals.ScanCtxListLock ); + + // + // Clean up the cache table if the volume supports one. + // + + if (FS_SUPPORTS_FILE_STATE_CACHE( instanceContext->VolumeFSType )) { + PAV_GENERIC_TABLE_ENTRY entry = NULL; + AvAcquireResourceExclusive( &instanceContext->Resource ); + + while (!RtlIsGenericTableEmpty( &instanceContext->FileStateCacheTable ) ) { + entry = RtlGetElementGenericTable(&instanceContext->FileStateCacheTable, 0); + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvInstanceTeardownStart: %I64x,%I64x requesting deletion, state:%d\n", + entry->FileId.FileId64.UpperZeroes, + entry->FileId.FileId64.Value, + entry->InfectedState) ); + RtlDeleteElementGenericTable(&instanceContext->FileStateCacheTable, entry); + } + + AvReleaseResource( &instanceContext->Resource ); + } + + FltReleaseContext( instanceContext ); + + FltDeleteInstanceContext( FltObjects->Instance, NULL ); +} + +VOID +AvInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the end of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvInstanceTeardownComplete: Entered\n") ); +} + + +/************************************************************************* + MiniFilter initialization and unload routines. +*************************************************************************/ + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers with FltMgr and initializes all global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PSECURITY_DESCRIPTOR sd = NULL; + + UNREFERENCED_PARAMETER( RegistryPath ); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] DriverEntry: Entered\n") ); + + // + // Set default global configuration + // + + RtlZeroMemory( &Globals, sizeof(Globals) ); + InitializeListHead( &Globals.ScanCtxListHead ); + ExInitializeResourceLite( &Globals.ScanCtxListLock ); + + Globals.ScanIdCounter = 0; + Globals.LocalScanTimeout = 30000; + Globals.NetworkScanTimeout = 60000; + +#if DBG + + Globals.DebugLevel = 0xffffffff; // AVDBG_TRACE_ERROR | AVDBG_TRACE_DEBUG; + +#endif + + try { + + // + // Set the filter configuration based on registry keys + // + + status = AvSetConfiguration( RegistryPath ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: DriverEntry: SetConfiguration FAILED. status = 0x%x\n", status) ); + + leave; + } + + // + // Register with FltMgr to tell it our callback routines + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: FltRegisterFilter FAILED. status = 0x%x\n", status) ); + leave; + } + + // + // Builds a default security descriptor for use with FltCreateCommunicationPort. + // + + status = FltBuildDefaultSecurityDescriptor( &sd, + FLT_PORT_ALL_ACCESS ); + + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: FltBuildDefaultSecurityDescriptor FAILED. status = 0x%x\n", status) ); + leave; + } + // + // Prepare ports between kernel and user. + // + + status = AvPrepareServerPort( sd, AvConnectForScan ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: AvPrepareServerPort Scan Port FAILED. status = 0x%x\n", status) ); + leave; + } + + status = AvPrepareServerPort( sd, AvConnectForAbort ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: AvPrepareServerPort Abort Port FAILED. status = 0x%x\n", status) ); + leave; + } + + status = AvPrepareServerPort( sd, AvConnectForQuery ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: AvPrepareServerPort Query Port FAILED. status = 0x%x\n", status) ); + leave; + } + + // + // Start filtering i/o + // + + status = FltStartFiltering( Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: FltStartFiltering FAILED. status = 0x%x\n", status) ); + leave; + } + + } finally { + + if ( sd != NULL ) { + + FltFreeSecurityDescriptor( sd ); + } + + if (!NT_SUCCESS( status ) ) { + + if (NULL != Globals.ScanServerPort) { + + FltCloseCommunicationPort( Globals.ScanServerPort ); + } + if (NULL != Globals.AbortServerPort) { + + FltCloseCommunicationPort( Globals.AbortServerPort ); + } + if (NULL != Globals.QueryServerPort) { + + FltCloseCommunicationPort( Globals.QueryServerPort ); + } + if (NULL != Globals.Filter) { + + FltUnregisterFilter( Globals.Filter ); + Globals.Filter = NULL; + } + + ExDeleteResourceLite( &Globals.ScanCtxListLock ); + } + } + + return status; +} + +NTSTATUS +AvUnload ( + _Unreferenced_parameter_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter driver. This is called + when the minifilter is about to be unloaded. We can fail this unload + request if this is not a mandatory unloaded indicated by the Flags + parameter. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Flags ); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvUnload: Entered\n") ); + + // + // Traverse the scan context list, and cancel the scan if it exists. + // + + AvAcquireResourceExclusive( &Globals.ScanCtxListLock ); + Globals.Unloading = TRUE; + AvReleaseResource( &Globals.ScanCtxListLock ); + + // + // This function will wait for the user to abort the outstanding scan and + // close the section + // + + AvSendUnloadingToUser(); + + FltCloseCommunicationPort( Globals.ScanServerPort ); + Globals.ScanServerPort = NULL; + FltCloseCommunicationPort( Globals.AbortServerPort ); + Globals.AbortServerPort = NULL; + FltCloseCommunicationPort( Globals.QueryServerPort ); + Globals.QueryServerPort = NULL; + FltUnregisterFilter( Globals.Filter ); // This will typically trigger instance tear down. + Globals.Filter = NULL; + + ExDeleteResourceLite( &Globals.ScanCtxListLock ); + + return STATUS_SUCCESS; +} + + +/************************************************************************* + Local utility routines. +*************************************************************************/ + +BOOLEAN +AvOperationsModifyingFile ( + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This identifies those operations we need to set the file to be modified. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + TRUE - If we want the file associated with the request to be modified. + FALSE - If we don't + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + + PAGED_CODE(); + + switch(iopb->MajorFunction) { + + case IRP_MJ_WRITE: + return TRUE; + + case IRP_MJ_FILE_SYSTEM_CONTROL: + switch ( iopb->Parameters.FileSystemControl.Common.FsControlCode ) { + case FSCTL_OFFLOAD_WRITE: + case FSCTL_WRITE_RAW_ENCRYPTED: + case FSCTL_SET_ZERO_DATA: + return TRUE; + default: break; + } + break; + + case IRP_MJ_SET_INFORMATION: + switch ( iopb->Parameters.SetFileInformation.FileInformationClass ) { + case FileEndOfFileInformation: + case FileValidDataLengthInformation: + return TRUE; + default: break; + } + break; + default: + break; + } + return FALSE; +} + +NTSTATUS +AvQueryTransactionOutcome( + _In_ PKTRANSACTION Transaction, + _Out_ PULONG TxOutcome + ) +/*++ + +Routine Description: + + This is a helper function that qeury the KTM that how trasnaction was ended. + +Arguments: + + Transaction - Pointer to transaction object. + + TxOutcome - Output. Specifies the type of transaction outcome. + +Return Value: + + The status of the operation +--*/ +{ + HANDLE transactionHandle; + NTSTATUS status; + TRANSACTION_BASIC_INFORMATION txBasicInfo = {0}; + + PAGED_CODE(); + + status = ObOpenObjectByPointer( Transaction, + OBJ_KERNEL_HANDLE, + NULL, + GENERIC_READ, + *TmTransactionObjectType, + KernelMode, + &transactionHandle ); + + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvQueryTransactionOutcome: ObOpenObjectByPointer failed.\n") ); + return status; + } + + status = ZwQueryInformationTransaction( transactionHandle, + TransactionBasicInformation, + &txBasicInfo, + sizeof(TRANSACTION_BASIC_INFORMATION), + NULL ); + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvQueryTransactionOutcome: ObOpenObjectByPointer failed.\n") ); + goto Cleanup; + } + + *TxOutcome = txBasicInfo.Outcome; + +Cleanup: + + ZwClose(transactionHandle); + + return status; +} + +FORCEINLINE +VOID +AvPropagateFileState( + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _In_ ULONG TransactionOutcome + ) +/*++ + +Routine Description: + + An inline function that propagate the TxState to State in stream context. + +Arguments: + + StreamContext - The stream context to be propagated. + + TransactionOutcome - TRANSACTION_OUTCOME enumeration indicating how transaction was ended. + + +Return Value: + + None. + +--*/ +{ + // + // Only when the transaction was committed will we propagate the state. + // + + if (TransactionOutcome == TransactionOutcomeCommitted) { + + AV_FILE_INFECTED_STATE oldTxState = InterlockedExchange( &StreamContext->TxState, AvFileModified ); + switch (oldTxState) { + case AvFileModified: + case AvFileInfected: + case AvFileNotInfected: + + // + // Propagate the file state from TxState to State. + // + + InterlockedExchange( &StreamContext->State, oldTxState ); + break; + case AvFileScanning: + + // + // It is possible at KTM callback, file Tx state is still in scanning. + // All we can do here is to be conservative, that is to assume that + // this commit did involve the modification of the file. + // + + InterlockedExchange( &StreamContext->State, AvFileModified ); + break; + default: + FLT_ASSERTMSG("AvPropagateFileState does not handle the state", FALSE); + break; + } + } + + // + // Either cleanup or commited, we need to reset TxState to be default state. + // + + SET_FILE_TX_MODIFIED( StreamContext ); +} + +NTSTATUS +AvProcessTransactionOutcome ( + _Inout_ PAV_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG TransactionOutcome + ) +/*++ + +Routine Description: + + This is a helper function that process transaction commitment or rollback + +Arguments: + + TransactionContext - Pointer to the minifilter driver's transaction context + set at PostCreate. + + TransactionOutcome - Specifies the type of notifications. Should be either + TransactionOutcomeCommitted or TransactionOutcomeAborted + +Return Value: + + STATUS_SUCCESS - Returning this status value indicates that the minifilter + driver is finished with the transaction. This is a success code. + +--*/ +{ + PLIST_ENTRY scan; + PLIST_ENTRY next; + PAV_STREAM_CONTEXT streamContext = NULL; + PAV_TRANSACTION_CONTEXT oldTxCtx = NULL; + + PAGED_CODE(); + + // + // Tranversing the stream context list, and + // sync the TxState -> State. + // + // Either commit or rollback, we need to cleanup the list + // Tear down stream context list inside transactionContext + // + + AvAcquireResourceExclusive( TransactionContext->Resource ); + + LIST_FOR_EACH_SAFE( scan, next, &TransactionContext->ScListHead ) { + + streamContext = CONTAINING_RECORD( scan, AV_STREAM_CONTEXT, ListInTransaction ); + oldTxCtx = InterlockedCompareExchangePointer( &streamContext->TxContext, NULL, TransactionContext ); + if (oldTxCtx == TransactionContext) { + + // + // The exchange pointer was successful + // + + RemoveEntryList ( scan ); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvProcessTransactionOutcome: Requesting deletion of entry in transaction context: %I64x,%I64x, modified: %d\n", + streamContext->FileId.FileId64.UpperZeroes, + streamContext->FileId.FileId64.Value, + IS_FILE_MODIFIED( streamContext ) ) ); + AvPropagateFileState( streamContext, TransactionOutcome ); + FltReleaseContext( oldTxCtx ); + FltReleaseContext( streamContext ); + } + } + SetFlag( TransactionContext->Flags, AV_TXCTX_LISTDRAINED ); + AvReleaseResource( TransactionContext->Resource ); + + return STATUS_SUCCESS; +} + +NTSTATUS +AvLoadFileStateFromCache ( + _In_ PFLT_INSTANCE Instance, + _In_ PAV_FILE_REFERENCE FileId, + _Out_ LONG volatile *State, + _Out_ PLONGLONG VolumeRevision, + _Out_ PLONGLONG CacheRevision, + _Out_ PLONGLONG FileRevision + ) +/*++ + +Routine Description: + + This routine lookups the file state in the cache table. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + FileID - The ID to lookup in the cache + + State - The cached state for the file + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + AV_GENERIC_TABLE_ENTRY query = {0}; + PAV_GENERIC_TABLE_ENTRY entry = NULL; + + PAGED_CODE(); + + // + // We should never be trying to cache with an invalid fileID. + // + + ASSERT( !AV_INVALID_FILE_REFERENCE(*FileId) ); + + status = FltGetInstanceContext( Instance, + &instanceContext ); + + if (!NT_SUCCESS( status )){ + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvLoadFileStateFromCache: failed to get instance context.\n") ); + return status; + } + + if (! FS_SUPPORTS_FILE_STATE_CACHE( instanceContext->VolumeFSType )) { + + status = STATUS_NOT_FOUND; + goto Cleanup; + } + + RtlCopyMemory( &query.FileId, FileId, sizeof(query.FileId) ); + + AvAcquireResourceShared( &instanceContext->Resource ); + + entry = RtlLookupElementGenericTable( &instanceContext->FileStateCacheTable, + &query ); + + if (entry != NULL) { + *State = entry->InfectedState; + *VolumeRevision = entry->VolumeRevision; + *CacheRevision = entry->CacheRevision; + *FileRevision = entry->FileRevision; + } else { + status = STATUS_NOT_FOUND; + } + + AvReleaseResource( &instanceContext->Resource ); + +Cleanup: + + FltReleaseContext( instanceContext ); + return status; +} + +NTSTATUS +AvSyncCache ( + _In_ PFLT_INSTANCE Instance, + _In_ PAV_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine sync the file state from stream context to volatile cache table. + It is file system transparent. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + StreamContext - The stream context of the target file. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN inserted = FALSE; + AV_GENERIC_TABLE_ENTRY entry = {0}; + PAV_GENERIC_TABLE_ENTRY pEntry = NULL; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + if ((NULL == Instance) || + (NULL == StreamContext)) { + + return STATUS_INVALID_PARAMETER; + } + + status = FltGetInstanceContext( Instance, &instanceContext ); + + if (!NT_SUCCESS( status )){ + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvSyncCache: failed to get instance context.\n") ); + return status; + } + + // + // If the file system is not NTFS, CSVFS or REFS, do nothing + // + + if (!FS_SUPPORTS_FILE_STATE_CACHE( instanceContext->VolumeFSType )) { + goto Cleanup; + } + + // + // If originally, we failed to get the file id, + // then we do not cache it. + // + + if (AV_INVALID_FILE_REFERENCE( StreamContext->FileId )) { + goto Cleanup; + } + + // + // If the file system is NTFS, CSVFS or REFS, overwrite the entry in the + // cache table if exists + // + + RtlCopyMemory( &entry.FileId, &StreamContext->FileId, sizeof(entry.FileId) ); + + AvAcquireResourceExclusive( &instanceContext->Resource ); + + pEntry = RtlInsertElementGenericTable( &instanceContext->FileStateCacheTable, + (PVOID) &entry, + AV_GENERIC_TABLE_ENTRY_SIZE, + &inserted); + if (pEntry) { + + // + // Note the cache may become stale as files are modified. + // + + // + // It is possible that after entering the following else-if + // branch, thread A modifies the file, and before thread A + // closes the handle, thread B opens the same file. This + // is fine because in such a case, the streamcontext exists + // AvLoadFileStateFromCache would return the state in stream + // context. Thus, thread B will need to scan the file. + // + + pEntry->InfectedState = StreamContext->State; + pEntry->VolumeRevision = StreamContext->VolumeRevision; + pEntry->CacheRevision = StreamContext->CacheRevision; + pEntry->FileRevision = StreamContext->FileRevision; + + } + + AvReleaseResource( &instanceContext->Resource ); + + if (!pEntry) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvSyncCache: RtlInsertElementGenericTable failed.\n") ); + } + +Cleanup: + + FltReleaseContext( instanceContext ); + return status; +} + +BOOLEAN +AvIsPrefetchEcpPresent ( + _In_ PFLT_FILTER Filter, + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This local function will return if this data stream is alternate or not. + It by default returns FALSE if it fails to retrieve the name information + from the file system. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + TRUE - This data stream is alternate. + FALSE - This data stream is NOT alternate. + +--*/ +{ + NTSTATUS status; + PECP_LIST ecpList; + PVOID ecpContext; + + PAGED_CODE(); + + status = FltGetEcpListFromCallbackData( Filter, Data, &ecpList ); + + if (NT_SUCCESS(status) && (ecpList != NULL)) { + + status = FltFindExtraCreateParameter( Filter, + ecpList, + &GUID_ECP_PREFETCH_OPEN, + &ecpContext, + NULL ); + + if (NT_SUCCESS(status)) { + + if (!FltIsEcpFromUserMode( Filter, ecpContext )) { + return TRUE; + } + } + } + + return FALSE; +} + +BOOLEAN +AvIsStreamAlternate( + _Inout_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This local function will return if this data stream is alternate or not. + It by default returns FALSE if it fails to retrieve the name information + from the file system. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + TRUE - This data stream is alternate. + FALSE - This data stream is NOT alternate. + +--*/ +{ + NTSTATUS status; + BOOLEAN alternate = FALSE; + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + + PAGED_CODE(); + + status = FltGetFileNameInformation( Data, + FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, + &nameInfo ); + + if (!NT_SUCCESS(status)) { + + goto Cleanup; + } + + status = FltParseFileNameInformation( nameInfo ); + + if (!NT_SUCCESS(status)) { + + goto Cleanup; + } + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[Av]: Dir: %wZ, FinalComponent: %wZ, Stream: %wZ, sLen: %d\n", + nameInfo->ParentDir, + nameInfo->FinalComponent, + nameInfo->Stream, + nameInfo->Stream.Length) ); + + alternate = (nameInfo->Stream.Length > 0); + +Cleanup: + if (nameInfo != NULL) { + + FltReleaseFileNameInformation( nameInfo ); + nameInfo = NULL; + } + return alternate; +} + +NTSTATUS +AvScan ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ AV_SCAN_MODE ScanMode, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _Inout_ PAV_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine kicks of a scan. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + ScanMode - Can either be AvUserMode or AvKernelMode. + + IOMajorFunctionAtScan - Major function of an IRP. + + StreamContext - The stream context of the target file. + +Return Value: + + Returns the final status of this operation. + STATUS_TIMEOUT - if scan in user mode and the thread reference fails, + then it would wait for the scan finish event with a timeout. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + LONGLONG fileSize; + FLT_VOLUME_PROPERTIES volumeProperties; + ULONG volumePropertiesLength; + + PAGED_CODE(); + + // + // Skip the empty file. + // + + status = AvGetFileSize( FltObjects->Instance, + FltObjects->FileObject, + &fileSize ); + + if (NT_SUCCESS( status ) && + (0 == fileSize)) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[Av]: AvScan: Skip the EMPTY file.\n") ); + + // As if we have 'scanned' this empty file. + SET_FILE_NOT_INFECTED( StreamContext ); + return STATUS_SUCCESS; + } + + // + // We could cause deadlocks if the thread were suspended once + // we have started scanning so enter a critical region. + // + + FsRtlEnterFileSystem(); + + // + // Wait here for an existing scan on the stream to complete. + // We wait indefinitely since scans themselves will timeout. + // + + status = FltCancellableWaitForSingleObject( StreamContext->ScanSynchronizationEvent, + NULL, + Data ); + + if (NT_SUCCESS(status)) { + + // + // Check again in case the file was scanned during the wait + // and is already known to be clean + // + + if (IS_FILE_NEED_SCAN( StreamContext )){ + + if (ScanMode == AvUserMode) { + + status = FltGetVolumeProperties( FltObjects->Volume, + &volumeProperties, + sizeof(volumeProperties), + &volumePropertiesLength ); + if (!NT_SUCCESS(status)) { + volumeProperties.DeviceType = FILE_DEVICE_NETWORK; + } + + // + // If the scan mode is user mode, the section context will + // be created as needed (at MessageNotification callback). + // + // Setting the file state will be done at + // MessageNotification callback as well. + // + + status = AvScanInUser( Data, + FltObjects, + IOMajorFunctionAtScan, + IsInTxWriter, + volumeProperties.DeviceType ); + + if (!NT_SUCCESS( status ) || status == STATUS_TIMEOUT) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvScan: failed to scan the file.\n") ); + } + + } else { + + status = AvScanInKernel( FltObjects, + IOMajorFunctionAtScan, + IsInTxWriter, + StreamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvScan: failed to scan the file.\n") ); + } + + } + + } + + // + // Signal ScanSynchronizationEvent to release any con-current scan of the stream, + // + KeSetEvent( StreamContext->ScanSynchronizationEvent, 0, FALSE ); + + } else if (IOMajorFunctionAtScan == IRP_MJ_CREATE) { + + // + // I/O requesting thread if waiting on synchronization event is cancelled, + // we need to clean up the file object too. + // + AvCancelFileOpen(Data, FltObjects, status); + } + + FsRtlExitFileSystem(); + + return status; +} + +VOID +AvDoCancelScanAndRelease ( + _In_ PAV_SCAN_CONTEXT ScanContext, + _In_ PAV_SECTION_CONTEXT SectionContext + ) +/*++ + +Routine Description: + + This routine closes the section object, and released all waiting threads. + +Arguments: + + ScanContext - The scan context. + + SectionContext - The section context associated with the scan context. + +Return Value: + + None. + +--*/ +{ + NTSTATUS status; + PAV_STREAM_CONTEXT streamContext = NULL; + + PAGED_CODE(); + + AvFinalizeSectionContext( SectionContext ); + + status = FltGetStreamContext( ScanContext->FilterInstance, + ScanContext->FileObject, + &streamContext ); + + if (NT_SUCCESS( status )) { + + KeSetEvent( streamContext->ScanSynchronizationEvent, 0, FALSE ); + FltReleaseContext( streamContext ); + } + + // + // Release I/O request thread. + // + + KeSetEvent( &ScanContext->ScanCompleteNotification, 0, FALSE ); + return; +} + +NTSTATUS +AvSendAbortToUser ( + _In_ ULONG ScanThreadId, + _In_ LONGLONG ScanId + ) +/*++ + +Routine Description: + + This routine sends an abortion message to the user scan thread. + The cancel callback is asynchronous and thus we send which + scan id to abort; otherwise the worker thread in the user + may abort the 'next' scan task. + +Arguments: + + ScanThreadId - The thread identifier of whom to be aborted. + + ScanId - Which scan task to be aborted. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG replyLength = 0; + LARGE_INTEGER timeout = {0}; + AV_SCANNER_NOTIFICATION notification = {0}; + + PAGED_CODE(); + + notification.Message = AvMsgAbortScanning; + notification.ScanThreadId = ScanThreadId; + notification.ScanId = ScanId; + + timeout.QuadPart = -((LONGLONG)10) * (LONGLONG)1000 * (LONGLONG)1000; // 1s + + // + // Tell the user-scanner to abort the scan. + // + + status = FltSendMessage( Globals.Filter, + &Globals.AbortClientPort, + ¬ification, + sizeof(AV_SCANNER_NOTIFICATION), + NULL, + &replyLength, + &timeout ); + + + if (!NT_SUCCESS( status ) || + (status == STATUS_TIMEOUT)) { + + if ((status != STATUS_PORT_DISCONNECTED) && + (status != STATUS_TIMEOUT)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvSendAbortToUser: Failed to FltSendMessage.\n, 0x%08x\n", + status) ); + } + return status; + } + return status; +} + +NTSTATUS +AvSendUnloadingToUser ( + VOID + ) +/*++ + +Routine Description: + + This routine sends unloading message to the user program. + +Arguments: + + None. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + ULONG abortThreadId; + NTSTATUS status = STATUS_SUCCESS; + ULONG replyLength = sizeof(ULONG); + AV_SCANNER_NOTIFICATION notification = {0}; + + PAGED_CODE(); + + notification.Message = AvMsgFilterUnloading; + + // + // Tell the user-scanner that we are unloading the filter. + // and waits for its reply. + // + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvSendUnloadingToUser: BEFORE...\n") ); + + status = FltSendMessage( Globals.Filter, + &Globals.AbortClientPort, + ¬ification, + sizeof(AV_SCANNER_NOTIFICATION), + &abortThreadId, + &replyLength, + NULL ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvSendUnloadingToUser: Failed to FltSendMessage.\n, 0x%08x\n", + status) ); + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvSendUnloadingToUser: After...\n") ); + + return status; +} + +/************************************************************************* + MiniFilter callback routines. +*************************************************************************/ + +FLT_PREOP_CALLBACK_STATUS +AvPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the registered callback routine for filtering + the "write" operation, i.e. the operations that have potentials + to modify the file. + + This is non-pageable because it could be called on the paging path + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PAV_STREAM_CONTEXT streamContext = NULL; + PAV_STREAMHANDLE_CONTEXT streamHandleContext = NULL; + ULONG flags; + + UNREFERENCED_PARAMETER( CompletionContext ); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPreOperationCallback: Entered\n") ); + + if (!AvOperationsModifyingFile(Data)) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Skip prefetcher handles to avoid deadlocks + // + + status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &streamHandleContext ); + if (NT_SUCCESS(status)) { + + flags = streamHandleContext->Flags; + + FltReleaseContext( streamHandleContext ); + + if (FlagOn( flags, AV_FLAG_PREFETCH )) { + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + } + + status = FltGetStreamContext( FltObjects->Instance, + FltObjects->FileObject, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPreOperationCallback: get stream context failed. rq: %d\n", + Data->Iopb->MajorFunction) ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // If this operation is performed in a transacted writer view. + // + + if ((streamContext->TxContext != NULL) && + (FltObjects->Transaction != NULL)) { + +#if DBG + PAV_TRANSACTION_CONTEXT transactionContext = NULL; + + NTSTATUS statusTx = FltGetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + &transactionContext ); + + FLT_ASSERTMSG( "Transaction context should not fail, because it is supposed to be created at post create.\n", NT_SUCCESS( statusTx )); + FLT_ASSERTMSG( "The file's TxCtx should be identical with the target TxCtx.\n", + streamContext->TxContext == transactionContext); + + if (NT_SUCCESS( statusTx )) { + FltReleaseContext( transactionContext ); + } + +#endif // DBG + + // + // Instead of updating State, we update TxState here, + // because the file is part of a transaction writer + // + + SET_FILE_TX_MODIFIED( streamContext ); + + } else { + + // + // Consider an optimization for the case where another thread + // is already scanning the file as it is being modified here. + // + + SET_FILE_MODIFIED( streamContext ); + } + + FltReleaseContext( streamContext ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + +FLT_PREOP_CALLBACK_STATUS +AvPreFsControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre-file system control callback. This filter example does not support save point feature. + So, we explicitly fail the request here. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + + PAGED_CODE(); + + if (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_TXFS_SAVEPOINT_INFORMATION ) { + + // + // We explicitly fail the request of save point here since we + // are deprecating savepoint support for the OS version targeted + // for this filter. + // + + Data->IoStatus.Status = STATUS_NOT_SUPPORTED; + return FLT_PREOP_COMPLETE; + } + return AvPreOperationCallback(Data, FltObjects, CompletionContext); +} + +FLT_PREOP_CALLBACK_STATUS +AvPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the pre-create completion routine. + + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + FLT_PREOP_SYNCHRONIZE - PostCreate needs to be called back synchronizedly. + FLT_PREOP_SUCCESS_NO_CALLBACK - PostCreate does not need to be called. + +--*/ +{ + ULONG_PTR stackLow; + ULONG_PTR stackHigh; + PFILE_OBJECT FileObject = Data->Iopb->TargetFileObject; + AV_STREAMHANDLE_CONTEXT streamHandleContext; + + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPreCreate: Entered\n") ); + + streamHandleContext.Flags = 0; + + // + // Stack file objects are never scanned. + // + + IoGetStackLimits( &stackLow, &stackHigh ); + + if (((ULONG_PTR)FileObject > stackLow) && + ((ULONG_PTR)FileObject < stackHigh)) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Directory opens don't need to be scanned. + // + + if (FlagOn( Data->Iopb->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Skip pre-rename operations which always open a directory. + // + + if ( FlagOn( Data->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY )) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Skip paging files. + // + + if (FlagOn( Data->Iopb->OperationFlags, SL_OPEN_PAGING_FILE )) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Skip scanning DASD opens + // + + if (FlagOn( FltObjects->FileObject->Flags, FO_VOLUME_OPEN )) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Skip scanning any files being opened by CSVFS for its downlevel + // processing. This includes filters on the hidden NTFS stack and + // for filters attached to MUP + // + if (AvIsCsvDlEcpPresent( FltObjects->Filter, Data ) ) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + + // + // Flag prefetch handles so they can be skipped. Performing IO + // using a prefetch fileobject could lead to a deadlock. + // + + if (AvIsPrefetchEcpPresent( FltObjects->Filter, Data )) { + + SetFlag( streamHandleContext.Flags, AV_FLAG_PREFETCH ); + } + + *CompletionContext = (PVOID)streamHandleContext.Flags; + + // + // Perform any CSVFS pre create processing + // + AvPreCreateCsvfs( Data, FltObjects ); + + // + // return status can be safely ignored + // + + // + // Return FLT_PREOP_SYNCHRONIZE at PreCreate to ensure PostCreate + // is in the same thread at passive level. + // EResource can't be acquired at DPC. + // + + return FLT_PREOP_SYNCHRONIZE; + +} + +NTSTATUS +AvProcessPreviousTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine is transaction related implmentation, and is expected to be + invoked at post-create. Note that this function will enlist the newly + allocated transaction context via FltEnlistInTransaction if it needs to. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + StreamContext - The stream context. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_TRANSACTION_CONTEXT oldTxCtx = NULL; + PAV_TRANSACTION_CONTEXT transactionContext = NULL; + + PAGED_CODE(); + + if (FltObjects->Transaction != NULL ) { + + // + // Get transaction context + // + + status = AvFindOrCreateTransactionContext( FltObjects, + &transactionContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvProcessPreviousTransaction: AvFindOrCreateTransactionContext FAILED\n") ); + transactionContext = NULL; + goto Cleanup; + } + + // + // Enlist it if haven't. + // + + if (! FlagOn(transactionContext->Flags, AV_TXCTX_ENLISTED) ) { + + // + // You can also consider to register TRANSACTION_NOTIFY_PREPARE, + // and scan the file at TRANSACTION_NOTIFY_PREPARE callback if it was modified. + // + + status = FltEnlistInTransaction( FltObjects->Instance, + FltObjects->Transaction, + transactionContext, + TRANSACTION_NOTIFY_COMMIT_FINALIZE | TRANSACTION_NOTIFY_ROLLBACK ); + + if (!NT_SUCCESS( status ) && + (status != STATUS_FLT_ALREADY_ENLISTED)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvProcessPreviousTransaction: FltEnlistInTransaction FAILED!!!!\n") ); + goto Cleanup; + } + status = STATUS_SUCCESS; + SetFlag( transactionContext->Flags, AV_TXCTX_ENLISTED ); + } + } + + // + // Here we have five cases: + // + // 1) + // oldTxCtx : NULL + // transCtx : B + // 2) + // oldTxCtx : A + // transCtx : NULL + // 3) + // oldTxCtx : A + // transCtx : B + // 4) + // oldTxCtx : A + // transCtx : A + // 5) + // oldTxCtx : NULL + // transCtx : NULL + // + + // + // Synchronize the replacement of StreamContext->TxContext with KTM callback. + // + + oldTxCtx = InterlockedExchangePointer( &StreamContext->TxContext, transactionContext ); + + if (oldTxCtx != transactionContext) { // case 1,2,3 + + // + // txOutcome is by default set as committed because we are conservative about + // propagating the file state if AvQueryTransactionOutcome failed, it may cause + // redundant scan but will not overlook infected file anyway. + // + + ULONG txOutcome = TransactionOutcomeCommitted; + + if ( oldTxCtx == NULL ) { // case 1 + + // This file was not linked in a transaction context yet, and is about to. + // + // Increment TxContext's reference count because stream context has a reference to it. + // + + FltReferenceContext ( transactionContext ); + + // + // Before insertion into the FcList in transaction context, we increment stream context's ref count + // + + AvAcquireResourceExclusive( transactionContext->Resource ); + + if (!FlagOn(transactionContext->Flags, AV_TXCTX_LISTDRAINED)) { + + FltReferenceContext ( StreamContext ); // Q + InsertTailList( &transactionContext->ScListHead, + &StreamContext->ListInTransaction ); + } + + AvReleaseResource( transactionContext->Resource ); + + goto Cleanup; + } + + // case 2,3 + + // + // We have to query transaction outcome in order to know how we + // can process the previously outstanding transaction context. + // + + status = AvQueryTransactionOutcome( oldTxCtx->Transaction, &txOutcome ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvProcessPreviousTransaction: AvQueryTransactionOutcome FAILED!!!!\n") ); + + // + // We have exchanged the pointer anyway, if we cannot query its outcome, + // we have to go through. + // + } + + AvAcquireResourceExclusive( oldTxCtx->Resource ); + RemoveEntryList ( &StreamContext->ListInTransaction ); + AvReleaseResource( oldTxCtx->Resource ); + + AvPropagateFileState ( StreamContext, txOutcome ); + + if ( transactionContext ) { // case 3 + + FltReferenceContext( transactionContext ); + + AvAcquireResourceExclusive( transactionContext->Resource ); + + if (!FlagOn(transactionContext->Flags, AV_TXCTX_LISTDRAINED)) { + + InsertTailList( &transactionContext->ScListHead, + &StreamContext->ListInTransaction ); + + } else { + + FltReleaseContext( StreamContext ); + } + + AvReleaseResource( transactionContext->Resource ); + + } else { // case 2 + + FltReleaseContext ( StreamContext ); // Release reference count at Q + } + + // case 2,3 + + FltReleaseContext( oldTxCtx ); // Release reference count in stream context originally. + + } + + // + // We don't care about case 4, 5. + // + +Cleanup: + + if (transactionContext) { + + FltReleaseContext( transactionContext ); // Release the ref count grabbed at AvFindOrCreateTransactionContext(...) + } + + return status; +} + +FLT_POSTOP_CALLBACK_STATUS +AvPostCreate (_Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-create completion routine. + In this routine, stream context and/or transaction context shall be + created if not exits. + + Note that we only allocate and set the stream context to filter manager + at post create. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The completion context set in the pre-create routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status = Data->IoStatus.Status; + BOOLEAN isDir = FALSE; + BOOLEAN isTxWriter = FALSE; + + PAV_STREAM_CONTEXT streamContext = NULL; + PAV_STREAM_CONTEXT oldStreamContext = NULL; + PAV_STREAMHANDLE_CONTEXT streamHandleContext = NULL; + ACCESS_MASK desiredAccess = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess; + + BOOLEAN updateRevisionNumbers; + LONGLONG VolumeRevision, CacheRevision, FileRevision; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + if (!NT_SUCCESS( status ) || + (status == STATUS_REPARSE)) { + + // + // File Creation may fail. + // + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPostCreate: file creation failed\n") ); + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // After creation, skip it if it is directory. + // + + status = FltIsDirectory( FltObjects->FileObject, + FltObjects->Instance, + &isDir ); + + // + // If FltIsDirectory failed, we do not know if it is a directoy, + // we let it go through because if it is a directory, it will fail + // at section creation anyway. + // + + if ( NT_SUCCESS( status ) && isDir ) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // We skip the encrypted file open without FILE_WRITE_DATA and FILE_READ_DATA + // This is because if application calls OpenEncryptedFileRaw(...) for backup, + // it won't have to decrypt the file. In such case, if we scan it, we will hit + // an assertion error in NTFS because it does not have the encryption context. + // Thus, we have to skip the encrypted file not open for read/write. + // + + if (!(FlagOn(desiredAccess, FILE_WRITE_DATA)) && + !(FlagOn(desiredAccess, FILE_READ_DATA)) ) { + + BOOLEAN encrypted = FALSE; + status = AvGetFileEncrypted( FltObjects->Instance, + FltObjects->FileObject, + &encrypted ); + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPostCreate: AvGetFileEncrypted FAILED!! \n0x%x\n", status) ); + } + if (encrypted) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + } + + // + // In this sample, we skip the alternate data stream. However, you may decide + // to scan it and modify accordingly. + // + + if (AvIsStreamAlternate( Data )) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Skip a prefetch open and flag it so we skip subsequent + // IO operations on the handle. + // + + if (FlagOn((ULONG_PTR)CompletionContext, AV_FLAG_PREFETCH)) { + + if (!FltSupportsStreamHandleContexts( FltObjects->FileObject )) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + status = AvCreateStreamHandleContext( FltObjects->Filter, + &streamHandleContext ); + + if (!NT_SUCCESS(status)) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + SetFlag( streamHandleContext->Flags, AV_FLAG_PREFETCH ); + + status = FltSetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + streamHandleContext, + NULL ); + + FltReleaseContext( streamHandleContext ); + + if (!NT_SUCCESS(status)) { + + // + // Shouldn't find the handle already set + // + + ASSERT( status != STATUS_FLT_CONTEXT_ALREADY_DEFINED ); + } + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Find or create a stream context + // + + status = FltGetStreamContext( FltObjects->Instance, + FltObjects->FileObject, + &streamContext ); + + if (status == STATUS_NOT_FOUND) { + + // + // Create a stream context + // + + status = AvCreateStreamContext( FltObjects->Filter, &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to create stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + FltObjects->FileObject, + FltObjects->Instance) ); + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Attempt to get the stream infected state from our cache + // + + status = AvGetFileId( FltObjects->Instance, FltObjects->FileObject, &streamContext->FileId ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[Av]: Failed to get file id with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + FltObjects->FileObject, + FltObjects->Instance) ); + + // + // File id is optional and therefore should not affect the scan logic. + // + + AV_SET_INVALID_FILE_REFERENCE( streamContext->FileId ) + + } else { + + // + // This function will load the file infected state from the + // cache if the fileID is valid. Even if this function fails, + // we still have to move on because the cache is optional. + // + + AvLoadFileStateFromCache( FltObjects->Instance, + &streamContext->FileId, + &streamContext->State, + &streamContext->VolumeRevision, + &streamContext->CacheRevision, + &streamContext->FileRevision ); + } + + // + // Set the new context we just allocated on the file object + // + + status = FltSetStreamContext( FltObjects->Instance, + FltObjects->FileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + streamContext, + &oldStreamContext ); + + if (!NT_SUCCESS(status)) { + + if (status == STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + // + // Race condition. Someone has set a context after we queried it. + // Use the already set context instead + // + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Race: Stream context already defined. Retaining old stream context %p (FileObject = %p, Instance = %p)\n", + oldStreamContext, + FltObjects->FileObject, + FltObjects->Instance) ); + + FltReleaseContext( streamContext ); + + streamContext = oldStreamContext; + + } else { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to set stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + FltObjects->FileObject, + FltObjects->Instance) ); + goto Cleanup; + } + } + + } else if (!NT_SUCCESS(status)) { + + // + // We will get here if stream contexts are not supported + // + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to get stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + FltObjects->FileObject, + FltObjects->Instance) ); + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // If successfully opened a file with the desired access matching + // the "exclusive write" from a TxF point of view, we can guarantee that + // if previous transaction context exists, it must have been comitted + // or rollbacked. + // + + if (FlagOn( Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, + FILE_WRITE_DATA | FILE_APPEND_DATA | + DELETE | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | + WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY ) ) { + + // + // Either this file is opened in a transaction context or not, + // we need to process the previous transaction if it exists. + // AvProcessPreviousTransaction(...) handles these cases. + // + + status = AvProcessPreviousTransaction ( FltObjects, + streamContext ); + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPostCreate: AvProcessTransaction FAILED!! \n") ); + + goto Cleanup; + } + + isTxWriter = (FltObjects->Transaction != NULL); + } + + // + // Perform any CSVFS specific processing + // + AvPostCreateCsvfs( Data, + FltObjects, + streamContext, + &updateRevisionNumbers, + &VolumeRevision, + &CacheRevision, + &FileRevision ); + // + // Ignore return status + // + + + + if (IS_FILE_NEED_SCAN( streamContext )) { + + status = AvScan( Data, + FltObjects, + AvUserMode, + Data->Iopb->MajorFunction, + isTxWriter, + streamContext ); + if (!NT_SUCCESS( status ) || + (STATUS_TIMEOUT == status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPostCreate: AvScan FAILED!! \n") ); + + goto Cleanup; + } + } + + + // + // If needed, update the stream context with the latest revision + // numbers that correspond to the verion just scanned + // + if (updateRevisionNumbers) { + streamContext->VolumeRevision = VolumeRevision; + streamContext->CacheRevision = CacheRevision; + streamContext->FileRevision = FileRevision; + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvPostCreate: RevisionNumbers updated to %I64x:%I64x:%I64x\n", + VolumeRevision, + CacheRevision, + FileRevision) + ); + } + + if (IS_FILE_INFECTED( streamContext )) { + + // + // If the file is infected, deny the access. + // + AvCancelFileOpen(Data, FltObjects, STATUS_VIRUS_INFECTED); + + // + // If the scan timed-out or scan was failed, we let the create succeed, + // and it may cause security hole; + // + // Alternatively, you can add a state called AvFileScanFailure or equivalent, + // add a condition here and fail the create. This option will have better + // protection from viruses, but the apps will see the failures due to a + // lengthy scan or scan failure. It's a trade-off. + // + goto Cleanup; + } + +Cleanup: + + FltReleaseContext( streamContext ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + +FLT_PREOP_CALLBACK_STATUS +AvPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre-cleanup callback. Make the stream context persistent in the volatile cache. + If the file is transacted, it will be synced at KTM notification callback + if committed. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + BOOLEAN encrypted = FALSE; + PAV_STREAM_CONTEXT streamContext = NULL; + PAV_STREAMHANDLE_CONTEXT streamHandleContext = NULL; + ULONG_PTR stackLow; + ULONG_PTR stackHigh; + + BOOLEAN updateRevisionNumbers; + LONGLONG VolumeRevision, CacheRevision, FileRevision; + + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + // + // Skip scan on prefetcher handles to avoid deadlocks + // + + status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &streamHandleContext ); + if (NT_SUCCESS(status)) { + + if (FlagOn( streamHandleContext->Flags, AV_FLAG_PREFETCH )) { + + // + // Because the Memory Manager can cache the file object + // and use it for other applications performing mapped I/O, + // whenever a Cleanup operation is seen on a prefetcher + // file object, that file object should no longer be + // considered prefetcher-opened. + // + + RtlInterlockedClearBits( &streamHandleContext->Flags, + AV_FLAG_PREFETCH ); + + FltDeleteStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + NULL ); + + FltReleaseContext( streamHandleContext ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + FltReleaseContext( streamHandleContext ); + } + + // + // Stack file objects are never scanned. + // + + IoGetStackLimits( &stackLow, &stackHigh ); + + if (((ULONG_PTR)FltObjects->FileObject > stackLow) && + ((ULONG_PTR)FltObjects->FileObject < stackHigh)) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + status = FltGetStreamContext( FltObjects->Instance, + FltObjects->FileObject, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPreCleanup: find stream context failed.\n") ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // We skip encrypted files at cleanup time because we cannot be + // sure if the file is open raw for backup. It will get scanned + // on the next open anyway. + // + + status = AvGetFileEncrypted( FltObjects->Instance, + FltObjects->FileObject, + &encrypted ); + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPreCleanup: AvGetFileEncrypted FAILED!! \n") ); + + goto Cleanup; + } + + if (encrypted) { + + goto Cleanup; + } + + AvPreCleanupCsvfs( Data, + FltObjects, + streamContext, + &updateRevisionNumbers, + &VolumeRevision, + &CacheRevision, + &FileRevision ); + + // + // For applications, the typical calling sequence is, close the file handle + // and commit/rollback the changes. We skip the scan here for + // transacted writer because we do not know if the change will be + // rollbacked or not. If it eventually commits, it will be scanned + // at next create anyway. However, if it rollbacks, the scan here will + // be redundant. + // + + if ((streamContext->TxContext == NULL) && + IS_FILE_MODIFIED( streamContext )) { + + status = AvScan( Data, + FltObjects, + AvUserMode, + Data->Iopb->MajorFunction, + FALSE, + streamContext ); + + if (!NT_SUCCESS( status ) || STATUS_TIMEOUT == status) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPreCleanup: AvScan FAILED!! \n") ); + + goto Cleanup; + } + + + // + // If needed, update the stream context with the latest revision + // numbers that correspond to the verion just scanned + // + if (updateRevisionNumbers) { + streamContext->VolumeRevision = VolumeRevision; + streamContext->CacheRevision = CacheRevision; + streamContext->FileRevision = FileRevision; + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvPreCleanup: RevisionNumbers updated to %I64x:%I64x:%I64x\n", + VolumeRevision, + CacheRevision, + FileRevision) + ); + } + + } + +Cleanup: + + // + // We only insert the entry when the file is clean or infected. + // + + if (!IS_FILE_MODIFIED( streamContext ) || + IS_FILE_INFECTED( streamContext )) { + + if (!NT_SUCCESS ( AvSyncCache( FltObjects->Instance, streamContext ))) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPreCleanup: AvSyncCache FAILED!! \n") ); + } + } + + FltReleaseContext( streamContext ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + +NTSTATUS +AvKtmNotificationCallback ( + _Unreferenced_parameter_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ) +/*++ + +Routine Description: + + The registered routine of type PFLT_TRANSACTION_NOTIFICATION_CALLBACK + in FLT_REGISTRATION structure. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + TransactionContext - Pointer to the minifilter driver's transaction context + set at PostCreate. + + TransactionNotification - Specifies the type of notifications that the + filter manager is sending to the minifilter driver. + +Return Value: + + STATUS_SUCCESS - Returning this status value indicates that the minifilter + driver is finished with the transaction. This is a success code. + + STATUS_PENDING - Returning this status value indicates that the minifilter + driver is not yet finished with the transaction. This is a success code. + +--*/ +{ + PAV_TRANSACTION_CONTEXT transactionContext = (PAV_TRANSACTION_CONTEXT) TransactionContext; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( FltObjects ); + + FLT_ASSERTMSG("[AV] AvKtmNotificationCallback: The expected type of notifications registered at FltEnlistInTransaction(...).\n", + FlagOn( TransactionNotification, + (TRANSACTION_NOTIFY_COMMIT_FINALIZE | TRANSACTION_NOTIFY_ROLLBACK) ) ); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvKtmNotificationCallback: Entered\n") ); + + if (NULL != transactionContext) { + + if ( FlagOn( TransactionNotification, TRANSACTION_NOTIFY_COMMIT_FINALIZE ) ) { + + return AvProcessTransactionOutcome( TransactionContext, TransactionOutcomeCommitted ); + + } else { + + return AvProcessTransactionOutcome( TransactionContext, TransactionOutcomeAborted ); + } + } + + return STATUS_SUCCESS; +} + +NTSTATUS +AvScanAbortCallbackAsync ( + _Unreferenced_parameter_ PFLT_INSTANCE Instance, + _In_ PFLT_CONTEXT Context, + _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This routine is the registered cancel callback function in FLT_REGISTRATION. + It would be invoked by the file system if it decides to abort the scan. + As its name suggests, this function is asynchrounous, so the caller is not + blocked. + + Note: This routine may be called before FltCreateSectionForDataScan returns. + This means the SectionHandle and SectionObject may not yet be set in the + SectionContext. We can't take a dependency on these being set before needing + to abort the scan. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + Context - The section context. + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + PAV_SECTION_CONTEXT sectionCtx = (PAV_SECTION_CONTEXT) Context; + PAV_SCAN_CONTEXT scanCtx = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Instance ); + UNREFERENCED_PARAMETER( Data ); + + if (NULL == sectionCtx) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvScanAbortCallbackAsync: INVALID ARGUMENT.\n") ); + return STATUS_INVALID_PARAMETER_2; + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvScanAbortCallbackAsync: closesection handle=%p, object=%p, cancelable=%d\n", + sectionCtx->SectionHandle, + sectionCtx->SectionObject, + sectionCtx->CancelableOnConflictingIo) ); + + // + // Send abort signal only when the scanning + // happens in cancelable context (such as pre-cleanup). + // + + if (sectionCtx->CancelableOnConflictingIo) { + + // + // The only reason of scan context being NULL is that + // the section context is about to close anyway. + // Please see AvCloseSectionForDataScan(...) + // + scanCtx = InterlockedExchangePointer( §ionCtx->ScanContext, NULL ); + + if (scanCtx == NULL) { + + return STATUS_SUCCESS; + } + + sectionCtx->Aborted = TRUE; + AvSendAbortToUser( scanCtx->ScanThreadId, scanCtx->ScanId ); + + } + + return STATUS_SUCCESS; +} + +NTSTATUS +AvSetConfiguration ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Descrition: + + This routine sets the filter configuration based on registry values. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + Returns the status of this operation. + + +--*/ +{ + NTSTATUS status; + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey = NULL; + UNICODE_STRING valueName; + UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; + PKEY_VALUE_PARTIAL_INFORMATION value = (PKEY_VALUE_PARTIAL_INFORMATION)buffer; + ULONG valueLength = sizeof(buffer); + ULONG resultLength; + + // + // Open the SimRep registry key. + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (!NT_SUCCESS( status )) { + + goto Cleanup; + } + + +#if DBG + + // + // Query the debug level + // + + RtlInitUnicodeString( &valueName, L"DebugLevel" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + value, + valueLength, + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.DebugLevel = *(PULONG)value->Data; + } + +#endif + + // + // Query the local scan timeout + // + + RtlInitUnicodeString( &valueName, L"LocalScanTimeout" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + value, + valueLength, + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.LocalScanTimeout = (LONGLONG)(*(PULONG)value->Data); + } + + // + // Query the network scan timeout + // + + RtlInitUnicodeString( &valueName, L"NetworkScanTimeout" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + value, + valueLength, + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.NetworkScanTimeout = (LONGLONG)(*(PULONG)value->Data); + } + + status = STATUS_SUCCESS; + +Cleanup: + + if (driverRegKey != NULL) { + + ZwClose( driverRegKey ); + } + + return status; +} + + diff --git a/filesys/miniFilter/avscan/filter/avscan.h b/filesys/miniFilter/avscan/filter/avscan.h new file mode 100644 index 00000000..45c9ba3e --- /dev/null +++ b/filesys/miniFilter/avscan/filter/avscan.h @@ -0,0 +1,271 @@ +/*++ + +Copyright (c) 1989-2011 Microsoft Corporation + +Module Name: + + avscan.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. Mainly used by avscan module. + +Environment: + + Kernel mode + +--*/ +#ifndef __AVSCAN_H__ +#define __AVSCAN_H__ + +#ifndef RTL_USE_AVL_TABLES +#define RTL_USE_AVL_TABLES +#endif // RTL_USE_AVL_TABLES + +#define AV_VISTA (NTDDI_VERSION >= NTDDI_VISTA) + +#include +#include +#include +#include "utility.h" +#include "context.h" +#include "scan.h" +#include "csvfs.h" +#include "avlib.h" + + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +// +// Scan context. +// +// We chose to seperate scan context and section context to have one struct per concept. +// The I/O request thread does not need to know how scanner implement the scan, so +// that the I/O request thread has less coupling with scanner threads. +// +// You can also put all of fields of the scan context into a section context, and allocate +// section context at the place of allocation of scan context. +// + +typedef struct _AV_SCAN_CONTEXT { + + LONG RefCount; + PFLT_INSTANCE FilterInstance; + PFILE_OBJECT FileObject; + KEVENT ScanCompleteNotification; + LIST_ENTRY List; + PAV_SECTION_CONTEXT SectionContext; + LONGLONG ScanId; + ULONG ScanThreadId; + + UCHAR IOMajorFunctionAtScan; + BOOLEAN IsFileInTxWriter; + BOOLEAN IoWaitOnScanCompleteNotificationAborted; + +} AV_SCAN_CONTEXT, *PAV_SCAN_CONTEXT; + +// +// The global variable +// + +typedef struct _AV_SCANNER_GLOBAL_DATA { + + // + // A counter for Scan Id + // + + LONGLONG ScanIdCounter; + + // + // The global FLT_FILTER pointer. Many API needs this, such as + // FltAllocateContext(...) + // + + PFLT_FILTER Filter; + + // + // Server-side communicate ports. + // + + PFLT_PORT ScanServerPort; + PFLT_PORT AbortServerPort; + PFLT_PORT QueryServerPort; + + // + // The scan client ports. + // These ports are assigned at AvConnectNotifyCallback and cleaned at AvDisconnectNotifyCallback + // + // ScanClientPort is the connection port regarding the scan message. + // AbortClientPort is the connection port regarding the abort message. + // QueryClient is the connection port regarding the query command. + // + + PFLT_PORT ScanClientPort; + PFLT_PORT AbortClientPort; + PFLT_PORT QueryClientPort; + + // + // Scan context list head. + // At AvMessageNotifyCallback, when user passes ScanCtxId, we + // have to check the validity of the id by checking this list. + // + + LIST_ENTRY ScanCtxListHead; + + // + // The lock that synchronizes the accesses of the scan context list above. + // + + ERESOURCE ScanCtxListLock; + + // + // Timeout for local file scans in milliseconds + // + + LONGLONG LocalScanTimeout; + + // + // Timeout for network file scans in milliseconds + // + + LONGLONG NetworkScanTimeout; + +#if DBG + + // + // Field to control nature of debug output + // + + ULONG DebugLevel; +#endif + + // + // A flag that indicating that the filter is being unloaded. + // + + BOOLEAN Unloading; + +} AV_SCANNER_GLOBAL_DATA, *PAV_SCANNER_GLOBAL_DATA; + +AV_SCANNER_GLOBAL_DATA Globals; + +#if DBG + +// +// Debugging level flags. +// + +#define AVDBG_TRACE_ROUTINES 0x00000001 +#define AVDBG_TRACE_OPERATION_STATUS 0x00000002 +#define AVDBG_TRACE_DEBUG 0x00000004 +#define AVDBG_TRACE_ERROR 0x00000008 + +#define AV_DBG_PRINT( _dbgLevel, _string ) \ + if(FlagOn(Globals.DebugLevel,(_dbgLevel))) { \ + DbgPrint _string; \ + } + +#else + +#define AV_DBG_PRINT(_dbgLevel, _string) {NOTHING;} + +#endif + +FORCEINLINE +VOID +AvCancelFileOpen( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ NTSTATUS Status + ) +/*++ + +Routine Description: + + This function cancel the file open. This is supposed to be called at post create if + the I/O is cancelled. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + Status - The status code to be returned for this IRP. + +Return Value: + + None. + +--*/ + +{ + FltCancelFileOpen( FltObjects->Instance, FltObjects->FileObject ); + Data->IoStatus.Status = Status; + Data->IoStatus.Information = 0; +} + + +NTSTATUS +AvPrepareServerPort( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ AVSCAN_CONNECTION_TYPE ConnectionType + ); + +NTSTATUS +AvSendAbortToUser ( + _In_ ULONG ScanThreadId, + _In_ LONGLONG ScanId + ); + +NTSTATUS +AvAllocateScanContext( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Outptr_ PAV_SCAN_CONTEXT *ScanContext + ); + +NTSTATUS +AvReferenceScanContext( + _In_ PAV_SCAN_CONTEXT ScanContext + ); + +NTSTATUS +AvReleaseScanContext( + _In_ PAV_SCAN_CONTEXT ScanContext + ); + +// +// Fianlize function for scan context and section context. +// Wrapper functions of synchronization calling sequences. +// In the normal cases, the caller should call AvFinalizeScanAndSection +// when it finishes using it. +// +// Unless the caller wants to do things about section context inside scan context, +// then it should call AvFinalizeScanContext(), and followed by +// AvFinalizeSectionContext() +// +// These wrappers are designed to make the synchronization easier. +// +NTSTATUS +AvFinalizeScanAndSection ( + _Inout_ PAV_SCAN_CONTEXT ScanContext + ); + +NTSTATUS +AvFinalizeSectionContext ( + _Inout_ PAV_SECTION_CONTEXT SectionContext + ); + +VOID +AvFinalizeScanContext ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _Outptr_result_maybenull_ PAV_SECTION_CONTEXT *SectionContext + ); + +#endif + diff --git a/filesys/miniFilter/avscan/filter/avscan.rc b/filesys/miniFilter/avscan/filter/avscan.rc new file mode 100644 index 00000000..db19497a --- /dev/null +++ b/filesys/miniFilter/avscan/filter/avscan.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Anti-virus Filter Driver" +#define VER_INTERNALNAME_STR "avscan.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/avscan/filter/avscan.vcxproj b/filesys/miniFilter/avscan/filter/avscan.vcxproj new file mode 100644 index 00000000..d68461a9 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/avscan.vcxproj @@ -0,0 +1,185 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A} + $(MSBuildProjectName) + Debug + Win32 + {94FDDA0B-6FFA-4B09-A8F0-A303F46D8BF0} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + avscan + + + avscan + + + avscan + + + avscan + + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters b/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters new file mode 100644 index 00000000..56c60b39 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters @@ -0,0 +1,46 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {6A96A213-242A-44D1-87C3-9EF1E57E7EC9} + + + h;hpp;hxx;hm;inl;inc;xsd + {794C3035-6983-4AF3-A981-5EEAA51276B3} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A7F78AF9-20F5-4D0E-83A9-8E19405F5C0C} + + + inf;inv;inx;mof;mc; + {0D596D83-E754-4E17-BD2E-884C8CCFCF7D} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/avscan/filter/communication.c b/filesys/miniFilter/avscan/filter/communication.c new file mode 100644 index 00000000..ab3b00dd --- /dev/null +++ b/filesys/miniFilter/avscan/filter/communication.c @@ -0,0 +1,1407 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + communication.c + +Abstract: + + Communication module implementation. + This module contains the routines that involves the communication + between kernel mode and user mode. + +Environment: + + Kernel mode + +--*/ + +#include "avscan.h" + +NTSTATUS +AvConnectNotifyCallback ( + _In_ PFLT_PORT ClientPort, + _In_ PVOID ServerPortCookie, + _In_reads_bytes_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Outptr_result_maybenull_ PVOID *ConnectionCookie + ); + +VOID +AvDisconnectNotifyCallback( + _In_opt_ PVOID ConnectionCookie + ); + +NTSTATUS +AvMessageNotifyCallback ( + _In_ PVOID ConnectionCookie, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ ULONG InputBufferSize, + _Out_writes_bytes_to_opt_(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferSize, + _Out_ PULONG ReturnOutputBufferLength + ); + +// +// Local routines +// + +NTSTATUS +AvGetScanCtxSynchronized ( + _In_ LONGLONG ScanId, + _Out_ PAV_SCAN_CONTEXT *ScanCtx + ); + +NTSTATUS +AvGetInstanceContextByVolume ( + _In_ PFLT_VOLUME volumeObject, + _Out_ PAV_INSTANCE_CONTEXT *InstanceContext + ); + +NTSTATUS +AvGetInstanceContextByFileHandle ( + _In_ HANDLE Handle, + _Out_ PAV_INSTANCE_CONTEXT *InstanceContext + ); + +NTSTATUS +AvGetStreamContextByHandle ( + _In_ HANDLE Handle, + _Out_ PAV_STREAM_CONTEXT *StreamContext + ); + +NTSTATUS +AvUpdateStreamContextWithScanResult ( + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _In_ PAV_SCAN_CONTEXT ScanContext, + _In_ AVSCAN_RESULT ScanResult + ); + +NTSTATUS +AvHandleCmdCreateSectionForDataScan ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _Out_ PHANDLE SectionHandle + ); + +NTSTATUS +AvHandleCmdCloseSectionForDataScan ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _In_ AVSCAN_RESULT ScanResult + ); + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, AvMessageNotifyCallback) + #pragma alloc_text(PAGE, AvConnectNotifyCallback) + #pragma alloc_text(PAGE, AvDisconnectNotifyCallback) + #pragma alloc_text(PAGE, AvPrepareServerPort) + + #pragma alloc_text(PAGE, AvGetInstanceContextByVolume) + #pragma alloc_text(PAGE, AvGetInstanceContextByFileHandle) + #pragma alloc_text(PAGE, AvGetStreamContextByHandle) + #pragma alloc_text(PAGE, AvUpdateStreamContextWithScanResult) + #pragma alloc_text(PAGE, AvFinalizeScanAndSection) + #pragma alloc_text(PAGE, AvFinalizeScanContext) + #pragma alloc_text(PAGE, AvFinalizeSectionContext) + #pragma alloc_text(PAGE, AvHandleCmdCreateSectionForDataScan) + #pragma alloc_text(PAGE, AvHandleCmdCloseSectionForDataScan) +#endif + +NTSTATUS +AvConnectNotifyCallback ( + _In_ PFLT_PORT ClientPort, + _In_ PVOID ServerPortCookie, + _In_reads_bytes_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Outptr_result_maybenull_ PVOID *ConnectionCookie + ) +/*++ + +Routine Description + + Communication connection callback routine. + This is called when user-mode connects to the server port. + +Arguments + + ClientPort - This is the client connection port that will be used to send messages from the filter + + ServerPortCookie - Unused + + ConnectionContext - The connection context passed from the user. This is to recognize which type + connection the user is trying to connect. + + SizeofContext - The size of the connection context. + + ConnectionCookie - Propagation of the connection context to disconnection callback. + +Return Value + + STATUS_SUCCESS - to accept the connection + STATUS_INSUFFICIENT_RESOURCES - if memory is not enough + STATUS_INVALID_PARAMETER_3 - Connection context is not valid. +--*/ +{ + PAV_CONNECTION_CONTEXT connectionCtx = (PAV_CONNECTION_CONTEXT) ConnectionContext; + PAVSCAN_CONNECTION_TYPE connectionCookie = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ServerPortCookie ); + UNREFERENCED_PARAMETER( SizeOfContext ); + + if (NULL == connectionCtx) { + + return STATUS_INVALID_PARAMETER_3; + } + + // + // ConnectionContext passed in may be deleted. We need to make a copy of it. + // + + connectionCookie = ExAllocatePoolWithTag( PagedPool, + sizeof(AVSCAN_CONNECTION_TYPE), + AV_CONNECTION_CTX_TAG ); + if (NULL == connectionCookie) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + *connectionCookie = connectionCtx->Type; + switch (connectionCtx->Type) { + case AvConnectForScan: + Globals.ScanClientPort = ClientPort; + *ConnectionCookie = connectionCookie; + break; + case AvConnectForAbort: + Globals.AbortClientPort = ClientPort; + *ConnectionCookie = connectionCookie; + break; + case AvConnectForQuery: + Globals.QueryClientPort = ClientPort; + *ConnectionCookie = connectionCookie; + break; + default: + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: AvConnectNotifyCallback: No such connection type. \n") ); + ExFreePoolWithTag( connectionCookie, + AV_CONNECTION_CTX_TAG ); + *ConnectionCookie = NULL; + return STATUS_INVALID_PARAMETER_3; + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV]: AvConnectNotifyCallback entered. type: %d \n", connectionCtx->Type) ); + + return STATUS_SUCCESS; +} + +VOID +AvDisconnectNotifyCallback( + _In_opt_ PVOID ConnectionCookie + ) +/*++ + +Routine Description + + Communication disconnection callback routine. + This is called when user-mode disconnects the server port. + +Arguments + + ConnectionCookie - The cookie set in AvConnectNotifyCallback(...). It is connection context. + +Return Value + + None +--*/ +{ + PAVSCAN_CONNECTION_TYPE connectionType = (PAVSCAN_CONNECTION_TYPE) ConnectionCookie; + + PAGED_CODE(); + + if (NULL == connectionType) { + + return; + } + // + // Close communication handle + // + switch (*connectionType) { + case AvConnectForScan: + FltCloseClientPort( Globals.Filter, &Globals.ScanClientPort ); + Globals.ScanClientPort = NULL; + break; + case AvConnectForAbort: + FltCloseClientPort( Globals.Filter, &Globals.AbortClientPort ); + Globals.AbortClientPort = NULL; + break; + case AvConnectForQuery: + FltCloseClientPort( Globals.Filter, &Globals.QueryClientPort ); + Globals.QueryClientPort = NULL; + break; + default: + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: AvDisconnectNotifyCallback: No such connection type. \n") ); + return; + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV]: AvDisconnectNotifyCallback entered. type: %d \n", *connectionType) ); + + ExFreePoolWithTag( connectionType, + AV_CONNECTION_CTX_TAG ); + +} + +NTSTATUS +AvGetScanCtxSynchronized ( + _In_ LONGLONG ScanId, + _Out_ PAV_SCAN_CONTEXT *ScanCtx + ) +/*++ + +Routine Description + + A helper function to retrieve the scan context from its scan context id. + It is synchronized by a lock. + +Arguments + + ScanId - The scan id to be found. + ScanCtx - The output scan context. NULL if not found + +Return Value + + STATUS_SUCCESS - if found. + Otherwise - Error, or if not found. + +--*/ +{ + PLIST_ENTRY link; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN found = FALSE; + PAV_SCAN_CONTEXT scanCtx = NULL; + + // + // We only 'read' the scan context when we traversing the list + // + + AvAcquireResourceShared( &Globals.ScanCtxListLock ); + + for (link = Globals.ScanCtxListHead.Flink; + link != &Globals.ScanCtxListHead; + link = link->Flink) { + + scanCtx = CONTAINING_RECORD( link, AV_SCAN_CONTEXT, List ); + + if (scanCtx->ScanId == ScanId) { + found = TRUE; + AvReferenceScanContext( scanCtx ); + break; + } + + } + + AvReleaseResource( &Globals.ScanCtxListLock ); + + if (found) { + + *ScanCtx = scanCtx; + return STATUS_SUCCESS; + } + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetScanCtxSynchronized: scan context not found. \n") ); + + *ScanCtx = NULL; + + if (NT_SUCCESS( status )){ + + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + +NTSTATUS +AvGetInstanceContextByVolume ( + _In_ PFLT_VOLUME VolumeObject, + _Out_ PAV_INSTANCE_CONTEXT *InstanceContext + ) +/*++ + +Routine Description + + A helper function to retrieve the instance context from its volume object. + + The caller is responsible for dereference InstanceContext via calling + FltReleaseContext(...) if success. + +Arguments + + VolumeObject - The volume object. + InstanceContext - The output instance context. NULL if not found + +Return Value + + STATUS_SUCCESS - if found. + Otherwise - Error, or not found. +--*/ +{ + ULONG i; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN found = FALSE; + PFLT_INSTANCE *instArray = NULL; + ULONG instCnt = 0; + PAV_INSTANCE_CONTEXT instCtx = NULL; + + PAGED_CODE(); + + status = AvEnumerateInstances ( &instArray, &instCnt ); + + if ( !NT_SUCCESS(status) ) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetInstanceContextByVolume: Failed to enumerate instances. \n") ); + return status; + } + + for (i = 0; i < instCnt; i++) { + + status = FltGetInstanceContext( instArray[i], &instCtx ); + + if ( !NT_SUCCESS(status) ) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetInstanceContextByVolume: Failed to get instance context. \n") ); + break; + } + + if (instCtx->Volume == VolumeObject) { + + // + // When found, we do not release the reference of instance context + // because the caller is responsible for releasing it. + // + + found = TRUE; + break; + } + + FltReleaseContext( instCtx ); + } + + AvFreeInstances( instArray, instCnt ); + instArray = NULL; + + if (found) { + + *InstanceContext = instCtx; + return STATUS_SUCCESS; + } + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetInstanceContextByVolume: instance context not found. \n") ); + + if ( NT_SUCCESS( status ) ){ + + status = STATUS_UNSUCCESSFUL; + } + + instCtx = NULL; + + return status; +} + +NTSTATUS +AvGetInstanceContextByFileHandle ( + _In_ HANDLE Handle, + _Out_ PAV_INSTANCE_CONTEXT *InstanceContext + ) +/*++ + +Routine Description + + A helper function to retrieve the instance context from file handle. + + The caller is responsible for dereference InstanceContext via calling + FltReleaseContext(...) if success. + +Arguments + + Handle - The file handle of interest. + InstanceContext - The output instance context. NULL if not found + +Return Value + + STATUS_SUCCESS - if found. + Otherwise - if not found; it will report the corresponding status. +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PFILE_OBJECT fileObject = NULL; + PFLT_VOLUME volumeObject = NULL; + + PAGED_CODE(); + + // + // Get file object by handle + // + + status = ObReferenceObjectByHandle ( + Handle, + 0, + *IoFileObjectType, + KernelMode, + (PVOID *)&fileObject, + NULL + ); + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetInstanceContextByFileHandle: Failed to get file object by handle. \n") ); + return status; + } + + try { + + status = FltGetVolumeFromFileObject( Globals.Filter, + fileObject, + &volumeObject ); + + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetInstanceContextByFileHandle: Failed to get volume by file object. \n") ); + leave; + } + + status = AvGetInstanceContextByVolume(volumeObject, InstanceContext); + + FltObjectDereference( volumeObject ); + + } finally { + + ObDereferenceObject( fileObject ); + } + + return status; +} + +NTSTATUS +AvGetStreamContextByHandle ( + _In_ HANDLE Handle, + _Out_ PAV_STREAM_CONTEXT *StreamContext + ) +/*++ + +Routine Description + + A helper function to retrieve the stream context from a file handle at message + callback routine. This function will increment the reference count of the + output stream context. + + The caller is responsible for dereference it via calling FltReleaseContext(...) + if success. + +Arguments + + Handle - The file handle of interest. + StreamContext - The output stream context. NULL if not found + +Return Value + + STATUS_SUCCESS - if found. + Otherwise - if not found; it will report the corresponding status. +--*/ +{ + NTSTATUS status; + PFILE_OBJECT fileObject = NULL; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + status = AvGetInstanceContextByFileHandle( Handle, &instanceContext); + + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: ***AvGetInstanceContextByFileHandle FAILED. \n") ); + return status; + } + try { + + status = ObReferenceObjectByHandle ( + Handle, + 0, + *IoFileObjectType, + KernelMode, + (PVOID *)&fileObject, + NULL + ); + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetStreamContextByHandle: Failed to get file object by handle. \n") ); + leave; + } + + status = FltGetStreamContext( instanceContext->Instance, + fileObject, + StreamContext ); + + ObDereferenceObject( fileObject ); + + } finally { + + FltReleaseContext( instanceContext ); + } + return status; +} + +NTSTATUS +AvHandleCmdCreateSectionForDataScan ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _Out_ PHANDLE SectionHandle + ) +/*++ + +Routine Description: + + This function handles CmdCreateSectionForDataScan message. + This function will create and return the section handle to the caller. + If any error occurs, it will trigger events to release the waiting threads. + + NOTE: this function does not check the buffer size etc. + It must be checked before passing into this function. + +Arguments: + + ScanContext - The scan context. + ScanThreadId - The thread ID of the thread doing the scan + SectionHandle - receives the section handle + +Return Value: + + Returns the status of processing the message. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_STREAM_CONTEXT streamContext = NULL; + PAV_SECTION_CONTEXT sectionContext = NULL; + HANDLE sectionHandle = NULL; + + PAGED_CODE(); + + status = FltGetStreamContext ( ScanContext->FilterInstance, + ScanContext->FileObject, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvHandleCmdCreateSectionForDataScan: failed to get stream context.\n") ); + + goto Cleanup; + } + + // + // It should be impossible for the stream state to change from + // uknown to clean since we kicked off this scan. + // + + ASSERT(IS_FILE_NEED_SCAN( streamContext )); + + status = AvCreateSectionContext( ScanContext->FilterInstance, + ScanContext->FileObject, + §ionContext); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvHandleCmdCreateSectionForDataScan: failed to create section context.\n") ); + + goto Cleanup; + } + + // + // Before we are going to create section object, if this flag is set (by the thread that requests for scan), + // it means that the thread is trying to cancel this scan, and thus we don't want the scan to proceed anymore. + // + if (ScanContext->IoWaitOnScanCompleteNotificationAborted) { + + status = STATUS_CANCELLED; + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvHandleCmdCreateSectionForDataScan: Before FltCreateSectionForDataScan, it found Io is trying to abort the wait.\n") ); + + goto Cleanup; + } + + sectionContext->ScanContext = ScanContext; + sectionContext->CancelableOnConflictingIo = (ScanContext->IOMajorFunctionAtScan == IRP_MJ_CLEANUP); + + // + // Note: the section conflict callback could be called before + // this routine returns. It is even possible that the context + // passed to the callback won't have the SectionHandle and + // SectionObject fields set yet. + // + + status = FltCreateSectionForDataScan( ScanContext->FilterInstance, + ScanContext->FileObject, + sectionContext, + SECTION_MAP_READ, + NULL, + NULL, + PAGE_READONLY, + SEC_COMMIT, + 0, + §ionContext->SectionHandle, + §ionContext->SectionObject, + NULL ); + + sectionHandle = sectionContext->SectionHandle; + + if (!NT_SUCCESS( status )) { + +#if DBG + NTSTATUS sta = STATUS_SUCCESS; + PFLT_VOLUME volumeObject = NULL; + ULONG length = 0; + UCHAR volPropBuffer[sizeof(FLT_VOLUME_PROPERTIES)+256]; //enough space for names + PFLT_VOLUME_PROPERTIES property = (PFLT_VOLUME_PROPERTIES)volPropBuffer; + + sta = FltGetVolumeFromFileObject( Globals.Filter, + ScanContext->FileObject, + &volumeObject ); + if (NT_SUCCESS( sta )) { + sta = FltGetVolumeProperties( volumeObject, + property, + sizeof(volPropBuffer), + &length ); + if (NT_SUCCESS( sta )) { + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] ############## %wZ, %wZ, %wZ\n", + property->FileSystemDriverName, + property->FileSystemDeviceName, + property->RealDeviceName) ); + } + FltObjectDereference( volumeObject ); + } + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvHandleCmdCreateSectionForDataScan: %I64x,%I64x failed to create section object. 0x%x\n", + streamContext->FileId.FileId64.UpperZeroes, + streamContext->FileId.FileId64.Value, + sta) ); +#endif // DBG + + goto Cleanup; + } + + // + // Before scanning, we set the file status as scanning. + // This is important when another thread is writing to this file while we are scanning + // this file. + // + + SET_FILE_SCANNING_EX( ScanContext->IsFileInTxWriter, streamContext ); + + // + // Only after the section object is successfully created, we put a section context pointer + // into the scan context. + // + + FltReferenceContext( sectionContext ); + ScanContext->SectionContext = sectionContext; + + *SectionHandle = sectionHandle; + +Cleanup: + + // + // The I/O request thread is waiting for this event. + // If any error occurs, we have to release the waiting thread. + // if status is a success code, the thread will get released when + // the user send message to close the section object. + // + + if (!NT_SUCCESS( status )) { + + KeSetEvent( &ScanContext->ScanCompleteNotification, 0, FALSE ); + } + + if (streamContext) { + + // + // On error signal the event to release any threads waiting to + // scan the same file. On success it will get released when the + // message is sent to close the section object. + // + + if (!NT_SUCCESS( status )) { + + SET_FILE_MODIFIED_EX( ScanContext->IsFileInTxWriter, streamContext ); + } + + FltReleaseContext( streamContext ); + streamContext = NULL; + } + + if (sectionContext) { + + FltReleaseContext( sectionContext ); + sectionContext = NULL; + } + + // + // After this routine assigned section context into scan context and created sectionHandle, + // we need to check if notification abort flag was set, if it was, we need to fail the section creation + // because at this point, the request for scan was abondaned anyway, we 'stop' the scan by + // returning STATUS_CANCELLED to the user. + // + if (NT_SUCCESS( status ) && + ScanContext->IoWaitOnScanCompleteNotificationAborted) { + + status = STATUS_CANCELLED; + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvHandleCmdCreateSectionForDataScan: After FltCreateSectionForDataScan, it found Io is trying to abort the wait.\n") ); + + // + // We explicitly call NtClose() instead of ZwClose() so that PreviousMode() will be User. + // This prevents accidental closing of a kernel handle and also will not bugcheck the + // system if the handle value is no longer valid + // + NtClose( sectionHandle ); + + // + // This user mode handle is supposed to be closed in the user mode program. + // We close in the context of the same process context. + // + AvFinalizeScanAndSection( ScanContext ); + } + + return status; +} + + +NTSTATUS +AvUpdateStreamContextWithScanResult ( + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _In_ PAV_SCAN_CONTEXT ScanContext, + _In_ AVSCAN_RESULT ScanResult + ) +/*++ + +Routine Description: + + This function updates StreamContex according to ScanResult. + e.g. Set the stream as modified, infected, etc. + +Arguments: + + StreamContext - The stream context to be updated. + + ScanContext - The scan context. + + ScanResult - The scan result. Please see the definition of AVSCAN_RESULT. + +Return Value: + + Returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + switch( ScanResult ) { + + case AvScanResultUndetermined: + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("***[AV] AvUpdateScanResult: the caller did not specify the scan result.\n") ); + // + // If for some reason, the scan result returns undetermined, we have to + // set the file state back to AvFileModifed. + // + + SET_FILE_MODIFIED_EX( ScanContext->IsFileInTxWriter, StreamContext); + break; + case AvScanResultInfected: + // + // If after the scan and before setting this file as clean, the file gets modified, + // then we have to leave it as modified. + // + + if (ScanContext->IsFileInTxWriter) { + + InterlockedCompareExchange( &StreamContext->TxState, AvFileInfected, AvFileScanning ); + + } else { + + InterlockedCompareExchange( &StreamContext->State, AvFileInfected, AvFileScanning ); + } + break; + case AvScanResultClean: + + // + // If after the scan and before setting this file as clean, the file gets modified, + // then we have to leave it as modified. + // + + if (ScanContext->IsFileInTxWriter) { + + InterlockedCompareExchange( &StreamContext->TxState, AvFileNotInfected, AvFileScanning ); + + } else { + + InterlockedCompareExchange( &StreamContext->State, AvFileNotInfected, AvFileScanning ); + } + + break; + default: + FLT_ASSERTMSG( "No such scan result.\n", FALSE); + break; + } + + return status; +} + +NTSTATUS +AvFinalizeScanAndSection ( + _Inout_ PAV_SCAN_CONTEXT ScanContext + ) +/*++ + +Routine Description: + + This function is a wrapper function to finalize scan context and section context. + Normally, you should call this function if you don't need to use section context before + closing it. + +Arguments: + + ScanContext - The scan context. + +Return Value: + + Returns the status code from FltCloseSectionForDataScan. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_SECTION_CONTEXT sectionContext = NULL; + + PAGED_CODE(); + + AvFinalizeScanContext( ScanContext, §ionContext ); + + // + // This thread won the race, and is responsible for finalizing the section context + // + if (sectionContext != NULL) { + + status = AvFinalizeSectionContext( sectionContext ); + } + return status; +} + +VOID +AvFinalizeScanContext ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _Outptr_result_maybenull_ PAV_SECTION_CONTEXT *SectionContext + ) +/*++ + +Routine Description: + + This function interlocked-exchange the section context inside the scan context and + release the waiting I/O request thread. + + The caller is responsible for releasing the reference count of SectionContext + when it successfully exchanges a non-NULL section context. + +Arguments: + + ScanContext - The scan context. + + SectionContext - Receives the sectioncontext address indicating the caller is + responsible for tearing down the sectioncontext. + Receives NULL if the context is already being torn down by another thread. + +Return Value: + + None. + +--*/ +{ + PAV_SECTION_CONTEXT oldSectionCtx = NULL; + + PAGED_CODE(); + + *SectionContext = NULL; + + // + // Synchronization between AvInstanceTeardownStart(...) or timeout + // processing in the IO thread. + // + + oldSectionCtx = InterlockedExchangePointer( &ScanContext->SectionContext, NULL ); + + // + // If sectionContext is NULL, it means that another thread has + // already begun teardown of the section. + // + + if (oldSectionCtx) { + + // + // The caller is responsible for releasing the reference count when assigned in ScanContext. + // + *SectionContext = oldSectionCtx; + } + + // + // The I/O request thread is waiting for this event. + // + + KeSetEvent( &ScanContext->ScanCompleteNotification, 0, FALSE ); +} + +NTSTATUS +AvFinalizeSectionContext ( + _Inout_ PAV_SECTION_CONTEXT SectionContext + ) +/*++ + +Routine Description: + + This function is a wrapper function to finalize section context. + It closes the section context/object and release its reference. + +Arguments: + + SectionContext - The section context. + +Return Value: + + Returns the status code from FltCloseSectionForDataScan. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + status = AvCloseSectionForDataScan( SectionContext ); + + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("***[AV]: AvFinalizeSectionContext: Close section failed.\n") ); + } + FltReleaseContext( SectionContext ); + return status; +} + +NTSTATUS +AvHandleCmdCloseSectionForDataScan ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _In_ AVSCAN_RESULT ScanResult + ) +/*++ + +Routine Description: + + This function handles AvCmdCloseSectionForDataScan message. + This function will + + 1) close the section object + 2) Set the file clean or infected. + 3) trigger events to release the waiting threads. + +Arguments: + + ScanContext - The scan context. + + ScanResult - The scan result. Please see the definition of AVSCAN_RESULT. + +Return Value: + + Returns the status of processing the message. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_STREAM_CONTEXT streamContext = NULL; + + PAGED_CODE(); + + status = FltGetStreamContext ( ScanContext->FilterInstance, + ScanContext->FileObject, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("***[AV] AvHandleCmdCloseSectionForDataScan: failed to get stream context.\n") ); + + goto Cleanup; + } + + // + // Update stream context will succeed. + // + AvUpdateStreamContextWithScanResult(streamContext, ScanContext, ScanResult); + +Cleanup: + + status = AvFinalizeScanAndSection( ScanContext ); + + // + // Either the above operations are successful, or any error occur, + // we have to release the stream context. + // + + if ( streamContext ) { + + FltReleaseContext( streamContext ); + } + + return status; + +} + +NTSTATUS +AvMessageNotifyCallback ( + _In_ PVOID ConnectionCookie, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ ULONG InputBufferSize, + _Out_writes_bytes_to_opt_(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferSize, + _Out_ PULONG ReturnOutputBufferLength + ) +/*++ + +Routine Description: + + This routine is called whenever the user program sends message to + filter via FilterSendMessage(...). + + The user space scanner sends message to + + 1) Create the section for data scan + 2) Close the section for data scan + 3) Set a certain file to be infected + 4) Query the file state of a file + +Arguments: + + InputBuffer - A buffer containing input data, can be NULL if there + is no input data. + + InputBufferSize - The size in bytes of the InputBuffer. + + OutputBuffer - A buffer provided by the application that originated + the communication in which to store data to be returned to the + application. + + OutputBufferSize - The size in bytes of the OutputBuffer. + + ReturnOutputBufferSize - The size in bytes of meaningful data + returned in the OutputBuffer. + +Return Value: + + Returns the status of processing the message. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + AVSCAN_COMMAND command; + HANDLE hFile = NULL; + LONGLONG scanId = 0; + PAV_SCAN_CONTEXT scanContext = NULL; + AVSCAN_RESULT scanResult = AvScanResultUndetermined; + PAV_STREAM_CONTEXT streamContext; + HANDLE sectionHandle; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ConnectionCookie ); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV]: AvMessageNotifyCallback entered. \n") ); + + + if ((InputBuffer == NULL) || + (InputBufferSize < (FIELD_OFFSET(COMMAND_MESSAGE, Command) + + sizeof(AVSCAN_COMMAND)))) { + + return STATUS_INVALID_PARAMETER; + } + + try { + + // + // Probe and capture input message: the message is raw user mode + // buffer, so need to protect with exception handler + // + + command = ((PCOMMAND_MESSAGE) InputBuffer)->Command; + scanId = ((PCOMMAND_MESSAGE) InputBuffer)->ScanId; + + } except (AvExceptionFilter( GetExceptionInformation(), TRUE )) { + + return GetExceptionCode(); + } + + // + // Only + // AvCmdCreateSectionForDataScan + // AvCmdCloseSectionForDataScan + // require the check of scanCtxId + // + // We also check the output buffer size, and its alignment here. + // + + switch (command) { + + case AvCmdCreateSectionForDataScan: + + if ((OutputBufferSize < sizeof (HANDLE)) || + (OutputBuffer == NULL)) { + + return STATUS_INVALID_PARAMETER; + } + + if (!IS_ALIGNED(OutputBuffer,sizeof(HANDLE))) { + + return STATUS_DATATYPE_MISALIGNMENT; + } + + status = AvGetScanCtxSynchronized( scanId, + &scanContext ); + + if (!NT_SUCCESS( status )) { + + return STATUS_NOT_FOUND; + } + + status = AvHandleCmdCreateSectionForDataScan( scanContext, + §ionHandle ); + + if (NT_SUCCESS(status)) { + // + // We succesfully created a section object/handle. + // Try to set the handle in the OutputBuffer + // + try { + + (*(PHANDLE)OutputBuffer) = sectionHandle; + *ReturnOutputBufferLength = sizeof(HANDLE); + + } except (AvExceptionFilter( GetExceptionInformation(), TRUE )) { + // + // We cannot depend on user service program to close this handle for us. + // We explicitly call NtClose() instead of ZwClose() so that PreviousMode() will be User. + // This prevents accidental closing of a kernel handle and also will not bugcheck the + // system if the handle value is no longer valid + // + NtClose( sectionHandle ); + + // + // Close section and release the waiting I/O request thread + // We treat invalid user buffer as an exception and remove + // section object inside scan context. You can also design a protocol + // that have user program to re-try for section creation failure. + // + AvFinalizeScanAndSection( scanContext ); + status = GetExceptionCode(); + } + } + + // + // AvGetScanCtxSynchronized incremented the ref count of scan context + // + AvReleaseScanContext( scanContext ); + + break; + + case AvCmdCloseSectionForDataScan: + + try { + + scanResult = ((PCOMMAND_MESSAGE) InputBuffer)->ScanResult; + + if (scanResult == AvScanResultInfected) { + AV_DBG_PRINT( AVDBG_TRACE_OPERATION_STATUS, + ("[AV]: *******AvCmdCreateSectionForDataScan FAILED. \n") ); + } + + } except (AvExceptionFilter( GetExceptionInformation(), TRUE )) { + + return GetExceptionCode(); + } + + status = AvGetScanCtxSynchronized( scanId, + &scanContext ); + + if (!NT_SUCCESS( status )) { + + return STATUS_NOT_FOUND; + } + + status = AvHandleCmdCloseSectionForDataScan( scanContext, scanResult ); + + if (NT_SUCCESS(status)) { + *ReturnOutputBufferLength = 0; + } + + // + // AvGetScanCtxSynchronized incremented the ref count of scan context + // + AvReleaseScanContext( scanContext ); + + break; + + case AvIsFileModified: + + try { + + hFile = ((PCOMMAND_MESSAGE) InputBuffer)->FileHandle; + + } except (AvExceptionFilter( GetExceptionInformation(), TRUE )) { + + return GetExceptionCode(); + } + + if ((OutputBufferSize < sizeof (BOOLEAN)) || + (OutputBuffer == NULL)) { + + return STATUS_INVALID_PARAMETER; + } + + if (!IS_ALIGNED(OutputBuffer,sizeof(BOOLEAN))) { + + return STATUS_DATATYPE_MISALIGNMENT; + } + + // + // Get file object by file handle + // Get PFLT_VOLUME by file object + // Get instance context by PFLT_VOLUME + // Get filter instance in instance context + // Get stream context by file object and instance context + // Return if the file was previously modified + // + + status = AvGetStreamContextByHandle( hFile, &streamContext ); + + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: **************************AvGetStreamContextByHandle FAILED. \n") ); + break; + } + + try { + + (*(PBOOLEAN) OutputBuffer) = (BOOLEAN) IS_FILE_MODIFIED( streamContext ); + *ReturnOutputBufferLength = (ULONG) sizeof( BOOLEAN ); + + } except (AvExceptionFilter( GetExceptionInformation(), TRUE )) { + + status = GetExceptionCode(); + } + + FltReleaseContext( streamContext ); + + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + return status; + +} + +NTSTATUS +AvPrepareServerPort( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ AVSCAN_CONNECTION_TYPE ConnectionType + ) +/*++ + +Routine Description: + + A wrapper function that prepare the communicate port. + +Arguments: + + SecurityDescriptor - Specifies a security descriptor to InitializeObjectAttributes(...). + + ConnectionType - The type of connection: AvConnectForScan, AvConnectForAbort, AvConnectForQuery + +Return Value: + + Returns the status of the prepartion. + +--*/ +{ + NTSTATUS status; + OBJECT_ATTRIBUTES oa; + UNICODE_STRING uniString; + LONG maxConnections = 1; + PCWSTR portName = NULL; + PFLT_PORT *pServerPort = NULL; + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV]: AvPrepareServerPort entered. \n") ); + + switch( ConnectionType ) { + case AvConnectForScan: + portName = AV_SCAN_PORT_NAME; + pServerPort = &Globals.ScanServerPort; + break; + case AvConnectForAbort: + portName = AV_ABORT_PORT_NAME; + pServerPort = &Globals.AbortServerPort; + break; + case AvConnectForQuery: + portName = AV_QUERY_PORT_NAME; + pServerPort = &Globals.QueryServerPort; + break; + default: + FLT_ASSERTMSG( "No such connection type.\n", FALSE); + break; + } + + RtlInitUnicodeString( &uniString, portName ); + + InitializeObjectAttributes( &oa, + &uniString, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + NULL, + SecurityDescriptor ); + + status = FltCreateCommunicationPort( Globals.Filter, + pServerPort, // this is the output to server port. + &oa, + NULL, + AvConnectNotifyCallback, + AvDisconnectNotifyCallback, + AvMessageNotifyCallback, + maxConnections ); + + return status; +} + + diff --git a/filesys/miniFilter/avscan/filter/context.c b/filesys/miniFilter/avscan/filter/context.c new file mode 100644 index 00000000..8977014d --- /dev/null +++ b/filesys/miniFilter/avscan/filter/context.c @@ -0,0 +1,943 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + context.c + +Abstract: + + Filter Context-related module implementation. + +Environment: + + Kernel mode + +--*/ + +#include "avscan.h" + +// +// Local function prototypes. +// + +VOID +AvStreamContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +AvTransactionContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +AvSectionContextCleanup( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +AvInstanceContextCleanup( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, AvCreateStreamContext) +#pragma alloc_text(PAGE, AvCreateStreamHandleContext) +#pragma alloc_text(PAGE, AvFindOrCreateTransactionContext) +#pragma alloc_text(PAGE, AvCreateSectionContext) +#pragma alloc_text(PAGE, AvStreamContextCleanup) +#pragma alloc_text(PAGE, AvTransactionContextCleanup) +#pragma alloc_text(PAGE, AvSectionContextCleanup) +#pragma alloc_text(PAGE, AvInstanceContextCleanup) +#pragma alloc_text(PAGE, AvAllocateScanContext) +#pragma alloc_text(PAGE, AvReferenceScanContext) +#pragma alloc_text(PAGE, AvReleaseScanContext) +#endif + + +// +// Context registration structure +// + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_STREAM_CONTEXT, + 0, + AvStreamContextCleanup, + AV_STREAM_CONTEXT_SIZE, + AV_STREAM_CONTEXT_TAG }, + + { FLT_STREAMHANDLE_CONTEXT, + 0, + NULL, + AV_STREAMHANDLE_CONTEXT_SIZE, + AV_STREAMHANDLE_CONTEXT_TAG }, + + { FLT_TRANSACTION_CONTEXT, + 0, + AvTransactionContextCleanup, + AV_TRANSACTION_CONTEXT_SIZE, + AV_TRANSACTION_CONTEXT_TAG }, + + { FLT_SECTION_CONTEXT, + 0, + AvSectionContextCleanup, + AV_SECTION_CONTEXT_SIZE, + AV_SECTION_CONTEXT_TAG }, + + { FLT_INSTANCE_CONTEXT, + 0, + AvInstanceContextCleanup, + AV_INSTANCE_CONTEXT_SIZE, + AV_INSTANCE_CONTEXT_TAG }, + + { FLT_CONTEXT_END } +}; + + +VOID +AvStreamContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This function is called by the filter manager before freeing any of the minifilter + driver's contexts of that type. + + In this routine, the driver has to perform any needed cleanup, such as freeing + additional memory that the minifilter driver allocated inside the context structure + +Arguments: + + Context - Pointer to the minifilter driver's portion of the context. + ContextType - Supposed to be FLT_STREAM_CONTEXT. + +Return Value: + + None + +--*/ +{ + PAV_STREAM_CONTEXT streamContext = (PAV_STREAM_CONTEXT) Context; + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + FLT_ASSERTMSG( "[AV]: Stream context is not supposed to be in the transaction context list at cleanup.!\n", + NULL == streamContext->TxContext ); + + AvFreeKevent( streamContext->ScanSynchronizationEvent ); +} + +VOID +AvTransactionContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This function is called by the filter manager before freeing any of the minifilter + driver's contexts of that type. + + In this routine, the driver has to perform any needed cleanup, such as freeing + additional memory that the minifilter driver allocated inside the context structure + + We delete the stream context list in transaction context here. + +Arguments: + + Context - Pointer to the minifilter driver's portion of the context. + ContextType - Supposed to be FLT_TRANSACTION_CONTEXT. + +Return Value: + + None + +--*/ +{ + PAV_TRANSACTION_CONTEXT transactionContext = (PAV_TRANSACTION_CONTEXT) Context; + + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvTransactionContextCleanup context cleanup entered.\n") ); + + ExDeleteResourceLite( transactionContext->Resource ); + AvFreeResource( transactionContext->Resource ); + transactionContext->Resource = NULL; + ObDereferenceObject( transactionContext->Transaction ); + transactionContext->Transaction = NULL; +} + +VOID +AvSectionContextCleanup( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This function is called by the filter manager before freeing any of the minifilter + driver's contexts of that type. + + In this routine, the driver has to perform any needed cleanup, such as freeing + additional memory that the minifilter driver allocated inside the context structure + +Arguments: + + Context - Pointer to the minifilter driver's portion of the context. + ContextType - Supposed to be FLT_SECTION_CONTEXT (win8 or later). + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Context ); + UNREFERENCED_PARAMETER( ContextType ); + + FLT_ASSERTMSG( "[AV] AvSectionContextCleanup: Section handle should be NULL at cleanup.\n", + ((PAV_SECTION_CONTEXT) Context)->SectionHandle == NULL ); + FLT_ASSERTMSG( "[AV] AvSectionContextCleanup: Section object should be NULL at cleanup.\n", + ((PAV_SECTION_CONTEXT) Context)->SectionObject == NULL ); + +} + +VOID +AvInstanceContextCleanup( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This function is called by the filter manager before freeing any of the minifilter + driver's contexts of that type. + + In this routine, the driver has to perform any needed cleanup, such as freeing + additional memory that the minifilter driver allocated inside the context structure. + + We delete the cache table if the file system supports one. + +Arguments: + + Context - Pointer to the minifilter driver's portion of the context. + ContextType - Supposed to be FLT_INSTANCE_CONTEXT (win8 or later). + +Return Value: + + None + +--*/ +{ + + PAV_INSTANCE_CONTEXT instanceContext = (PAV_INSTANCE_CONTEXT) Context; + + UNREFERENCED_PARAMETER( Context ); + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ( "[Av]: AvInstanceContextCleanup context cleanup entered\n") ); + + if (FS_SUPPORTS_FILE_STATE_CACHE( instanceContext->VolumeFSType )) { + + FLT_ASSERTMSG( "[AV] AvInstanceContextCleanup: The generic table should be empty at cleanup.\n", + RtlIsGenericTableEmpty( &instanceContext->FileStateCacheTable ) ); + ExDeleteResourceLite( &instanceContext->Resource ); + } +} + +NTSTATUS +AvCreateStreamHandleContext ( + _In_ PFLT_FILTER Filter, + _Outptr_ PAV_STREAMHANDLE_CONTEXT *StreamHandleContext + ) +/*++ + +Routine Description: + + This routine creates a new streamhandle context + +Arguments: + + StreamHandleContext - Returns the streamhandle context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PAV_STREAMHANDLE_CONTEXT streamHandleContext; + + PAGED_CODE(); + + // + // Allocate a streamhandle context + // + + status = FltAllocateContext( Filter, + FLT_STREAMHANDLE_CONTEXT, + AV_STREAMHANDLE_CONTEXT_SIZE, + PagedPool, + &streamHandleContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to allocate stream handle context with status 0x%x \n", + status) ); + return status; + } + + // + // Initialize the newly created context + // + + RtlZeroMemory(streamHandleContext, AV_STREAMHANDLE_CONTEXT_SIZE); + *StreamHandleContext = streamHandleContext; + + return STATUS_SUCCESS; +} + +NTSTATUS +AvCreateStreamContext ( + _In_ PFLT_FILTER Filter, + _Outptr_ PAV_STREAM_CONTEXT *StreamContext + ) +/*++ + +Routine Description: + + This routine creates a new stream context + +Arguments: + + StreamContext - Returns the stream context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PKEVENT event = NULL; + PAV_STREAM_CONTEXT streamContext; + + PAGED_CODE(); + + // + // Allocate the kernel event object + // + + event = AvAllocateKevent(); + + if (NULL == event) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Allocate a stream context + // + + status = FltAllocateContext( Filter, + FLT_STREAM_CONTEXT, + AV_STREAM_CONTEXT_SIZE, + PagedPool, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to allocate stream context with status 0x%x \n", + status) ); + AvFreeKevent( event ); + return status; + } + + // + // Initialize the newly created context + // + + RtlZeroMemory(streamContext, AV_STREAM_CONTEXT_SIZE); + streamContext->ScanSynchronizationEvent = event; + KeInitializeEvent( streamContext->ScanSynchronizationEvent, SynchronizationEvent, TRUE ); + SET_FILE_MODIFIED( streamContext ); + SET_FILE_TX_MODIFIED( streamContext ); + *StreamContext = streamContext; + + return STATUS_SUCCESS; +} + +NTSTATUS +AvFindOrCreateTransactionContext( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_ PAV_TRANSACTION_CONTEXT *TransactionContext + ) +/*++ + +Routine Description + + This routine finds the transaction context, if not found, it will + try to create a new one. The caller is responsible for calling + FltReleaseContext to decrement its reference count. + +Arguments + + FltObjects - Contains parameters required to enlist in a transaction. + + TransactionContext - Returns the transaction context + +Return value + + Returns STATUS_SUCCESS if we were able to successfully find/create + a transaction context. Returns an appropriate error code on a failure. + +--*/ +{ + NTSTATUS status; + PAV_TRANSACTION_CONTEXT transactionContext = NULL; + PAV_TRANSACTION_CONTEXT oldTransactionContext = NULL; + PERESOURCE pResource = NULL; + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvFindOrCreateTransactionContext entered. \n") ); + + status = FltGetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + &transactionContext ); + + if (NT_SUCCESS( status )) { + + *TransactionContext = transactionContext; + return STATUS_SUCCESS; + } + + if (status != STATUS_NOT_FOUND) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: Failed to get transaction context with status 0x%x \n", + status) ); + return status; + } + + // + // Allocate the resource + // + + pResource = AvAllocateResource(); + + if ( NULL == pResource ) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Allocate a transaction context. + // + + status = FltAllocateContext( Globals.Filter, + FLT_TRANSACTION_CONTEXT, + AV_TRANSACTION_CONTEXT_SIZE, + PagedPool, + &transactionContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: Failed to allocate transaction context with status 0x%x \n", + status) ); + AvFreeResource( pResource ); + return status; + } + + FLT_ASSERTMSG( "[AV]: Transaction object pointer is not supposed to be NULL !\n", FltObjects->Transaction != NULL); + + // + // Initialization of transaction context. + // The reason we allocate eResource seperately is because + // eResource has to be allocated in the non-paged pool. + // + + RtlZeroMemory(transactionContext, AV_TRANSACTION_CONTEXT_SIZE); + transactionContext->Resource = pResource; + ObReferenceObject( FltObjects->Transaction ); + transactionContext->Transaction = FltObjects->Transaction; + InitializeListHead( &transactionContext->ScListHead ); + ExInitializeResourceLite( transactionContext->Resource ); + + status = FltSetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + transactionContext, + &oldTransactionContext ); + + if (NT_SUCCESS( status )) { + + *TransactionContext = transactionContext; + return STATUS_SUCCESS; + } + + FltReleaseContext( transactionContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to set transaction context with status 0x%x \n", + status) ); + + return status; + } + + if (NULL == oldTransactionContext) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to set transaction context oldTransactionContext is NULL \n") ); + + return status; + } + + *TransactionContext = oldTransactionContext; + + + return STATUS_SUCCESS; +} + +NTSTATUS +AvCreateSectionContext ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Outptr_ PAV_SECTION_CONTEXT *SectionContext + ) +/*++ + +Routine Description: + + This routine creates a new section context. + +Arguments: + + Instance - Opaque instance pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + SectionContext - Returns the section context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + LONGLONG fileSize; + PAV_SECTION_CONTEXT sectionContext = NULL; + + PAGED_CODE(); + + status = FltAllocateContext( Globals.Filter, + FLT_SECTION_CONTEXT, + AV_SECTION_CONTEXT_SIZE, + PagedPool, + §ionContext ); + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to allocate section context.\n, 0x%08x\n", + status) ); + return status; + } + + RtlZeroMemory(sectionContext, AV_SECTION_CONTEXT_SIZE); + + status = AvGetFileSize( Instance, + FileObject, + &fileSize ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to get file size with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + FileObject, + Instance )); + } else { + + sectionContext->FileSize = fileSize; + } + + *SectionContext = sectionContext; + + return STATUS_SUCCESS; +} + +NTSTATUS +AvEnumerateInstances( + _Outptr_result_buffer_(*NumberInstances) PFLT_INSTANCE **InstanceArray, + _Out_ PULONG NumberInstances + ) +/*++ + +Routine Description: + + This routine returns all the instances available of this filter + The caller is responsible for calling AvFreeInstances(...) to release the instance references. + +Arguments: + + InstanceArray - This function will allocate the memory of the arrary containing the instances. + + NumberInstances - The number of instances in InstanceArray. + +Return Value: + + Status + +--*/ +{ + PFLT_INSTANCE *instArray = NULL; + NTSTATUS status = STATUS_SUCCESS; + ULONG i = 0; + ULONG instCnt = 0; + ULONG newCount = 0; + + + // + // Get a count of how many instances there are + // + + status = FltEnumerateInstances( NULL, + Globals.Filter, + NULL, + 0, + &instCnt ); + + if (!NT_SUCCESS(status) && + (status != STATUS_BUFFER_TOO_SMALL)) { + + goto Cleanup; + } + + // + // Get handles for all instances. This will loop in case too many + // filters load between the time we got the count and the time + // we actually get the list. + // + + for (;;) { + + // + // Free old memory if we have some + // + + if (instArray != NULL) { + + ExFreePoolWithTag( instArray, AV_INSTANCES_ARRAY_TAG); + instArray = NULL; + } + + // + // Allocate memory for list, add a couple of entries in case + // a filter loads while we are doing this + // + + instCnt += 2; + + instArray = ExAllocatePoolWithTag( PagedPool, + (instCnt * sizeof(PFLT_INSTANCE)), + AV_INSTANCES_ARRAY_TAG ); + + if (instArray == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + // + // This time get list of filters (and a new count) + // + + status = FltEnumerateInstances( NULL, + Globals.Filter, + instArray, + instCnt, + &newCount ); + + // + // exit loop if we succeeded + // + + if (NT_SUCCESS(status)) { + + instCnt = newCount; + break; + } + + // + // If it was an unexpected error, quit processing, else allocate + // more memory and try again + // + + if (status != STATUS_BUFFER_TOO_SMALL) { + + goto Cleanup; + } + + // + // The buffer was too small, try again + // + + FLT_ASSERT(newCount > instCnt); + instCnt = newCount; + } + + *InstanceArray = instArray; + *NumberInstances = instCnt; + +Cleanup: + + if ( !NT_SUCCESS(status) ) { + + if (instArray) { + + // + // Release all the objects in the array + // + + for (i = 0; i < instCnt; i++) { + + FltObjectDereference( instArray[i] ); + instArray[i] = NULL; + } + + ExFreePoolWithTag( instArray, AV_INSTANCES_ARRAY_TAG ); + instArray = NULL; + } + } + + + return status; + +} + +VOID +AvFreeInstances ( + _In_reads_(InstanceCount) PFLT_INSTANCE *InstanceArray, + _In_ ULONG InstanceCount + ) +/*++ + +Routine Description: + + This routine frees the reference count and memory of instance array obtained from AvEnumerateInstances(...). + +Arguments: + + InstanceArray - The instance arrary to be freed. + + NumberInstances - The number of instances in InstanceArray. + +Return Value: + + None. + +--*/ +{ + ULONG i = 0; + + // + // Release all the objects in the array + // + + for (i = 0; i < InstanceCount; i++) { + + FltObjectDereference( InstanceArray[i] ); + InstanceArray[i] = NULL; + } + + ExFreePoolWithTag( InstanceArray, AV_INSTANCES_ARRAY_TAG ); +} + +NTSTATUS +AvAllocateScanContext( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Outptr_ PAV_SCAN_CONTEXT *ScanContext + ) +/*++ + +Routine Description: + + The routine allocates the scan context + +Arguments: + + Instance - Opaque instance pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + ScanContext - The output scan context. + +Return Value: + + STATUS_INSUFFICIENT_RESOURCES if allocation failed. + STATUS_SUCCESS if successfully allocated. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_SCAN_CONTEXT scanCtx = NULL; + + PAGED_CODE(); + + ASSERT(Instance != NULL); + ASSERT(FileObject != NULL); + + scanCtx = ExAllocatePoolWithTag( NonPagedPoolNx, + sizeof(AV_SCAN_CONTEXT), + AV_SCAN_CTX_TAG ); + + if (NULL == scanCtx) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + scanCtx->RefCount = 1; + scanCtx->IoWaitOnScanCompleteNotificationAborted = FALSE; + + // + // Keeps a reference in scan contex. + // We also handle the case that the instance is being torn down. + // + status = FltObjectReference( Instance ); + if (!NT_SUCCESS(status)) { + + ExFreePoolWithTag( scanCtx, AV_SCAN_CTX_TAG ); + return status; + } + scanCtx->FilterInstance = Instance; + + // + // Keeps a reference in scan context + // + ObReferenceObject( FileObject ); + scanCtx->FileObject = FileObject; + + *ScanContext = scanCtx; + return STATUS_SUCCESS; +} + +NTSTATUS +AvReferenceScanContext( + _In_ PAV_SCAN_CONTEXT ScanContext + ) +/*++ + +Routine Description: + + The routine increments the reference count of scan context to prevent it from deletion. + +Arguments: + + ScanContext - The scan context to be added reference. + +Return Value: + + STATUS_INVALID_PARAMETER if ScanContext is NULL. + STATUS_SUCCESS if successfully incremented. + +--*/ +{ + PAGED_CODE(); + + if (ScanContext == NULL) { + + return STATUS_INVALID_PARAMETER; + } + + ASSERT(ScanContext->RefCount != 0); + ASSERT(ScanContext->FilterInstance != NULL); + ASSERT(ScanContext->FileObject != NULL); + + InterlockedIncrement(&ScanContext->RefCount); + + return STATUS_SUCCESS; +} + +NTSTATUS +AvReleaseScanContext( + _In_ PAV_SCAN_CONTEXT ScanContext + ) +/*++ + +Routine Description: + + The routine decrements the reference count of scan context. + Release it if reference count goes to zero. + +Arguments: + + ScanContext - The scan context to be released. + +Return Value: + + STATUS_INVALID_PARAMETER if ScanContext is NULL. + STATUS_SUCCESS if successfully decremented. + +--*/ +{ + ULONG newRefCount = 0; + + PAGED_CODE(); + + if (ScanContext == NULL) { + + return STATUS_INVALID_PARAMETER; + } + + ASSERT(ScanContext->FilterInstance != NULL); + ASSERT(ScanContext->FileObject != NULL); + + // + // Assume the usage of AvReferenceScanContext and AvReleaseScanContext are not raced, + // This simple version would suffice. + // + + newRefCount = InterlockedDecrement(&ScanContext->RefCount); + if (newRefCount == 0) { + + // + // Before freeing scan context, we need to release the file object and instance. + // + FltObjectDereference( ScanContext->FilterInstance ); + ObDereferenceObject( ScanContext->FileObject ); + ExFreePoolWithTag( ScanContext, AV_SCAN_CTX_TAG ); + } + return STATUS_SUCCESS; +} + + diff --git a/filesys/miniFilter/avscan/filter/context.h b/filesys/miniFilter/avscan/filter/context.h new file mode 100644 index 00000000..f9c0eeec --- /dev/null +++ b/filesys/miniFilter/avscan/filter/context.h @@ -0,0 +1,357 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + context.h + +Abstract: + + Header file which contains context-related data + structures, type definitions, constants, + global variables and function prototypes. + +Environment: + + Kernel mode + +--*/ + +#ifndef __CONTEXT_H__ +#define __CONTEXT_H__ + +// +// The file infected state. +// + +typedef enum _AV_FILE_INFECTED_STATE { + + AvFileUnknown, + AvFileInfected, + AvFileNotInfected, // clean. + AvFileModified, + AvFileScanning + +} AV_FILE_INFECTED_STATE; + +#define AV_STREAMHANDLE_CONTEXT_TAG 'hSvA' +#define AV_STREAM_CONTEXT_TAG 'cSvA' +#define AV_TRANSACTION_CONTEXT_TAG 'cTvA' +#define AV_SECTION_CONTEXT_TAG 'eSvA' +#define AV_INSTANCE_CONTEXT_TAG 'cIvA' +#define AV_INSTANCES_ARRAY_TAG 'aIvA' +#define AV_CONNECTION_CTX_TAG 'cCvA' +#define AV_SCAN_CTX_TAG 'cMvA' + +// +// Defines the transaction context structure +// +#define AV_TXCTX_ENLISTED 0x01 +#define AV_TXCTX_LISTDRAINED 0x02 + +typedef struct _AV_TRANSACTION_CONTEXT { + + // + // Transaction object pointer + // + + PKTRANSACTION Transaction; + + // + // List head for stream context list. + // + + LIST_ENTRY ScListHead; + + // + // Lock used to protect this context. + // + + PERESOURCE Resource; + + // + // A flag that tracks: + // AV_TXCTX_ENLISTED: if it has been enlisted in transaction + // AV_TXCTX_LISTDRAINED: list is drained. + // + + ULONG Flags; + +} AV_TRANSACTION_CONTEXT, *PAV_TRANSACTION_CONTEXT; + +#define AV_TRANSACTION_CONTEXT_SIZE sizeof( AV_TRANSACTION_CONTEXT ) + + +#define IS_FILE_MODIFIED( _sCtx ) ( (_sCtx)->State == AvFileModified ) +#define IS_FILE_INFECTED( _sCtx ) ( (_sCtx)->State == AvFileInfected ) +#define IS_FILE_NOT_INFECTED( _sCtx ) ( (_sCtx)->State == AvFileNotInfected ) + +#define IS_FILE_TX_MODIFIED( _sCtx ) ( (_sCtx)->TxState == AvFileModified ) +#define IS_FILE_TX_INFECTED( _sCtx ) ( (_sCtx)->TxState == AvFileInfected ) +#define IS_FILE_TX_NOT_INFECTED( _sCtx ) ( (_sCtx)->TxState == AvFileNotInfected ) + +#define IS_FILE_NEED_SCAN( _sCtx ) ((((_sCtx)->TxContext == NULL) && IS_FILE_MODIFIED( _sCtx )) || \ + (((_sCtx)->TxContext != NULL) && IS_FILE_TX_MODIFIED( _sCtx ))) + + +#define SET_FILE_UNKNOWN( _sCtx ) InterlockedExchange(&(_sCtx)->State, AvFileUnknown) +#define SET_FILE_MODIFIED( _sCtx ) InterlockedExchange(&(_sCtx)->State, AvFileModified) +#define SET_FILE_INFECTED( _sCtx ) InterlockedExchange(&(_sCtx)->State, AvFileInfected) +#define SET_FILE_NOT_INFECTED( _sCtx ) InterlockedExchange(&(_sCtx)->State, AvFileNotInfected) +#define SET_FILE_SCANNING( _sCtx ) InterlockedExchange(&(_sCtx)->State, AvFileScanning) + +#define SET_FILE_TX_UNKNOWN( _sCtx ) InterlockedExchange(&(_sCtx)->TxState, AvFileUnknown) +#define SET_FILE_TX_MODIFIED( _sCtx ) InterlockedExchange(&(_sCtx)->TxState, AvFileModified) +#define SET_FILE_TX_INFECTED( _sCtx ) InterlockedExchange(&(_sCtx)->TxState, AvFileInfected) +#define SET_FILE_TX_NOT_INFECTED( _sCtx ) InterlockedExchange(&(_sCtx)->TxState, AvFileNotInfected) +#define SET_FILE_TX_SCANNING( _sCtx ) InterlockedExchange(&(_sCtx)->TxState, AvFileScanning) + +#define SET_FILE_UNKNOWN_EX( _flag, _sCtx ) {\ + if (_flag) { \ + SET_FILE_TX_UNKNOWN( _sCtx ); \ + } else { \ + SET_FILE_UNKNOWN( _sCtx ); \ + } \ + } +#define SET_FILE_MODIFIED_EX( _flag, _sCtx ) {\ + if (_flag) { \ + SET_FILE_TX_MODIFIED( _sCtx ); \ + } else { \ + SET_FILE_MODIFIED( _sCtx ); \ + } \ + } +#define SET_FILE_INFECTED_EX( _flag, _sCtx ) {\ + if (_flag) { \ + SET_FILE_TX_INFECTED( _sCtx ); \ + } else { \ + SET_FILE_INFECTED( _sCtx ); \ + } \ + } +#define SET_FILE_NOT_INFECTED_EX( _flag, _sCtx ) {\ + if (_flag) { \ + SET_FILE_TX_NOT_INFECTED( _sCtx ); \ + } else { \ + SET_FILE_NOT_INFECTED( _sCtx ); \ + } \ + } +#define SET_FILE_SCANNING_EX( _flag, _sCtx ) {\ + if (_flag) { \ + SET_FILE_TX_SCANNING( _sCtx ); \ + } else { \ + SET_FILE_SCANNING( _sCtx ); \ + } \ + } + +// +// Stream/Stream Handle flags +// + +#define AV_FLAG_PREFETCH 0x00000001 + +typedef struct _AV_STREAMHANDLE_CONTEXT { + + // + // Handle flags + // + + ULONG Flags; + +} AV_STREAMHANDLE_CONTEXT, *PAV_STREAMHANDLE_CONTEXT; + +#define AV_STREAMHANDLE_CONTEXT_SIZE sizeof( AV_STREAMHANDLE_CONTEXT ) + +typedef struct _AV_STREAM_CONTEXT { + + // + // Stream flags + // + + ULONG Flags; + + // + // File ID, obtained from querying the file system for + // FileInternalInformation or FileIdInformation. + // + + AV_FILE_REFERENCE FileId; + + // + // A pointer to the transaction context, so we can jump to list in the transaction. + // + + PAV_TRANSACTION_CONTEXT TxContext; + + // + // This list entry is exactly the embedded entry to + // form a doubly linked list inside transaction context. + // + + LIST_ENTRY ListInTransaction; + + // + // We need to synchronize the creation of the section object. + // If this syncrhonization is not made, FltCreateSectionForDataScan + // would return STATUS_FLT_CONTEXT_ALREADY_DEFINED when two threads + // are about to create the section for the same file. + // + + PKEVENT ScanSynchronizationEvent; + + // + // Please see AV_FILE_INFECTED_STATE for the definition of file state + // Note that we have TxState to maintain the isolation of + // the transacted writer's view. + // + + volatile LONG State; + + + volatile LONG TxState; + + // + // Revision numbers for files on CSVFS + // + LONGLONG VolumeRevision; + LONGLONG CacheRevision; + LONGLONG FileRevision; + +} AV_STREAM_CONTEXT, *PAV_STREAM_CONTEXT; + +#define AV_STREAM_CONTEXT_SIZE sizeof( AV_STREAM_CONTEXT ) + +// +// Defines the section context structure +// + +typedef struct _AV_SECTION_CONTEXT { + + // + // The associated section handle. + // + + HANDLE SectionHandle; + + // + // The associated section object. + // + + PVOID SectionObject; + + // + // The cancel flag (if scan in the kernel mode). + // + + BOOLEAN Aborted; + + + // + // The size of the file associated with the section object. + // + + LONGLONG FileSize; + + // + // This flag indicates if this section data scan can be cancelable. + // Right now, only at pre-cleanup is cancelable on conflicting Io. + // + + BOOLEAN CancelableOnConflictingIo; + + // + // In the context of a conflict notification callback, only section context is given. + // We need to remember associated scan context to have scan id, so that + // We know which scan to cancel. + // + PVOID ScanContext; + +} AV_SECTION_CONTEXT, *PAV_SECTION_CONTEXT; + +#define AV_SECTION_CONTEXT_SIZE sizeof( AV_SECTION_CONTEXT ) + +// +// Instance context +// + +typedef struct _AV_INSTANCE_CONTEXT { + + // + // The associated volume object pointer + // + + PFLT_VOLUME Volume; + + // + // The associated filter instance pointer + // + + PFLT_INSTANCE Instance; + + // + // The file system type of the volume + // + + FLT_FILESYSTEM_TYPE VolumeFSType; + + // + // If the file system is NTFS, then it will support a file state cache table + // that saves the state of the file. + // + + RTL_GENERIC_TABLE FileStateCacheTable; + + // + // The per-instance lock to protect the cache table above. + // + + ERESOURCE Resource; + + // + // When set this flag indicates that the filter is attached on the + // hidden NTFS volume corresponding to a CSVFS volume + // + BOOLEAN IsOnCsvMDS; + +} AV_INSTANCE_CONTEXT, *PAV_INSTANCE_CONTEXT; + +#define AV_INSTANCE_CONTEXT_SIZE sizeof( AV_INSTANCE_CONTEXT ) + +NTSTATUS +AvFindOrCreateTransactionContext( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_ PAV_TRANSACTION_CONTEXT *TransactionContext + ); + +NTSTATUS +AvCreateSectionContext ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Outptr_ PAV_SECTION_CONTEXT *SectionContext + ); + +NTSTATUS +AvCreateStreamHandleContext ( + _In_ PFLT_FILTER Filter, + _Outptr_ PAV_STREAMHANDLE_CONTEXT *StreamHandleContext + ); + +NTSTATUS +AvCreateStreamContext ( + _In_ PFLT_FILTER Filter, + _Outptr_ PAV_STREAM_CONTEXT *StreamContext + ); + +NTSTATUS +AvEnumerateInstances( + _Outptr_result_buffer_(*NumberInstances) PFLT_INSTANCE **InstanceArray, + _Out_ PULONG NumberInstances + ); + +VOID +AvFreeInstances ( + _In_reads_(InstanceCount) PFLT_INSTANCE *InstanceArray, + _In_ ULONG InstanceCount + ); + +#endif + diff --git a/filesys/miniFilter/avscan/filter/csvfs.c b/filesys/miniFilter/avscan/filter/csvfs.c new file mode 100644 index 00000000..8d29a7fc --- /dev/null +++ b/filesys/miniFilter/avscan/filter/csvfs.c @@ -0,0 +1,1039 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + csvfs.c + +Abstract: + + This is the csvfs specific module of the avscan mini-filter driver. + This filter demonstrates how to implement a transaction-aware + anti-virus filter. + + Av prefix denotes "Anti-virus" module. + + + CSVFS is a distributed file system where multiple nodes in a + cluster can expose the same volume namespace at the same time. To + achieve this the CSVFS volumes on each node act as a proxy for an + underlying NTFS volume where the NTFS volume runs only on one node + of the cluster. It is this NTFS volume where the file data and file + system metadata are stored. The node that exposes this NTFS volume + is the coordinator node. The underlying NTFS volume is made hidden + to keep applications from using it directly. All applications + should use the CSVFS namespace. + + CSVFS implements functionality known as Direct I/O. This + functionality allow the CSVFS proxy on each node to directly read + or write to the blocks on the disk and bypass the NTFS volume on the + coordinator node when it is safe to do so. A filter on node node + needs to be aware that it may not see all I/O to a file. + + In the CSVFS enviornment filters can layer on the CSVFS volume + stack, the hidden NTFS volume stack on the coordinator node and + also on the MUP stack. When on the MUP and the hidden NTFS stack + the filter should not scan any files that are opened with + GUID_ECP_CSV_DOWN_LEVEL_OPEN ECP attached. This ECP is used by + CSVFS for its internal file opens and should be ignored by filters. + Filters that layer on the CSVFS would be the components that scan + the files. + + It is recommended that filters be extremely careful when layered on + the hidden NTFS stack: + + Management issues + Volume is hidden and thus does not have volume guid, + mountpoint or drive letter that can be used to + represent this volume to the user in a command line or UI. + + The coordinator node for a CSVFS volume can move to another + node at any time. This would cause challenges with + maintaining filter configuration setttings and also be + challenging for the admin to know which node should be used. + + Interop with CSVFS issues + Changing file sizes in a filter layered above the hidden + NTFS volume may cause data corruption. + + Building and keeping a mapped section on hidden NTFS volume + might prevent direct IO from happening + + Building and keeping mapped section on hidden NTFS volume + might lead to cache coherency issues (stale cache) and + eventually to data corruption. + + Note that there is a feasible model for creating a distributed + filter with instances on the CSVFS and the hidden NTFS volume + stacks. The instance on the hidden NTFS volume would serve as a + centralized filter meta-data server where information shared + between node could be maintained. Each instance on CSVFS volume + would communicate with the hidden NTFS volume stack through a + downlevel file handle. + + CSVFS provides a very simple centralized meta-data cache which + contains revision numbers for tracking changes to files. This + sample shows how those revision numbers can be used to determine if + a file has been modified by another node and thus if it needs to be + scanned locally. + + Other things to be aware of: + + Filter should not have *any* global locks or else deadlock may + occur. File system requests will flow through the CSVFS proxy file + system and then be forwarded to the hidden NTFS file system. If the + filter is layered on both file systems and the filter instance on the + CSVFS volume takes a lock as the request passes through, when the + request is forwarded and reaches the filter instance on the hidden + NTFS or MUP volume, it will attempt to acquire the lock and deadlock will + occur. + + Do not make assumption that buffered IO will always go to cache and + eventually you will see paging IO. CsvFs, like RDR, does caching based + on the oplock it was able to get for this stream. For instance if a + file is being accessed from multiple nodes CsvFs would have oplock + level RH or R or none and all cached writes to the file will be sent + directly to NTFS without going to CC. You can think about that as if + from the perspective of a filter sitting above CSVFS cached IO is being + handles as if is it un buffered IO. Since in general filters would + not know when CsvFs loses/gains oplocks they should not make an + assumption that cached IO will go through CC. + + +Environment: + + Kernel mode + +--*/ + +#include "avscan.h" +#include + +/************************************************************************* + Local Function Prototypes +*************************************************************************/ +NTSTATUS +AvAddCsvRevisionECP ( + _Inout_ PFLT_CALLBACK_DATA Data + ); + +NTSTATUS +AvReadCsvRevisionECP ( + _Inout_ PFLT_CALLBACK_DATA Data, + _Out_ LONGLONG *VolumeRevision, + _Out_ LONGLONG *CacheRevision, + _Out_ LONGLONG *FileRevision + ); + +NTSTATUS +AvQueryCsvRevisionNumbers ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Out_ LONGLONG *VolumeRevision, + _Out_ LONGLONG *CacheRevision, + _Out_ LONGLONG *FileRevision + ); + +NTSTATUS +AvFindAckedECP ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ LPCGUID EcpGuid, + _Out_ PVOID *Ecp, + _Out_ ULONG *EcpSize + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, AvIsVolumeOnCsvDisk) +#pragma alloc_text(PAGE, AvIsCsvDlEcpPresent) +#pragma alloc_text(PAGE, AvPreCreateCsvfs) +#pragma alloc_text(PAGE, AvPostCreateCsvfs) +#pragma alloc_text(PAGE, AvPreCleanupCsvfs) +#pragma alloc_text(PAGE, AvQueryCsvRevisionNumbers) +#pragma alloc_text(PAGE, AvReadCsvRevisionECP) +#pragma alloc_text(PAGE, AvAddCsvRevisionECP) +#pragma alloc_text(PAGE, AvFindAckedECP) +#endif + +BOOLEAN +AvIsVolumeOnCsvDisk ( + _In_ PFLT_VOLUME Volume + ) + /*++ + + Routine Description: + + This routine checks if the volume indicated by Volume belongs to a disk that is CSV or not. + + Arguments: + + Volume - Pointer to the FLT_VOLUME. + + Return Value: + + The return value is TRUE or FALSE. + + --*/ +{ + + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN retValue = FALSE; + PDEVICE_OBJECT disk = NULL, refDeviceObject=NULL; + PIRP irp; + IO_STATUS_BLOCK iosb; + ULONG controlCode = IOCTL_DISK_GET_CLUSTER_INFO; + DISK_CLUSTER_INFO outBuf; + KEVENT event; + + PAGED_CODE(); + + status = FltGetDiskDeviceObject(Volume, &disk); + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("Failed to get disk object from volume, status 0x%x\n", status) ); + goto Cleanup; + } + + refDeviceObject = IoGetAttachedDeviceReference(disk); + + iosb.Information = 0; + RtlZeroMemory(&outBuf, sizeof(outBuf)); + KeInitializeEvent(&event, NotificationEvent, FALSE); + + irp = IoBuildDeviceIoControlRequest( controlCode, + refDeviceObject, + NULL, + 0, + &outBuf, + sizeof(outBuf), + FALSE, + &event, + &iosb ); + if (irp == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("Failed to allocate Irp, status 0x%x\n", status) ); + goto Cleanup; + } + + status = IoCallDriver( refDeviceObject, irp ); + if (status == STATUS_PENDING) { + KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL ); + status = iosb.Status; + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("DeviceIoControl returned status 0x%x\n", status) ); + + if(!NT_SUCCESS( status )) { + goto Cleanup; + } + + retValue = FlagOn( outBuf.Flags, DISK_CLUSTER_FLAG_CSV ) ? TRUE : FALSE; + if (FlagOn( outBuf.Flags, DISK_CLUSTER_FLAG_CSV) && FlagOn(outBuf.Flags, DISK_CLUSTER_FLAG_IN_MAINTENANCE )) { + // + // A CSV disk can be in maintenance mode. When in maintenance + // mode the CSV namespace is no longer exposed across the + // entire cluster but instead only exposed on the single node + // where the NTFS volume is exposed. In this case the filter + // should treat the volume as it would any other NTFS volume + // + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("Disk is CSV but in Maintenance\n") ); + retValue = FALSE; + } + + if(retValue == TRUE) { + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("Disk is CSV\n") ); + } + else { + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("Disk is not CSV\n") ); + } + +Cleanup: + + if (refDeviceObject) { + KeEnterCriticalRegion(); + ObDereferenceObject( refDeviceObject ); + refDeviceObject = NULL; + KeLeaveCriticalRegion(); + } + + if (disk) { + ObDereferenceObject( disk ); + disk = NULL; + } + + return retValue; +} + +NTSTATUS +AvAddCsvRevisionECP ( + _Inout_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This routine will include the Extra Create Parameter (ECP) that is + used on CSVFS file systems. This ECP will return the set of + revision numbers that are associated with the file being opened. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PECP_LIST ecpList = NULL; + PCSV_QUERY_FILE_REVISION_ECP_CONTEXT ecpContext; + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvAddCsvRevisionECP: Entered\n") ); + + status = FltGetEcpListFromCallbackData( Globals.Filter, + Data, + &ecpList ); + if (!NT_SUCCESS( status )) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvAddCsvRevisionECP: FltGetEcpListFromCallbackData failed 0x%x\n", status) ); + goto Cleanup; + } + + if (ecpList == NULL) { + // + // Create a new ecplist. + // + status = FltAllocateExtraCreateParameterList( Globals.Filter, 0, &ecpList ); + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvAddCsvRevisionECP: FltAllocateExtraCreateParameterList failed 0x%x", status) ); + goto Cleanup; + } + // + // Set it into CBD. + // + status = FltSetEcpListIntoCallbackData( Globals.Filter, Data, ecpList ); + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvAddCsvRevisionECP: FltSetEcpListIntoCallbackData failed 0x%x", status) ); + FltFreeExtraCreateParameterList( Globals.Filter, ecpList ); + goto Cleanup; + } + + } else { + // + // See if the ECP has already been added to the ECP list + // already. + // + status = FltFindExtraCreateParameter( Globals.Filter, + ecpList, + &GUID_ECP_CSV_QUERY_FILE_REVISION, + NULL, + NULL ); + if (status != STATUS_NOT_FOUND) { + goto Cleanup; + } + + } + + status = FltAllocateExtraCreateParameter( Globals.Filter, + &GUID_ECP_CSV_QUERY_FILE_REVISION, + sizeof(CSV_QUERY_FILE_REVISION_ECP_CONTEXT), + 0, + NULL, + AV_SCAN_CTX_TAG, + &ecpContext ); + + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvAddCsvRevisionECP: FltAllocateExtraCreateParameterFromLookasideList failed 0x%x\n", status) ); + goto Cleanup; + } + + RtlZeroMemory( ecpContext, sizeof(CSV_QUERY_FILE_REVISION_ECP_CONTEXT )); + status = FltInsertExtraCreateParameter( Globals.Filter, + ecpList, + ecpContext ); + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvAddCsvRevisionECP: FltInsertExtraCreateParameter failed 0x%x\n", status) ); + FltFreeExtraCreateParameter( Globals.Filter, ecpContext ); + goto Cleanup; + } + +Cleanup: + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvAddCsvRevisionECP: Leave\n") ); + + return status; +} + +NTSTATUS +AvFindAckedECP ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ LPCGUID EcpGuid, + _Out_ PVOID *Ecp, + _Out_ ULONG *EcpSize + ) +/*++ + +Routine Description: + + This routine will find the Extra Create Parameter (ECP) and if it + exists then check if it has been acknowledged + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + EcpGuid - Pointer to the guid that represents the ECP to find + + *Ecp - returns with a pointer to the ECP data + + *EcpSize - returns with the size of the ECP data + + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PECP_LIST ecpList = NULL; + PVOID ecpContext = NULL; + ULONG ecpContextSize = 0; + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvFindAckedECP: Entered\n") ); + + status = FltGetEcpListFromCallbackData( Globals.Filter, + Data, + &ecpList); + if (NT_SUCCESS(status)) { + + if (ecpList != NULL) { + + status = FltFindExtraCreateParameter( Globals.Filter, + ecpList, + EcpGuid, + &ecpContext, + &ecpContextSize); + + if (NT_SUCCESS(status)) { + + if (FltIsEcpAcknowledged( Globals.Filter, ecpContext )) { + *Ecp = ecpContext; + *EcpSize = ecpContextSize; + } else { + status = STATUS_UNSUCCESSFUL; + } + } + + } else { + status = STATUS_UNSUCCESSFUL; + } + } + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvFindAckedECP: leave 0x%x\n", status) ); + + return status; +} + +NTSTATUS +AvReadCsvRevisionECP ( + _Inout_ PFLT_CALLBACK_DATA Data, + _Out_ LONGLONG *VolumeRevision, + _Out_ LONGLONG *CacheRevision, + _Out_ LONGLONG *FileRevision + ) +/*++ + +Routine Description: + + This routine will read the Extra Create Parameter (ECP) that is + returned on CSVFS file systems. This ECP returns the set of + revision numbers that are associated with the file being opened. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + *VolumeRevision returns with the volume revision number + + *CacheRevision returns with the cache revision number + + *FileRevision returns with the file revision number + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PCSV_QUERY_FILE_REVISION_ECP_CONTEXT ecpContext = NULL; + ULONG ecpContextSize = 0; + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvReadCsvRevisionECP: Entered\n") ); + + + status = AvFindAckedECP( Data, + &GUID_ECP_CSV_QUERY_FILE_REVISION, + &ecpContext, + &ecpContextSize ); + + if (NT_SUCCESS( status )) { + *VolumeRevision = ecpContext->FileRevision[0]; + *CacheRevision = ecpContext->FileRevision[1]; + *FileRevision = ecpContext->FileRevision[2]; + } + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvReadCsvRevisionECP: leave 0x%x\n", status) ); + + return status; +} + +NTSTATUS +AvQueryCsvRevisionNumbers ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Out_ LONGLONG *VolumeRevision, + _Out_ LONGLONG *CacheRevision, + _Out_ LONGLONG *FileRevision + ) +/*++ + +Routine Description: + + Obtain the most updated revision numbers for a file on CSVFS + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + *VolumeRevision returns with the volume revision number + + *CacheRevision returns with the cache revision number + + *FileRevision returns with the file revision number + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + CSV_CONTROL_PARAM request; + CSV_QUERY_FILE_REVISION revision; + ULONG bytesReturned; + + PAGED_CODE(); + + RtlZeroMemory( &request, sizeof( request ) ); + request.Operation = CsvControlQueryFileRevision; + + status = FltFsControlFile( FltObjects->Instance, + FltObjects->FileObject, + FSCTL_CSV_CONTROL, + &request, + sizeof(request), + &revision, + sizeof(revision), + &bytesReturned ); + + if (NT_SUCCESS( status )) { + *VolumeRevision = revision.FileRevision[0]; + *CacheRevision = revision.FileRevision[1]; + *FileRevision = revision.FileRevision[2]; + } + + return status; +} + +BOOLEAN +AvIsCsvDlEcpPresent ( + _In_ PFLT_FILTER Filter, + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This local function will determine if there is a CSVFS downlevel + ECP attached. + +Arguments: + + Filter - Pointer to the filter structure + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + TRUE - CSVFS downlevel ECP is present + FALSE - CSVFS downlevel ECP is not present or an error occured + +--*/ +{ + NTSTATUS status; + PECP_LIST ecpList; + PVOID ecpContext; + + PAGED_CODE(); + + status = FltGetEcpListFromCallbackData( Filter, Data, &ecpList ); + + if (NT_SUCCESS(status) && (ecpList != NULL)) { + + status = FltFindExtraCreateParameter( Filter, + ecpList, + &GUID_ECP_CSV_DOWN_LEVEL_OPEN, + &ecpContext, + NULL ); + + if (NT_SUCCESS(status)) { + + return TRUE; + } + } + + return FALSE; +} + +NTSTATUS +AvPreCreateCsvfs ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects + ) +/*++ + +Routine Description: + + This function implements the PreCreate processing associated with a + CSVFS volume. The work done is to include the file revision ECP + into the ECP list so that CSVFS will return the revision numbers + when the create completes. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (NT_SUCCESS( status )) { + + if (instanceContext->VolumeFSType == FLT_FSTYPE_CSVFS) { + // + // Add ECP to retrieve the revision numbers + // + status = AvAddCsvRevisionECP( Data ); + + // + // we don't worry if this fails since if we do not get the + // revision numbers then we just assume they have changed + // + } + + FltReleaseContext( instanceContext ); + } else { + // + // If unable to get instance context then it is no problem. It + // means that the revision numbers won't be returned from + // CSVFS. It is not fatal but will result in the file being + // rescanned. + // + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPreCreateCsvfs: FltGetInstanceContext failed. status = 0x%x\n", status) ); + } + + return status; +} + +NTSTATUS +AvPostCreateCsvfs ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _Out_ BOOLEAN *UpdateRevisionNumbers, + _Out_ LONGLONG *VolumeRevisionPtr, + _Out_ LONGLONG *CacheRevisionPtr, + _Out_ LONGLONG *FileRevisionPtr + ) +/*++ + +Routine Description: + + This function implements the PostCreate processing associated with a + CSVFS volume. The work done is to determine if the revision numbers + have been returned and if so determine if a rescan of the file is + needed. A recan would be needed if the revision numbers are not + able to be retrieved, are not valid or do not match the previously + recorded revision numbers. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + StreamContext - Pointer to the AV stream context + + *UpdateRevisionNumbers returns TRUE if the revision numbers should + be updated in the stream context upon a successful scan + + *VolumeRevisionPtr return with the updated volume revision number + + *CacheRevisionPtr return with the updated cache revision number + + *FileRevisionPtr return with the updated file revision number + +Return Value: + + Status + +--*/ +{ + PAV_INSTANCE_CONTEXT instanceContext = NULL; + NTSTATUS status; + LONGLONG VolumeRevision = 0, CacheRevision = 0, FileRevision = 0; + BOOLEAN needRescanOnCsvfs = FALSE; + + PAGED_CODE(); + + *UpdateRevisionNumbers = FALSE; + *VolumeRevisionPtr = 0; + *CacheRevisionPtr = 0; + *FileRevisionPtr = 0; + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (NT_SUCCESS( status )) { + + if (instanceContext->VolumeFSType == FLT_FSTYPE_CSVFS) { + + // + // Read ECP to retrieve the revision numbers + // + status = AvReadCsvRevisionECP( Data, + &VolumeRevision, + &CacheRevision, + &FileRevision); + + if (NT_SUCCESS( status )) { + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + (" [AV] AvPostCreateCsvfs: %I64x:%I64x:%I64x\n", + VolumeRevision, + CacheRevision, + FileRevision) ); + + // + // It is very possible that the file was changed by + // another node in the cluster and so we need to check + // this case. So if any of the revision number have + // changed we need to assume that the file has changed. + // Note that this is a very pessimistic assumption as + // the Volume and Cache revision numbers could change + // without a corresponding file change but rescanning + // when these change will ensure that a file changed + // on another node will not be opened without being + // rescanned on this node. + // + // Also note that there are cases where the revision + // numbers are zero and thus not at all valid. In this + // case the file must be rescanned as it is not known + // if it was changed or not. + // + needRescanOnCsvfs = ( (VolumeRevision == 0) || + (CacheRevision == 0) || + (FileRevision == 0) || + (VolumeRevision != StreamContext->VolumeRevision) || + (CacheRevision != StreamContext->CacheRevision) || + (FileRevision != StreamContext->FileRevision) ); + } else { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + (" [AV] AvPostCreateCsvfs: Status 0x%x from AvReadCsvRevisionECP\n", status) ); + + // + // In this case the revision numbers are not available. + // Since there is no way to know if the file was + // changed on another node, it is safest to rescan. + // + needRescanOnCsvfs = TRUE; + } + } + FltReleaseContext( instanceContext ); + } else { + // + // If unable to get instance context then the code isn't sure + // if it is on a CSVFS volume or not. The safest assumption + // would be to rescan the file. + // + needRescanOnCsvfs = TRUE; + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPostCreateCsvfs: FltGetInstanceContext failed. status = 0x%x\n", status) ); + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvPostCreateCsvfs: %ws need rescan\n", + needRescanOnCsvfs ? L"Does" : L"Does not") + ); + + + // + // if it has been determined that a rescan is needed then set the + // file modified flag on the stream context to indicate this + // + if (needRescanOnCsvfs) { + if ( StreamContext->TxContext != NULL) { + + // + // Instead of updating State, we update TxState here, + // because the file is part of a transaction writer + // + + SET_FILE_TX_MODIFIED( StreamContext ); + + } else { + + SET_FILE_MODIFIED( StreamContext ); + } + + // + // If CSVFS has provided us with valid revison numbers then + // return them to the caller so it can update them in the + // stream context if the scan is successful. + // + if ((VolumeRevision != 0) && + (CacheRevision != 0) && + (FileRevision != 0)) { + *UpdateRevisionNumbers = TRUE; + *VolumeRevisionPtr = VolumeRevision; + *CacheRevisionPtr = CacheRevision; + *FileRevisionPtr = FileRevision; + } + } + + return status; +} + +NTSTATUS +AvPreCleanupCsvfs ( + _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _Out_ BOOLEAN *UpdateRevisionNumbers, + _Out_ LONGLONG *VolumeRevisionPtr, + _Out_ LONGLONG *CacheRevisionPtr, + _Out_ LONGLONG *FileRevisionPtr + ) +/*++ + +Routine Description: + + This function implements the PreCleanup processing associated with a + CSVFS volume. The work done is to retrieve the current revision + numbers and determine if a rescan of the file is needed. A recan + would be needed if the revision numbers are not + able to be retrieved, are not valid or do not match the previously + recorded revision numbers. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + StreamContext - Pointer to the AV stream context + + *UpdateRevisionNumbers returns TRUE if the revision numbers should + be updated in the stream context upon a successful scan + + *VolumeRevisionPtr return with the updated volume revision number + + *CacheRevisionPtr return with the updated cache revision number + + *FileRevisionPtr return with the updated file revision number + +Return Value: + + Status + +--*/ +{ + PAV_INSTANCE_CONTEXT instanceContext = NULL; + NTSTATUS status; + LONGLONG VolumeRevision = 0, CacheRevision = 0, FileRevision = 0; + BOOLEAN needRescanOnCsvfs = FALSE; + + UNREFERENCED_PARAMETER( Data ); + + PAGED_CODE(); + + *UpdateRevisionNumbers = FALSE; + *VolumeRevisionPtr = 0; + *CacheRevisionPtr = 0; + *FileRevisionPtr = 0; + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (NT_SUCCESS( status )) { + + if (instanceContext->VolumeFSType == FLT_FSTYPE_CSVFS) { + + // + // If this file is on CSVFS then we cannot completely rely + // upon tracking if the file is modified only on this node, + // but also need to track if the file has been modified on + // any node. So query for the updated revision numbers to + // see if a scan is needed. + // + status = AvQueryCsvRevisionNumbers( FltObjects, + &VolumeRevision, + &CacheRevision, + &FileRevision ); + + if (NT_SUCCESS( status )) { + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + (" [AV] AvPreCleanupCsvfs: %I64x:%I64x:%I64x\n", + VolumeRevision, + CacheRevision, + FileRevision) ); + + // + // It is very possible that the file was changed by + // another node in the cluster and so we need to check + // this case. So if any of the revision number have + // changed we need to assume that the file has changed. + // Note that this is a very pessimistic assumption as + // the Volume and Cache revision numbers could change + // without a corresponding file change but rescanning + // when these change will ensure that a file changed + // on another node will not be opened without being + // rescanned on this node. + // + // Also note that there are cases where the revision + // numbers are zero and thus not at all valid. In this + // case the file must be rescanned as it is not known + // if it was changed or not. + // + needRescanOnCsvfs = ( (VolumeRevision == 0) || + (CacheRevision == 0) || + (FileRevision == 0) || + (VolumeRevision != StreamContext->VolumeRevision) || + (CacheRevision != StreamContext->CacheRevision) || + (FileRevision != StreamContext->FileRevision) ); + } else { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + (" [AV] AvPreCleanupCsvfs: Status 0x%x from AvReadCsvRevisionECP\n", status) ); + + // + // In this case the revision numbers are not available. + // Since there is no way to know if the file was + // changed on another node, it is safest to rescan. + // + needRescanOnCsvfs = TRUE; + } + } + FltReleaseContext( instanceContext ); + } else { + // + // If unable to get instance context then the code isn't sure + // if it is on a CSVFS volume or not. The safest assumption + // would be to rescan the file. + // + needRescanOnCsvfs = TRUE; + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPreCleanupCsvfs: FltGetInstanceContext failed. status = 0x%x\n", status) ); + } + + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvPreCleanupCsvfs: %ws need rescan\n", + needRescanOnCsvfs ? L"Does" : L"Does not") + ); + + + // + // if it has been determined that a rescan is needed then set the + // file modified flag on the stream context to indicate this + // + if (needRescanOnCsvfs) { + if ( StreamContext->TxContext != NULL) { + + // + // Instead of updating State, we update TxState here, + // because the file is part of a transaction writer + // + + SET_FILE_TX_MODIFIED( StreamContext ); + + } else { + + SET_FILE_MODIFIED( StreamContext ); + } + + // + // If CSVFS has provided us with valid revison numbers then + // return them to the caller so it can update them in the + // stream context if the scan is successful. + // + if ((VolumeRevision != 0) && + (CacheRevision != 0) && + (FileRevision != 0)) { + *UpdateRevisionNumbers = TRUE; + *VolumeRevisionPtr = VolumeRevision; + *CacheRevisionPtr = CacheRevision; + *FileRevisionPtr = FileRevision; + } + } + + return status; +} diff --git a/filesys/miniFilter/avscan/filter/csvfs.h b/filesys/miniFilter/avscan/filter/csvfs.h new file mode 100644 index 00000000..7e34ce3c --- /dev/null +++ b/filesys/miniFilter/avscan/filter/csvfs.h @@ -0,0 +1,62 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + csvfs.h + +Abstract: + + This module contains the scan interface for AV filter to call. + +Environment: + + Kernel mode + +--*/ +#ifndef __CSVFS_H__ +#define __CSVFS_H__ + + +NTSTATUS +AvPreCleanupCsvfs ( + _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _Out_ BOOLEAN *UpdateRevisionNumbers, + _Out_ LONGLONG *VolumeRevisionPtr, + _Out_ LONGLONG *CacheRevisionPtr, + _Out_ LONGLONG *FileRevisionPtr + ); + +NTSTATUS +AvPostCreateCsvfs ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _Out_ BOOLEAN *UpdateRevisionNumbers, + _Out_ LONGLONG *VolumeRevisionPtr, + _Out_ LONGLONG *CacheRevisionPtr, + _Out_ LONGLONG *FileRevisionPtr + ); + +NTSTATUS +AvPreCreateCsvfs ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects + ); + +BOOLEAN +AvIsCsvDlEcpPresent ( + _In_ PFLT_FILTER Filter, + _In_ PFLT_CALLBACK_DATA Data + ); + +BOOLEAN +AvIsVolumeOnCsvDisk ( + _In_ PFLT_VOLUME Volume + ); + +#endif + diff --git a/filesys/miniFilter/avscan/filter/scan.c b/filesys/miniFilter/avscan/filter/scan.c new file mode 100644 index 00000000..12fe69c3 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/scan.c @@ -0,0 +1,571 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + scan.c + +Abstract: + + This modules wraps the scanning routines. + +Environment: + + Kernel mode + +--*/ + +#include "avscan.h" + +// +// Local routines prototypes. +// + +AVSCAN_RESULT +AvScanMemoryStream( + _In_reads_bytes_(Size) PVOID StartingAddress, + _In_ SIZE_T Size, + _In_ PBOOLEAN OperationCanceled + ); + +NTSTATUS +AvMapSectionAndScan( + _Inout_ PAV_SECTION_CONTEXT SectionContext, + _Out_ AVSCAN_RESULT *ScanResult + ); + +// +// Routine implementaions +// + +AVSCAN_RESULT +AvScanMemoryStream( + _In_reads_bytes_(Size) PVOID StartingAddress, + _In_ SIZE_T Size, + _In_ PBOOLEAN OperationCanceled + ) +/*++ + +Routine Description + + A helper function to scan the memory starting at StartingAddress. + This function is only called if the scan mode is AvKernelMode. + +Arguments + + StartingAddress - The starting memory address to be scanned. + + Size - The size of the memory to be scanned. + + OperationCanceled - In the scan loop, it is supposed to poll this flag, + to see if the operation has been canceled. + +Return Value + + The scan result + +--*/ +{ + UCHAR targetString[AV_DEFAULT_SEARCH_PATTERN_SIZE] = {0}; + SIZE_T searchStringLength = AV_DEFAULT_SEARCH_PATTERN_SIZE-1; + ULONG ind; + PUCHAR p; + PUCHAR start = StartingAddress; + PUCHAR end = start + Size - searchStringLength; + + // + // Decode the target pattern. + // + + RtlCopyMemory( (PVOID) targetString, + AV_DEFAULT_SEARCH_PATTERN, + AV_DEFAULT_SEARCH_PATTERN_SIZE ); + + for (ind = 0; + ind < searchStringLength; + ind++) { + + targetString[ind] = ((UCHAR)targetString[ind]) ^ AV_DEFAULT_PATTERN_XOR_KEY; + } + targetString[searchStringLength] = '\0'; + + // + // Scan the memory stream for the target pattern. + // + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[Av]: ASMS: %p, %p, %llu, %llu\n", + start, + end, + Size, + searchStringLength) ); + + for (p = start; p <= end; p++) { + + // if not canceled, continue to search for pattern + if((*OperationCanceled)) { + + return AvScanResultUndetermined; + } + + if (RtlEqualMemory( p, targetString, searchStringLength )) { + + return AvScanResultInfected; + } + } + + *OperationCanceled = FALSE; // Reset the cancel flag, after breaks out the loop. + + return AvScanResultClean; +} + +NTSTATUS +AvMapSectionAndScan( + _Inout_ PAV_SECTION_CONTEXT SectionContext, + _Out_ AVSCAN_RESULT *ScanResult + ) +/*++ + +Routine Description + + A helper function to map the section object and scan the mapped memory. + +Arguments + + SectionContext - Section context containing section object and handle. + + Infected - Return TRUE if the file is infected. + +Return Value + + Returns the status of this operation. + +--*/ +{ + NTSTATUS status; + CLIENT_ID clientId; + OBJECT_ATTRIBUTES objAttribs; + HANDLE processHandle = NULL; + PVOID scanAddress = NULL; + SIZE_T scanSize = 0; + AVSCAN_RESULT scanResult; + + clientId.UniqueThread = PsGetCurrentThreadId(); + clientId.UniqueProcess = PsGetCurrentProcessId(); + + InitializeObjectAttributes(&objAttribs, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenProcess( &processHandle, + PROCESS_ALL_ACCESS, + &objAttribs, + &clientId ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvMapSectionAndScan: Failed to open the process, 0x%08x\n", + status) ); + goto Cleanup; + } + + status = ZwMapViewOfSection( SectionContext->SectionHandle, + processHandle, + &scanAddress, + 0, + 0, + NULL, + &scanSize, + ViewUnmap, + 0, + PAGE_READONLY ); + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvMapSectionAndScan: Failed to map the view of the section, 0x%08x\n", + status) ); + + goto Cleanup; + } + + // + // The size here may have truncation. + // + scanResult = AvScanMemoryStream( scanAddress, + (SIZE_T)min((LONGLONG)scanSize, SectionContext->FileSize), + &SectionContext->Aborted ); + + *ScanResult = scanResult; + +Cleanup: + + if (scanAddress != NULL) { + + ZwUnmapViewOfSection( processHandle, scanAddress ); + } + + if (processHandle != NULL) { + + ZwClose( processHandle ); + } + + return status; +} + +NTSTATUS +AvScanInKernel ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _In_ PAV_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description + + This function is a high level function which + will do the kernel-mode data scan. + +Arguments + + FltObjects - related objects for the IO operation. + + IOMajorFunctionAtScan - The major function of the IRP that issues this scan. + + IsInTxWriter - If this file is enlisted in a transacted writer. + + StreamContext - The stream context of this data stream. + +Return Value + + Returns the status of this operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + OBJECT_ATTRIBUTES objAttribs; + PAV_SECTION_CONTEXT sectionContext; + AVSCAN_RESULT scanResult = AvScanResultUndetermined; + + status = AvCreateSectionContext( FltObjects->Instance, + FltObjects->FileObject, + §ionContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvScanInKernel: failed to create section context.\n") ); + + return status; + } + + sectionContext->CancelableOnConflictingIo = (IOMajorFunctionAtScan == IRP_MJ_CLEANUP); + + InitializeObjectAttributes(&objAttribs, + NULL, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = FltCreateSectionForDataScan( FltObjects->Instance, + FltObjects->FileObject, + sectionContext, + SECTION_MAP_READ, + &objAttribs, + NULL, + PAGE_READONLY, + SEC_COMMIT, + 0, + §ionContext->SectionHandle, + §ionContext->SectionObject, + NULL ); + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvScanInKernel: Failed to create section for data scan.\n, 0x%08x\n", + status) ); + return status; + } + + status = AvMapSectionAndScan( sectionContext, &scanResult ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvScanInKernel: Failed to scan the view of the section.\n, 0x%08x\n", + status) ); + } + + if (scanResult == AvScanResultClean) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvScanInKernel: file %I64x,%I64x is CLEAN!!\n", + StreamContext->FileId.FileId64.UpperZeroes, + StreamContext->FileId.FileId64.Value) ); + + SET_FILE_NOT_INFECTED_EX( IsInTxWriter, StreamContext ); + + } else if (scanResult == AvScanResultInfected) { + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvScanInKernel: file %I64x,%I64x is INFECTED!!\n", + StreamContext->FileId.FileId64.UpperZeroes, + StreamContext->FileId.FileId64.Value) ); + + SET_FILE_INFECTED_EX( IsInTxWriter, StreamContext ); + + } else { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvScanInKernel: file %I64x,%I64x is UNKNOWN!!\n", + StreamContext->FileId.FileId64.UpperZeroes, + StreamContext->FileId.FileId64.Value) ); + + SET_FILE_UNKNOWN_EX( IsInTxWriter, StreamContext ); + } + + status = AvFinalizeSectionContext(sectionContext); + + return status; +} + +NTSTATUS +AvScanInUser ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _In_ DEVICE_TYPE DeviceType + ) +/*++ + +Routine Description + + This function is a high level function which + will do the user-mode data scan. + +Arguments + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - related objects for the IO operation. + + IOMajorFunctionAtScan - The major function of the IRP that issues this scan. + + IsInTxWriter - If this file is enlisted in a transacted writer. + + StreamContext - The stream context of this data stream. + +Return Value + + Returns the status of this operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG scanThreadId; + ULONG replyLength = sizeof(ULONG); + PAV_SCAN_CONTEXT scanCtx = NULL; + AV_SCANNER_NOTIFICATION notification = {0}; + LONGLONG _1ms = 10000; + LARGE_INTEGER timeout = {0}; + + status = AvAllocateScanContext(FltObjects->Instance, + FltObjects->FileObject, + &scanCtx); + if (!NT_SUCCESS(status)) { + + return status; + } + + // + // Scan context is passed to the user service program. + // Initialize it here. + // + + KeInitializeEvent( &scanCtx->ScanCompleteNotification, NotificationEvent, FALSE ); + scanCtx->IOMajorFunctionAtScan = IOMajorFunctionAtScan; + scanCtx->IsFileInTxWriter = IsInTxWriter; + scanCtx->SectionContext = NULL; + + AvAcquireResourceExclusive( &Globals.ScanCtxListLock ); + if (Globals.Unloading) { + // + // If the filter is being unloaded, we failed the scan. + // + AvReleaseResource( &Globals.ScanCtxListLock ); + AvReleaseScanContext( scanCtx ); + + return STATUS_FLT_DELETING_OBJECT; + } + scanCtx->ScanId = (++Globals.ScanIdCounter); + InsertTailList (&Globals.ScanCtxListHead, &scanCtx->List); + AvReleaseResource( &Globals.ScanCtxListLock ); + + // + // Tell the user-scanner to start to scan the file + // + + notification.Message = AvMsgStartScanning; + notification.ScanId = scanCtx->ScanId; + notification.Reason = AvScanOnOpen; + + if (IOMajorFunctionAtScan == IRP_MJ_CLEANUP) { + notification.Reason = AvScanOnCleanup; + } + + // + // Set the scan timeout for this file based on if it is a local or + // network file. These values can come from the registry. + // + + if (DeviceType == FILE_DEVICE_NETWORK) { + timeout.QuadPart = Globals.NetworkScanTimeout; + } else { + timeout.QuadPart = Globals.LocalScanTimeout; + } + + timeout.QuadPart = -(timeout.QuadPart * _1ms); + + status = FltSendMessage( Globals.Filter, + &Globals.ScanClientPort, + ¬ification, + sizeof(AV_SCANNER_NOTIFICATION), + &scanThreadId, + &replyLength, + &timeout ); + // + // If the message is not delievered or time-out, we can make sure that + // the scanner thread did not acknowledged this scan task, and thus + // we can safely remove it from the list. + // + if (!NT_SUCCESS( status ) || status == STATUS_TIMEOUT) { + + if ((status != STATUS_PORT_DISCONNECTED) && + (status != STATUS_TIMEOUT)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvScanInUser: Failed to FltSendMessage.\n, 0x%08x\n", + status) ); + } + goto Cleanup; + } + + scanCtx->ScanThreadId = scanThreadId; + + // + // Wait for an event that the scanner completes or aborts. + // + + status = FltCancellableWaitForSingleObject( &scanCtx->ScanCompleteNotification, + &timeout, + Data ); + + if (!NT_SUCCESS(status) || + (status == STATUS_TIMEOUT)) { + + // + // At this point we came out of the wait with an error. We are in one of the following conditions: + // + // 1. This thread is being terminated + // 2. The IO operation represented by Data was cancelled + // 3. If we are in user-mode scan mode, the communication to the user mode component timed out + // 4. If we are in user-mode scan mode, the user-mode component died and the wait timed out. + // + + NTSTATUS statusAbort = STATUS_SUCCESS; + // + // Notify the user scan thread to abort the scan. + // + statusAbort = AvSendAbortToUser(scanCtx->ScanThreadId, + scanCtx->ScanId); + if (NT_SUCCESS(statusAbort) && + (statusAbort != STATUS_TIMEOUT)) { + + LARGE_INTEGER timeoutForAbortComplete = {0}; + timeoutForAbortComplete.QuadPart = - 1000 * (LONGLONG)_1ms; // 1s + // + // Wait again on completion notification. + // The scan thread should close the section very soon because we have already notified + // the scan thread to abort the task. + // + statusAbort = FltCancellableWaitForSingleObject( + &scanCtx->ScanCompleteNotification, + &timeoutForAbortComplete, + NULL ); + } + // + // If send abortion failed or wait failed, which general means the service is dead, + // we have to close section context/handle here by ourself. + // + if (!NT_SUCCESS(statusAbort) || + (statusAbort == STATUS_TIMEOUT)) { + + scanCtx->IoWaitOnScanCompleteNotificationAborted = TRUE; + // + // If this thread who the race, it will close the section. + // + AvFinalizeScanAndSection(scanCtx); + } + } + + // + // If the wait for scan to complete is cancelled (e.g. by CancelSynchronousIo ) + // + if (!NT_SUCCESS(status) && + (IOMajorFunctionAtScan == IRP_MJ_CREATE)) { + + AvCancelFileOpen(Data, FltObjects, status); + } + +Cleanup: + + // + // Here scanCtx must be non-NULL because we checked it in the beginning. + // + + AvAcquireResourceExclusive( &Globals.ScanCtxListLock ); + RemoveEntryList (&scanCtx->List); + AvReleaseResource( &Globals.ScanCtxListLock ); + + AvReleaseScanContext( scanCtx ); + + return status; +} + + +NTSTATUS +AvCloseSectionForDataScan( + _Inout_ PAV_SECTION_CONTEXT SectionContext + ) +/*++ + +Routine Description + + A wrapper function that wraps FltCloseSectionForDataScan and performs appropriate cleanup. + +Arguments + + SectionContext - The seciton handle and object will be cleaned up in sectino context. + +Return Value + + Returns the status of this operation. + +--*/ +{ + // + // Synchronized with AvScanAbortCallbackAsync(...) + // + InterlockedExchangePointer( &SectionContext->ScanContext, NULL ); + ObDereferenceObject( SectionContext->SectionObject ); + + SectionContext->SectionHandle = NULL; + SectionContext->SectionObject = NULL; + return FltCloseSectionForDataScan( (PFLT_CONTEXT)SectionContext ); +} + diff --git a/filesys/miniFilter/avscan/filter/scan.h b/filesys/miniFilter/avscan/filter/scan.h new file mode 100644 index 00000000..5a18abdc --- /dev/null +++ b/filesys/miniFilter/avscan/filter/scan.h @@ -0,0 +1,65 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + scan.h + +Abstract: + + This module contains the scan interface for AV filter to call. + +Environment: + + Kernel mode + +--*/ +#ifndef __SCAN_H__ +#define __SCAN_H__ + +#include "avlib.h" + +typedef enum _AV_SCAN_MODE { + + // + // AvKernelMode indicates the scanning occurs in the kernel, while + // AvUserMode indicates the scanning happens in the user space. + // + + AvKernelMode, + AvUserMode + +} AV_SCAN_MODE; + +NTSTATUS +AvScanInKernel ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _In_ PAV_STREAM_CONTEXT StreamContext + ); + +NTSTATUS +AvScanInUser ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _In_ DEVICE_TYPE DeviceType + ); + +NTSTATUS +AvCreateSectionForDataScan ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Inout_ PAV_SECTION_CONTEXT SectionContext + ); + +NTSTATUS +AvCloseSectionForDataScan( + _Inout_ PAV_SECTION_CONTEXT SectionContext + ); + +#endif + diff --git a/filesys/miniFilter/avscan/filter/utility.c b/filesys/miniFilter/avscan/filter/utility.c new file mode 100644 index 00000000..7b51a739 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/utility.c @@ -0,0 +1,370 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + utility.c + +Abstract: + + Utility module implementation. + 1) Generic table routines + 2) Query file information routines + +Environment: + + Kernel mode + +--*/ + +#include "avscan.h" + +// +// Generic table routines. +// + +RTL_GENERIC_COMPARE_RESULTS +AvCompareEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ PVOID Lhs, + _In_ PVOID Rhs + ) +/*++ + +Routine Description: + + This routine is the callback for the generic table routines. + +Arguments: + + Table - Table for which this is invoked. + + FirstStruct - An element in the table to compare. + + SecondStruct - Another element in the table to compare. + +Return Value: + + RTL_GENERIC_COMPARE_RESULTS. + +--*/ +{ + PAV_GENERIC_TABLE_ENTRY lhs = (PAV_GENERIC_TABLE_ENTRY)Lhs; + PAV_GENERIC_TABLE_ENTRY rhs = (PAV_GENERIC_TABLE_ENTRY)Rhs; + + UNREFERENCED_PARAMETER (Table); + + // + // Compare the 128 bit fileId in 64bit pieces for efficiency. + // Compare the lower 64 bits Value first since that is used + // in both 128 bit and 64 bit fileIds and doing so eliminates + // and unnecessary comparison of the UpperZeros field in the + // most common case. Note this comparison is not equivalent + // to a memcmp on the 128 bit values but that doesn't matter + // here since we just need the tree to be self-consistent. + // + + if (lhs->FileId.FileId64.Value < rhs->FileId.FileId64.Value) { + + return GenericLessThan; + + } else if (lhs->FileId.FileId64.Value > rhs->FileId.FileId64.Value) { + + return GenericGreaterThan; + + } else if (lhs->FileId.FileId64.UpperZeroes < rhs->FileId.FileId64.UpperZeroes) { + + return GenericLessThan; + + } else if (lhs->FileId.FileId64.UpperZeroes > rhs->FileId.FileId64.UpperZeroes) { + + return GenericGreaterThan; + } + + return GenericEqual; +} + + +PVOID +NTAPI +AvAllocateGenericTableEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ CLONG ByteSize + ) +/*++ + +Routine Description: + + This routine is the callback for allocation for entries in the generic table. + +Arguments: + + Table - Table for which this is invoked. + + ByteSize - Amount of memory to allocate. + +Return Value: + + Pointer to allocated memory if successful, else NULL. + +--*/ +{ + + UNREFERENCED_PARAMETER (Table); + + return ExAllocatePoolWithTag(PagedPool, ByteSize, AV_TABLE_ENTRY_TAG); +} + +VOID +NTAPI +AvFreeGenericTableEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Entry + ) +/*++ + +Routine Description: + + This routine is the callback for releasing memory for entries in the generic + table. + +Arguments: + + Table - Table for which this is invoked. + + Entry - Entry to free. + +Return Value: + + None. + +--*/ +{ + + UNREFERENCED_PARAMETER (Table); + + ExFreePoolWithTag( Entry, AV_TABLE_ENTRY_TAG ); +} + +// +// Query File Information Routines +// + +NTSTATUS +AvGetFileId ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PAV_FILE_REFERENCE FileId + ) +/*++ + +Routine Description: + + This routine obtains the File ID and saves it in the stream context. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + pFileId - Pointer to file id. This is the output + +Return Value: + + Returns statuses forwarded from FltQueryInformationFile. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FLT_FILESYSTEM_TYPE type; + + // + // Querying for FileInternalInformation gives you the file ID. + // + + status = FltGetFileSystemType( Instance, &type ); + + if (NT_SUCCESS( status )) { + + if (type == FLT_FSTYPE_REFS) { + + FILE_ID_INFORMATION fileIdInformation; + + status = FltQueryInformationFile( Instance, + FileObject, + &fileIdInformation, + sizeof(FILE_ID_INFORMATION), + FileIdInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + RtlCopyMemory(&(FileId->FileId128), &(fileIdInformation.FileId), sizeof(FileId->FileId128) ); + } + + } else { + + FILE_INTERNAL_INFORMATION fileInternalInformation; + + status = FltQueryInformationFile( Instance, + FileObject, + &fileInternalInformation, + sizeof(FILE_INTERNAL_INFORMATION), + FileInternalInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + FileId->FileId64.Value = fileInternalInformation.IndexNumber.QuadPart; + FileId->FileId64.UpperZeroes = 0ll; + } + } + } + + return status; +} + +NTSTATUS +AvGetFileSize ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PLONGLONG Size + ) +/*++ + +Routine Description: + + This routine obtains the size. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + Size - Pointer to a LONGLONG indicating the file size. This is the output. + +Return Value: + + Returns statuses forwarded from FltQueryInformationFile. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FILE_STANDARD_INFORMATION standardInfo; + + // + // Querying for FileStandardInformation gives you the offset of EOF. + // + + status = FltQueryInformationFile( Instance, + FileObject, + &standardInfo, + sizeof(FILE_STANDARD_INFORMATION), + FileStandardInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + *Size = standardInfo.EndOfFile.QuadPart; + } + + return status; +} + +NTSTATUS +AvGetFileEncrypted ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PBOOLEAN Encrypted + ) +/*++ + +Routine Description: + + This routine obtains the File ID and saves it in the stream context. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + Encrypted - Pointer to a boolean indicating if this file is encrypted or not. This is the output. + +Return Value: + + Returns statuses forwarded from FltQueryInformationFile. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FILE_BASIC_INFORMATION basicInfo; + + // + // Querying for basic information to get encryption. + // + + status = FltQueryInformationFile( Instance, + FileObject, + &basicInfo, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + *Encrypted = BooleanFlagOn( basicInfo.FileAttributes, FILE_ATTRIBUTE_ENCRYPTED ); + } + + return status; +} + +LONG +AvExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointer, + _In_ BOOLEAN AccessingUserBuffer + ) +/*++ + +Routine Description: + + Exception filter to catch errors touching user buffers. + +Arguments: + + ExceptionPointer - The exception record. + + AccessingUserBuffer - If TRUE, overrides FsRtlIsNtStatusExpected to allow + the caller to munge the error to a desired status. + +Return Value: + + EXCEPTION_EXECUTE_HANDLER - If the exception handler should be run. + + EXCEPTION_CONTINUE_SEARCH - If a higher exception handler should take care of + this exception. + +--*/ +{ + NTSTATUS Status; + + Status = ExceptionPointer->ExceptionRecord->ExceptionCode; + + // + // Certain exceptions shouldn't be dismissed within the filter + // unless we're touching user memory. + // + + if (!FsRtlIsNtstatusExpected( Status ) && + !AccessingUserBuffer) { + + return EXCEPTION_CONTINUE_SEARCH; + } + + return EXCEPTION_EXECUTE_HANDLER; +} + + diff --git a/filesys/miniFilter/avscan/filter/utility.h b/filesys/miniFilter/avscan/filter/utility.h new file mode 100644 index 00000000..97447665 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/utility.h @@ -0,0 +1,252 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + utility.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. The functions include + generic table routines. + +Environment: + + Kernel mode + +--*/ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + +#define AV_STRING_TAG 'tSvA' +#define AV_RESOURCE_TAG 'cRvA' +#define AV_KEVENT_TAG 'eKvA' +#define AV_TABLE_ENTRY_TAG 'eTvA' + +////////////////////////////////////////////////////////////////////////////// +// ReFS Compatibility Helpers // +////////////////////////////////////////////////////////////////////////////// + +// +// This helps us deal with ReFS 128-bit file IDs and NTFS 64-bit file IDs. +// + +#define AV_INVALID_FILE_REFERENCE( _fileid_ ) \ + (((_fileid_).FileId64.UpperZeroes == 0ll) && \ + ((_fileid_).FileId64.Value == (ULONGLONG)FILE_INVALID_FILE_ID)) + +#define AV_SET_INVALID_FILE_REFERENCE( _fileid_ ) \ + (_fileid_).FileId64.UpperZeroes = 0ll;\ + (_fileid_).FileId64.Value = (ULONGLONG)FILE_INVALID_FILE_ID; + +typedef union _AV_FILE_REFERENCE { + + struct { + ULONGLONG Value; + ULONGLONG UpperZeroes; + } FileId64; + + FILE_ID_128 FileId128; + +} AV_FILE_REFERENCE, *PAV_FILE_REFERENCE; + + +// +// The generic table entry data structure. +// + +typedef struct _AV_GENERIC_TABLE_ENTRY { + + AV_FILE_REFERENCE FileId; + ULONG InfectedState; + + // + // Revision numbers for files on CSVFS + // + LONGLONG VolumeRevision; + LONGLONG CacheRevision; + LONGLONG FileRevision; + +} AV_GENERIC_TABLE_ENTRY, *PAV_GENERIC_TABLE_ENTRY; + +#define AV_GENERIC_TABLE_ENTRY_SIZE sizeof( AV_GENERIC_TABLE_ENTRY ) + +/* +_IRQL_requires_same_ +_Function_class_(RTL_GENERIC_COMPARE_ROUTINE) +RTL_GENERIC_COMPARE_RESULTS +AvCompareEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ PVOID FirstStruct, + _In_ PVOID SecondStruct + ); + +_IRQL_requires_same_ +__drv_allocatesMem(Mem) +_Function_class_(RTL_GENERIC_ALLOCATE_ROUTINE) +PVOID +NTAPI +AvAllocateGenericTableEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ CLONG ByteSize + ); + +_IRQL_requires_same_ +_Function_class_(RTL_GENERIC_FREE_ROUTINE) +VOID +NTAPI +AvFreeGenericTableEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Entry + ); +*/ + +RTL_GENERIC_COMPARE_ROUTINE AvCompareEntry; + +RTL_GENERIC_ALLOCATE_ROUTINE AvAllocateGenericTableEntry; + +RTL_GENERIC_FREE_ROUTINE AvFreeGenericTableEntry; + +// +// NTFS supports a file state cache. Since CSVFS is built on top of +// NTFS, it can also support the cache. +// +#define FS_SUPPORTS_FILE_STATE_CACHE(VolumeFilesystemType) \ + ( ((VolumeFilesystemType) == FLT_FSTYPE_NTFS) || \ + ((VolumeFilesystemType) == FLT_FSTYPE_CSVFS) || \ + ((VolumeFilesystemType) == FLT_FSTYPE_REFS) ) + + +FORCEINLINE +PERESOURCE +AvAllocateResource ( + VOID + ) +{ + // + // eResource by its rule has to be in the non-paged pool + // NonPagedPoolNx: non-executable non-paged pool + // + + return ExAllocatePoolWithTag( NonPagedPoolNx, + sizeof( ERESOURCE ), + AV_RESOURCE_TAG ); +} + +FORCEINLINE +VOID +AvFreeResource ( + _In_ PERESOURCE Resource + ) +{ + + ExFreePoolWithTag( Resource, + AV_RESOURCE_TAG ); +} + +FORCEINLINE +PKEVENT +AvAllocateKevent ( + VOID + ) +{ + // + // KEVENT has to be in the non-paged pool + // + + return ExAllocatePoolWithTag( NonPagedPoolNx, + sizeof( KEVENT ), + AV_KEVENT_TAG ); +} + +FORCEINLINE +VOID +AvFreeKevent ( + _In_ PKEVENT Event + ) +{ + + ExFreePoolWithTag( Event, + AV_KEVENT_TAG ); +} + +NTSTATUS +AvGetFileId ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PAV_FILE_REFERENCE FileId + ); + +NTSTATUS +AvGetFileSize ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PLONGLONG Size + ); + +NTSTATUS +AvGetFileEncrypted ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PBOOLEAN Encrypted + ); + +LONG +AvExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointer, + _In_ BOOLEAN AccessingUserBuffer + ); + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +AvAcquireResourceExclusive ( + _Inout_ _Acquires_exclusive_lock_(*Resource) PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + !ExIsResourceAcquiredSharedLite(Resource)); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +AvAcquireResourceShared ( + _Inout_ _Acquires_shared_lock_(*Resource) PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceSharedLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Releases_lock_(_Global_critical_region_) +_Requires_lock_held_(_Global_critical_region_) +AvReleaseResource ( + _Inout_ _Requires_lock_held_(*Resource) _Releases_lock_(*Resource) PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + ExIsResourceAcquiredSharedLite(Resource)); + + ExReleaseResourceLite(Resource); + KeLeaveCriticalRegion(); +} + +#define LIST_FOR_EACH_SAFE(curr, n, head) \ + for (curr = (head)->Flink , n = curr->Flink ; curr != (head); \ + curr = n, n = curr->Flink ) + +#endif + diff --git a/filesys/miniFilter/avscan/inc/avlib.h b/filesys/miniFilter/avscan/inc/avlib.h new file mode 100644 index 00000000..a5d5ece6 --- /dev/null +++ b/filesys/miniFilter/avscan/inc/avlib.h @@ -0,0 +1,201 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + avlib.h + +Abstract: + + This header file defines the common data structure used by kernel and user. + +Environment: + + User mode + Kernel mode + +--*/ + +#ifndef __AVLIB_H__ +#define __AVLIB_H__ + +#if defined(_MSC_VER) +#if (_MSC_VER >= 1200) +#pragma warning(push) +#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union +#endif +#endif + +// +// Name of AV filter server ports +// + +#define AV_SCAN_PORT_NAME L"\\MicrosoftAvSampleFilterScanPort" +#define AV_ABORT_PORT_NAME L"\\MicrosoftAvSampleFilterAbortPort" +#define AV_QUERY_PORT_NAME L"\\MicrosoftAvSampleFilterQueryPort" + + +// +// Definition of invalide section handle for data scan +// + +#define AV_INVALID_SECTION_HANDLE ((HANDLE)((LONG_PTR)(-1))) + + +// +// Command type enumeration, please see COMMAND_MESSAGE below +// + +typedef enum _AVSCAN_COMMAND { + + AvIsFileModified, + AvCmdCreateSectionForDataScan, + AvCmdCloseSectionForDataScan + +} AVSCAN_COMMAND; + +// +// Message type enumeration, please see AV_SCANNER_NOTIFICATION below +// + +typedef enum _AVSCAN_MESSAGE { + + AvMsgStartScanning, + AvMsgAbortScanning, + AvMsgFilterUnloading + +} AVSCAN_MESSAGE; + +typedef enum _AVSCAN_REASON { + AvScanOnOpen, + AvScanOnCleanup + +} AVSCAN_REASON; + +typedef enum _AVSCAN_RESULT { + + AvScanResultUndetermined, + AvScanResultInfected, + AvScanResultClean + +} AVSCAN_RESULT; + +// +// Defines the commands between the user program and the filter +// Command: User -> Kernel +// + +typedef struct _COMMAND_MESSAGE { + + // + // Command type + // + + AVSCAN_COMMAND Command; + + // + // Scan identifier. + // This argument will be checked in message notificaiton callback. + // + + LONGLONG ScanId; + + // + // Scan thread id. This id will be used in cancel message passing. + // So that we will know which scan thread to cancel. + // + + ULONG ScanThreadId; + + union { + + // + // When user program is connecting for query (AvConnectForQuery) + // it has to pass the file handle to query the status of the file. + // Valid when Command == AvIsFileModified + // + + HANDLE FileHandle; + + // + // The result result. + // Valid when Command == AvCmdCloseSectionForDataScan + // + AVSCAN_RESULT ScanResult; + }; + +} COMMAND_MESSAGE, *PCOMMAND_MESSAGE; + +// +// Message: Kernel -> User Message +// + +typedef struct _SCANNER_NOTIFICATION { + + // + // Message type + // + + AVSCAN_MESSAGE Message; + + // + // Reason + // + + AVSCAN_REASON Reason; + + // + // Scan identifier. + // This argument will be checked in message notificaiton callback. + // + + LONGLONG ScanId; + + // + // Scan thread id. This id will be used in cancel message passing. + // So that we will know which scan thread to cancel. + // + + ULONG ScanThreadId; + +} AV_SCANNER_NOTIFICATION, *PAV_SCANNER_NOTIFICATION; + +// +// Connection type enumeration. It would be mainly used in connection context. +// + +typedef enum _AVSCAN_CONNECTION_TYPE { + + AvConnectForScan = 1, + AvConnectForAbort, + AvConnectForQuery + +} AVSCAN_CONNECTION_TYPE, *PAVSCAN_CONNECTION_TYPE; + +// +// Connection context. It will be passed through FilterConnectCommunicationPort(...) +// + +typedef struct _AV_CONNECTION_CONTEXT { + + AVSCAN_CONNECTION_TYPE Type; + +} AV_CONNECTION_CONTEXT, *PAV_CONNECTION_CONTEXT; + +// +// The following string is actully "message to be found" +// + +#define AV_DEFAULT_SEARCH_PATTERN "7?));=?z.5z8?z<5/4>" +#define AV_DEFAULT_SEARCH_PATTERN_SIZE sizeof(AV_DEFAULT_SEARCH_PATTERN) +#define AV_DEFAULT_PATTERN_XOR_KEY 90 + +#if defined(_MSC_VER) +#if (_MSC_VER >= 1200) +#pragma warning(pop) +#endif +#endif + +#endif + diff --git a/filesys/miniFilter/avscan/user/avscan.c b/filesys/miniFilter/avscan/user/avscan.c new file mode 100644 index 00000000..1b0e7b57 --- /dev/null +++ b/filesys/miniFilter/avscan/user/avscan.c @@ -0,0 +1,101 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + avscan.c + +Abstract: + + The user space anti-virus scanner. It is the entry point of + the user program. + + In its initialization, it forks scan listening threads and + run a couple of unit tests and wait for a user input. + + Before the user types 'q' to quit this program, the scan + threads will continue to work. + +Environment: + + User mode + +--*/ + +#include +#include +#include +#include "utility.h" +#include "avlib.h" +#include "userscan.h" + +int _cdecl +main ( + _Unreferenced_parameter_ int argc, + _Unreferenced_parameter_ char *argv[] + ) +/*++ + +Routine Description: + + Entry main function of the user space program. + +Arguments: + + argc - The number of arguments + argv - The arguments + +Return Value: + + 0 - No error occurs. + 255 - Error occurs. + +--*/ +{ + + UCHAR c; + HRESULT hr = S_OK; + USER_SCAN_CONTEXT userScanCtx = {0}; + + UNREFERENCED_PARAMETER( argc ); + UNREFERENCED_PARAMETER( argv ); + + + // + // Initialize scan listening threads. + // + + hr = UserScanInit(&userScanCtx); + if (FAILED(hr)) { + fprintf(stderr, "Failed to initialize user scan data\n"); + DisplayError( hr ); + return 255; + } + + // + // Read user's input until it reads 'q' + // + + for(;;) { + + printf("press 'q' to quit: "); + c = (unsigned char) getchar(); + if (c == 'q') { + + break; + } + } + + // + // Finalize the scan thread contexts. + // + + hr = UserScanFinalize(&userScanCtx); + if (FAILED(hr)) { + fprintf(stderr, "Failed to finalize the user scan data.\n"); + } + + return 0; +} + diff --git a/filesys/miniFilter/avscan/user/avscan.rc b/filesys/miniFilter/avscan/user/avscan.rc new file mode 100644 index 00000000..800467c8 --- /dev/null +++ b/filesys/miniFilter/avscan/user/avscan.rc @@ -0,0 +1,10 @@ +#include +#include + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "AvScan User Program" +#define VER_INTERNALNAME_STR "avscan.exe" +#define VER_ORIGINALFILENAME_STR "avscan.exe" + + diff --git a/filesys/miniFilter/avscan/user/avscan.vcxproj b/filesys/miniFilter/avscan/user/avscan.vcxproj new file mode 100644 index 00000000..7a3112b0 --- /dev/null +++ b/filesys/miniFilter/avscan/user/avscan.vcxproj @@ -0,0 +1,194 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {23D46B81-CF8D-48E5-BF28-3679E6106D7F} + $(MSBuildProjectName) + Debug + Win32 + {0D25D92A-41FF-4D6F-A937-A24EE3010777} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + avscan + + + avscan + + + avscan + + + avscan + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters b/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters new file mode 100644 index 00000000..5ca0df68 --- /dev/null +++ b/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters @@ -0,0 +1,33 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {7C20F6A5-EEFE-4E1E-ACA6-D81A9175944F} + + + h;hpp;hxx;hm;inl;inc;xsd + {A175AC1D-BDB6-4F3C-B19E-42160282E929} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {B2375B4F-9F4C-4FA5-9C19-76A963F2A7DA} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/avscan/user/userscan.c b/filesys/miniFilter/avscan/user/userscan.c new file mode 100644 index 00000000..71527823 --- /dev/null +++ b/filesys/miniFilter/avscan/user/userscan.c @@ -0,0 +1,1250 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + userscan.c + +Abstract: + + The implementation of user space scanning module. You have to install filter driver first, and + have filter manager load the minifilter. When filter driver is in its position, after calling + UserScanInit(...) all the subsequent CreateFile or CloseHandle would trigger the data scan if + the file is dirty. + + Before the user space scanner exit, it must call UserScanFinalize(...) to cleanup the data structure + and close the listening threads. + +Environment: + + User mode + +--*/ + +#include +#include +#include "userscan.h" +#include "utility.h" + +#define USER_SCAN_THREAD_COUNT 6 // the number of scanning worker threads. + +typedef struct _SCANNER_MESSAGE { + + // + // Required structure header. + // + + FILTER_MESSAGE_HEADER MessageHeader; + + // + // Private scanner-specific fields begin here. + // + + AV_SCANNER_NOTIFICATION Notification; + + // + // Overlapped structure: this is not really part of the message + // However we embed it here so that when we get pOvlp in + // GetQueuedCompletionStatus(...), we can restore the message + // via CONTAINING_RECORD macro. + // + + OVERLAPPED Ovlp; + +} SCANNER_MESSAGE, *PSCANNER_MESSAGE; + +#define SCANNER_MESSAGE_SIZE (sizeof(FILTER_MESSAGE_HEADER) + sizeof(AV_SCANNER_NOTIFICATION)) + +typedef struct _SCANNER_REPLY_MESSAGE { + + // + // Required structure header. + // + + FILTER_REPLY_HEADER ReplyHeader; + + // + // Private scanner-specific fields begin here. + // + + ULONG ThreadId; + +} SCANNER_REPLY_MESSAGE, *PSCANNER_REPLY_MESSAGE; + +#define SCANNER_REPLY_MESSAGE_SIZE (sizeof(FILTER_REPLY_HEADER) + sizeof(ULONG)) + +// +// Local routines +// + +AVSCAN_RESULT +UserScanMemoryStream( + _In_reads_bytes_(Size) PUCHAR StartingAddress, + _In_ SIZE_T Size, + _Inout_ PBOOLEAN pAbort + ); + +HRESULT +UserScanHandleStartScanMsg( + _In_ PUSER_SCAN_CONTEXT Context, + _In_ PSCANNER_MESSAGE Message, + _In_ PSCANNER_THREAD_CONTEXT ThreadCtx + ); + +HRESULT +UserScanWorker ( + _Inout_ PUSER_SCAN_CONTEXT Context + ); + +HRESULT +UserScanListenAbortProc ( + _Inout_ PUSER_SCAN_CONTEXT Context + ); + +DWORD +WaitForAll ( + _In_ PSCANNER_THREAD_CONTEXT ScanThreadCtxes + ); + +HRESULT +UserScanGetThreadContextById ( + _In_ DWORD ThreadId, + _In_ PUSER_SCAN_CONTEXT Context, + _Out_ PSCANNER_THREAD_CONTEXT *ScanThreadCtx + ); + +VOID +UserScanSynchronizedCancel ( + _In_ PUSER_SCAN_CONTEXT Context + ); + +HRESULT +UserScanClosePorts ( + _In_ PUSER_SCAN_CONTEXT Context + ); + +HRESULT +UserScanCleanup ( + _In_ PUSER_SCAN_CONTEXT Context + ); + +// +// Implementation of exported routines. +// Declared in userscan.h +// + +HRESULT +UserScanInit ( + _Inout_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine initializes all the necessary data structures and forks listening threads. + The caller thread is responsible for calling UserScanFinalize(...) to cleanup the + data structures and close the listening threads. + +Arguments: + + Context - User scan context, please see userscan.h + +Return Value: + + S_OK if successful. Otherwise, it returns a HRESULT error value. + +--*/ +{ + HRESULT hr = S_OK; + ULONG i = 0; + HANDLE hEvent = NULL; + PSCANNER_THREAD_CONTEXT scanThreadCtxes = NULL; + HANDLE hListenAbort = NULL; + AV_CONNECTION_CONTEXT connectionCtx = {0}; + + if (NULL == Context) { + + return MAKE_HRESULT(SEVERITY_ERROR, 0, E_POINTER); + } + + // + // Create the abort listening thead. + // This thread is particularly listening the abortion event. + // + + hListenAbort = CreateThread( NULL, + 0, + (LPTHREAD_START_ROUTINE)UserScanListenAbortProc, + Context, + CREATE_SUSPENDED, + NULL ); + + if (NULL == hListenAbort) { + + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Cleanup; + } + + // + // Initialize scan thread contexts. + // + + scanThreadCtxes = HeapAlloc(GetProcessHeap(), 0, sizeof(SCANNER_THREAD_CONTEXT) * USER_SCAN_THREAD_COUNT); + if (NULL == scanThreadCtxes) { + + hr = MAKE_HRESULT(SEVERITY_ERROR, 0, E_OUTOFMEMORY); + goto Cleanup; + } + + ZeroMemory(scanThreadCtxes, sizeof(SCANNER_THREAD_CONTEXT) * USER_SCAN_THREAD_COUNT); + + // + // Create scan listening threads. + // + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + scanThreadCtxes[i].Handle = CreateThread( NULL, + 0, + (LPTHREAD_START_ROUTINE)UserScanWorker, + Context, + CREATE_SUSPENDED, + &scanThreadCtxes[i].ThreadId ); + + if (NULL == scanThreadCtxes[i].Handle) { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Cleanup; + } + InitializeCriticalSection(&(scanThreadCtxes[i].Lock)); + } + + // + // Prepare the scan communication port. + // + + connectionCtx.Type = AvConnectForScan; + hr = FilterConnectCommunicationPort( AV_SCAN_PORT_NAME, + 0, + &connectionCtx, + sizeof(AV_CONNECTION_CONTEXT), + NULL, + &Context->ConnectionPort ); + if (FAILED(hr)) { + + Context->ConnectionPort = NULL; + goto Cleanup; + } + + // + // Create the IO completion port for asynchronous message passing. + // + + Context->Completion = CreateIoCompletionPort( Context->ConnectionPort, + NULL, + 0, + USER_SCAN_THREAD_COUNT ); + + if ( NULL == Context->Completion ) { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Cleanup; + } + + Context->ScanThreadCtxes = scanThreadCtxes; + Context->AbortThreadHandle = hListenAbort; + + // + // Resume all the scanning threads. + // + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + if ( ResumeThread( scanThreadCtxes[i].Handle ) == -1) { + + fprintf(stderr, "[UserScanInit]: ResumeThread scan listening thread failed.\n"); + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Cleanup; + } + } + + // + // Resume abort listening thread. + // + + if ( ResumeThread( hListenAbort ) == -1 ) { + fprintf(stderr, "[UserScanInit]: ResumeThread abort listening thread failed.\n"); + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Cleanup; + } + + // + // Pump messages into queue of completion port. + // + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + PSCANNER_MESSAGE msg = HeapAlloc( GetProcessHeap(), 0, sizeof( SCANNER_MESSAGE ) ); + + if (NULL == msg) { + + hr = MAKE_HRESULT(SEVERITY_ERROR, 0, E_OUTOFMEMORY); + goto Cleanup; + } + + FillMemory( &msg->Ovlp, sizeof(OVERLAPPED), 0); + hr = FilterGetMessage( Context->ConnectionPort, + &msg->MessageHeader, + FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ), + &msg->Ovlp ); + + if (hr == HRESULT_FROM_WIN32( ERROR_IO_PENDING )) { + + hr = S_OK; + + } else { + + fprintf(stderr, "[UserScanInit]: FilterGetMessage failed.\n"); + DisplayError(hr); + HeapFree(GetProcessHeap(), 0, msg ); + goto Cleanup; + } + } + + return hr; + +Cleanup: + + if (Context->Completion && !CloseHandle(Context->Completion)) { + + fprintf(stderr, "[UserScanInit] Error! Close completion port failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + if (Context->ConnectionPort && !CloseHandle(Context->ConnectionPort)) { + + fprintf(stderr, "[UserScanInit] Error! Close connection port failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + if (scanThreadCtxes) { + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + if (scanThreadCtxes[i].Handle && !CloseHandle(scanThreadCtxes[i].Handle)) { + + fprintf(stderr, "[UserScanInit] Error! Close scan thread failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + DeleteCriticalSection(&(scanThreadCtxes[i].Lock)); + } + HeapFree(GetProcessHeap(), 0, scanThreadCtxes); + } + if (hListenAbort && !CloseHandle(hListenAbort)) { + + fprintf(stderr, "[UserScanInit] Error! Close listen abort thread failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + if (hEvent && !CloseHandle(hEvent)) { + + fprintf(stderr, "[UserScanInit] Error! Close event handle failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + + return hr; +} + +HRESULT +UserScanFinalize ( + _In_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine cleans up all the necessary data structures and closes listening threads. + It does the following things: + 1) Cancel all the scanning threads and wait for them to terminate. + 2) Close all the thread handles + 3) Close all the port handles + 4) Free memory of scan thread contexts. + +Arguments: + + Context - User scan context, please see userscan.h + +Return Value: + + S_OK if successful. Otherwise, it returns a HRESULT error value. + +--*/ +{ + HRESULT hr = S_OK; + printf("=================finalize\n"); + + UserScanSynchronizedCancel( Context ); + + printf("[UserScanFinalize]: Closing connection port\n"); + + hr = UserScanCleanup( Context ); + + return hr; +} + + +// +// Implementation of local routines +// + +DWORD +WaitForAll ( + _In_ PSCANNER_THREAD_CONTEXT ScanThreadCtxes + ) +/*++ + +Routine Description: + + A local helper function that enable the caller to wair for all the scan threads. + +Arguments: + + ScanThreadCtxes - Scan thread contextes. + +Return Value: + + Please consult WaitForMultipleObjects(...) + +--*/ +{ + ULONG i = 0; + HANDLE hScanThreads[USER_SCAN_THREAD_COUNT] = {0}; + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + hScanThreads[i] = ScanThreadCtxes[i].Handle; + } + return WaitForMultipleObjects(USER_SCAN_THREAD_COUNT, hScanThreads, TRUE, INFINITE); +} + +HRESULT +UserScanGetThreadContextById ( + _In_ DWORD ThreadId, + _In_ PUSER_SCAN_CONTEXT Context, + _Out_ PSCANNER_THREAD_CONTEXT *ScanThreadCtx + ) +/*++ + +Routine Description: + + This routine search for the scan thread context by its thread id. + +Arguments: + + ThreadId - The thread id to be searched. + + Context - The user scan context. + + ScanThreadCtx - Output scan thread context. + +Return Value: + + S_OK if found, otherwise not found. + +--*/ +{ + HRESULT hr = S_OK; + ULONG i; + PSCANNER_THREAD_CONTEXT scanThreadCtx = Context->ScanThreadCtxes; + + *ScanThreadCtx = NULL; + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + if ( ThreadId == scanThreadCtx[i].ThreadId ) { + *ScanThreadCtx = (scanThreadCtx + i); + return hr; + } + } + return MAKE_HRESULT(SEVERITY_ERROR,0,E_FAIL); +} + +VOID +UserScanSynchronizedCancel ( + _In_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine tries to abort all the scanning threads and wait for them to terminate. + +Arguments: + + Context - User scan context, please see userscan.h + +Return Value: + + Please consult WaitForMultipleObjects(...) + +--*/ +{ + ULONG i; + PSCANNER_THREAD_CONTEXT scanThreadCtxes = Context->ScanThreadCtxes; + + if (NULL == scanThreadCtxes) { + fprintf(stderr, "Scan thread contexes are NOT suppoed to be NULL.\n"); + return; + } + + // + // Tell all scanning threads that the program is going to exit. + // + + Context->Finalized = TRUE; + + // + // Signal cancellation events for all scanning threads. + // + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + scanThreadCtxes[i].Aborted = TRUE; + } + + // + // Wake up the listening thread if it is waiting for message + // via GetQueuedCompletionStatus() + // + + CancelIoEx(Context->ConnectionPort, NULL); + + // + // Wait for all scan threads to complete cancellation, + // so we will be able to close the connection port and etc. + // + + WaitForAll(scanThreadCtxes); + + return; +} + +HRESULT +UserScanClosePorts ( + _In_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine cleans up all the necessary data structures and closes listening threads. + It does closing the scanning communication port and completion port. + +Arguments: + + Context - User scan context, please see userscan.h + +Return Value: + + S_OK if successful. Otherwise, it returns a HRESULT error value. + +--*/ +{ + HRESULT hr = S_OK; + if (!CloseHandle(Context->ConnectionPort)) { + fprintf(stderr, "[UserScanFinalize]: Failed to close the connection port.\n"); + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + Context->ConnectionPort = NULL; + + if (!CloseHandle(Context->Completion)) { + fprintf(stderr, "[UserScanFinalize]: Failed to close the completion port.\n"); + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + Context->Completion = NULL; + + return hr; +} + +HRESULT +UserScanCleanup ( + _In_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine cleans up all the necessary data structures and closes listening threads. + It does closing abort thread handle and all scanning threads. It also closes the ports + by calling UserScanClosePorts(...). + +Arguments: + + Context - User scan context, please see userscan.h + +Return Value: + + S_OK if successful. Otherwise, it returns a HRESULT error value. + +--*/ +{ + ULONG i = 0; + HRESULT hr = S_OK; + PSCANNER_THREAD_CONTEXT scanThreadCtxes = Context->ScanThreadCtxes; + + if (NULL == scanThreadCtxes) { + + fprintf(stderr, "Scan thread contexes are NOT suppoed to be NULL.\n"); + return E_POINTER; + } + + if (Context->AbortThreadHandle) { + + CloseHandle( Context->AbortThreadHandle ); + } + + hr = UserScanClosePorts( Context ); + + // + // Clean up scan thread contexts + // + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + if (scanThreadCtxes[i].Handle && !CloseHandle(scanThreadCtxes[i].Handle)) { + fprintf(stderr, "[UserScanInit] Error! Close scan thread failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + DeleteCriticalSection(&(scanThreadCtxes[i].Lock)); + } + HeapFree( GetProcessHeap(), 0, scanThreadCtxes ); + Context->ScanThreadCtxes = NULL; + return hr; +} + +AVSCAN_RESULT +UserScanMemoryStream( + _In_reads_bytes_(Size) PUCHAR StartingAddress, + _In_ SIZE_T Size, + _Inout_ PBOOLEAN pAbort + ) +/*++ + +Routine Description: + + This routine is a naive search for virus signiture. + Note that this function is by no means efficient and scalable, but + this is not the focus of this example. Thus, an anti-virus + vendor may want to focus on and expand this function. + + It will reset the abort flag if it is aborted. + +Arguments: + + StartingAddress - The starting address of the memory to be searched. + + Size - The size of the memory. + + pAbort - A pointer to a boolean that notifies the scanning should be canceled.. + + Infected - TRUE if this file is infected. FALSE, otherwise. + +Return Value: + + S_OK. + +--*/ +{ + ULONG i; + UCHAR targetString[AV_DEFAULT_SEARCH_PATTERN_SIZE] = {0}; + SIZE_T searchStringLength = AV_DEFAULT_SEARCH_PATTERN_SIZE-1; + ULONG ind; + PUCHAR p; + PUCHAR start = StartingAddress; + PUCHAR end = start + Size - searchStringLength; + + // + // Decode the target pattern. We could decode only once and cache it. + // + + CopyMemory( (PVOID) targetString, + AV_DEFAULT_SEARCH_PATTERN, + AV_DEFAULT_SEARCH_PATTERN_SIZE ); + + for (ind = 0; + ind < searchStringLength; + ind++) { + + targetString[ind] = ((UCHAR)targetString[ind]) ^ AV_DEFAULT_PATTERN_XOR_KEY; + } + targetString[searchStringLength] = '\0'; + + // + // Scan the memory stream for the target pattern. + // If not cancelled. + // + + for (p = start, i = 1; + p <= end ; + p++, i++) { + + // + // If (*pAbort == TRUE), then we abort the scanning in the loop. + // + + if ( *pAbort ) { + + *pAbort = FALSE; + return AvScanResultUndetermined; + } + + if ( !memcmp( p, targetString, searchStringLength )) { + + return AvScanResultInfected; + } + } + + return AvScanResultClean; +} + +HRESULT +UserScanHandleStartScanMsg( + _In_ PUSER_SCAN_CONTEXT Context, + _In_ PSCANNER_MESSAGE Message, + _In_ PSCANNER_THREAD_CONTEXT ThreadCtx + ) +/*++ + +Routine Description: + + After receiving the scan request from the kernel. This routine is + the main function that handle the scan request. + + This routine does not know which file it is scanning because it + does not need to know. + + Its main job includes: + + 1) Send message to the filter to create a section object. + 2) Map the view of the section. + 3) Scan the memory + 4) Send message to tell the filter the result of the scan + and close the section object. + +Arguments: + + Context - The user scan context. + + Message - The message recieved from the kernel. + + ThreadCtx - The scan thread context. + +Return Value: + + S_OK. + +--*/ +{ + HRESULT hr = S_OK; + ULONG bytesReturned = 0; + HANDLE sectionHandle = NULL; + DWORD dwErrCode = 0; + PVOID scanAddress = NULL; + MEMORY_BASIC_INFORMATION memoryInfo; + PAV_SCANNER_NOTIFICATION notification = &Message->Notification; + COMMAND_MESSAGE commandMessage = {0}; + DWORD flags = 0; + + // + // Send the message to the filter to create a section object for data scan. + // If success, we would get section handle. + // + // We just have to transparently pass ScanContextId to filter, which we + // obtained from the filter previously. + // + + commandMessage.Command = AvCmdCreateSectionForDataScan; + commandMessage.ScanId = notification->ScanId; + commandMessage.ScanThreadId = ThreadCtx->ThreadId; + + hr = FilterSendMessage( Context->ConnectionPort, + &commandMessage, + sizeof( COMMAND_MESSAGE ), + §ionHandle, + sizeof( HANDLE ), + &bytesReturned ); + + if (FAILED(hr)) { + + fprintf(stderr, + "[UserScanHandleStartScanMsg]: Failed to send message SendMessageToCreateSection to the minifilter.\n"); + DisplayError(hr); + return hr; + } + + scanAddress = MapViewOfFile( sectionHandle, + FILE_MAP_READ, + 0L, + 0L, + 0 ); + if (scanAddress == NULL) { + fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to map the view.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + goto Cleanup; + } + + if( !VirtualQuery( scanAddress, &memoryInfo, sizeof(memoryInfo) )) { + fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to query the view.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + goto Cleanup; + } + + // + // Data scan here. + // + + commandMessage.ScanResult = UserScanMemoryStream( (PUCHAR)scanAddress, + memoryInfo.RegionSize, + &ThreadCtx->Aborted ); + + // + // If scanning on file open, give the pages a transient boost + // since they may soon be accessed in read operations on the + // file. + // + + if (notification->Reason == AvScanOnOpen) { + flags = MEM_UNMAP_WITH_TRANSIENT_BOOST; + } + +Cleanup: + + if (scanAddress != NULL) { + + if (!UnmapViewOfFileEx( scanAddress, flags )) { + + fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to unmap the view.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + } + + // + // We have to close the section handle after we finish using it. + // It is required to close the section handle here in user mode. + // + + if (!CloseHandle(sectionHandle)) { + + fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to close the section handle.\n"); + DisplayError(HRESULT_FROM_WIN32(dwErrCode)); + } + + // + // Send the message to tell filter to close the section object. + // This call will set the file clean or infected depending on the scan result, and + // also trigger events and release the waiting I/O request thread. + // + + commandMessage.Command = AvCmdCloseSectionForDataScan; + hr = FilterSendMessage( Context->ConnectionPort, + &commandMessage, + sizeof( COMMAND_MESSAGE ), + NULL, + 0, + &bytesReturned ); + if (FAILED(hr)) { + + fprintf(stderr, + "[UserScanHandleStartScanMsg]: Failed to close message SendMessageToCreateSection to the minifilter.\n"); + DisplayError( hr ); + return hr; + } + + return hr; +} + +HRESULT +UserScanWorker ( + _Inout_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine is the scanning worker thread procedure. + The pseudo-code of this function is as follows, + + while(TRUE) { + 1) Get a overlap structure from the completion port. + 2) Obtain message from overlap structure. + 3) Process the message via calling UserScanHandleStartScanMsg(...) + 4) Pump overlap structure into completion port using FilterGetMessage(...) + } + +Arguments: + + Context - The user scan context. + +Return Value: + + S_OK if no error occurs; Otherwise, it would return appropriate HRESULT. + +--*/ +{ + HRESULT hr = S_OK; + + PSCANNER_MESSAGE message = NULL; + SCANNER_REPLY_MESSAGE replyMsg; + LPOVERLAPPED pOvlp = NULL; + + DWORD outSize; + ULONG_PTR key; + BOOL success = FALSE; + + PSCANNER_THREAD_CONTEXT threadCtx = NULL; + + hr = UserScanGetThreadContextById( GetCurrentThreadId(), Context, &threadCtx ); + if (FAILED(hr)) { + fprintf(stderr, + "[UserScanWorker]: Failed to get thread context.\n"); + return hr; + } + + ZeroMemory( &replyMsg, SCANNER_REPLY_MESSAGE_SIZE ); + + printf("Current thread handle %p, id:%u\n", threadCtx->Handle, threadCtx->ThreadId); + + // + // This thread is waiting for scan message from the driver + // + + for(;;) { + + message = NULL; + + // + // Get overlapped structure asynchronously, the overlapped structure + // was previously pumped by FilterGetMessage(...) + // + + success = GetQueuedCompletionStatus( Context->Completion, &outSize, &key, &pOvlp, INFINITE ); + + if (!success) { + + hr = HRESULT_FROM_WIN32(GetLastError()); + + // + // The completion port handle associated with it is closed + // while the call is outstanding, the function returns FALSE, + // *lpOverlapped will be NULL, and GetLastError will return ERROR_ABANDONED_WAIT_0 + // + + if (hr == E_HANDLE) { + + printf("Completion port becomes unavailable.\n"); + hr = S_OK; + + } else if (hr == HRESULT_FROM_WIN32(ERROR_ABANDONED_WAIT_0)) { + + printf("Completion port was closed.\n"); + hr = S_OK; + } + + break; + } + + // + // Recover message strcuture from overlapped structure. + // Remember we embedded overlapped structure inside SCANNER_MESSAGE. + // This is because the overlapped structure obtained from GetQueuedCompletionStatus(...) + // is asynchronously and not guranteed in order. + // + + message = CONTAINING_RECORD( pOvlp, SCANNER_MESSAGE, Ovlp ); + + if (AvMsgStartScanning == message->Notification.Message) { + + // + // Reset the abort flag since this is a new scan request and remember + // the scan context ID. This ID will allow us to match a cancel request + // with a given scan task. + // + + EnterCriticalSection(&(threadCtx->Lock)); + threadCtx->Aborted = FALSE; + threadCtx->ScanId = message->Notification.ScanId; + LeaveCriticalSection(&(threadCtx->Lock)); + + // + // Reply the scanning worker thread handle to the filter + // This is important because the filter will also wait for the scanning thread + // in case that the scanning thread is killed before telling filter + // the scan is done or aborted. + // + + ZeroMemory( &replyMsg, SCANNER_REPLY_MESSAGE_SIZE ); + replyMsg.ReplyHeader.MessageId = message->MessageHeader.MessageId; + replyMsg.ThreadId = threadCtx->ThreadId; + hr = FilterReplyMessage( Context->ConnectionPort, + &replyMsg.ReplyHeader, + SCANNER_REPLY_MESSAGE_SIZE ); + + if (FAILED(hr)) { + + fprintf(stderr, + "[UserScanWorker]: Failed to reply thread handle to the minifilter\n"); + DisplayError(hr); + break; + } + hr = UserScanHandleStartScanMsg( Context, message, threadCtx ); + + } else { + + assert( FALSE ); // This thread should not receive other kinds of message. + } + + + if (FAILED(hr)) { + + fprintf(stderr, + "[UserScanWorker]: Failed to handle the message.\n"); + } + + // + // If fianlized flag is set from main thread, + // then it would break the while loop. + // + + if (Context->Finalized) { + + break; + } + + // + // After we process the message, pump a overlapped structure into completion port again. + // + + hr = FilterGetMessage( Context->ConnectionPort, + &message->MessageHeader, + FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ), + &message->Ovlp ); + + if (hr == HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)) { + + printf("FilterGetMessage aborted.\n"); + break; + + } else if (hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING )) { + + fprintf(stderr, + "[UserScanWorker]: Failed to get message from the minifilter. \n0x%x, 0x%x\n", + hr, HRESULT_FROM_WIN32(GetLastError())); + DisplayError(hr); + break; + } + + } // end of while(TRUE) + + if (message) { + + // + // Free the memory, which originally allocated at UserScanInit(...) + // + + HeapFree(GetProcessHeap(), 0, message); + } + + printf("***Thread id %u exiting\n", threadCtx->ThreadId); + + return hr; +} + +HRESULT +UserScanListenAbortProc ( + _Inout_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine is the abort listening thread procedure. + This thread is particularly listening the abortion notifcation from the filter. + The pseudo-code of this function is as follows, + + while(TRUE) { + 1) Wair for and get a message from the filter via FilterGetMessage(...) + 2) Find the scan thread context by its thread id. + 3) Set the cancel flag to be TRUE. + } + +Arguments: + + Context - The user scan context. + +Return Value: + + S_OK if no error occurs; Otherwise, it would return appropriate HRESULT. + +--*/ +{ + HRESULT hr = S_OK; + HANDLE abortPort = NULL; // A port for listening the abort notification from driver. + SCANNER_MESSAGE message; + DWORD dwThisThread = GetCurrentThreadId(); + SCANNER_REPLY_MESSAGE replyMsg; + AV_CONNECTION_CONTEXT connectionCtx = {0}; + PSCANNER_THREAD_CONTEXT threadCtx = NULL; + + ZeroMemory( &message, SCANNER_MESSAGE_SIZE ); + + // + // Prepare the abort communication port. + // + + connectionCtx.Type = AvConnectForAbort; + hr = FilterConnectCommunicationPort( AV_ABORT_PORT_NAME, + 0, + &connectionCtx, + sizeof(AV_CONNECTION_CONTEXT), + NULL, + &abortPort ); + if (FAILED(hr)) { + + abortPort = NULL; + return hr; + } + + // + // This thread is listening an scan abortion notifcation or filter unloading from the kernel + // If it receives notification, its type must be AvMsgAbortScanning or AvMsgFilterUnloading + // + + for(;;) { + + // + // Wait until an abort command is sent from filter. + // + + hr = FilterGetMessage( abortPort, + &message.MessageHeader, + SCANNER_MESSAGE_SIZE, + NULL ); + + if (hr == HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)) { + + printf("[UserScanListenAbortProc]: FilterGetMessage aborted.\n"); + hr = S_OK; + break; + + } else if (FAILED(hr)) { + + fprintf(stderr, + "[UserScanListenAbortProc]: Failed to get message from the minifilter.\n" ); + DisplayError(hr); + continue; + } + + printf("[UserScanListenAbortProc]: Got message %llu. \n", message.MessageHeader.MessageId); + + if (AvMsgAbortScanning == message.Notification.Message) { + + // + // After this thread receives AvMsgAbortScanning + // it does + // 1) Find the user scan thread context + // 2) Set Aborted flag to be TRUE + // + + hr = UserScanGetThreadContextById(message.Notification.ScanThreadId, + Context, + &threadCtx); + if (SUCCEEDED(hr)) { + + printf("[UserScanListenAbortProc]: User Set AvMsgAbortScanning\n"); + + // + // Without critical section here, we cannot prevent the scanner thread from + // proceeding to the next task after we check the Id and before we set the abort flag. + // In short, without critical section, it will be a TOCTTOU bug. + // + EnterCriticalSection(&(threadCtx->Lock)); + if (threadCtx->ScanId == message.Notification.ScanId) { + + threadCtx->Aborted = TRUE; + printf("[UserScanListenAbortProc]: %lld aborted\n", message.Notification.ScanId); + } else { + + printf("[UserScanListenAbortProc]: tried to abort %lld, but current scan in this thread is %lld\n", + message.Notification.ScanId, + threadCtx->ScanId); + } + LeaveCriticalSection(&(threadCtx->Lock)); + + } else { + + fprintf(stderr, "[UserScanListenAbortProc]: Error! UserScanGetThreadContextById failed.\n"); + } + + } else if (AvMsgFilterUnloading == message.Notification.Message) { + + // + // After this thread receives AvMsgFilterUnloading + // it does + // 1) Cancell all the scanning threads + // 2) Wait for them to finish the cancel. + // 3) Reply to filter so that the filter can know it can close the server ports. + // 4) Close scan port, completion port, and abortion port. + // 5) Exit the process + // + + UserScanSynchronizedCancel( Context ); + printf("The filter is unloading, exit!\n"); + ZeroMemory( &replyMsg, SCANNER_REPLY_MESSAGE_SIZE ); + replyMsg.ReplyHeader.MessageId = message.MessageHeader.MessageId; + replyMsg.ThreadId = dwThisThread; + hr = FilterReplyMessage( abortPort, + &replyMsg.ReplyHeader, + SCANNER_REPLY_MESSAGE_SIZE ); + + if (FAILED(hr)) { + + fprintf(stderr, "[UserScanListenAbortProc]: Error! FilterReplyMessage failed.\n"); + } + + UserScanClosePorts( Context ); + CloseHandle( abortPort ); + ExitProcess( 0 ); + break; + + } else { + + assert( FALSE ); // This thread should not receive other kinds of message. + } + + if (FAILED(hr)) { + + fprintf(stderr, "[UserScanListenAbortProc]: Failed to handle the message.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + } // end of while(TRUE) + + if (!CloseHandle(abortPort)) { + + fprintf(stderr, "[UserScanListenAbortProc]: Failed to close the connection port.\n"); + } + abortPort = NULL; + + return hr; +} + diff --git a/filesys/miniFilter/avscan/user/userscan.h b/filesys/miniFilter/avscan/user/userscan.h new file mode 100644 index 00000000..6666e6d9 --- /dev/null +++ b/filesys/miniFilter/avscan/user/userscan.h @@ -0,0 +1,109 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + userscan.h + +Abstract: + + The scanning module. This module defines the thread contexts, + and user scan contexts, and the definitions of functions. + +Environment: + + User mode + +--*/ + +#ifndef __USERSCAN_H__ +#define __USERSCAN_H__ + +#include +#include +#include "avlib.h" + +#ifndef MAKE_HRESULT +#define MAKE_HRESULT(sev,fac,code) \ + ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) ) +#endif + +typedef struct _SCANNER_THREAD_CONTEXT { + + // + // Threand Handle + // + + HANDLE Handle; + + // + // Threand Id + // + + DWORD ThreadId; + + // + // We need to remember scan id to know which task to abort. + // + + LONGLONG ScanId; + + // + // A flag that indicates that if this scan thread has received cancel callback from the driver + // + + BOOLEAN Aborted; + + // + // A critical section that synchronize the read/write of ScanId and Aborted. + // + + CRITICAL_SECTION Lock; + +} SCANNER_THREAD_CONTEXT, *PSCANNER_THREAD_CONTEXT; + +typedef struct _USER_SCAN_CONTEXT { + + // + // Scan thread contexts + // + + PSCANNER_THREAD_CONTEXT ScanThreadCtxes; + + // + // The abortion thread handle + // + + HANDLE AbortThreadHandle; + + // + // Finalize flag, set at UserScanFinalize(...) + // + + BOOLEAN Finalized; + + // + // Handle of connection port to the filter. + // + + HANDLE ConnectionPort; + + // + // Completion port for asynchronous message passing + // + + HANDLE Completion; + +} USER_SCAN_CONTEXT, *PUSER_SCAN_CONTEXT; + +HRESULT UserScanInit ( + _Inout_ PUSER_SCAN_CONTEXT Context + ); + +HRESULT UserScanFinalize ( + _In_ PUSER_SCAN_CONTEXT Context + ); + +#endif + diff --git a/filesys/miniFilter/avscan/user/utility.c b/filesys/miniFilter/avscan/user/utility.c new file mode 100644 index 00000000..c5321f64 --- /dev/null +++ b/filesys/miniFilter/avscan/user/utility.c @@ -0,0 +1,126 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + utility.c + +Abstract: + + The commonly used routine by the user program. + It will display the HRESULT in a formated message. + +Environment: + + User mode + +--*/ + +#include +#include +#include +#include "utility.h" + +VOID +DisplayError ( + _In_ DWORD Code + ) + +/*++ + +Routine Description: + + This routine will display an error message based off of the Win32 error + code that is passed in. This allows the user to see an understandable + error message instead of just the code. + +Arguments: + + Code - The error code to be translated. + +Return Value: + + None. + +--*/ + +{ + _Null_terminated_ WCHAR buffer[MAX_PATH] = { 0 }; + DWORD count; + HMODULE module = NULL; + HRESULT status; + + count = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + Code, + 0, + buffer, + sizeof(buffer) / sizeof(WCHAR), + NULL); + + + if (count == 0) { + + count = GetSystemDirectory( buffer, + sizeof(buffer) / sizeof( WCHAR ) ); + + if (count==0 || count > sizeof(buffer) / sizeof( WCHAR )) { + + // + // In practice we expect buffer to be large enough to hold the + // system directory path. + // + + printf(" Could not translate error: %u\n", Code); + return; + } + + + status = StringCchCat( buffer, + sizeof(buffer) / sizeof( WCHAR ), + L"\\fltlib.dll" ); + + if (status != S_OK) { + + printf(" Could not translate error: %u\n", Code); + return; + } + + module = LoadLibraryExW( buffer, NULL, LOAD_LIBRARY_AS_DATAFILE ); + + // + // Translate the Win32 error code into a useful message. + // + + count = FormatMessage (FORMAT_MESSAGE_FROM_HMODULE, + module, + Code, + 0, + buffer, + sizeof(buffer) / sizeof(WCHAR), + NULL); + + if (module != NULL) { + + FreeLibrary( module ); + } + + // + // If we still couldn't resolve the message, generate a string + // + + if (count == 0) { + + printf(" Could not translate error: %u\n", Code); + return; + } + } + + // + // Display the translated error. + // + + printf(" %ws\n", buffer); +} + diff --git a/filesys/miniFilter/avscan/user/utility.h b/filesys/miniFilter/avscan/user/utility.h new file mode 100644 index 00000000..5dcc2dbe --- /dev/null +++ b/filesys/miniFilter/avscan/user/utility.h @@ -0,0 +1,30 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + utility.h + +Abstract: + + The header of commonly used routine by the user program. + +Environment: + + User mode + +--*/ + +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + +#include + +VOID +DisplayError ( + _In_ DWORD Code + ); + +#endif + diff --git a/filesys/miniFilter/cancelSafe/ReadMe.md b/filesys/miniFilter/cancelSafe/ReadMe.md new file mode 100644 index 00000000..786b331f --- /dev/null +++ b/filesys/miniFilter/cancelSafe/ReadMe.md @@ -0,0 +1,14 @@ +CancelSafe File System Minifilter Driver +======================================== + +The CancelSafe filter is a sample minifilter that you use if you want to use cancel-safe queues. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *CancelSafe* minifilter initializes a cancel-safe queue when it is attached to a volume. When the minifilter is deployed, it monitors read operations that are passing through the I/O stack. If the read operation is being performed on a file named csqdemo.txt, it is queued onto the cancel-safe queue. Queued operations are completed after a brief pause through a separate worker thread that is running in system context. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.c b/filesys/miniFilter/cancelSafe/cancelSafe.c new file mode 100644 index 00000000..202803ef --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.c @@ -0,0 +1,1932 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + cancelSafe.c + +Abstract: + + This is the main module of the cancelSafe miniFilter driver. + +Environment: + + Kernel mode + +--*/ + +#include +#include +#include + + +// +// Debug flags and helper functions +// + +#define CSQ_TRACE_ERROR 0x00000001 +#define CSQ_TRACE_LOAD_UNLOAD 0x00000002 +#define CSQ_TRACE_INSTANCE_CALLBACK 0x00000004 +#define CSQ_TRACE_CONTEXT_CALLBACK 0x00000008 +#define CSQ_TRACE_CBDQ_CALLBACK 0x00000010 +#define CSQ_TRACE_PRE_READ 0x00000020 +#define CSQ_TRACE_ALL 0xFFFFFFFF + +#define DebugTrace(Level, Data) \ + if ((Level) & Globals.DebugLevel) { \ + DbgPrint Data; \ + } + +// +// Memory Pool Tags +// + +#define INSTANCE_CONTEXT_TAG 'IqsC' +#define QUEUE_CONTEXT_TAG 'QqsC' +#define CSQ_REG_TAG 'RqsC' +#define CSQ_STRING_TAG 'SqsC' + +// +// Registry value names and default values +// + +#define CSQ_DEFAULT_TIME_DELAY 150000000 +#define CSQ_DEFAULT_MAPPING_PATH L"\\" +#define CSQ_KEY_NAME_DELAY L"OperatingDelay" +#define CSQ_KEY_NAME_PATH L"OperatingPath" +#define CSQ_KEY_NAME_DEBUG_LEVEL L"DebugLevel" +#define CSQ_MAX_PATH_LENGTH 256 + + +// +// Prototypes +// + +// +// Queue context data structure +// + +typedef struct _QUEUE_CONTEXT { + + FLT_CALLBACK_DATA_QUEUE_IO_CONTEXT CbdqIoContext; + +} QUEUE_CONTEXT, *PQUEUE_CONTEXT; + +// +// Instance context data structure +// + +typedef struct _INSTANCE_CONTEXT { + + // + // Instance for this context. + // + + PFLT_INSTANCE Instance; + + // + // Cancel safe queue members + // + + FLT_CALLBACK_DATA_QUEUE Cbdq; + LIST_ENTRY QueueHead; + FAST_MUTEX Lock; + + // + // Flag to control the life/death of the work item thread + // + + volatile LONG WorkerThreadFlag; + + // + // Notify the worker thread that the instance is being torndown + // + + KEVENT TeardownEvent; + +} INSTANCE_CONTEXT, *PINSTANCE_CONTEXT; + + +typedef struct _CSQ_GLOBAL_DATA { + + ULONG DebugLevel; + + PFLT_FILTER FilterHandle; + + NPAGED_LOOKASIDE_LIST QueueContextLookaside; + + UNICODE_STRING MappingPath; + + PWSTR PathBuffer; + + LONGLONG TimeDelay; + +} CSQ_GLOBAL_DATA; + + + +// +// Global variables +// + +CSQ_GLOBAL_DATA Globals; + + +// +// Local function prototypes +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +VOID +FreeGlobals( + ); + +NTSTATUS +Unload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +VOID +ContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +NTSTATUS +InstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +NTSTATUS +InstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +VOID +InstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +InstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +NTSTATUS +SetConfiguration ( + _In_ PUNICODE_STRING RegistryPath + ); + +VOID +_IRQL_requires_max_(APC_LEVEL) +_IRQL_raises_(APC_LEVEL) +_Requires_lock_not_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock) +_Acquires_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock) +CsqAcquire( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _Out_ PKIRQL Irql + ); + +VOID +_IRQL_requires_max_(APC_LEVEL) +_IRQL_requires_min_(APC_LEVEL) +_IRQL_raises_(PASSIVE_LEVEL) +_Requires_lock_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock) +_Releases_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock) +CsqRelease( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _In_ KIRQL Irql + ); + +NTSTATUS +CsqInsertIo( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _In_ PFLT_CALLBACK_DATA Data, + _In_opt_ PVOID Context + ); +VOID +CsqRemoveIo( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _In_ PFLT_CALLBACK_DATA Data + ); +PFLT_CALLBACK_DATA +CsqPeekNextIo( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _In_opt_ PFLT_CALLBACK_DATA Data, + _In_opt_ PVOID PeekContext + ); +VOID +CsqCompleteCanceledIo( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _Inout_ PFLT_CALLBACK_DATA Data + ); + +FLT_PREOP_CALLBACK_STATUS +PreRead ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +VOID +PreReadWorkItemRoutine( + _In_ PFLT_GENERIC_WORKITEM WorkItem, + _In_ PFLT_FILTER Filter, + _In_ PVOID Context + ); + +NTSTATUS +PreReadPendIo( + _In_ PINSTANCE_CONTEXT InstanceContext + ); + +NTSTATUS +PreReadProcessIo( + _Inout_ PFLT_CALLBACK_DATA Data + ); + +VOID +PreReadEmptyQueueAndComplete( + _In_ PINSTANCE_CONTEXT InstanceContext + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, SetConfiguration) +#pragma alloc_text(PAGE, Unload) +#pragma alloc_text(PAGE, FreeGlobals) +#pragma alloc_text(PAGE, ContextCleanup) +#pragma alloc_text(PAGE, InstanceSetup) +#pragma alloc_text(PAGE, InstanceQueryTeardown) +#pragma alloc_text(PAGE, InstanceTeardownStart) +#pragma alloc_text(PAGE, InstanceTeardownComplete) + +#endif + +// +// Filters callback routines +// + +FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_READ, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + PreRead, + NULL }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Filters context registration data structure +// + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_INSTANCE_CONTEXT, + 0, + ContextCleanup, + sizeof( INSTANCE_CONTEXT ), + INSTANCE_CONTEXT_TAG }, + + { FLT_CONTEXT_END } +}; + +// +// Filters registration data structure +// + +FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + ContextRegistration, // Context + Callbacks, // Operation callbacks + Unload, // Filters unload routine + InstanceSetup, // InstanceSetup routine + InstanceQueryTeardown, // InstanceQueryTeardown routine + InstanceTeardownStart, // InstanceTeardownStart routine + InstanceTeardownComplete, // InstanceTeardownComplete routine + NULL, NULL, NULL // Unused naming support callbacks +}; + +// +// Filter driver initialization and unload routines +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this filter driver. It registers + itself with the filter manager and initializes all its global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS Status; + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + // + // Initialize global lookaside list + // + + ExInitializeNPagedLookasideList( &Globals.QueueContextLookaside, + NULL, + NULL, + 0, + sizeof( QUEUE_CONTEXT ), + QUEUE_CONTEXT_TAG, + 0 ); + + // + // Initialize the configuration to default values + // + + Globals.DebugLevel = CSQ_TRACE_ERROR; + + Globals.TimeDelay = CSQ_DEFAULT_TIME_DELAY; + + Globals.PathBuffer = NULL; + + RtlInitUnicodeString( &Globals.MappingPath, CSQ_DEFAULT_MAPPING_PATH ); + + + // + // Modify the configuration based on values in the registry + // + + Status = SetConfiguration( RegistryPath ); + + if (!NT_SUCCESS( Status )) { + + goto DriverEntryCleanup; + } + + DebugTrace( CSQ_TRACE_LOAD_UNLOAD, + ("[Csq]: CancelSafe!DriverEntry\n") ); + + + + // + // Register with the filter manager + // + + Status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &Globals.FilterHandle ); + + if (!NT_SUCCESS( Status )) { + + DebugTrace( CSQ_TRACE_LOAD_UNLOAD | CSQ_TRACE_ERROR, + ("[Csq]: Failed to register filter (Status = 0x%x)\n", + Status) ); + + goto DriverEntryCleanup; + + } + + // + // Start filtering I/O + // + + Status = FltStartFiltering( Globals.FilterHandle ); + + if (!NT_SUCCESS( Status )) { + + DebugTrace( CSQ_TRACE_LOAD_UNLOAD | CSQ_TRACE_ERROR, + ("[Csq]: Failed to start filtering (Status = 0x%x)\n", + Status) ); + + FltUnregisterFilter( Globals.FilterHandle ); + + goto DriverEntryCleanup; + + } + + + DebugTrace( CSQ_TRACE_LOAD_UNLOAD, + ("[Csq]: Driver loaded complete\n") ); + +DriverEntryCleanup: + + if (!NT_SUCCESS( Status )) { + + FreeGlobals(); + } + + return Status; +} + + +NTSTATUS +SetConfiguration ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to configure the debuglevel, mapping path and + queue delay based on values in the registry. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + STATUS_SUCCESS if the function completes successfully. Otherwise a valid + NTSTATUS code is returned. + +--*/ +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES Attributes; + HANDLE DriverRegKey = NULL; + UNICODE_STRING ValueName; + BOOLEAN CloseHandle = FALSE; + UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + CSQ_MAX_PATH_LENGTH * sizeof(WCHAR)]; + PKEY_VALUE_PARTIAL_INFORMATION Value = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer; + ULONG ValueLength = sizeof(Buffer); + ULONG ResultLength; + ULONG Length; + + // + // Open the driver registry key. + // + + InitializeObjectAttributes( &Attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + Status = ZwOpenKey( &DriverRegKey, + KEY_READ, + &Attributes ); + + if (!NT_SUCCESS( Status )) { + + goto SetConfigurationCleanup; + } + + CloseHandle = TRUE; + + // + // Query the debug level + // + + RtlInitUnicodeString( &ValueName, CSQ_KEY_NAME_DEBUG_LEVEL ); + + Status = ZwQueryValueKey( DriverRegKey, + &ValueName, + KeyValuePartialInformation, + Value, + ValueLength, + &ResultLength ); + + if (NT_SUCCESS( Status )) { + + Globals.DebugLevel = *(PULONG)(Value->Data); + } + + + // + // Query the queue time delay + // + + + RtlInitUnicodeString( &ValueName, CSQ_KEY_NAME_DELAY ); + + Status = ZwQueryValueKey( DriverRegKey, + &ValueName, + KeyValuePartialInformation, + Value, + ValueLength, + &ResultLength ); + + if (NT_SUCCESS( Status )) { + + if (Value->Type != REG_DWORD) { + + Status = STATUS_INVALID_PARAMETER; + goto SetConfigurationCleanup; + } + + Globals.TimeDelay = (LONGLONG)(*(PULONG)(Value->Data)); + + } + + // + // Query the mapping path + // + + RtlInitUnicodeString( &ValueName, CSQ_KEY_NAME_PATH ); + + // + // For simplicity of this sample, the length of the mapping path + // allowed in the registry is limited to CSQ_MAX_PATH_LENGTH + // characters. If this size is exceeded the default mapping path + // will be used. + // + + Status = ZwQueryValueKey( DriverRegKey, + &ValueName, + KeyValuePartialInformation, + Value, + ValueLength, + &ValueLength ); + + if (NT_SUCCESS( Status )) { + + // + // Set up the mapping and ensure the mapping string format is "\a\...\". + // If the mapping path doesn't begin with '\' fail, if it doesn't end + // with a '\' append one. + // + + if (*(PWCHAR)(Value->Data) != L'\\') { + + Status = STATUS_INVALID_PARAMETER; + goto SetConfigurationCleanup; + } + + // + // Allocate enough space for an extra character in case a trailing '\' + // is missing and needs to be added. + // + + Length = Value->DataLength + sizeof(WCHAR), + + Globals.PathBuffer = ExAllocatePoolWithTag( NonPagedPool, Length, CSQ_STRING_TAG ); + + if (Globals.PathBuffer == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto SetConfigurationCleanup; + } + + RtlCopyMemory( Globals.PathBuffer, Value->Data, Value->DataLength ); + + Globals.PathBuffer[Length / sizeof(WCHAR) - 1] = L'\0'; + + // + // Add a trailing '\' if one is missing. + // + + if (Globals.PathBuffer[Length/sizeof(WCHAR) - 3] != L'\\') { + + Globals.PathBuffer[Length/sizeof(WCHAR) - 2] = L'\\'; + + } + + RtlInitUnicodeString(&Globals.MappingPath, Globals.PathBuffer); + + } + + // + // Ignore errors when looking for values in the registry. + // Default values will be used. + // + + Status = STATUS_SUCCESS; + +SetConfigurationCleanup: + + if (CloseHandle) { + + ZwClose( DriverRegKey ); + } + + return Status; + +} + + +VOID +FreeGlobals( + ) +/*++ + +Routine Descrition: + + This routine cleans up the global buffers on both + teardown and initialization failure. + +Arguments: + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + Globals.FilterHandle = NULL; + + ExDeleteNPagedLookasideList( &Globals.QueueContextLookaside ); + + if (Globals.PathBuffer != NULL) { + + ExFreePoolWithTag( Globals.PathBuffer, CSQ_STRING_TAG ); + Globals.PathBuffer = NULL; + } + + RtlInitUnicodeString( &Globals.MappingPath, NULL ); +} + + +NTSTATUS +Unload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this filter driver. This is called + when the minifilter is about to be unloaded. We can fail this unload + request if this is not a mandatory unloaded indicated by the Flags + parameter. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( CSQ_TRACE_LOAD_UNLOAD, + ("[Csq]: CancelSafe!Unload\n") ); + + FltUnregisterFilter( Globals.FilterHandle ); + + FreeGlobals(); + + return STATUS_SUCCESS; +} + + +// +// Context cleanup routine. +// + +VOID +ContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + FltMgr calls this routine immediately before it deletes the context. + +Arguments: + + Context - Pointer to the minifilter driver's portion of the context. + + ContextType - Type of context. Must be one of the following values: + FLT_FILE_CONTEXT (Microsoft Windows Vista and later only.), + FLT_INSTANCE_CONTEXT, FLT_STREAM_CONTEXT, FLT_STREAMHANDLE_CONTEXT, + FLT_TRANSACTION_CONTEXT (Windows Vista and later only.), and + FLT_VOLUME_CONTEXT + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( Context ); + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + DebugTrace( CSQ_TRACE_CONTEXT_CALLBACK, + ("[Csq]: CancelSafe!ContextCleanup\n") ); +} + +// +// Instance setup/teardown routines. +// + +NTSTATUS +InstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. This + gives us a chance to decide if we need to attach to this volume or not. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + + VolumeDeviceType - Device type of the file system volume. + Must be one of the following: FILE_DEVICE_CD_ROM_FILE_SYSTEM, + FILE_DEVICE_DISK_FILE_SYSTEM, and FILE_DEVICE_NETWORK_FILE_SYSTEM. + + VolumeFilesystemType - File system type of the volume. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + PINSTANCE_CONTEXT InstCtx = NULL; + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK, + ("[Csq]: CancelSafe!InstanceSetup\n") ); + + // + // Allocate and initialize the instance context. + // + + Status = FltAllocateContext( FltObjects->Filter, + FLT_INSTANCE_CONTEXT, + sizeof( INSTANCE_CONTEXT ), + NonPagedPool, + &InstCtx ); + + if (!NT_SUCCESS( Status )) { + + DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK | CSQ_TRACE_ERROR, + ("[Csq]: Failed to allocate instance context (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + Status) ); + + goto InstanceSetupCleanup; + } + + Status = FltCbdqInitialize( FltObjects->Instance, + &InstCtx->Cbdq, + CsqInsertIo, + CsqRemoveIo, + CsqPeekNextIo, + CsqAcquire, + CsqRelease, + CsqCompleteCanceledIo ); + + if (!NT_SUCCESS( Status )) { + + DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK | CSQ_TRACE_ERROR, + ("[Csq]: Failed to initialize callback data queue (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + Status) ); + + goto InstanceSetupCleanup; + } + + // + // Initialize the internal queue head and lock of the cancel safe queue. + // + + InitializeListHead( &InstCtx->QueueHead ); + + ExInitializeFastMutex( &InstCtx->Lock ); + + // + // Initialize other members of the instance context. + // + + InstCtx->Instance = FltObjects->Instance; + + InstCtx->WorkerThreadFlag = 0; + + KeInitializeEvent( &InstCtx->TeardownEvent, NotificationEvent, FALSE ); + + // + // Set the instance context. + // + + Status = FltSetInstanceContext( FltObjects->Instance, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + InstCtx, + NULL ); + + if (!NT_SUCCESS( Status )) { + + DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK | CSQ_TRACE_ERROR, + ("[Csq]: Failed to set instance context (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + Status) ); + + goto InstanceSetupCleanup; + } + + +InstanceSetupCleanup: + + if (InstCtx != NULL) { + + FltReleaseContext( InstCtx ); + } + + return Status; +} + + +NTSTATUS +InstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach thereby giving us a + chance to fail that detach request. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Returns the status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK, + ("[Csq]: CancelSafe!InstanceQueryTeardown\n") ); + + return STATUS_SUCCESS; +} + + +VOID +InstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the start of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + PINSTANCE_CONTEXT InstCtx = 0; + NTSTATUS Status; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK, + ("[Csq]: CancelSafe!InstanceTeardownStart\n") ); + + // + // Get a pointer to the instance context. + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstCtx ); + + if (!NT_SUCCESS( Status )) + { + FLT_ASSERT( !"Instance Context is missing" ); + return; + } + + // + // Disable the insert to the cancel safe queue. + // + + FltCbdqDisable( &InstCtx->Cbdq ); + + // + // Remove all callback data from the queue and complete them. + // + + PreReadEmptyQueueAndComplete( InstCtx ); + + // + // Signal the worker thread if it is pended. + // + + KeSetEvent( &InstCtx->TeardownEvent, 0, FALSE ); + + // + // Cleanup + // + + FltReleaseContext( InstCtx ); +} + + +VOID +InstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the end of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK, + ("[Csq]: CancelSafe!InstanceTeardownComplete\n") ); + + PAGED_CODE(); +} + + +// +// Cbdq callback routines. +// + +VOID +_IRQL_requires_max_(APC_LEVEL) +_IRQL_raises_(APC_LEVEL) +_Requires_lock_not_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock) +_Acquires_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock) +CsqAcquire( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _Out_ PKIRQL Irql + ) +/*++ + +Routine Description: + + FltMgr calls this routine to acquire the lock protecting the queue. + +Arguments: + + DataQueue - Supplies a pointer to the queue itself. + + Irql - Returns the previous IRQL if a spinlock is acquired. We do not use + any spinlocks, so we ignore this. + +Return Value: + + None. + +--*/ +{ + PINSTANCE_CONTEXT InstCtx; + + DebugTrace( CSQ_TRACE_CBDQ_CALLBACK, + ("[Csq]: CancelSafe!CsqAcquire\n") ); + + // + // Get a pointer to the instance context. + // + + InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ); + + // + // Acquire the lock. + // + + ExAcquireFastMutex( &InstCtx->Lock ); + + *Irql = 0; +} + + +VOID +_IRQL_requires_max_(APC_LEVEL) +_IRQL_requires_min_(APC_LEVEL) +_IRQL_raises_(PASSIVE_LEVEL) +_Requires_lock_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock) +_Releases_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock) +CsqRelease( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _In_ KIRQL Irql + ) +/*++ + +Routine Description: + + FltMgr calls this routine to release the lock protecting the queue. + +Arguments: + + DataQueue - Supplies a pointer to the queue itself. + + Irql - Supplies the previous IRQL if a spinlock is acquired. We do not use + any spinlocks, so we ignore this. + +Return Value: + + None. + +--*/ +{ + PINSTANCE_CONTEXT InstCtx; + + UNREFERENCED_PARAMETER( Irql ); + + DebugTrace( CSQ_TRACE_CBDQ_CALLBACK, + ("[Csq]: CancelSafe!CsqRelease\n") ); + + // + // Get a pointer to the instance context. + // + + InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ); + + // + // Release the lock. + // + + ExReleaseFastMutex( &InstCtx->Lock ); +} + + +NTSTATUS +CsqInsertIo( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _In_ PFLT_CALLBACK_DATA Data, + _In_opt_ PVOID Context + ) +/*++ + +Routine Description: + + FltMgr calls this routine to insert an entry into our pending I/O queue. + The queue is already locked before this routine is called. + +Arguments: + + DataQueue - Supplies a pointer to the queue itself. + + Data - Supplies the callback data for the operation that is being + inserted into the queue. + + Context - Supplies user-defined context information. + +Return Value: + + STATUS_SUCCESS if the function completes successfully. Otherwise a valid + NTSTATUS code is returned. + +--*/ +{ + PINSTANCE_CONTEXT InstCtx; + PFLT_GENERIC_WORKITEM WorkItem = NULL; + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN WasQueueEmpty; + + UNREFERENCED_PARAMETER( Context ); + + DebugTrace( CSQ_TRACE_CBDQ_CALLBACK, + ("[Csq]: CancelSafe!CsqInsertIo\n") ); + + // + // Get a pointer to the instance context. + // + + InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ); + + // + // Save the queue state before inserting to it. + // + + WasQueueEmpty = IsListEmpty( &InstCtx->QueueHead ); + + // + // Insert the callback data entry into the queue. + // + + InsertTailList( &InstCtx->QueueHead, + &Data->QueueLinks ); + + // + // Queue a work item if no worker thread present. + // + + if (WasQueueEmpty && + InterlockedIncrement( &InstCtx->WorkerThreadFlag ) == 1) { + + WorkItem = FltAllocateGenericWorkItem(); + + if (WorkItem) { + + Status = FltQueueGenericWorkItem( WorkItem, + InstCtx->Instance, + PreReadWorkItemRoutine, + DelayedWorkQueue, + InstCtx->Instance ); + + if (!NT_SUCCESS( Status )) { + + DebugTrace( CSQ_TRACE_CBDQ_CALLBACK | CSQ_TRACE_ERROR, + ("[Csq]: Failed to queue the work item (Status = 0x%x)\n", + Status) ); + + FltFreeGenericWorkItem( WorkItem ); + } + + } else { + + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (!NT_SUCCESS( Status )) { + + // + // Remove the callback data that was inserted into the queue. + // + + RemoveTailList( &InstCtx->QueueHead ); + } + } + + return Status; +} + + +VOID +CsqRemoveIo( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + FltMgr calls this routine to remove an entry from our pending I/O queue. + The queue is already locked before this routine is called. + +Arguments: + + DataQueue - Supplies a pointer to the queue itself. + + Data - Supplies the callback data that is to be removed. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( DataQueue ); + + DebugTrace( CSQ_TRACE_CBDQ_CALLBACK, + ("[Csq]: CancelSafe!CsqRemoveIo\n") ); + + // + // Remove the callback data entry from the queue. + // + + RemoveEntryList( &Data->QueueLinks ); +} + + +PFLT_CALLBACK_DATA +CsqPeekNextIo( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _In_opt_ PFLT_CALLBACK_DATA Data, + _In_opt_ PVOID PeekContext + ) +/*++ + +Routine Description: + + FltMgr calls this routine to look for an entry on our pending I/O queue. + The queue is already locked before this routine is called. + +Arguments: + + DataQueue - Supplies a pointer to the queue itself. + + Data - Supplies the callback data we should start our search from. + If this is NULL, we start at the beginning of the list. + + PeekContext - Supplies user-defined context information. + +Return Value: + + A pointer to the next callback data structure, or NULL. + +--*/ +{ + PINSTANCE_CONTEXT InstCtx; + PLIST_ENTRY NextEntry; + PFLT_CALLBACK_DATA NextData; + + UNREFERENCED_PARAMETER( PeekContext ); + + DebugTrace( CSQ_TRACE_CBDQ_CALLBACK, + ("[Csq]: CancelSafe!CsqPeekNextIo\n") ); + + // + // Get a pointer to the instance context. + // + + InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ); + + // + // If the supplied callback "Data" is NULL, the "NextIo" is the first entry + // in the queue; or it is the next list entry in the queue. + // + + if (Data == NULL) { + + NextEntry = InstCtx->QueueHead.Flink; + + } else { + + NextEntry = Data->QueueLinks.Flink; + } + + // + // Return NULL if we hit the end of the queue or the queue is empty. + // + + if (NextEntry == &InstCtx->QueueHead) { + + return NULL; + } + + NextData = CONTAINING_RECORD( NextEntry, FLT_CALLBACK_DATA, QueueLinks ); + + return NextData; +} + + +VOID +CsqCompleteCanceledIo( + _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, + _Inout_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + FltMgr calls this routine to complete an operation as cancelled that was + previously pended. The queue is already locked before this routine is called. + +Arguments: + + DataQueue - Supplies a pointer to the queue itself. + + Data - Supplies the callback data that is to be canceled. + +Return Value: + + None. + +--*/ +{ + PQUEUE_CONTEXT QueueCtx; + + UNREFERENCED_PARAMETER( DataQueue ); + + DebugTrace( CSQ_TRACE_CBDQ_CALLBACK, + ("[Csq]: CancelSafe!CsqCompleteCanceledIo\n") ); + + QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0]; + + // + // Just complete the operation as canceled. + // + + Data->IoStatus.Status = STATUS_CANCELLED; + Data->IoStatus.Information = 0; + + FltCompletePendedPreOperation( Data, + FLT_PREOP_COMPLETE, + 0 ); + + // + // Free the extra storage that was allocated for this canceled I/O. + // + + ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside, + QueueCtx ); +} + + +FLT_PREOP_CALLBACK_STATUS +PreRead ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Handle pre-read. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + + PINSTANCE_CONTEXT InstCtx = NULL; + PQUEUE_CONTEXT QueueCtx = NULL; + PFLT_FILE_NAME_INFORMATION NameInfo = NULL; + NTSTATUS CbStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + NTSTATUS Status; + + UNREFERENCED_PARAMETER( CompletionContext ); + + DebugTrace( CSQ_TRACE_PRE_READ, + ("[Csq]: CancelSafe!PreRead\n") ); + + // + // Skip IRP_PAGING_IO, IRP_SYNCHRONOUS_PAGING_IO and + // TopLevelIrp. + // + + if ((Data->Iopb->IrpFlags & IRP_PAGING_IO) || + (Data->Iopb->IrpFlags & IRP_SYNCHRONOUS_PAGING_IO) || + IoGetTopLevelIrp()) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Get and parse the file name + // + + Status = FltGetFileNameInformation( Data, + FLT_FILE_NAME_NORMALIZED + | FLT_FILE_NAME_QUERY_DEFAULT, + &NameInfo ); + + if (!NT_SUCCESS( Status )) { + + DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR, + ("[Csq]: Failed to get filename (Status = 0x%x)\n", + Status) ); + + goto PreReadCleanup; + } + + Status = FltParseFileNameInformation( NameInfo ); + + if (!NT_SUCCESS( Status )) { + + DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR, + ("[Csq]: Failed to parse filename (Name = %wZ, Status = 0x%x)\n", + &NameInfo->Name, + Status) ); + + goto PreReadCleanup; + } + + // + // Compare to see if this file I/O is to be pended. + // + + if (!RtlPrefixUnicodeString( &Globals.MappingPath, &NameInfo->ParentDir, TRUE )) { + + goto PreReadCleanup; + } + + // + // Since Fast I/O operations cannot be queued, we could return + // FLT_PREOP_SUCCESS_NO_CALLBACK at this point. In this sample, + // we disallow Fast I/O for this magic file in order to force an IRP + // to be sent to us again. The purpose of doing that is to demonstrate + // the cancel safe queue, which may not be true in the real world. + // + + if (!FLT_IS_IRP_OPERATION( Data )) { + + CbStatus = FLT_PREOP_DISALLOW_FASTIO; + goto PreReadCleanup; + } + + // + // Allocate a context for each I/O to be inserted into the queue. + // + + QueueCtx = ExAllocateFromNPagedLookasideList( &Globals.QueueContextLookaside ); + + if (QueueCtx == NULL) { + + DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR, + ("[Csq]: Failed to allocate from NPagedLookasideList (Status = 0x%x)\n", + Status) ); + + goto PreReadCleanup; + } + + RtlZeroMemory(QueueCtx, sizeof(QUEUE_CONTEXT)); + + // + // Get the instance context. + // + + Status = FltGetInstanceContext( FltObjects->Instance, + &InstCtx ); + + if (!NT_SUCCESS( Status )) { + + FLT_ASSERT( !"Instance context is missing" ); + goto PreReadCleanup; + } + + // + // Set the queue context + // + + Data->QueueContext[0] = (PVOID) QueueCtx; + Data->QueueContext[1] = NULL; + + // + // Insert the callback data into the cancel safe queue + // + + Status = FltCbdqInsertIo( &InstCtx->Cbdq, + Data, + &QueueCtx->CbdqIoContext, + 0 ); + + if (Status == STATUS_SUCCESS) { + + // + // In general, we can create a worker thread here as long as we can + // correctly handle the insert/remove race conditions b/w multi threads. + // In this sample, the worker thread creation is done in CsqInsertIo. + // This is a simpler solution because CsqInsertIo is atomic with + // respect to other CsqXxxIo callback routines. + // + + CbStatus = FLT_PREOP_PENDING; + + } else { + + DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR, + ("[Csq]: Failed to insert into cbdq (Status = 0x%x)\n", + Status) ); + } + +PreReadCleanup: + + // + // Clean up + // + + if (QueueCtx && CbStatus != FLT_PREOP_PENDING) { + + ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside, QueueCtx ); + } + + if (NameInfo) { + + FltReleaseFileNameInformation( NameInfo ); + } + + if (InstCtx) { + + FltReleaseContext( InstCtx ); + } + + return CbStatus; +} + + +VOID +PreReadWorkItemRoutine( + _In_ PFLT_GENERIC_WORKITEM WorkItem, + _In_ PFLT_FILTER Filter, + _In_ PVOID Context + ) +/*++ + +Routine Description: + + This WorkItem routine is called in the system thread context to process + all the pended I/O in this mini filter's cancel safe queue. For each I/O + in the queue, it completes the I/O after pending the operation for a + period of time. The thread exits when the queue is empty. + +Arguments: + + WorkItem - Unused. + + Filter - Unused. + + Context - Context information. + +Return Value: + + None. + +--*/ +{ + PINSTANCE_CONTEXT InstCtx = NULL; + PFLT_CALLBACK_DATA Data; + PFLT_INSTANCE Instance = (PFLT_INSTANCE)Context; + PQUEUE_CONTEXT QueueCtx; + NTSTATUS Status; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + + UNREFERENCED_PARAMETER( WorkItem ); + UNREFERENCED_PARAMETER( Filter ); + + DebugTrace( CSQ_TRACE_PRE_READ, + ("[Csq]: CancelSafe!PreReadWorkItemRoutine\n") ); + + // + // Get a pointer to the instance context. + // + + Status = FltGetInstanceContext( Instance, + &InstCtx ); + + if (!NT_SUCCESS( Status )) + { + FLT_ASSERT( !"Instance Context is missing" ); + return; + } + + // + // Process all the pended I/O in the cancel safe queue + // + + for (;;) { + + callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + + PreReadPendIo( InstCtx ); + + // + // WorkerThreadFlag >= 1; + // Here we reduce it to 1. + // + + InterlockedExchange( &InstCtx->WorkerThreadFlag, 1 ); + + // + // Remove an I/O from the cancel safe queue. + // + + Data = FltCbdqRemoveNextIo( &InstCtx->Cbdq, + NULL); + + if (Data) { + + QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0]; + + PreReadProcessIo( Data ); + + // + // Check to see if we need to lock the user buffer. + // + // If the FLTFL_CALLBACK_DATA_SYSTEM_BUFFER flag is set we don't + // have to lock the buffer because its already a system buffer. + // + // If the MdlAddress is NULL and the buffer is a user buffer, + // then we have to construct one in order to look at the buffer. + // + // If the length of the buffer is zero there is nothing to read, + // so we cannot construct a MDL. + // + + if (!FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) && + Data->Iopb->Parameters.Read.MdlAddress == NULL && + Data->Iopb->Parameters.Read.Length > 0) { + + Status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS( Status )) { + + // + // If could not lock the user buffer we cannot + // allow the IO to go below us. Because we are + // in a different VA space and the buffer is a + // user mode address, we will either fault or + // corrpt data + // + + DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR, + ("[Csq]: Failed to lock user buffer (Status = 0x%x)\n", + Status) ); + + callbackStatus = FLT_PREOP_COMPLETE; + Data->IoStatus.Status = Status; + } + } + + // + // Complete the I/O + // + + FltCompletePendedPreOperation( Data, + callbackStatus, + NULL ); + + // + // Free the extra storage that was allocated for this I/O. + // + + ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside, + QueueCtx ); + + } else { + + // + // At this moment it is possible that a new IO is being inserted + // into the queue in the CsqInsertIo routine. Now that the queue is + // empty, CsqInsertIo needs to make a decision on whether to create + // a new worker thread. The decision is based on the race between + // the InterlockedIncrement in CsqInsertIo and the + // InterlockedDecrement as below. There are two situations: + // + // (1) If the decrement executes earlier before the increment, + // the flag will be decremented to 0 so this worker thread + // will return. Then CsqInsertIo will increment the flag + // from 0 to 1, and therefore create a new worker thread. + // (2) If the increment executes earlier before the decrement, + // the flag will be first incremented to 2 in CsqInsertIo + // so a new worker thread will not be satisfied. Then the + // decrement as below will lower the flag down to 1, and + // therefore continue this worker thread. + // + + if (InterlockedDecrement( &InstCtx->WorkerThreadFlag ) == 0) { + + break; + } + + } + } + + // + // Clean up + // + + FltReleaseContext(InstCtx); + + FltFreeGenericWorkItem(WorkItem); +} + + +NTSTATUS +PreReadPendIo( + _In_ PINSTANCE_CONTEXT InstanceContext + ) +/*++ + +Routine Description: + + This routine waits for a period of time or until the instance is + torndown. + +Arguments: + + InstanceContext - Supplies a pointer to the instance context. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + LARGE_INTEGER DueTime; + NTSTATUS Status; + + // + // Delay or get signaled if the instance is torndown. + // + + DueTime.QuadPart = (LONGLONG) - Globals.TimeDelay; + + Status = KeWaitForSingleObject( &InstanceContext->TeardownEvent, + Executive, + KernelMode, + FALSE, + &DueTime ); + + return Status; +} + + +NTSTATUS +PreReadProcessIo( + _Inout_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This routine process the I/O that was removed from the queue. + +Arguments: + + Data - Supplies the callback data that was removed from the queue. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Data ); + + return STATUS_SUCCESS; +} + + +VOID +PreReadEmptyQueueAndComplete( + _In_ PINSTANCE_CONTEXT InstanceContext + ) +/*++ + +Routine Description: + + This routine empties the cancel safe queue and complete all the + pended pre-read operations. + +Arguments: + + InstanceContext - Supplies a pointer to the instance context. + +Return Value: + + None. + +--*/ +{ + NTSTATUS Status; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + PFLT_CALLBACK_DATA Data; + PQUEUE_CONTEXT QueueCtx; + + do { + + callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + + Data = FltCbdqRemoveNextIo( &InstanceContext->Cbdq, + NULL ); + + if (Data) { + + QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0]; + + // + // Check to see if we need to lock the user buffer. + // + // If the FLTFL_CALLBACK_DATA_SYSTEM_BUFFER flag is set we don't + // have to lock the buffer because its already a system buffer. + // + // If the MdlAddress is NULL and the buffer is a user buffer, + // then we have to construct one in order to look at the buffer. + // + // If the length of the buffer is zero there is nothing to read, + // so we cannot construct a MDL. + // + + if (!FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) && + Data->Iopb->Parameters.Read.MdlAddress == NULL && + Data->Iopb->Parameters.Read.Length > 0) { + + Status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS( Status )) { + + // + // If could not lock the user buffer we cannot + // allow the IO to go below us. Because we are + // in a different VA space and the buffer is a + // user mode address, we will either fault or + // corrpt data + // + + callbackStatus = FLT_PREOP_COMPLETE; + Data->IoStatus.Status = Status; + } + } + + FltCompletePendedPreOperation( Data, + callbackStatus, + NULL ); + + ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside, + QueueCtx ); + } + + } while (Data); +} + diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.inf b/filesys/miniFilter/cancelSafe/cancelSafe.inf new file mode 100644 index 00000000..d13586f7 --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.inf @@ -0,0 +1,99 @@ +;;; +;;; CancelSafe +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.0 +CatalogFile = cancelsafe.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"OperatingDelay",0x00010001 ,150000000 ; Delay in 100 nano sec units +HKR,,"OperatingPath",0x00000000,%OperatingPath% +HKR,,"DebugFlags",0x00010001 ,0x0 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +cancelsafe.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "CancelSafe Mini-Filter Driver" +ServiceName = "CancelSafe" +DriverName = "CancelSafe" +DiskId1 = "CancelSafe Device Installation Disk" +OperatingPath = "\testdir\" + +;Instances specific information. +DefaultInstance = "CancelSafe Instance" +Instance1.Name = "CancelSafe Instance" +Instance1.Altitude = "370050" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.rc b/filesys/miniFilter/cancelSafe/cancelSafe.rc new file mode 100644 index 00000000..39c1f317 --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "CancelSafe Filter Driver" +#define VER_INTERNALNAME_STR "cancelSafe.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.sln b/filesys/miniFilter/cancelSafe/cancelSafe.sln new file mode 100644 index 00000000..17d34d65 --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancelSafe", "cancelSafe.vcxproj", "{AC99C662-1480-43BF-B6C1-5B3055F228E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|Win32.ActiveCfg = Debug|Win32 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|Win32.Build.0 = Debug|Win32 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|Win32.ActiveCfg = Release|Win32 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|Win32.Build.0 = Release|Win32 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|x64.ActiveCfg = Debug|x64 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|x64.Build.0 = Debug|x64 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|x64.ActiveCfg = Release|x64 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj new file mode 100644 index 00000000..8e6a1960 --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj @@ -0,0 +1,184 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {AC99C662-1480-43BF-B6C1-5B3055F228E2} + $(MSBuildProjectName) + Debug + Win32 + {71FE15A0-262E-47F8-BEFF-3C73167F2349} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + cancelSafe + + + cancelSafe + + + cancelSafe + + + cancelSafe + + + + %(AdditionalOptions) /map + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + true + Level4 + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(AdditionalOptions) /map + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + true + Level4 + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(AdditionalOptions) /map + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + true + Level4 + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(AdditionalOptions) /map + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + true + Level4 + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters new file mode 100644 index 00000000..11736279 --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {4671E7BF-8D01-45C4-A3C1-20A08F5BBEDF} + + + h;hpp;hxx;hm;inl;inc;xsd + {ABBB6D26-48D1-4341-AC20-F5D5C5329766} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {483287C7-E0B5-452F-BB3A-486026CBBB4F} + + + inf;inv;inx;mof;mc; + {DF4558B9-9BF3-4D98-B89E-718878687D2C} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/cdo/Cdo.rc b/filesys/miniFilter/cdo/Cdo.rc new file mode 100644 index 00000000..4408672c --- /dev/null +++ b/filesys/miniFilter/cdo/Cdo.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Control Device Object Sample Mini-Filter" +#define VER_INTERNALNAME_STR "cdo.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/cdo/CdoInit.c b/filesys/miniFilter/cdo/CdoInit.c new file mode 100644 index 00000000..fa6e37aa --- /dev/null +++ b/filesys/miniFilter/cdo/CdoInit.c @@ -0,0 +1,374 @@ +/*++ + +Copyright (c) 1999 - 2003 Microsoft Corporation + +Module Name: + + CdoInit.c + +Abstract: + + This is the main module of the kernel mode filter driver implementing + the control device object sample. + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + + +// +// Local function prototypes +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +CdoUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +CdoInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +#if DBG + +VOID +CdoInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ); + +#endif + + +// +// Global variables +// + +CDO_GLOBAL_DATA Globals; + + +// +// Pragma defintiion table +// + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) + +#if DBG +#pragma alloc_text(INIT, CdoInitializeDebugLevel) +#endif + +#pragma alloc_text(PAGE, CdoUnload) +#endif + + + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS status; + + + // + // This defines what we want to filter with FltMgr + // + + CONST FLT_REGISTRATION filterRegistration = { + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + NULL, // Context + NULL, // Operation callbacks + CdoUnload, // MiniFilterUnload + CdoInstanceSetup, // InstanceSetup + NULL, // InstanceQueryTeardown + NULL, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + NULL,NULL // NameProvider callbacks + }; + + + RtlZeroMemory( &Globals, sizeof( Globals ) ); + +#if DBG + + // + // Initialize global debug level + // + + CdoInitializeDebugLevel( RegistryPath ); + +#else + + UNREFERENCED_PARAMETER( RegistryPath ); + +#endif + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Cdo]: Driver being loaded\n") ); + + // + // Initialize the resource + // + + ExInitializeResourceLite( &Globals.Resource ); + + // + // Record the driver object + // + + Globals.FilterDriverObject = DriverObject; + + // + // Register with FltMgr to tell it our callback routines + // + + status = FltRegisterFilter( DriverObject, + &filterRegistration, + &Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + ExDeleteResourceLite( &Globals.Resource ); + return status; + } + + // + // Now create our control device object + // + + status = CdoCreateControlDeviceObject( DriverObject ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( Globals.Filter ); + ExDeleteResourceLite( &Globals.Resource ); + return status; + } + + // + // Start filtering i/o + // + + status = FltStartFiltering( Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + CdoDeleteControlDeviceObject(); + FltUnregisterFilter( Globals.Filter ); + ExDeleteResourceLite( &Globals.Resource ); + return status; + } + + return status; +} + +#if DBG + +VOID +CdoInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to read the filter DebugLevel parameter from + the registry. This value will be found in the registry location + indicated by the RegistryPath passed in. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + None. + +--*/ +{ + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey; + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING valueName; + UCHAR buffer[sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + sizeof( LONG )]; + + Globals.DebugLevel = DEBUG_TRACE_ERROR; + + // + // Open the desired registry key + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (NT_SUCCESS( status )) { + + // + // Read the DebugFlags value from the registry. + // + + RtlInitUnicodeString( &valueName, L"DebugLevel" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.DebugLevel = *((PULONG) &(((PKEY_VALUE_PARTIAL_INFORMATION) buffer)->Data)); + } + } + + // + // Close the registry entry + // + + ZwClose( driverRegKey ); +} + +#endif + + +NTSTATUS +CdoUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this filter driver. This is called + when the minifilter is about to be unloaded. We can fail this unload + request if this is not a mandatory unloaded indicated by the Flags + parameter. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Flags ); + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Cdo]: Unloading driver\n") ); + + // + // If the CDO is still referenced and the unload is not mandatry + // then fail the unload + // + + CdoAcquireResourceShared( &Globals.Resource ); + + if (FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF) && + !FlagOn(Flags,FLTFL_FILTER_UNLOAD_MANDATORY)) { + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD | DEBUG_TRACE_ERROR, + ("[Cdo]: Fail unloading driver since the unload is optional and the CDO is open\n") ); + CdoReleaseResource( &Globals.Resource ); + return STATUS_FLT_DO_NOT_DETACH; + } + + + // + // Cleanup and unload + // + + FltUnregisterFilter( Globals.Filter ); + Globals.Filter = NULL; + CdoDeleteControlDeviceObject(); + + + CdoReleaseResource( &Globals.Resource ); + + ExDeleteResourceLite( &Globals.Resource ); + + return STATUS_SUCCESS; +} + + +// +// Instance setup routine +// + +NTSTATUS +CdoInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. This + gives us a chance to decide if we need to attach to this volume or not. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + +Return Value: + + STATUS_FLT_DO_NOT_ATTACH - do not attach because we do not want to + attach to any volume + +--*/ +{ + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + return STATUS_FLT_DO_NOT_ATTACH; +} + + + diff --git a/filesys/miniFilter/cdo/CdoOperations.c b/filesys/miniFilter/cdo/CdoOperations.c new file mode 100644 index 00000000..8193f437 --- /dev/null +++ b/filesys/miniFilter/cdo/CdoOperations.c @@ -0,0 +1,2226 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + operations.c + +Abstract: + + This is the CDO i/o operations module of the kernel mode filter driver implementing + CDO sample + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA + #pragma alloc_text( PAGE, CdoCreateControlDeviceObject) + #pragma alloc_text( PAGE, CdoDeleteControlDeviceObject) + #pragma alloc_text( PAGE, CdoMajorFunction) + #pragma alloc_text( PAGE, CdoHandlePrivateOpen) + #pragma alloc_text( PAGE, CdoHandlePrivateCleanup) + #pragma alloc_text( PAGE, CdoHandlePrivateClose) + #pragma alloc_text( PAGE, CdoHandlePrivateFsControl) + #pragma alloc_text( PAGE, CdoFastIoCheckIfPossible) + #pragma alloc_text( PAGE, CdoFastIoRead) + #pragma alloc_text( PAGE, CdoFastIoWrite) + #pragma alloc_text( PAGE, CdoFastIoQueryBasicInfo) + #pragma alloc_text( PAGE, CdoFastIoQueryStandardInfo) + #pragma alloc_text( PAGE, CdoFastIoLock) + #pragma alloc_text( PAGE, CdoFastIoUnlockSingle) + #pragma alloc_text( PAGE, CdoFastIoUnlockAll) + #pragma alloc_text( PAGE, CdoFastIoUnlockAllByKey) + #pragma alloc_text( PAGE, CdoFastIoDeviceControl) + #pragma alloc_text( PAGE, CdoFastIoQueryNetworkOpenInfo) + #pragma alloc_text( PAGE, CdoFastIoMdlRead) + #pragma alloc_text( NONPAGED, CdoFastIoMdlReadComplete) + #pragma alloc_text( PAGE, CdoFastIoPrepareMdlWrite) + #pragma alloc_text( NONPAGED, CdoFastIoMdlWriteComplete) + #pragma alloc_text( PAGE, CdoFastIoReadCompressed) + #pragma alloc_text( PAGE, CdoFastIoWriteCompressed) + #pragma alloc_text( NONPAGED, CdoFastIoMdlReadCompleteCompressed) + #pragma alloc_text( NONPAGED, CdoFastIoMdlWriteCompleteCompressed) + #pragma alloc_text( PAGE, CdoFastIoQueryOpen) + #pragma alloc_text( PAGE, CdoHandlePrivateOpen ) + #pragma alloc_text( PAGE, CdoHandlePrivateCleanup ) + #pragma alloc_text( PAGE, CdoHandlePrivateClose ) + #pragma alloc_text( PAGE, CdoHandlePrivateFsControl ) + +#endif + + +// +// Fast IO dispatch routines +// + +FAST_IO_DISPATCH CdoFastIoDispatch = +{ + sizeof(FAST_IO_DISPATCH), + CdoFastIoCheckIfPossible, // CheckForFastIo + CdoFastIoRead, // FastIoRead + CdoFastIoWrite, // FastIoWrite + CdoFastIoQueryBasicInfo, // FastIoQueryBasicInfo + CdoFastIoQueryStandardInfo, // FastIoQueryStandardInfo + CdoFastIoLock, // FastIoLock + CdoFastIoUnlockSingle, // FastIoUnlockSingle + CdoFastIoUnlockAll, // FastIoUnlockAll + CdoFastIoUnlockAllByKey, // FastIoUnlockAllByKey + CdoFastIoDeviceControl, // FastIoDeviceControl + NULL, // AcquireFileForNtCreateSection + NULL, // ReleaseFileForNtCreateSection + NULL, // FastIoDetachDevice + CdoFastIoQueryNetworkOpenInfo, // FastIoQueryNetworkOpenInfo + NULL, // AcquireForModWrite + CdoFastIoMdlRead, // MdlRead + CdoFastIoMdlReadComplete, // MdlReadComplete + CdoFastIoPrepareMdlWrite, // PrepareMdlWrite + CdoFastIoMdlWriteComplete, // MdlWriteComplete + CdoFastIoReadCompressed, // FastIoReadCompressed + CdoFastIoWriteCompressed, // FastIoWriteCompressed + CdoFastIoMdlReadCompleteCompressed, // MdlReadCompleteCompressed + CdoFastIoMdlWriteCompleteCompressed, // MdlWriteCompleteCompressed + CdoFastIoQueryOpen, // FastIoQueryOpen + NULL, // ReleaseForModWrite + NULL, // AcquireForCcFlush + NULL, // ReleaseForCcFlush +}; + + + +NTSTATUS +_Function_class_(DRIVER_INITIALIZE) +CdoCreateControlDeviceObject( + _Inout_ PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine handles the IRPs that are directed to the control + device object. + +Arguments: + + DriverObject - driver object for this driver + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status; + UNICODE_STRING nameString; + ULONG i; + + PAGED_CODE(); + + // + // Create our control device object + // + + DebugTrace( DEBUG_TRACE_CDO_CREATE_DELETE, + ("[Cdo]: Creating CDO ... \n") ); + + RtlInitUnicodeString( &nameString, CONTROL_DEVICE_OBJECT_NAME ); + status = IoCreateDevice( DriverObject, + 0, + &nameString, + FILE_DEVICE_DISK_FILE_SYSTEM, + FILE_DEVICE_SECURE_OPEN, + FALSE, + &Globals.FilterControlDeviceObject); + + if ( !NT_SUCCESS( status ) ) { + + DebugTrace( DEBUG_TRACE_CDO_CREATE_DELETE | DEBUG_TRACE_ERROR, + ("[Cdo]: Failure to create CDO. IoCreateDevice failed with status 0x%x. \n", + status) ); + return status; + } + + // + // Initialize the driver object with this driver's entry points. + // Most are simply passed through to some other device driver. + // + + for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { + +#pragma prefast(suppress:__WARNING_DISPATCH_MISMATCH __WARNING_DISPATCH_MISSING, "CdoMajorFunction is the dispatch routine for every major code, so no tag applies to it.") + DriverObject->MajorFunction[i] = CdoMajorFunction; + } + +#pragma prefast(suppress:__WARNING_INACCESSIBLE_MEMBER, "The Cdo sample is allowed to set the FastIo Dispatch routine because he is setting up a Cdo.") + DriverObject->FastIoDispatch = &CdoFastIoDispatch; + + DebugTrace( DEBUG_TRACE_CDO_CREATE_DELETE, + ("[Cdo]: Creating CDO successful\n") ); + + return STATUS_SUCCESS; +} + + + +VOID +CdoDeleteControlDeviceObject( + VOID + ) +/*++ + +Routine Description: + + This routine deletes the control device object. + +Arguments: + + None + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + // + // Delete our control device object + // + + DebugTrace( DEBUG_TRACE_CDO_CREATE_DELETE, + ("[Cdo]: Deleting CDO ... \n") ); + + IoDeleteDevice( Globals.FilterControlDeviceObject ); + + DebugTrace( DEBUG_TRACE_CDO_CREATE_DELETE, + ("[Cdo]: Deleting CDO successful\n") ); + +} + + +DRIVER_DISPATCH CdoMajorFunction; +NTSTATUS +CdoMajorFunction( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles the IRPs that are directed to the control + device object. + +Arguments: + + DeviceObject - control device object + Irp - the current Irp to process + +Return Value: + + Returns STATUS_INVALID_DEVICE_REQUEST if the CDO doesn't support that request + type, or the appropriate status otherwise. + +--*/ +{ + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + + UNREFERENCED_PARAMETER( DeviceObject ); + + PAGED_CODE(); + + FLT_ASSERT( IS_MY_CONTROL_DEVICE_OBJECT( DeviceObject ) ); + + + // + // default to success + // + + status = STATUS_SUCCESS; + + irpSp = IoGetCurrentIrpStackLocation(Irp); + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS, + ("[Cdo]: CdoMajorFunction entry ( Irp = %p, irpSp->MajorFunction = 0x%x )\n", + Irp, + irpSp->MajorFunction) ); + + switch (irpSp->MajorFunction) { + + // + // IRP_MJ_CREATE is called to create a new HANDLE on CDO + // + + case IRP_MJ_CREATE: + { + + // + // Handle our private open + // + + status = CdoHandlePrivateOpen(Irp); + + Irp->IoStatus.Status = status; + + if(NT_SUCCESS(status)) + { + // + // If successful, return the file was opened + // + + Irp->IoStatus.Information = FILE_OPENED; + } + else + { + Irp->IoStatus.Information = 0; + } + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + break; + } + + // + // IRP_MJ_CLOSE is called when all references are gone. + // Note: this operation can not be failed. It must succeed. + // + + case IRP_MJ_CLOSE: + { + + CdoHandlePrivateClose( Irp ); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + break; + } + + // + // IRP_MJ_DEVICE_CONTROL is how most user-mode api's drop into here + // + + case IRP_MJ_FILE_SYSTEM_CONTROL: + { + ULONG Operation; + ULONG OutputBufferLength; + ULONG InputBufferLength; + PVOID InputBuffer; + PVOID OutputBuffer; + + Operation = irpSp->Parameters.FileSystemControl.FsControlCode; + InputBufferLength = irpSp->Parameters.FileSystemControl.InputBufferLength; + OutputBufferLength = irpSp->Parameters.FileSystemControl.OutputBufferLength; + + InputBuffer = Irp->AssociatedIrp.SystemBuffer; + OutputBuffer = Irp->AssociatedIrp.SystemBuffer; + + // + // The caller will update the IO status block + // + + status = CdoHandlePrivateFsControl (DeviceObject, + Operation, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength, + &Irp->IoStatus, + Irp ); + break; + } + + // + // IRP_MJ_CLEANUP is called when all handles are closed + // Note: this operation can not be failed. It must succeed. + // + + case IRP_MJ_CLEANUP: + { + + CdoHandlePrivateCleanup( Irp ); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + break; + } + + default: + { + // + // unsupported! + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: Unsupported Major Function 0x%x ( Irp = %p )\n", + irpSp->MajorFunction, + Irp) ); + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS, + ("[Cdo]: CdoMajorFunction exit ( Irp = %p, irpSp->MajorFunction = 0x%x, status = 0x%x )\n", + Irp, + irpSp->MajorFunction, + status) ); + + return status; + + +} + + +NTSTATUS +CdoHandlePrivateOpen( + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles create IRPs that are directed to the control + device object. + +Arguments: + + Irp - the current Irp to process + +Return Value: + + Returns STATUS_DEVICE_ALREADY_ATTACHED if the CDO has already been opened + Returns STATUS_SUCCESS otherwise + +Note: + + This sample supports only one outstanding create on the CDO at a time + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( Irp ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateOpen entry ( Irp = %p )\n", + Irp) ); + + CdoAcquireResourceExclusive( &Globals.Resource ); + + if (FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE ) || + FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF )) { + + // + // Sanity - if we have a handle open against this CDO + // we must have an outstanding reference as well + // + + FLT_ASSERT( !FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE ) || + FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ) ); + + + // + // The CDO is already open - fail this open + // + + status = STATUS_DEVICE_ALREADY_ATTACHED; + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoHandlePrivateOpen -> Device open failure. Device already opened. ( Irp = %p, Flags = 0x%x, status = 0x%x )\n", + Irp, + Globals.Flags, + status) ); + + } else { + + // + // Flag that the CDO is opened so that we will fail future creates + // until the CDO is closed by the current caller + // + // + // If we suceed the create we are guaranteed to get a Cleanup (where we + // will reset GLOBAL_DATA_F_CDO_OPEN_HANDLE) and Close (where we will + // reset GLOBAL_DATA_F_CDO_OPEN_REF) + // + + SetFlag( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ); + SetFlag( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE ); + + status = STATUS_SUCCESS; + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoHandlePrivateOpen -> Device open successful. ( Irp = %p, Flags = 0x%x, status = 0x%x )\n", + Irp, + Globals.Flags, + status) ); + } + + + // + // The filter may want to do additional processing here to set up the structures it + // needs to service this create request. + // + + + CdoReleaseResource( &Globals.Resource ); + + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateOpen exit ( Irp = %p, status = 0x%x )\n", + Irp, + status) ); + + + return status; +} + +NTSTATUS +CdoHandlePrivateCleanup( + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles cleanup IRPs that are directed to the control + device object. + +Arguments: + + Irp - the current Irp to process + +Return Value: + + Returns STATUS_SUCCESS + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( Irp ); + + PAGED_CODE(); + + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateCleanup entry ( Irp = %p )\n", + Irp) ); + + + CdoAcquireResourceExclusive( &Globals.Resource ); + + // + // Sanity - the CDO must have a handle and a reference for us to get a cleanup on it + // + + FLT_ASSERT( FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ) && + FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE) ); + + + // + // Reset the flag that indicates the CDO has a open handle + // + + ClearFlag( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE); + + status = STATUS_SUCCESS; + + // + // The filter may want to do additional processing here to cleanup up the structures it + // needed to service the handle that is being closed. + // + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoHandlePrivateCleanup -> Device cleanup successful. ( Irp = %p, Flags = 0x%x, status = 0x%x )\n", + Irp, + Globals.Flags, + status) ); + + + CdoReleaseResource( &Globals.Resource ); + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateCleanup exit ( Irp = %p, status = 0x%x )\n", + Irp, + status) ); + + + + return status; +} + +NTSTATUS +CdoHandlePrivateClose( + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles close IRPs that are directed to the control + device object. + +Arguments: + + Irp - the current Irp to process + +Return Value: + + Returns STATUS_SUCCESS + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( Irp ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateClose entry ( Irp = %p )\n", + Irp) ); + + CdoAcquireResourceExclusive( &Globals.Resource ); + + // + // Sanity - the connection must have a reference but have no handle open, + // for us to get a close on it + // + + FLT_ASSERT( FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ) && + !FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE )); + + + // + // Reset the flag that indicates the CDO is opened so that we will suceed + // future creates + // + + ClearFlag( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ); + + + // + // The filter may want to do additional processing here to cleanup up the structures it + // needed to service the user mode attachment that is being closed. + // + + + status = STATUS_SUCCESS; + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoHandlePrivateClose -> Device close successful. ( Irp = %p, Flags = 0x%x, status = 0x%x )\n", + Irp, + Globals.Flags, + status) ); + + CdoReleaseResource( &Globals.Resource ); + + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateClose exit ( Irp = %p, status = 0x%x )\n", + Irp, + status) ); + + + return status; + +} + +NTSTATUS +CdoHandlePrivateFsControl ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ ULONG IoControlCode, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_opt_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine is invoked whenever an I/O Request Packet (IRP) w/a major + function code of IRP_MJ_FILE_SYSTEM_CONTROL is encountered for the CDO. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + IoControlCode - Control code for this IOCTL + InputBuffer - Input buffer + InputBufferLength - Input buffer length + OutputBuffer - Output buffer + OutputBufferLength - Output buffer length + IoStatus - IO status block for this request + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( IoControlCode ); + UNREFERENCED_PARAMETER( InputBuffer ); + UNREFERENCED_PARAMETER( InputBufferLength ); + UNREFERENCED_PARAMETER( OutputBuffer ); + UNREFERENCED_PARAMETER( OutputBufferLength ); + UNREFERENCED_PARAMETER( Irp ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateFsControl entry ( Irp = %p )\n" + "\tIoControlCode = 0x%x\n" + "\tInputBuffer = %p\n" + "\tInputBufferLength = 0x%x\n" + "\tOutputBuffer = %p\n" + "\tOutputBufferLength = 0x%x\n", + Irp, + IoControlCode, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength) ); + + CdoAcquireResourceShared( &Globals.Resource ); + + // + // Sanity - there must atleast be a reference open for us to get a IOCTL on the CDO + // + + FLT_ASSERT( FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ) ); + + + if (!FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE)) { + + // + // If there is no handle open to the CDO fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoHandlePrivateFsControl -> Failing IOCTL since no handle to CDO is open. ( Irp = %p, IoControlCode = 0x%x, Flags = 0x%x )\n", + Irp, + IoControlCode, + Globals.Flags) ); + + status = STATUS_INVALID_DEVICE_STATE; + CdoReleaseResource( &Globals.Resource ); + goto CdoHandlePrivateFsControlCleanup; + } + + // + // Here the filter may perform any action that requires that + // the handle to the CDO still be open + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateFsControl -> Processing IOCTL while handle to CDO is definitely open. ( Irp = %p, IoControlCode = 0x%x )\n", + Irp, + IoControlCode) ); + + CdoReleaseResource( &Globals.Resource ); + + // + // Since the resource has been released the CDO may complete a cleanup before we + // do any of the following. + // + + + // + // Here the filter may perform any action that does not require that + // the handle to the CDO still be open. For example, the IOCTL may have + // been used to trigger off an asynchronous background task that will + // continue executing even after the handle has been closed + // + // Note that the system will still maintain a reference to the CDO. So, + // the filter will not see a Close on the CDO until it finishes servicing + // IRP_MJ_FILE_SYSTEM_CONTROL + // + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateFsControl -> Processing IOCTL while handle to CDO may not be open. ( Irp = %p, IoControlCode = 0x%x )\n", + Irp, + IoControlCode) ); + + status = STATUS_SUCCESS; + +CdoHandlePrivateFsControlCleanup: + + IoStatus->Status = status; + IoStatus->Information = 0; + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateFsControl exit ( Irp = %p, IoControlCode = 0x%x, status = 0x%x )\n", + Irp, + IoControlCode, + status) ); + + + return status; +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// FastIO Handling routines +// +///////////////////////////////////////////////////////////////////////////// + + + +BOOLEAN +CdoFastIoCheckIfPossible ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _In_ BOOLEAN CheckForReadOperation, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for checking to see + whether fast I/O is possible for this file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be operated on. + + FileOffset - Byte offset in the file for the operation. + + Length - Length of the operation to be performed. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + LockKey - Provides the caller's key for file locks. + + CheckForReadOperation - Indicates whether the caller is checking for a + read (TRUE) or a write operation. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(CheckForReadOperation); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoCheckIfPossible -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +BOOLEAN +CdoFastIoRead ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _Out_writes_bytes_(Length) PVOID Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for reading from a + file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be read. + + FileOffset - Byte offset in the file of the read. + + Length - Length of the read operation to be performed. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + LockKey - Provides the caller's key for file locks. + + Buffer - Pointer to the caller's buffer to receive the data read. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoRead -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + + +BOOLEAN +CdoFastIoWrite ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _In_reads_bytes_(Length) PVOID Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for writing to a + file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be written. + + FileOffset - Byte offset in the file of the write operation. + + Length - Length of the write operation to be performed. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + LockKey - Provides the caller's key for file locks. + + Buffer - Pointer to the caller's buffer that contains the data to be + written. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoWrite -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryBasicInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_BASIC_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for querying basic + information about the file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be queried. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + Buffer - Pointer to the caller's buffer to receive the information about + the file. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoQueryBasicInfo -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryStandardInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_STANDARD_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for querying standard + information about the file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be queried. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + Buffer - Pointer to the caller's buffer to receive the information about + the file. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoQueryStandardInfo -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +BOOLEAN +CdoFastIoLock ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PLARGE_INTEGER Length, + _In_ PEPROCESS ProcessId, + _In_ ULONG Key, + _In_ BOOLEAN FailImmediately, + _In_ BOOLEAN ExclusiveLock, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for locking a byte + range within a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be locked. + + FileOffset - Starting byte offset from the base of the file to be locked. + + Length - Length of the byte range to be locked. + + ProcessId - ID of the process requesting the file lock. + + Key - Lock key to associate with the file lock. + + FailImmediately - Indicates whether or not the lock request is to fail + if it cannot be immediately be granted. + + ExclusiveLock - Indicates whether the lock to be taken is exclusive (TRUE) + or shared. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(ProcessId); + UNREFERENCED_PARAMETER(Key); + UNREFERENCED_PARAMETER(FailImmediately); + UNREFERENCED_PARAMETER(ExclusiveLock); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoLock -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +BOOLEAN +CdoFastIoUnlockSingle ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PLARGE_INTEGER Length, + _In_ PEPROCESS ProcessId, + _In_ ULONG Key, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for unlocking a byte + range within a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be unlocked. + + FileOffset - Starting byte offset from the base of the file to be + unlocked. + + Length - Length of the byte range to be unlocked. + + ProcessId - ID of the process requesting the unlock operation. + + Key - Lock key associated with the file lock. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(ProcessId); + UNREFERENCED_PARAMETER(Key); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoUnlockSingle -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + + +BOOLEAN +CdoFastIoUnlockAll ( + _In_ PFILE_OBJECT FileObject, + _In_ PEPROCESS ProcessId, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for unlocking all + locks within a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be unlocked. + + ProcessId - ID of the process requesting the unlock operation. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(ProcessId); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoUnlockAll -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +BOOLEAN +CdoFastIoUnlockAllByKey ( + _In_ PFILE_OBJECT FileObject, + _In_ PVOID ProcessId, + _In_ ULONG Key, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for unlocking all + locks within a file based on a specified key. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be unlocked. + + ProcessId - ID of the process requesting the unlock operation. + + Key - Lock key associated with the locks on the file to be released. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(ProcessId); + UNREFERENCED_PARAMETER(Key); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoUnlockAllByKey -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + + +BOOLEAN +CdoFastIoDeviceControl ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _In_ ULONG IoControlCode, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for device I/O control + operations on a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object representing the device to be + serviced. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + InputBuffer - Optional pointer to a buffer to be passed into the driver. + + InputBufferLength - Length of the optional InputBuffer, if one was + specified. + + OutputBuffer - Optional pointer to a buffer to receive data from the + driver. + + OutputBufferLength - Length of the optional OutputBuffer, if one was + specified. + + IoControlCode - I/O control code indicating the operation to be performed + on the device. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(Wait); + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoFastIoDeviceControl Entry ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + // + // The caller will update the IO status block + // + + CdoHandlePrivateFsControl ( DeviceObject, + IoControlCode, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength, + IoStatus, + NULL ); + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoFastIoDeviceControl Exit ( FileObject = %p, DeviceObject = %p, Status = 0x%x )\n", + FileObject, + DeviceObject, + IoStatus->Status) ); + + return TRUE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryNetworkOpenInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_NETWORK_OPEN_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for querying network + information about a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be queried. + + Wait - Indicates whether or not the caller can handle the file system + having to wait and tie up the current thread. + + Buffer - Pointer to a buffer to receive the network information about the + file. + + IoStatus - Pointer to a variable to receive the final status of the query + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoQueryNetworkOpenInfo -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoMdlRead ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for reading a file + using MDLs as buffers. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object that is to be read. + + FileOffset - Supplies the offset into the file to begin the read operation. + + Length - Specifies the number of bytes to be read from the file. + + LockKey - The key to be used in byte range lock checks. + + MdlChain - A pointer to a variable to be filled in w/a pointer to the MDL + chain built to describe the data read. + + IoStatus - Variable to receive the final status of the read operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoMdlRead -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + +BOOLEAN +CdoFastIoMdlReadComplete ( + _In_ PFILE_OBJECT FileObject, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for completing an + MDL read operation. + + This function simply invokes the file system's corresponding routine, if + it has one. It should be the case that this routine is invoked only if + the MdlRead function is supported by the underlying file system, and + therefore this function will also be supported, but this is not assumed + by this driver. + +Arguments: + + FileObject - Pointer to the file object to complete the MDL read upon. + + MdlChain - Pointer to the MDL chain used to perform the read operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE, depending on whether or not it is + possible to invoke this function on the fast I/O path. + +--*/ + +{ + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, return not supported + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoMdlReadComplete -> Unsupported as FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + return FALSE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoPrepareMdlWrite ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for preparing for an + MDL write operation. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object that will be written. + + FileOffset - Supplies the offset into the file to begin the write operation. + + Length - Specifies the number of bytes to be write to the file. + + LockKey - The key to be used in byte range lock checks. + + MdlChain - A pointer to a variable to be filled in w/a pointer to the MDL + chain built to describe the data written. + + IoStatus - Variable to receive the final status of the write operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoPrepareMdlWrite -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + + + +BOOLEAN +CdoFastIoMdlWriteComplete ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for completing an + MDL write operation. + + This function simply invokes the file system's corresponding routine, if + it has one. It should be the case that this routine is invoked only if + the PrepareMdlWrite function is supported by the underlying file system, + and therefore this function will also be supported, but this is not + assumed by this driver. + +Arguments: + + FileObject - Pointer to the file object to complete the MDL write upon. + + FileOffset - Supplies the file offset at which the write took place. + + MdlChain - Pointer to the MDL chain used to perform the write operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE, depending on whether or not it is + possible to invoke this function on the fast I/O path. + +--*/ +{ + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, return not supported + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoMdlWriteComplete -> Unsupported as FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + + return FALSE; +} + + +/********************************************************************************* + UNIMPLEMENTED FAST IO ROUTINES + + The following four Fast IO routines are for compression on the wire + which is not yet implemented in NT. + + NOTE: It is highly recommended that you include these routines (which + do a pass-through call) so your filter will not need to be + modified in the future when this functionality is implemented in + the OS. + + FastIoReadCompressed, FastIoWriteCompressed, + FastIoMdlReadCompleteCompressed, FastIoMdlWriteCompleteCompressed +**********************************************************************************/ + + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoReadCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Out_writes_bytes_(Length) PVOID Buffer, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _Out_writes_bytes_(CompressedDataInfoLength) struct _COMPRESSED_DATA_INFO *CompressedDataInfo, + _In_ ULONG CompressedDataInfoLength, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for reading compressed + data from a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object that will be read. + + FileOffset - Supplies the offset into the file to begin the read operation. + + Length - Specifies the number of bytes to be read from the file. + + LockKey - The key to be used in byte range lock checks. + + Buffer - Pointer to a buffer to receive the compressed data read. + + MdlChain - A pointer to a variable to be filled in w/a pointer to the MDL + chain built to describe the data read. + + IoStatus - Variable to receive the final status of the read operation. + + CompressedDataInfo - A buffer to receive the description of the compressed + data. + + CompressedDataInfoLength - Specifies the size of the buffer described by + the CompressedDataInfo parameter. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(CompressedDataInfo); + UNREFERENCED_PARAMETER(CompressedDataInfoLength); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoReadCompressed -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoWriteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _In_reads_bytes_(Length) PVOID Buffer, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_reads_bytes_(CompressedDataInfoLength) struct _COMPRESSED_DATA_INFO *CompressedDataInfo, + _In_ ULONG CompressedDataInfoLength, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for writing compressed + data to a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object that will be written. + + FileOffset - Supplies the offset into the file to begin the write operation. + + Length - Specifies the number of bytes to be write to the file. + + LockKey - The key to be used in byte range lock checks. + + Buffer - Pointer to the buffer containing the data to be written. + + MdlChain - A pointer to a variable to be filled in w/a pointer to the MDL + chain built to describe the data written. + + IoStatus - Variable to receive the final status of the write operation. + + CompressedDataInfo - A buffer to containing the description of the + compressed data. + + CompressedDataInfoLength - Specifies the size of the buffer described by + the CompressedDataInfo parameter. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(CompressedDataInfo); + UNREFERENCED_PARAMETER(CompressedDataInfoLength); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoWriteCompressed -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + + + +BOOLEAN +CdoFastIoMdlReadCompleteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for completing an + MDL read compressed operation. + + This function simply invokes the file system's corresponding routine, if + it has one. It should be the case that this routine is invoked only if + the read compressed function is supported by the underlying file system, + and therefore this function will also be supported, but this is not assumed + by this driver. + +Arguments: + + FileObject - Pointer to the file object to complete the compressed read + upon. + + MdlChain - Pointer to the MDL chain used to perform the read operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE, depending on whether or not it is + possible to invoke this function on the fast I/O path. + +--*/ +{ + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, return not supported + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoMdlReadCompleteCompressed -> Unsupported as FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + return FALSE; +} + + + +BOOLEAN +CdoFastIoMdlWriteCompleteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for completing a + write compressed operation. + + This function simply invokes the file system's corresponding routine, if + it has one. It should be the case that this routine is invoked only if + the write compressed function is supported by the underlying file system, + and therefore this function will also be supported, but this is not assumed + by this driver. + +Arguments: + + FileObject - Pointer to the file object to complete the compressed write + upon. + + FileOffset - Supplies the file offset at which the file write operation + began. + + MdlChain - Pointer to the MDL chain used to perform the write operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE, depending on whether or not it is + possible to invoke this function on the fast I/O path. + +--*/ +{ + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, return not supported + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoMdlWriteCompleteCompressed -> Unsupported as FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + return FALSE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(Irp->IoStatus.Status == 0) +BOOLEAN +CdoFastIoQueryOpen ( + _In_ PIRP Irp, + _Out_ PFILE_NETWORK_OPEN_INFORMATION NetworkInformation, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for opening a file + and returning network information for it. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + Irp - Pointer to a create IRP that represents this open operation. It is + to be used by the file system for common open/create code, but not + actually completed. + + NetworkInformation - A buffer to receive the information required by the + network about the file being opened. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(NetworkInformation); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoQueryOpen -> Unsupported FastIO call ( Irp = %p, DeviceObject = %p )\n", + Irp, + DeviceObject) ); + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Information = 0; + + return TRUE; +} + + + diff --git a/filesys/miniFilter/cdo/CdoProc.h b/filesys/miniFilter/cdo/CdoProc.h new file mode 100644 index 00000000..4efe27ad --- /dev/null +++ b/filesys/miniFilter/cdo/CdoProc.h @@ -0,0 +1,328 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + CdoProc.h + +Abstract: + + This is the header file defining the functions of the kernel mode + filter driver implementing the CDO sample. + + +Environment: + + Kernel mode + + +--*/ + + +// +// Functions implemented in operations.c +// + + + +// +// Functions implemented in CdoOperations.c +// + +_Function_class_(DRIVER_INITIALIZE) +NTSTATUS +CdoCreateControlDeviceObject( + _Inout_ PDRIVER_OBJECT DriverObject + ); + +VOID +CdoDeleteControlDeviceObject( + VOID + ); + +// +// Functions implemented in CdoOperations.c +// + +DRIVER_DISPATCH CdoMajorFunction; +NTSTATUS +CdoMajorFunction( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ); + +NTSTATUS +CdoHandlePrivateOpen( + _In_ PIRP Irp + ); + +NTSTATUS +CdoHandlePrivateCleanup( + _In_ PIRP Irp + ); + +NTSTATUS +CdoHandlePrivateClose( + _In_ PIRP Irp + ); + +NTSTATUS +CdoHandlePrivateFsControl ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ ULONG IoControlCode, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_opt_ PIRP Irp + ); + +BOOLEAN +CdoFastIoCheckIfPossible ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _In_ BOOLEAN CheckForReadOperation, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoRead ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _Out_writes_bytes_(Length) PVOID Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoWrite ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _In_reads_bytes_(Length) PVOID Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryBasicInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_BASIC_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryStandardInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_STANDARD_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoLock ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PLARGE_INTEGER Length, + _In_ PEPROCESS ProcessId, + _In_ ULONG Key, + _In_ BOOLEAN FailImmediately, + _In_ BOOLEAN ExclusiveLock, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoUnlockSingle ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PLARGE_INTEGER Length, + _In_ PEPROCESS ProcessId, + _In_ ULONG Key, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoUnlockAll ( + _In_ PFILE_OBJECT FileObject, + _In_ PEPROCESS ProcessId, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoUnlockAllByKey ( + _In_ PFILE_OBJECT FileObject, + _In_ PVOID ProcessId, + _In_ ULONG Key, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoDeviceControl ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _In_ ULONG IoControlCode, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryNetworkOpenInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_NETWORK_OPEN_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoMdlRead ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoMdlReadComplete ( + _In_ PFILE_OBJECT FileObject, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject ); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoPrepareMdlWrite ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoMdlWriteComplete ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject ); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoReadCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Out_writes_bytes_(Length) PVOID Buffer, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _Out_writes_bytes_(CompressedDataInfoLength) struct _COMPRESSED_DATA_INFO *CompressedDataInfo, + _In_ ULONG CompressedDataInfoLength, + _In_ PDEVICE_OBJECT DeviceObject); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoWriteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _In_reads_bytes_(Length) PVOID Buffer, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_reads_bytes_(CompressedDataInfoLength) struct _COMPRESSED_DATA_INFO *CompressedDataInfo, + _In_ ULONG CompressedDataInfoLength, + _In_ PDEVICE_OBJECT DeviceObject); + +BOOLEAN +CdoFastIoMdlReadCompleteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject); + +BOOLEAN +CdoFastIoMdlWriteCompleteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject); + +_Success_(Irp->IoStatus.Status == 0) +BOOLEAN +CdoFastIoQueryOpen ( + _In_ PIRP Irp, + _Out_ PFILE_NETWORK_OPEN_INFORMATION NetworkInformation, + _In_ PDEVICE_OBJECT DeviceObject); + + + + +// +// Resource support +// + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +CdoAcquireResourceExclusive ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_exclusive_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + !ExIsResourceAcquiredSharedLite(Resource)); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +CdoAcquireResourceShared ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_shared_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceSharedLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Releases_lock_(_Global_critical_region_) +_Requires_lock_held_(_Global_critical_region_) +CdoReleaseResource ( + _Inout_ _Requires_lock_held_(*_Curr_) _Releases_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + ExIsResourceAcquiredSharedLite(Resource)); + + ExReleaseResourceLite(Resource); + KeLeaveCriticalRegion(); +} + + diff --git a/filesys/miniFilter/cdo/CdoStruct.h b/filesys/miniFilter/cdo/CdoStruct.h new file mode 100644 index 00000000..6e05a9b1 --- /dev/null +++ b/filesys/miniFilter/cdo/CdoStruct.h @@ -0,0 +1,137 @@ +/*++ + +Copyright (c) 1999 - 2003 Microsoft Corporation + +Module Name: + + CdoStruct.h + +Abstract: + + This is the header file defining the data structures used by the kernel mode + filter driver implementing the control device object sample. + + +Environment: + + Kernel mode + + +--*/ + +// +// CDO sample filter global data +// + +// +// GLOBAL_DATA_F_xxx flags +// + +// +// Indicates that there is a open reference to the CDO +// +#define GLOBAL_DATA_F_CDO_OPEN_REF 0x00000001 + +// +// Indicates that there is a open handle to the CDO +// + +#define GLOBAL_DATA_F_CDO_OPEN_HANDLE 0x00000002 + +// +// Globals +// + +typedef struct _CDO_GLOBAL_DATA { + + // + // Handle to minifilter returned from FltRegisterFilter() + // + + PFLT_FILTER Filter; + + // + // Driver object for this filter + // + + PDRIVER_OBJECT FilterDriverObject; + + // + // Control Device Object for this filter + // + + PDEVICE_OBJECT FilterControlDeviceObject; + + // + // Flags - GLOBAL_DATA_F_xxx + // + + ULONG Flags; + + // + // Resource to synchronize access to flags + // + + ERESOURCE Resource; + +#if DBG + + // + // Field to control nature of debug output + // + + ULONG DebugLevel; +#endif + +} CDO_GLOBAL_DATA, *PCDO_GLOBAL_DATA; + +extern CDO_GLOBAL_DATA Globals; + +// +// The name of the CDO created by this filter +// + +#define CONTROL_DEVICE_OBJECT_NAME L"\\FileSystem\\Filters\\CdoSample" + +// +// Macro to test if this is my control device object +// + +#define IS_MY_CONTROL_DEVICE_OBJECT(_devObj) \ + (((_devObj) == Globals.FilterControlDeviceObject) ? \ + (FLT_ASSERT(((_devObj)->DriverObject == Globals.FilterDriverObject) && \ + ((_devObj)->DeviceExtension == NULL)), TRUE) : \ + FALSE) + + +// +// Debug helper functions +// + +#if DBG + + +#define DEBUG_TRACE_ERROR 0x00000001 // Errors - whenever we return a failure code +#define DEBUG_TRACE_LOAD_UNLOAD 0x00000002 // Loading/unloading of the filter + +#define DEBUG_TRACE_CDO_CREATE_DELETE 0x00000004 // Creation/Deletion of CDO +#define DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS 0x00000008 // Supported operations on CDO +#define DEBUG_TRACE_CDO_FASTIO_OPERATIONS 0x00000010 // FastIO operations on CDO +#define DEBUG_TRACE_CDO_ALL_OPERATIONS 0x00000020 // All operations on CDO + +#define DEBUG_TRACE_ALL 0xFFFFFFFF // All flags + + +#define DebugTrace(Level, Data) \ + if ((Level) & Globals.DebugLevel) { \ + DbgPrint Data; \ + } + + +#else + +#define DebugTrace(Level, Data) {NOTHING;} + +#endif + + diff --git a/filesys/miniFilter/cdo/ReadMe.md b/filesys/miniFilter/cdo/ReadMe.md new file mode 100644 index 00000000..750de7b7 --- /dev/null +++ b/filesys/miniFilter/cdo/ReadMe.md @@ -0,0 +1,16 @@ +CDO File System Minifilter Driver +================================= + +The CDO minifilter sample is an example if you intend to use a control device object (CDO) with your minifilters. + +Although the filter manager infrastructure provides a message interface for communication between applications and minifilters, you might need explicit CDOs while the minifilters interface with legacy software. This sample shows how to create and use a CDO with minifilters. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +When the CDO minifilter is deployed, it creates a CDO object named "FileSystem\\Filters\\CdoSample" in the Microsoft Windows object namespace and enables applications to open it and perform certain operations on it. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. diff --git a/filesys/miniFilter/cdo/cdo.inf b/filesys/miniFilter/cdo/cdo.inf new file mode 100644 index 00000000..327fba11 --- /dev/null +++ b/filesys/miniFilter/cdo/cdo.inf @@ -0,0 +1,96 @@ +;;; +;;; Control Device Object File System Filter Driver Sample +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.1 +CatalogFile = cdo.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"DebugLevel",0x00010001,0x00000001 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +cdo.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Control Device Object File System Filter Driver Sample" +ServiceName = "CDO" +DriverName = "cdo" +DiskId1 = "CDO Device Installation Disk" + +;Instances specific information. +DefaultInstance = "CDO" +Instance1.Name = "CDO" +Instance1.Altitude = "370080" +Instance1.Flags = 0x0 diff --git a/filesys/miniFilter/cdo/cdo.sln b/filesys/miniFilter/cdo/cdo.sln new file mode 100644 index 00000000..c0fd8f6d --- /dev/null +++ b/filesys/miniFilter/cdo/cdo.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cdo", "cdo.vcxproj", "{1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|Win32.ActiveCfg = Debug|Win32 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|Win32.Build.0 = Debug|Win32 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|Win32.ActiveCfg = Release|Win32 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|Win32.Build.0 = Release|Win32 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|x64.ActiveCfg = Debug|x64 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|x64.Build.0 = Debug|x64 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|x64.ActiveCfg = Release|x64 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/cdo/cdo.vcxproj b/filesys/miniFilter/cdo/cdo.vcxproj new file mode 100644 index 00000000..52dc8e52 --- /dev/null +++ b/filesys/miniFilter/cdo/cdo.vcxproj @@ -0,0 +1,153 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F} + $(MSBuildProjectName) + Debug + Win32 + {6ABBD5BA-9307-4ABF-821A-66D082F38769} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + cdo + + + cdo + + + cdo + + + cdo + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/cdo/cdo.vcxproj.Filters b/filesys/miniFilter/cdo/cdo.vcxproj.Filters new file mode 100644 index 00000000..04f6e7f0 --- /dev/null +++ b/filesys/miniFilter/cdo/cdo.vcxproj.Filters @@ -0,0 +1,34 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {33D6FDE5-707F-4D9E-A316-A031D1F83BC8} + + + h;hpp;hxx;hm;inl;inc;xsd + {3BD87E43-9C30-4470-9754-3114B37E0BFD} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {4A9AC38F-BAFE-45E1-929D-047A9F3680ED} + + + inf;inv;inx;mof;mc; + {AA17AAA7-BDC6-41C8-9342-FF2F9D6AE3CB} + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/cdo/pch.h b/filesys/miniFilter/cdo/pch.h new file mode 100644 index 00000000..c932a141 --- /dev/null +++ b/filesys/miniFilter/cdo/pch.h @@ -0,0 +1,48 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + pch.h + +Abstract: + + This module includes all the headers which need to be + precompiled & are included by all the source files in this + project + + +Environment: + + Kernel mode + + +--*/ + +#ifndef __CDO_PCH_H__ +#define __CDO_PCH_H__ + +// +// Enabled warnings +// + +#pragma warning(error:4100) // Enable-Unreferenced formal parameter +#pragma warning(error:4101) // Enable-Unreferenced local variable +#pragma warning(error:4061) // Eenable-missing enumeration in switch statement +#pragma warning(error:4505) // Enable-identify dead functions + +// +// Includes +// + +#include +#include +#include +#include "CdoStruct.h" +#include "CdoProc.h" + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +#endif __CDO_PCH_H__ + diff --git a/filesys/miniFilter/change/ReadMe.md b/filesys/miniFilter/change/ReadMe.md new file mode 100644 index 00000000..b40130e6 --- /dev/null +++ b/filesys/miniFilter/change/ReadMe.md @@ -0,0 +1,17 @@ +Change File System Minifilter Driver +==================================== + +The Change minifilter is a transaction-aware filter that monitors file changes in real time. + +This filter tracks if the files are 'dirty' by intercepting write I/O requests. This provides a way to track modifications to a file. Additionally, this filter handles the case where the transaction commits or rollbacks. + +The primary tasks of the filter for tracking a transacted file are the following: + +1. In the post create callback, if a transacted file is open with attribute FILE\_WRITE\_DATA or FILE\_APPEND\_DATA, then enlist its file context into the transaction context. +2. In the pre-operation callback, if the operation needs to be dirty, such as IRP\_MJ\_WRITE and the file is part of a transaction, update the transacted dirty record instead of the non-transacted dirty record. +3. In the kernel transaction manager (KTM) notification callback, if the transaction is committed, then propagate the dirty information from the transacted dirty record to the non-transacted dirty record; if rollback, do not propagate. +4. Properly remove the context structure in the TransactionContextCleanup routine. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + diff --git a/filesys/miniFilter/change/change.c b/filesys/miniFilter/change/change.c new file mode 100644 index 00000000..ec9096c0 --- /dev/null +++ b/filesys/miniFilter/change/change.c @@ -0,0 +1,1393 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Module Name: + + change.c + +Abstract: + + This is the main module of the change miniFilter driver. + This transaction-aware filter monitors file changes in real time. + Cg prefix denotes "Change" module. + + This module tracks if the files are dirty. In order to do this, + we have to intercept the "write" I/O requests. In particular, + the operations are collected in CgOperationsNeedDirty(...) function. + If you care about more than the contents of the file, you may need to + modify this function accordingly. + + This sample demonstrates how to track whether a file has been modified. + + In addition, this filter handles the case that the transaction + commits or rollbacks. The overview of a transaction-aware + minifilter is stated as follows + + 1. At post create, if a transacted file is open with attribute + FILE_WRITE_DATA or FILE_APPEND_DATA, then we would enlist its + file context into the transaction context. + + 2. At pre-operation callback, if the operation needs to be dirty, + such as IRP_MJ_WRITE and the file is part of a transaction, + we update its TxDirty instead of Dirty. + + 3. At KTM notification callback, if the transaction committed, + then propagate the dirty information from TxDirty to Dirty; + if rollbacked, do not propagate. + + 4. Properly remove the list at TransactionContextCleanup. + +Environment: + + Kernel mode + +--*/ + +#include "change.h" + +/************************************************************************* + Local Function Prototypes +*************************************************************************/ + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +CgInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +VOID +CgInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +CgInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +NTSTATUS +CgUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +CgInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +CgPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +CgPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +CgPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +CgPreClose ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +CgPreFsControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +NTSTATUS +CgKtmNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ); + +// +// Local routines +// + +BOOLEAN +CgOperationsNeedDirty ( + _In_ PFLT_CALLBACK_DATA Data + ); + +NTSTATUS +CgQueryTransactionOutcome( + _In_ PKTRANSACTION Transaction, + _Out_ PULONG TxOutcome + ); + +NTSTATUS +CgProcessPreviousTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PCG_FILE_CONTEXT FileContext + ); + +NTSTATUS +CgProcessTransactionOutcome( + _Inout_ PCG_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG TransactionOutcome + ); + + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, CgUnload) +#pragma alloc_text(PAGE, CgInstanceQueryTeardown) +#pragma alloc_text(PAGE, CgInstanceSetup) +#pragma alloc_text(PAGE, CgInstanceTeardownStart) +#pragma alloc_text(PAGE, CgInstanceTeardownComplete) +#pragma alloc_text(PAGE, CgInstanceTeardownComplete) +#pragma alloc_text(PAGE, CgPreCreate) +#pragma alloc_text(PAGE, CgPreFsControl) +#pragma alloc_text(PAGE, CgPostCreate) +#pragma alloc_text(PAGE, CgPreClose) +#pragma alloc_text(PAGE, CgKtmNotificationCallback) +#pragma alloc_text(PAGE, CgProcessPreviousTransaction) +#pragma alloc_text(PAGE, CgProcessTransactionOutcome) +#pragma alloc_text(PAGE, CgQueryTransactionOutcome) +#endif + + +// +// operation registration +// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_CREATE, + 0, + CgPreCreate, + CgPostCreate }, + + { IRP_MJ_CLOSE, + 0, + CgPreClose, + NULL }, + + { IRP_MJ_WRITE, + 0, + CgPreOperationCallback, + NULL }, + + { IRP_MJ_SET_INFORMATION, + 0, + CgPreOperationCallback, + NULL }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + CgPreFsControl, + NULL }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Context registraction construct defined in context.c +// + +extern const FLT_CONTEXT_REGISTRATION ContextRegistration[]; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + ContextRegistration, // Context + Callbacks, // Operation callbacks + + CgUnload, // MiniFilterUnload + + CgInstanceSetup, // InstanceSetup + CgInstanceQueryTeardown, // InstanceQueryTeardown + CgInstanceTeardownStart, // InstanceTeardownStart + CgInstanceTeardownComplete, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL, // NormalizeNameComponent + CgKtmNotificationCallback // KTM notification callback + +}; + + + +NTSTATUS +CgInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. This + gives us a chance to decide if we need to attach to this volume or not. + + If this routine is not defined in the registration structure, automatic + instances are always created. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgInstanceSetup: Entered\n") ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +CgInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach thereby giving us a + chance to fail that detach request. + + If this routine is not defined in the registration structure, explicit + detach requests via FltDetachVolume or FilterDetach will always be + failed. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Returns the status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgInstanceQueryTeardown: Entered\n") ); + + return STATUS_SUCCESS; +} + + +VOID +CgInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the start of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgInstanceTeardownStart: Entered\n") ); +} + + +VOID +CgInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the end of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgInstanceTeardownComplete: Entered\n") ); +} + + +/************************************************************************* + MiniFilter initialization and unload routines. +*************************************************************************/ + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers with FltMgr and initializes all global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( RegistryPath ); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] DriverEntry: Entered\n") ); + + // + // Register with FltMgr to tell it our callback routines + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &gFilterInstance ); + + if (NT_SUCCESS( status )) { + + // + // Start filtering i/o + // + + status = FltStartFiltering( gFilterInstance ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( gFilterInstance ); + } + } + + return status; +} + +NTSTATUS +CgUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter driver. This is called + when the minifilter is about to be unloaded. We can fail this unload + request if this is not a mandatory unloaded indicated by the Flags + parameter. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgUnload: Entered\n") ); + + + FltUnregisterFilter( gFilterInstance ); + gFilterInstance = NULL; + + return STATUS_SUCCESS; +} + + +/************************************************************************* + Local utility routines. +*************************************************************************/ + +BOOLEAN +CgOperationsNeedDirty ( + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This identifies those operations we need to set the file to be dirty. + This is non-pageable because it could be called on the paging path + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + TRUE - If we want the file associated with the request to be dirty. + FALSE - If we don't + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + + // + // In this example, we only care about the "contents" of the file. + // The dirty concept depends on what you care about. If you care + // about the file metadata, for example, then you should have to add + // the operations that modify the file metadata as well. + // + + switch(iopb->MajorFunction) { + + case IRP_MJ_WRITE: + return TRUE; + + case IRP_MJ_FILE_SYSTEM_CONTROL: + switch ( iopb->Parameters.FileSystemControl.Common.FsControlCode ) { + case FSCTL_OFFLOAD_WRITE: + case FSCTL_WRITE_RAW_ENCRYPTED: + case FSCTL_SET_ZERO_DATA: + return TRUE; + default: break; + } + break; + + case IRP_MJ_SET_INFORMATION: + switch ( iopb->Parameters.SetFileInformation.FileInformationClass ) { + case FileEndOfFileInformation: + case FileValidDataLengthInformation: + return TRUE; + default: break; + } + break; + default: + break; + } + return FALSE; +} + +NTSTATUS +CgQueryTransactionOutcome( + _In_ PKTRANSACTION Transaction, + _Out_ PULONG TxOutcome + ) +/*++ + +Routine Description: + + This is a helper function that qeury the KTM that how trasnaction was ended. + +Arguments: + + Transaction - Pointer to transaction object. + + TxOutcome - Output. Specifies the type of transaction outcome. + +Return Value: + + The status of the operation +--*/ +{ + HANDLE transactionHandle; + NTSTATUS status; + TRANSACTION_BASIC_INFORMATION txBasicInfo = {0}; + + PAGED_CODE(); + + status = ObOpenObjectByPointer( Transaction, + OBJ_KERNEL_HANDLE, + NULL, + GENERIC_READ, + *TmTransactionObjectType, + KernelMode, + &transactionHandle ); + + if (!NT_SUCCESS(status)) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgQueryTransactionOutcome: ObOpenObjectByPointer failed.\n") ); + return status; + } + + status = ZwQueryInformationTransaction( transactionHandle, + TransactionBasicInformation, + &txBasicInfo, + sizeof(TRANSACTION_BASIC_INFORMATION), + NULL ); + if (!NT_SUCCESS(status)) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgQueryTransactionOutcome: ObOpenObjectByPointer failed.\n") ); + goto Cleanup; + } + + *TxOutcome = txBasicInfo.Outcome; + +Cleanup: + + ZwClose(transactionHandle); + + return status; +} + +FORCEINLINE +VOID +CgPropagateDirty( + _Inout_ PCG_FILE_CONTEXT FileContext, + _In_ ULONG TransactionOutcome + ) +{ + + if (TransactionOutcome == TransactionOutcomeCommitted) { + + // + // The 'or' operator here handles the case below: + // + // It is possible that the user only read the file even if it opens the file transacted file read and write. + // So, fileContext->TxDirty is possible to be FALSE. + // + // Since KTM callback is asynchrounous, notifications are not necessarily received in order. + // It is possible that dirty information will be wiped out if we use + // + // fileContext->Dirty = fileContext->TxDirty; + // + + FileContext->Dirty |= FileContext->TxDirty; + } + + // + // Clear TxDirty regardless of transaction outcome. + // + + FileContext->TxDirty = FALSE; +} + +NTSTATUS +CgProcessPreviousTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PCG_FILE_CONTEXT FileContext + ) +/*++ + +Routine Description: + + This routine is transaction related implmentation, and is expected to be + invoked at post-create. Note that this function will enlist the newly + allocated transaction context via FltEnlistInTransaction if it needs to. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + FileContext - The file context. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + ULONG txOutcome = TransactionOutcomeUndetermined; + NTSTATUS status = STATUS_SUCCESS; + PCG_TRANSACTION_CONTEXT oldTxCtx = NULL; + PCG_TRANSACTION_CONTEXT transactionContext = NULL; + + PAGED_CODE(); + + if (FltObjects->Transaction != NULL) { + + // + // Get transaction context + // + + status = CgFindOrCreateTransactionContext( FltObjects, + &transactionContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgProcessPreviousTransaction: CgFindOrCreateTransactionContext FAILED\n") ); + transactionContext = NULL; + goto Cleanup; + } + + // + // Enlist it if haven't. + // + + if (!transactionContext->Enlisted) { + + status = FltEnlistInTransaction( FltObjects->Instance, + FltObjects->Transaction, + transactionContext, + TRANSACTION_NOTIFY_COMMIT_FINALIZE | TRANSACTION_NOTIFY_ROLLBACK ); + + if (!NT_SUCCESS( status ) && (status != STATUS_FLT_ALREADY_ENLISTED) ) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgProcessPreviousTransaction: FltEnlistInTransaction FAILED!!!!\n") ); + goto Cleanup; + } + + status = STATUS_SUCCESS; + transactionContext->Enlisted = TRUE; + } + } + + // + // Here we have five cases: + // + // 1) + // oldTxCtx : NULL + // transCtx : B + // 2) + // oldTxCtx : A + // transCtx : NULL + // 3) + // oldTxCtx : A + // transCtx : B + // 4) + // oldTxCtx : A + // transCtx : A + // 5) + // oldTxCtx : NULL + // transCtx : NULL + // + + // + // Synchronize the replacement of FileContext->TxContext with KTM callback. + // + + oldTxCtx = InterlockedExchangePointer( &FileContext->TxContext, transactionContext ); + + if (oldTxCtx != transactionContext) { // case 1,2,3 + + if ( oldTxCtx == NULL ) { // case 1 + + // + // Since we exchanged the pointer, we need to increment the referece count + // + + FltReferenceContext ( transactionContext ); + + // + // Before insertion into the FcList in transaction context, we increment file context's ref count + // + + ExAcquireFastMutex( transactionContext->Mutex ); + + if (!transactionContext->ListDrained) { + + FltReferenceContext ( FileContext ); // Q + InsertTailList( &transactionContext->ScListHead, + &FileContext->ListInTransaction ); + } + + ExReleaseFastMutex( transactionContext->Mutex ); + + goto Cleanup; + } + + // case 2,3 + + // + // There can only be one transacted writer for the file so the previous + // transaction must have finished. Whether or not the TxDirty state is propagated + // depends on its outcome so query that now. + // + + status = CgQueryTransactionOutcome( oldTxCtx->Transaction, &txOutcome ); + + if (!NT_SUCCESS( status )) { + + // + // We have exchanged the transaction pointer already. If we can't get the outcome, + // we have to proceed anyway. + // + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgProcessPreviousTransaction: CgQueryTransactionOutcome FAILED!!!!\n") ); + } + + // + // Remove the file context from the original transaction context. + // + + ExAcquireFastMutex( oldTxCtx->Mutex ); + RemoveEntryList ( &FileContext->ListInTransaction ); + ExReleaseFastMutex( oldTxCtx->Mutex ); + + CgPropagateDirty ( FileContext, txOutcome ); + + if ( transactionContext ) { // case 3 + + FltReferenceContext( transactionContext ); + + ExAcquireFastMutex( transactionContext->Mutex ); + + if (!transactionContext->ListDrained) { + + InsertTailList( &transactionContext->ScListHead, + &FileContext->ListInTransaction ); + + } else { + + FltReleaseContext( FileContext ); + } + + ExReleaseFastMutex( transactionContext->Mutex ); + + } else { // case 2 + + FltReleaseContext ( FileContext ); // Release reference count at Q + } + + // case 2,3 + + FltReleaseContext( oldTxCtx ); // Release reference count in file context originally. + + } + // + // We don't care about case 4, 5. + // + +Cleanup: + + if (transactionContext) { + + FltReleaseContext( transactionContext ); // Release the ref count grabbed at CgFindOrCreateTransactionContext(...) + } + + return status; +} + +NTSTATUS +CgProcessTransactionOutcome( + _Inout_ PCG_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG TransactionOutcome + ) +/*++ + +Routine Description: + + This is a helper function that process transaction commitment or rollback + +Arguments: + + TransactionContext - Pointer to the minifilter driver's transaction context + set at PostCreate. + + TransactionOutcome - Specifies the type of notifications. Should be either + TransactionOutcomeCommitted or TransactionOutcomeAborted + +Return Value: + + STATUS_SUCCESS - Returning this status value indicates that the minifilter + driver is finished with the transaction. This is a success code. + +--*/ +{ + PLIST_ENTRY scan; + PLIST_ENTRY next; + PCG_FILE_CONTEXT fileContext = NULL; + PCG_TRANSACTION_CONTEXT oldTxCtx = NULL; + + PAGED_CODE(); + + // + // Tranversing the file context list, and + // sync the TxDirty -> Dirty. + // + // Either commit or rollback, we need to cleanup the list + // Tear down file context list inside transactionContext + // + + ExAcquireFastMutex( TransactionContext->Mutex ); + + LIST_FOR_EACH_SAFE( scan, next, &TransactionContext->ScListHead ) { + + fileContext = CONTAINING_RECORD( scan, CG_FILE_CONTEXT, ListInTransaction ); + oldTxCtx = InterlockedCompareExchangePointer( &fileContext->TxContext, NULL, TransactionContext ); + if (oldTxCtx == TransactionContext) { + + // + // When oldTxCtx and TransactionContext are equal, it means that + // fileContext->TxContext has been successfully set to NULL. + // + + RemoveEntryList ( scan ); + CgPropagateDirty( fileContext, TransactionOutcome ); + FltReleaseContext( oldTxCtx ); + + // + // This sample demonstrates how we propagate TxDirty to Dirty. + // If the file becomes dirty, we print out here. + // + + if (fileContext->Dirty) { + + CG_DBG_PRINT( CGDBG_TRACE_DEBUG, + ("[CG] CgProcessTransactionOutcome: Transacted file ID %I64x,%I64x is dirty\n", + fileContext->FileID.FileId64.UpperZeroes, + fileContext->FileID.FileId64.Value) ); + } + + FltReleaseContext( fileContext ); + } + } + TransactionContext->ListDrained = TRUE; + ExReleaseFastMutex( TransactionContext->Mutex ); + + + + return STATUS_SUCCESS; +} + +/************************************************************************* + MiniFilter callback routines. +*************************************************************************/ + +FLT_PREOP_CALLBACK_STATUS +CgPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the registered callback routine for filtering + the "write" operation, i.e. the operations that have potential + to make the file dirty. + + This is non-pageable because it could be called on the paging path + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PCG_FILE_CONTEXT fileContext = NULL; + + UNREFERENCED_PARAMETER( CompletionContext ); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPreOperationCallback: Entered\n") ); + + if (!CgOperationsNeedDirty(Data)) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + status = FltGetFileContext( FltObjects->Instance, + FltObjects->FileObject, + &fileContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPreOperationCallback: get file context failed. rq: %d\n", + Data->Iopb->MajorFunction) ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // If this operation is performed in a transacted writer view. + // + + if (fileContext->TxContext != NULL) { + +#if DBG + PCG_TRANSACTION_CONTEXT transactionContext = NULL; + + NTSTATUS statusTx = FltGetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + &transactionContext ); + + FLT_ASSERTMSG( "Transaction context should not fail, because it is supposed to be created at post create.\n", NT_SUCCESS( statusTx )); + FLT_ASSERTMSG( "The file's TxCtx should be identical with the target TxCtx.\n", + fileContext->TxContext == transactionContext); + + if (NT_SUCCESS( statusTx )) { + FltReleaseContext( transactionContext ); + } + +#endif // DBG + + // + // Instead of updating Dirty, we update TxDirty here, + // because this modification is occurring in the context of transaction + // so if the transaction rolls back then the file will not be set as + // Dirty, i.e. the dirty will not be propagated from TxDirty to Dity. + // This is why we have TxDirty here. + // + + fileContext->TxDirty = TRUE; + + } else { + + fileContext->Dirty = TRUE; + } + + FltReleaseContext( fileContext ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + +FLT_PREOP_CALLBACK_STATUS +CgPreFsControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre-file system control callback. This filter example does not support save point feature. + So, we explicitly fail the request here. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + PAGED_CODE(); + + if (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_TXFS_SAVEPOINT_INFORMATION ) { + + // + // We explicitly fail the request of save point here. + // + + Data->IoStatus.Status = STATUS_NOT_SUPPORTED; + return FLT_PREOP_COMPLETE; + } + return CgPreOperationCallback(Data, FltObjects, CompletionContext); +} + + +FLT_PREOP_CALLBACK_STATUS +CgPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the pre-create completion routine. + In this routine, file context and/or transaction context shall be + created if not exits. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + FLT_PREOP_SYNCHRONIZE + +--*/ +{ + + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPreOperationCallback: Entered\n") ); + + // + // Return FLT_PREOP_SYNCHRONIZE at PreCreate due to + // some callback of PostCreate may be at DPC level. + // eResource is required at level < DPC. + // + + return FLT_PREOP_SYNCHRONIZE; + +} + +FLT_POSTOP_CALLBACK_STATUS +CgPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-create completion routine. + In this routine, file context and/or transaction context shall be + created if not exits. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The completion context set in the pre-create routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status = Data->IoStatus.Status; + PCG_FILE_CONTEXT fileContext = NULL; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + if (!NT_SUCCESS( status ) || + (status == STATUS_REPARSE)) { + + // + // File creation may fail. + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPostCreate: file creation failed\n") ); + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Find or create a file context + // + + status = CgFindOrCreateFileContext( Data, + &fileContext ); + + if (!NT_SUCCESS( status )) { + + // + // In this filter sample, if creation or retrieval of the contexts fails, + // we let the creation go through because this example focuses on being + // an non-intrusive filter. However, if tracking is critical for your + // filter, then you should fail the create via FltCancelFileOpen(...). + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPostCreate: find file context failed. \n") ); + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // If successfully opened a file with the desired access matching + // the "exclusive write" from a TxF point of view, we can know that + // if previous transaction context exists, it must have been comitted + // or rollbacked. + // + + if (FlagOn( Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, + FILE_WRITE_DATA | FILE_APPEND_DATA | + DELETE | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | + WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY ) ) { + + status = CgProcessPreviousTransaction ( FltObjects, + fileContext ); + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgPostCreate: CgProcessTransaction FAILED!! \n") ); + + goto Cleanup; + } + } + + +Cleanup: + + FltReleaseContext( fileContext ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +CgPreClose ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre-close callback. Make the file context persistent in the volatile cache. + If the file is transacted, it will be synced at KTM notification callback + if committed. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + + NTSTATUS status; + PCG_FILE_CONTEXT fileContext = NULL; + + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + status = FltGetFileContext( FltObjects->Instance, + FltObjects->FileObject, + &fileContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPreClose: find file context failed.\n") ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // For non-transacted files, + // we just print out the file is dirty or not + // + + if ((FltObjects->Transaction == NULL) && + fileContext->Dirty) { + + CG_DBG_PRINT( CGDBG_TRACE_DEBUG, + ("[CG] CgPreClose: Non-transacted file ID %I64x,%I64x is dirty\n", + fileContext->FileID.FileId64.UpperZeroes, + fileContext->FileID.FileId64.Value) ); + } + + FltReleaseContext( fileContext ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + +NTSTATUS +CgKtmNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ) +/*++ + +Routine Description: + + The registered routine of type PFLT_TRANSACTION_NOTIFICATION_CALLBACK + in FLT_REGISTRATION structure. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + TransactionContext - Pointer to the minifilter driver's transaction context + set at PostCreate. + + TransactionNotification - Specifies the type of notifications that the + filter manager is sending to the minifilter driver. + +Return Value: + + STATUS_SUCCESS - Returning this status value indicates that the minifilter + driver is finished with the transaction. This is a success code. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgKtmNotificationCallback: Entered\n") ); + + FLT_ASSERTMSG("[CG] CgKtmNotificationCallback: The expected type of notifications registered at FltEnlistInTransaction(...).\n", + FlagOn( TransactionNotification, + (TRANSACTION_NOTIFY_COMMIT_FINALIZE | TRANSACTION_NOTIFY_ROLLBACK) ) ); + + if (NULL != TransactionContext) { + + if ( FlagOn( TransactionNotification, TRANSACTION_NOTIFY_COMMIT_FINALIZE ) ) { + + return CgProcessTransactionOutcome( TransactionContext, TransactionOutcomeCommitted ); + + } else { + + return CgProcessTransactionOutcome( TransactionContext, TransactionOutcomeAborted ); + } + } + + return STATUS_SUCCESS; +} + diff --git a/filesys/miniFilter/change/change.h b/filesys/miniFilter/change/change.h new file mode 100644 index 00000000..b8c8c9f2 --- /dev/null +++ b/filesys/miniFilter/change/change.h @@ -0,0 +1,50 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Module Name: + + change.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. Mainly used by change module. + +Environment: + + Kernel mode + +--*/ +#ifndef __CHANGE_H__ +#define __CHANGE_H__ + +#define CG_VISTA (NTDDI_VERSION >= NTDDI_VISTA) + +#include +#include +#include "context.h" +#include "utility.h" + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +// +// The global variable +// + +PFLT_FILTER gFilterInstance; + +#define CGDBG_TRACE_ROUTINES 0x00000001 +#define CGDBG_TRACE_OPERATION_STATUS 0x00000002 +#define CGDBG_TRACE_DEBUG 0x00000004 +#define CGDBG_TRACE_ERROR 0x00000008 + +static ULONG gTraceFlags = CGDBG_TRACE_DEBUG | CGDBG_TRACE_ERROR; + +#define CG_DBG_PRINT( _dbgLevel, _string ) \ + (FlagOn(gTraceFlags,(_dbgLevel)) ? \ + DbgPrint _string : \ + ((int)0)) + +#endif diff --git a/filesys/miniFilter/change/change.inf b/filesys/miniFilter/change/change.inf new file mode 100644 index 00000000..71d602fb --- /dev/null +++ b/filesys/miniFilter/change/change.inf @@ -0,0 +1,95 @@ +;;; +;;; Change +;;; +;;; +;;; Copyright (c) Microsoft Corporation. All Rights Reserved +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2011,1.0.0.1 +CatalogFile = change.cat + + +[DestinationDirs] +DefaultDestDir = 12 +Change.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = Change.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,Change.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = Change.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[Change.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = Change.AddRegistry + +; +; Registry Modifications +; + +[Change.AddRegistry] +HKR,,"DebugFlags",0x00010001 ,0x0 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[Change.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +change.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "A File Change Monitoring Mini-Filter Driver" +ServiceName = "change" +DriverName = "change" +DiskId1 = "File Change Monitoring Device Installation Disk" + +;Instances specific information. +DefaultInstance = "change Instance" +Instance1.Name = "change Instance" +Instance1.Altitude = "370160" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/change/change.rc b/filesys/miniFilter/change/change.rc new file mode 100644 index 00000000..b36223cd --- /dev/null +++ b/filesys/miniFilter/change/change.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "File Change Monitoring Filter Driver" +#define VER_INTERNALNAME_STR "change.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/change/change.sln b/filesys/miniFilter/change/change.sln new file mode 100644 index 00000000..98e886ed --- /dev/null +++ b/filesys/miniFilter/change/change.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "change", "change.vcxproj", "{0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|Win32.ActiveCfg = Debug|Win32 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|Win32.Build.0 = Debug|Win32 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|Win32.ActiveCfg = Release|Win32 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|Win32.Build.0 = Release|Win32 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|x64.ActiveCfg = Debug|x64 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|x64.Build.0 = Debug|x64 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|x64.ActiveCfg = Release|x64 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/change/change.vcxproj b/filesys/miniFilter/change/change.vcxproj new file mode 100644 index 00000000..d16c83bf --- /dev/null +++ b/filesys/miniFilter/change/change.vcxproj @@ -0,0 +1,181 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442} + $(MSBuildProjectName) + Debug + Win32 + {1794254B-AC98-41EB-A845-110FCD1AA932} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + change + + + change + + + change + + + change + + + + true + Level4 + %(PreprocessorDefinitions) + + + + + %(PreprocessorDefinitions) + + + %(PreprocessorDefinitions) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + %(PreprocessorDefinitions) + + + + + %(PreprocessorDefinitions) + + + %(PreprocessorDefinitions) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + %(PreprocessorDefinitions) + + + + + %(PreprocessorDefinitions) + + + %(PreprocessorDefinitions) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + %(PreprocessorDefinitions) + + + + + %(PreprocessorDefinitions) + + + %(PreprocessorDefinitions) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/change/change.vcxproj.Filters b/filesys/miniFilter/change/change.vcxproj.Filters new file mode 100644 index 00000000..1bca247f --- /dev/null +++ b/filesys/miniFilter/change/change.vcxproj.Filters @@ -0,0 +1,34 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {D9D46B85-B0A5-4C33-8BE4-28566220F42D} + + + h;hpp;hxx;hm;inl;inc;xsd + {B66FFE81-FB9C-4422-BF7B-795BA16B48F1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {78FB7669-0DC6-4483-9EAB-571950829035} + + + inf;inv;inx;mof;mc; + {2E70873B-AF69-4324-ACF7-7318A567F6B3} + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/change/context.c b/filesys/miniFilter/change/context.c new file mode 100644 index 00000000..b15ebac4 --- /dev/null +++ b/filesys/miniFilter/change/context.c @@ -0,0 +1,609 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Module Name: + + context.c + +Abstract: + + Filter Context-related module implementation. + +Environment: + + Kernel mode + +--*/ + +#include "change.h" + +// +// Local function prototypes. +// + +NTSTATUS +CgCreateFileContext ( + _Outptr_ PCG_FILE_CONTEXT *FileContext + ); + +VOID +CgFileContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +CgTransactionContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CgCreateFileContext) +#pragma alloc_text(PAGE, CgFindOrCreateFileContext) +#pragma alloc_text(PAGE, CgFindOrCreateTransactionContext) +#pragma alloc_text(PAGE, CgFileContextCleanup) +#pragma alloc_text(PAGE, CgTransactionContextCleanup) +#endif + +// +// Context registration structure +// + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_FILE_CONTEXT, + 0, + CgFileContextCleanup, + CG_FILE_CONTEXT_SIZE, + CG_FILE_CONTEXT_TAG }, + + { FLT_TRANSACTION_CONTEXT, + 0, + CgTransactionContextCleanup, + CG_TRANSACTION_CONTEXT_SIZE, + CG_TRANSACTION_CONTEXT_TAG }, + + { FLT_CONTEXT_END } +}; + + +VOID +CgFileContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine is called whenever the file context is about to be destroyed. + Typically we need to clean the data structure inside it. + +Arguments: + + Context - Pointer to the PCG_FILE_CONTEXT data structure. + + ContextType - This value should be FLT_FILE_CONTEXT. + +Return Value: + + None + +--*/ +{ + PCG_FILE_CONTEXT fileContext = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ContextType ); + + fileContext = (PCG_FILE_CONTEXT) Context; + + FLT_ASSERTMSG( "[CG]: File context is not supposed to be in the transaction context list at cleanup.!\n", + NULL == fileContext->TxContext ); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: Cleaning up file context for file ID %I64x,%I64x (FileContext = %p), dirty = %d\n", + fileContext->FileID.FileId64.UpperZeroes, + fileContext->FileID.FileId64.Value, + fileContext, + fileContext->Dirty) ); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: File context cleanup complete.\n") ); + + +} + +VOID +CgTransactionContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine is called whenever the file context is about to be destroyed. + Typically we need to clean the data structure inside it. + +Arguments: + + Context - Pointer to the PCG_TRANSACTION_CONTEXT data structure. + + ContextType - This value should be FLT_TRANSACTION_CONTEXT. + +Return Value: + + None + +--*/ +{ + PCG_TRANSACTION_CONTEXT transactionContext = (PCG_TRANSACTION_CONTEXT) Context; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ContextType ); + + CG_DBG_PRINT( CGDBG_TRACE_DEBUG, + ("[CG]: CgTransactionContextCleanup context cleanup entered.\n") ); + + CgFreeMutex( transactionContext->Mutex ); + transactionContext->Mutex = NULL; + ObDereferenceObject( transactionContext->Transaction ); + transactionContext->Transaction = NULL; +} + +NTSTATUS +CgGetFileId ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PCG_FILE_REFERENCE FileId + ) +/*++ + +Routine Description: + + This routine gets the File ID, given a file object. It deals with both, + the 128-bit (ReFS) and 64-bits FileIDs. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + FileId - Pointer to file id. This is the output + +Return Value: + + Returns status forwarded from FltQueryInformationFile. + On success, FileId will hold the FileID for the file. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FLT_FILESYSTEM_TYPE type; + + // + // Query for what type of filesystem we are sitting on top of. + // + + status = FltGetFileSystemType( Instance, &type ); + + if (NT_SUCCESS( status )) { + + if (type == FLT_FSTYPE_REFS) { + + FILE_ID_INFORMATION fileIdInformation; + + status = FltQueryInformationFile( Instance, + FileObject, + &fileIdInformation, + sizeof(FILE_ID_INFORMATION), + FileIdInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + RtlCopyMemory(&FileId->FileId128, &fileIdInformation.FileId, sizeof(FileId->FileId128) ); + } + + } else { + + FILE_INTERNAL_INFORMATION fileInternalInformation; + + status = FltQueryInformationFile( Instance, + FileObject, + &fileInternalInformation, + sizeof(FILE_INTERNAL_INFORMATION), + FileInternalInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + FileId->FileId64.Value = fileInternalInformation.IndexNumber.QuadPart; + FileId->FileId64.UpperZeroes = 0LL; + } + } + } + + return status; +} + +NTSTATUS +CgFindOrCreateFileContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _Outptr_ PCG_FILE_CONTEXT *FileContext + ) +/*++ + +Routine Description: + + This routine finds the file context for the target file. + If the context does not exist this routing creates + a new one and attaches the context to the file. + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + FileContext - Returns the file context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCG_FILE_CONTEXT fileContext; + PCG_FILE_CONTEXT oldFileContext; + + PAGED_CODE(); + + *FileContext = NULL; + + // + // First try to get the file context. + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: Trying to get file context (FileObject = %p, Instance = %p, rq = %d)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance, + Cbd->Iopb->MajorFunction) ); + + status = FltGetFileContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + &fileContext ); + + // + // If the call failed because the context does not exist + // and the user wants to creat a new one, then create a + // new context + // + + if (status == STATUS_NOT_FOUND) { + + CG_FILE_REFERENCE fileID; + + status = CgGetFileId( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + &fileID ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to get file id with status 0x%x. (FileObject = %p, Instance = %p, rq = %d)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance, + Cbd->Iopb->MajorFunction) ); + + return status; + + } + + // + // Create a file context + // + + status = CgCreateFileContext( &fileContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to create file context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // Initiailize fileContext + // + + RtlCopyMemory( &fileContext->FileID, &fileID, sizeof(fileContext->FileID) ); + + // + // Set the new context we just allocated on the file object + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: Setting file context %p (FileObject = %p, Instance = %p)\n", + fileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = FltSetFileContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + fileContext, + &oldFileContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to set file context with status 0x%x. (FileObject = %p, Instance = %p, rq = %d)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance, + Cbd->Iopb->MajorFunction) ); + // + // We release the context here because FltSetFileContext failed + // + // If FltSetFileContext succeeded then the context will be returned + // to the caller. The caller will use the context and then release it + // when he is done with the context. + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: Releasing file context %p (FileObject = %p, Instance = %p)\n", + fileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + FltReleaseContext( fileContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + // + // FltSetFileContext failed for a reason other than the context already + // existing on the file. So the object now does not have any context set + // on it. So we return failure to the caller. + // + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to set file context with status 0x%x != STATUS_FLT_CONTEXT_ALREADY_DEFINED. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // Race condition. Someone has set a context after we queried it. + // Use the already set context instead + // + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Race: File context already defined. Retaining old file context %p (FileObject = %p, Instance = %p)\n", + oldFileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + // + // Return the existing context. Note that the new context that we allocated has already been + // realeased above. + // + + fileContext = oldFileContext; + status = STATUS_SUCCESS; + + } + } + + *FileContext = fileContext; + + return status; +} + + +NTSTATUS +CgCreateFileContext ( + _Outptr_ PCG_FILE_CONTEXT *FileContext + ) +/*++ + +Routine Description: + + This routine creates a new file context + +Arguments: + + FileContext - Returns the file context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCG_FILE_CONTEXT fileContext; + + PAGED_CODE(); + + // + // Allocate a file context + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: Allocating file context \n") ); + + status = FltAllocateContext( gFilterInstance, + FLT_FILE_CONTEXT, + CG_FILE_CONTEXT_SIZE, + PagedPool, + &fileContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to allocate file context with status 0x%x \n", + status) ); + return status; + } + + // + // Initialize the newly created context + // + + RtlZeroMemory(fileContext, CG_FILE_CONTEXT_SIZE); + *FileContext = fileContext; + + return STATUS_SUCCESS; +} + +NTSTATUS +CgFindOrCreateTransactionContext( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_ PCG_TRANSACTION_CONTEXT *TransactionContext + ) +/*++ + +Routine Description + + This routine finds the transaction context, if not found, it will + try to create a new one. The caller is responsible for calling + FltReleaseContext to decrement its reference count. + +Arguments + + FltObjects - Contains parameters required to enlist in a transaction. + TransactionContext - Returns the transaction context + +Return value + + Returns STATUS_SUCCESS if we were able to successfully find/create + a transaction context. Returns an appropriate error code on a failure. + +--*/ +{ + NTSTATUS status; + PCG_TRANSACTION_CONTEXT transactionContext = NULL; + PCG_TRANSACTION_CONTEXT oldTransactionContext = NULL; + PFAST_MUTEX pFastMutex = NULL; + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_DEBUG, + ("[CG]: CgFindOrCreateTransactionContext entered. \n") ); + + FLT_ASSERTMSG( "[CG]: Transaction object pointer is not supposed to be NULL !\n", FltObjects->Transaction != NULL); + + status = FltGetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + &transactionContext ); + + if (NT_SUCCESS( status )) { + + *TransactionContext = transactionContext; + return STATUS_SUCCESS; + } + + if (status != STATUS_NOT_FOUND) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to get transaction context with status 0x%x \n", + status) ); + return status; + } + + // + // Allocate the resource + // + + pFastMutex = CgAllocateMutex(); + + if ( NULL == pFastMutex ) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Allocate a transaction context. + // + + status = FltAllocateContext( gFilterInstance, + FLT_TRANSACTION_CONTEXT, + CG_TRANSACTION_CONTEXT_SIZE, + PagedPool, + &transactionContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to allocate transaction context with status 0x%x \n", + status) ); + + CgFreeMutex( pFastMutex ); + return status; + } + + FLT_ASSERTMSG( "[CG]: Transaction object pointer is not supposed to be NULL !\n", FltObjects->Transaction != NULL); + + // + // Initialization of transaction context. + // The reason we allocate eResource seperately is because + // eResource has to be allocated in the non-paged pool. + // + + RtlZeroMemory(transactionContext, CG_TRANSACTION_CONTEXT_SIZE); + transactionContext->Mutex = pFastMutex; + ObReferenceObject( FltObjects->Transaction ); + transactionContext->Transaction = FltObjects->Transaction; + InitializeListHead( &transactionContext->ScListHead ); + ExInitializeFastMutex( transactionContext->Mutex ); + + status = FltSetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + transactionContext, + &oldTransactionContext ); + + if (NT_SUCCESS( status )) { + + *TransactionContext = transactionContext; + return STATUS_SUCCESS; + } + + + FltReleaseContext( transactionContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to set transaction context with status 0x%x \n", + status) ); + + return status; + } + + FLT_ASSERTMSG( "[CG]: if FltSetTransactionContext returns STATUS_FLT_CONTEXT_ALREADY_DEFINED, the pointer should not be NULL.\n", + oldTransactionContext != NULL); + + *TransactionContext = oldTransactionContext; + + + return STATUS_SUCCESS; +} + + diff --git a/filesys/miniFilter/change/context.h b/filesys/miniFilter/change/context.h new file mode 100644 index 00000000..33bc0aa8 --- /dev/null +++ b/filesys/miniFilter/change/context.h @@ -0,0 +1,151 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Module Name: + + context.h + +Abstract: + + Header file which contains context-related data + structures, type definitions, constants, + global variables and function prototypes. + +Environment: + + Kernel mode + +--*/ + +#ifndef __CONTEXT_H__ +#define __CONTEXT_H__ + +#define CG_FILE_CONTEXT_TAG 'cFcG' +#define CG_TRANSACTION_CONTEXT_TAG 'cTcG' + +// +// Defines the transaction context structure +// + +typedef struct _CG_TRANSACTION_CONTEXT { + + // + // Transaction object pointer + // + + PKTRANSACTION Transaction; + + // + // A flag that tracks if it has ben enlisted in transaction + // + + BOOLEAN Enlisted; + + // + // A flag that indicates if the fc list is drained + // + + BOOLEAN ListDrained; + + // + // List head for file context list. + // The list is grown only when transacted writers are part of the + // transaction, i.e. this list contains all file contexts likely + // to be modified in a transaction. + // + + LIST_ENTRY ScListHead; + + // + // Lock used to protect the list. + // + + PFAST_MUTEX Mutex; + +} CG_TRANSACTION_CONTEXT, *PCG_TRANSACTION_CONTEXT; + +#define CG_TRANSACTION_CONTEXT_SIZE sizeof( CG_TRANSACTION_CONTEXT ) + +// +// This is to deal with ReFS' 128-bit file IDs & NTFS' 64-bit FileIDs. +// + +typedef union _CG_FILE_REFERENCE { + + // + // For 64-bit fileIDs the upper 64-bits are always zeroes. + // + + struct { + ULONGLONG Value; + ULONGLONG UpperZeroes; + } FileId64; + + FILE_ID_128 FileId128; + +} CG_FILE_REFERENCE, *PCG_FILE_REFERENCE; + +// +// File context data structure +// + +typedef struct _CG_FILE_CONTEXT { + + // + // File ID, obtained from querying the file system for + // FileInternalInformation or FileIdInformation. + // + + CG_FILE_REFERENCE FileID; + + // + // The flag that we use to record if the file is dirty + // if we have seen it before. + // + + BOOLEAN Dirty; + + // + // TxDirty is to record if the file is dirty in a + // transaction + // + + BOOLEAN TxDirty; + + // + // A pointer to the transaction context, so we can jump to list in the transaction. + // + + PCG_TRANSACTION_CONTEXT TxContext; + + // + // This list entry is exactly the embedded entry to + // form a doubly linked list inside transaction context. + // + + LIST_ENTRY ListInTransaction; + +} CG_FILE_CONTEXT, *PCG_FILE_CONTEXT; + +#define CG_FILE_CONTEXT_SIZE sizeof( CG_FILE_CONTEXT ) + + + + +NTSTATUS +CgFindOrCreateFileContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _Outptr_ PCG_FILE_CONTEXT *FileContext + ); + + +NTSTATUS +CgFindOrCreateTransactionContext( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_ PCG_TRANSACTION_CONTEXT *TransactionContext + ); + + +#endif + diff --git a/filesys/miniFilter/change/utility.h b/filesys/miniFilter/change/utility.h new file mode 100644 index 00000000..bb7743ad --- /dev/null +++ b/filesys/miniFilter/change/utility.h @@ -0,0 +1,58 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Module Name: + + utility.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. The functions include + generic table routines. + +Environment: + + Kernel mode + +--*/ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + +#define CG_MUTEX_TAG 'tMgC' + +FORCEINLINE +PFAST_MUTEX +CgAllocateMutex ( + VOID + ) +{ + // + // Fast mutex by its rule has to be in the non-paged pool + // + + return ExAllocatePoolWithTag( NonPagedPoolNx, + sizeof( FAST_MUTEX ), + CG_MUTEX_TAG ); +} + +FORCEINLINE +VOID +CgFreeMutex ( + _In_ PFAST_MUTEX Mutex + ) +{ + + ExFreePoolWithTag( Mutex, + CG_MUTEX_TAG ); +} + +#define LIST_FOR_EACH_SAFE(curr, n, head) \ + for (curr = (head)->Flink , n = curr->Flink ; curr != (head); \ + curr = n, n = curr->Flink ) + + +#endif + diff --git a/filesys/miniFilter/ctx/CtxInit.c b/filesys/miniFilter/ctx/CtxInit.c new file mode 100644 index 00000000..2654b9f9 --- /dev/null +++ b/filesys/miniFilter/ctx/CtxInit.c @@ -0,0 +1,908 @@ +/*++ + +Copyright (c) 1999 - 2003 Microsoft Corporation + +Module Name: + + ContextInit.c + +Abstract: + + This is the main module of the kernel mode filter driver implementing + the context sample. + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + +// +// Global variables +// + +CTX_GLOBAL_DATA Globals; + + +// +// Local function prototypes +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +CtxUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +VOID +CtxContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +NTSTATUS +CtxInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +NTSTATUS +CtxInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +VOID +CtxInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +CtxInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +#if DBG + +VOID +CtxInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ); + +#endif + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) + +#if DBG +#pragma alloc_text(INIT, CtxInitializeDebugLevel) +#endif + +#pragma alloc_text(PAGE, CtxUnload) +#pragma alloc_text(PAGE, CtxContextCleanup) +#pragma alloc_text(PAGE, CtxInstanceSetup) +#pragma alloc_text(PAGE, CtxInstanceQueryTeardown) +#pragma alloc_text(PAGE, CtxInstanceTeardownStart) +#pragma alloc_text(PAGE, CtxInstanceTeardownComplete) +#endif + + +// +// Filters callback routines +// + +FLT_OPERATION_REGISTRATION Callbacks[] = { + + { IRP_MJ_CREATE, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + CtxPreCreate, + CtxPostCreate }, + + { IRP_MJ_CLEANUP, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + CtxPreCleanup, + NULL }, + + { IRP_MJ_CLOSE, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + CtxPreClose, + NULL }, + + { IRP_MJ_SET_INFORMATION, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + CtxPreSetInfo, + CtxPostSetInfo }, + + { IRP_MJ_OPERATION_END } +}; + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_INSTANCE_CONTEXT, + 0, + CtxContextCleanup, + CTX_INSTANCE_CONTEXT_SIZE, + CTX_INSTANCE_CONTEXT_TAG }, + + { FLT_FILE_CONTEXT, + 0, + CtxContextCleanup, + CTX_FILE_CONTEXT_SIZE, + CTX_FILE_CONTEXT_TAG }, + + { FLT_STREAM_CONTEXT, + 0, + CtxContextCleanup, + CTX_STREAM_CONTEXT_SIZE, + CTX_STREAM_CONTEXT_TAG }, + + { FLT_STREAMHANDLE_CONTEXT, + 0, + CtxContextCleanup, + CTX_STREAMHANDLE_CONTEXT_SIZE, + CTX_STREAMHANDLE_CONTEXT_TAG }, + + { FLT_CONTEXT_END } +}; + +// +// Filters registration data structure +// + +FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + ContextRegistration, // Context + Callbacks, // Operation callbacks + CtxUnload, // Filters unload routine + CtxInstanceSetup, // InstanceSetup routine + CtxInstanceQueryTeardown, // InstanceQueryTeardown routine + CtxInstanceTeardownStart, // InstanceTeardownStart routine + CtxInstanceTeardownComplete, // InstanceTeardownComplete routine + NULL, NULL, NULL // Unused naming support callbacks +}; + +// +// Filter driver initialization and unload routines +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this filter driver. It registers + itself with the filter manager and initializes all its global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS status; + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + RtlZeroMemory( &Globals, sizeof( Globals ) ); + +#if DBG + + // + // Initialize global debug level + // + + CtxInitializeDebugLevel( RegistryPath ); + +#else + + UNREFERENCED_PARAMETER( RegistryPath ); + +#endif + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Ctx]: Driver being loaded\n") ); + + + + // + // Register with the filter manager + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // Start filtering I/O + // + + status = FltStartFiltering( Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( Globals.Filter ); + } + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Ctx]: Driver loaded complete (Status = 0x%08X)\n", + status) ); + + return status; +} + +#if DBG + +VOID +CtxInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to read the filter DebugLevel parameter from + the registry. This value will be found in the registry location + indicated by the RegistryPath passed in. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + None. + +--*/ +{ + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey; + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING valueName; + UCHAR buffer[sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + sizeof( LONG )]; + + Globals.DebugLevel = DEBUG_TRACE_ERROR; + + // + // Open the desired registry key + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (NT_SUCCESS( status )) { + + // + // Read the DebugFlags value from the registry. + // + + RtlInitUnicodeString( &valueName, L"DebugLevel" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.DebugLevel = *((PULONG) &(((PKEY_VALUE_PARTIAL_INFORMATION) buffer)->Data)); + } + + // + // Close the registry entry + // + + ZwClose( driverRegKey ); + + } + +} + +#endif + +NTSTATUS +CtxUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this filter driver. This is called + when the minifilter is about to be unloaded. We can fail this unload + request if this is not a mandatory unloaded indicated by the Flags + parameter. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Ctx]: Unloading driver\n") ); + + + FltUnregisterFilter( Globals.Filter ); + Globals.Filter = NULL; + + return STATUS_SUCCESS; +} + +VOID +CtxContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +{ + PCTX_INSTANCE_CONTEXT instanceContext; + PCTX_FILE_CONTEXT fileContext; + PCTX_STREAM_CONTEXT streamContext; + PCTX_STREAMHANDLE_CONTEXT streamHandleContext; + + PAGED_CODE(); + + switch(ContextType) { + + case FLT_INSTANCE_CONTEXT: + + instanceContext = (PCTX_INSTANCE_CONTEXT) Context; + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Cleaning up instance context for volume %wZ (Context = %p)\n", + &instanceContext->VolumeName, + Context) ); + + // + // Here the filter should free memory or synchronization objects allocated to + // objects within the instance context. The instance context itself should NOT + // be freed. It will be freed by Filter Manager when the ref count on the + // context falls to zero. + // + + CtxFreeUnicodeString( &instanceContext->VolumeName ); + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Instance context cleanup complete.\n") ); + + break; + + + case FLT_FILE_CONTEXT: + + fileContext = (PCTX_FILE_CONTEXT) Context; + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Cleaning up file context for file %wZ (FileContext = %p)\n", + &fileContext->FileName, + fileContext) ); + + + // + // Free the file name + // + + if (fileContext->FileName.Buffer != NULL) { + + CtxFreeUnicodeString(&fileContext->FileName); + } + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: File context cleanup complete.\n") ); + + break; + + case FLT_STREAM_CONTEXT: + + streamContext = (PCTX_STREAM_CONTEXT) Context; + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Cleaning up stream context for file %wZ (StreamContext = %p) \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + &streamContext->FileName, + streamContext, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Delete the resource and memory the memory allocated for the resource + // + + if (streamContext->Resource != NULL) { + + ExDeleteResourceLite( streamContext->Resource ); + CtxFreeResource( streamContext->Resource ); + } + + // + // Free the file name + // + + if (streamContext->FileName.Buffer != NULL) { + + CtxFreeUnicodeString(&streamContext->FileName); + } + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Stream context cleanup complete.\n") ); + + break; + + case FLT_STREAMHANDLE_CONTEXT: + + streamHandleContext = (PCTX_STREAMHANDLE_CONTEXT) Context; + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Cleaning up stream handle context for file %wZ (StreamContext = %p)\n", + &streamHandleContext->FileName, + streamHandleContext) ); + + // + // Delete the resource and memory the memory allocated for the resource + // + + if (streamHandleContext->Resource != NULL) { + + ExDeleteResourceLite( streamHandleContext->Resource ); + CtxFreeResource( streamHandleContext->Resource ); + } + + // + // Free the file name + // + + if (streamHandleContext->FileName.Buffer != NULL) { + + CtxFreeUnicodeString(&streamHandleContext->FileName); + } + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Stream handle context cleanup complete.\n") ); + + break; + + } + +} + +// +// Instance setup/teardown routines. +// + +NTSTATUS +CtxInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. This + gives us a chance to decide if we need to attach to this volume or not. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + PCTX_INSTANCE_CONTEXT instanceContext = NULL; + NTSTATUS status = STATUS_SUCCESS; + ULONG volumeNameLength; + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance setup started (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + + // + // Allocate and initialize the context for this volume + // + + + // + // Allocate the instance context + // + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Allocating instance context (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + status = FltAllocateContext( FltObjects->Filter, + FLT_INSTANCE_CONTEXT, + CTX_INSTANCE_CONTEXT_SIZE, + NonPagedPool, + &instanceContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate instance context (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + + goto CtxInstanceSetupCleanup; + } + + // + // Get the NT volume name length + // + + status = FltGetVolumeName( FltObjects->Volume, NULL, &volumeNameLength ); + + if( !NT_SUCCESS( status ) && + (status != STATUS_BUFFER_TOO_SMALL) ) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Unexpected failure in FltGetVolumeName. (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + + goto CtxInstanceSetupCleanup; + } + + // + // Allocate a string big enough to take the volume name + // + + instanceContext->VolumeName.MaximumLength = (USHORT) volumeNameLength; + status = CtxAllocateUnicodeString( &instanceContext->VolumeName ); + + if( !NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate volume name string. (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + + goto CtxInstanceSetupCleanup; + } + + // + // Get the NT volume name + // + + status = FltGetVolumeName( FltObjects->Volume, &instanceContext->VolumeName, &volumeNameLength ); + + if( !NT_SUCCESS( status ) ) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Unexpected failure in FltGetVolumeName. (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + + goto CtxInstanceSetupCleanup; + } + + + instanceContext->Instance = FltObjects->Instance; + instanceContext->Volume = FltObjects->Volume; + + // + // Set the instance context. + // + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Setting instance context %p for volume %wZ (Volume = %p, Instance = %p)\n", + instanceContext, + &instanceContext->VolumeName, + FltObjects->Volume, + FltObjects->Instance) ); + + status = FltSetInstanceContext( FltObjects->Instance, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + instanceContext, + NULL ); + + if( !NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to set instance context for volume %wZ (Volume = %p, Instance = %p, Status = 0x%08X)\n", + &instanceContext->VolumeName, + FltObjects->Volume, + FltObjects->Instance, + status) ); + goto CtxInstanceSetupCleanup; + } + + +CtxInstanceSetupCleanup: + + // + // If FltAllocateContext suceeded then we MUST release the context, + // irrespective of whether FltSetInstanceContext suceeded or not. + // + // FltAllocateContext increments the ref count by one. + // A successful FltSetInstanceContext increments the ref count by one + // and also associates the context with the file system object + // + // FltReleaseContext decrements the ref count by one. + // + // When FltSetInstanceContext succeeds, calling FltReleaseContext will + // leave the context with a ref count of 1 corresponding to the internal + // reference to the context from the file system structures + // + // When FltSetInstanceContext fails, calling FltReleaseContext will + // leave the context with a ref count of 0 which is correct since + // there is no reference to the context from the file system structures + // + + if ( instanceContext != NULL ) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing instance context %p (Volume = %p, Instance = %p)\n", + instanceContext, + FltObjects->Volume, + FltObjects->Instance) ); + + FltReleaseContext( instanceContext ); + } + + + if (NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance setup complete (Volume = %p, Instance = %p). Filter will attach to the volume.\n", + FltObjects->Volume, + FltObjects->Instance) ); + } else { + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance setup complete (Volume = %p, Instance = %p). Filter will not attach to the volume.\n", + FltObjects->Volume, + FltObjects->Instance) ); + } + + return status; +} + + +NTSTATUS +CtxInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach thereby giving us a + chance to fail that detach request. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Returns the status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance query teardown started (Instance = %p)\n", + FltObjects->Instance) ); + + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance query teadown ended (Instance = %p)\n", + FltObjects->Instance) ); + return STATUS_SUCCESS; +} + + +VOID +CtxInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the start of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance teardown start started (Instance = %p)\n", + FltObjects->Instance) ); + + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance teardown start ended (Instance = %p)\n", + FltObjects->Instance) ); +} + + +VOID +CtxInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the end of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + PCTX_INSTANCE_CONTEXT instanceContext; + NTSTATUS status; + + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance teardown complete started (Instance = %p)\n", + FltObjects->Instance) ); + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Getting instance context (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Instance teardown for volume %wZ (Volume = %p, Instance = %p, InstanceContext = %p)\n", + &instanceContext->VolumeName, + FltObjects->Volume, + FltObjects->Instance, + instanceContext) ); + + + // + // Here the filter may perform any teardown of its own structures associated + // with this instance. + // + // The filter should not free memory or synchronization objects allocated to + // objects within the instance context. That should be performed in the + // cleanup callback for the instance context + // + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing instance context %p for volume %wZ (Volume = %p, Instance = %p)\n", + instanceContext, + &instanceContext->VolumeName, + FltObjects->Volume, + FltObjects->Instance) ); + + FltReleaseContext( instanceContext ); + } else { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to get instance context (Volume = %p, Instance = %p Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + } + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance teardown complete ended (Instance = %p)\n", + FltObjects->Instance) ); +} + diff --git a/filesys/miniFilter/ctx/CtxProc.h b/filesys/miniFilter/ctx/CtxProc.h new file mode 100644 index 00000000..06873a23 --- /dev/null +++ b/filesys/miniFilter/ctx/CtxProc.h @@ -0,0 +1,230 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + CtxProc.h + +Abstract: + + This is the header file defining the functions of the kernel mode + filter driver implementing the context sample. + + +Environment: + + Kernel mode + + +--*/ + + +// +// Functions implemented in operations.c +// + +FLT_PREOP_CALLBACK_STATUS +CtxPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +CtxPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_opt_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +CtxPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +CtxPreClose ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + + +FLT_PREOP_CALLBACK_STATUS +CtxPreSetInfo ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +CtxPostSetInfo ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_opt_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + + +// +// Functions implemented in context.c +// + +NTSTATUS +CtxFindOrCreateFileContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN CreateIfNotFound, + _When_( CreateIfNotFound != FALSE, _In_ ) _When_( CreateIfNotFound == FALSE, _In_opt_ ) PUNICODE_STRING FileName, + _Outptr_ PCTX_FILE_CONTEXT *StreamContext, + _Out_opt_ PBOOLEAN ContextCreated + ); + +NTSTATUS +CtxCreateFileContext ( + _In_ PUNICODE_STRING FileName, + _Outptr_ PCTX_FILE_CONTEXT *StreamContext + ); + + +NTSTATUS +CtxFindOrCreateStreamContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN CreateIfNotFound, + _Outptr_ PCTX_STREAM_CONTEXT *StreamContext, + _Out_opt_ PBOOLEAN ContextCreated + ); + +NTSTATUS +CtxCreateStreamContext ( + _Outptr_ PCTX_STREAM_CONTEXT *StreamContext + ); + +NTSTATUS +CtxUpdateNameInStreamContext ( + _In_ PUNICODE_STRING DirectoryName, + _Inout_ PCTX_STREAM_CONTEXT StreamContext + ); + +NTSTATUS +CtxCreateOrReplaceStreamHandleContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN ReplaceIfExists, + _Outptr_ PCTX_STREAMHANDLE_CONTEXT *StreamHandleContext, + _Out_opt_ PBOOLEAN ContextReplaced + ); + +NTSTATUS +CtxCreateStreamHandleContext ( + _Outptr_ PCTX_STREAMHANDLE_CONTEXT *StreamHandleContext + ); + +NTSTATUS +CtxUpdateNameInStreamHandleContext ( + _In_ PUNICODE_STRING DirectoryName, + _Inout_ PCTX_STREAMHANDLE_CONTEXT StreamHandleContext + ); + + +// +// Functions implemented in support.c +// + +_At_(String->Length, _Out_range_(==, 0)) +_At_(String->MaximumLength, _In_) +_At_(String->Buffer, _Pre_maybenull_ _Post_notnull_ _Post_writable_byte_size_(String->MaximumLength)) +NTSTATUS +CtxAllocateUnicodeString ( + _Out_ PUNICODE_STRING String + ); + +_At_(String->Length, _Out_range_(==, 0)) +_At_(String->MaximumLength, _Out_range_(==, 0)) +_At_(String->Buffer, _Pre_notnull_ _Post_null_) +VOID +CtxFreeUnicodeString ( + _Pre_notnull_ PUNICODE_STRING String + ); + + +// +// Resource support +// + +FORCEINLINE +PERESOURCE +CtxAllocateResource ( + VOID + ) +{ + + return ExAllocatePoolWithTag( NonPagedPool, + sizeof( ERESOURCE ), + CTX_RESOURCE_TAG ); +} + +FORCEINLINE +VOID +CtxFreeResource ( + _In_ PERESOURCE Resource + ) +{ + + ExFreePoolWithTag( Resource, + CTX_RESOURCE_TAG ); +} + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +CtxAcquireResourceExclusive ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_exclusive_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + !ExIsResourceAcquiredSharedLite(Resource)); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +CtxAcquireResourceShared ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_shared_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceSharedLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Releases_lock_(_Global_critical_region_) +_Requires_lock_held_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +CtxReleaseResource ( + _Inout_ _Requires_lock_held_(*_Curr_) _Releases_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + ExIsResourceAcquiredSharedLite(Resource)); + + ExReleaseResourceLite(Resource); + KeLeaveCriticalRegion(); +} + + diff --git a/filesys/miniFilter/ctx/CtxStruc.h b/filesys/miniFilter/ctx/CtxStruc.h new file mode 100644 index 00000000..831c08f1 --- /dev/null +++ b/filesys/miniFilter/ctx/CtxStruc.h @@ -0,0 +1,213 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + CtxStruct.h + +Abstract: + + This is the header file defining the data structures used by the kernel mode + filter driver implementing the context sample. + + +Environment: + + Kernel mode + + +--*/ + +// +// Memory Pool Tags +// + +#define CTX_STRING_TAG 'tSxC' +#define CTX_RESOURCE_TAG 'cRxC' +#define CTX_INSTANCE_CONTEXT_TAG 'cIxC' +#define CTX_FILE_CONTEXT_TAG 'cFxC' +#define CTX_STREAM_CONTEXT_TAG 'cSxC' +#define CTX_STREAMHANDLE_CONTEXT_TAG 'cHxC' + + +// +// Context sample filter global data +// + +typedef struct _CTX_GLOBAL_DATA { + + // + // Handle to minifilter returned from FltRegisterFilter() + // + + PFLT_FILTER Filter; + +#if DBG + + // + // Field to control nature of debug output + // + + ULONG DebugLevel; +#endif + +} CTX_GLOBAL_DATA, *PCTX_GLOBAL_DATA; + +extern CTX_GLOBAL_DATA Globals; + + + + +// +// Instance context data structure +// + +typedef struct _CTX_INSTANCE_CONTEXT { + + // + // Instance for this context. + // + + PFLT_INSTANCE Instance; + + // + // Volume associated with this instance. + // + + PFLT_VOLUME Volume; + + // + // Name of the volume associated with this instance. + // + + UNICODE_STRING VolumeName; + +} CTX_INSTANCE_CONTEXT, *PCTX_INSTANCE_CONTEXT; + +#define CTX_INSTANCE_CONTEXT_SIZE sizeof( CTX_INSTANCE_CONTEXT ) + + +// +// File context data structure +// + +typedef struct _CTX_FILE_CONTEXT { + + // + // Name of the file associated with this context. + // + + UNICODE_STRING FileName; + + // + // There is no resource to protect the context since the + // filename in the context is never modified. The filename + // is put in when the context is created and then freed + // with context is cleaned-up + // + +} CTX_FILE_CONTEXT, *PCTX_FILE_CONTEXT; + +#define CTX_FILE_CONTEXT_SIZE sizeof( CTX_FILE_CONTEXT ) + + + +// +// Stream context data structure +// + +typedef struct _CTX_STREAM_CONTEXT { + + // + // Name of the file associated with this context. + // + + UNICODE_STRING FileName; + + // + // Number of times we saw a create on this stream + // + + ULONG CreateCount; + + // + // Number of times we saw a cleanup on this stream + // + + ULONG CleanupCount; + + // + // Number of times we saw a close on this stream + // + + ULONG CloseCount; + + // + // Lock used to protect this context. + // + + PERESOURCE Resource; + +} CTX_STREAM_CONTEXT, *PCTX_STREAM_CONTEXT; + +#define CTX_STREAM_CONTEXT_SIZE sizeof( CTX_STREAM_CONTEXT ) + + + +// +// Stream handle context data structure +// + +typedef struct _CTX_STREAMHANDLE_CONTEXT { + + // + // Name of the file associated with this context. + // + + UNICODE_STRING FileName; + + // + // Lock used to protect this context. + // + + PERESOURCE Resource; + +} CTX_STREAMHANDLE_CONTEXT, *PCTX_STREAMHANDLE_CONTEXT; + +#define CTX_STREAMHANDLE_CONTEXT_SIZE sizeof( CTX_STREAMHANDLE_CONTEXT ) + + +// +// Debug helper functions +// + +#if DBG + + +#define DEBUG_TRACE_ERROR 0x00000001 // Errors - whenever we return a failure code +#define DEBUG_TRACE_LOAD_UNLOAD 0x00000002 // Loading/unloading of the filter +#define DEBUG_TRACE_INSTANCES 0x00000004 // Attach / detatch of instances + +#define DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS 0x00000008 // Operation on instance context +#define DEBUG_TRACE_FILE_CONTEXT_OPERATIONS 0x00000010 // Operation on file context +#define DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS 0x00000020 // Operation on stream context +#define DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS 0x00000040 // Operation on stream handle context + +#define DEBUG_TRACE_ALL_IO 0x00000080 // All IO operations tracked by this filter + +#define DEBUG_TRACE_ALL 0xFFFFFFFF // All flags + + +#define DebugTrace(Level, Data) \ + if ((Level) & Globals.DebugLevel) { \ + DbgPrint Data; \ + } + + +#else + +#define DebugTrace(Level, Data) {NOTHING;} + +#endif + diff --git a/filesys/miniFilter/ctx/ReadMe.md b/filesys/miniFilter/ctx/ReadMe.md new file mode 100644 index 00000000..e9dd284e --- /dev/null +++ b/filesys/miniFilter/ctx/ReadMe.md @@ -0,0 +1,14 @@ +Ctx File System Minifilter Driver +================================= + +The Ctx minifilter is an example that demonstrates how to attach contexts to instances, files, streams, and stream handles in your minifilter. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *Ctx* minifilter demonstrates how to attach and remove contexts from instances, files, steams, and stream handles. *Ctx* attaches a context whenever one of these objects is created. While attaching a context to a file, the sample also creates a stream and stream handle context. All contexts are ultimately deleted by the filter manager using the callback function that the *Ctx* minifilter provides. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. diff --git a/filesys/miniFilter/ctx/context.c b/filesys/miniFilter/ctx/context.c new file mode 100644 index 00000000..f9770987 --- /dev/null +++ b/filesys/miniFilter/ctx/context.c @@ -0,0 +1,885 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + context.c + +Abstract: + + This is the stream nd stream handle context module of the kernel mode + context sample filter driver + + +Environment: + + Kernel mode + + +--*/ + + +#include "pch.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CtxFindOrCreateFileContext) +#pragma alloc_text(PAGE, CtxCreateFileContext) +#pragma alloc_text(PAGE, CtxFindOrCreateStreamContext) +#pragma alloc_text(PAGE, CtxCreateStreamContext) +#pragma alloc_text(PAGE, CtxUpdateNameInStreamContext) +#pragma alloc_text(PAGE, CtxCreateOrReplaceStreamHandleContext) +#pragma alloc_text(PAGE, CtxCreateStreamHandleContext) +#pragma alloc_text(PAGE, CtxUpdateNameInStreamHandleContext) +#endif + + + + +NTSTATUS +CtxFindOrCreateFileContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN CreateIfNotFound, + _When_( CreateIfNotFound != FALSE, _In_ ) _When_( CreateIfNotFound == FALSE, _In_opt_ ) PUNICODE_STRING FileName, + _Outptr_ PCTX_FILE_CONTEXT *FileContext, + _Out_opt_ PBOOLEAN ContextCreated + ) +/*++ + +Routine Description: + + This routine finds the file context for the target file. + Optionally, if the context does not exist this routing creates + a new one and attaches the context to the file. + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + CreateIfNotFound - Supplies if the file context must be created if missing + FileName - Supplies the file name + FileContext - Returns the file context + ContextCreated - Returns if a new context was created + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_FILE_CONTEXT fileContext; + PCTX_FILE_CONTEXT oldFileContext; + + PAGED_CODE(); + + *FileContext = NULL; + if (ContextCreated != NULL) *ContextCreated = FALSE; + + // + // First try to get the file context. + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Trying to get file context (FileObject = %p, Instance = %p)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = FltGetFileContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + &fileContext ); + + // + // If the call failed because the context does not exist + // and the user wants to creat a new one, the create a + // new context + // + + if (!NT_SUCCESS( status ) && + (status == STATUS_NOT_FOUND) && + CreateIfNotFound) { + + + // + // Create a file context + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Creating file context (FileObject = %p, Instance = %p)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = CtxCreateFileContext( FileName, &fileContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to create file context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + + // + // Set the new context we just allocated on the file object + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Setting file context %p (FileObject = %p, Instance = %p)\n", + fileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = FltSetFileContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + fileContext, + &oldFileContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set file context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + // + // We release the context here because FltSetFileContext failed + // + // If FltSetFileContext succeeded then the context will be returned + // to the caller. The caller will use the context and then release it + // when he is done with the context. + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing file context %p (FileObject = %p, Instance = %p)\n", + fileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + FltReleaseContext( fileContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + // + // FltSetFileContext failed for a reason other than the context already + // existing on the file. So the object now does not have any context set + // on it. So we return failure to the caller. + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set file context with status 0x%x != STATUS_FLT_CONTEXT_ALREADY_DEFINED. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // Race condition. Someone has set a context after we queried it. + // Use the already set context instead + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: File context already defined. Retaining old file context %p (FileObject = %p, Instance = %p)\n", + oldFileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + // + // Return the existing context. Note that the new context that we allocated has already been + // realeased above. + // + + fileContext = oldFileContext; + status = STATUS_SUCCESS; + + } else { + + if (ContextCreated != NULL) *ContextCreated = TRUE; + } + } + + *FileContext = fileContext; + + return status; +} + + +NTSTATUS +CtxCreateFileContext ( + _In_ PUNICODE_STRING FileName, + _Outptr_ PCTX_FILE_CONTEXT *FileContext + ) +/*++ + +Routine Description: + + This routine creates a new file context + +Arguments: + + FileName - Supplies the file name + FileContext - Returns the file context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_FILE_CONTEXT fileContext; + + PAGED_CODE(); + + // + // Allocate a file context + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Allocating file context \n") ); + + status = FltAllocateContext( Globals.Filter, + FLT_FILE_CONTEXT, + CTX_FILE_CONTEXT_SIZE, + PagedPool, + &fileContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate file context with status 0x%x \n", + status) ); + return status; + } + + // + // Initialize the newly created context + // + + // + // Allocate and copy off the file name + // + + fileContext->FileName.MaximumLength = FileName->Length; + status = CtxAllocateUnicodeString( &fileContext->FileName ); + if (NT_SUCCESS( status )) { + + RtlCopyUnicodeString( &fileContext->FileName, FileName ); + } + + *FileContext = fileContext; + + return STATUS_SUCCESS; +} + + +NTSTATUS +CtxFindOrCreateStreamContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN CreateIfNotFound, + _Outptr_ PCTX_STREAM_CONTEXT *StreamContext, + _Out_opt_ PBOOLEAN ContextCreated + ) +/*++ + +Routine Description: + + This routine finds the stream context for the target stream. + Optionally, if the context does not exist this routing creates + a new one and attaches the context to the stream. + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + CreateIfNotFound - Supplies if the stream must be created if missing + StreamContext - Returns the stream context + ContextCreated - Returns if a new context was created + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_STREAM_CONTEXT streamContext; + PCTX_STREAM_CONTEXT oldStreamContext; + + PAGED_CODE(); + + *StreamContext = NULL; + if (ContextCreated != NULL) *ContextCreated = FALSE; + + // + // First try to get the stream context. + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Trying to get stream context (FileObject = %p, Instance = %p)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = FltGetStreamContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + &streamContext ); + + // + // If the call failed because the context does not exist + // and the user wants to creat a new one, the create a + // new context + // + + if (!NT_SUCCESS( status ) && + (status == STATUS_NOT_FOUND) && + CreateIfNotFound) { + + + // + // Create a stream context + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Creating stream context (FileObject = %p, Instance = %p)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = CtxCreateStreamContext( &streamContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to create stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + + // + // Set the new context we just allocated on the file object + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Setting stream context %p (FileObject = %p, Instance = %p)\n", + streamContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = FltSetStreamContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + streamContext, + &oldStreamContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + // + // We release the context here because FltSetStreamContext failed + // + // If FltSetStreamContext succeeded then the context will be returned + // to the caller. The caller will use the context and then release it + // when he is done with the context. + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing stream context %p (FileObject = %p, Instance = %p)\n", + streamContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + FltReleaseContext( streamContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + // + // FltSetStreamContext failed for a reason other than the context already + // existing on the stream. So the object now does not have any context set + // on it. So we return failure to the caller. + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set stream context with status 0x%x != STATUS_FLT_CONTEXT_ALREADY_DEFINED. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // Race condition. Someone has set a context after we queried it. + // Use the already set context instead + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Stream context already defined. Retaining old stream context %p (FileObject = %p, Instance = %p)\n", + oldStreamContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + // + // Return the existing context. Note that the new context that we allocated has already been + // realeased above. + // + + streamContext = oldStreamContext; + status = STATUS_SUCCESS; + + } else { + + if (ContextCreated != NULL) *ContextCreated = TRUE; + } + } + + *StreamContext = streamContext; + + return status; +} + + + + +NTSTATUS +CtxCreateStreamContext ( + _Outptr_ PCTX_STREAM_CONTEXT *StreamContext + ) +/*++ + +Routine Description: + + This routine creates a new stream context + +Arguments: + + StreamContext - Returns the stream context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_STREAM_CONTEXT streamContext; + + PAGED_CODE(); + + // + // Allocate a stream context + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Allocating stream context \n") ); + + status = FltAllocateContext( Globals.Filter, + FLT_STREAM_CONTEXT, + CTX_STREAM_CONTEXT_SIZE, + PagedPool, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate stream context with status 0x%x \n", + status) ); + return status; + } + + // + // Initialize the newly created context + // + + RtlZeroMemory( streamContext, CTX_STREAM_CONTEXT_SIZE ); + + streamContext->Resource = CtxAllocateResource(); + if(streamContext->Resource == NULL) { + + FltReleaseContext( streamContext ); + return STATUS_INSUFFICIENT_RESOURCES; + } + ExInitializeResourceLite( streamContext->Resource ); + + *StreamContext = streamContext; + + return STATUS_SUCCESS; +} + + +NTSTATUS +CtxUpdateNameInStreamContext ( + _In_ PUNICODE_STRING DirectoryName, + _Inout_ PCTX_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine updates the name of the target in the supplied stream context + +Arguments: + + DirectoryName - Supplies the directory name + StreamContext - Returns the updated name in the stream context + +Return Value: + + Status + +Note: + + The caller must synchronize access to the context. This routine does no + synchronization + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + // + // Free any existing name + // + + if (StreamContext->FileName.Buffer != NULL) { + + CtxFreeUnicodeString(&StreamContext->FileName); + } + + + // + // Allocate and copy off the directory name + // + + StreamContext->FileName.MaximumLength = DirectoryName->Length; + status = CtxAllocateUnicodeString(&StreamContext->FileName); + if (NT_SUCCESS(status)) { + + RtlCopyUnicodeString(&StreamContext->FileName, DirectoryName); + } + + return status; +} + + + + +NTSTATUS +CtxCreateOrReplaceStreamHandleContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN ReplaceIfExists, + _Outptr_ PCTX_STREAMHANDLE_CONTEXT *StreamHandleContext, + _Out_opt_ PBOOLEAN ContextReplaced + ) +/*++ + +Routine Description: + + This routine creates a stream handle context for the target stream + handle. Optionally, if the context already exists, this routine + replaces it with the new context and releases the old context + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + ReplaceIfExists - Supplies if the stream handle context must be + replaced if already present + StreamContext - Returns the stream context + ContextReplaced - Returns if an existing context was replaced + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_STREAMHANDLE_CONTEXT streamHandleContext; + PCTX_STREAMHANDLE_CONTEXT oldStreamHandleContext; + + PAGED_CODE(); + + *StreamHandleContext = NULL; + if (ContextReplaced != NULL) *ContextReplaced = FALSE; + + // + // Create a stream context + // + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Creating stream handle context (FileObject = %p, Instance = %p)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = CtxCreateStreamHandleContext( &streamHandleContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to create stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // Set the new context we just allocated on the file object + // + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Setting stream context %p (FileObject = %p, Instance = %p, ReplaceIfExists = %x)\n", + streamHandleContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance, + ReplaceIfExists) ); + + status = FltSetStreamHandleContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + ReplaceIfExists ? FLT_SET_CONTEXT_REPLACE_IF_EXISTS : FLT_SET_CONTEXT_KEEP_IF_EXISTS, + streamHandleContext, + &oldStreamHandleContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set stream handle context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + // + // We release the context here because FltSetStreamContext failed + // + // If FltSetStreamContext succeeded then the context will be returned + // to the caller. The caller will use the context and then release it + // when he is done with the context. + // + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing stream handle context %p (FileObject = %p, Instance = %p)\n", + streamHandleContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + FltReleaseContext( streamHandleContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + // + // FltSetStreamContext failed for a reason other than the context already + // existing on the stream. So the object now does not have any context set + // on it. So we return failure to the caller. + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set stream context with status 0x%x != STATUS_FLT_CONTEXT_ALREADY_DEFINED. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // We will reach here only if we have failed with STATUS_FLT_CONTEXT_ALREADY_DEFINED + // and we can fail with that code only if the context already exists and we have used + // the FLT_SET_CONTEXT_KEEP_IF_EXISTS flag + + FLT_ASSERT( ReplaceIfExists == FALSE ); + + // + // Race condition. Someone has set a context after we queried it. + // Use the already set context instead + // + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Stream context already defined. Retaining old stream context %p (FileObject = %p, Instance = %p)\n", + oldStreamHandleContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + // + // Return the existing context. Note that the new context that we allocated has already been + // realeased above. + // + + streamHandleContext = oldStreamHandleContext; + status = STATUS_SUCCESS; + + } else { + + // + // FltSetStreamContext has suceeded. The new context will be returned + // to the caller. The caller will use the context and then release it + // when he is done with the context. + // + // However, if we have replaced an existing context then we need to + // release the old context so as to decrement the ref count on it. + // + // Note that the memory allocated to the objects within the context + // will be freed in the context cleanup and must not be done here. + // + + if ( ReplaceIfExists && + oldStreamHandleContext != NULL) { + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing old stream handle context %p (FileObject = %p, Instance = %p)\n", + oldStreamHandleContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + FltReleaseContext( oldStreamHandleContext ); + if (ContextReplaced != NULL) *ContextReplaced = TRUE; + } + } + + *StreamHandleContext = streamHandleContext; + + return status; +} + + + + + +NTSTATUS +CtxCreateStreamHandleContext ( + _Outptr_ PCTX_STREAMHANDLE_CONTEXT *StreamHandleContext + ) +/*++ + +Routine Description: + + This routine creates a new stream context + +Arguments: + + StreamContext - Returns the stream context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_STREAMHANDLE_CONTEXT streamHandleContext; + + PAGED_CODE(); + + // + // Allocate a stream context + // + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Allocating stream handle context \n") ); + + status = FltAllocateContext( Globals.Filter, + FLT_STREAMHANDLE_CONTEXT, + CTX_STREAMHANDLE_CONTEXT_SIZE, + PagedPool, + &streamHandleContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate stream handle context with status 0x%x \n", + status) ); + + return status; + } + + // + // Initialize the newly created context + // + + RtlZeroMemory( streamHandleContext, CTX_STREAMHANDLE_CONTEXT_SIZE ); + + streamHandleContext->Resource = CtxAllocateResource(); + if(streamHandleContext->Resource == NULL) { + + FltReleaseContext( streamHandleContext ); + return STATUS_INSUFFICIENT_RESOURCES; + } + ExInitializeResourceLite( streamHandleContext->Resource ); + + *StreamHandleContext = streamHandleContext; + + return STATUS_SUCCESS; +} + + + +NTSTATUS +CtxUpdateNameInStreamHandleContext ( + _In_ PUNICODE_STRING DirectoryName, + _Inout_ PCTX_STREAMHANDLE_CONTEXT StreamHandleContext + ) +/*++ + +Routine Description: + + This routine updates the name of the target in the supplied stream handle context + +Arguments: + + DirectoryName - Supplies the directory name + StreamHandleContext - Returns the updated name in the stream context + +Return Value: + + Status + +Note: + + The caller must synchronize access to the context. This routine does no + synchronization + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + // + // Free any existing name + // + + if (StreamHandleContext->FileName.Buffer != NULL) { + + CtxFreeUnicodeString(&StreamHandleContext->FileName); + } + + + // + // Allocate and copy off the directory name + // + + StreamHandleContext->FileName.MaximumLength = DirectoryName->Length; + status = CtxAllocateUnicodeString(&StreamHandleContext->FileName); + if (NT_SUCCESS(status)) { + + RtlCopyUnicodeString(&StreamHandleContext->FileName, DirectoryName); + } + + return status; +} + diff --git a/filesys/miniFilter/ctx/ctx.inf b/filesys/miniFilter/ctx/ctx.inf new file mode 100644 index 00000000..9ba06e18 --- /dev/null +++ b/filesys/miniFilter/ctx/ctx.inf @@ -0,0 +1,96 @@ +;;; +;;; Context File System Filter Driver Sample +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.1 +CatalogFile = ctx.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"DebugLevel",0x00010001,0x00000001 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +ctx.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Context File System Filter Driver Sample" +ServiceName = "Ctx" +DriverName = "ctx" +DiskId1 = "Ctx Device Installation Disk" + +;Instances specific information. +DefaultInstance = "Ctx" +Instance1.Name = "Ctx" +Instance1.Altitude = "370070" +Instance1.Flags = 0x0 diff --git a/filesys/miniFilter/ctx/ctx.rc b/filesys/miniFilter/ctx/ctx.rc new file mode 100644 index 00000000..19df388b --- /dev/null +++ b/filesys/miniFilter/ctx/ctx.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Context Sample Mini-Filter" +#define VER_INTERNALNAME_STR "ctx.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/ctx/ctx.sln b/filesys/miniFilter/ctx/ctx.sln new file mode 100644 index 00000000..316647ca --- /dev/null +++ b/filesys/miniFilter/ctx/ctx.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ctx", "ctx.vcxproj", "{8A826E76-F53C-4951-B81A-8653A4FC7BF0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|Win32.ActiveCfg = Debug|Win32 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|Win32.Build.0 = Debug|Win32 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|Win32.ActiveCfg = Release|Win32 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|Win32.Build.0 = Release|Win32 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|x64.ActiveCfg = Debug|x64 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|x64.Build.0 = Debug|x64 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|x64.ActiveCfg = Release|x64 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/ctx/ctx.vcxproj b/filesys/miniFilter/ctx/ctx.vcxproj new file mode 100644 index 00000000..65ce09b0 --- /dev/null +++ b/filesys/miniFilter/ctx/ctx.vcxproj @@ -0,0 +1,183 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {8A826E76-F53C-4951-B81A-8653A4FC7BF0} + $(MSBuildProjectName) + Debug + Win32 + {E0BB0921-D467-4DDA-82CD-8B1FAADDBC55} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + ctx + + + ctx + + + ctx + + + ctx + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/ctx/ctx.vcxproj.Filters b/filesys/miniFilter/ctx/ctx.vcxproj.Filters new file mode 100644 index 00000000..4eef3fc4 --- /dev/null +++ b/filesys/miniFilter/ctx/ctx.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {872ED434-D252-450B-ACD5-B7DDB6FF6D4E} + + + h;hpp;hxx;hm;inl;inc;xsd + {FC7A2108-DAAB-43D9-AEFA-120FC32876D7} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {C1AC955C-1C1A-4B66-8AA8-C6173E6B2EDD} + + + inf;inv;inx;mof;mc; + {1E203714-FFC5-4465-BC3D-2ED22ED46D83} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/ctx/operations.c b/filesys/miniFilter/ctx/operations.c new file mode 100644 index 00000000..743fcd4c --- /dev/null +++ b/filesys/miniFilter/ctx/operations.c @@ -0,0 +1,1080 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + operations.c + +Abstract: + + This is the i/o operations module of the kernel mode filter driver implementing + context sample + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CtxPreCreate) +#pragma alloc_text(PAGE, CtxPostCreate) +#pragma alloc_text(PAGE, CtxPreCleanup) +#pragma alloc_text(PAGE, CtxPreClose) +#pragma alloc_text(PAGE, CtxPreSetInfo) +#pragma alloc_text(PAGE, CtxPostSetInfo) +#endif + + +FLT_PREOP_CALLBACK_STATUS +CtxPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + UNREFERENCED_PARAMETER( Cbd ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreCreate -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreCreate -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Force a post-op callback so we can add our contexts to the opened + // objects + // + + return FLT_PREOP_SUCCESS_WITH_CALLBACK; + +} + + +FLT_POSTOP_CALLBACK_STATUS +CtxPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_opt_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +{ + + PCTX_FILE_CONTEXT fileContext = NULL; + PCTX_STREAM_CONTEXT streamContext = NULL; + PCTX_STREAMHANDLE_CONTEXT streamHandleContext = NULL; + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + UNICODE_STRING fileName; + + NTSTATUS status; + BOOLEAN fileContextCreated, streamContextCreated, streamHandleContextReplaced; + + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( CbdContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPostCreate -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Initialize defaults + // + + status = STATUS_SUCCESS; + + // + // If the Create has failed, do nothing + // + + if (!NT_SUCCESS( Cbd->IoStatus.Status )) { + + goto CtxPostCreateCleanup; + } + + + // + // Get the file name + // + + status = FltGetFileNameInformation( Cbd, + FLT_FILE_NAME_NORMALIZED | + FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to get name information (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + + // + // Find or create a stream context + // + + status = CtxFindOrCreateStreamContext(Cbd, + TRUE, + &streamContext, + &streamContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to find or create stream context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Getting/Creating stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p. StreamContextCreated = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamContext, + streamContextCreated) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive(streamContext->Resource); + + // + // Increment the create count + // + + streamContext->CreateCount++; + + // + // Update the file name in the context + // + + status = CtxUpdateNameInStreamContext( &nameInfo->Name, + streamContext); + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Stream context info for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource(streamContext->Resource); + + // + // Quit on failure after we have given up + // the resource + // + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to update name in stream context for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + + + // + // Create or replace a stream handle context + // + + status = CtxCreateOrReplaceStreamHandleContext(Cbd, + TRUE, + &streamHandleContext, + &streamHandleContextReplaced); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to find or create stream handle context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Creating/Replacing stream handle context for file %wZ (Cbd = %p, FileObject = %p StreamHandleContext = %p, StreamHandleContextReplaced = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamHandleContext, + streamHandleContextReplaced) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive( streamHandleContext->Resource ); + + // + // Update the file name in the context + // + + status = CtxUpdateNameInStreamHandleContext( &nameInfo->Name, + streamHandleContext); + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Stream handle context info for file %wZ (Cbd = %p, FileObject = %p, StreamHandleContext = %p) \n\tName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamHandleContext, + &streamHandleContext->FileName) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource(streamHandleContext->Resource); + + // + // Quit on failure after we have given up + // the resource + // + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to update name in stream handle context for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + // + // After FltParseFileNameInformation, nameInfo->Name also + // contains the stream name. We need only the filename and do + // not want to include the stream name in the file context + // + + fileName.Buffer = nameInfo->Name.Buffer; + fileName.Length = nameInfo->Name.Length - nameInfo->Stream.Length; + fileName.MaximumLength = fileName.Length; + + // + // Find or create a file context + // + + status = CtxFindOrCreateFileContext( Cbd, + TRUE, + &fileName, + &fileContext, + &fileContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because file contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to find or create file context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Getting/Creating file context for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p. FileContextCreated = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + fileContext, + fileContextCreated) ); + + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> File context info for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p) \n\tName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + fileContext, + &fileContext->FileName) ); + + +CtxPostCreateCleanup: + + + // + // Release the references we have acquired + // + + if (nameInfo != NULL) { + + FltReleaseFileNameInformation( nameInfo ); + } + + if (fileContext != NULL) { + + FltReleaseContext( fileContext ); + } + + if (streamContext != NULL) { + + FltReleaseContext( streamContext ); + } + + if (streamHandleContext != NULL) { + + FltReleaseContext( streamHandleContext ); + } + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Ctx]: CtxPostCreate -> Failed with status 0x%x \n", + status) ); + + // + // It doesn't make sense to udate Cbd->IoStatus.Status on failure since the + // file system has successfully completed the operation + // + + } + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPostCreate -> Exit (Cbd = %p, FileObject = %p, Status = 0x%x)\n", + Cbd, + FltObjects->FileObject, + Cbd->IoStatus.Status) ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +CtxPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + + PCTX_STREAM_CONTEXT streamContext = NULL; + NTSTATUS status; + BOOLEAN streamContextCreated; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreCleanup -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Get the stream context + // + + status = CtxFindOrCreateStreamContext(Cbd, + FALSE, // do not create if one does not exist + &streamContext, + &streamContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreCleanup -> Failed to find stream context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPreCleanupCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreCleanup -> Getting stream context for file (Cbd = %p, FileObject = %p, StreamContext = %p. StreamContextCreated = %x)\n", + Cbd, + FltObjects->FileObject, + streamContext, + streamContextCreated) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive(streamContext->Resource); + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreCleanup -> Old info in stream context for file(Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + + + // + // Update the cleanup count in the context + // + + streamContext->CleanupCount++; + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreCleanup -> New info in stream context for file (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource(streamContext->Resource); + + +CtxPreCleanupCleanup: + + // + // Release the references we have acquired + // + + if (streamContext != NULL) { + + FltReleaseContext( streamContext ); + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreCleanup -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // It doesn't make sense to fail the cleanup - so ignore any errors we may + // encounter and return success + // + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + + + + +FLT_PREOP_CALLBACK_STATUS +CtxPreClose ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + + PCTX_STREAM_CONTEXT streamContext = NULL; + NTSTATUS status; + BOOLEAN streamContextCreated; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreClose -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Get the stream context + // + + status = CtxFindOrCreateStreamContext(Cbd, + FALSE, // do not create if one does not exist + &streamContext, + &streamContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreClose -> Failed to find stream context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPreCloseCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreClose -> Getting stream context for file (Cbd = %p, FileObject = %p, StreamContext = %p. StreamContextCreated = %x)\n", + Cbd, + FltObjects->FileObject, + streamContext, + streamContextCreated) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive(streamContext->Resource); + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreClose -> Old info in stream context for file(Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Update the close count in the context + // + + streamContext->CloseCount++; + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreClose -> New info in stream context for file (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource(streamContext->Resource); + + +CtxPreCloseCleanup: + + // + // Release the references we have acquired + // + + if (streamContext != NULL) { + + FltReleaseContext( streamContext ); + } + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreClose -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // It doesn't make sense to fail the cleanup - so ignore any errors we may + // encounter and return success + // + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +FLT_PREOP_CALLBACK_STATUS +CtxPreSetInfo ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + FILE_INFORMATION_CLASS fileInformationClass; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreSetInfo -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; // pass through - default is no post op callback + + fileInformationClass = Cbd->Iopb->Parameters.SetFileInformation.FileInformationClass; + + // + // Ignore the ops we do not care about + // + + if ((fileInformationClass != FileRenameInformation)) { + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreSetInfo -> Ignoring SetInfo operations other than FileRenameInformation (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPreSetInfoCleanup; + } + + // + // We want to process renames in the post-op callback + // + + callbackStatus = FLT_PREOP_SYNCHRONIZE; + + +CtxPreSetInfoCleanup: + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreSetInfo -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return callbackStatus; + +} + + + + +FLT_POSTOP_CALLBACK_STATUS +CtxPostSetInfo ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_opt_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +{ + PCTX_INSTANCE_CONTEXT instanceContext = NULL; + PCTX_FILE_CONTEXT fileContext = NULL; + PCTX_STREAM_CONTEXT streamContext = NULL; + PCTX_STREAMHANDLE_CONTEXT streamHandleContext = NULL; + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + + NTSTATUS status; + BOOLEAN streamContextCreated, fileContextCreated, streamHandleContextReplaced; + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CbdContext ); + + // + // The pre-operation callback will return FLT_PREOP_SYNCHRONIZE if it needs a + // post operation callback. In this case, the Filter Manager will call the + // minifilter's post-operation callback in the context of the pre-operation + // thread, at IRQL <= APC_LEVEL. This allows the post-operation code to be + // pagable and also allows it to access paged data + // + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPostSetInfo -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + + // + // Initialize defaults + // + + status = STATUS_SUCCESS; + + // + // If the SetInfo has failed, do nothing + // + + if (!NT_SUCCESS( Cbd->IoStatus.Status )) { + + goto CtxPostSetInfoCleanup; + } + + + // + // Get the instance context for the target instance + // + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Trying to get instance context (TargetInstance = %p, Cbd = %p, FileObject = %p)\n", + Cbd->Iopb->TargetInstance, + Cbd, + FltObjects->FileObject) ); + + status = FltGetInstanceContext( Cbd->Iopb->TargetInstance, + &instanceContext ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: CtxPostSetInfo -> Failed to get instance context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Instance context info for volume %wZ (Cbd = %p, FileObject = %p, InstanceContext = %p) \n\tVolumeName = %wZ \n\tInstance = %p \n\tVolume = %p\n", + &instanceContext->VolumeName, + Cbd, + FltObjects->FileObject, + instanceContext, + &instanceContext->VolumeName, + &instanceContext->Instance, + &instanceContext->Volume) ); + + + // + // Get the directory name + // + + status = FltGetFileNameInformation( Cbd, + FLT_FILE_NAME_NORMALIZED | + FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to get file name information (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + + // + // Get the stream context + // + + status = CtxFindOrCreateStreamContext(Cbd, + FALSE, // do not create if one does not exist + &streamContext, + &streamContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to find stream context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Getting stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p. StreamContextCreated = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamContext, + streamContextCreated) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive(streamContext->Resource); + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Old info in stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + + + // + // Update the file name in the context + // + + status = CtxUpdateNameInStreamContext( &nameInfo->Name, + streamContext); + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> New info in stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource(streamContext->Resource); + + // + // Quit on failure after we have given up + // the resource + // + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to update name in stream context for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + + + // + // Create or replace a stream handle context + // + + status = CtxCreateOrReplaceStreamHandleContext(Cbd, + TRUE, + &streamHandleContext, + &streamHandleContextReplaced); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to find or create stream handle context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Creating/Replacing stream handle context for file %wZ (Cbd = %p, FileObject = %p StreamHandleContext = %p, StreamHandleContextReplaced = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamHandleContext, + streamHandleContextReplaced) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive(streamHandleContext->Resource); + + // + // Update the file name in the context + // + + status = CtxUpdateNameInStreamHandleContext( &nameInfo->Name, + streamHandleContext); + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Stream handle context info for file %wZ (Cbd = %p, FileObject = %p, StreamHandleContext = %p) \n\tName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamHandleContext, + &streamHandleContext->FileName) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource( streamHandleContext->Resource ); + + // + // Quit on failure after we have given up + // the resource + // + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to update name in stream handle context for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + + // + // Get the file context + // + + status = CtxFindOrCreateFileContext( Cbd, + FALSE, // do not create if one does not exist + NULL, + &fileContext, + &fileContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because file contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to find file context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Getting file context for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p. FileContextCreated = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + fileContext, + fileContextCreated) ); + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> File context info for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p) \n\tName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + fileContext, + &fileContext->FileName) ); + + +CtxPostSetInfoCleanup: + + + // + // Release the references we have acquired + // + + if (instanceContext != NULL) { + + FltReleaseContext( instanceContext ); + } + + if (fileContext != NULL) { + + FltReleaseContext( fileContext ); + } + + if (streamContext != NULL) { + + FltReleaseContext( streamContext ); + } + + if (streamHandleContext != NULL) { + + FltReleaseContext( streamHandleContext ); + } + + if (nameInfo != NULL) { + + FltReleaseFileNameInformation( nameInfo ); + } + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Ctx]: CtxPostSetInfo -> Failed with status 0x%x \n", + status) ); + + // + // It doesn't make sense to udate Cbd->IoStatus.Status on failure since the + // file system has suceesfully completed the operation + // + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPostSetInfo -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + diff --git a/filesys/miniFilter/ctx/pch.h b/filesys/miniFilter/ctx/pch.h new file mode 100644 index 00000000..7d2e3ef0 --- /dev/null +++ b/filesys/miniFilter/ctx/pch.h @@ -0,0 +1,46 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + pch.h + +Abstract: + + This module includes all the headers which need to be + precompiled & are included by all the source files in this + project + + +Environment: + + Kernel mode + + +--*/ + +#ifndef __CTX_PCH_H__ +#define __CTX_PCH_H__ + +// +// Enabled warnings +// + +#pragma warning(error:4100) // Enable-Unreferenced formal parameter +#pragma warning(error:4101) // Enable-Unreferenced local variable +#pragma warning(error:4061) // Eenable-missing enumeration in switch statement +#pragma warning(error:4505) // Enable-identify dead functions + +// +// Includes +// + +#include +#include +#include +#include "CtxStruc.h" +#include "CtxProc.h" + +#endif __CTX_PCH_H__ + diff --git a/filesys/miniFilter/ctx/support.c b/filesys/miniFilter/ctx/support.c new file mode 100644 index 00000000..7e3db95f --- /dev/null +++ b/filesys/miniFilter/ctx/support.c @@ -0,0 +1,117 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + operations.c + +Abstract: + + This is the support routines module of the kernel mode filter driver implementing + context sample. + + +Environment: + + Kernel mode + + +--*/ + + + +#include "pch.h" + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CtxAllocateUnicodeString) +#pragma alloc_text(PAGE, CtxFreeUnicodeString) +#endif + +// +// Support Routines +// + +_At_(String->Length, _Out_range_(==, 0)) +_At_(String->MaximumLength, _In_) +_At_(String->Buffer, _Pre_maybenull_ _Post_notnull_ _Post_writable_byte_size_(String->MaximumLength)) +NTSTATUS +CtxAllocateUnicodeString ( + _Out_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This routine allocates a unicode string + +Arguments: + + String - supplies the size of the string to be allocated in the MaximumLength field + return the unicode string + +Return Value: + + STATUS_SUCCESS - success + STATUS_INSUFFICIENT_RESOURCES - failure + +--*/ +{ + PAGED_CODE(); + + String->Buffer = ExAllocatePoolWithTag( PagedPool, + String->MaximumLength, + CTX_STRING_TAG ); + + if (String->Buffer == NULL) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate unicode string of size 0x%x\n", + String->MaximumLength) ); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + String->Length = 0; + + return STATUS_SUCCESS; +} + +_At_(String->Length, _Out_range_(==, 0)) +_At_(String->MaximumLength, _Out_range_(==, 0)) +_At_(String->Buffer, _Pre_notnull_ _Post_null_) +VOID +CtxFreeUnicodeString ( + _Pre_notnull_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This routine frees a unicode string + +Arguments: + + String - supplies the string to be freed + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + ExFreePoolWithTag( String->Buffer, + CTX_STRING_TAG ); + + String->Length = String->MaximumLength = 0; + String->Buffer = NULL; +} + + + diff --git a/filesys/miniFilter/delete/ReadMe.md b/filesys/miniFilter/delete/ReadMe.md new file mode 100644 index 00000000..50cfb0dd --- /dev/null +++ b/filesys/miniFilter/delete/ReadMe.md @@ -0,0 +1,16 @@ +Delete File System Minifilter Driver +==================================== + +The Delete minifilter is an example that demonstrates how to detect deletions of files or streams. Deletions are reported as debug output. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *delete* minifilter illustrates how to detect deletion of files and streams. It monitors IRP\_MJ\_CREATE requests for the FILE\_DELETE\_ON\_CLOSE flag. Also, it detects IRP\_MJ\_SET\_INFORMATION requests for setting FileDispositionInformation. The sample also illustrates how to handle racing deletes (in the form of multiple parallel IRP\_MJ\_SET\_INFORMATION operations), and how to distinguish deletion of an entire file from deletion of just one stream of the file. + +**Note**  Because of the way in which the Windows operating system deletes files, it is not possible for the minifilter to detect in advance that a file or stream will be deleted. The minifilter can only detect operations that may cause a deletion, and then determine if the deletion took place after the operation completes. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. diff --git a/filesys/miniFilter/delete/delete.c b/filesys/miniFilter/delete/delete.c new file mode 100644 index 00000000..ec515c81 --- /dev/null +++ b/filesys/miniFilter/delete/delete.c @@ -0,0 +1,3253 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + delete.c + +Abstract: + + This is the main file for the delete detection sample minifilter. + + +Environment: + + Kernel mode + + +--*/ + + +#include +#include +#include + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + + + +#define DFDBG_TRACE_ERRORS 0x00000001 +#define DFDBG_TRACE_ROUTINES 0x00000002 +#define DFDBG_TRACE_OPERATION_STATUS 0x00000004 + +#define DF_VOLUME_GUID_NAME_SIZE 48 + +#define DF_INSTANCE_CONTEXT_POOL_TAG 'nIfD' +#define DF_STREAM_CONTEXT_POOL_TAG 'xSfD' +#define DF_TRANSACTION_CONTEXT_POOL_TAG 'xTfD' +#define DF_ERESOURCE_POOL_TAG 'sRfD' +#define DF_DELETE_NOTIFY_POOL_TAG 'nDfD' +#define DF_STRING_POOL_TAG 'rSfD' + +#define DF_CONTEXT_POOL_TYPE PagedPool + +#define DF_NOTIFICATION_MASK (TRANSACTION_NOTIFY_COMMIT_FINALIZE | \ + TRANSACTION_NOTIFY_ROLLBACK) + + +////////////////////////////////////////////////////////////////////////////// +// Macros // +////////////////////////////////////////////////////////////////////////////// + +#define DF_PRINT( ... ) \ + DbgPrintEx( DPFLTR_FLTMGR_ID, DPFLTR_ERROR_LEVEL, __VA_ARGS__ ) + +#define DF_DBG_PRINT( _dbgLevel, ... ) \ + (FlagOn( gTraceFlags, (_dbgLevel) ) ? \ + DF_PRINT( __VA_ARGS__ ): \ + (0)) + +#define FlagOnAll( F, T ) \ + (FlagOn( F, T ) == T) + + +////////////////////////////////////////////////////////////////////////////// +// Main Globals // +////////////////////////////////////////////////////////////////////////////// + +PFLT_FILTER gFilterHandle; +ULONG gTraceFlags = DFDBG_TRACE_ERRORS; + + +////////////////////////////////////////////////////////////////////////////// +// ReFS Compatibility Helpers // +////////////////////////////////////////////////////////////////////////////// + +// +// This helps us deal with ReFS 128-bit file IDs and NTFS 64-bit file IDs. +// + +typedef union _DF_FILE_REFERENCE { + + struct { + ULONGLONG Value; // The 64-bit file ID lives here. + ULONGLONG UpperZeroes; // In a 64-bit file ID this will be 0. + } FileId64; + + UCHAR FileId128[16]; // The 128-bit file ID lives here. + +} DF_FILE_REFERENCE, *PDF_FILE_REFERENCE; + +#define DfSizeofFileId(FID) ( \ + ((FID).FileId64.UpperZeroes == 0ll) ? \ + sizeof((FID).FileId64.Value) : \ + sizeof((FID).FileId128) \ + ) + + +////////////////////////////////////////////////////////////////////////////// +// Types // +////////////////////////////////////////////////////////////////////////////// + +// +// This is the instance context for this minifilter, it stores the volume's +// GUID name. +// + +typedef struct _DF_INSTANCE_CONTEXT { + + // + // Volume GUID name. + // + + UNICODE_STRING VolumeGuidName; + +} DF_INSTANCE_CONTEXT, *PDF_INSTANCE_CONTEXT; + + +// +// This is the stream context for this minifilter, attached whenever a stream +// becomes a candidate for deletion. +// + +typedef struct _DF_STREAM_CONTEXT { + + // + // FLT_FILE_NAME_INFORMATION structure with the names for this stream + // and file. This is only used for printing out the opened name when + // notifying deletes. This will be the result of an opened query name + // done at the last pre-cleanup on the file/stream. + // + // Therefore, there is no requirement of maintaining the file name + // information (for the purposes we use it) in sync with the FltMgr name + // cache or the file system. This makes it okay to store it in the stream + // context. + // + + PFLT_FILE_NAME_INFORMATION NameInfo; + + // + // File ID, obtained from querying the file system for FileInternalInformation. + // If the File ID is 128 bits (as in ReFS) we get it via FileIdInformation. + // + + DF_FILE_REFERENCE FileId; + + // + // Number of SetDisp operations in flight. + // + + volatile LONG NumOps; + + // + // IsNotified == 1 means a file/stream deletion was already notified. + // + + volatile LONG IsNotified; + + // + // Whether or not we've already queried the file ID. + // + + BOOLEAN FileIdSet; + + // + // Delete Disposition for this stream. + // + + BOOLEAN SetDisp; + + // + // Delete-on-Close state for this stream. + // + + BOOLEAN DeleteOnClose; + +} DF_STREAM_CONTEXT, *PDF_STREAM_CONTEXT; + + +// +// This is the transaction context for this minifilter, attached at post- +// -cleanup when notifying a delete within a transaction. +// + +typedef struct _DF_TRANSACTION_CONTEXT { + + // + // List of DF_DELETE_NOTIFY structures representing pending delete + // notifications. + // + + LIST_ENTRY DeleteNotifyList; + + // + // ERESOURCE for synchronized access to the DeleteNotifyList. + // + // ERESOURCEs must be allocated from NonPagedPool. If an ERESOURCE was + // declared here as a direct member of a structure, instead of just a + // pointer, then the whole transaction context would need to be allocated + // out of NonPagedPool. + // + // Therefore, declaring it as a pointer and only allocating at context + // initialization time helps us save some NonPagedPool. This is + // particularly important in larger context structures. + // + + PERESOURCE Resource; + +} DF_TRANSACTION_CONTEXT, *PDF_TRANSACTION_CONTEXT; + + +// +// This structure represents pending delete notifications for files that have +// been deleted in an open transaction. +// + +typedef struct _DF_DELETE_NOTIFY { + + // + // Links to other DF_DELETE_NOTIFY structures in the list. + // + + LIST_ENTRY Links; + + // + // Pointer to the stream context for the deleted stream/file. + // + + PDF_STREAM_CONTEXT StreamContext; + + // + // TRUE for a deleted file, FALSE for a stream. + // + + BOOLEAN FileDelete; + +} DF_DELETE_NOTIFY, *PDF_DELETE_NOTIFY; + + +////////////////////////////////////////////////////////////////////////////// +// Prototypes // +////////////////////////////////////////////////////////////////////////////// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +DfUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +DfInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +NTSTATUS +DfInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +VOID +DfInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +DfInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +NTSTATUS +DfSetupInstanceContext( + _In_ PCFLT_RELATED_OBJECTS FltObjects + ); + +NTSTATUS +DfAllocateContext ( + _In_ FLT_CONTEXT_TYPE ContextType, + _Outptr_ PFLT_CONTEXT *Context + ); + +NTSTATUS +DfSetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _In_ FLT_CONTEXT_TYPE ContextType, + _In_ PFLT_CONTEXT NewContext, + _Outptr_opt_result_maybenull_ PFLT_CONTEXT *OldContext + ); + +NTSTATUS +DfGetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _In_ FLT_CONTEXT_TYPE ContextType, + _Outptr_ PFLT_CONTEXT *Context + ); + +NTSTATUS +DfGetOrSetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _Outptr_ _Pre_valid_ PFLT_CONTEXT *Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +DfStreamContextCleanupCallback ( + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +DfTransactionContextCleanupCallback ( + _In_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +DfInstanceContextCleanupCallback ( + _In_ PDF_INSTANCE_CONTEXT InstanceContext, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +NTSTATUS +DfGetFileNameInformation ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PDF_STREAM_CONTEXT StreamContext + ); + +NTSTATUS +DfAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +VOID +DfFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +NTSTATUS +DfBuildFileIdString ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext, + _Out_ PUNICODE_STRING String + ); + +NTSTATUS +DfDetectDeleteByFileId ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext + ); + +NTSTATUS +DfIsFileDeleted ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ BOOLEAN IsTransaction + ); + +NTSTATUS +DfAddTransDeleteNotify ( + _Inout_ PDF_STREAM_CONTEXT StreamContext, + _Inout_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ BOOLEAN FileDelete + ); + +VOID +DfNotifyDelete ( + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ BOOLEAN IsFile, + _Inout_opt_ PDF_TRANSACTION_CONTEXT TransactionContext + ); + +VOID +DfNotifyDeleteOnTransactionEnd ( + _In_ PDF_DELETE_NOTIFY DeleteNotify, + _In_ BOOLEAN Commit + ); + +NTSTATUS +DfProcessDelete ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext + ); + +FLT_PREOP_CALLBACK_STATUS +DfPreCreateCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_result_maybenull_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +DfPostCreateCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +DfPreSetInfoCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +DfPostSetInfoCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +DfPreCleanupCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +DfPostCleanupCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +NTSTATUS +DfTransactionNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG NotificationMask + ); + +NTSTATUS +DfGetVolumeGuidName ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PUNICODE_STRING VolumeGuidName + ); + +NTSTATUS +DfGetFileId ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PDF_STREAM_CONTEXT StreamContext + ); + +////////////////////////////////////////////////////////////////////////////// +// Text section assignments for all routines // +////////////////////////////////////////////////////////////////////////////// + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, DfUnload) +#pragma alloc_text(PAGE, DfInstanceSetup) +#pragma alloc_text(PAGE, DfInstanceQueryTeardown) +#pragma alloc_text(PAGE, DfInstanceTeardownStart) +#pragma alloc_text(PAGE, DfInstanceTeardownComplete) +#pragma alloc_text(PAGE, DfSetupInstanceContext) +#pragma alloc_text(PAGE, DfAllocateContext) +#pragma alloc_text(PAGE, DfSetContext) +#pragma alloc_text(PAGE, DfGetContext) +#pragma alloc_text(PAGE, DfGetOrSetContext) +#pragma alloc_text(PAGE, DfStreamContextCleanupCallback) +#pragma alloc_text(PAGE, DfTransactionContextCleanupCallback) +#pragma alloc_text(PAGE, DfInstanceContextCleanupCallback) +#pragma alloc_text(PAGE, DfGetFileNameInformation) +#pragma alloc_text(PAGE, DfAllocateUnicodeString) +#pragma alloc_text(PAGE, DfFreeUnicodeString) +#pragma alloc_text(PAGE, DfBuildFileIdString) +#pragma alloc_text(PAGE, DfDetectDeleteByFileId) +#pragma alloc_text(PAGE, DfIsFileDeleted) +#pragma alloc_text(PAGE, DfAddTransDeleteNotify) +#pragma alloc_text(PAGE, DfNotifyDelete) +#pragma alloc_text(PAGE, DfNotifyDeleteOnTransactionEnd) +#pragma alloc_text(PAGE, DfProcessDelete) +#pragma alloc_text(PAGE, DfPreCreateCallback) +#pragma alloc_text(PAGE, DfPostCreateCallback) +#pragma alloc_text(PAGE, DfPreSetInfoCallback) +#pragma alloc_text(PAGE, DfPostSetInfoCallback) +#pragma alloc_text(PAGE, DfPreCleanupCallback) +#pragma alloc_text(PAGE, DfPostCleanupCallback) +#pragma alloc_text(PAGE, DfTransactionNotificationCallback) +#pragma alloc_text(PAGE, DfGetVolumeGuidName) +#pragma alloc_text(PAGE, DfGetFileId) +#endif + + +////////////////////////////////////////////////////////////////////////////// +// Context Registration // +////////////////////////////////////////////////////////////////////////////// + +CONST FLT_CONTEXT_REGISTRATION Contexts[] = { + + { FLT_INSTANCE_CONTEXT, + 0, + DfInstanceContextCleanupCallback, + sizeof(DF_INSTANCE_CONTEXT), + DF_INSTANCE_CONTEXT_POOL_TAG, + NULL, + NULL, + NULL }, + + { FLT_STREAM_CONTEXT, + 0, + DfStreamContextCleanupCallback, + sizeof(DF_STREAM_CONTEXT), + DF_STREAM_CONTEXT_POOL_TAG, + NULL, + NULL, + NULL }, + + { FLT_TRANSACTION_CONTEXT, + 0, + DfTransactionContextCleanupCallback, + sizeof(DF_TRANSACTION_CONTEXT), + DF_TRANSACTION_CONTEXT_POOL_TAG, + NULL, + NULL, + NULL }, + + { FLT_CONTEXT_END } + +}; + + +////////////////////////////////////////////////////////////////////////////// +// Operation Registration // +////////////////////////////////////////////////////////////////////////////// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + + { IRP_MJ_CREATE, + 0, + DfPreCreateCallback, + DfPostCreateCallback }, + + { IRP_MJ_SET_INFORMATION, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + DfPreSetInfoCallback, + DfPostSetInfoCallback }, + + { IRP_MJ_CLEANUP, + 0, + DfPreCleanupCallback, + DfPostCleanupCallback }, + + { IRP_MJ_OPERATION_END } + +}; + + +////////////////////////////////////////////////////////////////////////////// +// Filter Registration // +////////////////////////////////////////////////////////////////////////////// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + Contexts, // Context + Callbacks, // Operation callbacks + + DfUnload, // MiniFilterUnload + + DfInstanceSetup, // InstanceSetup + DfInstanceQueryTeardown, // InstanceQueryTeardown + DfInstanceTeardownStart, // InstanceTeardownStart + DfInstanceTeardownComplete, // InstanceTeardownComplete + NULL, // GenerateFileName + NULL, // NormalizeNameComponent + NULL, // NormalizeContextCleanup + DfTransactionNotificationCallback, // TransactionNotification + NULL // NormalizeNameComponentEx + +}; + + +////////////////////////////////////////////////////////////////////////////// +// MiniFilter initialization and unload routines // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers with FltMgr and initializes all global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( RegistryPath ); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DriverEntry: Entered\n" ); + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + // + // Register with FltMgr to tell it our callback routines + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &gFilterHandle ); + + ASSERT( NT_SUCCESS( status ) ); + + if (NT_SUCCESS( status )) { + + // + // Start filtering i/o + // + + status = FltStartFiltering( gFilterHandle ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( gFilterHandle ); + } + } + + return status; +} + + +NTSTATUS +DfUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter driver. This is called + when the minifilter is about to be unloaded. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfUnload: Entered\n" ); + + FltUnregisterFilter( gFilterHandle ); + + return STATUS_SUCCESS; +} + + +////////////////////////////////////////////////////////////////////////////// +// Filter Instance Callbacks (Setup/Teardown/QueryTeardown) // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DfInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. This + gives us a chance to decide if we need to attach to this volume or not. + + New instances are only created and attached to a volume if it is a writable + NTFS or ReFS volume. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + + VolumeFilesystemType - A FLT_FSTYPE_* value indicating which file system type + the Filter Manager is offering to attach us to. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN isWritable = FALSE; + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfInstanceSetup: Entered\n" ); + + status = FltIsVolumeWritable( FltObjects->Volume, + &isWritable ); + + if (!NT_SUCCESS( status )) { + + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Attaching to read-only volumes is pointless as you should not be able + // to delete files on such a volume. + // + + if (isWritable) { + + switch (VolumeFilesystemType) { + + case FLT_FSTYPE_NTFS: + case FLT_FSTYPE_REFS: + + status = STATUS_SUCCESS; + break; + + default: + + return STATUS_FLT_DO_NOT_ATTACH; + } + + } else { + + return STATUS_FLT_DO_NOT_ATTACH; + } + + return status; +} + + +NTSTATUS +DfInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach thereby giving us a + chance to fail that detach request. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Returns the status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfInstanceQueryTeardown: Entered\n" ); + + return STATUS_SUCCESS; +} + + +VOID +DfInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the start of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfInstanceTeardownStart: Entered\n" ); +} + + +VOID +DfInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the end of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfInstanceTeardownComplete: Entered\n" ); +} + + +////////////////////////////////////////////////////////////////////////////// +// Context manipulation functions // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DfAllocateContext ( + _In_ FLT_CONTEXT_TYPE ContextType, + _Outptr_ PFLT_CONTEXT *Context + ) +/*++ + +Routine Description: + + This routine allocates and initializes a context of given type. + +Arguments: + + ContextType - Type of context to be allocated/initialized. + + Context - Pointer to a context pointer. + +Return Value: + + Returns a status forwarded from FltAllocateContext. + +--*/ +{ + NTSTATUS status; + PDF_TRANSACTION_CONTEXT transactionContext; + + PAGED_CODE(); + + switch (ContextType) { + + case FLT_STREAM_CONTEXT: + + status = FltAllocateContext( gFilterHandle, + FLT_STREAM_CONTEXT, + sizeof(DF_STREAM_CONTEXT), + DF_CONTEXT_POOL_TYPE, + Context ); + + if (NT_SUCCESS( status )) { + RtlZeroMemory( *Context, sizeof(DF_STREAM_CONTEXT) ); + } + + return status; + + case FLT_TRANSACTION_CONTEXT: + + status = FltAllocateContext( gFilterHandle, + FLT_TRANSACTION_CONTEXT, + sizeof(DF_TRANSACTION_CONTEXT), + DF_CONTEXT_POOL_TYPE, + Context ); + + if (NT_SUCCESS( status )) { + RtlZeroMemory( *Context, sizeof(DF_TRANSACTION_CONTEXT) ); + + transactionContext = *Context; + + InitializeListHead( &transactionContext->DeleteNotifyList ); + + transactionContext->Resource = ExAllocatePoolWithTag( NonPagedPool, + sizeof(ERESOURCE), + DF_ERESOURCE_POOL_TAG ); + + if (NULL == transactionContext->Resource) { + FltReleaseContext( transactionContext ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ExInitializeResourceLite( transactionContext->Resource ); + } + + return status; + + case FLT_INSTANCE_CONTEXT: + + status = FltAllocateContext( gFilterHandle, + FLT_INSTANCE_CONTEXT, + sizeof(DF_INSTANCE_CONTEXT), + DF_CONTEXT_POOL_TYPE, + Context ); + + if (NT_SUCCESS( status )) { + RtlZeroMemory( *Context, sizeof(DF_INSTANCE_CONTEXT) ); + } + + return status; + + default: + + return STATUS_INVALID_PARAMETER; + } +} + + +NTSTATUS +DfSetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _In_ FLT_CONTEXT_TYPE ContextType, + _In_ PFLT_CONTEXT NewContext, + _Outptr_opt_result_maybenull_ PFLT_CONTEXT *OldContext + ) +/*++ + +Routine Description: + + This routine sets the given context to the target. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Target - Pointer to the target to which we want to attach the + context. It will actually be either a FILE_OBJECT or + a KTRANSACTION. For instance contexts, it's ignored, as + the target is the FLT_INSTANCE itself, obtained from + Data->Iopb->TargetInstance. + + ContextType - Type of context to get/allocate/attach. Also used to + disambiguate the target/context type as this minifilter + only has one type of context per target. + + NewContext - Pointer to the context the caller wants to attach. + + OldContext - Returns the context already attached to the target, if + that is the case. + +Return Value: + + Returns a status forwarded from FltSetXxxContext. + +--*/ +{ + PAGED_CODE(); + + switch (ContextType) { + + case FLT_STREAM_CONTEXT: + + return FltSetStreamContext( FltObjects->Instance, + (PFILE_OBJECT)Target, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + NewContext, + OldContext ); + + case FLT_TRANSACTION_CONTEXT: + + return FltSetTransactionContext( FltObjects->Instance, + (PKTRANSACTION)Target, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + NewContext, + OldContext ); + + case FLT_INSTANCE_CONTEXT: + + return FltSetInstanceContext( FltObjects->Instance, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + NewContext, + OldContext ); + + default: + + ASSERT( !"Unexpected context type!\n" ); + + return STATUS_INVALID_PARAMETER; + } +} + + +NTSTATUS +DfGetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _In_ FLT_CONTEXT_TYPE ContextType, + _Outptr_ PFLT_CONTEXT *Context + ) +/*++ + +Routine Description: + + This routine gets the given context from the target. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Target - Pointer to the target from which we want to obtain the + context. It will actually be either a FILE_OBJECT or + a KTRANSACTION. For instance contexts, it's ignored, as + the target is the FLT_INSTANCE itself, obtained from + Data->Iopb->TargetInstance. + + ContextType - Type of context to get. Also used to disambiguate + the target/context type as this minifilter + only has one type of context per target. + + Context - Pointer returning a pointer to the attached context. + +Return Value: + + Returns a status forwarded from FltSetXxxContext. + +--*/ +{ + PAGED_CODE(); + + switch (ContextType) { + + case FLT_STREAM_CONTEXT: + + return FltGetStreamContext( FltObjects->Instance, + (PFILE_OBJECT)Target, + Context ); + + case FLT_TRANSACTION_CONTEXT: + + return FltGetTransactionContext( FltObjects->Instance, + (PKTRANSACTION)Target, + Context ); + + case FLT_INSTANCE_CONTEXT: + + return FltGetInstanceContext( FltObjects->Instance, + Context ); + + default: + + return STATUS_INVALID_PARAMETER; + } +} + + +NTSTATUS +DfGetOrSetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _Outptr_ _Pre_valid_ PFLT_CONTEXT *Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine obtains a context of type ContextType that is attached to + Target. + + If a context is already attached to Target, it will be returned in + *Context. If a context is already attached, but *Context points to + another context, *Context will be released. + + If no context is attached, and *Context points to a previously allocated + context, *Context will be attached to the Target. + + Finally, if no previously allocated context is passed to this routine + (*Context is a NULL pointer), a new Context is created and then attached + to Target. + + In case of race conditions (or the presence of a previously allocated + context at *Context), the existing attached context is returned via + *Context. + + In case of a transaction context, this function will also enlist in the + transaction. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Target - Pointer to the target to which we want to attach the + context. It will actually be either a FILE_OBJECT or + a KTRANSACTION. It is NULL for an Instance context. + + Context - Pointer to a pointer to a context. Used both for + returning an allocated/attached context or for receiving + a context to attach to the Target. + + ContextType - Type of context to get/allocate/attach. Also used to + disambiguate the target/context type as this minifilter + only has one type of context per target. + +Return Value: + + Returns a status forwarded from Flt(((Get|Set)Xxx)|Allocate)Context or + FltEnlistInTransaction. + +--*/ +{ + NTSTATUS status; + PFLT_CONTEXT newContext; + PFLT_CONTEXT oldContext; + + PAGED_CODE(); + + ASSERT( NULL != Context ); + + newContext = *Context; + + // + // Is there already a context attached to the target? + // + + status = DfGetContext( FltObjects, + Target, + ContextType, + &oldContext ); + + if (STATUS_NOT_FOUND == status) { + + // + // There is no attached context. This means we have to either attach the + // one provided by the caller or allocate a new one and attach it. + // + + if (NULL == newContext) { + + // + // No provided context. Allocate one. + // + + status = DfAllocateContext( ContextType, &newContext ); + + if (!NT_SUCCESS( status )) { + + // + // We failed to allocate. + // + + return status; + } + } + + } else if (!NT_SUCCESS( status )) { + + // + // We failed trying to get a context from the target. + // + + return status; + + } else { + + // + // There is already a context attached to the target, so return + // that context. + // + // If a context was provided by the caller, release it if it's not + // the one attached to the target. + // + + // + // The caller is not allowed to set the same context on the target + // twice. + // + ASSERT( newContext != oldContext ); + + if (NULL != newContext) { + + FltReleaseContext( newContext ); + } + + *Context = oldContext; + return status; + } + + // + // At this point we should have a context to set on the target (newContext). + // + + status = DfSetContext( FltObjects, + Target, + ContextType, + newContext, + &oldContext ); + + if (!NT_SUCCESS( status )) { + + // + // FltSetStreamContext failed so we must release the new context. + // + + FltReleaseContext( newContext ); + + if (STATUS_FLT_CONTEXT_ALREADY_DEFINED == status) { + + // + // We're racing with some other call which managed to set the + // context before us. We will return that context instead, which + // will be in oldContext. + // + + *Context = oldContext; + return STATUS_SUCCESS; + + } else { + + // + // Failed to set the context. Return NULL. + // + + *Context = NULL; + return status; + } + } + + // + // If this is setting a transaction context, we want to enlist in the + // transaction as well. + // + + if (FLT_TRANSACTION_CONTEXT == ContextType) { + + status = FltEnlistInTransaction( FltObjects->Instance, + (PKTRANSACTION)Target, + newContext, + DF_NOTIFICATION_MASK ); + + } + + // + // Setting the context was successful so just return newContext. + // + + *Context = newContext; + return status; +} + + +////////////////////////////////////////////////////////////////////////////// +// Context Cleanup Callbacks // +////////////////////////////////////////////////////////////////////////////// + +VOID +DfStreamContextCleanupCallback ( + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine cleans up a stream context. The only cleanup necessary is + releasing the FLT_FILE_NAME_INFORMATION object of the NameInfo field. + +Arguments: + + StreamContext - Pointer to DF_STREAM_CONTEXT to be cleaned up. + + ContextType - Type of StreamContext. Must be FLT_STREAM_CONTEXT. + +--*/ +{ + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + ASSERT( ContextType == FLT_STREAM_CONTEXT ); + + // + // Release NameInfo if present. + // + + if (StreamContext->NameInfo != NULL) { + + FltReleaseFileNameInformation(StreamContext->NameInfo); + StreamContext->NameInfo = NULL; + } +} + + +VOID +DfTransactionContextCleanupCallback ( + _In_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine cleans up a transaction context. + This operation consists basically of walking the DeleteNotifyList and + deleting all the deletion notifications pending on behalf of this + transaction. + +Arguments: + + TransactionContext - Pointer to DF_TRANSACTION_CONTEXT to be cleaned up. + + ContextType - Type of TransactionContext. Must be FLT_TRANSACTION_CONTEXT. + +--*/ +{ + PDF_DELETE_NOTIFY deleteNotify = NULL; + + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + ASSERT( ContextType == FLT_TRANSACTION_CONTEXT ); + + if (NULL != TransactionContext->Resource) { + + FltAcquireResourceExclusive( TransactionContext->Resource ); + + while (!IsListEmpty( &TransactionContext->DeleteNotifyList )) { + + // + // Remove every DF_DELETE_NOTIFY, releasing their corresponding + // FLT_FILE_NAME_INFORMATION objects and freeing pool used by + // them. + // + + deleteNotify = CONTAINING_RECORD( RemoveHeadList( &TransactionContext->DeleteNotifyList ), + DF_DELETE_NOTIFY, + Links ); + + FltReleaseContext( deleteNotify->StreamContext ); + ExFreePool( deleteNotify ); + + } + + FltReleaseResource( TransactionContext->Resource ); + + // + // Delete and free the DeleteNotifyList synchronization resource. + // + + ExDeleteResourceLite( TransactionContext->Resource ); + ExFreePool( TransactionContext->Resource ); + } +} + + +VOID +DfInstanceContextCleanupCallback ( + _In_ PDF_INSTANCE_CONTEXT InstanceContext, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine cleans up an instance context, which consists on freeing + pool used by the volume GUID name string. + +Arguments: + + InstanceContext - Pointer to DF_INSTANCE_CONTEXT to be cleaned up. + + ContextType - Type of InstanceContext. Must be FLT_INSTANCE_CONTEXT. + +--*/ +{ + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + ASSERT( ContextType == FLT_INSTANCE_CONTEXT ); + + DfFreeUnicodeString( &InstanceContext->VolumeGuidName ); +} + + +////////////////////////////////////////////////////////////////////////////// +// Miscellaneous String, File Name and File ID Functions // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DfGetFileNameInformation ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PDF_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine gets and parses the file name information, obtains the File + ID and saves them in the stream context. + +Arguments: + + Data - Pointer to FLT_CALLBACK_DATA. + + StreamContext - Pointer to stream context that will receive the file + information. + +Return Value: + + Returns statuses forwarded from Flt(Get|Parse)FileNameInformation or + FltQueryInformationFile. + +--*/ +{ + NTSTATUS status; + PFLT_FILE_NAME_INFORMATION oldNameInfo; + PFLT_FILE_NAME_INFORMATION newNameInfo; + + PAGED_CODE(); + + // + // FltGetFileNameInformation - this is enough for a file name. + // + + status = FltGetFileNameInformation( Data, + (FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT), + &newNameInfo ); + + if (!NT_SUCCESS( status )) { + return status; + } + + // + // FltParseFileNameInformation - this fills in the other gaps, like the + // stream name, if present. + // + + status = FltParseFileNameInformation( newNameInfo ); + + if (!NT_SUCCESS( status )) { + return status; + } + + // + // Now that we have a good NameInfo, set it in the context, replacing + // the previous one. + // + + oldNameInfo = InterlockedExchangePointer( &StreamContext->NameInfo, + newNameInfo ); + + if (NULL != oldNameInfo) { + + FltReleaseFileNameInformation( oldNameInfo ); + } + + return status; +} + + +NTSTATUS +DfGetFileId ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PDF_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine obtains the File ID and saves it in the stream context. + +Arguments: + + Data - Pointer to FLT_CALLBACK_DATA. + + StreamContext - Pointer to stream context that will receive the file + ID. + +Return Value: + + Returns statuses forwarded from FltQueryInformationFile, including + STATUS_FILE_DELETED. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FILE_INTERNAL_INFORMATION fileInternalInformation; + + PAGED_CODE(); + + // + // Only query the file system for the file ID for the first time. + // This is just an optimization. It doesn't need any real synchronization + // because file IDs don't change. + // + + if (!StreamContext->FileIdSet) { + + // + // Querying for FileInternalInformation gives you the file ID. + // + + status = FltQueryInformationFile( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + &fileInternalInformation, + sizeof(FILE_INTERNAL_INFORMATION), + FileInternalInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + // + // ReFS uses 128-bit file IDs. FileInternalInformation supports 64- + // bit file IDs. ReFS signals that a particular file ID can only + // be represented in 128 bits by returning FILE_INVALID_FILE_ID as + // the file ID. In that case we need to use FileIdInformation. + // + + if (fileInternalInformation.IndexNumber.QuadPart == FILE_INVALID_FILE_ID) { + + FILE_ID_INFORMATION fileIdInformation; + + status = FltQueryInformationFile( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + &fileIdInformation, + sizeof(FILE_ID_INFORMATION), + FileIdInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + // + // We don't use DfSizeofFileId() here because we are not + // measuring the size of a DF_FILE_REFERENCE. We know we have + // a 128-bit value. + // + + RtlCopyMemory( &StreamContext->FileId, + &fileIdInformation.FileId, + sizeof(StreamContext->FileId) ); + + // + // Because there's (currently) no support for 128-bit values in + // the compiler we need to ensure the setting of the ID and our + // remembering that the file ID was set occur in the right order. + // + + KeMemoryBarrier(); + + StreamContext->FileIdSet = TRUE; + } + + } else { + + StreamContext->FileId.FileId64.Value = fileInternalInformation.IndexNumber.QuadPart; + StreamContext->FileId.FileId64.UpperZeroes = 0ll; + + // + // Because there's (currently) no support for 128-bit values in + // the compiler we need to ensure the setting of the ID and our + // remembering that the file ID was set occur in the right order. + // + + KeMemoryBarrier(); + + StreamContext->FileIdSet = TRUE; + } + } + } + + return status; +} + + +NTSTATUS +DfAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This helper routine simply allocates a buffer for a UNICODE_STRING and + initializes its Length to zero. + + It uses whatever value is present in the MaximumLength field as the size + for the allocation. + +Arguments: + + String - Pointer to UNICODE_STRING. + +Return Value: + + STATUS_INSUFFICIENT_RESOURCES if it was not possible to allocate the + buffer from pool. + + STATUS_SUCCESS otherwise. + +--*/ +{ + PAGED_CODE(); + + ASSERT( NULL != String ); + ASSERT( 0 != String->MaximumLength ); + + String->Length = 0; + + String->Buffer = ExAllocatePoolWithTag( DF_CONTEXT_POOL_TYPE, + String->MaximumLength, + DF_STRING_POOL_TAG ); + + if (NULL == String->Buffer) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + return STATUS_SUCCESS; +} + + +VOID +DfFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This helper routine frees the buffer of a UNICODE_STRING and resets its + Length to zero. + +Arguments: + + String - Pointer to UNICODE_STRING. + +--*/ +{ + PAGED_CODE(); + + ASSERT( NULL != String ); + ASSERT( 0 != String->MaximumLength ); + + String->Length = 0; + + if ( NULL != String->Buffer ) { + + String->MaximumLength = 0; + ExFreePool( String->Buffer ); + String->Buffer = NULL; + } +} + + +NTSTATUS +DfGetVolumeGuidName ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PUNICODE_STRING VolumeGuidName + ) +/*++ + +Routine Description: + + This helper routine returns a volume GUID name (with an added trailing + backslash for convenience) in the VolumeGuidName string passed by the + caller. + + The volume GUID name is cached in the instance context for the instance + attached to the volume, and this function will set up an instance context + with the cached name on it if there isn't one already attached to the + instance. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + VolumeGuidName - Pointer to UNICODE_STRING, returning the volume GUID name. + +Return Value: + + Return statuses forwarded by DfAllocateUnicodeString or + FltGetVolumeGuidName. On error, caller needs to DfFreeUnicodeString on + VolumeGuidName. + +--*/ +{ + NTSTATUS status; + PUNICODE_STRING sourceGuidName; + PDF_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + // + // Obtain an instance context. Target is NULL for instance context, as + // the FLT_INSTANCE can be obtained from the FltObjects. + // + + status = DfGetOrSetContext( FltObjects, + NULL, + &instanceContext, + FLT_INSTANCE_CONTEXT ); + + if (NT_SUCCESS( status )) { + + // + // sourceGuidName is the source from where we'll copy the volume + // GUID name. Hopefully the name is present in the instance context + // already (buffer is not NULL) so we'll try to use that. + // + + sourceGuidName = &instanceContext->VolumeGuidName; + + if (NULL == sourceGuidName->Buffer) { + + // + // The volume GUID name is not cached in the instance context + // yet, so we will have to query the volume for it and put it + // in the instance context, so future queries can get it directly + // from the context. + // + + UNICODE_STRING tempString; + + // + // Add sizeof(WCHAR) so it's possible to add a trailing backslash here. + // + + tempString.MaximumLength = DF_VOLUME_GUID_NAME_SIZE * + sizeof(WCHAR) + + sizeof(WCHAR); + + status = DfAllocateUnicodeString( &tempString ); + + if (!NT_SUCCESS( status )) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!%s: DfAllocateUnicodeString returned 0x%08x!\n", + __FUNCTION__, + status ); + + return status; + } + + // while there is no guid name, don't do the open by id deletion logic. + // (it's actually better to defer obtaining the volume GUID name up to + // the point when we actually need it, in the open by ID scenario.) + status = FltGetVolumeGuidName( FltObjects->Volume, + &tempString, + NULL ); + + if (!NT_SUCCESS( status )) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!%s: FltGetVolumeGuidName returned 0x%08x!\n", + __FUNCTION__, + status ); + + DfFreeUnicodeString( &tempString ); + + return status; + } + + // + // Append trailing backslash. + // + + RtlAppendUnicodeToString( &tempString, L"\\" ); + + // + // Now set the sourceGuidName to the tempString. It is okay to + // set Length and MaximumLength with no synchronization because + // those will always be the same value (size of a volume GUID + // name with an extra trailing backslash). + // + + sourceGuidName->Length = tempString.Length; + sourceGuidName->MaximumLength = tempString.MaximumLength; + + // + // Setting the buffer, however, requires some synchronization, + // because another thread might be attempting to do the same, + // and even though they're exactly the same string, they're + // different allocations (buffers) so if the other thread we're + // racing with manages to set the buffer before us, we need to + // free our temporary string buffer. + // + + InterlockedCompareExchangePointer( &sourceGuidName->Buffer, + tempString.Buffer, + NULL ); + + if (sourceGuidName->Buffer != tempString.Buffer) { + + // + // We didn't manage to set the buffer, so let's free the + // tempString buffer. + // + + DfFreeUnicodeString( &tempString ); + } + } + + // + // sourceGuidName now contains the correct GUID name, so copy that + // to the caller string. + // + + RtlCopyUnicodeString( VolumeGuidName, sourceGuidName ); + + // + // We're done with the instance context. + // + + FltReleaseContext( instanceContext ); + } + + return status; +} + + +NTSTATUS +DfBuildFileIdString ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext, + _Out_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This helper routine builds a string used to open a file by its ID. + + It will assume the file ID is properly loaded in the stream context + (StreamContext->FileId). + +Arguments: + + Data - Pointer to FLT_CALLBACK_DATA. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + StreamContext - Pointer to the stream context. + + String - Pointer to UNICODE_STRING (output). + +Return Value: + + Return statuses forwarded by DfAllocateUnicodeString or + FltGetInstanceContext. + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + ASSERT( NULL != String ); + + // + // We'll compose the string with: + // 1. The volume GUID name. + // 2. A backslash + // 3. The File ID. + // + + // + // Make sure the file ID is loaded in the StreamContext. Note that if the + // file has been deleted DfGetFileId will return STATUS_FILE_DELETED. + // Since we're interested in detecting whether the file has been deleted + // that's fine; the open-by-ID will not actually take place. We have to + // ensure it is loaded before building the string length below since we + // may get either a 64-bit or 128-bit file ID back. + // + + status = DfGetFileId( Data, + StreamContext ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // First add the lengths of 1, 2, 3 and allocate accordingly. + // Note that ReFS understands both 64- and 128-bit file IDs when opening + // by ID, so whichever size we get back from DfSizeofFileId will work. + // + + String->MaximumLength = DF_VOLUME_GUID_NAME_SIZE * sizeof(WCHAR) + + sizeof(WCHAR) + + DfSizeofFileId( StreamContext->FileId ); + + status = DfAllocateUnicodeString( String ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // Now obtain the volume GUID name with a trailing backslash (1 + 2). + // + + // obtain volume GUID name here and cache it in the InstanceContext. + status = DfGetVolumeGuidName( FltObjects, + String ); + + if (!NT_SUCCESS( status )) { + + DfFreeUnicodeString( String ); + + return status; + } + + // + // Now append the file ID to the end of the string. + // + + RtlCopyMemory( Add2Ptr( String->Buffer, String->Length ), + &StreamContext->FileId, + DfSizeofFileId( StreamContext->FileId )); + + String->Length += DfSizeofFileId( StreamContext->FileId ); + + ASSERT( String->Length == String->MaximumLength ); + + return status; +} + + +NTSTATUS +DfDetectDeleteByFileId ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This helper routine detects a deleted file by attempting to open it using + its file ID. + + If the file is successfully opened this routine closes the file before returning. + +Arguments: + + Data - Pointer to FLT_CALLBACK_DATA. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + StreamContext - Pointer to the stream context. + +Return Value: + + STATUS_FILE_DELETED - Returned through DfBuildFileIdString if the file has + been deleted. + + STATUS_INVALID_PARAMETER - Returned from FltCreateFileEx2 when opening by ID + a file that doesn't exist. + + STATUS_DELETE_PENDING - The file has been set to be deleted when the last handle + goes away, but there are still open handles. + + Also any other NTSTATUS returned from DfBuildFileIdString, FltCreateFileEx2, + or FltClose. + +--*/ +{ + NTSTATUS status; + UNICODE_STRING fileIdString; + HANDLE handle; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK ioStatus; + IO_DRIVER_CREATE_CONTEXT driverCreateContext; + + PAGED_CODE(); + + // + // First build the file ID string. Note that this may fail with STATUS_FILE_DELETED + // and short-circuit our open-by-ID. Since we're really trying to see if + // the file is deleted, that's perfectly okay. + // + + status = DfBuildFileIdString( Data, + FltObjects, + StreamContext, + &fileIdString ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + InitializeObjectAttributes( &objectAttributes, + &fileIdString, + OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + // + // It is important to initialize the IO_DRIVER_CREATE_CONTEXT structure's + // TxnParameters. We'll always want to do this open on behalf of a + // transaction because opening the file by ID is the method we use to + // detect if the whole file still exists when we're in a transaction. + // + + IoInitializeDriverCreateContext( &driverCreateContext ); + driverCreateContext.TxnParameters = + IoGetTransactionParameterBlock( Data->Iopb->TargetFileObject ); + + status = FltCreateFileEx2( gFilterHandle, + Data->Iopb->TargetInstance, + &handle, + NULL, + FILE_READ_ATTRIBUTES, + &objectAttributes, + &ioStatus, + (PLARGE_INTEGER) NULL, + 0L, + FILE_SHARE_VALID_FLAGS, + FILE_OPEN, + FILE_OPEN_REPARSE_POINT | FILE_OPEN_BY_FILE_ID, + (PVOID) NULL, + 0L, + IO_IGNORE_SHARE_ACCESS_CHECK, + &driverCreateContext ); + + if (NT_SUCCESS( status )) { + + status = FltClose( handle ); + ASSERT( NT_SUCCESS( status ) ); + } + + DfFreeUnicodeString( &fileIdString ); + + return status; +} + + +////////////////////////////////////////////////////////////////////////////// +// Deletion Verification & Processing Functions // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DfIsFileDeleted ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ BOOLEAN IsTransaction + ) +/*++ + +Routine Description: + + This routine returns whether a file was deleted. It is called from + DfProcessDelete after an alternate data stream is deleted. This needs to + be done for the case when the last outstanding handle to a delete-pending + file is a handle to a delete-pending alternate data stream. When that + handle is closed, the whole file goes away, and we want to report a whole + file deletion, not just an alternate data stream deletion. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + StreamContext - Pointer to the stream context. + + IsTransaction - TRUE if in a transaction, FALSE otherwise. + +Return Value: + + STATUS_FILE_DELETED - The whole file was deleted. + Successful status - The file still exists, this was probably just a named + data stream being deleted. + Anything else - Failure in finding out if the file was deleted. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FILE_OBJECTID_BUFFER fileObjectIdBuf; + + FLT_FILESYSTEM_TYPE fileSystemType; + + PAGED_CODE(); + + // + // We need to know whether we're on ReFS or NTFS. + // + + status = FltGetFileSystemType( FltObjects->Instance, + &fileSystemType ); + + if (status != STATUS_SUCCESS) { + + return status; + } + + // + // FSCTL_GET_OBJECT_ID does not return STATUS_FILE_DELETED if the + // file was deleted in a transaction, and this is why we need another + // method for detecting if the file is still present: opening by ID. + // + // If we're on ReFS we also need to open by file ID because ReFS does not + // support object IDs. + // + + if (IsTransaction || + (fileSystemType == FLT_FSTYPE_REFS)) { + + status = DfDetectDeleteByFileId( Data, + FltObjects, + StreamContext ); + + switch (status) { + + case STATUS_INVALID_PARAMETER: + + // + // The file was deleted. In this case, trying to open it + // by ID returns STATUS_INVALID_PARAMETER. + // + + return STATUS_FILE_DELETED; + + case STATUS_DELETE_PENDING: + + // + // In this case, the main file still exists, but is in + // a delete pending state, so we return STATUS_SUCCESS, + // signaling it still exists and wasn't deleted by this + // operation. + // + + return STATUS_SUCCESS; + + default: + + return status; + } + + } else { + + // + // When not in a transaction, attempting to get the object ID of the + // file is a cheaper alternative compared to opening the file by ID. + // + + status = FltFsControlFile( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + FSCTL_GET_OBJECT_ID, + NULL, + 0, + &fileObjectIdBuf, + sizeof(FILE_OBJECTID_BUFFER), + NULL ); + + switch (status) { + + case STATUS_OBJECTID_NOT_FOUND: + + // + // Getting back STATUS_OBJECTID_NOT_FOUND means the file + // still exists, it just doesn't have an object ID. + + return STATUS_SUCCESS; + + default: + + // + // Else we just get back STATUS_FILE_DELETED if the file + // doesn't exist anymore, or some error status, so no + // status conversion is necessary. + // + + NOTHING; + } + } + + return status; +} + + +NTSTATUS +DfAddTransDeleteNotify ( + _Inout_ PDF_STREAM_CONTEXT StreamContext, + _Inout_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ BOOLEAN FileDelete + ) +/*++ + +Routine Description: + + This routine adds a pending deletion notification (DF_DELETE_NOTIFY) + object to the transaction context DeleteNotifyList. It is called from + DfNotifyDelete when a file or stream gets deleted in a transaction. + +Arguments: + + StreamContext - Pointer to the stream context. + + TransactionContext - Pointer to the transaction context. + + FileDelete - TRUE if this is a FILE deletion, FALSE if it's a STREAM + deletion. + +Return Value: + + STATUS_SUCCESS. + +--*/ +{ + PDF_DELETE_NOTIFY deleteNotify; + + PAGED_CODE(); + + ASSERT( NULL != TransactionContext->Resource ); + + ASSERT( NULL != StreamContext ); + + deleteNotify = ExAllocatePoolWithTag( DF_CONTEXT_POOL_TYPE, + sizeof(DF_DELETE_NOTIFY), + DF_DELETE_NOTIFY_POOL_TAG ); + + if (NULL == deleteNotify) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory( deleteNotify, sizeof(DF_DELETE_NOTIFY) ); + + FltReferenceContext( StreamContext ); + deleteNotify->StreamContext = StreamContext; + deleteNotify->FileDelete = FileDelete; + + FltAcquireResourceExclusive( TransactionContext->Resource ); + + InsertTailList( &TransactionContext->DeleteNotifyList, + &deleteNotify->Links ); + + FltReleaseResource( TransactionContext->Resource ); + + return STATUS_SUCCESS; +} + + +VOID +DfNotifyDelete ( + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ BOOLEAN IsFile, + _Inout_opt_ PDF_TRANSACTION_CONTEXT TransactionContext + ) +/*++ + +Routine Description: + + This routine does the processing after it is verified, in the post-cleanup + callback, that a file or stream were deleted. It sorts out whether it's a + file or a stream delete, whether this is in a transacted context or not, + and issues the appropriate notifications. + +Arguments: + + StreamContext - Pointer to the stream context of the deleted file/stream. + + IsFile - TRUE if deleting a file, FALSE for an alternate data stream. + + TransactionContext - The transaction context. Present if in a transaction, + NULL otherwise. + +--*/ +{ + PAGED_CODE(); + + if (InterlockedIncrement( &StreamContext->IsNotified ) <= 1) { + + if (IsFile) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfPostCleanupCallback: " + "A file \"%wZ\" (%p) has been", + &StreamContext->NameInfo->Name, + StreamContext ); + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfPostCleanupCallback: " + "An alternate data stream \"%wZ\" (%p) has been", + &StreamContext->NameInfo->Name, + StreamContext ); + } + + // + // Flag that a delete has been notified on this file/stream. + // + + if (NULL == TransactionContext) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + " deleted!\n" ); + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + " deleted in a transaction!\n" ); + + DfAddTransDeleteNotify( StreamContext, + TransactionContext, + IsFile ); + } + } +} + + +VOID +DfNotifyDeleteOnTransactionEnd ( + _In_ PDF_DELETE_NOTIFY DeleteNotify, + _In_ BOOLEAN Commit + ) +/*++ + +Routine Description: + + This routine is called by the transaction notification callback to issue + the proper notifications for a file that has been deleted in the context + of that transaction. + The file will be reported as finally deleted, if the transaction was + committed, or "saved" if the transaction was rolled back. + +Arguments: + + DeleteNotify - Pointer to the DF_DELETE_NOTIFY object that contains the + data necessary for issuing this notification. + + Commit - TRUE if the transaction was committed, FALSE if it was + rolled back. + +--*/ +{ + PAGED_CODE(); + + if (DeleteNotify->FileDelete) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfTransactionNotificationCallback: " + "A file \"%wZ\" (%p) has been", + &DeleteNotify->StreamContext->NameInfo->Name, + DeleteNotify->StreamContext ); + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfTransactionNotificationCallback: " + "An alternate data stream \"%wZ\" (%p) has been", + &DeleteNotify->StreamContext->NameInfo->Name, + DeleteNotify->StreamContext ); + } + + if (Commit) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + " deleted due to a transaction commit!\n" ); + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + " saved due to a transaction rollback!\n" ); + } +} + + +NTSTATUS +DfProcessDelete ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine does the processing after it is verified, in the post-cleanup + callback, that a file or stream were deleted. It sorts out whether it's a + file or a stream delete, whether this is in a transacted context or not, + and issues the appropriate notifications. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + StreamContext - Pointer to the stream context of the deleted file/stream. + +Return Value: + + STATUS_SUCCESS. + +--*/ +{ + BOOLEAN isTransaction; + BOOLEAN isFileDeleted = FALSE; + NTSTATUS status; + PDF_TRANSACTION_CONTEXT transactionContext = NULL; + + PAGED_CODE(); + + // Is this in a transacted context? + isTransaction = (NULL != FltObjects->Transaction); + + if (isTransaction) { + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfProcessDelete: In a transaction!\n" ); + + status = DfGetOrSetContext( FltObjects, + FltObjects->Transaction, + &transactionContext, + FLT_TRANSACTION_CONTEXT ); + + if (!NT_SUCCESS( status )) { + + return status; + } + } + + // + // Notify deletion. If this is an Alternate Data Stream being deleted, + // check if the whole file was deleted (by calling DfIsFileDeleted) as + // this could be the last handle to a delete-pending file. + // + + status = DfIsFileDeleted( Data, + FltObjects, + StreamContext, + isTransaction ); + + if (STATUS_FILE_DELETED == status) { + + isFileDeleted = TRUE; + status = STATUS_SUCCESS; + + } else if (!NT_SUCCESS( status )) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!%s: DfIsFileDeleted returned 0x%08x!\n", + __FUNCTION__, + status ); + + goto _exit; + } + + DfNotifyDelete( StreamContext, + isFileDeleted, + transactionContext ); + +_exit: + + if (NULL != transactionContext) { + + FltReleaseContext( transactionContext ); + } + + return status; +} + + +////////////////////////////////////////////////////////////////////////////// +// MiniFilter Operation Callback Routines // +////////////////////////////////////////////////////////////////////////////// + +FLT_PREOP_CALLBACK_STATUS +DfPreCreateCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_result_maybenull_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the pre-operation completion routine for + IRP_MJ_CREATE in this miniFilter. + + In the pre-create phase we're concerned with creates with + FILE_DELETE_ON_CLOSE set, and in those cases we want to flag + this stream as a candidate for being deleted. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + FLT_PREOP_SUCCESS_WITH_CALLBACK - When FILE_DELETE_ON_CLOSE is set and + a stream context is created. + + FLT_PREOP_SUCCESS_NO_CALLBACK - When FILE_DELETE_ON_CLOSE is not set + and no stream context is created. + +--*/ +{ + PDF_STREAM_CONTEXT streamContext; + NTSTATUS status; + + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfPreCreateCallback: Entered\n" ); + + // + // Creates are only interesting in the FILE_DELETE_ON_CLOSE scenario, + // in which we'll want to flag this file as a candidate for being + // deleted. + // + // The way we do that is allocate a stream context for this and return + // FLT_PREOP_SUCCESS_NO_CALLBACK, passing down the stream context via + // the completion context, so that the post-create callback can, in case + // of a successful create, attach this context to the stream and flag it + // as a real deletion candidate. + // + + if (FlagOn( Data->Iopb->Parameters.Create.Options, FILE_DELETE_ON_CLOSE )) { + + status = DfAllocateContext( FLT_STREAM_CONTEXT, + &streamContext ); + + if (NT_SUCCESS( status )) { + + *CompletionContext = (PVOID)streamContext; + + return FLT_PREOP_SYNCHRONIZE; + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfPreCreateCallback: An error occurred with DfAllocateStreamContext!\n" ); + } + } + + *CompletionContext = NULL; + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +FLT_POSTOP_CALLBACK_STATUS +DfPostCreateCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-operation completion routine for + IRP_MJ_CREATE in this miniFilter. + + The post-create callback will only be called when this is a create with + FILE_DELETE_ON_CLOSE, meaning we have to flag it as a deletion candidate. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. This will point to a DF_STREAM_CONTEXT allocated by + DfPreCreateCallback, which will be used for flagging this stream + as a deletion candidate. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - we never do any sort of asynchronous + processing here. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDF_STREAM_CONTEXT streamContext = NULL; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + ASSERT( NULL != CompletionContext ); + + streamContext = (PDF_STREAM_CONTEXT)CompletionContext; + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfPostCreateCallback: Entered\n" ); + + // this status check handles the draining scenario. + if (NT_SUCCESS( Data->IoStatus.Status ) && + (STATUS_REPARSE != Data->IoStatus.Status)) { + + // assert we're not draining. + ASSERT( !FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) ); + + // + // Flag the stream as a deletion candidate: try setting the stream + // context on it to the stream context allocated by DfPreCreateCallback. + // If a context is already attached to the stream, DfGetOrSetContext + // will do the right thing and set streamContext to it, freeing the + // other context. + // + + status = DfGetOrSetContext( FltObjects, + Data->Iopb->TargetFileObject, + &streamContext, + FLT_STREAM_CONTEXT ); + + if (NT_SUCCESS( status )) { + + // + // Set DeleteOnClose on the stream context: a delete-on-close stream will + // always be checked for deletion on cleanup. + // + + streamContext->DeleteOnClose = BooleanFlagOn( Data->Iopb->Parameters.Create.Options, + FILE_DELETE_ON_CLOSE ); + } + } + + // + // We will have a context in streamContext, because if allocation fails + // in DfPreCreateCallback, FLT_PREOP_SUCCESS_NO_CALLBACK is returned, so + // there is no post-create callback. + // + // If DfGetOrSetContext failed, if will have released streamContext + // already, so only release it if status is successful. + // + + if (NT_SUCCESS( status )) { + + FltReleaseContext( streamContext ); + } + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +DfPreSetInfoCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the pre-operation completion routine for + IRP_MJ_SET_INFORMATION in this miniFilter. + + The pre-setinfo callback is important because setting + FileDispositionInformation is another way of putting the file in a + delete-pending state. + + Since the delete disposition is a reversible condition, we have to + make sure to do the right thing when multiple operations are racing: + we won't be able to tell the the final outcome of the delete + disposition state of the stream, so everytime a race like that happens, + we assume this stream as a permanent deletion candidate, so it will be + checked for deletion in the post-cleanup callback. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + FLT_PREOP_SYNCHRONIZE - we never do any sort of asynchronous processing + here, and we synchronize postop. + + FLT_PREOP_SUCCESS_NO_CALLBACK - if not FileDispositionInformation or we + can't set a streamcontext. + +--*/ +{ + NTSTATUS status; + PDF_STREAM_CONTEXT streamContext = NULL; + BOOLEAN race; + + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + switch (Data->Iopb->Parameters.SetFileInformation.FileInformationClass) { + + case FileDispositionInformation: + + // + // We're interested when the file delete disposition changes. + // + + status = DfGetOrSetContext( FltObjects, + Data->Iopb->TargetFileObject, + &streamContext, + FLT_STREAM_CONTEXT ); + + if (!NT_SUCCESS( status )) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Race detection logic. The NumOps field in the StreamContext + // counts the number of in-flight changes to delete disposition + // on the stream. + // + // If there's already some operations in flight, don't bother + // doing postop. Since there will be no postop, this value won't + // be decremented, staying forever 2 or more, which is one of + // the conditions for checking deletion at post-cleanup. + // + + race = (InterlockedIncrement( &streamContext->NumOps ) > 1); + + if (!race) { + + // + // This is the only operation in flight, so do a postop on + // it because the final outcome of the delete disposition + // state of the stream is deterministic. + // + + *CompletionContext = (PVOID)streamContext; + + return FLT_PREOP_SYNCHRONIZE; + + } else { + + FltReleaseContext( streamContext ); + } + + // FALL_THROUGH + + default: + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + + break; + } +} + + +FLT_POSTOP_CALLBACK_STATUS +DfPostSetInfoCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-operation completion routine for + IRP_MJ_SET_INFORMATION in this miniFilter. + + In this postop callback we will update the deletion disposition state + of this stream in the stream context. This callback will only be reached + when there's a single change to deletion disposition in flight for the + stream or when this was the first of many racing ops to hit the preop. + + In the latter case, the race is already detected and adequately flagged + in the other preops, so we're safe just decrementing NumOps, because the + other operations will never reach postop and NumOps won't ever be + decremented for them, guaranteeing that NumOps will stay nonzero forever, + effectively flagging the race. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - we never do any sort of asynchronous + processing here. + +--*/ +{ + PDF_STREAM_CONTEXT streamContext; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + // assert on FileDispositionInformation + ASSERT( Data->Iopb->Parameters.SetFileInformation.FileInformationClass + == FileDispositionInformation ); + + // pass from pre-callback to post-callback + ASSERT( NULL != CompletionContext ); + streamContext = (PDF_STREAM_CONTEXT) CompletionContext; + + // + // Reaching a postop for FileDispositionInformation means we + // MUST have a stream context passed in the CompletionContext. + // + + if (NT_SUCCESS( Data->IoStatus.Status )) { + + // + // No synchronization is needed to set the SetDisp field, + // because in case of races, the NumOps field will be perpetually + // positive, and it being positive is already an indication this + // file is a delete candidate, so it will be checked at post- + // -cleanup regardless of the value of SetDisp. + // + + streamContext->SetDisp = ((PFILE_DISPOSITION_INFORMATION) + Data->Iopb->Parameters.SetFileInformation.InfoBuffer)->DeleteFile; + } + + // + // Now that the operation is over, decrement NumOps. + // + + InterlockedDecrement( &streamContext->NumOps ); + + FltReleaseContext( streamContext ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +DfPreCleanupCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the pre-operation completion routine for + IRP_MJ_CLEANUP in this miniFilter. + + In the preop callback for cleanup, we obtain the file information and + save it in the stream context, just so we have a name to use when + reporting file deletions. + + That is done for every stream with an attached stream context because + those will be deletion candidates most of the time. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + FLT_PREOP_SYNCHRONIZE - we never do any sort of asynchronous processing + here, and we want to synchronize the postop. + + FLT_PREOP_SUCCESS_NO_CALLBACK - when we don't manage to get a stream + context. + +--*/ +{ + PDF_STREAM_CONTEXT streamContext; + NTSTATUS status; + + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfPreCleanupCallback: Entered\n" ); + + status = FltGetStreamContext( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + &streamContext ); + + if (NT_SUCCESS( status )) { + + // + // Only streams with stream context will be sent for deletion check + // in post-cleanup, which makes sense because they would only ever + // have one if they were flagged as candidates at some point. + // + // Gather file information here so that we have a name to report. + // The name will be accurate most of the times, and in the cases it + // won't, it serves as a good clue and the stream context pointer + // value should offer a way to disambiguate that in case of renames + // etc. + // + + status = DfGetFileNameInformation( Data, streamContext ); + + if (NT_SUCCESS( status )) { + + // pass from pre-callback to post-callback + *CompletionContext = (PVOID)streamContext; + + return FLT_PREOP_SYNCHRONIZE; + + } else { + + FltReleaseContext( streamContext ); + } + } + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +FLT_POSTOP_CALLBACK_STATUS +DfPostCleanupCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-operation completion routine for + IRP_MJ_CLEANUP in this miniFilter. + + Post-cleanup is the core of this minifilter. Here we check to see if + the stream or file were deleted and report that through DbgPrint. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The completion context set in the pre-operation routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - we never do any sort of asynchronous + processing here. + +--*/ +{ + FILE_STANDARD_INFORMATION fileInfo; + PDF_STREAM_CONTEXT streamContext = NULL; + NTSTATUS status; + + UNREFERENCED_PARAMETER( CompletionContext ); + + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfPostCleanupCallback: Entered\n" ); + + // assert we're not draining. + ASSERT( !FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) ); + + // pass from pre-callback to post-callback + ASSERT( NULL != CompletionContext ); + streamContext = (PDF_STREAM_CONTEXT) CompletionContext; + + if (NT_SUCCESS( Data->IoStatus.Status )) { + + // + // Determine whether or not we should check for deletion. What + // flags a file as a deletion candidate is one or more of the following: + // + // 1. NumOps > 0. This means there are or were racing changes to + // the file delete disposition state, and, in that case, + // we don't know what that state is. So, let's err to the side of + // caution and check if it was deleted. + // + // 2. SetDisp. If this is TRUE and we haven't raced in setting delete + // disposition, this reflects the true delete disposition state of the + // file, meaning we must check for deletes if it is set to TRUE. + // + // 3. DeleteOnClose. If the file was ever opened with + // FILE_DELETE_ON_CLOSE, we must check to see if it was deleted. + // + // Also, if a deletion of this stream was already notified, there is no + // point notifying it again. + // + + if (((streamContext->NumOps > 0) || + (streamContext->SetDisp) || + (streamContext->DeleteOnClose)) && + (0 == streamContext->IsNotified)) { + + // + // The check for deletion is done via a query to + // FileStandardInformation. If that returns STATUS_FILE_DELETED + // it means the stream was deleted. + // + + status = FltQueryInformationFile( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + &fileInfo, + sizeof(fileInfo), + FileStandardInformation, + NULL ); + + if (STATUS_FILE_DELETED == status) { + + status = DfProcessDelete( Data, + FltObjects, + streamContext ); + + if (!NT_SUCCESS( status )) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!%s: It was not possible to verify " + "deletion due to an error in DfProcessDelete (0x%08x)!\n", + __FUNCTION__, + status ); + } + } + } + } + + FltReleaseContext( streamContext ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +NTSTATUS +DfTransactionNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG NotificationMask + ) +/*++ + +Routine Description: + + This routine is the transaction notification callback for this minifilter. + It is called when a transaction we're enlisted in is committed or rolled + back so that it's possible to emit notifications about files that were + deleted in that transaction. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + TransactionContext - The transaction context, set/modified when a delete + is detected. + + NotificationMask - A mask of flags indicating the notifications received + from FltMgr. Should be either TRANSACTION_NOTIFY_COMMIT or + TRANSACTION_NOTIFY_ROLLBACK. + +Return Value: + + STATUS_SUCCESS - This operation is never pended. + +--*/ +{ + BOOLEAN commit = BooleanFlagOn( NotificationMask, TRANSACTION_NOTIFY_COMMIT_FINALIZE ); + PDF_DELETE_NOTIFY deleteNotify = NULL; + + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + // + // There is no such thing as a simultaneous commit and rollback, nor + // should we get notifications for events other than a commit or a + // rollback. + // + + ASSERT( (!FlagOnAll( NotificationMask, (DF_NOTIFICATION_MASK) )) && + FlagOn( NotificationMask, (DF_NOTIFICATION_MASK) ) ); + + if (commit) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfTransactionNotificationCallback: COMMIT!\n" ); + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfTransactionNotificationCallback: ROLLBACK!\n" ); + } + + ASSERT( NULL != TransactionContext->Resource ); + + FltAcquireResourceExclusive( TransactionContext->Resource ); + + while (!IsListEmpty( &TransactionContext->DeleteNotifyList )) { + + deleteNotify = CONTAINING_RECORD( RemoveHeadList( &TransactionContext->DeleteNotifyList ), + DF_DELETE_NOTIFY, + Links ); + + ASSERT( NULL != deleteNotify->StreamContext ); + + if (!commit) { + InterlockedDecrement( &deleteNotify->StreamContext->IsNotified ); + } + + DfNotifyDeleteOnTransactionEnd( deleteNotify, + commit ); + + // release stream context + FltReleaseContext( deleteNotify->StreamContext ); + ExFreePool( deleteNotify ); + } + + FltReleaseResource( TransactionContext->Resource ); + + return STATUS_SUCCESS; +} + + + diff --git a/filesys/miniFilter/delete/delete.inf b/filesys/miniFilter/delete/delete.inf new file mode 100644 index 00000000..350f6df7 --- /dev/null +++ b/filesys/miniFilter/delete/delete.inf @@ -0,0 +1,96 @@ +;;; +;;; delete +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.1 +CatalogFile = delete.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"DebugFlags",0x00010001 ,0x0 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +delete.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Delete Notification Mini-Filter Driver" +ServiceName = "delete" +DriverName = "delete" +DiskId1 = "delete Device Installation Disk" + +;Instances specific information. +DefaultInstance = "delete Instance" +Instance1.Name = "delete Instance" +Instance1.Altitude = "370150" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/delete/delete.rc b/filesys/miniFilter/delete/delete.rc new file mode 100644 index 00000000..fdffe048 --- /dev/null +++ b/filesys/miniFilter/delete/delete.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Delete Notification Filter Driver" +#define VER_INTERNALNAME_STR "delete.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/delete/delete.sln b/filesys/miniFilter/delete/delete.sln new file mode 100644 index 00000000..3af04bb5 --- /dev/null +++ b/filesys/miniFilter/delete/delete.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "delete", "delete.vcxproj", "{A872AE22-1A64-4531-A85F-03361BE73894}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|Win32.ActiveCfg = Debug|Win32 + {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|Win32.Build.0 = Debug|Win32 + {A872AE22-1A64-4531-A85F-03361BE73894}.Release|Win32.ActiveCfg = Release|Win32 + {A872AE22-1A64-4531-A85F-03361BE73894}.Release|Win32.Build.0 = Release|Win32 + {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|x64.ActiveCfg = Debug|x64 + {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|x64.Build.0 = Debug|x64 + {A872AE22-1A64-4531-A85F-03361BE73894}.Release|x64.ActiveCfg = Release|x64 + {A872AE22-1A64-4531-A85F-03361BE73894}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/delete/delete.vcxproj b/filesys/miniFilter/delete/delete.vcxproj new file mode 100644 index 00000000..c2863872 --- /dev/null +++ b/filesys/miniFilter/delete/delete.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A872AE22-1A64-4531-A85F-03361BE73894} + $(MSBuildProjectName) + Debug + Win32 + {DDF5DA16-05BC-4E87-9743-B11BFFA79395} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + delete + + + delete + + + delete + + + delete + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/delete/delete.vcxproj.Filters b/filesys/miniFilter/delete/delete.vcxproj.Filters new file mode 100644 index 00000000..994af6a4 --- /dev/null +++ b/filesys/miniFilter/delete/delete.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {0433160E-D11D-43E0-962A-F91BE56E9913} + + + h;hpp;hxx;hm;inl;inc;xsd + {1BDAC8A0-7CBD-4CC1-9BC9-531A473E2C93} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {391C5CBD-0397-4FCB-AFAE-CBDE3F118A7F} + + + inf;inv;inx;mof;mc; + {1791F586-ED65-4309-8E44-3A9A74B4E2D7} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/minispy/ReadMe.md b/filesys/miniFilter/minispy/ReadMe.md new file mode 100644 index 00000000..12c114d2 --- /dev/null +++ b/filesys/miniFilter/minispy/ReadMe.md @@ -0,0 +1,17 @@ +Minispy File System Minifilter Driver +===================================== + +The Minispy sample is a tool to monitor and log any I/O and transaction activity that occurs in the system. Minispy is implemented as a minifilter. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +Minispy consists of both user-mode and kernel-mode components. The kernel-mode component registers callback functions that correspond to various I/O and transaction operations with the filter manager. These callback functions help Minispy record any I/O and transaction activity occurring in the system. When a user can request the recorded information, the recorded information is passed to the user-mode component, which can either output it on screen or log it to a file on disk. + +To observe I/O activity on a device, you must explicitly attach Minispy to that device by using the Minispy user-mode component. Similarly, you can request Minispy to stop logging data for a particular device. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/minispy/filter/RegistrationData.c b/filesys/miniFilter/minispy/filter/RegistrationData.c new file mode 100644 index 00000000..52d42ec6 --- /dev/null +++ b/filesys/miniFilter/minispy/filter/RegistrationData.c @@ -0,0 +1,302 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + RegistrationData.c + +Abstract: + + This filters registration information. Note that this is in a unique file + so it could be set into the INIT section. + +Environment: + + Kernel mode + +--*/ + +#include "mspyKern.h" + +//--------------------------------------------------------------------------- +// Registration information for FLTMGR. +//--------------------------------------------------------------------------- + +// +// Tells the compiler to define all following DATA and CONSTANT DATA to +// be placed in the INIT segment. +// + +#ifdef ALLOC_DATA_PRAGMA + #pragma data_seg("INIT") + #pragma const_seg("INIT") +#endif + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_CREATE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_CREATE_NAMED_PIPE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_CLOSE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_READ, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_WRITE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_QUERY_INFORMATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SET_INFORMATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_QUERY_EA, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SET_EA, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_FLUSH_BUFFERS, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_QUERY_VOLUME_INFORMATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SET_VOLUME_INFORMATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_DIRECTORY_CONTROL, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_DEVICE_CONTROL, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_INTERNAL_DEVICE_CONTROL, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SHUTDOWN, + 0, + SpyPreOperationCallback, + NULL }, //post operation callback not supported + + { IRP_MJ_LOCK_CONTROL, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_CLEANUP, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_CREATE_MAILSLOT, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_QUERY_SECURITY, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SET_SECURITY, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_QUERY_QUOTA, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SET_QUOTA, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_PNP, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_ACQUIRE_FOR_MOD_WRITE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_RELEASE_FOR_MOD_WRITE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_ACQUIRE_FOR_CC_FLUSH, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_RELEASE_FOR_CC_FLUSH, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + +/* { IRP_MJ_NOTIFY_STREAM_FILE_OBJECT, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback },*/ + + { IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_NETWORK_QUERY_OPEN, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_MDL_READ, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_MDL_READ_COMPLETE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_PREPARE_MDL_WRITE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_MDL_WRITE_COMPLETE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_VOLUME_MOUNT, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_VOLUME_DISMOUNT, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_OPERATION_END } +}; + +const FLT_CONTEXT_REGISTRATION Contexts[] = { + +#if MINISPY_VISTA + + { FLT_TRANSACTION_CONTEXT, + 0, + SpyDeleteTxfContext, + sizeof(MINISPY_TRANSACTION_CONTEXT), + 'ypsM' }, + +#endif // MINISPY_VISTA + + { FLT_CONTEXT_END } +}; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof(FLT_REGISTRATION), // Size + FLT_REGISTRATION_VERSION, // Version +#if MINISPY_WIN8 + FLTFL_REGISTRATION_SUPPORT_NPFS_MSFS, // Flags +#else + 0, // Flags +#endif // MINISPY_WIN8 + + Contexts, // Context + Callbacks, // Operation callbacks + + SpyFilterUnload, // FilterUnload + + NULL, // InstanceSetup + SpyQueryTeardown, // InstanceQueryTeardown + NULL, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL // NormalizeNameComponent + +#if MINISPY_VISTA + + , + SpyKtmNotificationCallback // KTM notification callback + +#endif // MINISPY_VISTA + +}; + + +// +// Tells the compiler to restore the given section types back to their previous +// section definition. +// + +#ifdef ALLOC_DATA_PRAGMA + #pragma data_seg() + #pragma const_seg() +#endif + diff --git a/filesys/miniFilter/minispy/filter/minispy.c b/filesys/miniFilter/minispy/filter/minispy.c new file mode 100644 index 00000000..5e4c59cf --- /dev/null +++ b/filesys/miniFilter/minispy/filter/minispy.c @@ -0,0 +1,1380 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + MiniSpy.c + +Abstract: + + This is the main module for the MiniSpy mini-filter. + +Environment: + + Kernel mode + +--*/ + +#include "mspyKern.h" +#include + +// +// Global variables +// + +MINISPY_DATA MiniSpyData; +NTSTATUS StatusToBreakOn = 0; + +//--------------------------------------------------------------------------- +// Function prototypes +//--------------------------------------------------------------------------- +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + + +NTSTATUS +SpyMessage ( + _In_ PVOID ConnectionCookie, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ ULONG InputBufferSize, + _Out_writes_bytes_to_opt_(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferSize, + _Out_ PULONG ReturnOutputBufferLength + ); + +NTSTATUS +SpyConnect( + _In_ PFLT_PORT ClientPort, + _In_ PVOID ServerPortCookie, + _In_reads_bytes_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Flt_ConnectionCookie_Outptr_ PVOID *ConnectionCookie + ); + +VOID +SpyDisconnect( + _In_opt_ PVOID ConnectionCookie + ); + +NTSTATUS +SpyEnlistInTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects + ); + +//--------------------------------------------------------------------------- +// Assign text sections for each routine. +//--------------------------------------------------------------------------- + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(INIT, DriverEntry) + #pragma alloc_text(PAGE, SpyFilterUnload) + #pragma alloc_text(PAGE, SpyQueryTeardown) + #pragma alloc_text(PAGE, SpyConnect) + #pragma alloc_text(PAGE, SpyDisconnect) + #pragma alloc_text(PAGE, SpyMessage) +#endif + + +#define SetFlagInterlocked(_ptrFlags,_flagToSet) \ + ((VOID)InterlockedOr(((volatile LONG *)(_ptrFlags)),_flagToSet)) + +//--------------------------------------------------------------------------- +// ROUTINES +//--------------------------------------------------------------------------- + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine is called when a driver first loads. Its purpose is to + initialize global state and then register with FltMgr to start filtering. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Status of the operation. + +--*/ +{ + PSECURITY_DESCRIPTOR sd; + OBJECT_ATTRIBUTES oa; + UNICODE_STRING uniString; + NTSTATUS status = STATUS_SUCCESS; + + try { + + // + // Initialize global data structures. + // + + MiniSpyData.LogSequenceNumber = 0; + MiniSpyData.MaxRecordsToAllocate = DEFAULT_MAX_RECORDS_TO_ALLOCATE; + MiniSpyData.RecordsAllocated = 0; + MiniSpyData.NameQueryMethod = DEFAULT_NAME_QUERY_METHOD; + + MiniSpyData.DriverObject = DriverObject; + + InitializeListHead( &MiniSpyData.OutputBufferList ); + KeInitializeSpinLock( &MiniSpyData.OutputBufferLock ); + + ExInitializeNPagedLookasideList( &MiniSpyData.FreeBufferList, + NULL, + NULL, + POOL_NX_ALLOCATION, + RECORD_SIZE, + SPY_TAG, + 0 ); + +#if MINISPY_VISTA + + // + // Dynamically import FilterMgr APIs for transaction support + // + +#pragma warning(push) +#pragma warning(disable:4055) // type cast from data pointer to function pointer + MiniSpyData.PFltSetTransactionContext = (PFLT_SET_TRANSACTION_CONTEXT) FltGetRoutineAddress( "FltSetTransactionContext" ); + MiniSpyData.PFltGetTransactionContext = (PFLT_GET_TRANSACTION_CONTEXT) FltGetRoutineAddress( "FltGetTransactionContext" ); + MiniSpyData.PFltEnlistInTransaction = (PFLT_ENLIST_IN_TRANSACTION) FltGetRoutineAddress( "FltEnlistInTransaction" ); +#pragma warning(pop) + +#endif + + // + // Read the custom parameters for MiniSpy from the registry + // + + SpyReadDriverParameters(RegistryPath); + + // + // Now that our global configuration is complete, register with FltMgr. + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &MiniSpyData.Filter ); + + if (!NT_SUCCESS( status )) { + + leave; + } + + + status = FltBuildDefaultSecurityDescriptor( &sd, + FLT_PORT_ALL_ACCESS ); + + if (!NT_SUCCESS( status )) { + leave; + } + + RtlInitUnicodeString( &uniString, MINISPY_PORT_NAME ); + + InitializeObjectAttributes( &oa, + &uniString, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + NULL, + sd ); + + status = FltCreateCommunicationPort( MiniSpyData.Filter, + &MiniSpyData.ServerPort, + &oa, + NULL, + SpyConnect, + SpyDisconnect, + SpyMessage, + 1 ); + + FltFreeSecurityDescriptor( sd ); + + if (!NT_SUCCESS( status )) { + leave; + } + + // + // We are now ready to start filtering + // + + status = FltStartFiltering( MiniSpyData.Filter ); + + } finally { + + if (!NT_SUCCESS( status ) ) { + + if (NULL != MiniSpyData.ServerPort) { + FltCloseCommunicationPort( MiniSpyData.ServerPort ); + } + + if (NULL != MiniSpyData.Filter) { + FltUnregisterFilter( MiniSpyData.Filter ); + } + + ExDeleteNPagedLookasideList( &MiniSpyData.FreeBufferList ); + } + } + + return status; +} + +NTSTATUS +SpyConnect( + _In_ PFLT_PORT ClientPort, + _In_ PVOID ServerPortCookie, + _In_reads_bytes_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Flt_ConnectionCookie_Outptr_ PVOID *ConnectionCookie + ) +/*++ + +Routine Description + + This is called when user-mode connects to the server + port - to establish a connection + +Arguments + + ClientPort - This is the pointer to the client port that + will be used to send messages from the filter. + ServerPortCookie - unused + ConnectionContext - unused + SizeofContext - unused + ConnectionCookie - unused + +Return Value + + STATUS_SUCCESS - to accept the connection +--*/ +{ + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ServerPortCookie ); + UNREFERENCED_PARAMETER( ConnectionContext ); + UNREFERENCED_PARAMETER( SizeOfContext); + UNREFERENCED_PARAMETER( ConnectionCookie ); + + FLT_ASSERT( MiniSpyData.ClientPort == NULL ); + MiniSpyData.ClientPort = ClientPort; + return STATUS_SUCCESS; +} + + +VOID +SpyDisconnect( + _In_opt_ PVOID ConnectionCookie + ) +/*++ + +Routine Description + + This is called when the connection is torn-down. We use it to close our handle to the connection + +Arguments + + ConnectionCookie - unused + +Return value + + None +--*/ +{ + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ConnectionCookie ); + + // + // Close our handle + // + + FltCloseClientPort( MiniSpyData.Filter, &MiniSpyData.ClientPort ); +} + +NTSTATUS +SpyFilterUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when a request has been made to unload the filter. Unload + requests from the Operation System (ex: "sc stop minispy" can not be + failed. Other unload requests may be failed. + + You can disallow OS unload request by setting the + FLTREGFL_DO_NOT_SUPPORT_SERVICE_STOP flag in the FLT_REGISTARTION + structure. + +Arguments: + + Flags - Flags pertinent to this operation + +Return Value: + + Always success + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + // + // Close the server port. This will stop new connections. + // + + FltCloseCommunicationPort( MiniSpyData.ServerPort ); + + FltUnregisterFilter( MiniSpyData.Filter ); + + SpyEmptyOutputBufferList(); + ExDeleteNPagedLookasideList( &MiniSpyData.FreeBufferList ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +SpyQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This allows our filter to be manually detached from a volume. + +Arguments: + + FltObjects - Contains pointer to relevant objects for this operation. + Note that the FileObject field will always be NULL. + + Flags - Flags pertinent to this operation + +Return Value: + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + PAGED_CODE(); + return STATUS_SUCCESS; +} + + +NTSTATUS +SpyMessage ( + _In_ PVOID ConnectionCookie, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ ULONG InputBufferSize, + _Out_writes_bytes_to_opt_(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferSize, + _Out_ PULONG ReturnOutputBufferLength + ) +/*++ + +Routine Description: + + This is called whenever a user mode application wishes to communicate + with this minifilter. + +Arguments: + + ConnectionCookie - unused + + OperationCode - An identifier describing what type of message this + is. These codes are defined by the MiniFilter. + InputBuffer - A buffer containing input data, can be NULL if there + is no input data. + InputBufferSize - The size in bytes of the InputBuffer. + OutputBuffer - A buffer provided by the application that originated + the communication in which to store data to be returned to this + application. + OutputBufferSize - The size in bytes of the OutputBuffer. + ReturnOutputBufferSize - The size in bytes of meaningful data + returned in the OutputBuffer. + +Return Value: + + Returns the status of processing the message. + +--*/ +{ + MINISPY_COMMAND command; + NTSTATUS status; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ConnectionCookie ); + + // + // **** PLEASE READ **** + // + // The INPUT and OUTPUT buffers are raw user mode addresses. The filter + // manager has already done a ProbedForRead (on InputBuffer) and + // ProbedForWrite (on OutputBuffer) which guarentees they are valid + // addresses based on the access (user mode vs. kernel mode). The + // minifilter does not need to do their own probe. + // + // The filter manager is NOT doing any alignment checking on the pointers. + // The minifilter must do this themselves if they care (see below). + // + // The minifilter MUST continue to use a try/except around any access to + // these buffers. + // + + if ((InputBuffer != NULL) && + (InputBufferSize >= (FIELD_OFFSET(COMMAND_MESSAGE,Command) + + sizeof(MINISPY_COMMAND)))) { + + try { + + // + // Probe and capture input message: the message is raw user mode + // buffer, so need to protect with exception handler + // + + command = ((PCOMMAND_MESSAGE) InputBuffer)->Command; + + } except (SpyExceptionFilter( GetExceptionInformation(), TRUE )) { + + return GetExceptionCode(); + } + + switch (command) { + + case GetMiniSpyLog: + + // + // Return as many log records as can fit into the OutputBuffer + // + + if ((OutputBuffer == NULL) || (OutputBufferSize == 0)) { + + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // We want to validate that the given buffer is POINTER + // aligned. But if this is a 64bit system and we want to + // support 32bit applications we need to be careful with how + // we do the check. Note that the way SpyGetLog is written + // it actually does not care about alignment but we are + // demonstrating how to do this type of check. + // + +#if defined(_WIN64) + + if (IoIs32bitProcess( NULL )) { + + // + // Validate alignment for the 32bit process on a 64bit + // system + // + + if (!IS_ALIGNED(OutputBuffer,sizeof(ULONG))) { + + status = STATUS_DATATYPE_MISALIGNMENT; + break; + } + + } else { + +#endif + + if (!IS_ALIGNED(OutputBuffer,sizeof(PVOID))) { + + status = STATUS_DATATYPE_MISALIGNMENT; + break; + } + +#if defined(_WIN64) + + } + +#endif + + // + // Get the log record. + // + + status = SpyGetLog( OutputBuffer, + OutputBufferSize, + ReturnOutputBufferLength ); + break; + + + case GetMiniSpyVersion: + + // + // Return version of the MiniSpy filter driver. Verify + // we have a valid user buffer including valid + // alignment + // + + if ((OutputBufferSize < sizeof( MINISPYVER )) || + (OutputBuffer == NULL)) { + + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Validate Buffer alignment. If a minifilter cares about + // the alignment value of the buffer pointer they must do + // this check themselves. Note that a try/except will not + // capture alignment faults. + // + + if (!IS_ALIGNED(OutputBuffer,sizeof(ULONG))) { + + status = STATUS_DATATYPE_MISALIGNMENT; + break; + } + + // + // Protect access to raw user-mode output buffer with an + // exception handler + // + + try { + + ((PMINISPYVER)OutputBuffer)->Major = MINISPY_MAJ_VERSION; + ((PMINISPYVER)OutputBuffer)->Minor = MINISPY_MIN_VERSION; + + } except (SpyExceptionFilter( GetExceptionInformation(), TRUE )) { + + return GetExceptionCode(); + } + + *ReturnOutputBufferLength = sizeof( MINISPYVER ); + status = STATUS_SUCCESS; + break; + + default: + status = STATUS_INVALID_PARAMETER; + break; + } + + } else { + + status = STATUS_INVALID_PARAMETER; + } + + return status; +} + + +//--------------------------------------------------------------------------- +// Operation filtering routines +//--------------------------------------------------------------------------- + + +FLT_PREOP_CALLBACK_STATUS +#pragma warning(suppress: 6262) // higher than usual stack usage is considered safe in this case +SpyPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine receives ALL pre-operation callbacks for this filter. It then + tries to log information about the given operation. If we are able + to log information then we will call our post-operation callback routine. + + NOTE: This routine must be NON-PAGED because it can be called on the + paging path. + +Arguments: + + Data - Contains information about the given operation. + + FltObjects - Contains pointers to the various objects that are pertinent + to this operation. + + CompletionContext - This receives the address of our log buffer for this + operation. Our completion routine then receives this buffer address. + +Return Value: + + Identifies how processing should continue for this operation + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; //assume we are NOT going to call our completion routine + PRECORD_LIST recordList; + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + UNICODE_STRING defaultName; + PUNICODE_STRING nameToUse; + NTSTATUS status; + +#if MINISPY_VISTA + + PUNICODE_STRING ecpDataToUse = NULL; + UNICODE_STRING ecpData; + WCHAR ecpDataBuffer[MAX_NAME_SPACE/sizeof(WCHAR)]; + +#endif + +#if MINISPY_NOT_W2K + + WCHAR name[MAX_NAME_SPACE/sizeof(WCHAR)]; + +#endif + + // + // Try and get a log record + // + + recordList = SpyNewRecord(); + + if (recordList) { + + // + // We got a log record, if there is a file object, get its name. + // + // NOTE: By default, we use the query method + // FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP + // because MiniSpy would like to get the name as much as possible, but + // can cope if we can't retrieve a name. For a debugging type filter, + // like Minispy, this is reasonable, but for most production filters + // who need names reliably, they should query the name at times when it + // is known to be safe and use the query method + // FLT_FILE_NAME_QUERY_DEFAULT. + // + + if (FltObjects->FileObject != NULL) { + + status = FltGetFileNameInformation( Data, + FLT_FILE_NAME_NORMALIZED | + MiniSpyData.NameQueryMethod, + &nameInfo ); + + } else { + + // + // Can't get a name when there's no file object + // + + status = STATUS_UNSUCCESSFUL; + } + + // + // Use the name if we got it else use a default name + // + + if (NT_SUCCESS( status )) { + + nameToUse = &nameInfo->Name; + + // + // Parse the name if requested + // + + if (FlagOn( MiniSpyData.DebugFlags, SPY_DEBUG_PARSE_NAMES )) { + +#ifdef DBG + + FLT_ASSERT( NT_SUCCESS( FltParseFileNameInformation( nameInfo ) ) ); + +#else + + FltParseFileNameInformation( nameInfo ); + +#endif + + } + + } else { + +#if MINISPY_NOT_W2K + + NTSTATUS lstatus; + PFLT_FILE_NAME_INFORMATION lnameInfo; + + // + // If we couldn't get the "normalized" name try and get the + // "opened" name + // + + if (FltObjects->FileObject != NULL) { + + // + // Get the opened name + // + + lstatus = FltGetFileNameInformation( Data, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, + &lnameInfo ); + + + if (NT_SUCCESS(lstatus)) { + +#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + (VOID)_snwprintf( name, + sizeof(name)/sizeof(WCHAR), + L"<%08x> %wZ", + status, + &lnameInfo->Name ); + + FltReleaseFileNameInformation( lnameInfo ); + + } else { + + // + // If that failed report both NORMALIZED status and + // OPENED status + // + +#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + (VOID)_snwprintf( name, + sizeof(name)/sizeof(WCHAR), + L"", + status, + lstatus ); + } + + } else { + +#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + (VOID)_snwprintf( name, + sizeof(name)/sizeof(WCHAR), + L"" ); + + } + + // + // Name was initialized by _snwprintf() so it may not be null terminated + // if the buffer is insufficient. We will ignore this error and truncate + // the file name. + // + + name[(sizeof(name)/sizeof(WCHAR))-1] = L'\0'; + + RtlInitUnicodeString( &defaultName, name ); + nameToUse = &defaultName; + +#else + + // + // We were unable to get the String safe routine to work on W2K + // Do it the old safe way + // + + RtlInitUnicodeString( &defaultName, L"" ); + nameToUse = &defaultName; + +#endif //MINISPY_NOT_W2K + +#if DBG + + // + // Debug support to break on certain errors. + // + + if (FltObjects->FileObject != NULL) { + NTSTATUS retryStatus; + + if ((StatusToBreakOn != 0) && (status == StatusToBreakOn)) { + + DbgBreakPoint(); + } + + retryStatus = FltGetFileNameInformation( Data, + FLT_FILE_NAME_NORMALIZED | + MiniSpyData.NameQueryMethod, + &nameInfo ); + + if (!NT_SUCCESS( retryStatus )) { + + // + // We always release nameInfo, so ignore return value. + // + + NOTHING; + } + } + +#endif + + } + +#if MINISPY_VISTA + + // + // Look for ECPs, but only if it's a create operation + // + + if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) { + + // + // Initialize an empty string to receive an ECP data dump + // + + RtlInitEmptyUnicodeString( &ecpData, + ecpDataBuffer, + MAX_NAME_SPACE/sizeof(WCHAR) ); + + // + // Parse any extra create parameters + // + + SpyParseEcps( Data, recordList, &ecpData ); + + ecpDataToUse = &ecpData; + } + + // + // Store the name and ECP data (if any) + // + + SpySetRecordNameAndEcpData( &(recordList->LogRecord), nameToUse, ecpDataToUse ); + +#else + + // + // Store the name + // + + SpySetRecordName( &(recordList->LogRecord), nameToUse ); + +#endif + + // + // Release the name information structure (if defined) + // + + if (NULL != nameInfo) { + + FltReleaseFileNameInformation( nameInfo ); + } + + // + // Set all of the operation information into the record + // + + SpyLogPreOperationData( Data, FltObjects, recordList ); + + // + // Pass the record to our completions routine and return that + // we want our completion routine called. + // + + if (Data->Iopb->MajorFunction == IRP_MJ_SHUTDOWN) { + + // + // Since completion callbacks are not supported for + // this operation, do the completion processing now + // + + SpyPostOperationCallback( Data, + FltObjects, + recordList, + 0 ); + + returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + + } else { + + *CompletionContext = recordList; + returnStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK; + } + } + + return returnStatus; +} + + +FLT_POSTOP_CALLBACK_STATUS +SpyPostOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine receives ALL post-operation callbacks. This will take + the log record passed in the context parameter and update it with + the completion information. It will then insert it on a list to be + sent to the usermode component. + + NOTE: This routine must be NON-PAGED because it can be called at DPC level + +Arguments: + + Data - Contains information about the given operation. + + FltObjects - Contains pointers to the various objects that are pertinent + to this operation. + + CompletionContext - Pointer to the RECORD_LIST structure in which we + store the information we are logging. This was passed from the + pre-operation callback + + Flags - Contains information as to why this routine was called. + +Return Value: + + Identifies how processing should continue for this operation + +--*/ +{ + PRECORD_LIST recordList; + PRECORD_LIST reparseRecordList = NULL; + PLOG_RECORD reparseLogRecord; + PFLT_TAG_DATA_BUFFER tagData; + ULONG copyLength; + + UNREFERENCED_PARAMETER( FltObjects ); + + recordList = (PRECORD_LIST)CompletionContext; + + // + // If our instance is in the process of being torn down don't bother to + // log this record, free it now. + // + + if (FlagOn(Flags,FLTFL_POST_OPERATION_DRAINING)) { + + SpyFreeRecord( recordList ); + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Set completion information into the record + // + + SpyLogPostOperationData( Data, recordList ); + + // + // Log reparse tag information if specified. + // + + tagData = Data->TagData; + if (tagData) { + + reparseRecordList = SpyNewRecord(); + + if (reparseRecordList) { + + // + // only copy the DATA portion of the information + // + + RtlCopyMemory( &reparseRecordList->LogRecord.Data, + &recordList->LogRecord.Data, + sizeof(RECORD_DATA) ); + + reparseLogRecord = &reparseRecordList->LogRecord; + + copyLength = FLT_TAG_DATA_BUFFER_HEADER_SIZE + tagData->TagDataLength; + + if(copyLength > MAX_NAME_SPACE) { + + copyLength = MAX_NAME_SPACE; + } + + // + // Copy reparse data + // + + RtlCopyMemory( + &reparseRecordList->LogRecord.Name[0], + tagData, + copyLength + ); + + reparseLogRecord->RecordType |= RECORD_TYPE_FILETAG; + reparseLogRecord->Length += (ULONG) ROUND_TO_SIZE( copyLength, sizeof( PVOID ) ); + } + } + + // + // Send the logged information to the user service. + // + + SpyLog( recordList ); + + if (reparseRecordList) { + + SpyLog( reparseRecordList ); + } + + // + // For creates within a transaction enlist in the transaction + // if we haven't already done. + // + + if ((FltObjects->Transaction != NULL) && + (Data->Iopb->MajorFunction == IRP_MJ_CREATE) && + (Data->IoStatus.Status == STATUS_SUCCESS)) { + + // + // Enlist in the transaction. + // + + SpyEnlistInTransaction( FltObjects ); + } + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +NTSTATUS +SpyEnlistInTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects + ) +/*++ + +Routine Description + + Minispy calls this function to enlist in a transaction of interest. + +Arguments + + FltObjects - Contains parameters required to enlist in a transaction. + +Return value + + Returns STATUS_SUCCESS if we were able to successfully enlist in a new transcation or if we + were already enlisted in the transaction. Returns an appropriate error code on a failure. + +--*/ +{ + +#if MINISPY_VISTA + + PMINISPY_TRANSACTION_CONTEXT transactionContext = NULL; + PMINISPY_TRANSACTION_CONTEXT oldTransactionContext = NULL; + PRECORD_LIST recordList; + NTSTATUS status; + static ULONG Sequence=1; + + // + // This code is only built in the Vista environment, but + // we need to ensure this binary still runs down-level. Return + // at this point if the transaction dynamic imports were not found. + // + // If we find FltGetTransactionContext, we assume the other + // transaction APIs are also present. + // + + if (NULL == MiniSpyData.PFltGetTransactionContext) { + + return STATUS_SUCCESS; + } + + // + // Try to get our context for this transaction. If we get + // one we have already enlisted in this transaction. + // + + status = (*MiniSpyData.PFltGetTransactionContext)( FltObjects->Instance, + FltObjects->Transaction, + &transactionContext ); + + if (NT_SUCCESS( status )) { + + // + // Check if we have already enlisted in the transaction. + // + + if (FlagOn(transactionContext->Flags, MINISPY_ENLISTED_IN_TRANSACTION)) { + + // + // FltGetTransactionContext puts a reference on the context. Release + // that now and return success. + // + + FltReleaseContext( transactionContext ); + return STATUS_SUCCESS; + } + + // + // If we have not enlisted then we need to try and enlist in the transaction. + // + + goto ENLIST_IN_TRANSACTION; + } + + // + // If the context does not exist create a new one, else return the error + // status to the caller. + // + + if (status != STATUS_NOT_FOUND) { + + return status; + } + + // + // Allocate a transaction context. + // + + status = FltAllocateContext( FltObjects->Filter, + FLT_TRANSACTION_CONTEXT, + sizeof(MINISPY_TRANSACTION_CONTEXT), + PagedPool, + &transactionContext ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // Set the context into the transaction + // + + RtlZeroMemory(transactionContext, sizeof(MINISPY_TRANSACTION_CONTEXT)); + transactionContext->Count = Sequence++; + + FLT_ASSERT( MiniSpyData.PFltSetTransactionContext ); + + status = (*MiniSpyData.PFltSetTransactionContext)( FltObjects->Instance, + FltObjects->Transaction, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + transactionContext, + &oldTransactionContext ); + + if (!NT_SUCCESS( status )) { + + FltReleaseContext( transactionContext ); //this will free the context + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + return status; + } + + FLT_ASSERT(oldTransactionContext != NULL); + + if (FlagOn(oldTransactionContext->Flags, MINISPY_ENLISTED_IN_TRANSACTION)) { + + // + // If this context is already enlisted then release the reference + // which FltSetTransactionContext put on it and return success. + // + + FltReleaseContext( oldTransactionContext ); + return STATUS_SUCCESS; + } + + // + // If we found an existing transaction then we should try and + // enlist in it. There is a race here in which the thread + // which actually set the transaction context may fail to + // enlist in the transaction and delete it later. It might so + // happen that we picked up a reference to that context here + // and successfully enlisted in that transaction. For now + // we have chosen to ignore this scenario. + // + + // + // If we are not enlisted then assign the right transactionContext + // and attempt enlistment. + // + + transactionContext = oldTransactionContext; + } + +ENLIST_IN_TRANSACTION: + + // + // Enlist on this transaction for notifications. + // + + FLT_ASSERT( MiniSpyData.PFltEnlistInTransaction ); + + status = (*MiniSpyData.PFltEnlistInTransaction)( FltObjects->Instance, + FltObjects->Transaction, + transactionContext, + FLT_MAX_TRANSACTION_NOTIFICATIONS ); + + // + // If the enlistment failed we might have to delete the context and remove + // our count. + // + + if (!NT_SUCCESS( status )) { + + // + // If the error is that we are already enlisted then we do not need + // to delete the context. Otherwise we have to delete the context + // before releasing our reference. + // + + if (status == STATUS_FLT_ALREADY_ENLISTED) { + + status = STATUS_SUCCESS; + + } else { + + // + // It is worth noting that only the first caller of + // FltDeleteContext will remove the reference added by + // filter manager when the context was set. + // + + FltDeleteContext( transactionContext ); + } + + FltReleaseContext( transactionContext ); + return status; + } + + // + // Set the flag so that future enlistment efforts know that we + // successfully enlisted in the transaction. + // + + SetFlagInterlocked( &transactionContext->Flags, MINISPY_ENLISTED_IN_TRANSACTION ); + + // + // The operation succeeded, remove our count + // + + FltReleaseContext( transactionContext ); + + // + // Log a record that a new transaction has started. + // + + recordList = SpyNewRecord(); + + if (recordList) { + + SpyLogTransactionNotify( FltObjects, recordList, 0 ); + + // + // Send the logged information to the user service. + // + + SpyLog( recordList ); + } + +#endif // MINISPY_VISTA + + return STATUS_SUCCESS; +} + + +#if MINISPY_VISTA + +NTSTATUS +SpyKtmNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ) +{ + PRECORD_LIST recordList; + + UNREFERENCED_PARAMETER( TransactionContext ); + + // + // Try and get a log record + // + + recordList = SpyNewRecord(); + + if (recordList) { + + SpyLogTransactionNotify( FltObjects, recordList, TransactionNotification ); + + // + // Send the logged information to the user service. + // + + SpyLog( recordList ); + } + + return STATUS_SUCCESS; +} + +#endif // MINISPY_VISTA + +VOID +SpyDeleteTxfContext ( + _Inout_ PMINISPY_TRANSACTION_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +{ + UNREFERENCED_PARAMETER( Context ); + UNREFERENCED_PARAMETER( ContextType ); + + FLT_ASSERT(FLT_TRANSACTION_CONTEXT == ContextType); + FLT_ASSERT(Context->Count != 0); +} + + +LONG +SpyExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointer, + _In_ BOOLEAN AccessingUserBuffer + ) +/*++ + +Routine Description: + + Exception filter to catch errors touching user buffers. + +Arguments: + + ExceptionPointer - The exception record. + + AccessingUserBuffer - If TRUE, overrides FsRtlIsNtStatusExpected to allow + the caller to munge the error to a desired status. + +Return Value: + + EXCEPTION_EXECUTE_HANDLER - If the exception handler should be run. + + EXCEPTION_CONTINUE_SEARCH - If a higher exception handler should take care of + this exception. + +--*/ +{ + NTSTATUS Status; + + Status = ExceptionPointer->ExceptionRecord->ExceptionCode; + + // + // Certain exceptions shouldn't be dismissed within the namechanger filter + // unless we're touching user memory. + // + + if (!FsRtlIsNtstatusExpected( Status ) && + !AccessingUserBuffer) { + + return EXCEPTION_CONTINUE_SEARCH; + } + + return EXCEPTION_EXECUTE_HANDLER; +} + + diff --git a/filesys/miniFilter/minispy/filter/minispy.rc b/filesys/miniFilter/minispy/filter/minispy.rc new file mode 100644 index 00000000..b303a14b --- /dev/null +++ b/filesys/miniFilter/minispy/filter/minispy.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "MiniSpy Filter Driver" +#define VER_INTERNALNAME_STR "minispy.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/minispy/filter/minispy.vcxproj b/filesys/miniFilter/minispy/filter/minispy.vcxproj new file mode 100644 index 00000000..de6dac81 --- /dev/null +++ b/filesys/miniFilter/minispy/filter/minispy.vcxproj @@ -0,0 +1,198 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39} + $(MSBuildProjectName) + Debug + Win32 + {88181562-E49D-4E01-B470-89CECAA14E2E} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + minispy + + + minispy + + + minispy + + + minispy + + + + %(AdditionalOptions) /map + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalOptions) /map + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalOptions) /map + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalOptions) /map + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters b/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters new file mode 100644 index 00000000..7425e30c --- /dev/null +++ b/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters @@ -0,0 +1,37 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {3973B07F-3667-4849-9D74-E6225E72C259} + + + h;hpp;hxx;hm;inl;inc;xsd + {7BA4662B-3492-4B81-8081-ACA773397EA2} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {7C19E51C-41FA-46DE-9B69-35A11D9989A7} + + + inf;inv;inx;mof;mc; + {E81C4500-8F1F-45E5-92F1-B012E4D9FEB7} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/minispy/filter/mspyKern.h b/filesys/miniFilter/minispy/filter/mspyKern.h new file mode 100644 index 00000000..44bbc881 --- /dev/null +++ b/filesys/miniFilter/minispy/filter/mspyKern.h @@ -0,0 +1,412 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + mspyKern.h + +Abstract: + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. + +Environment: + + Kernel mode + +--*/ +#ifndef __MSPYKERN_H__ +#define __MSPYKERN_H__ + +#include +//#include +#include +#include "minispy.h" + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +// +// Memory allocation tag +// + +#define SPY_TAG 'ypSM' + +// +// Win8 define for support of NPFS/MSFS +// Win7 define for support of new ECPs. +// Vista define for including transaction support, +// older ECPs +// + +#define MINISPY_WIN8 (NTDDI_VERSION >= NTDDI_WIN8) +#define MINISPY_WIN7 (NTDDI_VERSION >= NTDDI_WIN7) +#define MINISPY_VISTA (NTDDI_VERSION >= NTDDI_VISTA) +#define MINISPY_NOT_W2K (OSVER(NTDDI_VERSION) > NTDDI_WIN2K) + +// +// Define callback types for Vista +// + +#if MINISPY_VISTA + +// +// Dynamically imported Filter Mgr APIs +// + +typedef NTSTATUS +(*PFLT_SET_TRANSACTION_CONTEXT)( + _In_ PFLT_INSTANCE Instance, + _In_ PKTRANSACTION Transaction, + _In_ FLT_SET_CONTEXT_OPERATION Operation, + _In_ PFLT_CONTEXT NewContext, + _Outptr_opt_ PFLT_CONTEXT *OldContext + ); + +typedef NTSTATUS +(*PFLT_GET_TRANSACTION_CONTEXT)( + _In_ PFLT_INSTANCE Instance, + _In_ PKTRANSACTION Transaction, + _Outptr_ PFLT_CONTEXT *Context + ); + +typedef NTSTATUS +(*PFLT_ENLIST_IN_TRANSACTION)( + _In_ PFLT_INSTANCE Instance, + _In_ PKTRANSACTION Transaction, + _In_ PFLT_CONTEXT TransactionContext, + _In_ NOTIFICATION_MASK NotificationMask + ); + +// +// Flags for the known ECPs +// + +#define ECP_TYPE_FLAG_PREFETCH 0x00000001 + +#if MINISPY_WIN7 + +#define ECP_TYPE_FLAG_OPLOCK_KEY 0x00000002 +#define ECP_TYPE_FLAG_NFS 0x00000004 +#define ECP_TYPE_FLAG_SRV 0x00000008 + +#endif + +#define ADDRESS_STRING_BUFFER_SIZE 64 + +// +// Enumerate the ECPs MiniSpy supports +// + +typedef enum _ECP_TYPE { + + EcpPrefetchOpen, + EcpOplockKey, + EcpNfsOpen, + EcpSrvOpen, + + NumKnownEcps + +} ECP_TYPE; + +#endif + +//--------------------------------------------------------------------------- +// Global variables +//--------------------------------------------------------------------------- + +typedef struct _MINISPY_DATA { + + // + // The object that identifies this driver. + // + + PDRIVER_OBJECT DriverObject; + + // + // The filter that results from a call to + // FltRegisterFilter. + // + + PFLT_FILTER Filter; + + // + // Server port: user mode connects to this port + // + + PFLT_PORT ServerPort; + + // + // Client connection port: only one connection is allowed at a time., + // + + PFLT_PORT ClientPort; + + // + // List of buffers with data to send to user mode. + // + + KSPIN_LOCK OutputBufferLock; + LIST_ENTRY OutputBufferList; + + // + // Lookaside list used for allocating buffers. + // + + NPAGED_LOOKASIDE_LIST FreeBufferList; + + // + // Variables used to throttle how many records buffer we can use + // + + LONG MaxRecordsToAllocate; + __volatile LONG RecordsAllocated; + + // + // static buffer used for sending an "out-of-memory" message + // to user mode. + // + + __volatile LONG StaticBufferInUse; + + // + // We need to make sure this buffer aligns on a PVOID boundary because + // minispy casts this buffer to a RECORD_LIST structure. + // That can cause alignment faults unless the structure starts on the + // proper PVOID boundary + // + + PVOID OutOfMemoryBuffer[RECORD_SIZE/sizeof( PVOID )]; + + // + // Variable and lock for maintaining LogRecord sequence numbers. + // + + __volatile LONG LogSequenceNumber; + + // + // The name query method to use. By default, it is set to + // FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, but it can be overridden + // by a setting in the registery. + // + + ULONG NameQueryMethod; + + // + // Global debug flags + // + + ULONG DebugFlags; + +#if MINISPY_VISTA + + // + // Dynamically imported Filter Mgr APIs + // + + PFLT_SET_TRANSACTION_CONTEXT PFltSetTransactionContext; + + PFLT_GET_TRANSACTION_CONTEXT PFltGetTransactionContext; + + PFLT_ENLIST_IN_TRANSACTION PFltEnlistInTransaction; + +#endif + +} MINISPY_DATA, *PMINISPY_DATA; + + +// +// Defines the minispy context structure +// + +typedef struct _MINISPY_TRANSACTION_CONTEXT { + ULONG Flags; + ULONG Count; + +}MINISPY_TRANSACTION_CONTEXT, *PMINISPY_TRANSACTION_CONTEXT; + +// +// This macro below is used to set the flags field in minispy's +// MINISPY_TRANSACTION_CONTEXT structure once it has been +// successfully enlisted in the transaction. +// + +#define MINISPY_ENLISTED_IN_TRANSACTION 0x01 + +// +// Minispy's global variables +// + +extern MINISPY_DATA MiniSpyData; + +#define DEFAULT_MAX_RECORDS_TO_ALLOCATE 500 +#define MAX_RECORDS_TO_ALLOCATE L"MaxRecords" + +#define DEFAULT_NAME_QUERY_METHOD FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP +#define NAME_QUERY_METHOD L"NameQueryMethod" + +// +// DebugFlag values +// + +#define SPY_DEBUG_PARSE_NAMES 0x00000001 + +//--------------------------------------------------------------------------- +// Registration structure +//--------------------------------------------------------------------------- + +extern const FLT_REGISTRATION FilterRegistration; + +//--------------------------------------------------------------------------- +// Function prototypes +//--------------------------------------------------------------------------- + +FLT_PREOP_CALLBACK_STATUS +SpyPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +SpyPostOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +NTSTATUS +SpyKtmNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ); + +NTSTATUS +SpyFilterUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +SpyQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +VOID +SpyReadDriverParameters ( + _In_ PUNICODE_STRING RegistryPath + ); + +LONG +SpyExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointer, + _In_ BOOLEAN AccessingUserBuffer + ); + +//--------------------------------------------------------------------------- +// Memory allocation routines +//--------------------------------------------------------------------------- + +PRECORD_LIST +SpyAllocateBuffer ( + _Out_ PULONG RecordType + ); + +VOID +SpyFreeBuffer ( + _In_ PVOID Buffer + ); + +//--------------------------------------------------------------------------- +// Logging routines +//--------------------------------------------------------------------------- +PRECORD_LIST +SpyNewRecord ( + VOID + ); + +VOID +SpyFreeRecord ( + _In_ PRECORD_LIST Record + ); + +#if MINISPY_VISTA + +VOID +SpyParseEcps ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PRECORD_LIST RecordList, + _Inout_ PUNICODE_STRING EcpData + ); + +VOID +SpyBuildEcpDataString ( + _In_ PRECORD_LIST RecordList, + _Inout_ PUNICODE_STRING EcpData, + _In_reads_(NumKnownEcps) PVOID * ContextPointers + ); + +VOID +SpySetRecordNameAndEcpData ( + _Inout_ PLOG_RECORD LogRecord, + _In_ PUNICODE_STRING Name, + _In_opt_ PUNICODE_STRING EcpData + ); + +#else + +VOID +SpySetRecordName ( + _Inout_ PLOG_RECORD LogRecord, + _In_ PUNICODE_STRING Name + ); + +#endif + +VOID +SpyLogPreOperationData ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PRECORD_LIST RecordList + ); + +VOID +SpyLogPostOperationData ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PRECORD_LIST RecordList + ); + +VOID +SpyLogTransactionNotify ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PRECORD_LIST RecordList, + _In_ ULONG TransactionNotification + ); + +VOID +SpyLog ( + _In_ PRECORD_LIST RecordList + ); + +NTSTATUS +SpyGetLog ( + _Out_writes_bytes_to_(OutputBufferLength,*ReturnOutputBufferLength) PUCHAR OutputBuffer, + _In_ ULONG OutputBufferLength, + _Out_ PULONG ReturnOutputBufferLength + ); + +VOID +SpyEmptyOutputBufferList ( + VOID + ); + +VOID +SpyDeleteTxfContext ( + _Inout_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +#endif //__MSPYKERN_H__ + diff --git a/filesys/miniFilter/minispy/filter/mspyLib.c b/filesys/miniFilter/minispy/filter/mspyLib.c new file mode 100644 index 00000000..521e907b --- /dev/null +++ b/filesys/miniFilter/minispy/filter/mspyLib.c @@ -0,0 +1,1541 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + mspyLib.c + +Abstract: + This contains library support routines for MiniSpy + +Environment: + + Kernel mode + +--*/ + +#include +#include + +#include "mspyKern.h" + +// +// Can't pull in wsk.h until after MINISPY_VISTA is defined +// + +#if MINISPY_VISTA +#include +#include +#endif + +//--------------------------------------------------------------------------- +// Assign text sections for each routine. +//--------------------------------------------------------------------------- + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(INIT, SpyReadDriverParameters) +#if MINISPY_VISTA + #pragma alloc_text(PAGE, SpyBuildEcpDataString) + #pragma alloc_text(PAGE, SpyParseEcps) +#endif +#endif + +UCHAR TxNotificationToMinorCode ( + _In_ ULONG TxNotification + ) +/*++ + +Routine Description: + + This routine has been written to convert a transaction notification code + to an Irp minor code. This function is needed because RECORD_DATA has a + UCHAR field for the Irp minor code whereas TxNotification is ULONG. As + of now all this function does is compute log_base_2(TxNotification) + 1. + That fits our need for now but might have to be evolved later. This + function is intricately tied with the enumeration TRANSACTION_NOTIFICATION_CODES + in mspyLog.h and the case statements related to transactions in the function + PrintIrpCode (Minispy\User\mspyLog.c). + +Arguments: + + TxNotification - The transaction notification received. + +Return Value: + + 0 if TxNotification is 0; + log_base_2(TxNotification) + 1 otherwise. + +--*/ +{ + UCHAR count = 0; + + if (TxNotification == 0) + return 0; + + // + // This assert verifies if no more than one flag is set + // in the TxNotification variable. TxNotification flags are + // supposed to be mutually exclusive. The assert below verifies + // if the value of TxNotification is a power of 2. If it is not + // then we will break. + // + + FLT_ASSERT( !(( TxNotification ) & ( TxNotification - 1 )) ); + + while (TxNotification) { + + count++; + + TxNotification >>= 1; + + // + // If we hit this assert then we have more notification codes than + // can fit in a UCHAR. We need to revaluate our approach for + // storing minor codes now. + // + + FLT_ASSERT( count != 0 ); + } + + return ( count ); +} + + +//--------------------------------------------------------------------------- +// Log Record allocation routines +//--------------------------------------------------------------------------- + +PRECORD_LIST +SpyAllocateBuffer ( + _Out_ PULONG RecordType + ) +/*++ + +Routine Description: + + Allocates a new buffer from the MiniSpyData.FreeBufferList if there is + enough memory to do so and we have not exceed our maximum buffer + count. + + NOTE: Because there is no interlock between testing if we have exceeded + the record allocation limit and actually increment the in use + count it is possible to temporarily allocate one or two buffers + more then the limit. Because this is such a rare situation there + is not point to handling this. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level. + +Arguments: + + RecordType - Receives information on what type of record was allocated. + +Return Value: + + Pointer to the allocated buffer, or NULL if the allocation failed. + +--*/ +{ + PVOID newBuffer; + ULONG newRecordType = RECORD_TYPE_NORMAL; + + // + // See if we have room to allocate more buffers + // + + if (MiniSpyData.RecordsAllocated < MiniSpyData.MaxRecordsToAllocate) { + + InterlockedIncrement( &MiniSpyData.RecordsAllocated ); + + newBuffer = ExAllocateFromNPagedLookasideList( &MiniSpyData.FreeBufferList ); + + if (newBuffer == NULL) { + + // + // We failed to allocate the memory. Decrement our global count + // and return what type of memory we have. + // + + InterlockedDecrement( &MiniSpyData.RecordsAllocated ); + + newRecordType = RECORD_TYPE_FLAG_OUT_OF_MEMORY; + } + + } else { + + // + // No more room to allocate memory, return we didn't get a buffer + // and why. + // + + newRecordType = RECORD_TYPE_FLAG_EXCEED_MEMORY_ALLOWANCE; + newBuffer = NULL; + } + + *RecordType = newRecordType; + return newBuffer; +} + + +VOID +SpyFreeBuffer ( + _In_ PVOID Buffer + ) +/*++ + +Routine Description: + + Free an allocate buffer. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level. + +Arguments: + + Buffer - The buffer to free. + +Return Value: + + None. + +--*/ +{ + // + // Free the memory, update the counter + // + + InterlockedDecrement( &MiniSpyData.RecordsAllocated ); + ExFreeToNPagedLookasideList( &MiniSpyData.FreeBufferList, Buffer ); +} + + +//--------------------------------------------------------------------------- +// Logging routines +//--------------------------------------------------------------------------- + +PRECORD_LIST +SpyNewRecord ( + VOID + ) +/*++ + +Routine Description: + + Allocates a new RECORD_LIST structure if there is enough memory to do so. A + sequence number is updated for each request for a new record. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level. + +Arguments: + + None + +Return Value: + + Pointer to the RECORD_LIST allocated, or NULL if no memory is available. + +--*/ +{ + PRECORD_LIST newRecord; + ULONG initialRecordType; + + // + // Allocate the buffer + // + + newRecord = SpyAllocateBuffer( &initialRecordType ); + + if (newRecord == NULL) { + + // + // We could not allocate a record, see if the static buffer is + // in use. If not, we will use it + // + + if (!InterlockedExchange( &MiniSpyData.StaticBufferInUse, TRUE )) { + + newRecord = (PRECORD_LIST)MiniSpyData.OutOfMemoryBuffer; + initialRecordType |= RECORD_TYPE_FLAG_STATIC; + } + } + + // + // If we got a record (doesn't matter if it is static or not), init it + // + + if (newRecord != NULL) { + + // + // Init the new record + // + + newRecord->LogRecord.RecordType = initialRecordType; + newRecord->LogRecord.Length = sizeof(LOG_RECORD); + newRecord->LogRecord.SequenceNumber = InterlockedIncrement( &MiniSpyData.LogSequenceNumber ); + RtlZeroMemory( &newRecord->LogRecord.Data, sizeof( RECORD_DATA ) ); + } + + return( newRecord ); +} + + +VOID +SpyFreeRecord ( + _In_ PRECORD_LIST Record + ) +/*++ + +Routine Description: + + Free the given buffer + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level. + +Arguments: + + Record - the buffer to free + +Return Value: + + None. + +--*/ +{ + if (FlagOn(Record->LogRecord.RecordType,RECORD_TYPE_FLAG_STATIC)) { + + // + // This was our static buffer, mark it available. + // + + FLT_ASSERT(MiniSpyData.StaticBufferInUse); + MiniSpyData.StaticBufferInUse = FALSE; + + } else { + + SpyFreeBuffer( Record ); + } +} + +#if MINISPY_VISTA + +VOID +SpyBuildEcpDataString ( + _In_ PRECORD_LIST RecordList, + _Inout_ PUNICODE_STRING EcpData, + _In_reads_(NumKnownEcps) PVOID * ContextPointers + ) +/*++ + +Routine Description: + + Given the ECP presence data and context pointers located in SpyParseEcps, + uses _snwprintf to write a human-readable log output to a string provided. + +Arguments: + + RecordList - Pointer to the record, so we can see ECP count and masking + + EcpData - Pointer to string to receive formatted ECP log + + ContextPointers - Pointer to array of pointers, each of which is either NULL + or a context structure specific to a given type of ECP + +Return Value: + + None. + +--*/ +{ + ULONG knownCount = 0; + SHORT wcharsCopied = 0; + PRECORD_DATA recordData = &RecordList->LogRecord.Data; + PWCHAR printPointer = EcpData->Buffer; + +#if MINISPY_WIN7 + TCHAR addressBuffer[ADDRESS_STRING_BUFFER_SIZE]; + ULONG addressBufferLen; + LONG addressConvStatus; +#endif + + PAGED_CODE(); + + FLT_ASSERT(NULL != ContextPointers); + + // + // Print initial ECP text + // + // NOTE: We don't check the return value of _snwprintf until the very end + // of this function. Because of this, if we run out of buffer space before we + // have printed all our information, we keep calling _snwprintf, although it + // does nothing. This is deliberate in the interest of keeping the code + // somewhat clean. + // + + #pragma prefast(push) + #pragma prefast(disable: __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY __WARNING_BANNED_API_USAGE, "reviewed and safe usage") + // Prefast complains here because _snwprintf has some oddities. + // We've code reviewed to ensure safe usage. + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"[%d ECPs:", + recordData->EcpCount ); + + // + // Next, check all the known ECPs against the mask that was set in SpyParseEcps. + // If we recognize any of the ECPs, add their data to the log string. + // + +#if MINISPY_WIN7 + + // + // Oplock key ECP + // + + if (FlagOn( recordData->KnownEcpMask, ECP_TYPE_FLAG_OPLOCK_KEY )) { + + POPLOCK_KEY_ECP_CONTEXT oplockEcpContext = NULL; + LPGUID oplockKeyGuid; + UNICODE_STRING oplockKeyGuidString; + + knownCount++; + + // + // We now know this context pointer points to a + // OPLOCK_KEY_ECP_CONTEXT structure + // + + oplockEcpContext = (POPLOCK_KEY_ECP_CONTEXT) ContextPointers[EcpOplockKey]; + + FLT_ASSERT(NULL != oplockEcpContext); + + oplockKeyGuid = &oplockEcpContext->OplockKey; + + if (NT_SUCCESS(RtlStringFromGUID( oplockKeyGuid, + &oplockKeyGuidString ))) { + + // + // Format an output string to display the key in GUID form + // + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s OPLOCK KEY: %wZ;", + printPointer, + &oplockKeyGuidString ); + + RtlFreeUnicodeString( &oplockKeyGuidString ); + + } else { + + // + // Error processing the GUID + // + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s INVALID OPLOCK KEY;", + printPointer ); + } + } + + // + // NFS ECP + // + + if (FlagOn( recordData->KnownEcpMask, ECP_TYPE_FLAG_NFS )) { + + PNFS_OPEN_ECP_CONTEXT nfsEcpContext = NULL; + PUNICODE_STRING nfsShareNameString; + PSOCKADDR_STORAGE_NFS nfsClientSocketAddr; + + knownCount++; + + // + // We now know this context pointer points to a + // NFS_OPEN_ECP_CONTEXT structure + // + + nfsEcpContext= (PNFS_OPEN_ECP_CONTEXT) ContextPointers[EcpNfsOpen]; + + FLT_ASSERT(NULL != nfsEcpContext); + + nfsShareNameString = nfsEcpContext->ExportAlias; + nfsClientSocketAddr = nfsEcpContext->ClientSocketAddress; + + // + // Print the share name, if the string (optional) is present + // + + if (nfsShareNameString) { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s NFS SHARE NAME: %wZ,", + printPointer, + nfsShareNameString ); + } + + FLT_ASSERT(nfsClientSocketAddr != NULL); + + addressConvStatus = STATUS_INVALID_PARAMETER; + addressBufferLen = ADDRESS_STRING_BUFFER_SIZE; + + if (nfsClientSocketAddr->ss_family == AF_INET) { + + PSOCKADDR_IN ipv4SocketAddr = (PSOCKADDR_IN) nfsClientSocketAddr; + + // + // Format IPv4 address and port + // + + addressConvStatus = RtlIpv4AddressToStringEx( + &ipv4SocketAddr->sin_addr, + ipv4SocketAddr->sin_port, + addressBuffer, + &addressBufferLen ); + + } else if (nfsClientSocketAddr->ss_family == AF_INET6) { + + PSOCKADDR_IN6 ipv6SocketAddr = (PSOCKADDR_IN6) nfsClientSocketAddr; + + // + // Format IPv6 address and port + // + + addressConvStatus = RtlIpv6AddressToStringEx( + &ipv6SocketAddr->sin6_addr, + 0, + ipv6SocketAddr->sin6_port, + addressBuffer, + &addressBufferLen ); + } + + // + // Print the address (and port) + // + + if ((STATUS_INVALID_PARAMETER != addressConvStatus) && + (0 < addressBufferLen)) { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s NFS SOCKET ADDR: %S;", + printPointer, + addressBuffer ); + + } else { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s NFS INVALID SOCKET ADDR;", + printPointer ); + } + } + + // + // SRV ECP + // + + if (FlagOn( recordData->KnownEcpMask, ECP_TYPE_FLAG_SRV )) { + + PSRV_OPEN_ECP_CONTEXT srvEcpContext = NULL; + PUNICODE_STRING srvShareNameString; + PSOCKADDR_STORAGE_NFS srvClientSocketAddr; + + knownCount++; + + // + // We now know this context pointer points to a + // SRV_OPEN_ECP_CONTEXT structure + // + + srvEcpContext= (PSRV_OPEN_ECP_CONTEXT) ContextPointers[EcpSrvOpen]; + + FLT_ASSERT(NULL != srvEcpContext); + + srvShareNameString = srvEcpContext->ShareName; + srvClientSocketAddr = srvEcpContext->SocketAddress; + + // + // Print the share name, if the string is present + // + + if (srvShareNameString) { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s SRV SHARE NAME: %wZ,", + printPointer, + srvShareNameString ); + } + + FLT_ASSERT(srvClientSocketAddr != NULL); + + addressConvStatus = STATUS_INVALID_PARAMETER; + addressBufferLen = ADDRESS_STRING_BUFFER_SIZE; + + // + // Print the address, whether it's IPv4 or IPv6 + // + + if (srvClientSocketAddr->ss_family == AF_INET) { + + PSOCKADDR_IN ipv4SocketAddr = (PSOCKADDR_IN) srvClientSocketAddr; + + // + // Format IPv4 address and port + // + + addressConvStatus = RtlIpv4AddressToStringEx( + &ipv4SocketAddr->sin_addr, + ipv4SocketAddr->sin_port, + addressBuffer, + &addressBufferLen ); + + } else if (srvClientSocketAddr->ss_family == AF_INET6) { + + PSOCKADDR_IN6 ipv6SocketAddr = (PSOCKADDR_IN6) srvClientSocketAddr; + + // + // Format IPv6 address and port + // + + addressConvStatus = RtlIpv6AddressToStringEx( + &ipv6SocketAddr->sin6_addr, + 0, + ipv6SocketAddr->sin6_port, + addressBuffer, + &addressBufferLen ); + } + + if ((STATUS_INVALID_PARAMETER != addressConvStatus) && + (0 < addressBufferLen)) { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s SRV SOCKET ADDR: %S;", + printPointer, + addressBuffer ); + + } else { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s SRV INVALID SOCKET ADDR;", + printPointer ); + } + + // + // Print SRV flags + // + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s SRV FLAGS: %s%s%s;", + printPointer, + (srvEcpContext->OplockBlockState) ? L"B" : L"-", + (srvEcpContext->OplockAppState) ? L"A" : L"-", + (srvEcpContext->OplockFinalState) ? L"F" : L"-" ); + } + +#else + UNREFERENCED_PARAMETER( ContextPointers ); +#endif + + // + // Prefetch ECP + // + + if (FlagOn( recordData->KnownEcpMask, ECP_TYPE_FLAG_PREFETCH )) { + + knownCount++; + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s PREFETCH;", + printPointer ); + } + + // + // Print closing ECP text + // + + if (knownCount < recordData->EcpCount) { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s %d unknown ECPs]", + printPointer, + recordData->EcpCount - knownCount ); + + } else { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s]", + printPointer ); + } + + // + // If wcharsCopied is negative, it means we maxed out our buffer + // and exited early. Otherwise, the length is the maximum space + // minus the leftover buffer space + // + + if (wcharsCopied >= 0) { + + EcpData->Length = wcharsCopied * sizeof(WCHAR); + + } else { + + // + // There wasn't enough buffer space, so manually truncate in a NULL + // + + EcpData->Length = MAX_NAME_SPACE_LESS_NULL; + EcpData->Buffer[MAX_NAME_WCHARS_LESS_NULL] = UNICODE_NULL; + } + + #pragma prefast(pop) +} + +VOID +SpyParseEcps ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PRECORD_LIST RecordList, + _Inout_ PUNICODE_STRING EcpData + ) + /*++ + +Routine Description: + + Extracts ECPs from the given callback data and logs them, + then calls SpyBuildEcpDataString to write a MiniSpy-specific + ECP log string. + +Arguments: + + Data - The Data structure that contains the information we want to record. + + RecordList - Pointer to the record, so we can set ECP count and masking + + EcpData - Pointer to string to receive formatted ECP log + +Return Value: + + None. + +--*/ +{ + NTSTATUS status; + PECP_LIST ecpList; + PRECORD_DATA recordData = &RecordList->LogRecord.Data; + PVOID ecpContext = NULL; + GUID ecpGuid = {0}; + ULONG ecpContextSize = 0; + ULONG ecpFlag; + PVOID contextPointers[NumKnownEcps]; + UCHAR offset = 0; + + PAGED_CODE(); + + RtlZeroMemory( contextPointers, sizeof(PVOID) * NumKnownEcps ); + + // + // Try to get an ECP list pointer from filter manager + // + + status = FltGetEcpListFromCallbackData( MiniSpyData.Filter, + Data, + &ecpList ); + + // + // Even if the operation was successful, ecpList may be NULL + // if there are no ECPs attached to this operation, so we must + // make both checks + // + + if (NT_SUCCESS(status) && (NULL != ecpList)) { + + // + // Now ask filter manager for each ECP + // + + while (NT_SUCCESS( + FltGetNextExtraCreateParameter( MiniSpyData.Filter, + ecpList, + ecpContext, + (LPGUID) &ecpGuid, + &ecpContext, + &ecpContextSize ))) { + + // + // At this point, we have all the information we should need for a given + // ECP, but processing of ECPs is contingent on knowledge of their + // specific context structure. From here, ECP processing is driver-specific. + // + + // + // MiniSpy supports several system-defined ECPs. What follows is + // MiniSpy-specific code to log any known ECPs and produce some + // meaningful output for the user + // + + ecpFlag = 0; + + if (IsEqualGUID( &GUID_ECP_PREFETCH_OPEN, &ecpGuid )) { + + // + // Prefetch ECP + // + + ecpFlag = ECP_TYPE_FLAG_PREFETCH; + offset = EcpPrefetchOpen; + } + +#if MINISPY_WIN7 + + // + // There are three system-defined ECPs that are only available + // as of Windows 7 + // + else if (IsEqualGUID( &GUID_ECP_OPLOCK_KEY, &ecpGuid )) { + + // + // Oplock key ECP + // + + ecpFlag = ECP_TYPE_FLAG_OPLOCK_KEY; + offset = EcpOplockKey; + + } else if (IsEqualGUID( &GUID_ECP_NFS_OPEN, &ecpGuid )) { + + // + // NFS open ECP + // + + ecpFlag = ECP_TYPE_FLAG_NFS; + offset = EcpNfsOpen; + + } else if (IsEqualGUID( &GUID_ECP_SRV_OPEN, &ecpGuid )) { + + // + // SRV ECP + // + + ecpFlag = ECP_TYPE_FLAG_SRV; + offset = EcpSrvOpen; + } + +#endif + + // + // We don't accept user mode ECPs because of the potential + // for bad buffers + // + + if ((0 != ecpFlag) && + !FltIsEcpFromUserMode( MiniSpyData.Filter, ecpContext )) { + + // + // If ecpFlag was set, we found a MiniSpy-supported ECP. + // Make sure we have not already found an ECP of this type + // for this particular operation + // + + FLT_ASSERT(!FlagOn( recordData->KnownEcpMask, ecpFlag )); + + // + // Set the flag to indicate a given type of ECP was found + // + + recordData->KnownEcpMask |= ecpFlag; + + // + // Save the context pointer so we can get detailed data later + // + + contextPointers[offset] = ecpContext; + } + + // + // Increment the number of total ECPs (counting both known and unknown) + // + + recordData->EcpCount++; + } + + // + // Call the Minispy-specific function to format the ECP data string for + // output + // + + if (0 < recordData->EcpCount) { + + SpyBuildEcpDataString( RecordList, EcpData, contextPointers ); + } + } +} + +VOID +SpySetRecordNameAndEcpData( + _Inout_ PLOG_RECORD LogRecord, + _In_ PUNICODE_STRING Name, + _In_opt_ PUNICODE_STRING EcpData + ) +/*++ + +Routine Description: + + Sets the given file name in the LogRecord. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path. + +Arguments: + + LogRecord - The record in which to set the name. + + Name - The name to insert + + EcpData - A string of variable-length ECP data to insert + +Return Value: + + None. + +--*/ +{ + + PWCHAR printPointer = (PWCHAR)LogRecord->Name; + SHORT wcharsCopied; + USHORT stringLength; + + FLT_ASSERT(NULL != Name); + + // + // Put as much of the two strings as possible into the final buffer, + // name first, followed by ECP information (if any) + // + + if (NULL != EcpData) { + + #pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%wZ %wZ", + Name, + EcpData ); + + } else { + + #pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%wZ", + Name ); + } + + if (wcharsCopied >= 0) { + + stringLength = wcharsCopied * sizeof(WCHAR); + + } else { + + // + // There wasn't enough buffer space, so manually truncate in a NULL + // because we can't trust _snwprintf to do so in that case. + // + + stringLength = MAX_NAME_SPACE_LESS_NULL; + printPointer[MAX_NAME_WCHARS_LESS_NULL] = UNICODE_NULL; + } + + // + // We will always round up log-record length to sizeof(PVOID) so that + // the next log record starts on the right PVOID boundary to prevent + // IA64 alignment faults. The length of the record of course + // includes the additional NULL at the end. + // + + LogRecord->Length = ROUND_TO_SIZE( (LogRecord->Length + + stringLength + + sizeof( UNICODE_NULL )), + sizeof( PVOID ) ); + + FLT_ASSERT(LogRecord->Length <= MAX_LOG_RECORD_LENGTH); +} + +#else + +VOID +SpySetRecordName( + _Inout_ PLOG_RECORD LogRecord, + _In_ PUNICODE_STRING Name + ) +/*++ + +Routine Description: + + Sets the given file name in the LogRecord. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path. + +Arguments: + + LogRecord - The record in which to set the name. + + Name - The name to insert + +Return Value: + + None. + +--*/ +{ + + PWCHAR printPointer = (PWCHAR)LogRecord->Name; + SHORT wcharsCopied; + USHORT stringLength; + + FLT_ASSERT(NULL != Name); + + #pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%wZ", + Name ); + + if (wcharsCopied >= 0) { + + stringLength = wcharsCopied * sizeof(WCHAR); + + } else { + + // + // There wasn't enough buffer space, so manually truncate in a NULL + // because we can't trust _snwprintf to do so in that case. + // + + stringLength = MAX_NAME_SPACE_LESS_NULL; + printPointer[MAX_NAME_WCHARS_LESS_NULL] = UNICODE_NULL; + } + + // + // We will always round up log-record length to sizeof(PVOID) so that + // the next log record starts on the right PVOID boundary to prevent + // IA64 alignment faults. The length of the record of course + // includes the additional NULL at the end. + // + + LogRecord->Length = ROUND_TO_SIZE( (LogRecord->Length + + stringLength + + sizeof( UNICODE_NULL )), + sizeof( PVOID ) ); + + FLT_ASSERT(LogRecord->Length <= MAX_LOG_RECORD_LENGTH); +} + +#endif + +VOID +SpyLogPreOperationData ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PRECORD_LIST RecordList + ) +/*++ + +Routine Description: + + This is called from the pre-operation callback routine to copy the + necessary information into the log record. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path. + +Arguments: + + Data - The Data structure that contains the information we want to record. + + FltObjects - Pointer to the io objects involved in this operation. + + RecordList - Where we want to save the data + +Return Value: + + None. + +--*/ +{ + PRECORD_DATA recordData = &RecordList->LogRecord.Data; + PDEVICE_OBJECT devObj; + NTSTATUS status; + + status = FltGetDeviceObject(FltObjects->Volume,&devObj); + if (NT_SUCCESS(status)) { + + ObDereferenceObject(devObj); + + } else { + + devObj = NULL; + } + + // + // Save the information we want + // + + recordData->CallbackMajorId = Data->Iopb->MajorFunction; + recordData->CallbackMinorId = Data->Iopb->MinorFunction; + recordData->IrpFlags = Data->Iopb->IrpFlags; + recordData->Flags = Data->Flags; + + recordData->DeviceObject = (FILE_ID)devObj; + recordData->FileObject = (FILE_ID)FltObjects->FileObject; + recordData->Transaction = (FILE_ID)FltObjects->Transaction; + recordData->ProcessId = (FILE_ID)PsGetCurrentProcessId(); + recordData->ThreadId = (FILE_ID)PsGetCurrentThreadId(); + + recordData->Arg1 = Data->Iopb->Parameters.Others.Argument1; + recordData->Arg2 = Data->Iopb->Parameters.Others.Argument2; + recordData->Arg3 = Data->Iopb->Parameters.Others.Argument3; + recordData->Arg4 = Data->Iopb->Parameters.Others.Argument4; + recordData->Arg5 = Data->Iopb->Parameters.Others.Argument5; + recordData->Arg6.QuadPart = Data->Iopb->Parameters.Others.Argument6.QuadPart; + + KeQuerySystemTime( &recordData->OriginatingTime ); +} + + +VOID +SpyLogPostOperationData ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PRECORD_LIST RecordList + ) +/*++ + +Routine Description: + + This is called from the post-operation callback routine to copy the + necessary information into the log record. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level. + +Arguments: + + Data - The Data structure that contains the information we want to record. + + RecordList - Where we want to save the data + +Return Value: + + None. + +--*/ +{ + PRECORD_DATA recordData = &RecordList->LogRecord.Data; + + recordData->Status = Data->IoStatus.Status; + recordData->Information = Data->IoStatus.Information; + KeQuerySystemTime( &recordData->CompletionTime ); +} + + +VOID +SpyLogTransactionNotify ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PRECORD_LIST RecordList, + _In_ ULONG TransactionNotification + ) +/*++ + +Routine Description: + + This routine logs the transaction notification. + +Arguments: + + FltObjects - Pointer to the io objects involved in this operation. + + RecordList - Where we want to save the data + + TransactionNotification - Notification for this transaction. + +Return Value: + + None. + +--*/ +{ + PRECORD_DATA recordData = &RecordList->LogRecord.Data; + PDEVICE_OBJECT devObj; + NTSTATUS status; + + status = FltGetDeviceObject(FltObjects->Volume,&devObj); + if (NT_SUCCESS(status)) { + + ObDereferenceObject(devObj); + + } else { + + devObj = NULL; + } + + + recordData->CallbackMajorId = IRP_MJ_TRANSACTION_NOTIFY; + recordData->CallbackMinorId = TxNotificationToMinorCode(TransactionNotification); + + recordData->DeviceObject = (FILE_ID)devObj; + recordData->FileObject = (FILE_ID)FltObjects->FileObject; + recordData->Transaction = (FILE_ID)FltObjects->Transaction; + recordData->ProcessId = (FILE_ID)PsGetCurrentProcessId(); + recordData->ThreadId = (FILE_ID)PsGetCurrentThreadId(); + + KeQuerySystemTime( &recordData->OriginatingTime ); +} + + +VOID +SpyLog ( + _In_ PRECORD_LIST RecordList + ) +/*++ + +Routine Description: + + This routine inserts the given log record into the list to be sent + to the user mode application. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level and uses a spin-lock + +Arguments: + + RecordList - The record to append to the MiniSpyData.OutputBufferList + +Return Value: + + The function returns STATUS_SUCCESS. + + + +--*/ +{ + KIRQL oldIrql; + + KeAcquireSpinLock(&MiniSpyData.OutputBufferLock, &oldIrql); + InsertTailList(&MiniSpyData.OutputBufferList, &RecordList->List); + KeReleaseSpinLock(&MiniSpyData.OutputBufferLock, oldIrql); +} + + +NTSTATUS +SpyGetLog ( + _Out_writes_bytes_to_(OutputBufferLength,*ReturnOutputBufferLength) PUCHAR OutputBuffer, + _In_ ULONG OutputBufferLength, + _Out_ PULONG ReturnOutputBufferLength + ) +/*++ + +Routine Description: + This function fills OutputBuffer with as many LOG_RECORDs as possible. + The LOG_RECORDs are variable sizes and are tightly packed in the + OutputBuffer. + + NOTE: This code must be NON-PAGED because it uses a spin-lock. + +Arguments: + OutputBuffer - The user's buffer to fill with the log data we have + collected + + OutputBufferLength - The size in bytes of OutputBuffer + + ReturnOutputBufferLength - The amount of data actually written into the + OutputBuffer. + +Return Value: + STATUS_SUCCESS if some records were able to be written to the OutputBuffer. + + STATUS_NO_MORE_ENTRIES if we have no data to return. + + STATUS_BUFFER_TOO_SMALL if the OutputBuffer is too small to + hold even one record and we have data to return. + +--*/ +{ + PLIST_ENTRY pList; + ULONG bytesWritten = 0; + PLOG_RECORD pLogRecord; + NTSTATUS status = STATUS_NO_MORE_ENTRIES; + PRECORD_LIST pRecordList; + KIRQL oldIrql; + BOOLEAN recordsAvailable = FALSE; + + KeAcquireSpinLock( &MiniSpyData.OutputBufferLock, &oldIrql ); + + while (!IsListEmpty( &MiniSpyData.OutputBufferList ) && (OutputBufferLength > 0)) { + + // + // Mark we have records + // + + recordsAvailable = TRUE; + + // + // Get the next available record + // + + pList = RemoveHeadList( &MiniSpyData.OutputBufferList ); + + pRecordList = CONTAINING_RECORD( pList, RECORD_LIST, List ); + + pLogRecord = &pRecordList->LogRecord; + + // + // If no filename was set then make it into a NULL file name. + // + + if (REMAINING_NAME_SPACE( pLogRecord ) == MAX_NAME_SPACE) { + + // + // We don't have a name, so return an empty string. + // We have to always start a new log record on a PVOID aligned boundary. + // + + pLogRecord->Length += ROUND_TO_SIZE( sizeof( UNICODE_NULL ), sizeof( PVOID ) ); + pLogRecord->Name[0] = UNICODE_NULL; + } + + // + // Put it back if we've run out of room. + // + + if (OutputBufferLength < pLogRecord->Length) { + + InsertHeadList( &MiniSpyData.OutputBufferList, pList ); + break; + } + + KeReleaseSpinLock( &MiniSpyData.OutputBufferLock, oldIrql ); + + // + // The lock is released, return the data, adjust pointers. + // Protect access to raw user-mode OutputBuffer with an exception handler + // + + try { + RtlCopyMemory( OutputBuffer, pLogRecord, pLogRecord->Length ); + } except (SpyExceptionFilter( GetExceptionInformation(), TRUE )) { + + // + // Put the record back in + // + + KeAcquireSpinLock( &MiniSpyData.OutputBufferLock, &oldIrql ); + InsertHeadList( &MiniSpyData.OutputBufferList, pList ); + KeReleaseSpinLock( &MiniSpyData.OutputBufferLock, oldIrql ); + + return GetExceptionCode(); + + } + + bytesWritten += pLogRecord->Length; + + OutputBufferLength -= pLogRecord->Length; + + OutputBuffer += pLogRecord->Length; + + SpyFreeRecord( pRecordList ); + + // + // Relock the list + // + + KeAcquireSpinLock( &MiniSpyData.OutputBufferLock, &oldIrql ); + } + + KeReleaseSpinLock( &MiniSpyData.OutputBufferLock, oldIrql ); + + // + // Set proper status + // + + if ((bytesWritten == 0) && recordsAvailable) { + + // + // There were records to be sent up but + // there was not enough room in the buffer. + // + + status = STATUS_BUFFER_TOO_SMALL; + + } else if (bytesWritten > 0) { + + // + // We were able to write some data to the output buffer, + // so this was a success. + // + + status = STATUS_SUCCESS; + } + + *ReturnOutputBufferLength = bytesWritten; + + return status; +} + + +VOID +SpyEmptyOutputBufferList ( + VOID + ) +/*++ + +Routine Description: + + This routine frees all the remaining log records in the OutputBufferList + that are not going to get sent up to the user mode application since + MiniSpy is shutting down. + + NOTE: This code must be NON-PAGED because it uses a spin-lock + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + PLIST_ENTRY pList; + PRECORD_LIST pRecordList; + KIRQL oldIrql; + + KeAcquireSpinLock( &MiniSpyData.OutputBufferLock, &oldIrql ); + + while (!IsListEmpty( &MiniSpyData.OutputBufferList )) { + + pList = RemoveHeadList( &MiniSpyData.OutputBufferList ); + KeReleaseSpinLock( &MiniSpyData.OutputBufferLock, oldIrql ); + + pRecordList = CONTAINING_RECORD( pList, RECORD_LIST, List ); + + SpyFreeRecord( pRecordList ); + + KeAcquireSpinLock( &MiniSpyData.OutputBufferLock, &oldIrql ); + } + + KeReleaseSpinLock( &MiniSpyData.OutputBufferLock, oldIrql ); +} + +//--------------------------------------------------------------------------- +// Logging routines +//--------------------------------------------------------------------------- + +VOID +SpyReadDriverParameters ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to read the MiniSpy-specific parameters from + the registry. These values will be found in the registry location + indicated by the RegistryPath passed in. + + This processes the following registry keys: + hklm\system\CurrentControlSet\Services\Minispy\MaxRecords + hklm\system\CurrentControlSet\Services\Minispy\NameQueryMethod + + +Arguments: + + RegistryPath - the path key which contains the values that are + the MiniSpy parameters + +Return Value: + + None. + +--*/ +{ + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey; + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING valueName; + PKEY_VALUE_PARTIAL_INFORMATION pValuePartialInfo; + UCHAR buffer[sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + sizeof( LONG )]; + + // + // Open the registry + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (!NT_SUCCESS( status )) { + + return; + } + + // + // Read the MaxRecordsToAllocate entry from the registry + // + + RtlInitUnicodeString( &valueName, MAX_RECORDS_TO_ALLOCATE ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + pValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION) buffer; + FLT_ASSERT( pValuePartialInfo->Type == REG_DWORD ); + MiniSpyData.MaxRecordsToAllocate = *((PLONG)&(pValuePartialInfo->Data)); + } + + // + // Read the NameQueryMethod entry from the registry + // + + RtlInitUnicodeString( &valueName, NAME_QUERY_METHOD ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + pValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION) buffer; + FLT_ASSERT( pValuePartialInfo->Type == REG_DWORD ); + MiniSpyData.NameQueryMethod = *((PLONG)&(pValuePartialInfo->Data)); + } + + ZwClose(driverRegKey); +} + diff --git a/filesys/miniFilter/minispy/inc/minispy.h b/filesys/miniFilter/minispy/inc/minispy.h new file mode 100644 index 00000000..a6043e62 --- /dev/null +++ b/filesys/miniFilter/minispy/inc/minispy.h @@ -0,0 +1,251 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + minispy.h + +Abstract: + + Header file which contains the structures, type definitions, + and constants that are shared between the kernel mode driver, + minispy.sys, and the user mode executable, minispy.exe. + +Environment: + + Kernel and user mode + +--*/ +#ifndef __MINISPY_H__ +#define __MINISPY_H__ + + +// +// FltMgr's IRP major codes +// + +#define IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION ((UCHAR)-1) +#define IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION ((UCHAR)-2) +#define IRP_MJ_ACQUIRE_FOR_MOD_WRITE ((UCHAR)-3) +#define IRP_MJ_RELEASE_FOR_MOD_WRITE ((UCHAR)-4) +#define IRP_MJ_ACQUIRE_FOR_CC_FLUSH ((UCHAR)-5) +#define IRP_MJ_RELEASE_FOR_CC_FLUSH ((UCHAR)-6) +#define IRP_MJ_NOTIFY_STREAM_FO_CREATION ((UCHAR)-7) + +#define IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE ((UCHAR)-13) +#define IRP_MJ_NETWORK_QUERY_OPEN ((UCHAR)-14) +#define IRP_MJ_MDL_READ ((UCHAR)-15) +#define IRP_MJ_MDL_READ_COMPLETE ((UCHAR)-16) +#define IRP_MJ_PREPARE_MDL_WRITE ((UCHAR)-17) +#define IRP_MJ_MDL_WRITE_COMPLETE ((UCHAR)-18) +#define IRP_MJ_VOLUME_MOUNT ((UCHAR)-19) +#define IRP_MJ_VOLUME_DISMOUNT ((UCHAR)-20) + +// +// My own definition for transaction notify command +// + +#define IRP_MJ_TRANSACTION_NOTIFY ((UCHAR)-40) + + +// +// Version definition +// + +#define MINISPY_MAJ_VERSION 2 +#define MINISPY_MIN_VERSION 0 + +typedef struct _MINISPYVER { + + USHORT Major; + USHORT Minor; + +} MINISPYVER, *PMINISPYVER; + +// +// Name of minispy's communication server port +// + +#define MINISPY_PORT_NAME L"\\MiniSpyPort" + +// +// Local definitions for passing parameters between the filter and user mode +// + +typedef ULONG_PTR FILE_ID; +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; + +// +// The maximum size of a record that can be passed from the filter +// + +#define RECORD_SIZE 1024 + +// +// This defines the type of record buffer this is along with certain flags. +// + +#define RECORD_TYPE_NORMAL 0x00000000 +#define RECORD_TYPE_FILETAG 0x00000004 + +#define RECORD_TYPE_FLAG_STATIC 0x80000000 +#define RECORD_TYPE_FLAG_EXCEED_MEMORY_ALLOWANCE 0x20000000 +#define RECORD_TYPE_FLAG_OUT_OF_MEMORY 0x10000000 +#define RECORD_TYPE_FLAG_MASK 0xffff0000 + +// +// The fixed data received for RECORD_TYPE_NORMAL +// + +typedef struct _RECORD_DATA { + + LARGE_INTEGER OriginatingTime; + LARGE_INTEGER CompletionTime; + + FILE_ID DeviceObject; + FILE_ID FileObject; + FILE_ID Transaction; + + FILE_ID ProcessId; + FILE_ID ThreadId; + + ULONG_PTR Information; + + NTSTATUS Status; + + ULONG IrpFlags; + ULONG Flags; + + UCHAR CallbackMajorId; + UCHAR CallbackMinorId; + UCHAR Reserved[2]; // Alignment on IA64 + + PVOID Arg1; + PVOID Arg2; + PVOID Arg3; + PVOID Arg4; + PVOID Arg5; + LARGE_INTEGER Arg6; + + ULONG EcpCount; + ULONG KnownEcpMask; + +} RECORD_DATA, *PRECORD_DATA; + +// +// What information we actually log. +// + +#pragma warning(push) +#pragma warning(disable:4200) // disable warnings for structures with zero length arrays. + +typedef struct _LOG_RECORD { + + + ULONG Length; // Length of log record. This Does not include + ULONG SequenceNumber; // space used by other members of RECORD_LIST + + ULONG RecordType; // The type of log record this is. + ULONG Reserved; // For alignment on IA64 + + RECORD_DATA Data; + WCHAR Name[]; // This is a null terminated string + +} LOG_RECORD, *PLOG_RECORD; + +#pragma warning(pop) + +// +// How the mini-filter manages the log records. +// + +typedef struct _RECORD_LIST { + + LIST_ENTRY List; + + // + // Must always be last item. See MAX_LOG_RECORD_LENGTH macro below. + // Must be aligned on PVOID boundary in this structure. This is because the + // log records are going to be packed one after another & accessed directly + // Size of log record must also be multiple of PVOID size to avoid alignment + // faults while accessing the log records on IA64 + // + + LOG_RECORD LogRecord; + +} RECORD_LIST, *PRECORD_LIST; + +// +// Defines the commands between the utility and the filter +// + +typedef enum _MINISPY_COMMAND { + + GetMiniSpyLog, + GetMiniSpyVersion + +} MINISPY_COMMAND; + +// +// Defines the command structure between the utility and the filter. +// + +#pragma warning(push) +#pragma warning(disable:4200) // disable warnings for structures with zero length arrays. + +typedef struct _COMMAND_MESSAGE { + MINISPY_COMMAND Command; + ULONG Reserved; // Alignment on IA64 + UCHAR Data[]; +} COMMAND_MESSAGE, *PCOMMAND_MESSAGE; + +#pragma warning(pop) + +// +// The maximum number of BYTES that can be used to store the file name in the +// RECORD_LIST structure +// + +#define MAX_NAME_SPACE ROUND_TO_SIZE( (RECORD_SIZE - sizeof(RECORD_LIST)), sizeof( PVOID )) + +// +// The maximum space, in bytes and WCHARs, available for the name (and ECP +// if present) string, not including the space that must be reserved for a NULL +// + +#define MAX_NAME_SPACE_LESS_NULL (MAX_NAME_SPACE - sizeof(UNICODE_NULL)) +#define MAX_NAME_WCHARS_LESS_NULL MAX_NAME_SPACE_LESS_NULL / sizeof(WCHAR) + +// +// Returns the number of BYTES unused in the RECORD_LIST structure. Note that +// LogRecord->Length already contains the size of LOG_RECORD which is why we +// have to remove it. +// + +#define REMAINING_NAME_SPACE(LogRecord) \ + (FLT_ASSERT((LogRecord)->Length >= sizeof(LOG_RECORD)), \ + (USHORT)(MAX_NAME_SPACE - ((LogRecord)->Length - sizeof(LOG_RECORD)))) + +#define MAX_LOG_RECORD_LENGTH (RECORD_SIZE - FIELD_OFFSET( RECORD_LIST, LogRecord )) + + +// +// Macros available in kernel mode which are not available in user mode +// + +#ifndef Add2Ptr +#define Add2Ptr(P,I) ((PVOID)((PUCHAR)(P) + (I))) +#endif + +#ifndef ROUND_TO_SIZE +#define ROUND_TO_SIZE(_length, _alignment) \ + (((_length) + ((_alignment)-1)) & ~((_alignment) - 1)) +#endif + +#ifndef FlagOn +#define FlagOn(_F,_SF) ((_F) & (_SF)) +#endif + +#endif /* __MINISPY_H__ */ + diff --git a/filesys/miniFilter/minispy/minispy.inf b/filesys/miniFilter/minispy/minispy.inf new file mode 100644 index 00000000..88cdcd40 --- /dev/null +++ b/filesys/miniFilter/minispy/minispy.inf @@ -0,0 +1,111 @@ +;;; +;;; Minispy +;;; +;;; +;;; Copyright (c) 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.0 +CatalogFile = minispy.cat + + +[DestinationDirs] +DefaultDestDir = 12 +Minispy.DriverFiles = 12 ;%windir%\system32\drivers +Minispy.UserFiles = 10,FltMgr ;%windir%\FltMgr + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = Minispy.DriverFiles, Minispy.UserFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,Minispy.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = Minispy.DriverFiles, Minispy.UserFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[Minispy.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = FltMgr +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = Minispy.AddRegistry + +; +; Registry Modifications +; + +[Minispy.AddRegistry] +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% +HKR,"Instances\"%Instance2.Name%,"Altitude",0x00000000,%Instance2.Altitude% +HKR,"Instances\"%Instance2.Name%,"Flags",0x00010001,%Instance2.Flags% +HKR,"Instances\"%Instance3.Name%,"Altitude",0x00000000,%Instance3.Altitude% +HKR,"Instances\"%Instance3.Name%,"Flags",0x00010001,%Instance3.Flags% + +; +; Copy Files +; + +[Minispy.DriverFiles] +%DriverName%.sys + +[Minispy.UserFiles] +%UserAppName%.exe + +[SourceDisksFiles] +minispy.sys = 1,, +minispy.exe = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Minispy mini-filter driver" +ServiceName = "Minispy" +DriverName = "minispy" +UserAppName = "minispy" +DiskId1 = "Minispy Device Installation Disk" + +;Instances specific information. +DefaultInstance = "Minispy - Top Instance" +Instance1.Name = "Minispy - Middle Instance" +Instance1.Altitude = "370000" +Instance1.Flags = 0x1 ; Suppress automatic attachments +Instance2.Name = "Minispy - Bottom Instance" +Instance2.Altitude = "361000" +Instance2.Flags = 0x1 ; Suppress automatic attachments +Instance3.Name = "Minispy - Top Instance" +Instance3.Altitude = "385100" +Instance3.Flags = 0x1 ; Suppress automatic attachments diff --git a/filesys/miniFilter/minispy/minispy.sln b/filesys/miniFilter/minispy/minispy.sln new file mode 100644 index 00000000..f6bc3c1c --- /dev/null +++ b/filesys/miniFilter/minispy/minispy.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{3B396921-0231-4B6C-B5E5-C7695F992BA7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{32B4D5F7-B865-467E-9344-627DDBB25267}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "filter\minispy.vcxproj", "{99B46F3E-1CC2-4689-8D3E-80CCBD448E39}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "user\minispy.vcxproj", "{3EFB308B-ED6C-42FE-9140-9883674EA0A2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|Win32.ActiveCfg = Debug|Win32 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|Win32.Build.0 = Debug|Win32 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|Win32.ActiveCfg = Release|Win32 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|Win32.Build.0 = Release|Win32 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|x64.ActiveCfg = Debug|x64 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|x64.Build.0 = Debug|x64 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|x64.ActiveCfg = Release|x64 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|x64.Build.0 = Release|x64 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|Win32.ActiveCfg = Debug|Win32 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|Win32.Build.0 = Debug|Win32 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|Win32.ActiveCfg = Release|Win32 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|Win32.Build.0 = Release|Win32 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|x64.ActiveCfg = Debug|x64 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|x64.Build.0 = Debug|x64 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|x64.ActiveCfg = Release|x64 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39} = {3B396921-0231-4B6C-B5E5-C7695F992BA7} + {3EFB308B-ED6C-42FE-9140-9883674EA0A2} = {32B4D5F7-B865-467E-9344-627DDBB25267} + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/minispy/user/minispy.vcxproj b/filesys/miniFilter/minispy/user/minispy.vcxproj new file mode 100644 index 00000000..7903c881 --- /dev/null +++ b/filesys/miniFilter/minispy/user/minispy.vcxproj @@ -0,0 +1,193 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {3EFB308B-ED6C-42FE-9140-9883674EA0A2} + $(MSBuildProjectName) + Debug + Win32 + {66B7506B-24F4-4F68-9941-68114AD91305} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + minispy + + + minispy + + + minispy + + + minispy + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters b/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters new file mode 100644 index 00000000..dff75ebd --- /dev/null +++ b/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters @@ -0,0 +1,30 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {FE182DA6-B396-42B5-BA36-09DA649AD386} + + + h;hpp;hxx;hm;inl;inc;xsd + {72009667-0B4E-4D0D-9548-CB2182BD6769} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9B01DDB6-745D-4785-BD95-F02013721E53} + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/minispy/user/mspyLog.c b/filesys/miniFilter/minispy/user/mspyLog.c new file mode 100644 index 00000000..758efe2d --- /dev/null +++ b/filesys/miniFilter/minispy/user/mspyLog.c @@ -0,0 +1,1193 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + mspyLog.c + +Abstract: + + This module contains functions used to retrieve and see the log records + recorded by MiniSpy.sys. + +Environment: + + User mode + +--*/ + +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include +#include +#include +#include +#include "mspyLog.h" + +#define TIME_BUFFER_LENGTH 20 +#define TIME_ERROR "time error" + +#define POLL_INTERVAL 200 // 200 milliseconds + +BOOLEAN +TranslateFileTag( + _In_ PLOG_RECORD logRecord + ) +/*++ + +Routine Description: + + If this is a mount point reparse point, move the given name string to the + correct position in the log record structure so it will be displayed + by the common routines. + +Arguments: + + logRecord - The log record to update + +Return Value: + + TRUE - if this is a mount point reparse point + FALSE - otherwise + +--*/ +{ + PFLT_TAG_DATA_BUFFER TagData; + ULONG Length; + + // + // The reparse data structure starts in the NAME field, point to it. + // + + TagData = (PFLT_TAG_DATA_BUFFER) &logRecord->Name[0]; + + // + // See if MOUNT POINT tag + // + + if (TagData->FileTag == IO_REPARSE_TAG_MOUNT_POINT) { + + // + // calculate how much to copy + // + + Length = min( MAX_NAME_SPACE - sizeof(UNICODE_NULL), TagData->MountPointReparseBuffer.SubstituteNameLength ); + + // + // Position the reparse name at the proper position in the buffer. + // Note that we are doing an overlapped copy + // + + MoveMemory( &logRecord->Name[0], + TagData->MountPointReparseBuffer.PathBuffer, + Length ); + + logRecord->Name[Length/sizeof(WCHAR)] = UNICODE_NULL; + return TRUE; + } + + return FALSE; +} + + +DWORD +WINAPI +RetrieveLogRecords( + _In_ LPVOID lpParameter + ) +/*++ + +Routine Description: + + This runs as a separate thread. Its job is to retrieve log records + from the filter and then output them + +Arguments: + + lpParameter - Contains context structure for synchronizing with the + main program thread. + +Return Value: + + The thread successfully terminated + +--*/ +{ + PLOG_CONTEXT context = (PLOG_CONTEXT)lpParameter; + DWORD bytesReturned = 0; + DWORD used; + PVOID alignedBuffer[BUFFER_SIZE/sizeof( PVOID )]; + PCHAR buffer = (PCHAR) alignedBuffer; + HRESULT hResult; + PLOG_RECORD pLogRecord; + PRECORD_DATA pRecordData; + COMMAND_MESSAGE commandMessage; + + //printf("Log: Starting up\n"); + +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant + + while (TRUE) { + +#pragma warning(pop) + + // + // Check to see if we should shut down. + // + + if (context->CleaningUp) { + + break; + } + + // + // Request log data from MiniSpy. + // + + commandMessage.Command = GetMiniSpyLog; + + hResult = FilterSendMessage( context->Port, + &commandMessage, + sizeof( COMMAND_MESSAGE ), + buffer, + sizeof(alignedBuffer), + &bytesReturned ); + + if (IS_ERROR( hResult )) { + + if (HRESULT_FROM_WIN32( ERROR_INVALID_HANDLE ) == hResult) { + + printf( "The kernel component of minispy has unloaded. Exiting\n" ); + ExitProcess( 0 ); + } else { + + if (hResult != HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS )) { + + printf( "UNEXPECTED ERROR received: %x\n", hResult ); + } + + Sleep( POLL_INTERVAL ); + } + + continue; + } + + // + // Buffer is filled with a series of LOG_RECORD structures, one + // right after another. Each LOG_RECORD says how long it is, so + // we know where the next LOG_RECORD begins. + // + + pLogRecord = (PLOG_RECORD) buffer; + used = 0; + + // + // Logic to write record to screen and/or file + // + + for (;;) { + + if (used+FIELD_OFFSET(LOG_RECORD,Name) > bytesReturned) { + + break; + } + + if (pLogRecord->Length < (sizeof(LOG_RECORD)+sizeof(WCHAR))) { + + printf( "UNEXPECTED LOG_RECORD->Length: length=%d expected>=%d\n", + pLogRecord->Length, + (sizeof(LOG_RECORD)+sizeof(WCHAR))); + + break; + } + + used += pLogRecord->Length; + + if (used > bytesReturned) { + + printf( "UNEXPECTED LOG_RECORD size: used=%d bytesReturned=%d\n", + used, + bytesReturned); + + break; + } + + pRecordData = &pLogRecord->Data; + + // + // See if a reparse point entry + // + + if (FlagOn(pLogRecord->RecordType,RECORD_TYPE_FILETAG)) { + + if (!TranslateFileTag( pLogRecord )){ + + // + // If this is a reparse point that can't be interpreted, move on. + // + + pLogRecord = (PLOG_RECORD)Add2Ptr(pLogRecord,pLogRecord->Length); + continue; + } + } + + if (context->LogToScreen) { + + ScreenDump( pLogRecord->SequenceNumber, + pLogRecord->Name, + pRecordData ); + } + + if (context->LogToFile) { + + FileDump( pLogRecord->SequenceNumber, + pLogRecord->Name, + pRecordData, + context->OutputFile ); + } + + // + // The RecordType could also designate that we are out of memory + // or hit our program defined memory limit, so check for these + // cases. + // + + if (FlagOn(pLogRecord->RecordType,RECORD_TYPE_FLAG_OUT_OF_MEMORY)) { + + if (context->LogToScreen) { + + printf( "M: %08X System Out of Memory\n", + pLogRecord->SequenceNumber ); + } + + if (context->LogToFile) { + + fprintf( context->OutputFile, + "M:\t0x%08X\tSystem Out of Memory\n", + pLogRecord->SequenceNumber ); + } + + } else if (FlagOn(pLogRecord->RecordType,RECORD_TYPE_FLAG_EXCEED_MEMORY_ALLOWANCE)) { + + if (context->LogToScreen) { + + printf( "M: %08X Exceeded Mamimum Allowed Memory Buffers\n", + pLogRecord->SequenceNumber ); + } + + if (context->LogToFile) { + + fprintf( context->OutputFile, + "M:\t0x%08X\tExceeded Mamimum Allowed Memory Buffers\n", + pLogRecord->SequenceNumber ); + } + } + + // + // Move to next LOG_RECORD + // + + pLogRecord = (PLOG_RECORD)Add2Ptr(pLogRecord,pLogRecord->Length); + } + + // + // If we didn't get any data, pause for 1/2 second + // + + if (bytesReturned == 0) { + + Sleep( POLL_INTERVAL ); + } + } + + printf( "Log: Shutting down\n" ); + ReleaseSemaphore( context->ShutDown, 1, NULL ); + printf( "Log: All done\n" ); + return 0; +} + + +VOID +PrintIrpCode( + _In_ UCHAR MajorCode, + _In_ UCHAR MinorCode, + _In_opt_ FILE *OutputFile, + _In_ BOOLEAN PrintMajorCode +) +/*++ + +Routine Description: + + Display the operation code + +Arguments: + + MajorCode - Major function code of operation + + MinorCode - Minor function code of operation + + OutputFile - If writing to a file (not the screen) the handle for that file + + PrintMajorCode - Only used when printing to the display: + TRUE - if we want to display the MAJOR CODE + FALSE - if we want to display the MINOR code + +Return Value: + + None + +--*/ +{ + CHAR *irpMajorString, *irpMinorString = NULL; + CHAR errorBuf[128]; + + switch (MajorCode) { + case IRP_MJ_CREATE: + irpMajorString = IRP_MJ_CREATE_STRING; + break; + case IRP_MJ_CREATE_NAMED_PIPE: + irpMajorString = IRP_MJ_CREATE_NAMED_PIPE_STRING; + break; + case IRP_MJ_CLOSE: + irpMajorString = IRP_MJ_CLOSE_STRING; + break; + case IRP_MJ_READ: + irpMajorString = IRP_MJ_READ_STRING; + switch (MinorCode) { + case IRP_MN_NORMAL: + irpMinorString = IRP_MN_NORMAL_STRING; + break; + case IRP_MN_DPC: + irpMinorString = IRP_MN_DPC_STRING; + break; + case IRP_MN_MDL: + irpMinorString = IRP_MN_MDL_STRING; + break; + case IRP_MN_COMPLETE: + irpMinorString = IRP_MN_COMPLETE_STRING; + break; + case IRP_MN_COMPRESSED: + irpMinorString = IRP_MN_COMPRESSED_STRING; + break; + case IRP_MN_MDL_DPC: + irpMinorString = IRP_MN_MDL_DPC_STRING; + break; + case IRP_MN_COMPLETE_MDL: + irpMinorString = IRP_MN_COMPLETE_MDL_STRING; + break; + case IRP_MN_COMPLETE_MDL_DPC: + irpMinorString = IRP_MN_COMPLETE_MDL_DPC_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_WRITE: + irpMajorString = IRP_MJ_WRITE_STRING; + switch (MinorCode) { + case IRP_MN_NORMAL: + irpMinorString = IRP_MN_NORMAL_STRING; + break; + case IRP_MN_DPC: + irpMinorString = IRP_MN_DPC_STRING; + break; + case IRP_MN_MDL: + irpMinorString = IRP_MN_MDL_STRING; + break; + case IRP_MN_COMPLETE: + irpMinorString = IRP_MN_COMPLETE_STRING; + break; + case IRP_MN_COMPRESSED: + irpMinorString = IRP_MN_COMPRESSED_STRING; + break; + case IRP_MN_MDL_DPC: + irpMinorString = IRP_MN_MDL_DPC_STRING; + break; + case IRP_MN_COMPLETE_MDL: + irpMinorString = IRP_MN_COMPLETE_MDL_STRING; + break; + case IRP_MN_COMPLETE_MDL_DPC: + irpMinorString = IRP_MN_COMPLETE_MDL_DPC_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_QUERY_INFORMATION: + irpMajorString = IRP_MJ_QUERY_INFORMATION_STRING; + break; + case IRP_MJ_SET_INFORMATION: + irpMajorString = IRP_MJ_SET_INFORMATION_STRING; + break; + case IRP_MJ_QUERY_EA: + irpMajorString = IRP_MJ_QUERY_EA_STRING; + break; + case IRP_MJ_SET_EA: + irpMajorString = IRP_MJ_SET_EA_STRING; + break; + case IRP_MJ_FLUSH_BUFFERS: + irpMajorString = IRP_MJ_FLUSH_BUFFERS_STRING; + break; + case IRP_MJ_QUERY_VOLUME_INFORMATION: + irpMajorString = IRP_MJ_QUERY_VOLUME_INFORMATION_STRING; + break; + case IRP_MJ_SET_VOLUME_INFORMATION: + irpMajorString = IRP_MJ_SET_VOLUME_INFORMATION_STRING; + break; + case IRP_MJ_DIRECTORY_CONTROL: + irpMajorString = IRP_MJ_DIRECTORY_CONTROL_STRING; + switch (MinorCode) { + case IRP_MN_QUERY_DIRECTORY: + irpMinorString = IRP_MN_QUERY_DIRECTORY_STRING; + break; + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: + irpMinorString = IRP_MN_NOTIFY_CHANGE_DIRECTORY_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_FILE_SYSTEM_CONTROL: + irpMajorString = IRP_MJ_FILE_SYSTEM_CONTROL_STRING; + switch (MinorCode) { + case IRP_MN_USER_FS_REQUEST: + irpMinorString = IRP_MN_USER_FS_REQUEST_STRING; + break; + case IRP_MN_MOUNT_VOLUME: + irpMinorString = IRP_MN_MOUNT_VOLUME_STRING; + break; + case IRP_MN_VERIFY_VOLUME: + irpMinorString = IRP_MN_VERIFY_VOLUME_STRING; + break; + case IRP_MN_LOAD_FILE_SYSTEM: + irpMinorString = IRP_MN_LOAD_FILE_SYSTEM_STRING; + break; + case IRP_MN_TRACK_LINK: + irpMinorString = IRP_MN_TRACK_LINK_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_DEVICE_CONTROL: + irpMajorString = IRP_MJ_DEVICE_CONTROL_STRING; + switch (MinorCode) { + case IRP_MN_SCSI_CLASS: + irpMinorString = IRP_MN_SCSI_CLASS_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + irpMajorString = IRP_MJ_INTERNAL_DEVICE_CONTROL_STRING; + break; + case IRP_MJ_SHUTDOWN: + irpMajorString = IRP_MJ_SHUTDOWN_STRING; + break; + case IRP_MJ_LOCK_CONTROL: + irpMajorString = IRP_MJ_LOCK_CONTROL_STRING; + switch (MinorCode) { + case IRP_MN_LOCK: + irpMinorString = IRP_MN_LOCK_STRING; + break; + case IRP_MN_UNLOCK_SINGLE: + irpMinorString = IRP_MN_UNLOCK_SINGLE_STRING; + break; + case IRP_MN_UNLOCK_ALL: + irpMinorString = IRP_MN_UNLOCK_ALL_STRING; + break; + case IRP_MN_UNLOCK_ALL_BY_KEY: + irpMinorString = IRP_MN_UNLOCK_ALL_BY_KEY_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_CLEANUP: + irpMajorString = IRP_MJ_CLEANUP_STRING; + break; + case IRP_MJ_CREATE_MAILSLOT: + irpMajorString = IRP_MJ_CREATE_MAILSLOT_STRING; + break; + case IRP_MJ_QUERY_SECURITY: + irpMajorString = IRP_MJ_QUERY_SECURITY_STRING; + break; + case IRP_MJ_SET_SECURITY: + irpMajorString = IRP_MJ_SET_SECURITY_STRING; + break; + case IRP_MJ_POWER: + irpMajorString = IRP_MJ_POWER_STRING; + switch (MinorCode) { + case IRP_MN_WAIT_WAKE: + irpMinorString = IRP_MN_WAIT_WAKE_STRING; + break; + case IRP_MN_POWER_SEQUENCE: + irpMinorString = IRP_MN_POWER_SEQUENCE_STRING; + break; + case IRP_MN_SET_POWER: + irpMinorString = IRP_MN_SET_POWER_STRING; + break; + case IRP_MN_QUERY_POWER: + irpMinorString = IRP_MN_QUERY_POWER_STRING; + break; + default : + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_SYSTEM_CONTROL: + irpMajorString = IRP_MJ_SYSTEM_CONTROL_STRING; + switch (MinorCode) { + case IRP_MN_QUERY_ALL_DATA: + irpMinorString = IRP_MN_QUERY_ALL_DATA_STRING; + break; + case IRP_MN_QUERY_SINGLE_INSTANCE: + irpMinorString = IRP_MN_QUERY_SINGLE_INSTANCE_STRING; + break; + case IRP_MN_CHANGE_SINGLE_INSTANCE: + irpMinorString = IRP_MN_CHANGE_SINGLE_INSTANCE_STRING; + break; + case IRP_MN_CHANGE_SINGLE_ITEM: + irpMinorString = IRP_MN_CHANGE_SINGLE_ITEM_STRING; + break; + case IRP_MN_ENABLE_EVENTS: + irpMinorString = IRP_MN_ENABLE_EVENTS_STRING; + break; + case IRP_MN_DISABLE_EVENTS: + irpMinorString = IRP_MN_DISABLE_EVENTS_STRING; + break; + case IRP_MN_ENABLE_COLLECTION: + irpMinorString = IRP_MN_ENABLE_COLLECTION_STRING; + break; + case IRP_MN_DISABLE_COLLECTION: + irpMinorString = IRP_MN_DISABLE_COLLECTION_STRING; + break; + case IRP_MN_REGINFO: + irpMinorString = IRP_MN_REGINFO_STRING; + break; + case IRP_MN_EXECUTE_METHOD: + irpMinorString = IRP_MN_EXECUTE_METHOD_STRING; + break; + default : + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_DEVICE_CHANGE: + irpMajorString = IRP_MJ_DEVICE_CHANGE_STRING; + break; + case IRP_MJ_QUERY_QUOTA: + irpMajorString = IRP_MJ_QUERY_QUOTA_STRING; + break; + case IRP_MJ_SET_QUOTA: + irpMajorString = IRP_MJ_SET_QUOTA_STRING; + break; + case IRP_MJ_PNP: + irpMajorString = IRP_MJ_PNP_STRING; + switch (MinorCode) { + case IRP_MN_START_DEVICE: + irpMinorString = IRP_MN_START_DEVICE_STRING; + break; + case IRP_MN_QUERY_REMOVE_DEVICE: + irpMinorString = IRP_MN_QUERY_REMOVE_DEVICE_STRING; + break; + case IRP_MN_REMOVE_DEVICE: + irpMinorString = IRP_MN_REMOVE_DEVICE_STRING; + break; + case IRP_MN_CANCEL_REMOVE_DEVICE: + irpMinorString = IRP_MN_CANCEL_REMOVE_DEVICE_STRING; + break; + case IRP_MN_STOP_DEVICE: + irpMinorString = IRP_MN_STOP_DEVICE_STRING; + break; + case IRP_MN_QUERY_STOP_DEVICE: + irpMinorString = IRP_MN_QUERY_STOP_DEVICE_STRING; + break; + case IRP_MN_CANCEL_STOP_DEVICE: + irpMinorString = IRP_MN_CANCEL_STOP_DEVICE_STRING; + break; + case IRP_MN_QUERY_DEVICE_RELATIONS: + irpMinorString = IRP_MN_QUERY_DEVICE_RELATIONS_STRING; + break; + case IRP_MN_QUERY_INTERFACE: + irpMinorString = IRP_MN_QUERY_INTERFACE_STRING; + break; + case IRP_MN_QUERY_CAPABILITIES: + irpMinorString = IRP_MN_QUERY_CAPABILITIES_STRING; + break; + case IRP_MN_QUERY_RESOURCES: + irpMinorString = IRP_MN_QUERY_RESOURCES_STRING; + break; + case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: + irpMinorString = IRP_MN_QUERY_RESOURCE_REQUIREMENTS_STRING; + break; + case IRP_MN_QUERY_DEVICE_TEXT: + irpMinorString = IRP_MN_QUERY_DEVICE_TEXT_STRING; + break; + case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: + irpMinorString = IRP_MN_FILTER_RESOURCE_REQUIREMENTS_STRING; + break; + case IRP_MN_READ_CONFIG: + irpMinorString = IRP_MN_READ_CONFIG_STRING; + break; + case IRP_MN_WRITE_CONFIG: + irpMinorString = IRP_MN_WRITE_CONFIG_STRING; + break; + case IRP_MN_EJECT: + irpMinorString = IRP_MN_EJECT_STRING; + break; + case IRP_MN_SET_LOCK: + irpMinorString = IRP_MN_SET_LOCK_STRING; + break; + case IRP_MN_QUERY_ID: + irpMinorString = IRP_MN_QUERY_ID_STRING; + break; + case IRP_MN_QUERY_PNP_DEVICE_STATE: + irpMinorString = IRP_MN_QUERY_PNP_DEVICE_STATE_STRING; + break; + case IRP_MN_QUERY_BUS_INFORMATION: + irpMinorString = IRP_MN_QUERY_BUS_INFORMATION_STRING; + break; + case IRP_MN_DEVICE_USAGE_NOTIFICATION: + irpMinorString = IRP_MN_DEVICE_USAGE_NOTIFICATION_STRING; + break; + case IRP_MN_SURPRISE_REMOVAL: + irpMinorString = IRP_MN_SURPRISE_REMOVAL_STRING; + break; + case IRP_MN_QUERY_LEGACY_BUS_INFORMATION: + irpMinorString = IRP_MN_QUERY_LEGACY_BUS_INFORMATION_STRING; + break; + default : + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + + case IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION: + irpMajorString = IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION_STRING; + break; + + case IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION: + irpMajorString = IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION_STRING; + break; + + case IRP_MJ_ACQUIRE_FOR_MOD_WRITE: + irpMajorString = IRP_MJ_ACQUIRE_FOR_MOD_WRITE_STRING; + break; + + case IRP_MJ_RELEASE_FOR_MOD_WRITE: + irpMajorString = IRP_MJ_RELEASE_FOR_MOD_WRITE_STRING; + break; + + case IRP_MJ_ACQUIRE_FOR_CC_FLUSH: + irpMajorString = IRP_MJ_ACQUIRE_FOR_CC_FLUSH_STRING; + break; + + case IRP_MJ_RELEASE_FOR_CC_FLUSH: + irpMajorString = IRP_MJ_RELEASE_FOR_CC_FLUSH_STRING; + break; + + case IRP_MJ_NOTIFY_STREAM_FO_CREATION: + irpMajorString = IRP_MJ_NOTIFY_STREAM_FO_CREATION_STRING; + break; + + + + case IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE: + irpMajorString = IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE_STRING; + break; + + case IRP_MJ_NETWORK_QUERY_OPEN: + irpMajorString = IRP_MJ_NETWORK_QUERY_OPEN_STRING; + break; + + case IRP_MJ_MDL_READ: + irpMajorString = IRP_MJ_MDL_READ_STRING; + break; + + case IRP_MJ_MDL_READ_COMPLETE: + irpMajorString = IRP_MJ_MDL_READ_COMPLETE_STRING; + break; + + case IRP_MJ_PREPARE_MDL_WRITE: + irpMajorString = IRP_MJ_PREPARE_MDL_WRITE_STRING; + break; + + case IRP_MJ_MDL_WRITE_COMPLETE: + irpMajorString = IRP_MJ_MDL_WRITE_COMPLETE_STRING; + break; + + case IRP_MJ_VOLUME_MOUNT: + irpMajorString = IRP_MJ_VOLUME_MOUNT_STRING; + break; + + case IRP_MJ_VOLUME_DISMOUNT: + irpMajorString = IRP_MJ_VOLUME_DISMOUNT_STRING; + break; + + case IRP_MJ_TRANSACTION_NOTIFY: + irpMajorString = IRP_MJ_TRANSACTION_NOTIFY_STRING; + switch (MinorCode) { + case 0: + irpMinorString = TRANSACTION_BEGIN; + break; + case TRANSACTION_NOTIFY_PREPREPARE_CODE: + irpMinorString = TRANSACTION_NOTIFY_PREPREPARE_STRING; + break; + case TRANSACTION_NOTIFY_PREPARE_CODE: + irpMinorString = TRANSACTION_NOTIFY_PREPARE_STRING; + break; + case TRANSACTION_NOTIFY_COMMIT_CODE: + irpMinorString = TRANSACTION_NOTIFY_COMMIT_STRING; + break; + case TRANSACTION_NOTIFY_COMMIT_FINALIZE_CODE: + irpMinorString = TRANSACTION_NOTIFY_COMMIT_FINALIZE_STRING; + break; + case TRANSACTION_NOTIFY_ROLLBACK_CODE: + irpMinorString = TRANSACTION_NOTIFY_ROLLBACK_STRING; + break; + case TRANSACTION_NOTIFY_PREPREPARE_COMPLETE_CODE: + irpMinorString = TRANSACTION_NOTIFY_PREPREPARE_COMPLETE_STRING; + break; + case TRANSACTION_NOTIFY_PREPARE_COMPLETE_CODE: + irpMinorString = TRANSACTION_NOTIFY_COMMIT_COMPLETE_STRING; + break; + case TRANSACTION_NOTIFY_ROLLBACK_COMPLETE_CODE: + irpMinorString = TRANSACTION_NOTIFY_ROLLBACK_COMPLETE_STRING; + break; + case TRANSACTION_NOTIFY_RECOVER_CODE: + irpMinorString = TRANSACTION_NOTIFY_RECOVER_STRING; + break; + case TRANSACTION_NOTIFY_SINGLE_PHASE_COMMIT_CODE: + irpMinorString = TRANSACTION_NOTIFY_SINGLE_PHASE_COMMIT_STRING; + break; + case TRANSACTION_NOTIFY_DELEGATE_COMMIT_CODE: + irpMinorString = TRANSACTION_NOTIFY_DELEGATE_COMMIT_STRING; + break; + case TRANSACTION_NOTIFY_RECOVER_QUERY_CODE: + irpMinorString = TRANSACTION_NOTIFY_RECOVER_QUERY_STRING; + break; + case TRANSACTION_NOTIFY_ENLIST_PREPREPARE_CODE: + irpMinorString = TRANSACTION_NOTIFY_ENLIST_PREPREPARE_STRING; + break; + case TRANSACTION_NOTIFY_LAST_RECOVER_CODE: + irpMinorString = TRANSACTION_NOTIFY_LAST_RECOVER_STRING; + break; + case TRANSACTION_NOTIFY_INDOUBT_CODE: + irpMinorString = TRANSACTION_NOTIFY_INDOUBT_STRING; + break; + case TRANSACTION_NOTIFY_PROPAGATE_PULL_CODE: + irpMinorString = TRANSACTION_NOTIFY_PROPAGATE_PULL_STRING; + break; + case TRANSACTION_NOTIFY_PROPAGATE_PUSH_CODE: + irpMinorString = TRANSACTION_NOTIFY_PROPAGATE_PUSH_STRING; + break; + case TRANSACTION_NOTIFY_MARSHAL_CODE: + irpMinorString = TRANSACTION_NOTIFY_MARSHAL_STRING; + break; + case TRANSACTION_NOTIFY_ENLIST_MASK_CODE: + irpMinorString = TRANSACTION_NOTIFY_ENLIST_MASK_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Transaction notication code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp major function (%d)",MajorCode); + irpMajorString = errorBuf; + break; + } + + if (OutputFile) { + + if (irpMinorString) { + + fprintf(OutputFile, "\t%-35s\t%-35s", irpMajorString, irpMinorString); + + } else { + + fprintf(OutputFile, "\t%-35s\t ", irpMajorString); + } + + } else { + + if (PrintMajorCode) { + + printf("%-35s ", irpMajorString); + + } else { + + if (irpMinorString) { + + printf(" %-35s\n", + irpMinorString); + } + } + } +} + + +ULONG +FormatSystemTime( + _In_ SYSTEMTIME *SystemTime, + _Out_writes_bytes_(BufferLength) CHAR *Buffer, + _In_ ULONG BufferLength + ) +/*++ +Routine Description: + + Formats the values in a SystemTime struct into the buffer + passed in. The resulting string is NULL terminated. The format + for the time is: + hours:minutes:seconds:milliseconds + +Arguments: + + SystemTime - the struct to format + Buffer - the buffer to place the formatted time in + BufferLength - the size of the buffer + +Return Value: + + The length of the string returned in Buffer. + +--*/ +{ + ULONG returnLength = 0; + + if (BufferLength < TIME_BUFFER_LENGTH) { + + // + // Buffer is too short so exit + // + + return 0; + } + + returnLength = sprintf_s( Buffer, + BufferLength, + "%02d:%02d:%02d:%03d", + SystemTime->wHour, + SystemTime->wMinute, + SystemTime->wSecond, + SystemTime->wMilliseconds ); + + return returnLength; +} + + +VOID +FileDump ( + _In_ ULONG SequenceNumber, + _In_ WCHAR CONST *Name, + _In_ PRECORD_DATA RecordData, + _In_ FILE *File + ) +/*++ +Routine Description: + + Prints a Data log record to the specified file. The output is in a tab + delimited format with the fields in the following order: + + SequenceNumber, OriginatingTime, CompletionTime, CallbackMajorId, CallbackMinorId, + Flags, NoCache, Paging I/O, Synchronous, Synchronous paging, FileName, + ReturnStatus, FileName + + +Arguments: + + SequenceNumber - the sequence number for this log record + Name - the name of the file that this Irp relates to + RecordData - the Data record to print + File - the file to print to + +Return Value: + + None. + +--*/ +{ + FILETIME localTime; + SYSTEMTIME systemTime; + CHAR time[TIME_BUFFER_LENGTH]; + static BOOLEAN didFileHeader = FALSE; + + // + // Is this an Irp or a FastIo? + // + + if (!didFileHeader) { + +#if defined(_WIN64) + fprintf( File, "Opr\t SeqNum \t PreOp Time \tPostOp Time \t Process.Thrd\t Major Operation \t Minor Operation \t IrpFlags \t DevObj \t FileObj \t Transactn \t status:inform \t Arg 1 \t Arg 2 \t Arg 3 \t Arg 4 \t Arg 5 \t Arg 6 \tName\n"); + fprintf( File, "---\t----------\t------------\t------------\t-------------\t-----------------------------------\t-----------------------------------\t---------------\t------------------\t------------------\t------------------\t-----------------------------\t------------------\t------------------\t------------------\t------------------\t------------------\t----------\t--------------------------------------------------\n"); +#else + fprintf( File, "Opr\t SeqNum \t PreOp Time \tPostOp Time \t Process.Thrd\t Major Operation \t Minor Operation \t IrpFlags \t DevObj \t FileObj \tTransactn \t status:inform \t Arg 1 \t Arg 2 \t Arg 3 \t Arg 4 \t Arg 5 \t Arg 6 \tName\n"); + fprintf( File, "---\t----------\t------------\t------------\t-------------\t-----------------------------------\t-----------------------------------\t---------------\t----------\t----------\t----------\t---------------------\t----------\t----------\t----------\t----------\t----------\t----------\t--------------------------------------------------\n"); +#endif + didFileHeader = TRUE; + } + + // + // Is this an Irp or a FastIo? + // + + if (RecordData->Flags & FLT_CALLBACK_DATA_IRP_OPERATION) { + + fprintf( File, "IRP"); + + } else if (RecordData->Flags & FLT_CALLBACK_DATA_FAST_IO_OPERATION) { + + fprintf( File, "FIO"); + + } else if (RecordData->Flags & FLT_CALLBACK_DATA_FS_FILTER_OPERATION) { + + fprintf( File, "FSF"); + + } else { + + fprintf( File, "ERR"); + } + + // + // Print the sequence number + // + + fprintf( File, "\t0x%08X", SequenceNumber ); + + // + // Convert originating time + // + + FileTimeToLocalFileTime( (FILETIME *)&(RecordData->OriginatingTime), + &localTime ); + FileTimeToSystemTime( &localTime, + &systemTime ); + + if (FormatSystemTime( &systemTime, time, TIME_BUFFER_LENGTH )) { + + fprintf( File, "\t%-12s", time ); + + } else { + + fprintf( File, "\t%-12s", TIME_ERROR ); + } + + // + // Convert completion time + // + + FileTimeToLocalFileTime( (FILETIME *)&(RecordData->CompletionTime), + &localTime ); + FileTimeToSystemTime( &localTime, + &systemTime ); + + if (FormatSystemTime( &systemTime, time, TIME_BUFFER_LENGTH )) { + + fprintf( File, "\t%-12s", time ); + + } else { + + fprintf( File, "\t%-12s", TIME_ERROR ); + } + + fprintf(File, "\t%8x.%-4x ", RecordData->ProcessId, RecordData->ThreadId); + + PrintIrpCode( RecordData->CallbackMajorId, + RecordData->CallbackMinorId, + File, + TRUE ); + + // + // Interpret set IrpFlags + // + + fprintf( File, "\t0x%08lx ", RecordData->IrpFlags ); + fprintf( File, "%s", (RecordData->IrpFlags & IRP_NOCACHE) ? "N":"-" ); + fprintf( File, "%s", (RecordData->IrpFlags & IRP_PAGING_IO) ? "P":"-" ); + fprintf( File, "%s", (RecordData->IrpFlags & IRP_SYNCHRONOUS_API) ? "S":"-" ); + fprintf( File, "%s", (RecordData->IrpFlags & IRP_SYNCHRONOUS_PAGING_IO) ? "Y":"-" ); + + fprintf( File, "\t0x%08p", (PVOID) RecordData->DeviceObject ); + fprintf( File, "\t0x%08p", (PVOID) RecordData->FileObject ); + fprintf( File, "\t0x%08p", (PVOID) RecordData->Transaction ); + fprintf( File, "\t0x%08lx:0x%p", RecordData->Status, (PVOID)RecordData->Information ); + + fprintf( File, "\t0x%p", RecordData->Arg1 ); + fprintf( File, "\t0x%p", RecordData->Arg2 ); + fprintf( File, "\t0x%p", RecordData->Arg3 ); + fprintf( File, "\t0x%p", RecordData->Arg4 ); + fprintf( File, "\t0x%p", RecordData->Arg5 ); + fprintf( File, "\t0x%08I64x", RecordData->Arg6.QuadPart ); + + fprintf( File, "\t%S", Name ); + fprintf( File, "\n" ); +} + + +VOID +ScreenDump( + _In_ ULONG SequenceNumber, + _In_ WCHAR CONST *Name, + _In_ PRECORD_DATA RecordData + ) +/*++ +Routine Description: + + Prints a Irp log record to the screen in the following order: + SequenceNumber, OriginatingTime, CompletionTime, IrpMajor, IrpMinor, + Flags, IrpFlags, NoCache, Paging I/O, Synchronous, Synchronous paging, + FileName, ReturnStatus, FileName + +Arguments: + + SequenceNumber - the sequence number for this log record + Name - the file name to which this Irp relates + RecordData - the Irp record to print + +Return Value: + + None. + +--*/ +{ + FILETIME localTime; + SYSTEMTIME systemTime; + CHAR time[TIME_BUFFER_LENGTH]; + static BOOLEAN didScreenHeader = FALSE; + + // + // Is this an Irp or a FastIo? + // + + if (!didScreenHeader) { + +#if defined(_WIN64) + printf("Opr SeqNum PreOp Time PostOp Time Process.Thrd Major/Minor Operation IrpFlags DevObj FileObj Transact status:inform Arguments Name\n"); + printf("--- -------- ------------ ------------ ------------- ----------------------------------- ------------- ---------------- ---------------- ---------------- ------------------------- --------------------------------------------------------------------------------------------------------- -----------------------------------\n"); +#else + printf("Opr SeqNum PreOp Time PostOp Time Process.Thrd Major/Minor Operation IrpFlags DevObj FileObj Transact status:inform Arguments Name\n"); + printf("--- -------- ------------ ------------ ------------- ----------------------------------- ------------- -------- -------- -------- ----------------- ----------------------------------------------------------------- -----------------------------------\n"); +#endif + didScreenHeader = TRUE; + } + + // + // Display informatoin + // + + if (RecordData->Flags & FLT_CALLBACK_DATA_IRP_OPERATION) { + + printf( "IRP "); + + } else if (RecordData->Flags & FLT_CALLBACK_DATA_FAST_IO_OPERATION) { + + printf( "FIO "); + + } else if (RecordData->Flags & FLT_CALLBACK_DATA_FS_FILTER_OPERATION) { + + printf( "FSF " ); + } else { + + printf( "ERR "); + } + + printf( "%08X ", SequenceNumber ); + + + // + // Convert originating time + // + + FileTimeToLocalFileTime( (FILETIME *)&(RecordData->OriginatingTime), + &localTime ); + FileTimeToSystemTime( &localTime, + &systemTime ); + + if (FormatSystemTime( &systemTime, time, TIME_BUFFER_LENGTH )) { + + printf( "%-12s ", time ); + + } else { + + printf( "%-12s ", TIME_ERROR ); + } + + // + // Convert completion time + // + + FileTimeToLocalFileTime( (FILETIME *)&(RecordData->CompletionTime), + &localTime ); + FileTimeToSystemTime( &localTime, + &systemTime ); + + if (FormatSystemTime( &systemTime, time, TIME_BUFFER_LENGTH )) { + + printf( "%-12s ", time ); + + } else { + + printf( "%-12s ", TIME_ERROR ); + } + + printf("%8x.%-4x ", RecordData->ProcessId, RecordData->ThreadId); + + PrintIrpCode( RecordData->CallbackMajorId, + RecordData->CallbackMinorId, + NULL, + TRUE ); + + // + // Interpret set IrpFlags + // + + printf( "%08lx ", RecordData->IrpFlags ); + printf( "%s", (RecordData->IrpFlags & IRP_NOCACHE) ? "N":"-" ); + printf( "%s", (RecordData->IrpFlags & IRP_PAGING_IO) ? "P":"-" ); + printf( "%s", (RecordData->IrpFlags & IRP_SYNCHRONOUS_API) ? "S":"-" ); + printf( "%s ", (RecordData->IrpFlags & IRP_SYNCHRONOUS_PAGING_IO) ? "Y":"-" ); + + printf( "%08p ", (PVOID) RecordData->DeviceObject ); + printf( "%08p ", (PVOID) RecordData->FileObject ); + printf( "%08p ", (PVOID) RecordData->Transaction ); + printf( "%08lx:%p ", RecordData->Status, (PVOID)RecordData->Information ); + + printf( "1:%p 2:%p 3:%p 4:%p 5:%p 6:%08I64x ", + RecordData->Arg1, + RecordData->Arg2, + RecordData->Arg3, + RecordData->Arg4, + RecordData->Arg5, + RecordData->Arg6.QuadPart ); + + printf( "%S", Name ); + printf( "\n" ); + PrintIrpCode( RecordData->CallbackMajorId, + RecordData->CallbackMinorId, + NULL, + FALSE ); +} + diff --git a/filesys/miniFilter/minispy/user/mspyLog.h b/filesys/miniFilter/minispy/user/mspyLog.h new file mode 100644 index 00000000..00377800 --- /dev/null +++ b/filesys/miniFilter/minispy/user/mspyLog.h @@ -0,0 +1,425 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + mspyLog.h + +Abstract: + + This module contains the structures and prototypes used by the user + program to retrieve and see the log records recorded by MiniSpy.sys. + +Environment: + + User mode + +--*/ +#ifndef __MSPYLOG_H__ +#define __MSPYLOG_H__ + +#include +#include +#include "minispy.h" + +#define BUFFER_SIZE 4096 + +// +// Structure for managing current state. +// + +typedef struct _LOG_CONTEXT { + + HANDLE Port; + BOOLEAN LogToScreen; + BOOLEAN LogToFile; + FILE *OutputFile; + + BOOLEAN NextLogToScreen; + + // + // For synchronizing shutting down of both threads + // + + BOOLEAN CleaningUp; + HANDLE ShutDown; + +} LOG_CONTEXT, *PLOG_CONTEXT; + +// +// Function prototypes +// + +DWORD WINAPI +RetrieveLogRecords( + _In_ LPVOID lpParameter + ); + +VOID +FileDump ( + _In_ ULONG SequenceNumber, + _In_ WCHAR CONST *Name, + _In_ PRECORD_DATA RecordData, + _In_ FILE *File + ); + +VOID +ScreenDump( + _In_ ULONG SequenceNumber, + _In_ WCHAR CONST *Name, + _In_ PRECORD_DATA RecordData + ); + +// +// Values set for the Flags field in a RECORD_DATA structure. +// These flags come from the FLT_CALLBACK_DATA structure. +// + +#define FLT_CALLBACK_DATA_IRP_OPERATION 0x00000001 // Set for Irp operations +#define FLT_CALLBACK_DATA_FAST_IO_OPERATION 0x00000002 // Set for Fast Io operations +#define FLT_CALLBACK_DATA_FS_FILTER_OPERATION 0x00000004 // Set for FsFilter operations + +// +// standard IRP_MJ string definitions +// + +#define IRP_MJ_CREATE_STRING "IRP_MJ_CREATE" +#define IRP_MJ_CREATE_NAMED_PIPE_STRING "IRP_MJ_CREATE_NAMED_PIPE" +#define IRP_MJ_CLOSE_STRING "IRP_MJ_CLOSE" +#define IRP_MJ_READ_STRING "IRP_MJ_READ" +#define IRP_MJ_WRITE_STRING "IRP_MJ_WRITE" +#define IRP_MJ_QUERY_INFORMATION_STRING "IRP_MJ_QUERY_INFORMATION" +#define IRP_MJ_SET_INFORMATION_STRING "IRP_MJ_SET_INFORMATION" +#define IRP_MJ_QUERY_EA_STRING "IRP_MJ_QUERY_EA" +#define IRP_MJ_SET_EA_STRING "IRP_MJ_SET_EA" +#define IRP_MJ_FLUSH_BUFFERS_STRING "IRP_MJ_FLUSH_BUFFERS" +#define IRP_MJ_QUERY_VOLUME_INFORMATION_STRING "IRP_MJ_QUERY_VOLUME_INFORMATION" +#define IRP_MJ_SET_VOLUME_INFORMATION_STRING "IRP_MJ_SET_VOLUME_INFORMATION" +#define IRP_MJ_DIRECTORY_CONTROL_STRING "IRP_MJ_DIRECTORY_CONTROL" +#define IRP_MJ_FILE_SYSTEM_CONTROL_STRING "IRP_MJ_FILE_SYSTEM_CONTROL" +#define IRP_MJ_DEVICE_CONTROL_STRING "IRP_MJ_DEVICE_CONTROL" +#define IRP_MJ_INTERNAL_DEVICE_CONTROL_STRING "IRP_MJ_INTERNAL_DEVICE_CONTROL" +#define IRP_MJ_SHUTDOWN_STRING "IRP_MJ_SHUTDOWN" +#define IRP_MJ_LOCK_CONTROL_STRING "IRP_MJ_LOCK_CONTROL" +#define IRP_MJ_CLEANUP_STRING "IRP_MJ_CLEANUP" +#define IRP_MJ_CREATE_MAILSLOT_STRING "IRP_MJ_CREATE_MAILSLOT" +#define IRP_MJ_QUERY_SECURITY_STRING "IRP_MJ_QUERY_SECURITY" +#define IRP_MJ_SET_SECURITY_STRING "IRP_MJ_SET_SECURITY" +#define IRP_MJ_POWER_STRING "IRP_MJ_POWER" +#define IRP_MJ_SYSTEM_CONTROL_STRING "IRP_MJ_SYSTEM_CONTROL" +#define IRP_MJ_DEVICE_CHANGE_STRING "IRP_MJ_DEVICE_CHANGE" +#define IRP_MJ_QUERY_QUOTA_STRING "IRP_MJ_QUERY_QUOTA" +#define IRP_MJ_SET_QUOTA_STRING "IRP_MJ_SET_QUOTA" +#define IRP_MJ_PNP_STRING "IRP_MJ_PNP" +#define IRP_MJ_MAXIMUM_FUNCTION_STRING "IRP_MJ_MAXIMUM_FUNCTION" + +// +// FSFilter string definitions +// + +#define IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION_STRING "IRP_MJ_ACQUIRE_FOR_SECTION_SYNC" +#define IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION_STRING "IRP_MJ_RELEASE_FOR_SECTION_SYNC" +#define IRP_MJ_ACQUIRE_FOR_MOD_WRITE_STRING "IRP_MJ_ACQUIRE_FOR_MOD_WRITE" +#define IRP_MJ_RELEASE_FOR_MOD_WRITE_STRING "IRP_MJ_RELEASE_FOR_MOD_WRITE" +#define IRP_MJ_ACQUIRE_FOR_CC_FLUSH_STRING "IRP_MJ_ACQUIRE_FOR_CC_FLUSH" +#define IRP_MJ_RELEASE_FOR_CC_FLUSH_STRING "IRP_MJ_RELEASE_FOR_CC_FLUSH" +#define IRP_MJ_NOTIFY_STREAM_FO_CREATION_STRING "IRP_MJ_NOTIFY_STREAM_FO_CREATION" + +// +// FAST_IO and other string definitions +// + +#define IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE_STRING "IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE" +#define IRP_MJ_DETACH_DEVICE_STRING "IRP_MJ_DETACH_DEVICE" +#define IRP_MJ_NETWORK_QUERY_OPEN_STRING "IRP_MJ_NETWORK_QUERY_OPEN" +#define IRP_MJ_MDL_READ_STRING "IRP_MJ_MDL_READ" +#define IRP_MJ_MDL_READ_COMPLETE_STRING "IRP_MJ_MDL_READ_COMPLETE" +#define IRP_MJ_PREPARE_MDL_WRITE_STRING "IRP_MJ_PREPARE_MDL_WRITE" +#define IRP_MJ_MDL_WRITE_COMPLETE_STRING "IRP_MJ_MDL_WRITE_COMPLETE" +#define IRP_MJ_VOLUME_MOUNT_STRING "IRP_MJ_VOLUME_MOUNT" +#define IRP_MJ_VOLUME_DISMOUNT_STRING "IRP_MJ_VOLUME_DISMOUNT" + +// +// Strings for the Irp minor codes +// + +#define IRP_MN_QUERY_DIRECTORY_STRING "IRP_MN_QUERY_DIRECTORY" +#define IRP_MN_NOTIFY_CHANGE_DIRECTORY_STRING "IRP_MN_NOTIFY_CHANGE_DIRECTORY" +#define IRP_MN_USER_FS_REQUEST_STRING "IRP_MN_USER_FS_REQUEST" +#define IRP_MN_MOUNT_VOLUME_STRING "IRP_MN_MOUNT_VOLUME" +#define IRP_MN_VERIFY_VOLUME_STRING "IRP_MN_VERIFY_VOLUME" +#define IRP_MN_LOAD_FILE_SYSTEM_STRING "IRP_MN_LOAD_FILE_SYSTEM" +#define IRP_MN_TRACK_LINK_STRING "IRP_MN_TRACK_LINK" +#define IRP_MN_LOCK_STRING "IRP_MN_LOCK" +#define IRP_MN_UNLOCK_SINGLE_STRING "IRP_MN_UNLOCK_SINGLE" +#define IRP_MN_UNLOCK_ALL_STRING "IRP_MN_UNLOCK_ALL" +#define IRP_MN_UNLOCK_ALL_BY_KEY_STRING "IRP_MN_UNLOCK_ALL_BY_KEY" +#define IRP_MN_NORMAL_STRING "IRP_MN_NORMAL" +#define IRP_MN_DPC_STRING "IRP_MN_DPC" +#define IRP_MN_MDL_STRING "IRP_MN_MDL" +#define IRP_MN_COMPLETE_STRING "IRP_MN_COMPLETE" +#define IRP_MN_COMPRESSED_STRING "IRP_MN_COMPRESSED" +#define IRP_MN_MDL_DPC_STRING "IRP_MN_MDL_DPC" +#define IRP_MN_COMPLETE_MDL_STRING "IRP_MN_COMPLETE_MDL" +#define IRP_MN_COMPLETE_MDL_DPC_STRING "IRP_MN_COMPLETE_MDL_DPC" +#define IRP_MN_SCSI_CLASS_STRING "IRP_MN_SCSI_CLASS" +#define IRP_MN_START_DEVICE_STRING "IRP_MN_START_DEVICE" +#define IRP_MN_QUERY_REMOVE_DEVICE_STRING "IRP_MN_QUERY_REMOVE_DEVICE" +#define IRP_MN_REMOVE_DEVICE_STRING "IRP_MN_REMOVE_DEVICE" +#define IRP_MN_CANCEL_REMOVE_DEVICE_STRING "IRP_MN_CANCEL_REMOVE_DEVICE" +#define IRP_MN_STOP_DEVICE_STRING "IRP_MN_STOP_DEVICE" +#define IRP_MN_QUERY_STOP_DEVICE_STRING "IRP_MN_QUERY_STOP_DEVICE" +#define IRP_MN_CANCEL_STOP_DEVICE_STRING "IRP_MN_CANCEL_STOP_DEVICE" +#define IRP_MN_QUERY_DEVICE_RELATIONS_STRING "IRP_MN_QUERY_DEVICE_RELATIONS" +#define IRP_MN_QUERY_INTERFACE_STRING "IRP_MN_QUERY_INTERFACE" +#define IRP_MN_QUERY_CAPABILITIES_STRING "IRP_MN_QUERY_CAPABILITIES" +#define IRP_MN_QUERY_RESOURCES_STRING "IRP_MN_QUERY_RESOURCES" +#define IRP_MN_QUERY_RESOURCE_REQUIREMENTS_STRING "IRP_MN_QUERY_RESOURCE_REQUIREMENTS" +#define IRP_MN_QUERY_DEVICE_TEXT_STRING "IRP_MN_QUERY_DEVICE_TEXT" +#define IRP_MN_FILTER_RESOURCE_REQUIREMENTS_STRING "IRP_MN_FILTER_RESOURCE_REQUIREMENTS" +#define IRP_MN_READ_CONFIG_STRING "IRP_MN_READ_CONFIG" +#define IRP_MN_WRITE_CONFIG_STRING "IRP_MN_WRITE_CONFIG" +#define IRP_MN_EJECT_STRING "IRP_MN_EJECT" +#define IRP_MN_SET_LOCK_STRING "IRP_MN_SET_LOCK" +#define IRP_MN_QUERY_ID_STRING "IRP_MN_QUERY_ID" +#define IRP_MN_QUERY_PNP_DEVICE_STATE_STRING "IRP_MN_QUERY_PNP_DEVICE_STATE" +#define IRP_MN_QUERY_BUS_INFORMATION_STRING "IRP_MN_QUERY_BUS_INFORMATION" +#define IRP_MN_DEVICE_USAGE_NOTIFICATION_STRING "IRP_MN_DEVICE_USAGE_NOTIFICATION" +#define IRP_MN_SURPRISE_REMOVAL_STRING "IRP_MN_SURPRISE_REMOVAL" +#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION_STRING "IRP_MN_QUERY_LEGACY_BUS_INFORMATION" +#define IRP_MN_WAIT_WAKE_STRING "IRP_MN_WAIT_WAKE" +#define IRP_MN_POWER_SEQUENCE_STRING "IRP_MN_POWER_SEQUENCE" +#define IRP_MN_SET_POWER_STRING "IRP_MN_SET_POWER" +#define IRP_MN_QUERY_POWER_STRING "IRP_MN_QUERY_POWER" +#define IRP_MN_QUERY_ALL_DATA_STRING "IRP_MN_QUERY_ALL_DATA" +#define IRP_MN_QUERY_SINGLE_INSTANCE_STRING "IRP_MN_QUERY_SINGLE_INSTANCE" +#define IRP_MN_CHANGE_SINGLE_INSTANCE_STRING "IRP_MN_CHANGE_SINGLE_INSTANCE" +#define IRP_MN_CHANGE_SINGLE_ITEM_STRING "IRP_MN_CHANGE_SINGLE_ITEM" +#define IRP_MN_ENABLE_EVENTS_STRING "IRP_MN_ENABLE_EVENTS" +#define IRP_MN_DISABLE_EVENTS_STRING "IRP_MN_DISABLE_EVENTS" +#define IRP_MN_ENABLE_COLLECTION_STRING "IRP_MN_ENABLE_COLLECTION" +#define IRP_MN_DISABLE_COLLECTION_STRING "IRP_MN_DISABLE_COLLECTION" +#define IRP_MN_REGINFO_STRING "IRP_MN_REGINFO" +#define IRP_MN_EXECUTE_METHOD_STRING "IRP_MN_EXECUTE_METHOD" + +// +// Transaction notification string definitions. +// + +#define IRP_MJ_TRANSACTION_NOTIFY_STRING "IRP_MJ_TRANSACTION_NOTIFY" + +#define TRANSACTION_BEGIN "BEGIN_TRANSACTION" +#define TRANSACTION_NOTIFY_PREPREPARE_STRING "TRANSACTION_NOTIFY_PREPREPARE" +#define TRANSACTION_NOTIFY_PREPARE_STRING "TRANSACTION_NOTIFY_PREPARE" +#define TRANSACTION_NOTIFY_COMMIT_STRING "TRANSACTION_NOTIFY_COMMIT" +#define TRANSACTION_NOTIFY_ROLLBACK_STRING "TRANSACTION_NOTIFY_ROLLBACK" +#define TRANSACTION_NOTIFY_PREPREPARE_COMPLETE_STRING "TRANSACTION_NOTIFY_PREPREPARE_COMPLETE" +#define TRANSACTION_NOTIFY_PREPARE_COMPLETE_STRING "TRANSACTION_NOTIFY_PREPARE_COMPLETE" +#define TRANSACTION_NOTIFY_COMMIT_COMPLETE_STRING "TRANSACTION_NOTIFY_COMMIT_COMPLETE" +#define TRANSACTION_NOTIFY_COMMIT_FINALIZE_STRING "TRANSACTION_NOTIFY_COMMIT_FINALIZE" +#define TRANSACTION_NOTIFY_ROLLBACK_COMPLETE_STRING "TRANSACTION_NOTIFY_ROLLBACK_COMPLETE" +#define TRANSACTION_NOTIFY_RECOVER_STRING "TRANSACTION_NOTIFY_RECOVER" +#define TRANSACTION_NOTIFY_SINGLE_PHASE_COMMIT_STRING "TRANSACTION_NOTIFY_SINGLE_PHASE_COMMIT" +#define TRANSACTION_NOTIFY_DELEGATE_COMMIT_STRING "TRANSACTION_NOTIFY_DELEGATE_COMMIT" +#define TRANSACTION_NOTIFY_RECOVER_QUERY_STRING "TRANSACTION_NOTIFY_RECOVER_QUERY" +#define TRANSACTION_NOTIFY_ENLIST_PREPREPARE_STRING "TRANSACTION_NOTIFY_ENLIST_PREPREPARE" +#define TRANSACTION_NOTIFY_LAST_RECOVER_STRING "TRANSACTION_NOTIFY_LAST_RECOVER" +#define TRANSACTION_NOTIFY_INDOUBT_STRING "TRANSACTION_NOTIFY_INDOUBT" +#define TRANSACTION_NOTIFY_PROPAGATE_PULL_STRING "TRANSACTION_NOTIFY_PROPAGATE_PULL" +#define TRANSACTION_NOTIFY_PROPAGATE_PUSH_STRING "TRANSACTION_NOTIFY_PROPAGATE_PUSH" +#define TRANSACTION_NOTIFY_MARSHAL_STRING "TRANSACTION_NOTIFY_MARSHAL" +#define TRANSACTION_NOTIFY_ENLIST_MASK_STRING "TRANSACTION_NOTIFY_ENLIST_MASK" + + +// +// FltMgr's IRP major codes +// + +#define IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION ((UCHAR)-1) +#define IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION ((UCHAR)-2) +#define IRP_MJ_ACQUIRE_FOR_MOD_WRITE ((UCHAR)-3) +#define IRP_MJ_RELEASE_FOR_MOD_WRITE ((UCHAR)-4) +#define IRP_MJ_ACQUIRE_FOR_CC_FLUSH ((UCHAR)-5) +#define IRP_MJ_RELEASE_FOR_CC_FLUSH ((UCHAR)-6) +#define IRP_MJ_NOTIFY_STREAM_FO_CREATION ((UCHAR)-7) + +#define IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE ((UCHAR)-13) +#define IRP_MJ_NETWORK_QUERY_OPEN ((UCHAR)-14) +#define IRP_MJ_MDL_READ ((UCHAR)-15) +#define IRP_MJ_MDL_READ_COMPLETE ((UCHAR)-16) +#define IRP_MJ_PREPARE_MDL_WRITE ((UCHAR)-17) +#define IRP_MJ_MDL_WRITE_COMPLETE ((UCHAR)-18) +#define IRP_MJ_VOLUME_MOUNT ((UCHAR)-19) +#define IRP_MJ_VOLUME_DISMOUNT ((UCHAR)-20) + + +typedef enum { + TRANSACTION_NOTIFY_PREPREPARE_CODE = 1, + TRANSACTION_NOTIFY_PREPARE_CODE, + TRANSACTION_NOTIFY_COMMIT_CODE, + TRANSACTION_NOTIFY_ROLLBACK_CODE, + TRANSACTION_NOTIFY_PREPREPARE_COMPLETE_CODE, + TRANSACTION_NOTIFY_PREPARE_COMPLETE_CODE, + TRANSACTION_NOTIFY_COMMIT_COMPLETE_CODE, + TRANSACTION_NOTIFY_ROLLBACK_COMPLETE_CODE, + TRANSACTION_NOTIFY_RECOVER_CODE, + TRANSACTION_NOTIFY_SINGLE_PHASE_COMMIT_CODE, + TRANSACTION_NOTIFY_DELEGATE_COMMIT_CODE, + TRANSACTION_NOTIFY_RECOVER_QUERY_CODE, + TRANSACTION_NOTIFY_ENLIST_PREPREPARE_CODE, + TRANSACTION_NOTIFY_LAST_RECOVER_CODE, + TRANSACTION_NOTIFY_INDOUBT_CODE, + TRANSACTION_NOTIFY_PROPAGATE_PULL_CODE, + TRANSACTION_NOTIFY_PROPAGATE_PUSH_CODE, + TRANSACTION_NOTIFY_MARSHAL_CODE, + TRANSACTION_NOTIFY_ENLIST_MASK_CODE, + TRANSACTION_NOTIFY_COMMIT_FINALIZE_CODE = 31 +} TRANSACTION_NOTIFICATION_CODES; + +// +// Standard IRP Major codes +// + +#define IRP_MJ_CREATE 0x00 +#define IRP_MJ_CREATE_NAMED_PIPE 0x01 +#define IRP_MJ_CLOSE 0x02 +#define IRP_MJ_READ 0x03 +#define IRP_MJ_WRITE 0x04 +#define IRP_MJ_QUERY_INFORMATION 0x05 +#define IRP_MJ_SET_INFORMATION 0x06 +#define IRP_MJ_QUERY_EA 0x07 +#define IRP_MJ_SET_EA 0x08 +#define IRP_MJ_FLUSH_BUFFERS 0x09 +#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a +#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b +#define IRP_MJ_DIRECTORY_CONTROL 0x0c +#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d +#define IRP_MJ_DEVICE_CONTROL 0x0e +#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f +#define IRP_MJ_SHUTDOWN 0x10 +#define IRP_MJ_LOCK_CONTROL 0x11 +#define IRP_MJ_CLEANUP 0x12 +#define IRP_MJ_CREATE_MAILSLOT 0x13 +#define IRP_MJ_QUERY_SECURITY 0x14 +#define IRP_MJ_SET_SECURITY 0x15 +#define IRP_MJ_POWER 0x16 +#define IRP_MJ_SYSTEM_CONTROL 0x17 +#define IRP_MJ_DEVICE_CHANGE 0x18 +#define IRP_MJ_QUERY_QUOTA 0x19 +#define IRP_MJ_SET_QUOTA 0x1a +#define IRP_MJ_PNP 0x1b +#define IRP_MJ_MAXIMUM_FUNCTION 0x1b + +// +// IRP minor codes +// + +#define IRP_MN_QUERY_DIRECTORY 0x01 +#define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x02 +#define IRP_MN_USER_FS_REQUEST 0x00 +#define IRP_MN_MOUNT_VOLUME 0x01 +#define IRP_MN_VERIFY_VOLUME 0x02 +#define IRP_MN_LOAD_FILE_SYSTEM 0x03 +#define IRP_MN_TRACK_LINK 0x04 +#define IRP_MN_LOCK 0x01 +#define IRP_MN_UNLOCK_SINGLE 0x02 +#define IRP_MN_UNLOCK_ALL 0x03 +#define IRP_MN_UNLOCK_ALL_BY_KEY 0x04 +#define IRP_MN_NORMAL 0x00 +#define IRP_MN_DPC 0x01 +#define IRP_MN_MDL 0x02 +#define IRP_MN_COMPLETE 0x04 +#define IRP_MN_COMPRESSED 0x08 +#define IRP_MN_MDL_DPC (IRP_MN_MDL | IRP_MN_DPC) +#define IRP_MN_COMPLETE_MDL (IRP_MN_COMPLETE | IRP_MN_MDL) +#define IRP_MN_COMPLETE_MDL_DPC (IRP_MN_COMPLETE_MDL | IRP_MN_DPC) +#define IRP_MN_SCSI_CLASS 0x01 +#define IRP_MN_START_DEVICE 0x00 +#define IRP_MN_QUERY_REMOVE_DEVICE 0x01 +#define IRP_MN_REMOVE_DEVICE 0x02 +#define IRP_MN_CANCEL_REMOVE_DEVICE 0x03 +#define IRP_MN_STOP_DEVICE 0x04 +#define IRP_MN_QUERY_STOP_DEVICE 0x05 +#define IRP_MN_CANCEL_STOP_DEVICE 0x06 +#define IRP_MN_QUERY_DEVICE_RELATIONS 0x07 +#define IRP_MN_QUERY_INTERFACE 0x08 +#define IRP_MN_QUERY_CAPABILITIES 0x09 +#define IRP_MN_QUERY_RESOURCES 0x0A +#define IRP_MN_QUERY_RESOURCE_REQUIREMENTS 0x0B +#define IRP_MN_QUERY_DEVICE_TEXT 0x0C +#define IRP_MN_FILTER_RESOURCE_REQUIREMENTS 0x0D +#define IRP_MN_READ_CONFIG 0x0F +#define IRP_MN_WRITE_CONFIG 0x10 +#define IRP_MN_EJECT 0x11 +#define IRP_MN_SET_LOCK 0x12 +#define IRP_MN_QUERY_ID 0x13 +#define IRP_MN_QUERY_PNP_DEVICE_STATE 0x14 +#define IRP_MN_QUERY_BUS_INFORMATION 0x15 +#define IRP_MN_DEVICE_USAGE_NOTIFICATION 0x16 +#define IRP_MN_SURPRISE_REMOVAL 0x17 +#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18 +#define IRP_MN_WAIT_WAKE 0x00 +#define IRP_MN_POWER_SEQUENCE 0x01 +#define IRP_MN_SET_POWER 0x02 +#define IRP_MN_QUERY_POWER 0x03 +#define IRP_MN_QUERY_ALL_DATA 0x00 +#define IRP_MN_QUERY_SINGLE_INSTANCE 0x01 +#define IRP_MN_CHANGE_SINGLE_INSTANCE 0x02 +#define IRP_MN_CHANGE_SINGLE_ITEM 0x03 +#define IRP_MN_ENABLE_EVENTS 0x04 +#define IRP_MN_DISABLE_EVENTS 0x05 +#define IRP_MN_ENABLE_COLLECTION 0x06 +#define IRP_MN_DISABLE_COLLECTION 0x07 +#define IRP_MN_REGINFO 0x08 +#define IRP_MN_EXECUTE_METHOD 0x09 + +// +// IRP Flags +// + +#define IRP_NOCACHE 0x00000001 +#define IRP_PAGING_IO 0x00000002 +#define IRP_SYNCHRONOUS_API 0x00000004 +#define IRP_SYNCHRONOUS_PAGING_IO 0x00000040 + +// +// Define the FLT_TAG_DATA structure so that we can display it. +// + +#pragma warning(push) +#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union + +typedef struct _FLT_TAG_DATA_BUFFER { + ULONG FileTag; + USHORT TagDataLength; + USHORT UnparsedNameLength; + union { + GUID TagGuid; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} FLT_TAG_DATA_BUFFER, *PFLT_TAG_DATA_BUFFER; + +#pragma warning(pop) + +#endif //__MSPYLOG_H__ + diff --git a/filesys/miniFilter/minispy/user/mspyUser.c b/filesys/miniFilter/minispy/user/mspyUser.c new file mode 100644 index 00000000..e5f5e2f5 --- /dev/null +++ b/filesys/miniFilter/minispy/user/mspyUser.c @@ -0,0 +1,979 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + mspyUser.c + +Abstract: + + This file contains the implementation for the main function of the + user application piece of MiniSpy. This function is responsible for + controlling the command mode available to the user to control the + kernel mode driver. + +Environment: + + User mode + +--*/ + +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include +#include +#include +#include +#include "mspyLog.h" +#include + +#define SUCCESS 0 +#define USAGE_ERROR 1 +#define EXIT_INTERPRETER 2 +#define EXIT_PROGRAM 4 + +#define INTERPRETER_EXIT_COMMAND1 "go" +#define INTERPRETER_EXIT_COMMAND2 "g" +#define PROGRAM_EXIT_COMMAND "exit" +#define CMDLINE_SIZE 256 +#define NUM_PARAMS 40 + +#define MINISPY_NAME L"MiniSpy" + +DWORD +InterpretCommand ( + _In_ int argc, + _In_reads_(argc) char *argv[], + _In_ PLOG_CONTEXT Context + ); + +VOID +ListDevices ( + VOID + ); + +VOID +DisplayError ( + _In_ DWORD Code + ) + +/*++ + +Routine Description: + + This routine will display an error message based off of the Win32 error + code that is passed in. This allows the user to see an understandable + error message instead of just the code. + +Arguments: + + Code - The error code to be translated. + +Return Value: + + None. + +--*/ + +{ + WCHAR buffer[MAX_PATH] = { 0 }; + DWORD count; + HMODULE module = NULL; + HRESULT status; + + count = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + Code, + 0, + buffer, + sizeof(buffer) / sizeof(WCHAR), + NULL); + + + if (count == 0) { + + count = GetSystemDirectory( buffer, + sizeof(buffer) / sizeof( WCHAR ) ); + + if (count==0 || count > sizeof(buffer) / sizeof( WCHAR )) { + + // + // In practice we expect buffer to be large enough to hold the + // system directory path. + // + + printf(" Could not translate error: %d\n", Code); + return; + } + + + status = StringCchCat( buffer, + sizeof(buffer) / sizeof( WCHAR ), + L"\\fltlib.dll" ); + + if (status != S_OK) { + + printf(" Could not translate error: %d\n", Code); + return; + } + + module = LoadLibraryExW( buffer, NULL, LOAD_LIBRARY_AS_DATAFILE ); + + // + // Translate the Win32 error code into a useful message. + // + + count = FormatMessage (FORMAT_MESSAGE_FROM_HMODULE, + module, + Code, + 0, + buffer, + sizeof(buffer) / sizeof(WCHAR), + NULL); + + if (module != NULL) { + + FreeLibrary( module ); + } + + // + // If we still couldn't resolve the message, generate a string + // + + if (count == 0) { + + printf(" Could not translate error: %d\n", Code); + return; + } + } + + // + // Display the translated error. + // + + printf(" %ws\n", buffer); +} + +// +// Main uses a loop which has an assignment in the while +// conditional statement. Suppress the compiler's warning. +// + +#pragma warning(push) +#pragma warning(disable:4706) // assignment within conditional expression + +int _cdecl +main ( + _In_ int argc, + _In_reads_(argc) char *argv[] + ) +/*++ + +Routine Description: + + Main routine for minispy + +Arguments: + +Return Value: + +--*/ +{ + HANDLE port = INVALID_HANDLE_VALUE; + HRESULT hResult = S_OK; + DWORD result; + ULONG threadId; + HANDLE thread = NULL; + LOG_CONTEXT context; + CHAR inputChar; + + // + // Initialize handle in case of error + // + + context.ShutDown = NULL; + + // + // Open the port that is used to talk to + // MiniSpy. + // + + printf( "Connecting to filter's port...\n" ); + + hResult = FilterConnectCommunicationPort( MINISPY_PORT_NAME, + 0, + NULL, + 0, + NULL, + &port ); + + if (IS_ERROR( hResult )) { + + printf( "Could not connect to filter: 0x%08x\n", hResult ); + DisplayError( hResult ); + goto Main_Exit; + } + + // + // Initialize the fields of the LOG_CONTEXT + // + + context.Port = port; + context.ShutDown = CreateSemaphore( NULL, + 0, + 1, + L"MiniSpy shut down" ); + context.CleaningUp = FALSE; + context.LogToFile = FALSE; + context.LogToScreen = FALSE; //don't start logging yet + context.NextLogToScreen = TRUE; + context.OutputFile = NULL; + + if (context.ShutDown == NULL) { + + result = GetLastError(); + printf( "Could not create semaphore: %d\n", result ); + DisplayError( result ); + goto Main_Exit; + } + + // + // Check the valid parameters for startup + // + + if (argc > 1) { + + if (InterpretCommand( argc - 1, &(argv[1]), &context ) == USAGE_ERROR) { + + goto Main_Exit; + } + } + + // + // Create the thread to read the log records that are gathered + // by MiniSpy.sys. + // + printf( "Creating logging thread...\n" ); + thread = CreateThread( NULL, + 0, + RetrieveLogRecords, + (LPVOID)&context, + 0, + &threadId); + + if (!thread) { + + result = GetLastError(); + printf( "Could not create logging thread: %d\n", result ); + DisplayError( result ); + goto Main_Exit; + } + + // + // Check to see what devices we are attached to from + // previous runs of this program. + // + + ListDevices(); + + // + // Process commands from the user + // + + printf( "\nHit [Enter] to begin command mode...\n\n" ); + fflush( stdout ); + + // + // set screen logging state + // + + context.LogToScreen = context.NextLogToScreen; + + while (inputChar = (CHAR)getchar()) { + + CHAR *parms[NUM_PARAMS]; + CHAR commandLine[CMDLINE_SIZE+1]; + INT parmCount, count; + DWORD returnValue = SUCCESS; + BOOL newParm; + CHAR ch; + + if (inputChar == '\n') { + + // + // Start command interpreter. First we must turn off logging + // to screen if we are. Also, remember the state of logging + // to the screen, so that we can reinstate that when command + // interpreter is finished. + // + + context.NextLogToScreen = context.LogToScreen; + context.LogToScreen = FALSE; + + while (returnValue != EXIT_INTERPRETER) { + + // + // Print prompt + // + printf( ">" ); + + // + // Read in next line, keeping track of the number of parameters + // as we go. + // + + parmCount = 0; + newParm = TRUE; + for ( count = 0; + (count < CMDLINE_SIZE) && ((ch = (CHAR)getchar()) != '\n'); + count++) + { + commandLine[count] = ch; + + if (newParm && (ch != ' ')) { + + parms[parmCount++] = &commandLine[count]; + } + + if (parmCount >= NUM_PARAMS) { + + break; + } + + // + // Always insert NULL's for spaces + // + + if (ch == ' ') { + + newParm = TRUE; + commandLine[count] = 0; + + } else { + + newParm = FALSE; + } + } + + commandLine[count] = '\0'; + + if (parmCount == 0) { + + continue; + } + + // + // We've got our parameter count and parameter list, so + // send it off to be interpreted. + // + + returnValue = InterpretCommand( parmCount, parms, &context ); + + if (returnValue == EXIT_PROGRAM) { + + // Time to stop the program + goto Main_Cleanup; + } + } + + // + // Set LogToScreen appropriately based on any commands seen + // + + context.LogToScreen = context.NextLogToScreen; + + if (context.LogToScreen) { + + printf( "Should be logging to screen...\n" ); + } + } + } + +Main_Cleanup: + + // + // Clean up the threads, then fall through to Main_Exit + // + + printf( "Cleaning up...\n" ); + + // + // Set the Cleaning up flag to TRUE to notify other threads + // that we are cleaning up + // + context.CleaningUp = TRUE; + + // + // Wait for everyone to shut down + // + + WaitForSingleObject( context.ShutDown, INFINITE ); + + if (context.LogToFile) { + + fclose( context.OutputFile ); + } + +Main_Exit: + + // + // Clean up the data that is always around and exit + // + + if(context.ShutDown) { + + CloseHandle( context.ShutDown ); + } + + if (thread) { + + CloseHandle( thread ); + } + + if (INVALID_HANDLE_VALUE != port) { + CloseHandle( port ); + } + return 0; +} + +#pragma warning(pop) + +DWORD +InterpretCommand ( + _In_ int argc, + _In_reads_(argc) char *argv[], + _In_ PLOG_CONTEXT Context + ) +/*++ + +Routine Description: + + Process options from the user + +Arguments: + +Return Value: + +--*/ +{ + LONG parmIndex; + PCHAR parm; + HRESULT hResult; + DWORD returnValue = SUCCESS; + CHAR buffer[BUFFER_SIZE]; + DWORD bufferLength; + PWCHAR instanceString; + WCHAR instanceName[INSTANCE_NAME_MAX_CHARS + 1]; + + // + // Interpret the command line parameters + // + for (parmIndex = 0; parmIndex < argc; parmIndex++) { + + parm = argv[parmIndex]; + + if (parm[0] == '/') { + + // + // Have the beginning of a switch + // + + switch (parm[1]) { + + case 'a': + case 'A': + + // + // Attach to the specified drive letter + // + + parmIndex++; + + if (parmIndex >= argc) { + + // + // Not enough parameters + // + + goto InterpretCommand_Usage; + } + + parm = argv[parmIndex]; + + printf( " Attaching to %s... ", parm ); + + bufferLength = MultiByteToWideChar( CP_ACP, + MB_ERR_INVALID_CHARS, + parm, + -1, + (LPWSTR)buffer, + BUFFER_SIZE/sizeof( WCHAR ) ); + + if (bufferLength == 0) { + + // + // We do not expect the user to provide a parm that + // causes buffer to overflow. + // + + goto InterpretCommand_Usage; + } + + hResult = FilterAttach( MINISPY_NAME, + (PWSTR)buffer, + NULL, // instance name + sizeof( instanceName ), + instanceName ); + + if (SUCCEEDED( hResult )) { + + printf( " Instance name: %S\n", instanceName ); + + } else { + + printf( "\n Could not attach to device: 0x%08x\n", hResult ); + DisplayError( hResult ); + returnValue = SUCCESS; + } + + break; + + case 'd': + case 'D': + + // + // Detach to the specified drive letter + // + + parmIndex++; + + if (parmIndex >= argc) { + + // + // Not enough parameters + // + + goto InterpretCommand_Usage; + } + + parm = argv[parmIndex]; + + printf( " Detaching from %s\n", parm ); + bufferLength = MultiByteToWideChar( CP_ACP, + MB_ERR_INVALID_CHARS, + parm, + -1, + (LPWSTR)buffer, + BUFFER_SIZE/sizeof( WCHAR ) ); + + if (bufferLength == 0) { + + // + // We do not expect the user to provide a parm that + // causes buffer to overflow. + // + + goto InterpretCommand_Usage; + } + + // + // Get the next argument to see if it is an InstanceId + // + + parmIndex++; + + if (parmIndex >= argc) { + + instanceString = NULL; + + } else { + + if (argv[parmIndex][0] == '/') { + + // + // This is just the next command, so don't + // internet it as the InstanceId. + // + + instanceString = NULL; + parmIndex--; + + } else { + + parm = argv[parmIndex]; + bufferLength = MultiByteToWideChar( CP_ACP, + MB_ERR_INVALID_CHARS, + parm, + -1, + (LPWSTR)instanceName, + sizeof( instanceName )/sizeof( WCHAR ) ); + + if (bufferLength == 0) { + + // + // We do not expect the user to provide a parm that + // causes buffer to overflow. + // + + goto InterpretCommand_Usage; + } + + instanceString = instanceName; + } + } + + // + // Detach from the volume and instance specified. + // + + hResult = FilterDetach( MINISPY_NAME, + (PWSTR)buffer, + instanceString ); + + if (IS_ERROR( hResult )) { + + printf( " Could not detach from device: 0x%08x\n", hResult ); + DisplayError( hResult ); + returnValue = SUCCESS; + } + break; + + case 'l': + case 'L': + + // + // List all devices that are currently being monitored + // + + ListDevices(); + break; + + case 's': + case 'S': + + // + // Output logging results to screen, save new value to + // instate when command interpreter is exited. + // + if (Context->NextLogToScreen) { + + printf( " Turning off logging to screen\n" ); + + } else { + + printf( " Turning on logging to screen\n" ); + } + + Context->NextLogToScreen = !Context->NextLogToScreen; + break; + + case 'f': + case 'F': + + // + // Output logging results to file + // + + if (Context->LogToFile) { + + printf( " Stop logging to file \n" ); + Context->LogToFile = FALSE; + assert( Context->OutputFile ); + _Analysis_assume_( Context->OutputFile != NULL ); + fclose( Context->OutputFile ); + Context->OutputFile = NULL; + + } else { + + parmIndex++; + + if (parmIndex >= argc) { + + // + // Not enough parameters + // + + goto InterpretCommand_Usage; + } + + parm = argv[parmIndex]; + printf( " Log to file %s\n", parm ); + + if (fopen_s( &Context->OutputFile, parm, "w" ) != 0 ) { + assert( Context->OutputFile ); + } + + Context->LogToFile = TRUE; + } + break; + + default: + + // + // Invalid switch, goto usage + // + goto InterpretCommand_Usage; + } + + } else { + + // + // Look for "go" or "g" to see if we should exit interpreter + // + + if (!_strnicmp( parm, + INTERPRETER_EXIT_COMMAND1, + sizeof( INTERPRETER_EXIT_COMMAND1 ))) { + + returnValue = EXIT_INTERPRETER; + goto InterpretCommand_Exit; + } + + if (!_strnicmp( parm, + INTERPRETER_EXIT_COMMAND2, + sizeof( INTERPRETER_EXIT_COMMAND2 ))) { + + returnValue = EXIT_INTERPRETER; + goto InterpretCommand_Exit; + } + + // + // Look for "exit" to see if we should exit program + // + + if (!_strnicmp( parm, + PROGRAM_EXIT_COMMAND, + sizeof( PROGRAM_EXIT_COMMAND ))) { + + returnValue = EXIT_PROGRAM; + goto InterpretCommand_Exit; + } + + // + // Invalid parameter + // + goto InterpretCommand_Usage; + } + } + +InterpretCommand_Exit: + return returnValue; + +InterpretCommand_Usage: + printf("Valid switches: [/a ] [/d ] [/l] [/s] [/f []]\n" + " [/a ] starts monitoring \n" + " [/d []] detaches filter from \n" + " [/l] lists all the drives the monitor is currently attached to\n" + " [/s] turns on and off showing logging output on the screen\n" + " [/f []] turns on and off logging to the specified file\n" + " If you are in command mode:\n" + " [enter] will enter command mode\n" + " [go|g] will exit command mode\n" + " [exit] will terminate this program\n" + ); + returnValue = USAGE_ERROR; + goto InterpretCommand_Exit; +} + + +ULONG +IsAttachedToVolume( + _In_ LPCWSTR VolumeName + ) +/*++ + +Routine Description: + + Determine if our filter is attached to this volume + +Arguments: + + VolumeName - The volume we are checking + +Return Value: + + TRUE - we are attached + FALSE - we are not attached (or we couldn't tell) + +--*/ +{ + PWCHAR filtername; + CHAR buffer[1024]; + PINSTANCE_FULL_INFORMATION data = (PINSTANCE_FULL_INFORMATION)buffer; + HANDLE volumeIterator = INVALID_HANDLE_VALUE; + ULONG bytesReturned; + ULONG instanceCount = 0; + HRESULT hResult; + + // + // Enumerate all instances on this volume + // + + hResult = FilterVolumeInstanceFindFirst( VolumeName, + InstanceFullInformation, + data, + sizeof(buffer)-sizeof(WCHAR), + &bytesReturned, + &volumeIterator ); + + if (IS_ERROR( hResult )) { + + return instanceCount; + } + + do { + + assert((data->FilterNameBufferOffset+data->FilterNameLength) <= (sizeof(buffer)-sizeof(WCHAR))); + _Analysis_assume_((data->FilterNameBufferOffset+data->FilterNameLength) <= (sizeof(buffer)-sizeof(WCHAR))); + + // + // Get the name. Note that we are NULL terminating the buffer + // in place. We can do this because we don't care about the other + // information and we have guaranteed that there is room for a NULL + // at the end of the buffer. + // + + + filtername = Add2Ptr(data,data->FilterNameBufferOffset); + filtername[data->FilterNameLength/sizeof( WCHAR )] = L'\0'; + + // + // Bump the instance count when we find a match + // + + if (_wcsicmp(filtername,MINISPY_NAME) == 0) { + + instanceCount++; + } + + } while (SUCCEEDED( FilterVolumeInstanceFindNext( volumeIterator, + InstanceFullInformation, + data, + sizeof(buffer)-sizeof(WCHAR), + &bytesReturned ) )); + + // + // Close the handle + // + + FilterVolumeInstanceFindClose( volumeIterator ); + return instanceCount; +} + + +void +ListDevices( + VOID + ) +/*++ + +Routine Description: + + Display the volumes we are attached to + +Arguments: + +Return Value: + +--*/ +{ + UCHAR buffer[1024]; + PFILTER_VOLUME_BASIC_INFORMATION volumeBuffer = (PFILTER_VOLUME_BASIC_INFORMATION)buffer; + HANDLE volumeIterator = INVALID_HANDLE_VALUE; + ULONG volumeBytesReturned; + HRESULT hResult = S_OK; + WCHAR driveLetter[15] = { 0 }; + ULONG instanceCount; + + try { + + // + // Find out size of buffer needed + // + + hResult = FilterVolumeFindFirst( FilterVolumeBasicInformation, + volumeBuffer, + sizeof(buffer)-sizeof(WCHAR), //save space to null terminate name + &volumeBytesReturned, + &volumeIterator ); + + if (IS_ERROR( hResult )) { + + leave; + } + + assert( INVALID_HANDLE_VALUE != volumeIterator ); + + // + // Output the header + // + + printf( "\n" + "Dos Name Volume Name Status \n" + "-------------- ------------------------------------ --------\n" ); + + // + // Loop through all of the filters, displaying instance information + // + + do { + + assert((FIELD_OFFSET(FILTER_VOLUME_BASIC_INFORMATION,FilterVolumeName) + volumeBuffer->FilterVolumeNameLength) <= (sizeof(buffer)-sizeof(WCHAR))); + _Analysis_assume_((FIELD_OFFSET(FILTER_VOLUME_BASIC_INFORMATION,FilterVolumeName) + volumeBuffer->FilterVolumeNameLength) <= (sizeof(buffer)-sizeof(WCHAR))); + + volumeBuffer->FilterVolumeName[volumeBuffer->FilterVolumeNameLength/sizeof( WCHAR )] = UNICODE_NULL; + + instanceCount = IsAttachedToVolume(volumeBuffer->FilterVolumeName); + + printf( "%-14ws %-36ws %s", + (SUCCEEDED( FilterGetDosName( + volumeBuffer->FilterVolumeName, + driveLetter, + sizeof(driveLetter)/sizeof(WCHAR) )) ? driveLetter : L""), + volumeBuffer->FilterVolumeName, + (instanceCount > 0) ? "Attached" : ""); + + if (instanceCount > 1) { + + printf( " (%d)\n", instanceCount ); + + } else { + + printf( "\n" ); + } + + } while (SUCCEEDED( hResult = FilterVolumeFindNext( volumeIterator, + FilterVolumeBasicInformation, + volumeBuffer, + sizeof(buffer)-sizeof(WCHAR), //save space to null terminate name + &volumeBytesReturned ) )); + + if (HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS ) == hResult) { + + hResult = S_OK; + } + + } finally { + + if (INVALID_HANDLE_VALUE != volumeIterator) { + + FilterVolumeFindClose( volumeIterator ); + } + + if (IS_ERROR( hResult )) { + + if (HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS ) == hResult) { + + printf( "No volumes found.\n" ); + + } else { + + printf( "Volume listing failed with error: 0x%08x\n", + hResult ); + } + } + } +} + diff --git a/filesys/miniFilter/minispy/user/mspyUser.rc b/filesys/miniFilter/minispy/user/mspyUser.rc new file mode 100644 index 00000000..3fcef6be --- /dev/null +++ b/filesys/miniFilter/minispy/user/mspyUser.rc @@ -0,0 +1,10 @@ +#include +#include + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "MiniSpy Control Program" +#define VER_INTERNALNAME_STR "MiniSpy.exe" +#define VER_ORIGINALFILENAME_STR "MiniSpy.exe" + +#include "common.ver" diff --git a/filesys/miniFilter/nullFilter/ReadMe.md b/filesys/miniFilter/nullFilter/ReadMe.md new file mode 100644 index 00000000..376618e7 --- /dev/null +++ b/filesys/miniFilter/nullFilter/ReadMe.md @@ -0,0 +1,15 @@ +NullFilter File System Minifilter Driver +======================================== + +The NullFilter minifilter is a sample minifilter that shows how to register a minifilter with the filter manager. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *NullFilter* minifilter is a simple minifilter that registers itself with the filter manager for no callback operations. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/nullFilter/nullFilter.c b/filesys/miniFilter/nullFilter/nullFilter.c new file mode 100644 index 00000000..9944db13 --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.c @@ -0,0 +1,240 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + nullFilter.c + +Abstract: + + This is the main module of the nullFilter mini filter driver. + It is a simple minifilter that registers itself with the main filter + for no callback operations. + +Environment: + + Kernel mode + +--*/ + +#include +#include +#include + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +//--------------------------------------------------------------------------- +// Global variables +//--------------------------------------------------------------------------- + +#define NULL_FILTER_FILTER_NAME L"NullFilter" + +typedef struct _NULL_FILTER_DATA { + + // + // The filter handle that results from a call to + // FltRegisterFilter. + // + + PFLT_FILTER FilterHandle; + +} NULL_FILTER_DATA, *PNULL_FILTER_DATA; + + +/************************************************************************* + Prototypes for the startup and unload routines used for + this Filter. + + Implementation in nullFilter.c +*************************************************************************/ + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +NullUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +NullQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +// +// Structure that contains all the global data structures +// used throughout NullFilter. +// + +NULL_FILTER_DATA NullFilterData; + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, NullUnload) +#pragma alloc_text(PAGE, NullQueryTeardown) +#endif + + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + NULL, // Context + NULL, // Operation callbacks + + NullUnload, // FilterUnload + + NULL, // InstanceSetup + NullQueryTeardown, // InstanceQueryTeardown + NULL, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL // NormalizeNameComponent + +}; + + +/************************************************************************* + Filter initialization and unload routines. +*************************************************************************/ + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers the miniFilter with FltMgr and initializes all + its global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( RegistryPath ); + + // + // Register with FltMgr + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &NullFilterData.FilterHandle ); + + FLT_ASSERT( NT_SUCCESS( status ) ); + + if (NT_SUCCESS( status )) { + + // + // Start filtering i/o + // + + status = FltStartFiltering( NullFilterData.FilterHandle ); + + if (!NT_SUCCESS( status )) { + FltUnregisterFilter( NullFilterData.FilterHandle ); + } + } + return status; +} + +NTSTATUS +NullUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter driver. This is called + when the minifilter is about to be unloaded. We can fail this unload + request if this is not a mandatory unloaded indicated by the Flags + parameter. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + FltUnregisterFilter( NullFilterData.FilterHandle ); + + return STATUS_SUCCESS; +} + +NTSTATUS +NullQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the instance detach routine for this miniFilter driver. + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach thereby giving us a + chance to fail that detach request. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Returns the status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + return STATUS_SUCCESS; +} + diff --git a/filesys/miniFilter/nullFilter/nullFilter.inf b/filesys/miniFilter/nullFilter/nullFilter.inf new file mode 100644 index 00000000..e1cb7db1 --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.inf @@ -0,0 +1,95 @@ +;;; +;;; NullFilter +;;; +;;; +;;; Copyright (c) 1999 - 2002, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.0 +CatalogFile = nullfilter.cat + + +[DestinationDirs] +DefaultDestDir = 12 +NullFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = NullFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,NullFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = NullFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[NullFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = NullFilter.AddRegistry + +; +; Registry Modifications +; + +[NullFilter.AddRegistry] +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[NullFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +nullfilter.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "NullFilter mini-filter driver" +ServiceName = "NullFilter" +DriverName = "NullFilter" +DiskId1 = "NullFilter Device Installation Disk" + +;Instances specific information. +DefaultInstance = "Null Instance" +Instance1.Name = "Null Instance" +Instance1.Altitude = "370020" +Instance1.Flags = 0x1 ; Suppress automatic attachments diff --git a/filesys/miniFilter/nullFilter/nullFilter.rc b/filesys/miniFilter/nullFilter/nullFilter.rc new file mode 100644 index 00000000..79355bf3 --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Null filter" +#define VER_INTERNALNAME_STR "nullFilter.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/nullFilter/nullFilter.sln b/filesys/miniFilter/nullFilter/nullFilter.sln new file mode 100644 index 00000000..9af52d43 --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nullFilter", "nullFilter.vcxproj", "{83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|Win32.ActiveCfg = Debug|Win32 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|Win32.Build.0 = Debug|Win32 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|Win32.ActiveCfg = Release|Win32 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|Win32.Build.0 = Release|Win32 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|x64.ActiveCfg = Debug|x64 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|x64.Build.0 = Debug|x64 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|x64.ActiveCfg = Release|x64 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/nullFilter/nullFilter.vcxproj b/filesys/miniFilter/nullFilter/nullFilter.vcxproj new file mode 100644 index 00000000..7e24749f --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5} + $(MSBuildProjectName) + Debug + Win32 + {6431C66E-F38F-45F1-BE1E-D06503CE7799} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + nullFilter + + + nullFilter + + + nullFilter + + + nullFilter + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters b/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters new file mode 100644 index 00000000..a199c829 --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {AC14DB33-6BC1-44EC-AAB1-816BA496A388} + + + h;hpp;hxx;hm;inl;inc;xsd + {F9EA13FF-7409-4FCD-B877-4F877D4E24BF} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {365A22DE-D53C-4640-8237-5E79E158A537} + + + inf;inv;inx;mof;mc; + {8F416348-B401-4C11-9DD0-4D79812667AD} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/passThrough/ReadMe.md b/filesys/miniFilter/passThrough/ReadMe.md new file mode 100644 index 00000000..cccc85de --- /dev/null +++ b/filesys/miniFilter/passThrough/ReadMe.md @@ -0,0 +1,15 @@ +PassThrough File System Minifilter Driver +========================================= + +The PassThrough minifilter demonstrates how to specify callback functions for different types of I/O requests. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *PassThrough* minifilter does not have any real functionality. For each type of I/O operation, the same pre and post callback functions are called. These callback functions simply forward the I/O request to the next filter on the stack. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/passThrough/passThrough.c b/filesys/miniFilter/passThrough/passThrough.c new file mode 100644 index 00000000..584fbe37 --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.c @@ -0,0 +1,887 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + passThrough.c + +Abstract: + + This is the main module of the passThrough miniFilter driver. + This filter hooks all IO operations for both pre and post operation + callbacks. The filter passes through the operations. + +Environment: + + Kernel mode + +--*/ + +#include +#include +#include + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + + +PFLT_FILTER gFilterHandle; +ULONG_PTR OperationStatusCtx = 1; + +#define PTDBG_TRACE_ROUTINES 0x00000001 +#define PTDBG_TRACE_OPERATION_STATUS 0x00000002 + +ULONG gTraceFlags = 0; + + +#define PT_DBG_PRINT( _dbgLevel, _string ) \ + (FlagOn(gTraceFlags,(_dbgLevel)) ? \ + DbgPrint _string : \ + ((int)0)) + +/************************************************************************* + Prototypes +*************************************************************************/ + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +PtInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +VOID +PtInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +PtInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +NTSTATUS +PtUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +PtInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +PtPreOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +VOID +PtOperationStatusCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_IO_PARAMETER_BLOCK ParameterSnapshot, + _In_ NTSTATUS OperationStatus, + _In_ PVOID RequesterContext + ); + +FLT_POSTOP_CALLBACK_STATUS +PtPostOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +PtPreOperationNoPostOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +BOOLEAN +PtDoRequestOperationStatus( + _In_ PFLT_CALLBACK_DATA Data + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, PtUnload) +#pragma alloc_text(PAGE, PtInstanceQueryTeardown) +#pragma alloc_text(PAGE, PtInstanceSetup) +#pragma alloc_text(PAGE, PtInstanceTeardownStart) +#pragma alloc_text(PAGE, PtInstanceTeardownComplete) +#endif + +// +// operation registration +// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_CREATE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_CREATE_NAMED_PIPE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_CLOSE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_READ, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_WRITE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_QUERY_INFORMATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SET_INFORMATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_QUERY_EA, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SET_EA, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_FLUSH_BUFFERS, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_QUERY_VOLUME_INFORMATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SET_VOLUME_INFORMATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_DIRECTORY_CONTROL, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_DEVICE_CONTROL, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_INTERNAL_DEVICE_CONTROL, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SHUTDOWN, + 0, + PtPreOperationNoPostOperationPassThrough, + NULL }, //post operations not supported + + { IRP_MJ_LOCK_CONTROL, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_CLEANUP, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_CREATE_MAILSLOT, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_QUERY_SECURITY, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SET_SECURITY, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_QUERY_QUOTA, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SET_QUOTA, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_PNP, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_ACQUIRE_FOR_MOD_WRITE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_RELEASE_FOR_MOD_WRITE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_ACQUIRE_FOR_CC_FLUSH, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_RELEASE_FOR_CC_FLUSH, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_NETWORK_QUERY_OPEN, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_MDL_READ, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_MDL_READ_COMPLETE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_PREPARE_MDL_WRITE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_MDL_WRITE_COMPLETE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_VOLUME_MOUNT, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_VOLUME_DISMOUNT, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_OPERATION_END } +}; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + NULL, // Context + Callbacks, // Operation callbacks + + PtUnload, // MiniFilterUnload + + PtInstanceSetup, // InstanceSetup + PtInstanceQueryTeardown, // InstanceQueryTeardown + PtInstanceTeardownStart, // InstanceTeardownStart + PtInstanceTeardownComplete, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL // NormalizeNameComponent + +}; + + + +NTSTATUS +PtInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. This + gives us a chance to decide if we need to attach to this volume or not. + + If this routine is not defined in the registration structure, automatic + instances are alwasys created. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtInstanceSetup: Entered\n") ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +PtInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach thereby giving us a + chance to fail that detach request. + + If this routine is not defined in the registration structure, explicit + detach requests via FltDetachVolume or FilterDetach will always be + failed. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Returns the status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtInstanceQueryTeardown: Entered\n") ); + + return STATUS_SUCCESS; +} + + +VOID +PtInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the start of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtInstanceTeardownStart: Entered\n") ); +} + + +VOID +PtInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the end of instance teardown. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Reason why this instance is been deleted. + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtInstanceTeardownComplete: Entered\n") ); +} + + +/************************************************************************* + MiniFilter initialization and unload routines. +*************************************************************************/ + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers with FltMgr and initializes all global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( RegistryPath ); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!DriverEntry: Entered\n") ); + + // + // Register with FltMgr to tell it our callback routines + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &gFilterHandle ); + + FLT_ASSERT( NT_SUCCESS( status ) ); + + if (NT_SUCCESS( status )) { + + // + // Start filtering i/o + // + + status = FltStartFiltering( gFilterHandle ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( gFilterHandle ); + } + } + + return status; +} + +NTSTATUS +PtUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter driver. This is called + when the minifilter is about to be unloaded. We can fail this unload + request if this is not a mandatory unloaded indicated by the Flags + parameter. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtUnload: Entered\n") ); + + FltUnregisterFilter( gFilterHandle ); + + return STATUS_SUCCESS; +} + + +/************************************************************************* + MiniFilter callback routines. +*************************************************************************/ +FLT_PREOP_CALLBACK_STATUS +PtPreOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the main pre-operation dispatch routine for this + miniFilter. Since this is just a simple passThrough miniFilter it + does not do anything with the callbackData but rather return + FLT_PREOP_SUCCESS_WITH_CALLBACK thereby passing it down to the next + miniFilter in the chain. + + This is non-pageable because it could be called on the paging path + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtPreOperationPassThrough: Entered\n") ); + + // + // See if this is an operation we would like the operation status + // for. If so request it. + // + // NOTE: most filters do NOT need to do this. You only need to make + // this call if, for example, you need to know if the oplock was + // actually granted. + // + + if (PtDoRequestOperationStatus( Data )) { + + status = FltRequestOperationStatusCallback( Data, + PtOperationStatusCallback, + (PVOID)(++OperationStatusCtx) ); + if (!NT_SUCCESS(status)) { + + PT_DBG_PRINT( PTDBG_TRACE_OPERATION_STATUS, + ("PassThrough!PtPreOperationPassThrough: FltRequestOperationStatusCallback Failed, status=%08x\n", + status) ); + } + } + + return FLT_PREOP_SUCCESS_WITH_CALLBACK; +} + + + +VOID +PtOperationStatusCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_IO_PARAMETER_BLOCK ParameterSnapshot, + _In_ NTSTATUS OperationStatus, + _In_ PVOID RequesterContext + ) +/*++ + +Routine Description: + + This routine is called when the given operation returns from the call + to IoCallDriver. This is useful for operations where STATUS_PENDING + means the operation was successfully queued. This is useful for OpLocks + and directory change notification operations. + + This callback is called in the context of the originating thread and will + never be called at DPC level. The file object has been correctly + referenced so that you can access it. It will be automatically + dereferenced upon return. + + This is non-pageable because it could be called on the paging path + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + RequesterContext - The context for the completion routine for this + operation. + + OperationStatus - + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtOperationStatusCallback: Entered\n") ); + + PT_DBG_PRINT( PTDBG_TRACE_OPERATION_STATUS, + ("PassThrough!PtOperationStatusCallback: Status=%08x ctx=%p IrpMj=%02x.%02x \"%s\"\n", + OperationStatus, + RequesterContext, + ParameterSnapshot->MajorFunction, + ParameterSnapshot->MinorFunction, + FltGetIrpName(ParameterSnapshot->MajorFunction)) ); +} + + +FLT_POSTOP_CALLBACK_STATUS +PtPostOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-operation completion routine for this + miniFilter. + + This is non-pageable because it may be called at DPC level. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The completion context set in the pre-operation routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtPostOperationPassThrough: Entered\n") ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +PtPreOperationNoPostOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the main pre-operation dispatch routine for this + miniFilter. Since this is just a simple passThrough miniFilter it + does not do anything with the callbackData but rather return + FLT_PREOP_SUCCESS_WITH_CALLBACK thereby passing it down to the next + miniFilter in the chain. + + This is non-pageable because it could be called on the paging path + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtPreOperationNoPostOperationPassThrough: Entered\n") ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +BOOLEAN +PtDoRequestOperationStatus( + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This identifies those operations we want the operation status for. These + are typically operations that return STATUS_PENDING as a normal completion + status. + +Arguments: + +Return Value: + + TRUE - If we want the operation status + FALSE - If we don't + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + + // + // return boolean state based on which operations we are interested in + // + + return (BOOLEAN) + + // + // Check for oplock operations + // + + (((iopb->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && + ((iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_FILTER_OPLOCK) || + (iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_BATCH_OPLOCK) || + (iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_1) || + (iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2))) + + || + + // + // Check for directy change notification + // + + ((iopb->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) && + (iopb->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY)) + ); +} + diff --git a/filesys/miniFilter/passThrough/passThrough.inf b/filesys/miniFilter/passThrough/passThrough.inf new file mode 100644 index 00000000..28eb9b4a --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.inf @@ -0,0 +1,96 @@ +;;; +;;; PassThrough +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.1 +CatalogFile = passthrough.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"DebugFlags",0x00010001 ,0x0 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +passthrough.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "PassThrough Mini-Filter Driver" +ServiceName = "PassThrough" +DriverName = "PassThrough" +DiskId1 = "PassThrough Device Installation Disk" + +;Instances specific information. +DefaultInstance = "PassThrough Instance" +Instance1.Name = "PassThrough Instance" +Instance1.Altitude = "370030" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/passThrough/passThrough.rc b/filesys/miniFilter/passThrough/passThrough.rc new file mode 100644 index 00000000..5c6fdc46 --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "PassThrough Filter Driver" +#define VER_INTERNALNAME_STR "passThrough.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/passThrough/passThrough.sln b/filesys/miniFilter/passThrough/passThrough.sln new file mode 100644 index 00000000..26698330 --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "passThrough", "passThrough.vcxproj", "{6925DFEC-4D62-4106-9465-F4BFC1CA280B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|Win32.ActiveCfg = Debug|Win32 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|Win32.Build.0 = Debug|Win32 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|Win32.ActiveCfg = Release|Win32 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|Win32.Build.0 = Release|Win32 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|x64.ActiveCfg = Debug|x64 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|x64.Build.0 = Debug|x64 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|x64.ActiveCfg = Release|x64 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/passThrough/passThrough.vcxproj b/filesys/miniFilter/passThrough/passThrough.vcxproj new file mode 100644 index 00000000..66521b00 --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {6925DFEC-4D62-4106-9465-F4BFC1CA280B} + $(MSBuildProjectName) + Debug + Win32 + {D0990CEE-D681-43CD-9797-60AB0D628853} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + passThrough + + + passThrough + + + passThrough + + + passThrough + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters b/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters new file mode 100644 index 00000000..24e2fc4c --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {D8FC9FC6-EBA8-4607-B388-0F5F1CACBA9C} + + + h;hpp;hxx;hm;inl;inc;xsd + {968D5F4E-042F-4EB1-89CA-8A4DC5FAD3B8} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {BD7D5A5F-EEB5-43CF-AEF9-602F292252B7} + + + inf;inv;inx;mof;mc; + {6C7310BB-F3EA-4F55-A624-111FE5C6DF8D} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/scanner/ReadMe.md b/filesys/miniFilter/scanner/ReadMe.md new file mode 100644 index 00000000..979b2c25 --- /dev/null +++ b/filesys/miniFilter/scanner/ReadMe.md @@ -0,0 +1,17 @@ +Scanner File System Minifilter Driver +===================================== + +The Scanner minifilter is an example for developers who intend to write filters that examine data in files. Typically, antivirus products fall into this category. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The Scanner minifilter comprises both kernel-mode and user-mode components. The kernel-mode component recognizes appropriate moments for scanning a file's data and passes it to the user-mode component for further validation. The user-mode component creates a number of threads that await validation requests and corresponding data from the kernel-mode component. After scanning the data for occurrences of a "foul" string, the user-mode component sends an appropriate response to the kernel-mode component. + +The kernel-mode component scans files with specific extensions only. The file is first scanned on a successful open. If the file was opened with write access, it is scanned again before a close. Scanning is also performed on data that is about to be written to a file. Writes will be rejected if any occurrences of a "foul" string are found in the data. If a "foul" string is detected during the closing of a file, a debug message is printed. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/scanner/filter/scanner.c b/filesys/miniFilter/scanner/filter/scanner.c new file mode 100644 index 00000000..cd72d71b --- /dev/null +++ b/filesys/miniFilter/scanner/filter/scanner.c @@ -0,0 +1,1685 @@ +/*++ + +Copyright (c) 1999-2002 Microsoft Corporation + +Module Name: + + scanner.c + +Abstract: + + This is the main module of the scanner filter. + + This filter scans the data in a file before allowing an open to proceed. This is similar + to what virus checkers do. + +Environment: + + Kernel mode + +--*/ + +#include +#include +#include +#include "scanuk.h" +#include "scanner.h" + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +#define SCANNER_REG_TAG 'Rncs' +#define SCANNER_STRING_TAG 'Sncs' + +// +// Structure that contains all the global data structures +// used throughout the scanner. +// + +SCANNER_DATA ScannerData; + +// +// This is a static list of file name extensions files we are interested in scanning +// + +PUNICODE_STRING ScannedExtensions; +ULONG ScannedExtensionCount; + +// +// The default extension to scan if not configured in the registry +// + +UNICODE_STRING ScannedExtensionDefault = RTL_CONSTANT_STRING( L"doc" ); + +// +// Function prototypes +// + +NTSTATUS +ScannerInitializeScannedExtensions( + _In_ PUNICODE_STRING RegistryPath + ); + +VOID +ScannerFreeExtensions( + ); + +NTSTATUS +ScannerAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +VOID +ScannerFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +NTSTATUS +ScannerPortConnect ( + _In_ PFLT_PORT ClientPort, + _In_opt_ PVOID ServerPortCookie, + _In_reads_bytes_opt_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Outptr_result_maybenull_ PVOID *ConnectionCookie + ); + +VOID +ScannerPortDisconnect ( + _In_opt_ PVOID ConnectionCookie + ); + +NTSTATUS +ScannerpScanFileInUserMode ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PBOOLEAN SafeToOpen + ); + +BOOLEAN +ScannerpCheckExtension ( + _In_ PUNICODE_STRING Extension + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(INIT, DriverEntry) + #pragma alloc_text(INIT, ScannerInitializeScannedExtensions) + #pragma alloc_text(PAGE, ScannerInstanceSetup) + #pragma alloc_text(PAGE, ScannerPreCreate) + #pragma alloc_text(PAGE, ScannerPortConnect) + #pragma alloc_text(PAGE, ScannerPortDisconnect) + #pragma alloc_text(PAGE, ScannerFreeExtensions) + #pragma alloc_text(PAGE, ScannerAllocateUnicodeString) + #pragma alloc_text(PAGE, ScannerFreeUnicodeString) +#endif + + +// +// Constant FLT_REGISTRATION structure for our filter. This +// initializes the callback routines our filter wants to register +// for. This is only used to register with the filter manager +// + +const FLT_OPERATION_REGISTRATION Callbacks[] = { + + { IRP_MJ_CREATE, + 0, + ScannerPreCreate, + ScannerPostCreate}, + + { IRP_MJ_CLEANUP, + 0, + ScannerPreCleanup, + NULL}, + + { IRP_MJ_WRITE, + 0, + ScannerPreWrite, + NULL}, + +#if (WINVER>=0x0602) + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + ScannerPreFileSystemControl, + NULL + }, + +#endif + + { IRP_MJ_OPERATION_END} +}; + + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_STREAMHANDLE_CONTEXT, + 0, + NULL, + sizeof(SCANNER_STREAM_HANDLE_CONTEXT), + 'chBS' }, + + { FLT_CONTEXT_END } +}; + +const FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + ContextRegistration, // Context Registration. + Callbacks, // Operation callbacks + ScannerUnload, // FilterUnload + ScannerInstanceSetup, // InstanceSetup + ScannerQueryTeardown, // InstanceQueryTeardown + NULL, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL // NormalizeNameComponent +}; + +//////////////////////////////////////////////////////////////////////////// +// +// Filter initialization and unload routines. +// +//////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for the Filter driver. This + registers the Filter with the filter manager and initializes all + its global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns STATUS_SUCCESS. +--*/ +{ + OBJECT_ATTRIBUTES oa; + UNICODE_STRING uniString; + PSECURITY_DESCRIPTOR sd; + NTSTATUS status; + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + // + // Register with filter manager. + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &ScannerData.Filter ); + + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // Obtain the extensions to scan from the registry + // + + status = ScannerInitializeScannedExtensions( RegistryPath ); + + if (!NT_SUCCESS( status )) { + + status = STATUS_SUCCESS; + + ScannedExtensions = &ScannedExtensionDefault; + ScannedExtensionCount = 1; + } + + // + // Create a communication port. + // + + RtlInitUnicodeString( &uniString, ScannerPortName ); + + // + // We secure the port so only ADMINs & SYSTEM can acecss it. + // + + status = FltBuildDefaultSecurityDescriptor( &sd, FLT_PORT_ALL_ACCESS ); + + if (NT_SUCCESS( status )) { + + InitializeObjectAttributes( &oa, + &uniString, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + sd ); + + status = FltCreateCommunicationPort( ScannerData.Filter, + &ScannerData.ServerPort, + &oa, + NULL, + ScannerPortConnect, + ScannerPortDisconnect, + NULL, + 1 ); + // + // Free the security descriptor in all cases. It is not needed once + // the call to FltCreateCommunicationPort() is made. + // + + FltFreeSecurityDescriptor( sd ); + + if (NT_SUCCESS( status )) { + + // + // Start filtering I/O. + // + + status = FltStartFiltering( ScannerData.Filter ); + + if (NT_SUCCESS( status )) { + + return STATUS_SUCCESS; + } + + FltCloseCommunicationPort( ScannerData.ServerPort ); + } + } + + ScannerFreeExtensions(); + + FltUnregisterFilter( ScannerData.Filter ); + + return status; +} + + +NTSTATUS +ScannerInitializeScannedExtensions( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Descrition: + + This routine sets the the extensions for files to be scanned based + on the registry. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + STATUS_SUCCESS if the function completes successfully. Otherwise a valid + NTSTATUS code is returned. + +--*/ +{ + NTSTATUS status; + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey = NULL; + UNICODE_STRING valueName; + PKEY_VALUE_PARTIAL_INFORMATION valueBuffer = NULL; + ULONG valueLength = 0; + BOOLEAN closeHandle = FALSE; + PWCHAR ch; + SIZE_T length; + ULONG count; + PUNICODE_STRING ext; + + PAGED_CODE(); + + ScannedExtensions = NULL; + ScannedExtensionCount = 0; + + // + // Open the driver registry key. + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (!NT_SUCCESS( status )) { + + goto ScannerInitializeScannedExtensionsCleanup; + } + + closeHandle = TRUE; + + // + // Query the length of the reg value + // + + RtlInitUnicodeString( &valueName, L"Extensions" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + NULL, + 0, + &valueLength ); + + if (status!=STATUS_BUFFER_TOO_SMALL && status!=STATUS_BUFFER_OVERFLOW) { + + status = STATUS_INVALID_PARAMETER; + goto ScannerInitializeScannedExtensionsCleanup; + } + + // + // Extract the path. + // + + valueBuffer = ExAllocatePoolWithTag( NonPagedPool, + valueLength, + SCANNER_REG_TAG ); + + if (valueBuffer == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto ScannerInitializeScannedExtensionsCleanup; + } + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + valueBuffer, + valueLength, + &valueLength ); + + if (!NT_SUCCESS( status )) { + + goto ScannerInitializeScannedExtensionsCleanup; + } + + ch = (PWCHAR)(valueBuffer->Data); + + count = 0; + + // + // Count how many strings are in the multi string + // + + while (*ch != '\0') { + + ch = ch + wcslen( ch ) + 1; + count++; + } + + ScannedExtensions = ExAllocatePoolWithTag( PagedPool, + count * sizeof(UNICODE_STRING), + SCANNER_STRING_TAG ); + + if (ScannedExtensions == NULL) { + goto ScannerInitializeScannedExtensionsCleanup; + } + + ch = (PWCHAR)((PKEY_VALUE_PARTIAL_INFORMATION)valueBuffer->Data); + ext = ScannedExtensions; + + while (ScannedExtensionCount < count) { + + length = wcslen( ch ) * sizeof(WCHAR); + + ext->MaximumLength = (USHORT) length; + + status = ScannerAllocateUnicodeString( ext ); + + if (!NT_SUCCESS( status )) { + goto ScannerInitializeScannedExtensionsCleanup; + } + + ext->Length = (USHORT)length; + + RtlCopyMemory( ext->Buffer, ch, length ); + + ch = ch + length/sizeof(WCHAR) + 1; + + ScannedExtensionCount++; + + ext++; + + } + +ScannerInitializeScannedExtensionsCleanup: + + // + // Note that this function leaks the global buffers. + // On failure DriverEntry will clean up the globals + // so we don't have to do that here. + // + + if (valueBuffer != NULL) { + + ExFreePoolWithTag( valueBuffer, SCANNER_REG_TAG ); + valueBuffer = NULL; + } + + if (closeHandle) { + + ZwClose( driverRegKey ); + } + + if (!NT_SUCCESS( status )) { + + ScannerFreeExtensions(); + } + + return status; +} + + +VOID +ScannerFreeExtensions( + ) +/*++ + +Routine Descrition: + + This routine cleans up the global buffers on both + teardown and initialization failure. + +Arguments: + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + // + // Free the strings in the scanned extension array + // + + while (ScannedExtensionCount > 0) { + + ScannedExtensionCount--; + + if (ScannedExtensions != &ScannedExtensionDefault) { + + ScannerFreeUnicodeString( ScannedExtensions + ScannedExtensionCount ); + } + } + + if (ScannedExtensions != &ScannedExtensionDefault && ScannedExtensions != NULL) { + + ExFreePoolWithTag( ScannedExtensions, SCANNER_STRING_TAG ); + } + + ScannedExtensions = NULL; + +} + + +NTSTATUS +ScannerAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This routine allocates a unicode string + +Arguments: + + String - supplies the size of the string to be allocated in the MaximumLength field + return the unicode string + +Return Value: + + STATUS_SUCCESS - success + STATUS_INSUFFICIENT_RESOURCES - failure + +--*/ +{ + + PAGED_CODE(); + + String->Buffer = ExAllocatePoolWithTag( NonPagedPool, + String->MaximumLength, + SCANNER_STRING_TAG ); + + if (String->Buffer == NULL) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + String->Length = 0; + + return STATUS_SUCCESS; +} + + +VOID +ScannerFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This routine frees a unicode string + +Arguments: + + String - supplies the string to be freed + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + if (String->Buffer) { + + ExFreePoolWithTag( String->Buffer, + SCANNER_STRING_TAG ); + String->Buffer = NULL; + } + + String->Length = String->MaximumLength = 0; + String->Buffer = NULL; +} + + +NTSTATUS +ScannerPortConnect ( + _In_ PFLT_PORT ClientPort, + _In_opt_ PVOID ServerPortCookie, + _In_reads_bytes_opt_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Outptr_result_maybenull_ PVOID *ConnectionCookie + ) +/*++ + +Routine Description + + This is called when user-mode connects to the server port - to establish a + connection + +Arguments + + ClientPort - This is the client connection port that will be used to + send messages from the filter + + ServerPortCookie - The context associated with this port when the + minifilter created this port. + + ConnectionContext - Context from entity connecting to this port (most likely + your user mode service) + + SizeofContext - Size of ConnectionContext in bytes + + ConnectionCookie - Context to be passed to the port disconnect routine. + +Return Value + + STATUS_SUCCESS - to accept the connection + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ServerPortCookie ); + UNREFERENCED_PARAMETER( ConnectionContext ); + UNREFERENCED_PARAMETER( SizeOfContext); + UNREFERENCED_PARAMETER( ConnectionCookie = NULL ); + + FLT_ASSERT( ScannerData.ClientPort == NULL ); + FLT_ASSERT( ScannerData.UserProcess == NULL ); + + // + // Set the user process and port. In a production filter it may + // be necessary to synchronize access to such fields with port + // lifetime. For instance, while filter manager will synchronize + // FltCloseClientPort with FltSendMessage's reading of the port + // handle, synchronizing access to the UserProcess would be up to + // the filter. + // + + ScannerData.UserProcess = PsGetCurrentProcess(); + ScannerData.ClientPort = ClientPort; + + DbgPrint( "!!! scanner.sys --- connected, port=0x%p\n", ClientPort ); + + return STATUS_SUCCESS; +} + + +VOID +ScannerPortDisconnect( + _In_opt_ PVOID ConnectionCookie + ) +/*++ + +Routine Description + + This is called when the connection is torn-down. We use it to close our + handle to the connection + +Arguments + + ConnectionCookie - Context from the port connect routine + +Return value + + None + +--*/ +{ + UNREFERENCED_PARAMETER( ConnectionCookie ); + + PAGED_CODE(); + + DbgPrint( "!!! scanner.sys --- disconnected, port=0x%p\n", ScannerData.ClientPort ); + + // + // Close our handle to the connection: note, since we limited max connections to 1, + // another connect will not be allowed until we return from the disconnect routine. + // + + FltCloseClientPort( ScannerData.Filter, &ScannerData.ClientPort ); + + // + // Reset the user-process field. + // + + ScannerData.UserProcess = NULL; +} + + +NTSTATUS +ScannerUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for the Filter driver. This unregisters the + Filter with the filter manager and frees any allocated global data + structures. + +Arguments: + + None. + +Return Value: + + Returns the final status of the deallocation routines. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + ScannerFreeExtensions(); + + // + // Close the server port. + // + + FltCloseCommunicationPort( ScannerData.ServerPort ); + + // + // Unregister the filter + // + + FltUnregisterFilter( ScannerData.Filter ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +ScannerInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called by the filter manager when a new instance is created. + We specified in the registry that we only want for manual attachments, + so that is all we should receive here. + +Arguments: + + FltObjects - Describes the instance and volume which we are being asked to + setup. + + Flags - Flags describing the type of attachment this is. + + VolumeDeviceType - The DEVICE_TYPE for the volume to which this instance + will attach. + + VolumeFileSystemType - The file system formatted on this volume. + +Return Value: + + STATUS_SUCCESS - we wish to attach to the volume + STATUS_FLT_DO_NOT_ATTACH - no, thank you + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + FLT_ASSERT( FltObjects->Filter == ScannerData.Filter ); + + // + // Don't attach to network volumes. + // + + if (VolumeDeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) { + + return STATUS_FLT_DO_NOT_ATTACH; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +ScannerQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the instance detach routine for the filter. This + routine is called by filter manager when a user initiates a manual instance + detach. This is a 'query' routine: if the filter does not want to support + manual detach, it can return a failure status + +Arguments: + + FltObjects - Describes the instance and volume for which we are receiving + this query teardown request. + + Flags - Unused + +Return Value: + + STATUS_SUCCESS - we allow instance detach to happen + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + return STATUS_SUCCESS; +} + + +FLT_PREOP_CALLBACK_STATUS +ScannerPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre create callback. We need to remember whether this file has been + opened for write access. If it has, we'll want to rescan it in cleanup. + This scheme results in extra scans in at least two cases: + -- if the create fails (perhaps for access denied) + -- the file is opened for write access but never actually written to + The assumption is that writes are more common than creates, and checking + or setting the context in the write path would be less efficient than + taking a good guess before the create. + +Arguments: + + Data - The structure which describes the operation parameters. + + FltObject - The structure which describes the objects affected by this + operation. + + CompletionContext - Output parameter which can be used to pass a context + from this pre-create callback to the post-create callback. + +Return Value: + + FLT_PREOP_SUCCESS_WITH_CALLBACK - If this is not our user-mode process. + FLT_PREOP_SUCCESS_NO_CALLBACK - All other threads. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext = NULL ); + + PAGED_CODE(); + + // + // See if this create is being done by our user process. + // + + if (IoThreadToProcess( Data->Thread ) == ScannerData.UserProcess) { + + DbgPrint( "!!! scanner.sys -- allowing create for trusted process \n" ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + return FLT_PREOP_SUCCESS_WITH_CALLBACK; +} + + +BOOLEAN +ScannerpCheckExtension ( + _In_ PUNICODE_STRING Extension + ) +/*++ + +Routine Description: + + Checks if this file name extension is something we are interested in + +Arguments + + Extension - Pointer to the file name extension + +Return Value + + TRUE - Yes we are interested + FALSE - No +--*/ +{ + ULONG count; + + if (Extension->Length == 0) { + + return FALSE; + } + + // + // Check if it matches any one of our static extension list + // + + for (count = 0; count < ScannedExtensionCount; count++) { + + if (RtlCompareUnicodeString( Extension, ScannedExtensions + count, TRUE ) == 0) { + + // + // A match. We are interested in this file + // + + return TRUE; + } + } + + return FALSE; +} + + +FLT_POSTOP_CALLBACK_STATUS +ScannerPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + Post create callback. We can't scan the file until after the create has + gone to the filesystem, since otherwise the filesystem wouldn't be ready + to read the file for us. + +Arguments: + + Data - The structure which describes the operation parameters. + + FltObject - The structure which describes the objects affected by this + operation. + + CompletionContext - The operation context passed fron the pre-create + callback. + + Flags - Flags to say why we are getting this post-operation callback. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - ok to open the file or we wish to deny + access to this file, hence undo the open + +--*/ +{ + PSCANNER_STREAM_HANDLE_CONTEXT scannerContext; + FLT_POSTOP_CALLBACK_STATUS returnStatus = FLT_POSTOP_FINISHED_PROCESSING; + PFLT_FILE_NAME_INFORMATION nameInfo; + NTSTATUS status; + BOOLEAN safeToOpen, scanFile; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + // + // If this create was failing anyway, don't bother scanning now. + // + + if (!NT_SUCCESS( Data->IoStatus.Status ) || + (STATUS_REPARSE == Data->IoStatus.Status)) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Check if we are interested in this file. + // + + status = FltGetFileNameInformation( Data, + FLT_FILE_NAME_NORMALIZED | + FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + + if (!NT_SUCCESS( status )) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + FltParseFileNameInformation( nameInfo ); + + // + // Check if the extension matches the list of extensions we are interested in + // + + scanFile = ScannerpCheckExtension( &nameInfo->Extension ); + + // + // Release file name info, we're done with it + // + + FltReleaseFileNameInformation( nameInfo ); + + if (!scanFile) { + + // + // Not an extension we are interested in + // + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + (VOID) ScannerpScanFileInUserMode( FltObjects->Instance, + FltObjects->FileObject, + &safeToOpen ); + + if (!safeToOpen) { + + // + // Ask the filter manager to undo the create. + // + + DbgPrint( "!!! scanner.sys -- foul language detected in postcreate !!!\n" ); + + DbgPrint( "!!! scanner.sys -- undoing create \n" ); + + FltCancelFileOpen( FltObjects->Instance, FltObjects->FileObject ); + + Data->IoStatus.Status = STATUS_ACCESS_DENIED; + Data->IoStatus.Information = 0; + + returnStatus = FLT_POSTOP_FINISHED_PROCESSING; + + } else if (FltObjects->FileObject->WriteAccess) { + + // + // + // The create has requested write access, mark to rescan the file. + // Allocate the context. + // + + status = FltAllocateContext( ScannerData.Filter, + FLT_STREAMHANDLE_CONTEXT, + sizeof(SCANNER_STREAM_HANDLE_CONTEXT), + PagedPool, + &scannerContext ); + + if (NT_SUCCESS(status)) { + + // + // Set the handle context. + // + + scannerContext->RescanRequired = TRUE; + + (VOID) FltSetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + FLT_SET_CONTEXT_REPLACE_IF_EXISTS, + scannerContext, + NULL ); + + // + // Normally we would check the results of FltSetStreamHandleContext + // for a variety of error cases. However, The only error status + // that could be returned, in this case, would tell us that + // contexts are not supported. Even if we got this error, + // we just want to release the context now and that will free + // this memory if it was not successfully set. + // + + // + // Release our reference on the context (the set adds a reference) + // + + FltReleaseContext( scannerContext ); + } + } + + return returnStatus; +} + + +FLT_PREOP_CALLBACK_STATUS +ScannerPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre cleanup callback. If this file was opened for write access, we want + to rescan it now. + +Arguments: + + Data - The structure which describes the operation parameters. + + FltObject - The structure which describes the objects affected by this + operation. + + CompletionContext - Output parameter which can be used to pass a context + from this pre-cleanup callback to the post-cleanup callback. + +Return Value: + + Always FLT_PREOP_SUCCESS_NO_CALLBACK. + +--*/ +{ + NTSTATUS status; + PSCANNER_STREAM_HANDLE_CONTEXT context; + BOOLEAN safe; + + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( CompletionContext ); + + status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &context ); + + if (NT_SUCCESS( status )) { + + if (context->RescanRequired) { + + (VOID) ScannerpScanFileInUserMode( FltObjects->Instance, + FltObjects->FileObject, + &safe ); + + if (!safe) { + + DbgPrint( "!!! scanner.sys -- foul language detected in precleanup !!!\n" ); + } + } + + FltReleaseContext( context ); + } + + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +FLT_PREOP_CALLBACK_STATUS +ScannerPreWrite ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre write callback. We want to scan what's being written now. + +Arguments: + + Data - The structure which describes the operation parameters. + + FltObject - The structure which describes the objects affected by this + operation. + + CompletionContext - Output parameter which can be used to pass a context + from this pre-write callback to the post-write callback. + +Return Value: + + Always FLT_PREOP_SUCCESS_NO_CALLBACK. + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + NTSTATUS status; + PSCANNER_NOTIFICATION notification = NULL; + PSCANNER_STREAM_HANDLE_CONTEXT context = NULL; + ULONG replyLength; + BOOLEAN safe = TRUE; + PUCHAR buffer; + + UNREFERENCED_PARAMETER( CompletionContext ); + + // + // If not client port just ignore this write. + // + + if (ScannerData.ClientPort == NULL) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &context ); + + if (!NT_SUCCESS( status )) { + + // + // We are not interested in this file + // + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Use try-finally to cleanup + // + + try { + + // + // Pass the contents of the buffer to user mode. + // + + if (Data->Iopb->Parameters.Write.Length != 0) { + + // + // Get the users buffer address. If there is a MDL defined, use + // it. If not use the given buffer address. + // + + if (Data->Iopb->Parameters.Write.MdlAddress != NULL) { + + buffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.Write.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + // + // If we have a MDL but could not get and address, we ran out + // of memory, report the correct error + // + + if (buffer == NULL) { + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + returnStatus = FLT_PREOP_COMPLETE; + leave; + } + + } else { + + // + // Use the users buffer + // + + buffer = Data->Iopb->Parameters.Write.WriteBuffer; + } + + // + // In a production-level filter, we would actually let user mode scan the file directly. + // Allocating & freeing huge amounts of non-paged pool like this is not very good for system perf. + // This is just a sample! + // + + notification = ExAllocatePoolWithTag( NonPagedPool, + sizeof( SCANNER_NOTIFICATION ), + 'nacS' ); + if (notification == NULL) { + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + returnStatus = FLT_PREOP_COMPLETE; + leave; + } + + notification->BytesToScan = min( Data->Iopb->Parameters.Write.Length, SCANNER_READ_BUFFER_SIZE ); + + // + // The buffer can be a raw user buffer. Protect access to it + // + + try { + + RtlCopyMemory( ¬ification->Contents, + buffer, + notification->BytesToScan ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + // + // Error accessing buffer. Complete i/o with failure + // + + Data->IoStatus.Status = GetExceptionCode() ; + Data->IoStatus.Information = 0; + returnStatus = FLT_PREOP_COMPLETE; + leave; + } + + // + // Send message to user mode to indicate it should scan the buffer. + // We don't have to synchronize between the send and close of the handle + // as FltSendMessage takes care of that. + // + + replyLength = sizeof( SCANNER_REPLY ); + + status = FltSendMessage( ScannerData.Filter, + &ScannerData.ClientPort, + notification, + sizeof( SCANNER_NOTIFICATION ), + notification, + &replyLength, + NULL ); + + if (STATUS_SUCCESS == status) { + + safe = ((PSCANNER_REPLY) notification)->SafeToOpen; + + } else { + + // + // Couldn't send message. This sample will let the i/o through. + // + + DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status ); + } + } + + if (!safe) { + + // + // Block this write if not paging i/o (as a result of course, this scanner will not prevent memory mapped writes of contaminated + // strings to the file, but only regular writes). The effect of getting ERROR_ACCESS_DENIED for many apps to delete the file they + // are trying to write usually. + // To handle memory mapped writes - we should be scanning at close time (which is when we can really establish that the file object + // is not going to be used for any more writes) + // + + DbgPrint( "!!! scanner.sys -- foul language detected in write !!!\n" ); + + if (!FlagOn( Data->Iopb->IrpFlags, IRP_PAGING_IO )) { + + DbgPrint( "!!! scanner.sys -- blocking the write !!!\n" ); + + Data->IoStatus.Status = STATUS_ACCESS_DENIED; + Data->IoStatus.Information = 0; + returnStatus = FLT_PREOP_COMPLETE; + } + } + + } finally { + + if (notification != NULL) { + + ExFreePoolWithTag( notification, 'nacS' ); + } + + if (context) { + + FltReleaseContext( context ); + } + } + + return returnStatus; +} + +#if (WINVER>=0x0602) + +FLT_PREOP_CALLBACK_STATUS +ScannerPreFileSystemControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre FS Control callback. + +Arguments: + + Data - The structure which describes the operation parameters. + + FltObject - The structure which describes the objects affected by this + operation. + + CompletionContext - Output parameter which can be used to pass a context + from this callback to the post-write callback. + +Return Value: + + FLT_PREOP_SUCCESS_NO_CALLBACK or FLT_PREOP_COMPLETE + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + NTSTATUS status; + ULONG fsControlCode; + PSCANNER_STREAM_HANDLE_CONTEXT context = NULL; + + UNREFERENCED_PARAMETER( CompletionContext ); + + FLT_ASSERT( Data != NULL ); + FLT_ASSERT( Data->Iopb != NULL ); + + // + // If not client port just ignore this write. + // + + if (ScannerData.ClientPort == NULL) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &context ); + + if (!NT_SUCCESS( status )) { + + // + // We are not interested in this file + // + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Use try-finally to cleanup + // + + try { + + fsControlCode = Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode; + + if (fsControlCode == FSCTL_OFFLOAD_WRITE) { + + // + // Scanner cannot access the data in this offload write request. + // In a production-level filter, we would actually let user mode + // scan the file after offload write completes (on cleanup etc). + // Since this is just a sample, block offload write with + // STATUS_ACCESS_DENIED, although this is not an acceptable + // production-level behavior. + // + + DbgPrint( "!!! scanner.sys -- blocking the offload write !!!\n" ); + + Data->IoStatus.Status = STATUS_ACCESS_DENIED; + Data->IoStatus.Information = 0; + + returnStatus = FLT_PREOP_COMPLETE; + } + + } finally { + + if (context) { + + FltReleaseContext( context ); + } + } + + return returnStatus; +} + +#endif + +////////////////////////////////////////////////////////////////////////// +// Local support routines. +// +///////////////////////////////////////////////////////////////////////// + +NTSTATUS +ScannerpScanFileInUserMode ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PBOOLEAN SafeToOpen + ) +/*++ + +Routine Description: + + This routine is called to send a request up to user mode to scan a given + file and tell our caller whether it's safe to open this file. + + Note that if the scan fails, we set SafeToOpen to TRUE. The scan may fail + because the service hasn't started, or perhaps because this create/cleanup + is for a directory, and there's no data to read & scan. + + If we failed creates when the service isn't running, there'd be a + bootstrapping problem -- how would we ever load the .exe for the service? + +Arguments: + + Instance - Handle to the filter instance for the scanner on this volume. + + FileObject - File to be scanned. + + SafeToOpen - Set to FALSE if the file is scanned successfully and it contains + foul language. + +Return Value: + + The status of the operation, hopefully STATUS_SUCCESS. The common failure + status will probably be STATUS_INSUFFICIENT_RESOURCES. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + PVOID buffer = NULL; + ULONG bytesRead; + PSCANNER_NOTIFICATION notification = NULL; + FLT_VOLUME_PROPERTIES volumeProps; + LARGE_INTEGER offset; + ULONG replyLength, length; + PFLT_VOLUME volume = NULL; + + *SafeToOpen = TRUE; + + // + // If not client port just return. + // + + if (ScannerData.ClientPort == NULL) { + + return STATUS_SUCCESS; + } + + try { + + // + // Obtain the volume object . + // + + status = FltGetVolumeFromInstance( Instance, &volume ); + + if (!NT_SUCCESS( status )) { + + leave; + } + + // + // Determine sector size. Noncached I/O can only be done at sector size offsets, and in lengths which are + // multiples of sector size. A more efficient way is to make this call once and remember the sector size in the + // instance setup routine and setup an instance context where we can cache it. + // + + status = FltGetVolumeProperties( volume, + &volumeProps, + sizeof( volumeProps ), + &length ); + // + // STATUS_BUFFER_OVERFLOW can be returned - however we only need the properties, not the names + // hence we only check for error status. + // + + if (NT_ERROR( status )) { + + leave; + } + + length = max( SCANNER_READ_BUFFER_SIZE, volumeProps.SectorSize ); + + // + // Use non-buffered i/o, so allocate aligned pool + // + + buffer = FltAllocatePoolAlignedWithTag( Instance, + NonPagedPool, + length, + 'nacS' ); + + if (NULL == buffer) { + + status = STATUS_INSUFFICIENT_RESOURCES; + leave; + } + + notification = ExAllocatePoolWithTag( NonPagedPool, + sizeof( SCANNER_NOTIFICATION ), + 'nacS' ); + + if(NULL == notification) { + + status = STATUS_INSUFFICIENT_RESOURCES; + leave; + } + + // + // Read the beginning of the file and pass the contents to user mode. + // + + offset.QuadPart = bytesRead = 0; + status = FltReadFile( Instance, + FileObject, + &offset, + length, + buffer, + FLTFL_IO_OPERATION_NON_CACHED | + FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET, + &bytesRead, + NULL, + NULL ); + + if (NT_SUCCESS( status ) && (0 != bytesRead)) { + + notification->BytesToScan = (ULONG) bytesRead; + + // + // Copy only as much as the buffer can hold + // + + RtlCopyMemory( ¬ification->Contents, + buffer, + min( notification->BytesToScan, SCANNER_READ_BUFFER_SIZE ) ); + + replyLength = sizeof( SCANNER_REPLY ); + + status = FltSendMessage( ScannerData.Filter, + &ScannerData.ClientPort, + notification, + sizeof(SCANNER_NOTIFICATION), + notification, + &replyLength, + NULL ); + + if (STATUS_SUCCESS == status) { + + *SafeToOpen = ((PSCANNER_REPLY) notification)->SafeToOpen; + + } else { + + // + // Couldn't send message + // + + DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status ); + } + } + + } finally { + + if (NULL != buffer) { + + FltFreePoolAlignedWithTag( Instance, buffer, 'nacS' ); + } + + if (NULL != notification) { + + ExFreePoolWithTag( notification, 'nacS' ); + } + + if (NULL != volume) { + + FltObjectDereference( volume ); + } + } + + return status; +} + diff --git a/filesys/miniFilter/scanner/filter/scanner.h b/filesys/miniFilter/scanner/filter/scanner.h new file mode 100644 index 00000000..c45592e9 --- /dev/null +++ b/filesys/miniFilter/scanner/filter/scanner.h @@ -0,0 +1,160 @@ +/*++ + +Copyright (c) 1999-2002 Microsoft Corporation + +Module Name: + + scrubber.h + +Abstract: + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. + +Environment: + + Kernel mode + +--*/ +#ifndef __SCANNER_H__ +#define __SCANNER_H__ + + +/////////////////////////////////////////////////////////////////////////// +// +// Global variables +// +/////////////////////////////////////////////////////////////////////////// + + +typedef struct _SCANNER_DATA { + + // + // The object that identifies this driver. + // + + PDRIVER_OBJECT DriverObject; + + // + // The filter handle that results from a call to + // FltRegisterFilter. + // + + PFLT_FILTER Filter; + + // + // Listens for incoming connections + // + + PFLT_PORT ServerPort; + + // + // User process that connected to the port + // + + PEPROCESS UserProcess; + + // + // Client port for a connection to user-mode + // + + PFLT_PORT ClientPort; + +} SCANNER_DATA, *PSCANNER_DATA; + +extern SCANNER_DATA ScannerData; + +typedef struct _SCANNER_STREAM_HANDLE_CONTEXT { + + BOOLEAN RescanRequired; + +} SCANNER_STREAM_HANDLE_CONTEXT, *PSCANNER_STREAM_HANDLE_CONTEXT; + +#pragma warning(push) +#pragma warning(disable:4200) // disable warnings for structures with zero length arrays. + +typedef struct _SCANNER_CREATE_PARAMS { + + WCHAR String[0]; + +} SCANNER_CREATE_PARAMS, *PSCANNER_CREATE_PARAMS; + +#pragma warning(pop) + + +/////////////////////////////////////////////////////////////////////////// +// +// Prototypes for the startup and unload routines used for +// this Filter. +// +// Implementation in scanner.c +// +/////////////////////////////////////////////////////////////////////////// +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +ScannerUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +ScannerQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +ScannerPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +ScannerPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +ScannerPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +ScannerPreWrite ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +#if (WINVER >= 0x0602) + +FLT_PREOP_CALLBACK_STATUS +ScannerPreFileSystemControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +#endif + +NTSTATUS +ScannerInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +#endif /* __SCANNER_H__ */ + diff --git a/filesys/miniFilter/scanner/filter/scanner.rc b/filesys/miniFilter/scanner/filter/scanner.rc new file mode 100644 index 00000000..9269b6cf --- /dev/null +++ b/filesys/miniFilter/scanner/filter/scanner.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Scanner Filter" +#define VER_INTERNALNAME_STR "scanner.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/scanner/filter/scanner.vcxproj b/filesys/miniFilter/scanner/filter/scanner.vcxproj new file mode 100644 index 00000000..99432692 --- /dev/null +++ b/filesys/miniFilter/scanner/filter/scanner.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3} + $(MSBuildProjectName) + Debug + Win32 + {82607450-8E93-44E4-9EDC-AA0C310E0E64} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + scanner + + + scanner + + + scanner + + + scanner + + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters b/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters new file mode 100644 index 00000000..37511409 --- /dev/null +++ b/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {714BE986-D639-4C08-B0B5-CEDD7F5A24B3} + + + h;hpp;hxx;hm;inl;inc;xsd + {8E4DEA17-46F6-459E-91CA-8EB73909B60A} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A4702A45-F819-43D0-B05B-56FFCA504075} + + + inf;inv;inx;mof;mc; + {A873A433-3F6A-4828-9BDA-9FDF6C9D4726} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/scanner/inc/scanuk.h b/filesys/miniFilter/scanner/inc/scanuk.h new file mode 100644 index 00000000..433657f6 --- /dev/null +++ b/filesys/miniFilter/scanner/inc/scanuk.h @@ -0,0 +1,49 @@ +/*++ + +Copyright (c) 1999-2002 Microsoft Corporation + +Module Name: + + scanuk.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + shared between kernel and user mode. + +Environment: + + Kernel & user mode + +--*/ + +#ifndef __SCANUK_H__ +#define __SCANUK_H__ + +// +// Name of port used to communicate +// + +const PWSTR ScannerPortName = L"\\ScannerPort"; + + +#define SCANNER_READ_BUFFER_SIZE 1024 + +typedef struct _SCANNER_NOTIFICATION { + + ULONG BytesToScan; + ULONG Reserved; // for quad-word alignement of the Contents structure + UCHAR Contents[SCANNER_READ_BUFFER_SIZE]; + +} SCANNER_NOTIFICATION, *PSCANNER_NOTIFICATION; + +typedef struct _SCANNER_REPLY { + + BOOLEAN SafeToOpen; + +} SCANNER_REPLY, *PSCANNER_REPLY; + +#endif // __SCANUK_H__ + + diff --git a/filesys/miniFilter/scanner/scanner.inf b/filesys/miniFilter/scanner/scanner.inf new file mode 100644 index 00000000..e4a06fc5 --- /dev/null +++ b/filesys/miniFilter/scanner/scanner.inf @@ -0,0 +1,104 @@ +;;; +;;; Scanner +;;; +;;; +;;; Copyright (c) 1999-2002, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ContentScreener" ;This is determined by the work this filter driver does +ClassGuid = {3e3f0674-c83c-4558-bb26-9820e1eba5c5} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.0 +CatalogFile = scanner.cat + + +[DestinationDirs] +DefaultDestDir = 12 +Scanner.DriverFiles = 12 ;%windir%\system32\drivers +Scanner.UserFiles = 10,FltMgr ;%windir%\FltMgr + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = Scanner.DriverFiles, Scanner.UserFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,Scanner.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = Scanner.DriverFiles, Scanner.UserFiles + + + +[DefaultUninstall.Services] +DelService = Scanner,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[Scanner.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Content Screener" +AddReg = Scanner.AddRegistry + +; +; Registry Modifications +; + +[Scanner.AddRegistry] +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% +HKR,,"Extensions",0x00010000,"exe","doc","txt","bat","cmd","inf" + +; +; Copy Files +; + +[Scanner.DriverFiles] +%DriverName%.sys + +[Scanner.UserFiles] +%UserAppName%.exe + +[SourceDisksFiles] +scanner.sys = 1,, +scanuser.exe = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Scanner mini-filter driver" +ServiceName = "Scanner" +DriverName = "scanner" +UserAppName = "scanuser" +DiskId1 = "Scanner Device Installation Disk" + +;Instances specific information. +DefaultInstance = "Scanner Instance" +Instance1.Name = "Scanner Instance" +Instance1.Altitude = "265000" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/scanner/scanner.sln b/filesys/miniFilter/scanner/scanner.sln new file mode 100644 index 00000000..f79705b4 --- /dev/null +++ b/filesys/miniFilter/scanner/scanner.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{826E5DA7-B03B-43C0-9CFF-E0B6E29942A3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{B27B6441-52BC-4DFD-9A25-1BC5972664F4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanner", "filter\scanner.vcxproj", "{6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanuser", "user\scanuser.vcxproj", "{EF224A50-E448-4767-AD5E-1EEF058D0E50}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|Win32.ActiveCfg = Debug|Win32 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|Win32.Build.0 = Debug|Win32 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|Win32.ActiveCfg = Release|Win32 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|Win32.Build.0 = Release|Win32 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|x64.ActiveCfg = Debug|x64 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|x64.Build.0 = Debug|x64 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|x64.ActiveCfg = Release|x64 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|x64.Build.0 = Release|x64 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|Win32.ActiveCfg = Debug|Win32 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|Win32.Build.0 = Debug|Win32 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|Win32.ActiveCfg = Release|Win32 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|Win32.Build.0 = Release|Win32 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|x64.ActiveCfg = Debug|x64 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|x64.Build.0 = Debug|x64 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|x64.ActiveCfg = Release|x64 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3} = {826E5DA7-B03B-43C0-9CFF-E0B6E29942A3} + {EF224A50-E448-4767-AD5E-1EEF058D0E50} = {B27B6441-52BC-4DFD-9A25-1BC5972664F4} + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/scanner/user/scanUser.c b/filesys/miniFilter/scanner/user/scanUser.c new file mode 100644 index 00000000..da57b75c --- /dev/null +++ b/filesys/miniFilter/scanner/user/scanUser.c @@ -0,0 +1,416 @@ +/*++ + +Copyright (c) 1999-2002 Microsoft Corporation + +Module Name: + + scanUser.c + +Abstract: + + This file contains the implementation for the main function of the + user application piece of scanner. This function is responsible for + actually scanning file contents. + +Environment: + + User mode + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "scanuk.h" +#include "scanuser.h" +#include + +// +// Default and Maximum number of threads. +// + +#define SCANNER_DEFAULT_REQUEST_COUNT 5 +#define SCANNER_DEFAULT_THREAD_COUNT 2 +#define SCANNER_MAX_THREAD_COUNT 64 + +UCHAR FoulString[] = "foul"; + +// +// Context passed to worker threads +// + +typedef struct _SCANNER_THREAD_CONTEXT { + + HANDLE Port; + HANDLE Completion; + +} SCANNER_THREAD_CONTEXT, *PSCANNER_THREAD_CONTEXT; + + +VOID +Usage ( + VOID + ) +/*++ + +Routine Description + + Prints usage + +Arguments + + None + +Return Value + + None + +--*/ +{ + + printf( "Connects to the scanner filter and scans buffers \n" ); + printf( "Usage: scanuser [requests per thread] [number of threads(1-64)]\n" ); +} + +BOOL +ScanBuffer ( + _In_reads_bytes_(BufferSize) PUCHAR Buffer, + _In_ ULONG BufferSize + ) +/*++ + +Routine Description + + Scans the supplied buffer for an instance of FoulString. + + Note: Pattern matching algorithm used here is just for illustration purposes, + there are many better algorithms available for real world filters + +Arguments + + Buffer - Pointer to buffer + BufferSize - Size of passed in buffer + +Return Value + + TRUE - Found an occurrence of the appropriate FoulString + FALSE - Buffer is ok + +--*/ +{ + PUCHAR p; + ULONG searchStringLength = sizeof(FoulString) - sizeof(UCHAR); + + for (p = Buffer; + p <= (Buffer + BufferSize - searchStringLength); + p++) { + + if (RtlEqualMemory( p, FoulString, searchStringLength )) { + + printf( "Found a string\n" ); + + // + // Once we find our search string, we're not interested in seeing + // whether it appears again. + // + + return TRUE; + } + } + + return FALSE; +} + + +DWORD +ScannerWorker( + _In_ PSCANNER_THREAD_CONTEXT Context + ) +/*++ + +Routine Description + + This is a worker thread that + + +Arguments + + Context - This thread context has a pointer to the port handle we use to send/receive messages, + and a completion port handle that was already associated with the comm. port by the caller + +Return Value + + HRESULT indicating the status of thread exit. + +--*/ +{ + PSCANNER_NOTIFICATION notification; + SCANNER_REPLY_MESSAGE replyMessage; + PSCANNER_MESSAGE message; + LPOVERLAPPED pOvlp; + BOOL result; + DWORD outSize; + HRESULT hr; + ULONG_PTR key; + +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant + + while (TRUE) { + +#pragma warning(pop) + + // + // Poll for messages from the filter component to scan. + // + + result = GetQueuedCompletionStatus( Context->Completion, &outSize, &key, &pOvlp, INFINITE ); + + // + // Obtain the message: note that the message we sent down via FltGetMessage() may NOT be + // the one dequeued off the completion queue: this is solely because there are multiple + // threads per single port handle. Any of the FilterGetMessage() issued messages can be + // completed in random order - and we will just dequeue a random one. + // + + message = CONTAINING_RECORD( pOvlp, SCANNER_MESSAGE, Ovlp ); + + if (!result) { + + // + // An error occured. + // + + hr = HRESULT_FROM_WIN32( GetLastError() ); + break; + } + + printf( "Received message, size %d\n", pOvlp->InternalHigh ); + + notification = &message->Notification; + + assert(notification->BytesToScan <= SCANNER_READ_BUFFER_SIZE); + _Analysis_assume_(notification->BytesToScan <= SCANNER_READ_BUFFER_SIZE); + + result = ScanBuffer( notification->Contents, notification->BytesToScan ); + + replyMessage.ReplyHeader.Status = 0; + replyMessage.ReplyHeader.MessageId = message->MessageHeader.MessageId; + + // + // Need to invert the boolean -- result is true if found + // foul language, in which case SafeToOpen should be set to false. + // + + replyMessage.Reply.SafeToOpen = !result; + + printf( "Replying message, SafeToOpen: %d\n", replyMessage.Reply.SafeToOpen ); + + hr = FilterReplyMessage( Context->Port, + (PFILTER_REPLY_HEADER) &replyMessage, + sizeof( replyMessage ) ); + + if (SUCCEEDED( hr )) { + + printf( "Replied message\n" ); + + } else { + + printf( "Scanner: Error replying message. Error = 0x%X\n", hr ); + break; + } + + memset( &message->Ovlp, 0, sizeof( OVERLAPPED ) ); + + hr = FilterGetMessage( Context->Port, + &message->MessageHeader, + FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ), + &message->Ovlp ); + + if (hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING )) { + + break; + } + } + + if (!SUCCEEDED( hr )) { + + if (hr == HRESULT_FROM_WIN32( ERROR_INVALID_HANDLE )) { + + // + // Scanner port disconncted. + // + + printf( "Scanner: Port is disconnected, probably due to scanner filter unloading.\n" ); + + } else { + + printf( "Scanner: Unknown error occured. Error = 0x%X\n", hr ); + } + } + + free( message ); + + return hr; +} + + +int _cdecl +main ( + _In_ int argc, + _In_reads_(argc) char *argv[] + ) +{ + DWORD requestCount = SCANNER_DEFAULT_REQUEST_COUNT; + DWORD threadCount = SCANNER_DEFAULT_THREAD_COUNT; + HANDLE threads[SCANNER_MAX_THREAD_COUNT]; + SCANNER_THREAD_CONTEXT context; + HANDLE port, completion; + PSCANNER_MESSAGE msg; + DWORD threadId; + HRESULT hr; + DWORD i, j; + + // + // Check how many threads and per thread requests are desired. + // + + if (argc > 1) { + + requestCount = atoi( argv[1] ); + + if (requestCount <= 0) { + + Usage(); + return 1; + } + + if (argc > 2) { + + threadCount = atoi( argv[2] ); + } + + if (threadCount <= 0 || threadCount > 64) { + + Usage(); + return 1; + } + } + + // + // Open a commuication channel to the filter + // + + printf( "Scanner: Connecting to the filter ...\n" ); + + hr = FilterConnectCommunicationPort( ScannerPortName, + 0, + NULL, + 0, + NULL, + &port ); + + if (IS_ERROR( hr )) { + + printf( "ERROR: Connecting to filter port: 0x%08x\n", hr ); + return 2; + } + + // + // Create a completion port to associate with this handle. + // + + completion = CreateIoCompletionPort( port, + NULL, + 0, + threadCount ); + + if (completion == NULL) { + + printf( "ERROR: Creating completion port: %d\n", GetLastError() ); + CloseHandle( port ); + return 3; + } + + printf( "Scanner: Port = 0x%p Completion = 0x%p\n", port, completion ); + + context.Port = port; + context.Completion = completion; + + // + // Create specified number of threads. + // + + for (i = 0; i < threadCount; i++) { + + threads[i] = CreateThread( NULL, + 0, + (LPTHREAD_START_ROUTINE) ScannerWorker, + &context, + 0, + &threadId ); + + if (threads[i] == NULL) { + + // + // Couldn't create thread. + // + + hr = GetLastError(); + printf( "ERROR: Couldn't create thread: %d\n", hr ); + goto main_cleanup; + } + + for (j = 0; j < requestCount; j++) { + + // + // Allocate the message. + // + +#pragma prefast(suppress:__WARNING_MEMORY_LEAK, "msg will not be leaked because it is freed in ScannerWorker") + msg = malloc( sizeof( SCANNER_MESSAGE ) ); + + if (msg == NULL) { + + hr = ERROR_NOT_ENOUGH_MEMORY; + goto main_cleanup; + } + + memset( &msg->Ovlp, 0, sizeof( OVERLAPPED ) ); + + // + // Request messages from the filter driver. + // + + hr = FilterGetMessage( port, + &msg->MessageHeader, + FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ), + &msg->Ovlp ); + + if (hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING )) { + + free( msg ); + goto main_cleanup; + } + } + } + + hr = S_OK; + + WaitForMultipleObjectsEx( i, threads, TRUE, INFINITE, FALSE ); + +main_cleanup: + + printf( "Scanner: All done. Result = 0x%08x\n", hr ); + + CloseHandle( port ); + CloseHandle( completion ); + + return hr; +} + diff --git a/filesys/miniFilter/scanner/user/scanUser.rc b/filesys/miniFilter/scanner/user/scanUser.rc new file mode 100644 index 00000000..3178c5b5 --- /dev/null +++ b/filesys/miniFilter/scanner/user/scanUser.rc @@ -0,0 +1,10 @@ +#include +#include + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Scanner control program" +#define VER_INTERNALNAME_STR "scanuser.exe" +#define VER_ORIGINALFILENAME_STR "scanuser.exe" + +#include "common.ver" diff --git a/filesys/miniFilter/scanner/user/scanuser.h b/filesys/miniFilter/scanner/user/scanuser.h new file mode 100644 index 00000000..6782db84 --- /dev/null +++ b/filesys/miniFilter/scanner/user/scanuser.h @@ -0,0 +1,67 @@ +/*++ + +Copyright (c) 1999-2002 Microsoft Corporation + +Module Name: + + scanuser.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes for the + user mode part of the scanner. + +Environment: + + Kernel & user mode + +--*/ +#ifndef __SCANUSER_H__ +#define __SCANUSER_H__ + +#pragma pack(1) + +typedef struct _SCANNER_MESSAGE { + + // + // Required structure header. + // + + FILTER_MESSAGE_HEADER MessageHeader; + + + // + // Private scanner-specific fields begin here. + // + + SCANNER_NOTIFICATION Notification; + + // + // Overlapped structure: this is not really part of the message + // However we embed it instead of using a separately allocated overlap structure + // + + OVERLAPPED Ovlp; + +} SCANNER_MESSAGE, *PSCANNER_MESSAGE; + +typedef struct _SCANNER_REPLY_MESSAGE { + + // + // Required structure header. + // + + FILTER_REPLY_HEADER ReplyHeader; + + // + // Private scanner-specific fields begin here. + // + + SCANNER_REPLY Reply; + +} SCANNER_REPLY_MESSAGE, *PSCANNER_REPLY_MESSAGE; + +#endif // __SCANUSER_H__ + + diff --git a/filesys/miniFilter/scanner/user/scanuser.vcxproj b/filesys/miniFilter/scanner/user/scanuser.vcxproj new file mode 100644 index 00000000..a60beb42 --- /dev/null +++ b/filesys/miniFilter/scanner/user/scanuser.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {EF224A50-E448-4767-AD5E-1EEF058D0E50} + $(MSBuildProjectName) + Debug + Win32 + {2640B41E-C043-4A8E-B6EE-136BFFA9D855} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + scanuser + + + scanuser + + + scanuser + + + scanuser + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);fltLib.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters b/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters new file mode 100644 index 00000000..468b6701 --- /dev/null +++ b/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters @@ -0,0 +1,27 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {2CE1DA5C-939F-4E19-AECC-C2BF08201270} + + + h;hpp;hxx;hm;inl;inc;xsd + {5E1F3D4B-5B51-4AD3-937E-D0FD3FC41DEC} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {FCEF13F3-09F2-4ED0-9EA6-455F60759ED5} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/simrep/ReadMe.md b/filesys/miniFilter/simrep/ReadMe.md new file mode 100644 index 00000000..03c6847c --- /dev/null +++ b/filesys/miniFilter/simrep/ReadMe.md @@ -0,0 +1,19 @@ +SimRep File System Minifilter Driver +==================================== + +SimRep is a sample filter that demonstrates how a file system filter can simulate file-system like reparse-point behavior to redirect a file open to an alternate path. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +Normally, if the file-system sees an open for a file with a reparse-point on it, the filesystem fills out the tag buffer and returns STATUS\_REPARSE. Minifilters see the post-operation callback for this create. As the create travels up file system filter stack in post-create path, each minifilter has the opportunity to interpret the reparse point if they own the tag. If no file system filter claims the tag, IO Manager will attempt to interpret the tag based on tags known to and serviced by IO Manager. If the tag is unknown to IO manager then the create is failed with STATUS\_IO\_REPARSE\_TAG\_NOT\_HANDLED. SimRep does not demonstrate how to handle the case where the file system hits a reparse-point on the file. Instead it "fakes" encountering a reparse point before the create reaches the filesystem. When SimRep detects a create for a path that it is redirecting, SimRep replaces the file name in the file object and completes the open with STATUS\_REPARSE. This means we reparse without actually going to the file system. + +SimRep decides to reparse according to a mapping. The mapping is made up of a "New Mapping Path" and an "Old Mapping Path". The old mapping path is the path which SimRep looks for on incoming opens. If the path specified for the create is down the Old Mapping Path, then SimRep will strip off the Old Mapping Path, and replace it with the New Mapping Path. By default, the Old Mapping Path is \\x\\y and the New Mapping Path is \\a\\b. So an open to \\x\\y\\z will be replaced with an open to \\a\\b\\z. These defaults are defined as registry keys at install time and are loaded on DriverEntry. See simrep.inf for details. + +It is important to note that SimRep does not take long and short names into account. It literally does a string comparison to detect overlap with the mapping paths. SimRep also handles IRP\_MJ\_NETWORK\_QUERY\_OPEN. Because network query opens are FastIo operations, they cannot be reparsed. This means network query opens which need to be redirected must be failed with FLT\_PREOP\_DISALLOW\_FASTIO. This will cause the Io Manager to reissue the open as a regular IRP based open. To prevent performance regression, SimRep only fails network query opens which need to be reparsed. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/simrep/simrep.c b/filesys/miniFilter/simrep/simrep.c new file mode 100644 index 00000000..4daf17ca --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.c @@ -0,0 +1,3006 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + SimRep.c + +Abstract: + +The Simulate Reparse Sample demonstrates how to return STATUS_REPARSE +on precreates. This allows the filter to redirect opens down one path +to another path. The Precreate path is complicated by network query opens +which come down as Fast IO. Fast IO cannot be redirected with Status Reparse +because reparse only works on IRP based IO. + +Simulating reparse points requires that the filter replace the name in the +file object. This will cause Driver Verifier to complain that the filter is +leaking pool and will prevent it from being unloaded. To solve this issue +SimRep attempts to use a Windows 7 Function called IoReplaceFileObjectName +which will allow IO Mgr to replace the name for us with the correct pool tag. +However, on downlevel OS Versions SimRep will go ahead and replace the name +itself. + +It is important to note that SimRep only demonstrates how to return +STATUS_REPARSE, not how to deal with file names on NT. SimRep uses two strings +to act as a mapping. When the file open name starts with the "old name mapping" +string the filter replaces it with the "new name mapping" string. This does not +take short names into account. + +SimRep can also be configured to redirect renames and creation of hardlinks. +This functionality is demonstrated in the code and can be turned on with a +registry key value indicated in the inf file. To correctly handle rename and +set link operations: +1. SimRep has to reparse opens with the SL_OPEN_TARGET_DIRECTORY flag set in + the pre-create, since this is the create that IoManager uses to open the + target of the rename. +2. SimRep implements a "pass-through" name provider. It needs to do this so + that the creates issued to resolve normalized name queries will be seen by + SimRep and it can redirect them correctly, so as to provide consistent names + to other filters. +3. SimRep has to monitor IRP_MJ_SET_INFORMATION for rename and set link + operations and re-issue them for the correct destination so that filters + below SimRep are made aware of this redirection. + +Note that SimRep simply redirects creates (and optionally renames and set +hardlink) operations. It makes no attempt to virtualize the namespace for +filters above SimRep. So the layers above SimRep will be aware of the +redirection if they query the name of the file once the create, rename or set +hardlink operation is complete. + + + +Environment: + + Kernel mode + + +--*/ + +// +// Enabled warnings +// + +#pragma warning(error:4100) // Enable-Unreferenced formal parameter +#pragma warning(error:4101) // Enable-Unreferenced local variable +#pragma warning(error:4061) // Enable-missing enumeration in switch statement +#pragma warning(error:4505) // Enable-identify dead functions + +// +// Includes +// + + +// +// This sample contains OS version specific code. If compiled for VISTA it +// will not run properly on older versions of Windows. +// +#define SIMREP_VISTA (NTDDI_VERSION >= NTDDI_VISTA) + +#include + + +// +// Memory Pool Tags +// + +#define SIMREP_STRING_TAG 'tSpR' +#define SIMREP_REG_TAG 'eRpR' + +// +// Constants +// + +#define REPLACE_ROUTINE_NAME_STRING L"IoReplaceFileObjectName" + +#define REPLACE_QUERY_DIRECTORY_FILE_ROUTINE_NAME_STRING "FltQueryDirectoryFile" + + +// +// Context sample filter global data structures. +// + +typedef struct _MAPPING_ENTRY { + + // + // Path underwhich we want to reparse. + // + + UNICODE_STRING OldName; + + // + // Path to reparse to. + // + + UNICODE_STRING NewName; + +} MAPPING_ENTRY, *PMAPPING_ENTRY; + + +// +// Starting with windows 7, the IO Manager provides IoReplaceFileObjectName, +// but old versions of Windows will not have this function. Rather than just +// writing our own function, and forfeiting future windows functionality, we can +// use MmGetRoutineAddr, which will allow us to dynamically import IoReplaceFileObjectName +// if it exists. If not it allows us to implement the function ourselves. +// + +typedef +NTSTATUS +(* PReplaceFileObjectName ) ( + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(FileNameLength) PWSTR NewFileName, + _In_ USHORT FileNameLength + ); + +typedef +NTSTATUS +(FLTAPI *PFltQueryDirectoryFile)( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(Length) PVOID FileInformationBuffer, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan, + _Out_opt_ PULONG LengthReturned + ); + + +typedef struct _SIMREP_GLOBAL_DATA { + + // + // Handle to minifilter returned from FltRegisterFilter() + // + + PFLT_FILTER Filter; + + // + // Structure to hold mapping information. + // + + MAPPING_ENTRY Mapping; + + // + // Pointer to the function we will use to + // replace file names. + // + + PReplaceFileObjectName ReplaceFileNameFunction; + + // + // Pointer to the function we will use to + // query directory file. + // + + PFltQueryDirectoryFile QueryDirectoryFileFunction; + + // + // Flag to control if the filter remaps renames + // + + BOOLEAN RemapRenamesAndLinks; + +#if DBG + + // + // Field to control nature of debug output + // + + ULONG DebugLevel; +#endif + +} SIMREP_GLOBAL_DATA, *PSIMREP_GLOBAL_DATA; + + +// +// Debug helper functions +// + +#if DBG + + +#define DEBUG_TRACE_ERROR 0x00000001 // Errors - whenever we return a failure code +#define DEBUG_TRACE_LOAD_UNLOAD 0x00000002 // Loading/unloading of the filter +#define DEBUG_TRACE_INSTANCES 0x00000004 // Attach / detach of instances + +#define DEBUG_TRACE_REPARSE_OPERATIONS 0x00000008 // Operations that are performed to determine if we should return STATUS_REPARSE +#define DEBUG_TRACE_REPARSED_OPERATIONS 0x00000010 // Operations that return STATUS_REPARSE +#define DEBUG_TRACE_REPARSED_REISSUE 0X00000020 // Operations that need to be reissued with an IRP. + +#define DEBUG_TRACE_NAME_OPERATIONS 0x00000040 // Operations involving name provider callbacks + +#define DEBUG_TRACE_RENAME_REDIRECTION_OPERATIONS 0x00000080 // Operations involving rename or hardlink redirection + +#define DEBUG_TRACE_ALL_IO 0x00000100 // All IO operations tracked by this filter + +#define DEBUG_TRACE_ALL 0xFFFFFFFF // All flags + + +#define DebugTrace(Level, Data) \ + if ((Level) & Globals.DebugLevel) { \ + DbgPrint Data; \ + } + + +#else + +#define DebugTrace(Level, Data) {NOTHING;} + +#endif + + +// +// Function that handle driver load/unload and instance setup/cleanup +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +SimRepSetConfiguration( + _In_ PUNICODE_STRING RegistryPath + ); + +VOID SimRepFreeGlobals( + ); + +NTSTATUS +SimRepUnload ( + FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +SimRepInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +NTSTATUS +SimRepInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +// +// Functions that track operations on the volume +// + +FLT_PREOP_CALLBACK_STATUS +SimRepPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +SimRepPreNetworkQueryOpen ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +// +// Functions to support rename and hard link creation remapping +// + +FLT_PREOP_CALLBACK_STATUS +SimRepPreSetInformation ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +// +// Functions that provide string allocation support +// + +_When_(return==0, _Post_satisfies_(String->Buffer != NULL)) +NTSTATUS +SimRepAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +VOID +SimRepFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +NTSTATUS +SimRepReplaceFileObjectName ( + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(FileNameLength) PWSTR NewFileName, + _In_ USHORT FileNameLength + ); + +BOOLEAN +SimRepCompareMapping( + _In_ PFLT_FILE_NAME_INFORMATION NameInfo, + _In_ PUNICODE_STRING MappingPath, + _In_ BOOLEAN IgnoreCase, + _Out_opt_ PBOOLEAN ExactMatch + ); + +NTSTATUS +SimRepMungeName( + _In_ PFLT_FILE_NAME_INFORMATION NameInfo, + _In_ PUNICODE_STRING SubPath, + _In_ PUNICODE_STRING NewSubPath, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ExactMatch, + _Out_ PUNICODE_STRING MungedPath + ); + +// +// Functions that implement a pass through name provider +// + +NTSTATUS +SimRepGenerateFileName ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _When_(FileObject->FsContext != NULL, _In_opt_) + _When_(FileObject->FsContext == NULL, _In_) + PFLT_CALLBACK_DATA Cbd, + _In_ FLT_FILE_NAME_OPTIONS NameOptions, + _Out_ PBOOLEAN CacheFileNameInformation, + _Inout_ PFLT_NAME_CONTROL FileName + ); + +NTSTATUS +SimRepNormalizeNameComponent ( + _In_ PFLT_INSTANCE Instance, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ); + +#if SIMREP_VISTA +NTSTATUS +SimRepNormalizeNameComponentEx ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ); +#endif + +NTSTATUS +SimRepQueryDirectoryFile ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_writes_bytes_(Length) PVOID FileInformationBuffer, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan, + _Out_opt_ PULONG LengthReturned + ); + + +// +// Filter callback routines +// + +FLT_OPERATION_REGISTRATION Callbacks[] = { + + { IRP_MJ_CREATE, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + SimRepPreCreate, + NULL }, + + { IRP_MJ_NETWORK_QUERY_OPEN, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + SimRepPreNetworkQueryOpen, + NULL }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Filter registration data structure +// + +FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + NULL, // Context + Callbacks, // Operation callbacks + SimRepUnload, // Filters unload routine + SimRepInstanceSetup, // InstanceSetup routine + SimRepInstanceQueryTeardown, // InstanceQueryTeardown routine + NULL, // InstanceTeardownStart routine + NULL, // InstanceTeardownComplete routine + NULL, // Filename generation support callback + NULL, // Filename normalization support callback + NULL, // Normalize name component cleanup callback +#if SIMREP_VISTA + NULL, // Transaction notification callback + NULL // Filename normalization support callback + +#endif // SIMREP_VISTA +}; + + +// +// Filter callback routines with rename handling +// + +FLT_OPERATION_REGISTRATION CallbacksWithRename[] = { + + { IRP_MJ_CREATE, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + SimRepPreCreate, + NULL }, + + { IRP_MJ_NETWORK_QUERY_OPEN, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + SimRepPreNetworkQueryOpen, + NULL }, + + { IRP_MJ_SET_INFORMATION, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + SimRepPreSetInformation, + NULL }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Filter registration data structure with renames +// Filter registers as a name provider and for SetInformation +// + +FLT_REGISTRATION FilterRegistrationWithRename = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + NULL, // Context + CallbacksWithRename, // Operation callbacks + SimRepUnload, // Filters unload routine + SimRepInstanceSetup, // InstanceSetup routine + SimRepInstanceQueryTeardown, // InstanceQueryTeardown routine + NULL, // InstanceTeardownStart routine + NULL, // InstanceTeardownComplete routine + SimRepGenerateFileName, // Filename generation support callback + SimRepNormalizeNameComponent, // Filename normalization support callback + NULL, // Normalize name component cleanup callback +#if SIMREP_VISTA + NULL, // Transaction notification callback + SimRepNormalizeNameComponentEx // Filename normalization support callback + +#endif // SIMREP_VISTA +}; + + +// +// Global variables +// + +SIMREP_GLOBAL_DATA Globals; + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, SimRepSetConfiguration) +#pragma alloc_text(PAGE, SimRepUnload) +#pragma alloc_text(PAGE, SimRepInstanceSetup) +#pragma alloc_text(PAGE, SimRepInstanceQueryTeardown) +#pragma alloc_text(PAGE, SimRepAllocateUnicodeString) +#pragma alloc_text(PAGE, SimRepFreeUnicodeString) +#pragma alloc_text(PAGE, SimRepReplaceFileObjectName) +#pragma alloc_text(PAGE, SimRepCompareMapping) +#pragma alloc_text(PAGE, SimRepMungeName) +#pragma alloc_text(PAGE, SimRepPreCreate) +#pragma alloc_text(PAGE, SimRepPreNetworkQueryOpen) +#pragma alloc_text(PAGE, SimRepPreSetInformation) +#pragma alloc_text(PAGE, SimRepFreeGlobals) +#pragma alloc_text(PAGE, SimRepGenerateFileName) +#pragma alloc_text(PAGE, SimRepNormalizeNameComponent) +#if SIMREP_VISTA +#pragma alloc_text(PAGE, SimRepNormalizeNameComponentEx) +#endif +#pragma alloc_text(PAGE, SimRepQueryDirectoryFile) + +#endif + +// +// Filter driver initialization and unload routines +// + +#pragma warning(push) +#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this filter driver. It registers + itself with the filter manager and initializes all its global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS status; + UNICODE_STRING replaceRoutineName; + PFLT_REGISTRATION Registration; + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + // + // Set default global configuration + // + +#if DBG + + Globals.DebugLevel = DEBUG_TRACE_ALL; + +#endif + + Globals.RemapRenamesAndLinks = FALSE; + + RtlInitUnicodeString( &Globals.Mapping.NewName, NULL ); + + RtlInitUnicodeString( &Globals.Mapping.OldName, NULL ); + + // + // Import function to replace file names. + // + + RtlInitUnicodeString( &replaceRoutineName, REPLACE_ROUTINE_NAME_STRING ); + + Globals.ReplaceFileNameFunction = MmGetSystemRoutineAddress( &replaceRoutineName ); + if (Globals.ReplaceFileNameFunction == NULL) { + + Globals.ReplaceFileNameFunction = SimRepReplaceFileObjectName; + } + + // + // If available (Windows Vista or later), use the FltQueryDirectoryFile API. + // + + Globals.QueryDirectoryFileFunction = FltGetRoutineAddress( REPLACE_QUERY_DIRECTORY_FILE_ROUTINE_NAME_STRING ); + + // + // Set the filter configuration based on registry keys + // + + status = SimRepSetConfiguration( RegistryPath ); + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[SimRep]: Driver being loaded\n") ); + + if (!NT_SUCCESS( status )) { + + goto DriverEntryCleanup; + } + + // + // Register with the filter manager. If the filter is not + // configured to remap renames and hardlink creation do not + // register name provider or SetInformation callbacks. + // + + Registration = (Globals.RemapRenamesAndLinks == FALSE) ? + &FilterRegistration : &FilterRegistrationWithRename; + + status = FltRegisterFilter( DriverObject, + Registration, + &Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + goto DriverEntryCleanup; + } + + // + // Start filtering I/O + // + + status = FltStartFiltering( Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( Globals.Filter ); + } + + +DriverEntryCleanup: + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[SimRep]: Driver loaded complete (Status = 0x%08X)\n", + status) ); + + if (!NT_SUCCESS( status )) { + + SimRepFreeGlobals(); + } + + return status; +} +#pragma warning(pop) + +NTSTATUS +SimRepSetConfiguration( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Descrition: + + This routine sets the filter configuration based on registry values. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + Returns the status of this operation. + + +--*/ +{ + NTSTATUS status; + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey = NULL; + UNICODE_STRING valueName; + UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; + PKEY_VALUE_PARTIAL_INFORMATION value = (PKEY_VALUE_PARTIAL_INFORMATION)buffer; + ULONG valueLength = sizeof(buffer); + ULONG resultLength; + PKEY_VALUE_PARTIAL_INFORMATION mappingValue = NULL; + ULONG mappingValueLength = 0; + WCHAR oldMappingTail; + WCHAR newMappingTail; + + PAGED_CODE(); + + // + // Open the SimRep registry key. + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (!NT_SUCCESS( status )) { + + goto SimRepSetConfigurationCleanup; + } + + +#if DBG + + // + // Query the debug level + // + + RtlInitUnicodeString( &valueName, L"DebugLevel" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + value, + valueLength, + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.DebugLevel = *(PULONG)value->Data; + } + +#endif + + + // + // Query the remap rename flag + // + + RtlInitUnicodeString( &valueName, L"RemapRenamesAndLinks" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + value, + valueLength, + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.RemapRenamesAndLinks = *(PULONG)value->Data > 0 ? TRUE : FALSE; + } + + // + // Query the length of the old mapping. + // + + RtlInitUnicodeString( &valueName, L"OldMapping" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + NULL, + 0, + &mappingValueLength ); + + if (status!=STATUS_BUFFER_TOO_SMALL && status!=STATUS_BUFFER_OVERFLOW) { + + status = STATUS_INVALID_PARAMETER; + goto SimRepSetConfigurationCleanup; + } + + // + // Extract the old mapping string. + // + + mappingValue = ExAllocatePoolWithTag( PagedPool, + mappingValueLength, + SIMREP_REG_TAG ); + + if (mappingValue == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto SimRepSetConfigurationCleanup; + } + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + mappingValue, + mappingValueLength, + &resultLength ); + + if (!NT_SUCCESS( status )) { + + goto SimRepSetConfigurationCleanup; + } + + if (mappingValue->Type != REG_SZ) { + + status = STATUS_INVALID_PARAMETER; + goto SimRepSetConfigurationCleanup; + } + + Globals.Mapping.OldName.MaximumLength = (USHORT)mappingValue->DataLength; + + status = SimRepAllocateUnicodeString( &Globals.Mapping.OldName ); + + if (!NT_SUCCESS( status )) { + + goto SimRepSetConfigurationCleanup; + } + + // + // The length which we receive from ZwQueryValueKey contains size for + // the NULL termination as well. Since we are dealing with unicode + // string we'll chop off the null termination in the length. + // + + Globals.Mapping.OldName.Length = (USHORT)mappingValue->DataLength - sizeof( UNICODE_NULL ); + + RtlCopyMemory(Globals.Mapping.OldName.Buffer, + mappingValue->Data, + Globals.Mapping.OldName.Length); + + // + // Query the length of the new mapping. + // + + RtlInitUnicodeString( &valueName, L"NewMapping" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + mappingValue, + mappingValueLength, + &mappingValueLength ); + + if (!NT_SUCCESS( status )) { + + if (status!=STATUS_BUFFER_TOO_SMALL && status!=STATUS_BUFFER_OVERFLOW) { + + goto SimRepSetConfigurationCleanup; + } + + ExFreePoolWithTag( mappingValue, SIMREP_REG_TAG ); + + mappingValue = ExAllocatePoolWithTag( PagedPool, + mappingValueLength, + SIMREP_REG_TAG ); + + if (mappingValue == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto SimRepSetConfigurationCleanup; + } + + } + + // + // Extract the new mapping string. + // + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + mappingValue, + mappingValueLength, + &mappingValueLength ); + + if (!NT_SUCCESS( status )) { + + goto SimRepSetConfigurationCleanup; + } + + + if (mappingValue->Type != REG_SZ) { + + status = STATUS_INVALID_PARAMETER; + goto SimRepSetConfigurationCleanup; + } + + Globals.Mapping.NewName.MaximumLength = (USHORT) mappingValue->DataLength; + + status = SimRepAllocateUnicodeString( &Globals.Mapping.NewName ); + + if (!NT_SUCCESS( status )) { + + goto SimRepSetConfigurationCleanup; + } + + // + // The length which we receive from ZwQueryValueKey contains size for + // the NULL termination as well. Since we are dealing with unicode + // string we'll chop off the null termination in the length. + // + + Globals.Mapping.NewName.Length = (USHORT)mappingValue->DataLength - sizeof( UNICODE_NULL ); + + RtlCopyMemory(Globals.Mapping.NewName.Buffer, + mappingValue->Data, + Globals.Mapping.NewName.Length); + + + // + // Ensure the old and new mapping are consistent in specifying either files or directories + // as determined by the presence of a trailing backslash + // + + oldMappingTail = (WCHAR)Globals.Mapping.OldName.Buffer[Globals.Mapping.OldName.Length / sizeof( WCHAR ) - 1]; + newMappingTail = (WCHAR)Globals.Mapping.NewName.Buffer[Globals.Mapping.NewName.Length / sizeof( WCHAR ) - 1]; + + if ((oldMappingTail != newMappingTail) && + ((oldMappingTail == OBJ_NAME_PATH_SEPARATOR) || + (newMappingTail == OBJ_NAME_PATH_SEPARATOR))) { + + status = STATUS_INVALID_PARAMETER; + goto SimRepSetConfigurationCleanup; + } + + +SimRepSetConfigurationCleanup: + + if (mappingValue != NULL) { + + ExFreePoolWithTag( mappingValue, SIMREP_REG_TAG ); + mappingValue = NULL; + } + + if (driverRegKey != NULL) { + + ZwClose( driverRegKey ); + } + + if (!NT_SUCCESS( status )) { + + SimRepFreeUnicodeString( &Globals.Mapping.NewName ); + SimRepFreeUnicodeString( &Globals.Mapping.OldName ); + } + + return status; +} + + + +VOID SimRepFreeGlobals( + ) +/*++ + +Routine Descrition: + + This routine cleans up the global structure on both + teardown and initialization failure. + +Arguments: + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + SimRepFreeUnicodeString( &Globals.Mapping.NewName ); + SimRepFreeUnicodeString( &Globals.Mapping.OldName ); +} + +NTSTATUS +SimRepUnload ( + FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this filter driver. This is called + when the minifilter is about to be unloaded. SimRep can unload + easily because it does not own any IOs. When the filter is unloaded + existing reparsed creates will continue to work, but new creates will + not be reparsed. This is fine from the filter's perspective, but could + result in unexpected bahavior for apps. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[SimRep]: Unloading driver\n") ); + + FltUnregisterFilter( Globals.Filter ); + + SimRepFreeGlobals(); + + return STATUS_SUCCESS; +} + + +// +// Instance setup/teardown routines. +// + +NTSTATUS +SimRepInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. This + gives us a chance to decide if we need to attach to this volume or not. + SimRep does not attach on automatic attachment, but will attach when asked + manually. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + if ( FlagOn( Flags, FLTFL_INSTANCE_SETUP_AUTOMATIC_ATTACHMENT ) ) { + + // + // Do not automatically attach to a volume. + // + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Simrep]: Instance setup skipped (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Attach on manual attachment. + // + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[SimRep]: Instance setup started (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + + return STATUS_SUCCESS; +} + + +NTSTATUS +SimRepInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach thereby giving us a + chance to fail that detach request. SimRep only implements it + because otherwise calls to FltDetachVolume or FilterDetach would + fail to detach. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Returns the status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[SimRep]: Instance query teadown ended (Instance = %p)\n", + FltObjects->Instance) ); + + return STATUS_SUCCESS; +} + + +FLT_PREOP_CALLBACK_STATUS +SimRepPreNetworkQueryOpen ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Because network query opens are FastIo operations, they cannot be reparsed. + This means network query opens which need to be redirected must be failed + with FLT_PREOP_DISALLOW_FASTIO. This will cause the Io Manager to reissue + the open as a regular IRP based open. To prevent performance regression, + only fail network query opens which need to be reparsed. + + This is pageable because it can not be called on the paging path + +Arguments: + + Cbd - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + NTSTATUS status; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + BOOLEAN match; + PIO_STACK_LOCATION irpSp; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepQueryOpen -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Initialize defaults + // + + status = STATUS_SUCCESS; + callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; // pass through - default is no post op callback + + // + // We only registered for this IRP, so thats all we better get! + // + + NT_ASSERT( Cbd->Iopb->MajorFunction == IRP_MJ_NETWORK_QUERY_OPEN ); + NT_ASSERT( FLT_IS_FASTIO_OPERATION( Cbd ) ); + + irpSp = IoGetCurrentIrpStackLocation(Cbd->Iopb->Parameters.NetworkQueryOpen.Irp); + + // + // Check if this is a paging file as we don't want to redirect + // the location of the paging file. + // + + if (FlagOn( irpSp->Flags, SL_OPEN_PAGING_FILE )) { + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Ignoring paging file open (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreNetworkQueryOpenCleanup; + } + + // + // We are not allowing volume opens to be reparsed in the sample. + // + + if (FlagOn( Cbd->Iopb->TargetFileObject->Flags, FO_VOLUME_OPEN )) { + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Ignoring volume open (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreNetworkQueryOpenCleanup; + + } + + // + // Don't reparse an open by ID because it is not possible to determine create path intent. + // + + if (FlagOn( irpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID )) { + + goto SimRepPreNetworkQueryOpenCleanup; + } + + // + // A rename should never come on the fast IO path + // + + NT_ASSERT( irpSp->Flags != SL_OPEN_TARGET_DIRECTORY ); + + status = FltGetFileNameInformation( Cbd, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Failed to get name information (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreNetworkQueryOpenCleanup; + } + + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Processing create for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + // + // Parse the filename information + // + + status = FltParseFileNameInformation( nameInfo ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Failed to parse name information for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreNetworkQueryOpenCleanup; + } + + // + // Determine if this query involes a path that matches the remapping path. + // Note: if the create is case sensitive this comparison must be as well. + // + + match = SimRepCompareMapping( nameInfo, + &Globals.Mapping.OldName, + !FlagOn( irpSp->Flags, SL_CASE_SENSITIVE ), + NULL ); + + if (match) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS, + ("[SimRep]: SimRepPreNetworkQueryOpen -> File name %wZ matches mapping. (Cbd = %p, FileObject = %p)\n" + "\tMapping.OldFileName = %wZ\n" + "\tMapping.NewFileName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + Globals.Mapping.OldName, + Globals.Mapping.NewName) ); + + // + // Because the file matched the mapping, we need to redirect this open with a new name. + // + + // + // We can't return STATUS_REPARSE because it is FastIO. Return + // FLT_PREOP_DISALLOW_FASTIO, so it will be reissued down the slow path. + // + + DebugTrace(DEBUG_TRACE_REPARSED_REISSUE, + ("[SimRep]: Disallow fast IO that is to a mapped path! %wZ\n", + &nameInfo->Name) ); + + callbackStatus = FLT_PREOP_DISALLOW_FASTIO; + + } + + +SimRepPreNetworkQueryOpenCleanup: + + // + // Release the references we have acquired + // + + if (nameInfo != NULL) { + + FltReleaseFileNameInformation( nameInfo ); + } + + if (!NT_SUCCESS( status )) { + + // + // An error occurred, fail the query + // + + DebugTrace( DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreCreate -> Failed with status 0x%x \n", + status) ); + + Cbd->IoStatus.Status = status; + callbackStatus = FLT_PREOP_COMPLETE; + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return callbackStatus; + +} + + +FLT_PREOP_CALLBACK_STATUS +SimRepPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine does the work for SimRep sample. SimRepPreCreate is called in + the pre-operation path for IRP_MJ_CREATE and IRP_MJ_NETWORK_QUERY_OPEN. + The function queries the requested file name for the create and compares + it to the mapping path. If the file is down the "old mapping path", the + filter checks to see if the request is fast io based. If it is we cannot + reparse the create because fast io does not support STATUS_REPARSE. + Instead we return FLT_PREOP_DISALLOW_FASTIO to force the io to be reissued + on the IRP path. If the create is IRP based, then we replace the file + object's file name field with a new path based on the "new mapping path". + + This is pageable because it could not be called on the paging path + +Arguments: + + Cbd - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + NTSTATUS status; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + UNICODE_STRING newFileName; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreCreate -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + + // + // Initialize defaults + // + + status = STATUS_SUCCESS; + callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; // pass through - default is no post op callback + + RtlInitUnicodeString( &newFileName, NULL ); + + // + // We only registered for this irp, so thats all we better get! + // + + NT_ASSERT( Cbd->Iopb->MajorFunction == IRP_MJ_CREATE ); + + // + // Check if this is a paging file as we don't want to redirect + // the location of the paging file. + // + + if (FlagOn( Cbd->Iopb->OperationFlags, SL_OPEN_PAGING_FILE )) { + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreCreate -> Ignoring paging file open (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreCreateCleanup; + } + + // + // We are not allowing volume opens to be reparsed in the sample. + // + + if (FlagOn( Cbd->Iopb->TargetFileObject->Flags, FO_VOLUME_OPEN )) { + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreCreate -> Ignoring volume open (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreCreateCleanup; + + } + + // + // SimRep does not honor the FILE_OPEN_REPARSE_POINT create option. For a + // symbolic the caller would pass this flag, for example, in order to open + // the link for deletion. There is no concept of deleting the mapping for + // this filter so it is not clear what the purpose of honoring this flag + // would be. + // + + // + // Don't reparse an open by ID because it is not possible to determine create path intent. + // + + if (FlagOn( Cbd->Iopb->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID )) { + + goto SimRepPreCreateCleanup; + } + + if (FlagOn( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY ) && + !Globals.RemapRenamesAndLinks) { + + // + // This is a prelude to a rename or hard link creation but the filter + // is NOT configured to filter these operations. To perform the operation + // successfully and in a consistent manner this create must not trigger + // a reparse. Pass through the create without attempting any redirection. + // + + goto SimRepPreCreateCleanup; + + } + + // + // Get the name information. + // + + if (FlagOn( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY )) { + + // + // The SL_OPEN_TARGET_DIRECTORY flag indicates the caller is attempting + // to open the target of a rename or hard link creation operation. We + // must clear this flag when asking fltmgr for the name or the result + // will not include the final component. We need the full path in order + // to compare the name to our mapping. + // + + ClearFlag( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY ); + + DebugTrace( DEBUG_TRACE_RENAME_REDIRECTION_OPERATIONS, + ("[SimRep]: SimRepPreCreate -> Clearing SL_OPEN_TARGET_DIRECTORY for %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + + // + // Get the filename as it appears below this filter. Note that we use + // FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY when querying the filename + // so that the filename as it appears below this filter does not end up + // in filter manager's name cache. + // + + status = FltGetFileNameInformation( Cbd, + FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY, + &nameInfo ); + + // + // Restore the SL_OPEN_TARGET_DIRECTORY flag so the create will proceed + // for the target. The file systems depend on this flag being set in + // the target create in order for the subsequent SET_INFORMATION + // operation to proceed correctly. + // + + SetFlag( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY ); + + + } else { + + // + // Note that we use FLT_FILE_NAME_QUERY_DEFAULT when querying the + // filename. In the precreate the filename should not be in filter + // manager's name cache so there is no point looking there. + // + + status = FltGetFileNameInformation( Cbd, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + } + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreCreate -> Failed to get name information (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreCreateCleanup; + } + + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS, + ("[SimRep]: SimRepPreCreate -> Processing create for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + // + // Parse the filename information + // + + status = FltParseFileNameInformation( nameInfo ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreCreate -> Failed to parse name information for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreCreateCleanup; + } + + // + // Munge the path from the old mapping to new mapping if the query overlaps + // the mapping path. Note: if the create is case sensitive this comparison + // must be as well. + // + + status = SimRepMungeName( nameInfo, + &Globals.Mapping.OldName, + &Globals.Mapping.NewName, + !FlagOn( Cbd->Iopb->OperationFlags, SL_CASE_SENSITIVE ), + FALSE, + &newFileName); + + if (!NT_SUCCESS( status )) { + + if (status == STATUS_NOT_FOUND) { + status = STATUS_SUCCESS; + } + + goto SimRepPreCreateCleanup; + } + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS, + ("[SimRep]: SimRepPreCreate -> File name %wZ matches mapping. (Cbd = %p, FileObject = %p)\n" + "\tMapping.OldFileName = %wZ\n" + "\tMapping.NewFileName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + Globals.Mapping.OldName, + Globals.Mapping.NewName) ); + + + // + // Switch names + // + + status = Globals.ReplaceFileNameFunction( Cbd->Iopb->TargetFileObject, + newFileName.Buffer, + newFileName.Length ); + + if ( !NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreCreate -> Failed to allocate string for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject )); + + goto SimRepPreCreateCleanup; + } + + // + // Set the status to STATUS_REPARSE + // + + status = STATUS_REPARSE; + + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_REPARSED_OPERATIONS, + ("[SimRep]: SimRepPreCreate -> Returning STATUS_REPARSE for file %wZ. (Cbd = %p, FileObject = %p)\n" + "\tNewName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + &newFileName) ); + +SimRepPreCreateCleanup: + + // + // Release the references we have acquired + // + + SimRepFreeUnicodeString( &newFileName ); + + if (nameInfo != NULL) { + + FltReleaseFileNameInformation( nameInfo ); + } + + if (status == STATUS_REPARSE) { + + // + // Reparse the open + // + + Cbd->IoStatus.Status = STATUS_REPARSE; + Cbd->IoStatus.Information = IO_REPARSE; + callbackStatus = FLT_PREOP_COMPLETE; + + } else if (!NT_SUCCESS( status )) { + + // + // An error occurred, fail the open + // + + DebugTrace( DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreCreate -> Failed with status 0x%x \n", + status) ); + + Cbd->IoStatus.Status = status; + callbackStatus = FLT_PREOP_COMPLETE; + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreCreate -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return callbackStatus; + +} + + +FLT_PREOP_CALLBACK_STATUS +SimRepPreSetInformation ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre callback for handling SetInformation. + +Arguments: + + Cdb - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The context for the completion routine for this + operation. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + NTSTATUS status = STATUS_SUCCESS; + PVOID buffer = NULL; + ULONG bufferLength = 0; + FILE_INFORMATION_CLASS fileInfoClass; + PFILE_RENAME_INFORMATION renameInfo = NULL; + PFILE_RENAME_INFORMATION newRenameInfo = NULL; + PFILE_LINK_INFORMATION linkInfo = NULL; + PFILE_LINK_INFORMATION newLinkInfo = NULL; + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + UNICODE_STRING newFileName; + + struct { + BOOLEAN ReplaceIfExists; + HANDLE RootDirectory; + ULONG FileNameLength; + PWSTR FileName; + } setInfo; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( CompletionContext ); + + RtlInitUnicodeString(&newFileName, NULL); + + NT_ASSERT( Globals.RemapRenamesAndLinks ); + + fileInfoClass = Cbd->Iopb->Parameters.SetFileInformation.FileInformationClass; + + switch (fileInfoClass) { + + case FileRenameInformation: + + // + // Note: We should never see a rename of the mapping path \x\y itself + // because the name would have been reparsed to the new mapping \a\b. + // This is different than the behavior of normal reparse points where + // the same operation would reassign the reparse point. + // + + renameInfo = Cbd->Iopb->Parameters.SetFileInformation.InfoBuffer; + + setInfo.ReplaceIfExists = renameInfo->ReplaceIfExists; + setInfo.RootDirectory = renameInfo->RootDirectory; + setInfo.FileNameLength = renameInfo->FileNameLength; + setInfo.FileName = renameInfo->FileName; + + break; + + case FileLinkInformation: + + linkInfo = Cbd->Iopb->Parameters.SetFileInformation.InfoBuffer; + + setInfo.ReplaceIfExists = linkInfo->ReplaceIfExists; + setInfo.RootDirectory = linkInfo->RootDirectory; + setInfo.FileNameLength = linkInfo->FileNameLength; + setInfo.FileName = linkInfo->FileName; + + break; + + case FileDirectoryInformation: // 1 + case FileFullDirectoryInformation: // 2 + case FileBothDirectoryInformation: // 3 + case FileBasicInformation: // 4 wdm + case FileStandardInformation: // 5 wdm + case FileInternalInformation: // 6 + case FileEaInformation: // 7 + case FileAccessInformation: // 8 + case FileNameInformation: // 9 + case FileNamesInformation: // 12 + case FileDispositionInformation: // 13 + case FilePositionInformation: // 14 wdm + case FileFullEaInformation: // 15 + case FileModeInformation: // 16 + case FileAlignmentInformation: // 17 + case FileAllInformation: // 18 + case FileAllocationInformation: // 19 + case FileEndOfFileInformation: // 20 wdm + case FileAlternateNameInformation: // 21 + case FileStreamInformation: // 22 + case FilePipeInformation: // 23 + case FilePipeLocalInformation: // 24 + case FilePipeRemoteInformation: // 25 + case FileMailslotQueryInformation: // 26 + case FileMailslotSetInformation: // 27 + case FileCompressionInformation: // 28 + case FileObjectIdInformation: // 29 + case FileCompletionInformation: // 30 + case FileMoveClusterInformation: // 31 + case FileQuotaInformation: // 32 + case FileReparsePointInformation: // 33 + case FileNetworkOpenInformation: // 34 + case FileAttributeTagInformation: // 35 + case FileTrackingInformation: // 36 + case FileIdBothDirectoryInformation: // 37 + case FileIdFullDirectoryInformation: // 38 + case FileValidDataLengthInformation: // 39 + case FileShortNameInformation: // 40 + + goto SimRepPreSetInformationCleanup; + + default: + + // + // It is risky to pass through information classes that we don't + // know about. Try to catch new or invalid classes in testing. + // + + NT_ASSERTMSG("SimRep passing through unknown information class\n", FALSE); + goto SimRepPreSetInformationCleanup; + } + + // + // When this filter is configured to remap renames and hardlinks we need + // to ensure other filters see a consistent destination for the + // operation. The FileName buffer will not match the actual rename path + // when a reparse is involved and if lower filters pass it to + // FltGetDestinationFileNameInformation they will get back the wrong + // destination. To fix this we'll need to munge the FileName buffer + // explicitly. + // + // The reason FltGetDestinationFileNameInformation gives the correct + // destination here is because we send it to ourselves (the current + // provider) and, as a name provider, our filter will get the creates + // issued for the parent directory name normalization and perform the + // reparse. + // + + status = FltGetDestinationFileNameInformation( FltObjects->Instance, + FltObjects->FileObject, + setInfo.RootDirectory, + setInfo.FileName, + setInfo.FileNameLength, + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER | FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_RENAME_REDIRECTION_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreSetInformation -> Failed to get destination filename information (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreSetInformationCleanup; + } + + status = FltParseFileNameInformation( nameInfo ); + + if (!NT_SUCCESS( status )) { + + goto SimRepPreSetInformationCleanup; + } + + // + // Stream operations are already consistent regardless of whether the file + // is redirected so there is nothing to do. + // + + if (nameInfo->Stream.Length != 0) { + + goto SimRepPreSetInformationCleanup; + } + + // + // If the operation destion overlaps the new mapping get a new filename + // string to send in the request. + // + + status = SimRepMungeName( nameInfo, + &Globals.Mapping.NewName, + &Globals.Mapping.NewName, + !FlagOn( FltObjects->FileObject->Flags, FO_OPENED_CASE_SENSITIVE ), + FALSE, + &newFileName ); + + if (status == STATUS_NOT_FOUND) { + + // + // If the operation destination overlaps the old mapping exactly, get + // a new filename string munged with the new mapping to send in the + // request. This is a special case where our name provider will not + // perform the reparse during name resolution because the parent + // directories don't overlap the mapping. + // + + status = SimRepMungeName( nameInfo, + &Globals.Mapping.OldName, + &Globals.Mapping.NewName, + !FlagOn( FltObjects->FileObject->Flags, FO_OPENED_CASE_SENSITIVE ), + TRUE, + &newFileName ); + } + + if (!NT_SUCCESS( status )) { + + // + // The rename doesn't overlap the mapping at all. No need to munge + // + + if (status == STATUS_NOT_FOUND) { + status = STATUS_SUCCESS; + } + + goto SimRepPreSetInformationCleanup; + } + + // + // Explicitly set the munged the name in the set information structure so + // lower filters who see this operation will see the correct + // destination from FLT_GET_DESTINATION_FILE_NAME_INFORMATION. + // + + if (fileInfoClass == FileRenameInformation) { + + bufferLength = FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName ) + newFileName.Length; + + buffer = ExAllocatePoolWithTag( PagedPool, SIMREP_STRING_TAG, bufferLength ); + + if (buffer == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto SimRepPreSetInformationCleanup; + } + + newRenameInfo = (PFILE_RENAME_INFORMATION)buffer; + + newRenameInfo->ReplaceIfExists = renameInfo->ReplaceIfExists; + newRenameInfo->RootDirectory = NULL; + newRenameInfo->FileNameLength = newFileName.Length; + + RtlCopyMemory( &newRenameInfo->FileName, newFileName.Buffer, newFileName.Length ); + + } else if (fileInfoClass == FileLinkInformation) { + + bufferLength = FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName ) + newFileName.Length; + + buffer = ExAllocatePoolWithTag( PagedPool, SIMREP_STRING_TAG, bufferLength ); + + if (buffer == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto SimRepPreSetInformationCleanup; + } + + newLinkInfo = (PFILE_LINK_INFORMATION)buffer; + + newLinkInfo->ReplaceIfExists = linkInfo->ReplaceIfExists; + newLinkInfo->RootDirectory = NULL; + newLinkInfo->FileNameLength = newFileName.Length; + + RtlCopyMemory( &newLinkInfo->FileName, newFileName.Buffer, newFileName.Length ); + + } + + status = FltSetInformationFile( FltObjects->Instance, + FltObjects->FileObject, + buffer, + bufferLength, + fileInfoClass ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_RENAME_REDIRECTION_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreSetInformation -> Failed sending FltSetInformationFile (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreSetInformationCleanup; + } + + Cbd->IoStatus.Status = status; + + returnStatus = FLT_PREOP_COMPLETE; + + +SimRepPreSetInformationCleanup: + + if (nameInfo) { + + FltReleaseFileNameInformation( nameInfo ); + } + + if (buffer) { + + ExFreePoolWithTag( buffer, SIMREP_STRING_TAG ); + } + + SimRepFreeUnicodeString( &newFileName ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepSetInformation -> Failed with status 0x%x \n", + status) ); + + Cbd->IoStatus.Status = status; + + returnStatus = FLT_PREOP_COMPLETE; + } + + return returnStatus; + +} + + +// +// Support Routines +// + +_When_(return==0, _Post_satisfies_(String->Buffer != NULL)) +NTSTATUS +SimRepAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This routine allocates a unicode string + +Arguments: + + Size - the size in bytes needed for the string buffer + + String - supplies the size of the string to be allocated in the MaximumLength field + return the unicode string + +Return Value: + + STATUS_SUCCESS - success + STATUS_INSUFFICIENT_RESOURCES - failure + +--*/ +{ + + PAGED_CODE(); + + String->Buffer = ExAllocatePoolWithTag( NonPagedPool, + String->MaximumLength, + SIMREP_STRING_TAG ); + + if (String->Buffer == NULL) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[SimRep]: Failed to allocate unicode string of size 0x%x\n", + String->MaximumLength) ); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + String->Length = 0; + + return STATUS_SUCCESS; +} + + +VOID +SimRepFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This routine frees a unicode string + +Arguments: + + String - supplies the string to be freed + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + if (String->Buffer) { + + ExFreePoolWithTag( String->Buffer, + SIMREP_STRING_TAG ); + String->Buffer = NULL; + } + + String->Length = String->MaximumLength = 0; + String->Buffer = NULL; +} + + +NTSTATUS +SimRepReplaceFileObjectName ( + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(FileNameLength) PWSTR NewFileName, + _In_ USHORT FileNameLength + ) +/*++ +Routine Description: + + This routine is used to replace a file object's name + with a provided name. This should only be called if + IoReplaceFileObjectName is not on the system. + If this function is used and verifier is enabled + the filter will fail to unload due to a false + positive on the leaked pool test. + +Arguments: + + FileObject - Pointer to file object whose name is to be replaced. + + NewFileName - Pointer to buffer containing the new name. + + FileNameLength - Length of the new name in bytes. + +Return Value: + + STATUS_INSUFFICIENT_RESOURCES - No memory to allocate the new buffer. + + STATUS_SUCCESS otherwise. + +--*/ +{ + PWSTR buffer; + PUNICODE_STRING fileName; + USHORT newMaxLength; + + PAGED_CODE(); + + fileName = &FileObject->FileName; + + // + // If the new name fits inside the current buffer we simply copy it over + // instead of allocating a new buffer (and keep the MaximumLength value + // the same). + // + if (FileNameLength <= fileName->MaximumLength) { + + goto CopyAndReturn; + } + + // + // Use an optimal buffer size + // + newMaxLength = FileNameLength; + + buffer = ExAllocatePoolWithTag( PagedPool, + newMaxLength, + SIMREP_STRING_TAG ); + + if (!buffer) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (fileName->Buffer != NULL) { + + ExFreePool(fileName->Buffer); + } + + fileName->Buffer = buffer; + fileName->MaximumLength = newMaxLength; + +CopyAndReturn: + + fileName->Length = FileNameLength; + RtlZeroMemory(fileName->Buffer, fileName->MaximumLength); + RtlCopyMemory(fileName->Buffer, NewFileName, FileNameLength); + + return STATUS_SUCCESS; +} + + +NTSTATUS +SimRepMungeName( + _In_ PFLT_FILE_NAME_INFORMATION NameInfo, + _In_ PUNICODE_STRING SubPath, + _In_ PUNICODE_STRING NewSubPath, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ExactMatch, + _Out_ PUNICODE_STRING MungedPath + ) +/*++ +Routine Description: + + This routine will create a new path by munginging a new subpath + over and existing subpath. + +Arguments: + + NameInfo - Pointer to the name information for the file. + + SubPath - The path to munge. + + IgnoreCase - If TRUE do a case insenstive comparison. + + ExactMatch - If TRUE only proceed if the whole path will be replaced + + MungedPath - A unicode string to received the munged path created. The + buffer of the string will be allocated in this function. + +Return Value: + + STATUS_SUCCESS - the path was successfully munged + STATUS_NOT_FOUND - the SubPath was not found or is not an exact match + An appropriate NTSTATUS error otherwise. + +--*/ +{ + NTSTATUS status = STATUS_NOT_FOUND; + BOOLEAN match; + BOOLEAN exactMatch; + USHORT length; + + PAGED_CODE(); + + match = SimRepCompareMapping( NameInfo, SubPath, IgnoreCase, &exactMatch ); + + if (match) { + + if (ExactMatch && !exactMatch) { + + goto SimRepMungeNameCleanup; + } + + NT_ASSERT( NameInfo->Name.Length >= SubPath->Length ); + + length = NameInfo->Name.Length - SubPath->Length + NewSubPath->Length; + + RtlInitUnicodeString( MungedPath, NULL ); + + MungedPath->MaximumLength = (USHORT)length; + + status = SimRepAllocateUnicodeString( MungedPath ); + + if (!NT_SUCCESS( status )) { + + goto SimRepMungeNameCleanup; + } + + // + // Copy the volume portion of the name (part of the name preceding the matching part) + // + + RtlCopyUnicodeString( MungedPath, &NameInfo->Volume ); + + // + // Copy the new file name in place of the matching part of the name + // + + status = RtlAppendUnicodeStringToString( MungedPath, NewSubPath ); + + NT_ASSERT( NT_SUCCESS( status ) ); + + // + // Copy the portion of the name following the matching part of the name + // + + RtlCopyMemory( Add2Ptr( MungedPath->Buffer, NameInfo->Volume.Length + NewSubPath->Length ), + Add2Ptr( NameInfo->Name.Buffer, NameInfo->Volume.Length + SubPath->Length ), + NameInfo->Name.Length - NameInfo->Volume.Length - SubPath->Length ); + + // + // Compute the final length of the new name + // + + MungedPath->Length = length; + + } + +SimRepMungeNameCleanup: + + return status; +} + +BOOLEAN +SimRepCompareMapping( + _In_ PFLT_FILE_NAME_INFORMATION NameInfo, + _In_ PUNICODE_STRING MappingPath, + _In_ BOOLEAN IgnoreCase, + _Out_opt_ PBOOLEAN ExactMatch + ) +/*++ +Routine Description: + + This routine will compare the file specified by the + name information structure to the given mapping path + to determine if the file is the mapping path itself + or a child of the mapping path. + +Arguments: + + NameInfo - Pointer to the name information for the file. + + MappingPath - The mapping path to compare against. + + IgnoreCase - If TRUE do a case insenstive comparison. + + ExactMatch - If supplied receives TRUE if the name exactly + matches the mapping path. + +Return Value: + + TRUE - the file matches the mapping path + + FALSE - the file is not in the mapping path + +--*/ +{ + UNICODE_STRING fileName; + BOOLEAN match; + BOOLEAN exactMatch; + + PAGED_CODE(); + + // + // The NameInfo parameter is assumed to have been parsed + // + + NT_ASSERT (FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_FINAL_COMPONENT) && + FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_EXTENSION) && + FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_STREAM) && + FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_PARENT_DIR)); + + // + // Point filename to the name of the file, excluding the name of the volume + // + + NT_ASSERT( NameInfo->Name.Buffer == NameInfo->Volume.Buffer ); + NT_ASSERT( NameInfo->Name.Length >= NameInfo->Volume.Length); + + match = FALSE; + exactMatch = FALSE; + fileName.Buffer = Add2Ptr( NameInfo->Name.Buffer, NameInfo->Volume.Length ); + fileName.MaximumLength = NameInfo->Name.Length - NameInfo->Volume.Length; + fileName.Length = fileName.MaximumLength; + + // + // Check if the filename matches this mapping entry (is the mapping + // entry itself or some child directory of the mapping entry) + // + + if (RtlPrefixUnicodeString( MappingPath, &fileName, IgnoreCase )) { + + if (fileName.Length == MappingPath->Length) { + + // + // This path is the mapping itself + // + + match = TRUE; + + exactMatch = TRUE; + + } else if (fileName.Buffer[(MappingPath->Length/sizeof( WCHAR ))] == OBJ_NAME_PATH_SEPARATOR) { + + // + // This path is a child of the mapping + // + + match = TRUE; + } + + // + // No match here means the path simply overlaps the mapping like + // \a\b\c overlaps \a\b\cd.txt + // + + } + + if (ARGUMENT_PRESENT( ExactMatch )) { + *ExactMatch = exactMatch; + } + + return match; +} + + +// +// In order to remap renames and hard links correctly SimRep needs +// to be called as part of name resolution. To achieve this SimRep +// must be a name provider, albeit a simple "pass through" provider. +// SimRep is is only demonstrating how to simulate reparse points, +// not how to virtualize a namespace. Hence the name provider does +// not munge the names but simply passes the name queries through. +// + +NTSTATUS +SimRepGenerateFileName ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _When_(FileObject->FsContext != NULL, _In_opt_) + _When_(FileObject->FsContext == NULL, _In_) + PFLT_CALLBACK_DATA Cbd, + _In_ FLT_FILE_NAME_OPTIONS NameOptions, + _Out_ PBOOLEAN CacheFileNameInformation, + _Inout_ PFLT_NAME_CONTROL FileName + ) +/*++ + +Routine Description: + + This routine generates a file name of the type specified in NameFormat + for the specified file object. + +Arguments: + + Instance - Opaque instance pointer for the minifilter driver instance that + this callback routine is registered for. + + FileObject - The fileobject for which the name is being requested. + + Cbd - If non-NULL, the CallbackData structure defining the operation + we are in the midst of processing when this name is queried. + + NameOptions - value that specifies the name format, query method, and flags + for this file name information query + + CacheFileNameInformation - A pointer to a Boolean value specifying whether + this name can be cached. + + FileName - A pointer to a filter manager-allocated FLT_NAME_CONTROL + structure to receive the file name on output + +Return Value: + + Returns STATUS_SUCCESS if a name could be returned, or the appropriate + error otherwise. + +--*/ +{ + PFLT_FILE_NAME_INFORMATION userFileNameInfo = NULL; + PUNICODE_STRING userFileName; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + + // + // Clear FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER from the name options + // We pass the same name options when we issue a name query to satisfy this + // name query. We want that name query to be targeted below simrep.sys and + // not recurse into simrep.sys + // + + ClearFlag( NameOptions, FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER ); + + if (FileObject->FsContext == NULL) { + + + // + // This file object has not yet been opened. We will query the filter + // manager for the name and return that name. We must use the original + // NameOptions we received in the query. If we were to swallow flags + // such as FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY or + // FLT_FILE_NAME_DO_NOT_CACHE we could corrupt the name cache. + // + + status = FltGetFileNameInformation( Cbd, + NameOptions, + &userFileNameInfo ); + + if (!NT_SUCCESS( status )) { + + goto SimRepGenerateFileNameCleanup; + } + + userFileName = &userFileNameInfo->Name; + + } else { + + // + // The file has been opened. If the call is not in the context of an IO + // operation (we don't have a callback data), we have to get the + // filename with FltGetFilenameInformationUnsafe using the fileobject. + // Note, the only way we won't have a callback is if someone called + // FltGetFileNameInformationUnsafe already. + // + + if (ARGUMENT_PRESENT( Cbd )) { + + status = FltGetFileNameInformation( Cbd, + NameOptions, + &userFileNameInfo ); + + } else { + + status = FltGetFileNameInformationUnsafe( FileObject, + Instance, + NameOptions, + &userFileNameInfo ); + + } + + if (!NT_SUCCESS( status )) { + + goto SimRepGenerateFileNameCleanup; + } + + userFileName = &userFileNameInfo->Name; + + } + + status = FltCheckAndGrowNameControl( FileName, + userFileName->Length ); + + if (!NT_SUCCESS( status )) { + + goto SimRepGenerateFileNameCleanup; + } + + RtlCopyUnicodeString( &FileName->Name, userFileName ); + + // + // If the file object is unopened then the name of the stream represented by + // the file object may change from pre-create to post-create. + // For example, the name being opened could actually be a symbolic link + // + + *CacheFileNameInformation = (FileObject->FsContext != NULL); + + +SimRepGenerateFileNameCleanup: + + if (userFileNameInfo != NULL) { + + FltReleaseFileNameInformation( userFileNameInfo ); + } + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_NAME_OPERATIONS | DEBUG_TRACE_ERROR, + ("SimRepGenerateFileName: failed %x\n", + status) ); + } + + return status; +} + + +NTSTATUS +SimRepNormalizeNameComponent ( + _In_ PFLT_INSTANCE Instance, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ) +/*++ + +Routine Description: + + This routine normalizes, converts to a long name if needed, a name component. + +Arguments: + + Instance - Opaque instance pointer for the minifilter driver instance that + this callback routine is registered for. + + ParentDirectory - Pointer to a UNICODE_STRING structure that contains the + name of the parent directory for this name component. + + VolumeNameLength - Length, in bytes, of the parent directory name that is + stored in the structure that the ParentDirectory parameter points to. + + Component - Pointer to a UNICODE_STRING structure that contains the name + component to be expanded. + + ExpandComponentName - Pointer to a FILE_NAMES_INFORMATION structure that + receives the expanded (normalized) file name information for the name component. + + ExpandComponentNameLength - Length, in bytes, of the buffer that the + ExpandComponentName parameter points to. + + Flags - Name normalization flags. + + NormalizationContext - Pointer to minifilter driver-provided context + information to be passed in any subsequent calls to this callback routine + that are made to normalize the remaining components in the same file name + path. + +Return Value: + + Returns STATUS_SUCCESS if a name could be returned, or the appropriate + error otherwise. + +--*/ +{ + NTSTATUS status; + HANDLE directoryHandle = NULL; + PFILE_OBJECT directoryFileObject = NULL; + OBJECT_ATTRIBUTES objAttributes; + IO_STATUS_BLOCK ioStatusBlock; + BOOLEAN ignoreCase = !BooleanFlagOn( Flags, + FLTFL_NORMALIZE_NAME_CASE_SENSITIVE ); + + UNREFERENCED_PARAMETER( NormalizationContext ); + UNREFERENCED_PARAMETER( DeviceNameLength ); + + PAGED_CODE(); + + // + // Validate the buffer is big enough + // + + if (ExpandComponentNameLength < sizeof(FILE_NAMES_INFORMATION)) { + + return STATUS_INVALID_PARAMETER; + } + + InitializeObjectAttributes( &objAttributes, + (PUNICODE_STRING)ParentDirectory, + OBJ_KERNEL_HANDLE + | (ignoreCase ? OBJ_CASE_INSENSITIVE : 0), + NULL, + NULL ); + + status = FltCreateFile( Globals.Filter, + Instance, + &directoryHandle, + FILE_LIST_DIRECTORY | SYNCHRONIZE, // DesiredAccess + &objAttributes, + &ioStatusBlock, + NULL, // AllocationSize + FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_NORMAL, // FileAttributes + FILE_SHARE_READ + | FILE_SHARE_WRITE + | FILE_SHARE_DELETE, // ShareAccess + FILE_OPEN, // CreateDisposition + FILE_DIRECTORY_FILE + | FILE_SYNCHRONOUS_IO_NONALERT + | FILE_OPEN_FOR_BACKUP_INTENT, // CreateOptions + NULL, // EaBuffer + 0, // EaLength + IO_IGNORE_SHARE_ACCESS_CHECK ); // Flags + + if (!NT_SUCCESS( status )) { + + goto SimRepNormalizeNameComponentCleanup; + } + + status = ObReferenceObjectByHandle( directoryHandle, + FILE_LIST_DIRECTORY | SYNCHRONIZE, // DesiredAccess + *IoFileObjectType, + KernelMode, + &directoryFileObject, + NULL ); + + + if (!NT_SUCCESS( status )) { + + goto SimRepNormalizeNameComponentCleanup; + } + + // + // Query the file entry to get the long name + // + + status = SimRepQueryDirectoryFile( Instance, + directoryFileObject, + ExpandComponentName, + ExpandComponentNameLength, + FileNamesInformation, + TRUE, /* ReturnSingleEntry */ + (PUNICODE_STRING)Component, + TRUE, /* restartScan */ + NULL ); + + +SimRepNormalizeNameComponentCleanup: + + + if (NULL != directoryHandle) { + + FltClose( directoryHandle ); + } + + if (NULL != directoryFileObject) { + + ObDereferenceObject( directoryFileObject ); + } + + return status; + +} + + +#if SIMREP_VISTA +NTSTATUS +SimRepNormalizeNameComponentEx ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ) +/*++ + +Routine Description: + + This routine normalizes, converts to a long name if needed, a name component. + +Arguments: + + Instance - Opaque instance pointer for the minifilter driver instance that + this callback routine is registered for. + + FileObject - Pointer to the file object for the file whose name is being + requested or the file that is the target of the IRP_MJ_SET_INFORMATION + operation if the FLTFL_NORMALIZE_NAME_DESTINATION_FILE_NAME flag is set. + ee the Flags parameter below for more information. + + ParentDirectory - Pointer to a UNICODE_STRING structure that contains the + name of the parent directory for this name component. + + VolumeNameLength - Length, in bytes, of the parent directory name that is + stored in the structure that the ParentDirectory parameter points to. + + Component - Pointer to a UNICODE_STRING structure that contains the name + component to be expanded. + + ExpandComponentName - Pointer to a FILE_NAMES_INFORMATION structure that + receives the expanded (normalized) file name information for the name component. + + ExpandComponentNameLength - Length, in bytes, of the buffer that the + ExpandComponentName parameter points to. + + Flags - Name normalization flags. + + NormalizationContext - Pointer to minifilter driver-provided context + information to be passed in any subsequent calls to this callback routine + that are made to normalize the remaining components in the same file name + path. + +Return Value: + + Returns STATUS_SUCCESS if a name could be returned, or the appropriate + error otherwise. + +--*/ +{ + NTSTATUS status; + HANDLE directoryHandle = NULL; + PFILE_OBJECT directoryFileObject = NULL; + OBJECT_ATTRIBUTES objAttributes; + IO_STATUS_BLOCK ioStatusBlock; + BOOLEAN ignoreCase = !BooleanFlagOn( Flags, + FLTFL_NORMALIZE_NAME_CASE_SENSITIVE ); + IO_DRIVER_CREATE_CONTEXT createContext; + TXN_PARAMETER_BLOCK txnBlock; + PTXN_PARAMETER_BLOCK originalTxnBlock; + + UNREFERENCED_PARAMETER( NormalizationContext ); + UNREFERENCED_PARAMETER( DeviceNameLength ); + + PAGED_CODE(); + + // + // Validate the buffer is big enough + // + + if (ExpandComponentNameLength < sizeof(FILE_NAMES_INFORMATION)) { + + return STATUS_INVALID_PARAMETER; + } + + InitializeObjectAttributes( &objAttributes, + (PUNICODE_STRING)ParentDirectory, + OBJ_KERNEL_HANDLE + | (ignoreCase ? OBJ_CASE_INSENSITIVE : 0), + NULL, + NULL ); + + ASSERT( ARGUMENT_PRESENT( FileObject ) ); + + // + // On Vista and beyond, we need to query the normalized name in the context + // of the same transaction as the name query + // + + IoInitializeDriverCreateContext( &createContext ); + + originalTxnBlock = IoGetTransactionParameterBlock( FileObject ); + + if (originalTxnBlock != NULL) { + + // + // Do not propagate the miniversion for the parent open + // as directories don't have a miniversion. + // + + txnBlock.Length = sizeof( txnBlock ); + txnBlock.TransactionObject = originalTxnBlock->TransactionObject; + txnBlock.TxFsContext = TXF_MINIVERSION_DEFAULT_VIEW; + + createContext.TxnParameters = &txnBlock; + } + + status = FltCreateFileEx2( Globals.Filter, + Instance, + &directoryHandle, + &directoryFileObject, + FILE_LIST_DIRECTORY | SYNCHRONIZE, // DesiredAccess + &objAttributes, + &ioStatusBlock, + NULL, // AllocationSize + FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_NORMAL, // FileAttributes + FILE_SHARE_READ + | FILE_SHARE_WRITE + | FILE_SHARE_DELETE, // ShareAccess + FILE_OPEN, // CreateDisposition + FILE_DIRECTORY_FILE + | FILE_SYNCHRONOUS_IO_NONALERT + | FILE_OPEN_FOR_BACKUP_INTENT, // CreateOptions + NULL, // EaBuffer + 0, // EaLength + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + &createContext ); + + + if (!NT_SUCCESS( status )) { + + goto SimRepNormalizeNameComponentExCleanup; + } + + // + // Query the file entry to get the long name + // + + status = SimRepQueryDirectoryFile( Instance, + directoryFileObject, + ExpandComponentName, + ExpandComponentNameLength, + FileNamesInformation, + TRUE, /* ReturnSingleEntry */ + (PUNICODE_STRING)Component, + TRUE, /* restartScan */ + NULL ); + + +SimRepNormalizeNameComponentExCleanup: + + + if (NULL != directoryHandle) { + + FltClose( directoryHandle ); + } + + if (NULL != directoryFileObject) { + + ObDereferenceObject( directoryFileObject ); + } + + return status; +} +#endif + +NTSTATUS +SimRepQueryDirectoryFile ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_writes_bytes_(Length) PVOID FileInformationBuffer, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan, + _Out_opt_ PULONG LengthReturned + ) +/*++ + +Routine Description: + + This function is like ZwQueryDirectoryFile for filters + +Arguments: + + Instance - Supplies the Instance initiating this IO. + + FileObject - Supplies the file object about which the requested + information should be changed. + + FileInformation - Supplies a buffer containing the information which should + be changed on the file. + + Length - Supplies the length, in bytes, of the FileInformation buffer. + + FileInformationClass - Specifies the type of information which should be + changed about the file. + + ReturnSingleEntry - If this parameter is TRUE, SimRepQueryDirectoryFile + returns only the first entry that is found. + + FileName - An optional pointer to a caller-allocated Unicode string + containing the name of a file (or multiple files, if wildcards are used) + within the directory specified by FileHandle. This parameter is optional + and can be NULL. + + RestartScan - Set to TRUE if the scan is to start at the first entry in + the directory. Set to FALSE if resuming the scan from a previous call. + + +Return Value: + + The status returned is the final completion status of the operation. + +--*/ + +{ + PFLT_CALLBACK_DATA data; + NTSTATUS status; + + PAGED_CODE(); + + if (Globals.QueryDirectoryFileFunction != NULL) { + + return Globals.QueryDirectoryFileFunction( Instance, + FileObject, + FileInformationBuffer, + Length, + FileInformationClass, + ReturnSingleEntry, + FileName, + RestartScan, + LengthReturned ); + } + + // + // Customized FltQueryDirectoryFile if it is not exported from FltMgr. + // + + status = FltAllocateCallbackData( Instance, FileObject, &data ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + data->Iopb->MajorFunction = IRP_MJ_DIRECTORY_CONTROL; + data->Iopb->MinorFunction = IRP_MN_QUERY_DIRECTORY; + + data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length = Length; + data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileName = FileName; + data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass = FileInformationClass; + data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileIndex = 0; + + data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer = FileInformationBuffer; + data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress = NULL; + + if (RestartScan) { + + data->Iopb->OperationFlags |= SL_RESTART_SCAN; + } + + if (ReturnSingleEntry) { + + data->Iopb->OperationFlags |= SL_RETURN_SINGLE_ENTRY; + } + + // + // Perform a synchronous operation. + // + + FltPerformSynchronousIo( data ); + + status = data->IoStatus.Status; + + if (ARGUMENT_PRESENT(LengthReturned) && + NT_SUCCESS( status )) { + + *LengthReturned = (ULONG) data->IoStatus.Information; + } + + FltFreeCallbackData( data ); + + return status; +} + + + diff --git a/filesys/miniFilter/simrep/simrep.inf b/filesys/miniFilter/simrep/simrep.inf new file mode 100644 index 00000000..ba9f8af9 --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.inf @@ -0,0 +1,110 @@ +;;; +;;; Simulate Reparse File System Filter Driver Sample +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} +Provider = %Msft% +DriverVer = 01/01/2004,1.0.0.1 +CatalogFile = simrep.cat + +[DestinationDirs] +DefaultDestDir = 12 +SimRep.DriverFiles = 12 ;%windir%\system32\drivers + +[SourceDisksNames] +1 = %Disk1% + +[SourceDisksFiles] +simrep.sys = 1 + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %SimRepServiceDesc% +CopyFiles = SimRep.DriverFiles + +[DefaultInstall.Services] +AddService = %SimRepServiceName%,,SimRep.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = SimRep.DriverFiles +DelReg = SimRep.DelRegistry + + +[DefaultUninstall.Services] +DelService = SimRep,0x204 + +; +; Services Section +; + +[SimRep.Service] +DisplayName = %SimRepServiceName% +Description = %SimRepServiceDesc% +ServiceBinary = %12%\simrep.sys ;%windir%\system32\drivers\simrep.sys +Dependencies = %FltmgrServiceName% ;FltMgr +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = SimRep.AddRegistry + +; +; Registry Modifications +; + +[SimRep.AddRegistry] +HKR,,%SimRepDebugLevel%,0x00010001,0x1F +HKR,,%SimRepRemapRenamesAndLinks%,0x00010001,0x00 +HKR,,"NewMapping",0x00000000,%NewMapping% +HKR,,"OldMapping",0x00000000,%OldMapping% +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,%RegInstancesSubkeyName%,%RegDefaultInstanceValueName%,0x00000000,%DefaultInstance% +HKR,%RegInstancesSubkeyName%"\"%Instance1.Name%,%RegAltitudeValueName%,0x00000000,%Instance1.Altitude% +HKR,%RegInstancesSubkeyName%"\"%Instance1.Name%,%RegFlagsValueName%,0x00010001,%Instance1.Flags% + +[SimRep.DelRegistry] + +; +; Copy Files +; + +[SimRep.DriverFiles] +simrep.sys + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +SimRepServiceDesc = "Simulate Reparse File System Filter Driver Sample" +SimRepServiceName = "SimRep" +SimRepDebugLevel = "DebugLevel" +SimRepRemapRenamesAndLinks = "RemapRenamesAndLinks" +FltmgrServiceName = "FltMgr" +RegInstancesSubkeyName = "Instances" +RegDefaultInstanceValueName = "DefaultInstance" +RegAltitudeValueName = "Altitude" +RegFlagsValueName = "Flags" +Disk1 = "SimRep Source Media" +NewMapping = "\a\b" +OldMapping = "\x\y" + +;Instances specific information. +DefaultInstance = "SimRep" +Instance1.Name = "SimRep" +Instance1.Altitude = "371100" +Instance1.Flags = 0x0 diff --git a/filesys/miniFilter/simrep/simrep.rc b/filesys/miniFilter/simrep/simrep.rc new file mode 100644 index 00000000..a9475aae --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Simulate Reparse Sample Mini-Filter" +#define VER_INTERNALNAME_STR "simrep.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/simrep/simrep.sln b/filesys/miniFilter/simrep/simrep.sln new file mode 100644 index 00000000..179a1e3c --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simrep", "simrep.vcxproj", "{8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|Win32.ActiveCfg = Debug|Win32 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|Win32.Build.0 = Debug|Win32 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|Win32.ActiveCfg = Release|Win32 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|Win32.Build.0 = Release|Win32 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|x64.ActiveCfg = Debug|x64 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|x64.Build.0 = Debug|x64 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|x64.ActiveCfg = Release|x64 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/simrep/simrep.vcxproj b/filesys/miniFilter/simrep/simrep.vcxproj new file mode 100644 index 00000000..41f903f0 --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B} + $(MSBuildProjectName) + Debug + Win32 + {E65EC005-19A7-464F-8CA6-C0A78FD9F8C8} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + simrep + + + simrep + + + simrep + + + simrep + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);POOL_NX_OPTIN=1 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/simrep/simrep.vcxproj.Filters b/filesys/miniFilter/simrep/simrep.vcxproj.Filters new file mode 100644 index 00000000..e5b8f9ad --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {D5037E9C-B07A-4282-8BD3-F0F1852E3411} + + + h;hpp;hxx;hm;inl;inc;xsd + {793B13DB-7C30-4334-A398-EA998A473206} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {65B4A597-DA92-4875-BC82-E7B93DBCAA49} + + + inf;inv;inx;mof;mc; + {37180E6D-5204-4358-98A7-E348F7EBB425} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/miniFilter/swapBuffers/ReadMe.md b/filesys/miniFilter/swapBuffers/ReadMe.md new file mode 100644 index 00000000..da19b5ed --- /dev/null +++ b/filesys/miniFilter/swapBuffers/ReadMe.md @@ -0,0 +1,15 @@ +SwapBuffer File System Minifilter Driver +======================================== + +The SwapBuffers minifilter demonstrates how to switch buffers between reads and writes of data. This technique is particularly useful for encryption filters because they have to encrypt data before writing it to disk and decrypt it after reading it from disk. Because encryption/decryption has to be done transparently, you cannot use system-supplied buffers directly, so intermediate buffers have to be introduced. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *SwapBuffers* minifilter introduces a new buffer before a read/write or directory control operations. The corresponding operation is then performed on the new buffer instead of the buffer that was originally provided. After the operation completes, the contents of the new buffer are copied back in to the original buffer. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.c b/filesys/miniFilter/swapBuffers/swapBuffers.c new file mode 100644 index 00000000..47893870 --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.c @@ -0,0 +1,2334 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + SwapBuffers.c + +Abstract: + + This is a sample filter which demonstrates proper access of data buffer + and a general guideline of how to swap buffers. + For now it only swaps buffers for: + + IRP_MJ_READ + IRP_MJ_WRITE + IRP_MJ_DIRECTORY_CONTROL + + By default this filter attaches to all volumes it is notified about. It + does support having multiple instances on a given volume. + +Environment: + + Kernel mode + +--*/ + +#include +#include +#include + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + + +PFLT_FILTER gFilterHandle; + +/************************************************************************* + Pool Tags +*************************************************************************/ + +#define BUFFER_SWAP_TAG 'bdBS' +#define CONTEXT_TAG 'xcBS' +#define NAME_TAG 'mnBS' +#define PRE_2_POST_TAG 'ppBS' + +/************************************************************************* + Local structures +*************************************************************************/ + +// +// This is a volume context, one of these are attached to each volume +// we monitor. This is used to get a "DOS" name for debug display. +// + +typedef struct _VOLUME_CONTEXT { + + // + // Holds the name to display + // + + UNICODE_STRING Name; + + // + // Holds the sector size for this volume. + // + + ULONG SectorSize; + +} VOLUME_CONTEXT, *PVOLUME_CONTEXT; + +#define MIN_SECTOR_SIZE 0x200 + + +// +// This is a context structure that is used to pass state from our +// pre-operation callback to our post-operation callback. +// + +typedef struct _PRE_2_POST_CONTEXT { + + // + // Pointer to our volume context structure. We always get the context + // in the preOperation path because you can not safely get it at DPC + // level. We then release it in the postOperation path. It is safe + // to release contexts at DPC level. + // + + PVOLUME_CONTEXT VolCtx; + + // + // Since the post-operation parameters always receive the "original" + // parameters passed to the operation, we need to pass our new destination + // buffer to our post operation routine so we can free it. + // + + PVOID SwappedBuffer; + +} PRE_2_POST_CONTEXT, *PPRE_2_POST_CONTEXT; + +// +// This is a lookAside list used to allocate our pre-2-post structure. +// + +NPAGED_LOOKASIDE_LIST Pre2PostContextList; + +/************************************************************************* + Prototypes +*************************************************************************/ + +NTSTATUS +InstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +VOID +CleanupVolumeContext( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +NTSTATUS +InstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +FilterUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +SwapPreReadBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +SwapPostReadBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_POSTOP_CALLBACK_STATUS +SwapPostReadBuffersWhenSafe ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +SwapPreDirCtrlBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +SwapPostDirCtrlBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_POSTOP_CALLBACK_STATUS +SwapPostDirCtrlBuffersWhenSafe ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +SwapPreWriteBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +SwapPostWriteBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +VOID +ReadDriverParameters ( + _In_ PUNICODE_STRING RegistryPath + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, InstanceSetup) +#pragma alloc_text(PAGE, CleanupVolumeContext) +#pragma alloc_text(PAGE, InstanceQueryTeardown) +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, ReadDriverParameters) +#pragma alloc_text(PAGE, FilterUnload) +#endif + +// +// Operation we currently care about. +// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_READ, + 0, + SwapPreReadBuffers, + SwapPostReadBuffers }, + + { IRP_MJ_WRITE, + 0, + SwapPreWriteBuffers, + SwapPostWriteBuffers }, + + { IRP_MJ_DIRECTORY_CONTROL, + 0, + SwapPreDirCtrlBuffers, + SwapPostDirCtrlBuffers }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Context definitions we currently care about. Note that the system will +// create a lookAside list for the volume context because an explicit size +// of the context is specified. +// + +CONST FLT_CONTEXT_REGISTRATION ContextNotifications[] = { + + { FLT_VOLUME_CONTEXT, + 0, + CleanupVolumeContext, + sizeof(VOLUME_CONTEXT), + CONTEXT_TAG }, + + { FLT_CONTEXT_END } +}; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + ContextNotifications, // Context + Callbacks, // Operation callbacks + + FilterUnload, // MiniFilterUnload + + InstanceSetup, // InstanceSetup + InstanceQueryTeardown, // InstanceQueryTeardown + NULL, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL // NormalizeNameComponent + +}; + +/************************************************************************* + Debug tracing information +*************************************************************************/ + +// +// Definitions to display log messages. The registry DWORD entry: +// "hklm\system\CurrentControlSet\Services\Swapbuffers\DebugFlags" defines +// the default state of these logging flags +// + +#define LOGFL_ERRORS 0x00000001 // if set, display error messages +#define LOGFL_READ 0x00000002 // if set, display READ operation info +#define LOGFL_WRITE 0x00000004 // if set, display WRITE operation info +#define LOGFL_DIRCTRL 0x00000008 // if set, display DIRCTRL operation info +#define LOGFL_VOLCTX 0x00000010 // if set, display VOLCTX operation info + +ULONG LoggingFlags = 0; // all disabled by default + +#define LOG_PRINT( _logFlag, _string ) \ + (FlagOn(LoggingFlags,(_logFlag)) ? \ + DbgPrint _string : \ + ((int)0)) + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// +// Routines +// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +InstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ) +/*++ + +Routine Description: + + This routine is called whenever a new instance is created on a volume. + + By default we want to attach to all volumes. This routine will try and + get a "DOS" name for the given volume. If it can't, it will try and + get the "NT" name for the volume (which is what happens on network + volumes). If a name is retrieved a volume context will be created with + that name. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Flags describing the reason for this attach request. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + PDEVICE_OBJECT devObj = NULL; + PVOLUME_CONTEXT ctx = NULL; + NTSTATUS status = STATUS_SUCCESS; + ULONG retLen; + PUNICODE_STRING workingName; + USHORT size; + UCHAR volPropBuffer[sizeof(FLT_VOLUME_PROPERTIES)+512]; + PFLT_VOLUME_PROPERTIES volProp = (PFLT_VOLUME_PROPERTIES)volPropBuffer; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + try { + + // + // Allocate a volume context structure. + // + + status = FltAllocateContext( FltObjects->Filter, + FLT_VOLUME_CONTEXT, + sizeof(VOLUME_CONTEXT), + NonPagedPool, + &ctx ); + + if (!NT_SUCCESS(status)) { + + // + // We could not allocate a context, quit now + // + + leave; + } + + // + // Always get the volume properties, so I can get a sector size + // + + status = FltGetVolumeProperties( FltObjects->Volume, + volProp, + sizeof(volPropBuffer), + &retLen ); + + if (!NT_SUCCESS(status)) { + + leave; + } + + // + // Save the sector size in the context for later use. Note that + // we will pick a minimum sector size if a sector size is not + // specified. + // + + FLT_ASSERT((volProp->SectorSize == 0) || (volProp->SectorSize >= MIN_SECTOR_SIZE)); + + ctx->SectorSize = max(volProp->SectorSize,MIN_SECTOR_SIZE); + + // + // Init the buffer field (which may be allocated later). + // + + ctx->Name.Buffer = NULL; + + // + // Get the storage device object we want a name for. + // + + status = FltGetDiskDeviceObject( FltObjects->Volume, &devObj ); + + if (NT_SUCCESS(status)) { + + // + // Try and get the DOS name. If it succeeds we will have + // an allocated name buffer. If not, it will be NULL + // + +#pragma prefast(suppress:__WARNING_USE_OTHER_FUNCTION, "Used to maintain compatability with Win 2k") + status = RtlVolumeDeviceToDosName( devObj, &ctx->Name ); + } + + // + // If we could not get a DOS name, get the NT name. + // + + if (!NT_SUCCESS(status)) { + + FLT_ASSERT(ctx->Name.Buffer == NULL); + + // + // Figure out which name to use from the properties + // + + if (volProp->RealDeviceName.Length > 0) { + + workingName = &volProp->RealDeviceName; + + } else if (volProp->FileSystemDeviceName.Length > 0) { + + workingName = &volProp->FileSystemDeviceName; + + } else { + + // + // No name, don't save the context + // + + status = STATUS_FLT_DO_NOT_ATTACH; + leave; + } + + // + // Get size of buffer to allocate. This is the length of the + // string plus room for a trailing colon. + // + + size = workingName->Length + sizeof(WCHAR); + + // + // Now allocate a buffer to hold this name + // + +#pragma prefast(suppress:__WARNING_MEMORY_LEAK, "ctx->Name.Buffer will not be leaked because it is freed in CleanupVolumeContext") + ctx->Name.Buffer = ExAllocatePoolWithTag( NonPagedPool, + size, + NAME_TAG ); + if (ctx->Name.Buffer == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + leave; + } + + // + // Init the rest of the fields + // + + ctx->Name.Length = 0; + ctx->Name.MaximumLength = size; + + // + // Copy the name in + // + + RtlCopyUnicodeString( &ctx->Name, + workingName ); + + // + // Put a trailing colon to make the display look good + // + + RtlAppendUnicodeToString( &ctx->Name, + L":" ); + } + + // + // Set the context + // + + status = FltSetVolumeContext( FltObjects->Volume, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + ctx, + NULL ); + + // + // Log debug info + // + + LOG_PRINT( LOGFL_VOLCTX, + ("SwapBuffers!InstanceSetup: Real SectSize=0x%04x, Used SectSize=0x%04x, Name=\"%wZ\"\n", + volProp->SectorSize, + ctx->SectorSize, + &ctx->Name) ); + + // + // It is OK for the context to already be defined. + // + + if (status == STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + status = STATUS_SUCCESS; + } + + } finally { + + // + // Always release the context. If the set failed, it will free the + // context. If not, it will remove the reference added by the set. + // Note that the name buffer in the ctx will get freed by the context + // cleanup routine. + // + + if (ctx) { + + FltReleaseContext( ctx ); + } + + // + // Remove the reference added to the device object by + // FltGetDiskDeviceObject. + // + + if (devObj) { + + ObDereferenceObject( devObj ); + } + } + + return status; +} + + +VOID +CleanupVolumeContext( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + The given context is being freed. + Free the allocated name buffer if there one. + +Arguments: + + Context - The context being freed + + ContextType - The type of context this is + +Return Value: + + None + +--*/ +{ + PVOLUME_CONTEXT ctx = Context; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ContextType ); + + FLT_ASSERT(ContextType == FLT_VOLUME_CONTEXT); + + if (ctx->Name.Buffer != NULL) { + + ExFreePool(ctx->Name.Buffer); + ctx->Name.Buffer = NULL; + } +} + + +NTSTATUS +InstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when an instance is being manually deleted by a + call to FltDetachVolume or FilterDetach. We always return it is OK to + detach. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Flags - Indicating where this detach request came from. + +Return Value: + + Always succeed. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + return STATUS_SUCCESS; +} + + +/************************************************************************* + Initialization and unload routines. +*************************************************************************/ + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine. This registers with FltMgr and + initializes all global data structures. + +Arguments: + + DriverObject - Pointer to driver object created by the system to + represent this driver. + + RegistryPath - Unicode string identifying where the parameters for this + driver are located in the registry. + +Return Value: + + Status of the operation + +--*/ +{ + NTSTATUS status; + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + // + // Get debug trace flags + // + + ReadDriverParameters( RegistryPath ); + + // + // Init lookaside list used to allocate our context structure used to + // pass information from out preOperation callback to our postOperation + // callback. + // + + ExInitializeNPagedLookasideList( &Pre2PostContextList, + NULL, + NULL, + 0, + sizeof(PRE_2_POST_CONTEXT), + PRE_2_POST_TAG, + 0 ); + + // + // Register with FltMgr + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &gFilterHandle ); + + if (! NT_SUCCESS( status )) { + + goto SwapDriverEntryExit; + } + + // + // Start filtering i/o + // + + status = FltStartFiltering( gFilterHandle ); + + if (! NT_SUCCESS( status )) { + + FltUnregisterFilter( gFilterHandle ); + goto SwapDriverEntryExit; + } + +SwapDriverEntryExit: + + if(! NT_SUCCESS( status )) { + + ExDeleteNPagedLookasideList( &Pre2PostContextList ); + } + + return status; +} + + +NTSTATUS +FilterUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + Called when this mini-filter is about to be unloaded. We unregister + from the FltMgr and then return it is OK to unload + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Flags ); + + // + // Unregister from FLT mgr + // + + FltUnregisterFilter( gFilterHandle ); + + // + // Delete lookaside list + // + + ExDeleteNPagedLookasideList( &Pre2PostContextList ); + + return STATUS_SUCCESS; +} + + +/************************************************************************* + MiniFilter callback routines. +*************************************************************************/ + +FLT_PREOP_CALLBACK_STATUS +SwapPreReadBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine demonstrates how to swap buffers for the READ operation. + + Note that it handles all errors by simply not doing the buffer swap. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - Receives the context that will be passed to the + post-operation callback. + +Return Value: + + FLT_PREOP_SUCCESS_WITH_CALLBACK - we want a postOpeation callback + FLT_PREOP_SUCCESS_NO_CALLBACK - we don't want a postOperation callback + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + FLT_PREOP_CALLBACK_STATUS retValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + PVOID newBuf = NULL; + PMDL newMdl = NULL; + PVOLUME_CONTEXT volCtx = NULL; + PPRE_2_POST_CONTEXT p2pCtx; + NTSTATUS status; + ULONG readLen = iopb->Parameters.Read.Length; + + try { + + // + // If they are trying to read ZERO bytes, then don't do anything and + // we don't need a post-operation callback. + // + + if (readLen == 0) { + + leave; + } + + // + // Get our volume context so we can display our volume name in the + // debug output. + // + + status = FltGetVolumeContext( FltObjects->Filter, + FltObjects->Volume, + &volCtx ); + + if (!NT_SUCCESS(status)) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreReadBuffers: Error getting volume context, status=%x\n", + status) ); + + leave; + } + + // + // If this is a non-cached I/O we need to round the length up to the + // sector size for this device. We must do this because the file + // systems do this and we need to make sure our buffer is as big + // as they are expecting. + // + + if (FlagOn(IRP_NOCACHE,iopb->IrpFlags)) { + + readLen = (ULONG)ROUND_TO_SIZE(readLen,volCtx->SectorSize); + } + + // + // Allocate aligned nonPaged memory for the buffer we are swapping + // to. This is really only necessary for noncached IO but we always + // do it here for simplification. If we fail to get the memory, just + // don't swap buffers on this operation. + // + + newBuf = FltAllocatePoolAlignedWithTag( FltObjects->Instance, + NonPagedPool, + (SIZE_T) readLen, + BUFFER_SWAP_TAG ); + if (newBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreReadBuffers: %wZ Failed to allocate %d bytes of memory\n", + &volCtx->Name, + readLen) ); + + leave; + } + + // + // We only need to build a MDL for IRP operations. We don't need to + // do this for a FASTIO operation since the FASTIO interface has no + // parameter for passing the MDL to the file system. + // + + if (FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_IRP_OPERATION)) { + + // + // Allocate a MDL for the new allocated memory. If we fail + // the MDL allocation then we won't swap buffer for this operation + // + + newMdl = IoAllocateMdl( newBuf, + readLen, + FALSE, + FALSE, + NULL ); + + if (newMdl == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreReadBuffers: %wZ Failed to allocate MDL\n", + &volCtx->Name) ); + + leave; + } + + // + // setup the MDL for the non-paged pool we just allocated + // + + MmBuildMdlForNonPagedPool( newMdl ); + } + + // + // We are ready to swap buffers, get a pre2Post context structure. + // We need it to pass the volume context and the allocate memory + // buffer to the post operation callback. + // + + p2pCtx = ExAllocateFromNPagedLookasideList( &Pre2PostContextList ); + + if (p2pCtx == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreReadBuffers: %wZ Failed to allocate pre2Post context structure\n", + &volCtx->Name) ); + + leave; + } + + // + // Log that we are swapping + // + + LOG_PRINT( LOGFL_READ, + ("SwapBuffers!SwapPreReadBuffers: %wZ newB=%p newMdl=%p oldB=%p oldMdl=%p len=%d\n", + &volCtx->Name, + newBuf, + newMdl, + iopb->Parameters.Read.ReadBuffer, + iopb->Parameters.Read.MdlAddress, + readLen) ); + + // + // Update the buffer pointers and MDL address, mark we have changed + // something. + // + + iopb->Parameters.Read.ReadBuffer = newBuf; + iopb->Parameters.Read.MdlAddress = newMdl; + FltSetCallbackDataDirty( Data ); + + // + // Pass state to our post-operation callback. + // + + p2pCtx->SwappedBuffer = newBuf; + p2pCtx->VolCtx = volCtx; + + *CompletionContext = p2pCtx; + + // + // Return we want a post-operation callback + // + + retValue = FLT_PREOP_SUCCESS_WITH_CALLBACK; + + } finally { + + // + // If we don't want a post-operation callback, then cleanup state. + // + + if (retValue != FLT_PREOP_SUCCESS_WITH_CALLBACK) { + + if (newBuf != NULL) { + + FltFreePoolAlignedWithTag( FltObjects->Instance, + newBuf, + BUFFER_SWAP_TAG ); + } + + if (newMdl != NULL) { + + IoFreeMdl( newMdl ); + } + + if (volCtx != NULL) { + + FltReleaseContext( volCtx ); + } + } + } + + return retValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +SwapPostReadBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine does postRead buffer swap handling + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The completion context set in the pre-operation routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING + FLT_POSTOP_MORE_PROCESSING_REQUIRED + +--*/ +{ + PVOID origBuf; + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + FLT_POSTOP_CALLBACK_STATUS retValue = FLT_POSTOP_FINISHED_PROCESSING; + PPRE_2_POST_CONTEXT p2pCtx = CompletionContext; + BOOLEAN cleanupAllocatedBuffer = TRUE; + + // + // This system won't draining an operation with swapped buffers, verify + // the draining flag is not set. + // + + FLT_ASSERT(!FlagOn(Flags, FLTFL_POST_OPERATION_DRAINING)); + + try { + + // + // If the operation failed or the count is zero, there is no data to + // copy so just return now. + // + + if (!NT_SUCCESS(Data->IoStatus.Status) || + (Data->IoStatus.Information == 0)) { + + LOG_PRINT( LOGFL_READ, + ("SwapBuffers!SwapPostReadBuffers: %wZ newB=%p No data read, status=%x, info=%Iu\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Status, + Data->IoStatus.Information) ); + + leave; + } + + // + // We need to copy the read data back into the users buffer. Note + // that the parameters passed in are for the users original buffers + // not our swapped buffers. + // + + if (iopb->Parameters.Read.MdlAddress != NULL) { + + // + // This should be a simple MDL. We don't expect chained MDLs + // this high up the stack + // + + FLT_ASSERT( ((PMDL)iopb->Parameters.Read.MdlAddress)->Next == NULL); + + // + // Since there is a MDL defined for the original buffer, get a + // system address for it so we can copy the data back to it. + // We must do this because we don't know what thread context + // we are in. + // + + origBuf = MmGetSystemAddressForMdlSafe( iopb->Parameters.Read.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (origBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostReadBuffers: %wZ Failed to get system address for MDL: %p\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.Read.MdlAddress) ); + + // + // If we failed to get a SYSTEM address, mark that the read + // failed and return. + // + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + leave; + } + + } else if (FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) || + FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_FAST_IO_OPERATION)) { + + // + // If this is a system buffer, just use the given address because + // it is valid in all thread contexts. + // If this is a FASTIO operation, we can just use the + // buffer (inside a try/except) since we know we are in + // the correct thread context (you can't pend FASTIO's). + // + + origBuf = iopb->Parameters.Read.ReadBuffer; + + } else { + + // + // They don't have a MDL and this is not a system buffer + // or a fastio so this is probably some arbitrary user + // buffer. We can not do the processing at DPC level so + // try and get to a safe IRQL so we can do the processing. + // + + if (FltDoCompletionProcessingWhenSafe( Data, + FltObjects, + CompletionContext, + Flags, + SwapPostReadBuffersWhenSafe, + &retValue )) { + + // + // This operation has been moved to a safe IRQL, the called + // routine will do (or has done) the freeing so don't do it + // in our routine. + // + + cleanupAllocatedBuffer = FALSE; + + } else { + + // + // We are in a state where we can not get to a safe IRQL and + // we do not have a MDL. There is nothing we can do to safely + // copy the data back to the users buffer, fail the operation + // and return. This shouldn't ever happen because in those + // situations where it is not safe to post, we should have + // a MDL. + // + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostReadBuffers: %wZ Unable to post to a safe IRQL\n", + &p2pCtx->VolCtx->Name) ); + + Data->IoStatus.Status = STATUS_UNSUCCESSFUL; + Data->IoStatus.Information = 0; + } + + leave; + } + + // + // We either have a system buffer or this is a fastio operation + // so we are in the proper context. Copy the data handling an + // exception. + // + + try { + + RtlCopyMemory( origBuf, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information ); + + } except (EXCEPTION_EXECUTE_HANDLER) { + + // + // The copy failed, return an error, failing the operation. + // + + Data->IoStatus.Status = GetExceptionCode(); + Data->IoStatus.Information = 0; + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostReadBuffers: %wZ Invalid user buffer, oldB=%p, status=%x\n", + &p2pCtx->VolCtx->Name, + origBuf, + Data->IoStatus.Status) ); + } + + } finally { + + // + // If we are supposed to, cleanup the allocated memory and release + // the volume context. The freeing of the MDL (if there is one) is + // handled by FltMgr. + // + + if (cleanupAllocatedBuffer) { + + LOG_PRINT( LOGFL_READ, + ("SwapBuffers!SwapPostReadBuffers: %wZ newB=%p info=%Iu Freeing\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information) ); + + FltFreePoolAlignedWithTag( FltObjects->Instance, + p2pCtx->SwappedBuffer, + BUFFER_SWAP_TAG ); + + FltReleaseContext( p2pCtx->VolCtx ); + + ExFreeToNPagedLookasideList( &Pre2PostContextList, + p2pCtx ); + } + } + + return retValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +SwapPostReadBuffersWhenSafe ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + We had an arbitrary users buffer without a MDL so we needed to get + to a safe IRQL so we could lock it and then copy the data. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - Contains state from our PreOperation callback + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - This is always returned. + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + PPRE_2_POST_CONTEXT p2pCtx = CompletionContext; + PVOID origBuf; + NTSTATUS status; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + FLT_ASSERT(Data->IoStatus.Information != 0); + + // + // This is some sort of user buffer without a MDL, lock the user buffer + // so we can access it. This will create a MDL for it. + // + + status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS(status)) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostReadBuffersWhenSafe: %wZ Could not lock user buffer, oldB=%p, status=%x\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.Read.ReadBuffer, + status) ); + + // + // If we can't lock the buffer, fail the operation + // + + Data->IoStatus.Status = status; + Data->IoStatus.Information = 0; + + } else { + + // + // Get a system address for this buffer. + // + + origBuf = MmGetSystemAddressForMdlSafe( iopb->Parameters.Read.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (origBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostReadBuffersWhenSafe: %wZ Failed to get system address for MDL: %p\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.Read.MdlAddress) ); + + // + // If we couldn't get a SYSTEM buffer address, fail the operation + // + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + + } else { + + // + // Copy the data back to the original buffer. Note that we + // don't need a try/except because we will always have a system + // buffer address. + // + + RtlCopyMemory( origBuf, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information ); + } + } + + // + // Free allocated memory and release the volume context + // + + LOG_PRINT( LOGFL_READ, + ("SwapBuffers!SwapPostReadBuffersWhenSafe: %wZ newB=%p info=%Iu Freeing\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information) ); + + FltFreePoolAlignedWithTag( FltObjects->Instance, + p2pCtx->SwappedBuffer, + BUFFER_SWAP_TAG ); + + FltReleaseContext( p2pCtx->VolCtx ); + + ExFreeToNPagedLookasideList( &Pre2PostContextList, + p2pCtx ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +SwapPreDirCtrlBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine demonstrates how to swap buffers for the Directory Control + operations. The reason this routine is here is because directory change + notifications are long lived and this allows you to see how FltMgr + handles long lived IRP operations that have swapped buffers when the + mini-filter is unloaded. It does this by canceling the IRP. + + Note that it handles all errors by simply not doing the + buffer swap. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - Receives the context that will be passed to the + post-operation callback. + +Return Value: + + FLT_PREOP_SUCCESS_WITH_CALLBACK - we want a postOpeation callback + FLT_PREOP_SUCCESS_NO_CALLBACK - we don't want a postOperation callback + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + FLT_PREOP_CALLBACK_STATUS retValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + PVOID newBuf = NULL; + PMDL newMdl = NULL; + PVOLUME_CONTEXT volCtx = NULL; + PPRE_2_POST_CONTEXT p2pCtx; + NTSTATUS status; + + try { + + // + // If they are trying to get ZERO bytes, then don't do anything and + // we don't need a post-operation callback. + // + + if (iopb->Parameters.DirectoryControl.QueryDirectory.Length == 0) { + + leave; + } + + // + // Get our volume context. If we can't get it, just return. + // + + status = FltGetVolumeContext( FltObjects->Filter, + FltObjects->Volume, + &volCtx ); + + if (!NT_SUCCESS(status)) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreDirCtrlBuffers: Error getting volume context, status=%x\n", + status) ); + + leave; + } + + // + // Allocate nonPaged memory for the buffer we are swapping to. + // If we fail to get the memory, just don't swap buffers on this + // operation. + // + + newBuf = ExAllocatePoolWithTag( NonPagedPool, + iopb->Parameters.DirectoryControl.QueryDirectory.Length, + BUFFER_SWAP_TAG ); + + if (newBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreDirCtrlBuffers: %wZ Failed to allocate %d bytes of memory.\n", + &volCtx->Name, + iopb->Parameters.DirectoryControl.QueryDirectory.Length) ); + + leave; + } + + // + // Zero the new buffer so as not to potentially expose any sensitive + // data to the user. + // + + RtlZeroMemory( newBuf, iopb->Parameters.DirectoryControl.QueryDirectory.Length ); + + + // + // We need to build a MDL because Directory Control Operations are always IRP operations. + // + + + // + // Allocate a MDL for the new allocated memory. If we fail + // the MDL allocation then we won't swap buffer for this operation + // + + newMdl = IoAllocateMdl( newBuf, + iopb->Parameters.DirectoryControl.QueryDirectory.Length, + FALSE, + FALSE, + NULL ); + + if (newMdl == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreDirCtrlBuffers: %wZ Failed to allocate MDL.\n", + &volCtx->Name) ); + + leave; + } + + // + // setup the MDL for the non-paged pool we just allocated + // + + MmBuildMdlForNonPagedPool( newMdl ); + + // + // We are ready to swap buffers, get a pre2Post context structure. + // We need it to pass the volume context and the allocate memory + // buffer to the post operation callback. + // + + p2pCtx = ExAllocateFromNPagedLookasideList( &Pre2PostContextList ); + + if (p2pCtx == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreDirCtrlBuffers: %wZ Failed to allocate pre2Post context structure\n", + &volCtx->Name) ); + + leave; + } + + // + // Log that we are swapping + // + + LOG_PRINT( LOGFL_DIRCTRL, + ("SwapBuffers!SwapPreDirCtrlBuffers: %wZ newB=%p newMdl=%p oldB=%p oldMdl=%p len=%d\n", + &volCtx->Name, + newBuf, + newMdl, + iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer, + iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress, + iopb->Parameters.DirectoryControl.QueryDirectory.Length) ); + + // + // Update the buffer pointers and MDL address + // + + iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer = newBuf; + iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress = newMdl; + FltSetCallbackDataDirty( Data ); + + // + // Pass state to our post-operation callback. + // + + p2pCtx->SwappedBuffer = newBuf; + p2pCtx->VolCtx = volCtx; + + *CompletionContext = p2pCtx; + + // + // Return we want a post-operation callback + // + + retValue = FLT_PREOP_SUCCESS_WITH_CALLBACK; + + } finally { + + // + // If we don't want a post-operation callback, then cleanup state. + // + + if (retValue != FLT_PREOP_SUCCESS_WITH_CALLBACK) { + + if (newBuf != NULL) { + + ExFreePool( newBuf ); + } + + if (newMdl != NULL) { + + IoFreeMdl( newMdl ); + } + + if (volCtx != NULL) { + + FltReleaseContext( volCtx ); + } + } + } + + return retValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +SwapPostDirCtrlBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine does the post Directory Control buffer swap handling. + +Arguments: + + This routine does postRead buffer swap handling + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The completion context set in the pre-operation routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING + FLT_POSTOP_MORE_PROCESSING_REQUIRED + +--*/ +{ + PVOID origBuf; + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + FLT_POSTOP_CALLBACK_STATUS retValue = FLT_POSTOP_FINISHED_PROCESSING; + PPRE_2_POST_CONTEXT p2pCtx = CompletionContext; + BOOLEAN cleanupAllocatedBuffer = TRUE; + + // + // Verify we are not draining an operation with swapped buffers + // + + FLT_ASSERT(!FlagOn(Flags, FLTFL_POST_OPERATION_DRAINING)); + + try { + + // + // If the operation failed or the count is zero, there is no data to + // copy so just return now. + // + + if (!NT_SUCCESS(Data->IoStatus.Status) || + (Data->IoStatus.Information == 0)) { + + LOG_PRINT( LOGFL_DIRCTRL, + ("SwapBuffers!SwapPostDirCtrlBuffers: %wZ newB=%p No data read, status=%x, info=%Ix\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Status, + Data->IoStatus.Information) ); + + leave; + } + + // + // We need to copy the read data back into the users buffer. Note + // that the parameters passed in are for the users original buffers + // not our swapped buffers + // + + if (iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress != NULL) { + + // + // There is a MDL defined for the original buffer, get a + // system address for it so we can copy the data back to it. + // We must do this because we don't know what thread context + // we are in. + // + + origBuf = MmGetSystemAddressForMdlSafe( iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (origBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostDirCtrlBuffers: %wZ Failed to get system address for MDL: %p\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress) ); + + // + // If we failed to get a SYSTEM address, mark that the + // operation failed and return. + // + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + leave; + } + + } else if (FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) || + FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_FAST_IO_OPERATION)) { + + // + // If this is a system buffer, just use the given address because + // it is valid in all thread contexts. + // If this is a FASTIO operation, we can just use the + // buffer (inside a try/except) since we know we are in + // the correct thread context. + // + + origBuf = iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer; + + } else { + + // + // They don't have a MDL and this is not a system buffer + // or a fastio so this is probably some arbitrary user + // buffer. We can not do the processing at DPC level so + // try and get to a safe IRQL so we can do the processing. + // + + if (FltDoCompletionProcessingWhenSafe( Data, + FltObjects, + CompletionContext, + Flags, + SwapPostDirCtrlBuffersWhenSafe, + &retValue )) { + + // + // This operation has been moved to a safe IRQL, the called + // routine will do (or has done) the freeing so don't do it + // in our routine. + // + + cleanupAllocatedBuffer = FALSE; + + } else { + + // + // We are in a state where we can not get to a safe IRQL and + // we do not have a MDL. There is nothing we can do to safely + // copy the data back to the users buffer, fail the operation + // and return. This shouldn't ever happen because in those + // situations where it is not safe to post, we should have + // a MDL. + // + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostDirCtrlBuffers: %wZ Unable to post to a safe IRQL\n", + &p2pCtx->VolCtx->Name) ); + + Data->IoStatus.Status = STATUS_UNSUCCESSFUL; + Data->IoStatus.Information = 0; + } + + leave; + } + + // + // We either have a system buffer or this is a fastio operation + // so we are in the proper context. Copy the data handling an + // exception. + // + // NOTE: Due to a bug in FASTFAT where it is returning the wrong + // length in the information field (it is sort) we are always + // going to copy the original buffer length. Please note that + // this is a potential security problem because we will copy + // more than what was touched by the FS. So we have to make + // sure the buffer is clean before calling into the FS or we + // risk exposing sensitive data to the user. + // + + try { + + RtlCopyMemory( origBuf, + p2pCtx->SwappedBuffer, + /*Data->IoStatus.Information*/ + iopb->Parameters.DirectoryControl.QueryDirectory.Length ); + + } except (EXCEPTION_EXECUTE_HANDLER) { + + Data->IoStatus.Status = GetExceptionCode(); + Data->IoStatus.Information = 0; + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostDirCtrlBuffers: %wZ Invalid user buffer, oldB=%p, status=%x, info=%Iu\n", + &p2pCtx->VolCtx->Name, + origBuf, + Data->IoStatus.Status, + Data->IoStatus.Information) ); + } + + } finally { + + // + // If we are supposed to, cleanup the allocate memory and release + // the volume context. The freeing of the MDL (if there is one) is + // handled by FltMgr. + // + + if (cleanupAllocatedBuffer) { + + LOG_PRINT( LOGFL_DIRCTRL, + ("SwapBuffers!SwapPostDirCtrlBuffers: %wZ newB=%p info=%Iu Freeing\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information) ); + + ExFreePool( p2pCtx->SwappedBuffer ); + FltReleaseContext( p2pCtx->VolCtx ); + + ExFreeToNPagedLookasideList( &Pre2PostContextList, + p2pCtx ); + } + } + + return retValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +SwapPostDirCtrlBuffersWhenSafe ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + We had an arbitrary users buffer without a MDL so we needed to get + to a safe IRQL so we could lock it and then copy the data. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - The buffer we allocated and swapped to + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - This is always returned. + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + PPRE_2_POST_CONTEXT p2pCtx = CompletionContext; + PVOID origBuf; + NTSTATUS status; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + FLT_ASSERT(Data->IoStatus.Information != 0); + + // + // This is some sort of user buffer without a MDL, lock the + // user buffer so we can access it + // + + status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS(status)) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostDirCtrlBuffersWhenSafe: %wZ Could not lock user buffer, oldB=%p, status=%x\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer, + status) ); + + // + // If we can't lock the buffer, fail the operation + // + + Data->IoStatus.Status = status; + Data->IoStatus.Information = 0; + + } else { + + // + // Get a system address for this buffer. + // + + origBuf = MmGetSystemAddressForMdlSafe( iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (origBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostDirCtrlBuffersWhenSafe: %wZ Failed to get System address for MDL: %p\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress) ); + + // + // If we couldn't get a SYSTEM buffer address, fail the operation + // + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + + } else { + + // + // Copy the data back to the original buffer + // + // NOTE: Due to a bug in FASTFAT where it is returning the wrong + // length in the information field (it is short) we are + // always going to copy the original buffer length. + // + + RtlCopyMemory( origBuf, + p2pCtx->SwappedBuffer, + /*Data->IoStatus.Information*/ + iopb->Parameters.DirectoryControl.QueryDirectory.Length ); + } + } + + // + // Free the memory we allocated and return + // + + LOG_PRINT( LOGFL_DIRCTRL, + ("SwapBuffers!SwapPostDirCtrlBuffersWhenSafe: %wZ newB=%p info=%Iu Freeing\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information) ); + + ExFreePool( p2pCtx->SwappedBuffer ); + FltReleaseContext( p2pCtx->VolCtx ); + + ExFreeToNPagedLookasideList( &Pre2PostContextList, + p2pCtx ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +SwapPreWriteBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine demonstrates how to swap buffers for the WRITE operation. + + Note that it handles all errors by simply not doing the buffer swap. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + CompletionContext - Receives the context that will be passed to the + post-operation callback. + +Return Value: + + FLT_PREOP_SUCCESS_WITH_CALLBACK - we want a postOpeation callback + FLT_PREOP_SUCCESS_NO_CALLBACK - we don't want a postOperation callback + FLT_PREOP_COMPLETE - +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + FLT_PREOP_CALLBACK_STATUS retValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + PVOID newBuf = NULL; + PMDL newMdl = NULL; + PVOLUME_CONTEXT volCtx = NULL; + PPRE_2_POST_CONTEXT p2pCtx; + PVOID origBuf; + NTSTATUS status; + ULONG writeLen = iopb->Parameters.Write.Length; + + try { + + // + // If they are trying to write ZERO bytes, then don't do anything and + // we don't need a post-operation callback. + // + + if (writeLen == 0) { + + leave; + } + + // + // Get our volume context so we can display our volume name in the + // debug output. + // + + status = FltGetVolumeContext( FltObjects->Filter, + FltObjects->Volume, + &volCtx ); + + if (!NT_SUCCESS(status)) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: Error getting volume context, status=%x\n", + status) ); + + leave; + } + + // + // If this is a non-cached I/O we need to round the length up to the + // sector size for this device. We must do this because the file + // systems do this and we need to make sure our buffer is as big + // as they are expecting. + // + + if (FlagOn(IRP_NOCACHE,iopb->IrpFlags)) { + + writeLen = (ULONG)ROUND_TO_SIZE(writeLen,volCtx->SectorSize); + } + + // + // Allocate aligned nonPaged memory for the buffer we are swapping + // to. This is really only necessary for noncached IO but we always + // do it here for simplification. If we fail to get the memory, just + // don't swap buffers on this operation. + // + + newBuf = FltAllocatePoolAlignedWithTag( FltObjects->Instance, + NonPagedPool, + (SIZE_T) writeLen, + BUFFER_SWAP_TAG ); + + if (newBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: %wZ Failed to allocate %d bytes of memory.\n", + &volCtx->Name, + writeLen) ); + + leave; + } + + // + // We only need to build a MDL for IRP operations. We don't need to + // do this for a FASTIO operation because it is a waste of time since + // the FASTIO interface has no parameter for passing the MDL to the + // file system. + // + + if (FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_IRP_OPERATION)) { + + // + // Allocate a MDL for the new allocated memory. If we fail + // the MDL allocation then we won't swap buffer for this operation + // + + newMdl = IoAllocateMdl( newBuf, + writeLen, + FALSE, + FALSE, + NULL ); + + if (newMdl == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: %wZ Failed to allocate MDL.\n", + &volCtx->Name) ); + + leave; + } + + // + // setup the MDL for the non-paged pool we just allocated + // + + MmBuildMdlForNonPagedPool( newMdl ); + } + + // + // If the users original buffer had a MDL, get a system address. + // + + if (iopb->Parameters.Write.MdlAddress != NULL) { + + // + // This should be a simple MDL. We don't expect chained MDLs + // this high up the stack + // + + FLT_ASSERT( ((PMDL)iopb->Parameters.Write.MdlAddress)->Next == NULL); + + origBuf = MmGetSystemAddressForMdlSafe( iopb->Parameters.Write.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (origBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: %wZ Failed to get system address for MDL: %p\n", + &volCtx->Name, + iopb->Parameters.Write.MdlAddress) ); + + // + // If we could not get a system address for the users buffer, + // then we are going to fail this operation. + // + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + retValue = FLT_PREOP_COMPLETE; + leave; + } + + } else { + + // + // There was no MDL defined, use the given buffer address. + // + + origBuf = iopb->Parameters.Write.WriteBuffer; + } + + // + // Copy the memory, we must do this inside the try/except because we + // may be using a users buffer address + // + + try { + + RtlCopyMemory( newBuf, + origBuf, + writeLen ); + + } except (EXCEPTION_EXECUTE_HANDLER) { + + // + // The copy failed, return an error, failing the operation. + // + + Data->IoStatus.Status = GetExceptionCode(); + Data->IoStatus.Information = 0; + retValue = FLT_PREOP_COMPLETE; + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: %wZ Invalid user buffer, oldB=%p, status=%x\n", + &volCtx->Name, + origBuf, + Data->IoStatus.Status) ); + + leave; + } + + // + // We are ready to swap buffers, get a pre2Post context structure. + // We need it to pass the volume context and the allocate memory + // buffer to the post operation callback. + // + + p2pCtx = ExAllocateFromNPagedLookasideList( &Pre2PostContextList ); + + if (p2pCtx == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: %wZ Failed to allocate pre2Post context structure\n", + &volCtx->Name) ); + + leave; + } + + // + // Set new buffers + // + + LOG_PRINT( LOGFL_WRITE, + ("SwapBuffers!SwapPreWriteBuffers: %wZ newB=%p newMdl=%p oldB=%p oldMdl=%p len=%d\n", + &volCtx->Name, + newBuf, + newMdl, + iopb->Parameters.Write.WriteBuffer, + iopb->Parameters.Write.MdlAddress, + writeLen) ); + + iopb->Parameters.Write.WriteBuffer = newBuf; + iopb->Parameters.Write.MdlAddress = newMdl; + FltSetCallbackDataDirty( Data ); + + // + // Pass state to our post-operation callback. + // + + p2pCtx->SwappedBuffer = newBuf; + p2pCtx->VolCtx = volCtx; + + *CompletionContext = p2pCtx; + + // + // Return we want a post-operation callback + // + + retValue = FLT_PREOP_SUCCESS_WITH_CALLBACK; + + } finally { + + // + // If we don't want a post-operation callback, then free the buffer + // or MDL if it was allocated. + // + + if (retValue != FLT_PREOP_SUCCESS_WITH_CALLBACK) { + + if (newBuf != NULL) { + + FltFreePoolAlignedWithTag( FltObjects->Instance, + newBuf, + BUFFER_SWAP_TAG ); + + } + + if (newMdl != NULL) { + + IoFreeMdl( newMdl ); + } + + if (volCtx != NULL) { + + FltReleaseContext( volCtx ); + } + } + } + + return retValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +SwapPostWriteBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + +--*/ +{ + PPRE_2_POST_CONTEXT p2pCtx = CompletionContext; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + LOG_PRINT( LOGFL_WRITE, + ("SwapBuffers!SwapPostWriteBuffers: %wZ newB=%p info=%Iu Freeing\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information) ); + + // + // Free allocate POOL and volume context + // + + FltFreePoolAlignedWithTag( FltObjects->Instance, + p2pCtx->SwappedBuffer, + BUFFER_SWAP_TAG ); + + FltReleaseContext( p2pCtx->VolCtx ); + + ExFreeToNPagedLookasideList( &Pre2PostContextList, + p2pCtx ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +VOID +ReadDriverParameters ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to read the driver-specific parameters from + the registry. These values will be found in the registry location + indicated by the RegistryPath passed in. + +Arguments: + + RegistryPath - the path key passed to the driver during driver entry. + +Return Value: + + None. + +--*/ +{ + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey; + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING valueName; + UCHAR buffer[sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + sizeof( LONG )]; + + // + // If this value is not zero then somebody has already explicitly set it + // so don't override those settings. + // + + if (0 == LoggingFlags) { + + // + // Open the desired registry key + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (!NT_SUCCESS( status )) { + + return; + } + + // + // Read the given value from the registry. + // + + RtlInitUnicodeString( &valueName, L"DebugFlags" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + LoggingFlags = *((PULONG) &(((PKEY_VALUE_PARTIAL_INFORMATION)buffer)->Data)); + } + + // + // Close the registry entry + // + + ZwClose(driverRegKey); + } +} + diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.inf b/filesys/miniFilter/swapBuffers/swapBuffers.inf new file mode 100644 index 00000000..d6a5fc3a --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.inf @@ -0,0 +1,95 @@ +;;; +;;; SwapBuffers +;;; +;;; +;;; Copyright (c) 2001, Microsoft Corporation +;;; + +[Version] +signature = "$Windows NT$" +Class = "Encryption" ;This is determined by the work this filter driver does +ClassGuid = {a0a701c0-a511-42ff-aa6c-06dc0395576f} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.3 +CatalogFile = swapbuffers.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = SwapBuffers,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +;StartType = 0 ;SERVICE_BOOT_START +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Encryption" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%Instance1.Name% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +swapbuffers.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Swap Buffers Sample Mini-Filter Driver" +ServiceName = "SwapBuffers" +DriverName = "SwapBuffers" +DiskId1 = "SwapBuffers Device Installation Disk" + +;Instances specific information. +Instance1.Name = "SwapBuffers Instance" +Instance1.Altitude = "141000" +Instance1.Flags = 0x0 ; allow automatic attachments diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.rc b/filesys/miniFilter/swapBuffers/swapBuffers.rc new file mode 100644 index 00000000..436c15cf --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "SwapBuffers Filter Driver" +#define VER_INTERNALNAME_STR "swapBuffers.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.sln b/filesys/miniFilter/swapBuffers/swapBuffers.sln new file mode 100644 index 00000000..88ca2992 --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "swapBuffers", "swapBuffers.vcxproj", "{A7B7D32A-A301-4497-A68B-349341047B69}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|Win32.ActiveCfg = Debug|Win32 + {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|Win32.Build.0 = Debug|Win32 + {A7B7D32A-A301-4497-A68B-349341047B69}.Release|Win32.ActiveCfg = Release|Win32 + {A7B7D32A-A301-4497-A68B-349341047B69}.Release|Win32.Build.0 = Release|Win32 + {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|x64.ActiveCfg = Debug|x64 + {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|x64.Build.0 = Debug|x64 + {A7B7D32A-A301-4497-A68B-349341047B69}.Release|x64.ActiveCfg = Release|x64 + {A7B7D32A-A301-4497-A68B-349341047B69}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj new file mode 100644 index 00000000..3c7b7151 --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A7B7D32A-A301-4497-A68B-349341047B69} + $(MSBuildProjectName) + Debug + Win32 + {3AA28CE9-14ED-49C0-9FC7-A918130F15DA} + + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + swapBuffers + + + swapBuffers + + + swapBuffers + + + swapBuffers + + + + true + Level4 + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + true + Level4 + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters new file mode 100644 index 00000000..eba4de22 --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {35572264-BD73-4FA0-88AC-7FCC7C0917C7} + + + h;hpp;hxx;hm;inl;inc;xsd + {FF6920BD-0267-4DBD-B420-DED4DD132F5C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {2BAFD988-E41F-4DB0-A580-19F92CFFE2F4} + + + inf;inv;inx;mof;mc; + {0D9FC8FE-1009-48A7-8A94-DD71E176AEE9} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/PLX9x5x/PLX9x5x.sln b/general/PLX9x5x/PLX9x5x.sln new file mode 100644 index 00000000..c54db7f0 --- /dev/null +++ b/general/PLX9x5x/PLX9x5x.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{C3251AB4-8086-4862-985F-35302E40A027}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{954C16D2-23E1-4D8C-A040-5D4FB830D689}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Pci9x5x", "sys\Pci9x5x.vcxproj", "{12A2C4C0-856A-49BE-9F54-30D4D040C3E9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plx", "test\plx.vcxproj", "{BF101CB1-B147-40AC-8F84-13AC122A2D37}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {12A2C4C0-856A-49BE-9F54-30D4D040C3E9}.Debug|Win32.ActiveCfg = Debug|Win32 + {12A2C4C0-856A-49BE-9F54-30D4D040C3E9}.Debug|Win32.Build.0 = Debug|Win32 + {12A2C4C0-856A-49BE-9F54-30D4D040C3E9}.Release|Win32.ActiveCfg = Release|Win32 + {12A2C4C0-856A-49BE-9F54-30D4D040C3E9}.Release|Win32.Build.0 = Release|Win32 + {12A2C4C0-856A-49BE-9F54-30D4D040C3E9}.Debug|x64.ActiveCfg = Debug|x64 + {12A2C4C0-856A-49BE-9F54-30D4D040C3E9}.Debug|x64.Build.0 = Debug|x64 + {12A2C4C0-856A-49BE-9F54-30D4D040C3E9}.Release|x64.ActiveCfg = Release|x64 + {12A2C4C0-856A-49BE-9F54-30D4D040C3E9}.Release|x64.Build.0 = Release|x64 + {BF101CB1-B147-40AC-8F84-13AC122A2D37}.Debug|Win32.ActiveCfg = Debug|Win32 + {BF101CB1-B147-40AC-8F84-13AC122A2D37}.Debug|Win32.Build.0 = Debug|Win32 + {BF101CB1-B147-40AC-8F84-13AC122A2D37}.Release|Win32.ActiveCfg = Release|Win32 + {BF101CB1-B147-40AC-8F84-13AC122A2D37}.Release|Win32.Build.0 = Release|Win32 + {BF101CB1-B147-40AC-8F84-13AC122A2D37}.Debug|x64.ActiveCfg = Debug|x64 + {BF101CB1-B147-40AC-8F84-13AC122A2D37}.Debug|x64.Build.0 = Debug|x64 + {BF101CB1-B147-40AC-8F84-13AC122A2D37}.Release|x64.ActiveCfg = Release|x64 + {BF101CB1-B147-40AC-8F84-13AC122A2D37}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {12A2C4C0-856A-49BE-9F54-30D4D040C3E9} = {C3251AB4-8086-4862-985F-35302E40A027} + {BF101CB1-B147-40AC-8F84-13AC122A2D37} = {954C16D2-23E1-4D8C-A040-5D4FB830D689} + EndGlobalSection +EndGlobal diff --git a/general/PLX9x5x/ReadMe.md b/general/PLX9x5x/ReadMe.md new file mode 100644 index 00000000..a706b7d0 --- /dev/null +++ b/general/PLX9x5x/ReadMe.md @@ -0,0 +1,23 @@ +PLX9x5x PCI Driver +================== + +This sample demonstrates how to write driver for a generic PCI device using Windows Driver Framework. The target hardware for this driver is PLX9656/9653RDK-LITE board. The product kit and the hardware specification are available at . + +For more information, see [Peripheral Component Interconnect (PCI) Bus Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff537451). + +The device is a PCI device with port, memory, interrupt and DMA resources. Device can be stopped and started at run-time and also supports low power states. The driver is capable of doing concurrent read and write operations to the device but it can handle only one read or write request at any time. The following lists the driver framework interfaces demonstrated in this sample: + +- Handling PnP & Power Events +- Registering a Device Interface +- Hardware resource mapping: Port, Memory & Interrupt +- DMA Interfaces +- Serialized Default Queue for Write requests +- Serialized custom Queue for Read requests +- Handling Interrupt & DPC + +To test the driver, run the PLX.EXE test application. + +This sample driver is a minimal driver meant to demonstrate the usage of the Windows Driver Framework. It is not intended for use in a production environment. + + + diff --git a/general/PLX9x5x/sys/Init.c b/general/PLX9x5x/sys/Init.c new file mode 100644 index 00000000..b18c80c6 --- /dev/null +++ b/general/PLX9x5x/sys/Init.c @@ -0,0 +1,777 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Init.c + +Abstract: + + Contains most of initialization functions + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" + +#include "Init.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, PLxInitializeDeviceExtension) +#pragma alloc_text (PAGE, PLxPrepareHardware) +#pragma alloc_text (PAGE, PLxInitializeDMA) +#endif + +PVOID LocalMmMapIoSpace( + _In_ PHYSICAL_ADDRESS PhysicalAddress, + _In_ SIZE_T NumberOfBytes + ) +{ + typedef + PVOID + (*PFN_MM_MAP_IO_SPACE_EX) ( + _In_ PHYSICAL_ADDRESS PhysicalAddress, + _In_ SIZE_T NumberOfBytes, + _In_ ULONG Protect + ); + + UNICODE_STRING name; + PFN_MM_MAP_IO_SPACE_EX pMmMapIoSpaceEx; + + RtlInitUnicodeString(&name, L"MmMapIoSpaceEx"); + pMmMapIoSpaceEx = (PFN_MM_MAP_IO_SPACE_EX) (ULONG_PTR)MmGetSystemRoutineAddress(&name); + + if (pMmMapIoSpaceEx != NULL){ + // + // Call WIN10 API if available + // + return pMmMapIoSpaceEx(PhysicalAddress, + NumberOfBytes, + PAGE_READWRITE | PAGE_NOCACHE); + } + + return MmMapIoSpace(PhysicalAddress, NumberOfBytes, MmNonCached); +} + + +NTSTATUS +PLxInitializeDeviceExtension( + IN PDEVICE_EXTENSION DevExt + ) +/*++ +Routine Description: + + This routine is called by EvtDeviceAdd. Here the device context is + initialized and all the software resources required by the device is + allocated. + +Arguments: + + DevExt Pointer to the Device Extension + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status; + ULONG dteCount; + WDF_IO_QUEUE_CONFIG queueConfig; + + PAGED_CODE(); + + // + // Set Maximum Transfer Length (which must be less than the SRAM size). + // + DevExt->MaximumTransferLength = PCI9656_MAXIMUM_TRANSFER_LENGTH; + if(DevExt->MaximumTransferLength > PCI9656_SRAM_SIZE) { + DevExt->MaximumTransferLength = PCI9656_SRAM_SIZE; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "MaximumTransferLength %d", DevExt->MaximumTransferLength); + + // + // Calculate the number of DMA_TRANSFER_ELEMENTS + 1 needed to + // support the MaximumTransferLength. + // + dteCount = BYTES_TO_PAGES((ULONG) ROUND_TO_PAGES( + DevExt->MaximumTransferLength) + PAGE_SIZE); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "Number of DTEs %d", dteCount); + + // + // Set the number of DMA_TRANSFER_ELEMENTs (DTE) to be available. + // + DevExt->WriteTransferElements = dteCount; + DevExt->ReadTransferElements = dteCount; + + // + // The PCI9656 has two DMA Channels. This driver will use DMA Channel 0 + // as the "ToDevice" channel (Writes) and DMA Channel 1 as the + // "From Device" channel (Reads). + // + // In order to support "duplex" DMA operation (the ability to have + // concurrent reads and writes) two Dispatch Queues are created: + // one for the Write (ToDevice) requests and another for the Read + // (FromDevice) requests. While eache Dispatch Queue will operate + // independently for each other, the requests within a given Dispatch + // Queue will be serialized. This is hardware can only process one request + // per DMA Channel at a time. + // + + + // + // Setup a queue to handle only IRP_MJ_WRITE requests in Sequential + // dispatch mode. This mode ensures there is only one write request + // outstanding in the driver at any time. Framework will present the next + // request only if the current request is completed. + // Since we have configured the queue to dispatch all the specific requests + // we care about, we don't need a default queue. A default queue is + // used to receive requests that are not preconfigured to goto + // a specific queue. + // + WDF_IO_QUEUE_CONFIG_INIT ( &queueConfig, + WdfIoQueueDispatchSequential); + + queueConfig.EvtIoWrite = PLxEvtIoWrite; + + // + // Static Driver Verifier (SDV) displays a warning if it doesn't find the + // EvtIoStop callback on a power-managed queue. The 'assume' below lets + // SDV know not to worry about the EvtIoStop. + // If not explicitly set, the framework creates power-managed queues when + // the device is not a filter driver. Normally the EvtIoStop is required + // for power-managed queues, but for this driver it is not need b/c the + // driver doesn't hold on to the requests for long time or forward them to + // other drivers. + // If the EvtIoStop callback is not implemented, the framework + // waits for all in-flight (driver owned) requests to be done before + // moving the device in the Dx/sleep states or before removing the device, + // which is the correct behavior for this type of driver. + // If the requests were taking an undetermined amount of time to complete, + // or the requests were forwarded to a lower driver/another stack, the + // queue should have an EvtIoStop/EvtIoResume. + // + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate( DevExt->Device, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &DevExt->WriteQueue ); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed: %!STATUS!", status); + return status; + } + + // + // Set the Write Queue forwarding for IRP_MJ_WRITE requests. + // + status = WdfDeviceConfigureRequestDispatching( DevExt->Device, + DevExt->WriteQueue, + WdfRequestTypeWrite); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "DeviceConfigureRequestDispatching failed: %!STATUS!", status); + return status; + } + + + // + // Create a new IO Queue for IRP_MJ_READ requests in sequential mode. + // + WDF_IO_QUEUE_CONFIG_INIT( &queueConfig, + WdfIoQueueDispatchSequential); + + queueConfig.EvtIoRead = PLxEvtIoRead; + + // + // By default, Static Driver Verifier (SDV) displays a warning if it + // doesn't find the EvtIoStop callback on a power-managed queue. + // The 'assume' below causes SDV to suppress this warning. If the driver + // has not explicitly set PowerManaged to WdfFalse, the framework creates + // power-managed queues when the device is not a filter driver. Normally + // the EvtIoStop is required for power-managed queues, but for this driver + // it is not needed b/c the driver doesn't hold on to the requests for + // long time or forward them to other drivers. + // If the EvtIoStop callback is not implemented, the framework waits for + // all driver-owned requests to be done before moving in the Dx/sleep + // states or before removing the device, which is the correct behavior + // for this type of driver. If the requests were taking an indeterminate + // amount of time to complete, or if the driver forwarded the requests + // to a lower driver/another stack, the queue should have an + // EvtIoStop/EvtIoResume. + // + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate( DevExt->Device, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &DevExt->ReadQueue ); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed: %!STATUS!", status); + return status; + } + + // + // Set the Read Queue forwarding for IRP_MJ_READ requests. + // + status = WdfDeviceConfigureRequestDispatching( DevExt->Device, + DevExt->ReadQueue, + WdfRequestTypeRead); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "DeviceConfigureRequestDispatching failed: %!STATUS!", status); + return status; + } + + + // + // Create a WDFINTERRUPT object. + // + status = PLxInterruptCreate(DevExt); + + if (!NT_SUCCESS(status)) { + return status; + } + + status = PLxInitializeDMA( DevExt ); + + if (!NT_SUCCESS(status)) { + return status; + } + + return status; +} + + +NTSTATUS +PLxPrepareHardware( + IN PDEVICE_EXTENSION DevExt, + IN WDFCMRESLIST ResourcesTranslated + ) +/*++ +Routine Description: + + Gets the HW resources assigned by the bus driver from the start-irp + and maps it to system address space. + +Arguments: + + DevExt Pointer to our DEVICE_EXTENSION + +Return Value: + + None + +--*/ +{ + ULONG i; + NTSTATUS status = STATUS_SUCCESS; + CHAR * bar; + + BOOLEAN foundRegs = FALSE; + PHYSICAL_ADDRESS regsBasePA = {0}; + ULONG regsLength = 0; + + BOOLEAN foundSRAM = FALSE; + PHYSICAL_ADDRESS SRAMBasePA = {0}; + ULONG SRAMLength = 0; + + BOOLEAN foundSRAM2 = FALSE; + //PHYSICAL_ADDRESS SRAM2BasePA = {0}; + //ULONG SRAM2Length = 0; + + BOOLEAN foundPort = FALSE; + + PCM_PARTIAL_RESOURCE_DESCRIPTOR desc; + + PAGED_CODE(); + + // + // Parse the resource list and save the resource information. + // + for (i=0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++) { + + desc = WdfCmResourceListGetDescriptor( ResourcesTranslated, i ); + + if(!desc) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfResourceCmGetDescriptor failed"); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + switch (desc->Type) { + + case CmResourceTypeMemory: + + bar = NULL; + + if (foundSRAM && !foundSRAM2 && + desc->u.Memory.Length == PCI9656_SRAM_SIZE) { + + //SRAM2BasePA = desc->u.Memory.Start; + //SRAM2Length = desc->u.Memory.Length; + foundSRAM2 = TRUE; + bar = "BAR3"; + } + + if (foundRegs && !foundSRAM && + desc->u.Memory.Length == PCI9656_SRAM_SIZE) { + + SRAMBasePA = desc->u.Memory.Start; + SRAMLength = desc->u.Memory.Length; + foundSRAM = TRUE; + bar = "BAR2"; + } + + if (!foundRegs && + desc->u.Memory.Length == 0x200) { + + regsBasePA = desc->u.Memory.Start; + regsLength = desc->u.Memory.Length; + foundRegs = TRUE; + bar = "BAR0"; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + " - Memory Resource [%I64X-%I64X] %s", + desc->u.Memory.Start.QuadPart, + desc->u.Memory.Start.QuadPart + + desc->u.Memory.Length, + (bar) ? bar : "" ); + break; + + case CmResourceTypePort: + + bar = NULL; + + if (!foundPort && + desc->u.Port.Length >= 0x100) { + foundPort = TRUE; + bar = "BAR1"; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + " - Port Resource [%08I64X-%08I64X] %s", + desc->u.Port.Start.QuadPart, + desc->u.Port.Start.QuadPart + + desc->u.Port.Length, + (bar) ? bar : "" ); + break; + + default: + // + // Ignore all other descriptors + // + break; + } + } + + if (!(foundRegs && foundSRAM)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "PLxMapResources: Missing resources"); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + + // + // Map in the Registers Memory resource: BAR0 + // + DevExt->RegsBase = (PUCHAR) LocalMmMapIoSpace(regsBasePA, + regsLength); + + if (!DevExt->RegsBase) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + " - Unable to map Registers memory %08I64X, length %d", + regsBasePA.QuadPart, regsLength); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DevExt->RegsLength = regsLength; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + " - Registers %p, length %d", + DevExt->RegsBase, DevExt->RegsLength ); + + // + // Set seperated pointer to PCI9656_REGS structure. + // + DevExt->Regs = (PPCI9656_REGS) DevExt->RegsBase; + + // + // Map in the SRAM Memory Space resource: BAR2 + // + DevExt->SRAMBase = (PUCHAR) LocalMmMapIoSpace(SRAMBasePA, + SRAMLength); + + if (!DevExt->SRAMBase) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + " - Unable to map SRAM memory %08I64X, length %d", + SRAMBasePA.QuadPart, SRAMLength); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DevExt->SRAMLength = SRAMLength; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + " - SRAM %p, length %d", + DevExt->SRAMBase, DevExt->SRAMLength ); + + return status; +} + +NTSTATUS +PLxInitializeDMA( + IN PDEVICE_EXTENSION DevExt + ) +/*++ +Routine Description: + + Initializes the DMA adapter. + +Arguments: + + DevExt Pointer to our DEVICE_EXTENSION + +Return Value: + + None + +--*/ +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES attributes; + + PAGED_CODE(); + + // + // PLx PCI9656 DMA_TRANSFER_ELEMENTS must be 16-byte aligned + // + WdfDeviceSetAlignmentRequirement( DevExt->Device, + PCI9656_DTE_ALIGNMENT_16 ); + + // + // Create a new DMA Enabler instance. + // Use Scatter/Gather, 64-bit Addresses, Duplex-type profile. + // + { + WDF_DMA_ENABLER_CONFIG dmaConfig; + + WDF_DMA_ENABLER_CONFIG_INIT( &dmaConfig, + WdfDmaProfileScatterGather64Duplex, + DevExt->MaximumTransferLength ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + " - The DMA Profile is WdfDmaProfileScatterGather64Duplex"); + + status = WdfDmaEnablerCreate( DevExt->Device, + &dmaConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &DevExt->DmaEnabler ); + + if (!NT_SUCCESS (status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDmaEnablerCreate failed: %!STATUS!", status); + return status; + } + } + + // + // Allocate common buffer for building writes + // + // NOTE: This common buffer will not be cached. + // Perhaps in some future revision, cached option could + // be used. This would have faster access, but requires + // flushing before starting the DMA in PLxStartWriteDma. + // + DevExt->WriteCommonBufferSize = + sizeof(DMA_TRANSFER_ELEMENT) * DevExt->WriteTransferElements; + + _Analysis_assume_(DevExt->WriteCommonBufferSize > 0); + status = WdfCommonBufferCreate( DevExt->DmaEnabler, + DevExt->WriteCommonBufferSize, + WDF_NO_OBJECT_ATTRIBUTES, + &DevExt->WriteCommonBuffer ); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfCommonBufferCreate (write) failed: %!STATUS!", status); + return status; + } + + + DevExt->WriteCommonBufferBase = + WdfCommonBufferGetAlignedVirtualAddress(DevExt->WriteCommonBuffer); + + DevExt->WriteCommonBufferBaseLA = + WdfCommonBufferGetAlignedLogicalAddress(DevExt->WriteCommonBuffer); + + RtlZeroMemory( DevExt->WriteCommonBufferBase, + DevExt->WriteCommonBufferSize); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "WriteCommonBuffer 0x%p (#0x%I64X), length %I64d", + DevExt->WriteCommonBufferBase, + DevExt->WriteCommonBufferBaseLA.QuadPart, + WdfCommonBufferGetLength(DevExt->WriteCommonBuffer) ); + + // + // Allocate common buffer for building reads + // + // NOTE: This common buffer will not be cached. + // Perhaps in some future revision, cached option could + // be used. This would have faster access, but requires + // flushing before starting the DMA in PLxStartReadDma. + // + DevExt->ReadCommonBufferSize = + sizeof(DMA_TRANSFER_ELEMENT) * DevExt->ReadTransferElements; + + _Analysis_assume_(DevExt->ReadCommonBufferSize > 0); + status = WdfCommonBufferCreate( DevExt->DmaEnabler, + DevExt->ReadCommonBufferSize, + WDF_NO_OBJECT_ATTRIBUTES, + &DevExt->ReadCommonBuffer ); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfCommonBufferCreate (read) failed %!STATUS!", status); + return status; + } + + DevExt->ReadCommonBufferBase = + WdfCommonBufferGetAlignedVirtualAddress(DevExt->ReadCommonBuffer); + + DevExt->ReadCommonBufferBaseLA = + WdfCommonBufferGetAlignedLogicalAddress(DevExt->ReadCommonBuffer); + + RtlZeroMemory( DevExt->ReadCommonBufferBase, + DevExt->ReadCommonBufferSize); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "ReadCommonBuffer 0x%p (#0x%I64X), length %I64d", + DevExt->ReadCommonBufferBase, + DevExt->ReadCommonBufferBaseLA.QuadPart, + WdfCommonBufferGetLength(DevExt->ReadCommonBuffer) ); + + // + // Since we are using sequential queue and processing one request + // at a time, we will create transaction objects upfront and reuse + // them to do DMA transfer. Transactions objects are parented to + // DMA enabler object by default. They will be deleted along with + // along with the DMA enabler object. So need to delete them + // explicitly. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, TRANSACTION_CONTEXT); + + status = WdfDmaTransactionCreate( DevExt->DmaEnabler, + &attributes, + &DevExt->ReadDmaTransaction); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfDmaTransactionCreate(read) failed: %!STATUS!", status); + return status; + } + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, TRANSACTION_CONTEXT); + // + // Create a new DmaTransaction. + // + status = WdfDmaTransactionCreate( DevExt->DmaEnabler, + &attributes, + &DevExt->WriteDmaTransaction ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfDmaTransactionCreate(write) failed: %!STATUS!", status); + return status; + } + + return status; +} + + +NTSTATUS +PLxInitWrite( + IN PDEVICE_EXTENSION DevExt + ) +/*++ +Routine Description: + + Initialize write data structures + +Arguments: + + DevExt Pointer to Device Extension + +Return Value: + + None + +--*/ +{ + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> PLxInitWrite"); + + // + // Make sure the Dma0 DAC (Dual Address Cycle) register is set to 0. + // + WRITE_REGISTER_ULONG( (PULONG) &DevExt->Regs->Dma0_PCI_DAC, 0 ); + + // + // Clear the saved copy of the DMA Channel 0's CSR + // + DevExt->Dma0Csr.uchar = 0; + + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- PLxInitWrite"); + + return STATUS_SUCCESS; +} + + +NTSTATUS +PLxInitRead( + IN PDEVICE_EXTENSION DevExt + ) +/*++ +Routine Description: + + Initialize read data structures + +Arguments: + + DevExt Pointer to Device Extension + +Return Value: + +--*/ +{ + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> PLxInitRead"); + + // + // Make sure the DMA Chan 1 DAC (Dual Address Cycle) is set to 0. + // + WRITE_REGISTER_ULONG( (PULONG) &DevExt->Regs->Dma1_PCI_DAC, 0 ); + + // + // Clear the saved copy of the DMA Channel 1's CSR + // + DevExt->Dma1Csr.uchar = 0; + + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- PLxInitRead"); + + return STATUS_SUCCESS; +} + +VOID +PLxShutdown( + IN PDEVICE_EXTENSION DevExt + ) +/*++ + +Routine Description: + + Reset the device to put the device in a known initial state. + This is called from D0Exit when the device is torn down or + when the system is shutdown. Note that Wdf has already + called out EvtDisable callback to disable the interrupt. + +Arguments: + + DevExt - Pointer to our adapter + +Return Value: + + None + +--*/ +{ + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "---> PLxShutdown"); + + // + // WdfInterrupt is already disabled so issue a full reset + // + if (DevExt->Regs) { + + PLxHardwareReset(DevExt); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<--- PLxShutdown"); +} + +VOID +PLxHardwareReset( + IN PDEVICE_EXTENSION DevExt + ) +/*++ +Routine Description: + + Called by D0Exit when the device is being disabled or when the system is shutdown to + put the device in a known initial state. + +Arguments: + + DevExt Pointer to Device Extension + +Return Value: + +--*/ +{ + LARGE_INTEGER delay; + + union { + EEPROM_CSR bits; + ULONG ulong; + } eeCSR; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> PLxIssueFullReset"); + + // + // Drive the 9656 into soft reset. + // + eeCSR.ulong = + READ_REGISTER_ULONG( (PULONG) &DevExt->Regs->EEPROM_Ctrl_Stat ); + + eeCSR.bits.SoftwareReset = TRUE; + + WRITE_REGISTER_ULONG( (PULONG) &DevExt->Regs->EEPROM_Ctrl_Stat, + eeCSR.ulong ); + + // + // Wait 100 msec. + // + delay.QuadPart = WDF_REL_TIMEOUT_IN_MS(100); + + KeDelayExecutionThread( KernelMode, TRUE, &delay ); + + // + // Finally pull the 9656 out of reset. + // + eeCSR.bits.SoftwareReset = FALSE; + + WRITE_REGISTER_ULONG( (PULONG) &DevExt->Regs->EEPROM_Ctrl_Stat, + eeCSR.ulong ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- PLxIssueFullReset"); +} + + + diff --git a/general/PLX9x5x/sys/IsrDpc.c b/general/PLX9x5x/sys/IsrDpc.c new file mode 100644 index 00000000..a7d319d3 --- /dev/null +++ b/general/PLX9x5x/sys/IsrDpc.c @@ -0,0 +1,441 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + IsrDpc.c + +Abstract: + + Contains routines related to interrupt and dpc handling. + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" + +#include "IsrDpc.tmh" + +NTSTATUS +PLxInterruptCreate( + IN PDEVICE_EXTENSION DevExt + ) +/*++ +Routine Description: + + Configure and create the WDFINTERRUPT object. + This routine is called by EvtDeviceAdd callback. + +Arguments: + + DevExt Pointer to our DEVICE_EXTENSION + +Return Value: + + NTSTATUS code + +--*/ +{ + NTSTATUS status; + WDF_INTERRUPT_CONFIG InterruptConfig; + + WDF_INTERRUPT_CONFIG_INIT( &InterruptConfig, + PLxEvtInterruptIsr, + PLxEvtInterruptDpc ); + + InterruptConfig.EvtInterruptEnable = PLxEvtInterruptEnable; + InterruptConfig.EvtInterruptDisable = PLxEvtInterruptDisable; + + // JOHNR: Enable testing of the DpcForIsr Synchronization + InterruptConfig.AutomaticSerialization = TRUE; + + // + // Unlike WDM, framework driver should create interrupt object in EvtDeviceAdd and + // let the framework do the resource parsing and registration of ISR with the kernel. + // Framework connects the interrupt after invoking the EvtDeviceD0Entry callback + // and disconnect before invoking EvtDeviceD0Exit. EvtInterruptEnable is called after + // the interrupt interrupt is connected and EvtInterruptDisable before the interrupt is + // disconnected. + // + status = WdfInterruptCreate( DevExt->Device, + &InterruptConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &DevExt->Interrupt ); + + if( !NT_SUCCESS(status) ) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfInterruptCreate failed: %!STATUS!", status); + } + + return status; +} + +BOOLEAN +PLxEvtInterruptIsr( + IN WDFINTERRUPT Interrupt, + IN ULONG MessageID + ) +/*++ +Routine Description: + + Interrupt handler for this driver. Called at DIRQL level when the + device or another device sharing the same interrupt line asserts + the interrupt. The driver first checks the device to make sure whether + this interrupt is generated by its device and if so clear the interrupt + register to disable further generation of interrupts and queue a + DPC to do other I/O work related to interrupt - such as reading + the device memory, starting a DMA transaction, coping it to + the request buffer and completing the request, etc. + +Arguments: + + Interupt - Handle to WDFINTERRUPT Object for this device. + MessageID - MSI message ID (always 0 in this configuration) + +Return Value: + + TRUE -- This device generated the interrupt. + FALSE -- This device did not generated this interrupt. + +--*/ +{ + PDEVICE_EXTENSION devExt; + BOOLEAN isRecognized = FALSE; + + union { + INT_CSR bits; + ULONG ulong; + } intCsr; + + UNREFERENCED_PARAMETER(MessageID); + + //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, + // "--> PLxInterruptHandler"); + + devExt = PLxGetDeviceContext(WdfInterruptGetDevice(Interrupt)); + + // + // Read the Interrupt CSR register (INTCSR) + // + intCsr.ulong = READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr ); + + // + // Is DMA channel 0 (Write-side) Active? + // + if (intCsr.bits.DmaChan0IntActive) { + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, + " Interrupt for DMA Channel 0 (write)"); + + devExt->IntCsr.bits.DmaChan0IntActive = TRUE; + + // + // Clear this interrupt. + // + devExt->Dma0Csr.uchar = + READ_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma0_Csr ); + + devExt->Dma0Csr.bits.Clear = TRUE; + + WRITE_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma0_Csr, + devExt->Dma0Csr.uchar ); + + isRecognized = TRUE; + } + + // + // Is DMA channel 1 (Read-side) Active? + // + if (intCsr.bits.DmaChan1IntActive) { + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, + " Interrupt for DMA Channel 1 (read)"); + + devExt->IntCsr.bits.DmaChan1IntActive = TRUE; + + // + // Clear this interrupt. + // + devExt->Dma1Csr.uchar = + READ_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma1_Csr ); + + devExt->Dma1Csr.bits.Clear = TRUE; + + WRITE_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma1_Csr, + devExt->Dma1Csr.uchar ); + + isRecognized = TRUE; + } + + if ((isRecognized) && + ((devExt->Dma0Csr.bits.Done) || + (devExt->Dma1Csr.bits.Done))) { + // + // A read or a write or both is done. Queue a DPC. + // + WdfInterruptQueueDpcForIsr( devExt->Interrupt ); + } + + //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, + // "<-- PLxInterruptHandler"); + + return isRecognized; +} + +_Use_decl_annotations_ +VOID +PLxEvtInterruptDpc( + WDFINTERRUPT Interrupt, + WDFOBJECT Device + ) +/*++ + +Routine Description: + + DPC callback for ISR. Please note that on a multiprocessor system, + you could have more than one DPCs running simulataneously on + multiple processors. So if you are accesing any global resources + make sure to synchrnonize the accesses with a spinlock. + +Arguments: + + Interupt - Handle to WDFINTERRUPT Object for this device. + Device - WDFDEVICE object passed to InterruptCreate + +Return Value: + +--*/ +{ + NTSTATUS status; + WDFDMATRANSACTION dmaTransaction; + PDEVICE_EXTENSION devExt; + BOOLEAN writeInterrupt = FALSE; + BOOLEAN readInterrupt = FALSE; + + UNREFERENCED_PARAMETER(Device); + + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "--> EvtInterruptDpc"); + + devExt = PLxGetDeviceContext(WdfInterruptGetDevice(Interrupt)); + + // + // Acquire this device's InterruptSpinLock. + // + WdfInterruptAcquireLock( Interrupt ); + + + if ((devExt->IntCsr.bits.DmaChan0IntActive) && + (devExt->Dma0Csr.bits.Done)) { + + // + // If Dma0 channel 0 (write) is interrupting and the + // Done bit is set in the Dma0 CSR, + // we're interrupting because a WRITE is complete. + // Clear the done bit and channel interrupting bit from + // our copies... + // + devExt->IntCsr.bits.DmaChan0IntActive = FALSE; + devExt->Dma0Csr.uchar = 0; + + writeInterrupt = TRUE; + } + + if ((devExt->IntCsr.bits.DmaChan1IntActive) && + (devExt->Dma1Csr.bits.Done)) { + + // + // If DMA channel 1 is interrupting and the + // DONE bit is set in the DMA1 control/status + // register, we're interrupting because a READ + // is complete. + // Clear the done bit and channel interrupting bit from + // our copies... + // + devExt->IntCsr.bits.DmaChan1IntActive = FALSE; + devExt->Dma0Csr.uchar = 0; + + readInterrupt = TRUE; + } + + // + // Release our interrupt spinlock + // + WdfInterruptReleaseLock( Interrupt ); + + // + // Did a Write DMA complete? + // + if (writeInterrupt) { + + BOOLEAN transactionComplete; + + // + // Get the current Write DmaTransaction. + // + dmaTransaction = devExt->WriteDmaTransaction; + + // + // Indicate this DMA operation has completed: + // This may drive the transfer on the next packet if + // there is still data to be transfered in the request. + // + transactionComplete = WdfDmaTransactionDmaCompleted( dmaTransaction, + &status ); + + if (transactionComplete) { + // + // Complete this DmaTransaction. + // + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, + "Completing Write request in the DpcForIsr"); + + PLxWriteRequestComplete( dmaTransaction, status ); + + } + } + + // + // Did a Read DMA complete? + // + if (readInterrupt) { + + BOOLEAN transactionComplete; + PDMA_TRANSFER_ELEMENT dteVA; + size_t length; + + // + // Get the current Read DmaTransaction. + // + dmaTransaction = devExt->ReadDmaTransaction; + + // + // Only on Read-side -- + // Use "DMA Clear-Count Mode" to get complemetary + // transferred byte count. + // + length = WdfDmaTransactionGetCurrentDmaTransferLength( dmaTransaction ); + + dteVA = (PDMA_TRANSFER_ELEMENT) devExt->ReadCommonBufferBase; + + while(dteVA->DescPtr.LastElement == FALSE) { + length -= dteVA->TransferSize; + dteVA++; + } + length -= dteVA->TransferSize; + + // + // Indicate this DMA operation has completed: + // This may drive the transfer on the next packet if + // there is still data to be transfered in the request. + // + transactionComplete = + WdfDmaTransactionDmaCompletedWithLength( dmaTransaction, + length, + &status ); + + if (transactionComplete) { + // + // Complete this DmaTransaction. + // + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, + "Completing Read request in the DpcForIsr"); + + PLxReadRequestComplete( dmaTransaction, status ); + + } + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "<-- EvtInterruptDpc"); + + return; +} + +NTSTATUS +PLxEvtInterruptEnable( + IN WDFINTERRUPT Interrupt, + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + Called by the framework at DIRQL immediately after registering the ISR with the kernel + by calling IoConnectInterrupt. + +Return Value: + + NTSTATUS +--*/ +{ + PDEVICE_EXTENSION devExt; + + union { + INT_CSR bits; + ULONG ulong; + } intCSR; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, + "PLxEvtInterruptEnable: Interrupt 0x%p, Device 0x%p\n", + Interrupt, Device); + + devExt = PLxGetDeviceContext(WdfInterruptGetDevice(Interrupt)); + + intCSR.ulong = READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr ); + + intCSR.bits.PciIntEnable = TRUE; + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr, + intCSR.ulong ); + + return STATUS_SUCCESS; +} + +NTSTATUS +PLxEvtInterruptDisable( + IN WDFINTERRUPT Interrupt, + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + Called by the framework at DIRQL before Deregistering the ISR with the kernel + by calling IoDisconnectInterrupt. + +Return Value: + + NTSTATUS +--*/ +{ + PDEVICE_EXTENSION devExt; + + union { + INT_CSR bits; + ULONG ulong; + } intCSR; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, + "PLxEvtInterruptDisable: Interrupt 0x%p, Device 0x%p\n", + Interrupt, Device); + + devExt = PLxGetDeviceContext(WdfInterruptGetDevice(Interrupt)); + + intCSR.ulong = READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr ); + + intCSR.bits.PciIntEnable = FALSE; + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr, + intCSR.ulong ); + + return STATUS_SUCCESS; +} diff --git a/general/PLX9x5x/sys/Pci9656.c b/general/PLX9x5x/sys/Pci9656.c new file mode 100644 index 00000000..176b9ca7 --- /dev/null +++ b/general/PLX9x5x/sys/Pci9656.c @@ -0,0 +1,623 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Pci9656.c + +Abstract: + + This is a generic WDF sample driver for PLx PCI9656RDK-Lite reference + adapter. It illustrates how to use the WDF DmaObject to perform + Scatter/Gather DMA operations. + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" +// +// The trace message header (.tmh) file must be included in a source file +// before any WPP macro calls and after defining a WPP_CONTROL_GUIDS +// macro (defined in toaster.h). During the compilation, WPP scans the source +// files for DoTraceMessage() calls and builds a .tmh file which stores a unique +// data GUID for each message, the text resource string for each message, +// and the data types of the variables passed in for each message. This file +// is automatically generated by the WPP preprocessor. +// +#include "Pci9656.tmh" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, PLxEvtDeviceAdd) +#pragma alloc_text (PAGE, PLxEvtDevicePrepareHardware) +#pragma alloc_text (PAGE, PLxEvtDeviceReleaseHardware) +#pragma alloc_text (PAGE, PLxEvtDeviceD0Exit) +#pragma alloc_text (PAGE, PlxEvtDriverContextCleanup) +#pragma alloc_text (PAGE, PLxSetIdleAndWakeSettings) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path, + to driver-specific key in the registry. + +Return Value: + + NTSTATUS - if the status value is not STATUS_SUCCESS, + the driver will get unloaded immediately. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG config; + WDF_OBJECT_ATTRIBUTES attributes; + + // + // Initialize WDF WPP tracing. + // + WPP_INIT_TRACING( DriverObject, RegistryPath ); + + // + // TraceEvents function is mapped to DoTraceMessage provided by + // WPP by using a directive in the sources file. + // + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "Pci9656 Sample - Driver Framework Edition."); + + // + // Initialize the Driver Config structure. + // + WDF_DRIVER_CONFIG_INIT( &config, PLxEvtDeviceAdd ); + + // + // Register a cleanup callback so that we can call WPP_CLEANUP when + // the framework driver object is deleted during driver unload. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.EvtCleanupCallback = PlxEvtDriverContextCleanup; + + status = WdfDriverCreate( DriverObject, + RegistryPath, + &attributes, + &config, + WDF_NO_HANDLE); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDriverCreate failed with status %!STATUS!", status); + // + // Cleanup tracing here because DriverContextCleanup will not be called + // as we have failed to create WDFDRIVER object itself. + // Please note that if your return failure from DriverEntry after the + // WDFDRIVER object is created successfully, you don't have to + // call WPP cleanup because in those cases DriverContextCleanup + // will be executed when the framework deletes the DriverObject. + // + WPP_CLEANUP(DriverObject); + } + + return status; +} + + +NTSTATUS +PLxEvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ + +Routine Description: + + EvtDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. Here the driver should register all the + PNP, power and Io callbacks, register interfaces and allocate other + software resources required by the device. The driver can query + any interfaces or get the config space information from the bus driver + but cannot access hardware registers or initialize the device. + +Arguments: + +Return Value: + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDF_OBJECT_ATTRIBUTES attributes; + WDFDEVICE device; + PDEVICE_EXTENSION devExt = NULL; + + UNREFERENCED_PARAMETER( Driver ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> PLxEvtDeviceAdd"); + + PAGED_CODE(); + + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect); + + // + // Zero out the PnpPowerCallbacks structure. + // + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); + + // + // Set Callbacks for any of the functions we are interested in. + // If no callback is set, Framework will take the default action + // by itself. + // + pnpPowerCallbacks.EvtDevicePrepareHardware = PLxEvtDevicePrepareHardware; + pnpPowerCallbacks.EvtDeviceReleaseHardware = PLxEvtDeviceReleaseHardware; + + // + // These two callbacks set up and tear down hardware state that must be + // done every time the device moves in and out of the D0-working state. + // + pnpPowerCallbacks.EvtDeviceD0Entry = PLxEvtDeviceD0Entry; + pnpPowerCallbacks.EvtDeviceD0Exit = PLxEvtDeviceD0Exit; + + // + // Register the PnP Callbacks.. + // + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + // + // Initialize Fdo Attributes. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_EXTENSION); + // + // By opting for SynchronizationScopeDevice, we tell the framework to + // synchronize callbacks events of all the objects directly associated + // with the device. In this driver, we will associate queues and + // and DpcForIsr. By doing that we don't have to worrry about synchronizing + // access to device-context by Io Events and DpcForIsr because they would + // not concurrently ever. Framework will serialize them by using an + // internal device-lock. + // + attributes.SynchronizationScope = WdfSynchronizationScopeDevice; + + // + // Create the device + // + status = WdfDeviceCreate( &DeviceInit, &attributes, &device ); + + if (!NT_SUCCESS(status)) { + // + // Device Initialization failed. + // + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "DeviceCreate failed %!STATUS!", status); + return status; + } + + // + // Get the DeviceExtension and initialize it. PLxGetDeviceContext is an inline function + // defined by WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro in the + // private header file. This function will do the type checking and return + // the device context. If you pass a wrong object a wrong object handle + // it will return NULL and assert if run under framework verifier mode. + // + devExt = PLxGetDeviceContext(device); + + devExt->Device = device; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + " AddDevice PDO (0x%p) FDO (0x%p), DevExt (0x%p)", + WdfDeviceWdmGetPhysicalDevice(device), + WdfDeviceWdmGetDeviceObject(device), devExt); + + // + // Tell the Framework that this device will need an interface + // + // NOTE: See the note in Public.h concerning this GUID value. + // + status = WdfDeviceCreateDeviceInterface( device, + (LPGUID) &GUID_PLX_INTERFACE, + NULL ); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "<-- DeviceCreateDeviceInterface " + "failed %!STATUS!", status); + return status; + } + + // + // Set the idle and wait-wake policy for this device. + // + status = PLxSetIdleAndWakeSettings(devExt); + + if (!NT_SUCCESS (status)) { + // + // NOTE: The attempt to set the Idle and Wake options + // is a best-effort try. Failure is probably due to + // the non-driver environmentals, such as the system, + // bus or OS indicating that Wake is not supported for + // this case. + // All that being said, it probably not desirable to + // return the failure code as it would cause the + // AddDevice to fail and Idle and Wake are probably not + // "must-have" options. + // + // You must decide for your case whether Idle/Wake are + // "must-have" options...but my guess is probably not. + // +#if 1 + status = STATUS_SUCCESS; +#else + return status; +#endif + } + + // + // Initalize the Device Extension. + // + status = PLxInitializeDeviceExtension(devExt); + + if (!NT_SUCCESS(status)) { + return status; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PLxEvtDeviceAdd %!STATUS!", status); + + return status; +} + +NTSTATUS +PLxEvtDevicePrepareHardware ( + WDFDEVICE Device, + WDFCMRESLIST Resources, + WDFCMRESLIST ResourcesTranslated + ) +/*++ + +Routine Description: + + Performs whatever initialization is needed to setup the device, setting up + a DMA channel or mapping any I/O port resources. This will only be called + as a device starts or restarts, not every time the device moves into the D0 + state. Consequently, most hardware initialization belongs elsewhere. + +Arguments: + + Device - A handle to the WDFDEVICE + + Resources - The raw PnP resources associated with the device. Most of the + time, these aren't useful for a PCI device. + + ResourcesTranslated - The translated PnP resources associated with the + device. This is what is important to a PCI device. + +Return Value: + + NT status code - failure will result in the device stack being torn down + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDEVICE_EXTENSION devExt; + + UNREFERENCED_PARAMETER(Resources); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PLxEvtDevicePrepareHardware"); + + devExt = PLxGetDeviceContext(Device); + + status = PLxPrepareHardware(devExt, ResourcesTranslated); + if (!NT_SUCCESS (status)){ + return status; + } + + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PLxEvtDevicePrepareHardware, status %!STATUS!", status); + + return status; +} + +NTSTATUS +PLxEvtDeviceReleaseHardware( + IN WDFDEVICE Device, + IN WDFCMRESLIST ResourcesTranslated + ) +/*++ + +Routine Description: + + Unmap the resources that were mapped in PLxEvtDevicePrepareHardware. + This will only be called when the device stopped for resource rebalance, + surprise-removed or query-removed. + +Arguments: + + Device - A handle to the WDFDEVICE + + ResourcesTranslated - The translated PnP resources associated with the + device. This is what is important to a PCI device. + +Return Value: + + NT status code - failure will result in the device stack being torn down + +--*/ +{ + PDEVICE_EXTENSION devExt; + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(ResourcesTranslated); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PLxEvtDeviceReleaseHardware"); + + devExt = PLxGetDeviceContext(Device); + + if (devExt->RegsBase) { + + MmUnmapIoSpace(devExt->RegsBase, devExt->RegsLength); + devExt->RegsBase = NULL; + } + + if(devExt->SRAMBase){ + MmUnmapIoSpace(devExt->SRAMBase, devExt->SRAMLength); + devExt->SRAMBase = NULL; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PLxEvtDeviceReleaseHardware"); + + return status; +} + + +NTSTATUS +PLxEvtDeviceD0Entry( + IN WDFDEVICE Device, + IN WDF_POWER_DEVICE_STATE PreviousState + ) +/*++ + +Routine Description: + + This routine prepares the device for use. It is called whenever the device + enters the D0 state, which happens when the device is started, when it is + restarted, and when it has been powered off. + + Note that interrupts will not be enabled at the time that this is called. + They will be enabled after this callback completes. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - The handle to the WDF device object + + PreviousState - The state the device was in before this callback was invoked. + +Return Value: + + NTSTATUS + + Success implies that the device can be used. + + Failure will result in the device stack being torn down. + +--*/ +{ + PDEVICE_EXTENSION devExt; + NTSTATUS status; + + UNREFERENCED_PARAMETER(PreviousState); + + devExt = PLxGetDeviceContext(Device); + + status = PLxInitWrite( devExt ); + if (NT_SUCCESS(status)) { + + status = PLxInitRead( devExt ); + + } + + return status; +} + +NTSTATUS +PLxEvtDeviceD0Exit( + IN WDFDEVICE Device, + IN WDF_POWER_DEVICE_STATE TargetState + ) +/*++ + +Routine Description: + + This routine undoes anything done in PLxEvtDeviceD0Entry. It is called + whenever the device leaves the D0 state, which happens when the device + is stopped, when it is removed, and when it is powered off. + + The device is still in D0 when this callback is invoked, which means that + the driver can still touch hardware in this routine. + + Note that interrupts have already been disabled by the time that this + callback is invoked. + +Arguments: + + Device - The handle to the WDF device object + + TargetState - The state the device will go to when this callback completes. + +Return Value: + + Success implies that the device can be used. Failure will result in the + device stack being torn down. + +--*/ +{ + PDEVICE_EXTENSION devExt; + + PAGED_CODE(); + + devExt = PLxGetDeviceContext(Device); + + switch (TargetState) { + case WdfPowerDeviceD1: + case WdfPowerDeviceD2: + case WdfPowerDeviceD3: + + // + // Fill in any code to save hardware state here. + // + + // + // Fill in any code to put the device in a low-power state here. + // + break; + + case WdfPowerDevicePrepareForHibernation: + + // + // Fill in any code to save hardware state here. Do not put in any + // code to shut the device off. If this device cannot support being + // in the paging path (or being a parent or grandparent of a paging + // path device) then this whole case can be deleted. + // + + break; + + case WdfPowerDeviceD3Final: + default: + + // + // Reset the hardware, as we're shutting down for the last time. + // + PLxShutdown(devExt); + break; + } + + return STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +PlxEvtDriverContextCleanup( + WDFOBJECT Driver + ) +/*++ +Routine Description: + + Free all the resources allocated in DriverEntry. + +Arguments: + + Driver - handle to a WDF Driver object. + +Return Value: + + VOID. + +--*/ +{ + PAGED_CODE (); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "PlxEvtDriverContextCleanup: enter"); + + WPP_CLEANUP( WdfDriverWdmGetDriverObject( Driver ) ); + +} + + + +NTSTATUS +PLxSetIdleAndWakeSettings( + IN PDEVICE_EXTENSION FdoData + ) +/*++ +Routine Description: + + Called by EvtDeviceAdd to set the idle and wait-wake policy. Registering this policy + causes Power Management Tab to show up in the device manager. By default these + options are enabled and the user is provided control to change the settings. + +Return Value: + + NTSTATUS - Failure status is returned if the device is not capable of suspending + or wait-waking the machine by an external event. Framework checks the + capability information reported by the bus driver to decide whether the device is + capable of waking the machine. + +--*/ +{ + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; + WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings; + NTSTATUS status = STATUS_SUCCESS; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> PLxSetIdleAndWakeSettings"); + + PAGED_CODE(); + + // + // Init the idle policy structure. + // + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleCanWakeFromS0); + idleSettings.IdleTimeout = 10000; // 10-sec + + status = WdfDeviceAssignS0IdleSettings(FdoData->Device, &idleSettings); + if ( !NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "DeviceSetPowerPolicyS0IdlePolicy failed %!STATUS!", status); + return status; + } + + // + // Init wait-wake policy structure. + // + WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings); + + status = WdfDeviceAssignSxWakeSettings(FdoData->Device, &wakeSettings); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "DeviceAssignSxWakeSettings failed %!STATUS!", status); + return status; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- PLxSetIdleAndWakeSettings"); + + return status; +} + + diff --git a/general/PLX9x5x/sys/Pci9656.h b/general/PLX9x5x/sys/Pci9656.h new file mode 100644 index 00000000..48e9260d --- /dev/null +++ b/general/PLX9x5x/sys/Pci9656.h @@ -0,0 +1,14 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "WDF Driver for PLx PCI9656RDK-Lite Adapter" +#define VER_INTERNALNAME_STR "PCI9656.sys" +#define VER_ORIGINALFILENAME_STR "PCI9656.sys" + +#include "common.ver" + + + diff --git a/general/PLX9x5x/sys/Pci9656.rc b/general/PLX9x5x/sys/Pci9656.rc new file mode 100644 index 00000000..48e9260d --- /dev/null +++ b/general/PLX9x5x/sys/Pci9656.rc @@ -0,0 +1,14 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "WDF Driver for PLx PCI9656RDK-Lite Adapter" +#define VER_INTERNALNAME_STR "PCI9656.sys" +#define VER_ORIGINALFILENAME_STR "PCI9656.sys" + +#include "common.ver" + + + diff --git a/general/PLX9x5x/sys/Pci9x5x.vcxproj b/general/PLX9x5x/sys/Pci9x5x.vcxproj new file mode 100644 index 00000000..7493d098 --- /dev/null +++ b/general/PLX9x5x/sys/Pci9x5x.vcxproj @@ -0,0 +1,221 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {12A2C4C0-856A-49BE-9F54-30D4D040C3E9} + $(MSBuildProjectName) + 1 + 9 + Debug + Win32 + {0DB7EDF4-ADCA-4623-A75A-2D53E0B95D89} + + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + $(InfArch) + true + .\$(IntDir)\pci9x5x.inf + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + + + + Pci9x5x + + + Pci9x5x + + + Pci9x5x + + + Pci9x5x + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/PLX9x5x/sys/Pci9x5x.vcxproj.Filters b/general/PLX9x5x/sys/Pci9x5x.vcxproj.Filters new file mode 100644 index 00000000..1cf6e8b2 --- /dev/null +++ b/general/PLX9x5x/sys/Pci9x5x.vcxproj.Filters @@ -0,0 +1,54 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {CFE8A049-4352-4797-A933-02B2457B9660} + + + h;hpp;hxx;hm;inl;inc;xsd + {EA19AC8F-5C37-4641-AAFA-4924984AF0F7} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {D407D40B-9D6E-4731-85DE-808B502FF92E} + + + inf;inv;inx;mof;mc; + {39E92CE4-17AE-463E-B0CB-746990AE72D1} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/PLX9x5x/sys/Precomp.h b/general/PLX9x5x/sys/Precomp.h new file mode 100644 index 00000000..ada4939a --- /dev/null +++ b/general/PLX9x5x/sys/Precomp.h @@ -0,0 +1,17 @@ +#define WIN9X_COMPAT_SPINLOCK +#include +#pragma warning(disable:4201) // nameless struct/union warning + +#include +#include + +#pragma warning(default:4201) + +#include // required for GUID definitions +#include // required for WMILIB_CONTEXT + +#include "Reg9656.h" +#include "Public.h" +#include "Private.h" +#include "trace.h" + diff --git a/general/PLX9x5x/sys/Private.h b/general/PLX9x5x/sys/Private.h new file mode 100644 index 00000000..8eae7a45 --- /dev/null +++ b/general/PLX9x5x/sys/Private.h @@ -0,0 +1,211 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Private.h + +Abstract: + +Environment: + + Kernel mode + +--*/ + + +#if !defined(_PCI9656_H_) +#define _PCI9659_H_ + +// +// The device extension for the device object +// +typedef struct _DEVICE_EXTENSION { + + WDFDEVICE Device; + + // Following fields are specific to the hardware + // Configuration + + // HW Resources + PPCI9656_REGS Regs; // Registers address + PUCHAR RegsBase; // Registers base address + ULONG RegsLength; // Registers base length + + PUCHAR PortBase; // Port base address + ULONG PortLength; // Port base length + + PUCHAR SRAMBase; // SRAM base address + ULONG SRAMLength; // SRAM base length + + PUCHAR SRAM2Base; // SRAM (alt) base address + ULONG SRAM2Length; // SRAM (alt) base length + + WDFINTERRUPT Interrupt; // Returned by InterruptCreate + + union { + INT_CSR bits; + ULONG ulong; + } IntCsr; + + union { + DMA_CSR bits; + UCHAR uchar; + } Dma0Csr; + + union { + DMA_CSR bits; + UCHAR uchar; + } Dma1Csr; + + // DmaEnabler + WDFDMAENABLER DmaEnabler; + ULONG MaximumTransferLength; + + // Write + WDFQUEUE WriteQueue; + + WDFDMATRANSACTION WriteDmaTransaction; + + ULONG WriteTransferElements; + WDFCOMMONBUFFER WriteCommonBuffer; + size_t WriteCommonBufferSize; + _Field_size_(WriteCommonBufferSize) PUCHAR WriteCommonBufferBase; + PHYSICAL_ADDRESS WriteCommonBufferBaseLA; // Logical Address + + // Read + ULONG ReadTransferElements; + WDFCOMMONBUFFER ReadCommonBuffer; + size_t ReadCommonBufferSize; + _Field_size_(ReadCommonBufferSize) PUCHAR ReadCommonBufferBase; + PHYSICAL_ADDRESS ReadCommonBufferBaseLA; // Logical Address + + WDFDMATRANSACTION ReadDmaTransaction; + + WDFQUEUE ReadQueue; + + ULONG HwErrCount; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +// +// This will generate the function named PLxGetDeviceContext to be use for +// retreiving the DEVICE_EXTENSION pointer. +// +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, PLxGetDeviceContext) + +#if !defined(ASSOC_WRITE_REQUEST_WITH_DMA_TRANSACTION) +// +// The context structure used with WdfDmaTransactionCreate +// +typedef struct TRANSACTION_CONTEXT { + + WDFREQUEST Request; + +} TRANSACTION_CONTEXT, * PTRANSACTION_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(TRANSACTION_CONTEXT, PLxGetTransactionContext) + +#endif + +// +// Function prototypes +// +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD PLxEvtDeviceAdd; + +EVT_WDF_OBJECT_CONTEXT_CLEANUP PlxEvtDriverContextCleanup; + +EVT_WDF_DEVICE_D0_ENTRY PLxEvtDeviceD0Entry; +EVT_WDF_DEVICE_D0_EXIT PLxEvtDeviceD0Exit; +EVT_WDF_DEVICE_PREPARE_HARDWARE PLxEvtDevicePrepareHardware; +EVT_WDF_DEVICE_RELEASE_HARDWARE PLxEvtDeviceReleaseHardware; + +EVT_WDF_IO_QUEUE_IO_READ PLxEvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE PLxEvtIoWrite; + +EVT_WDF_INTERRUPT_ISR PLxEvtInterruptIsr; +EVT_WDF_INTERRUPT_DPC PLxEvtInterruptDpc; +EVT_WDF_INTERRUPT_ENABLE PLxEvtInterruptEnable; +EVT_WDF_INTERRUPT_DISABLE PLxEvtInterruptDisable; + +NTSTATUS +PLxSetIdleAndWakeSettings( + IN PDEVICE_EXTENSION FdoData + ); + +NTSTATUS +PLxInitializeDeviceExtension( + IN PDEVICE_EXTENSION DevExt + ); + +NTSTATUS +PLxPrepareHardware( + IN PDEVICE_EXTENSION DevExt, + IN WDFCMRESLIST ResourcesTranslated + ); + +NTSTATUS +PLxInitRead( + IN PDEVICE_EXTENSION DevExt + ); + +NTSTATUS +PLxInitWrite( + IN PDEVICE_EXTENSION DevExt + ); + +// +// WDFINTERRUPT Support +// +NTSTATUS +PLxInterruptCreate( + IN PDEVICE_EXTENSION DevExt + ); + +VOID +PLxReadRequestComplete( + IN WDFDMATRANSACTION DmaTransaction, + IN NTSTATUS Status + ); + +VOID +PLxWriteRequestComplete( + IN WDFDMATRANSACTION DmaTransaction, + IN NTSTATUS Status + ); + +NTSTATUS +PLxInitializeHardware( + IN PDEVICE_EXTENSION DevExt + ); + +VOID +PLxShutdown( + IN PDEVICE_EXTENSION DevExt + ); + +EVT_WDF_PROGRAM_DMA PLxEvtProgramReadDma; +EVT_WDF_PROGRAM_DMA PLxEvtProgramWriteDma; + +VOID +PLxHardwareReset( + IN PDEVICE_EXTENSION DevExt + ); + +NTSTATUS +PLxInitializeDMA( + IN PDEVICE_EXTENSION DevExt + ); + +#pragma warning(disable:4127) // avoid conditional expression is constant error with W4 + +#endif // _PCI9656_H_ + diff --git a/general/PLX9x5x/sys/Public.h b/general/PLX9x5x/sys/Public.h new file mode 100644 index 00000000..16c425ae --- /dev/null +++ b/general/PLX9x5x/sys/Public.h @@ -0,0 +1,35 @@ +/*++ + Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Public.h + +Abstract: + + This module contains the common declarations shared by driver + and user applications. + +Environment: + + user and kernel + +--*/ + +// +// The following value is arbitrarily chosen from the space defined +// by Microsoft as being "for non-Microsoft use" +// +// NOTE: we use OSR's GUID_OSR_PLX_INTERFACE GUID value so that we +// can use OSR's PLxTest program :-) +// +// {29D2A384-2E47-49b5-AEBF-6962C22BD7C2} +DEFINE_GUID (GUID_PLX_INTERFACE, + 0x29d2a384, 0x2e47, 0x49b5, 0xae, 0xbf, 0x69, 0x62, 0xc2, 0x2b, 0xd7, 0xc2); + + diff --git a/general/PLX9x5x/sys/Read.c b/general/PLX9x5x/sys/Read.c new file mode 100644 index 00000000..319cf094 --- /dev/null +++ b/general/PLX9x5x/sys/Read.c @@ -0,0 +1,453 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Read.c + +Abstract: + + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" + +#include "Read.tmh" + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +VOID +PLxEvtIoRead( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + Called by the framework as soon as it receives a read request. + If the device is not ready, fail the request. + Otherwise get scatter-gather list for this request and send the + packet to the hardware for DMA. + +Arguments: + + Queue - Default queue handle + Request - Handle to the write request + Lenght - Length of the data buffer associated with the request. + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + +--*/ +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + PDEVICE_EXTENSION devExt; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + "--> PLxEvtIoRead: Request %p", Request); + + // + // Get the DevExt from the Queue handle + // + devExt = PLxGetDeviceContext(WdfIoQueueGetDevice(Queue)); + + do { + // + // Validate the Length parameter. + // + if (Length > PCI9656_SRAM_SIZE) { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + // + // Initialize this new DmaTransaction. + // + status = WdfDmaTransactionInitializeUsingRequest( + devExt->ReadDmaTransaction, + Request, + PLxEvtProgramReadDma, + WdfDmaDirectionReadFromDevice ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "WdfDmaTransactionInitializeUsingRequest " + "failed: %!STATUS!", status); + break; + } + +#if 0 // FYI + // + // Modify the MaximumLength for this DmaTransaction only. + // + // Note: The new length must be less than or equal to that set when + // the DmaEnabler was created. + // + { + ULONG length = devExt->MaximumTransferLength / 2; + + //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + // "Setting a new MaxLen %d\n", length); + + WdfDmaTransactionSetMaximumLength( devExt->ReadDmaTransaction, + length ); + } +#endif + + // + // Execute this DmaTransaction. + // + status = WdfDmaTransactionExecute( devExt->ReadDmaTransaction, + WDF_NO_CONTEXT); + + if(!NT_SUCCESS(status)) { + // + // Couldn't execute this DmaTransaction, so fail Request. + // + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "WdfDmaTransactionExecute failed: %!STATUS!", status); + break; + } + + // + // Indicate that Dma transaction has been started successfully. + // The request will be complete by the Dpc routine when the DMA + // transaction completes. + // + status = STATUS_SUCCESS; + + } while (0); + + // + // If there are errors, then clean up and complete the Request. + // + if (!NT_SUCCESS(status )) { + WdfDmaTransactionRelease(devExt->ReadDmaTransaction); + WdfRequestComplete(Request, status); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + "<-- PLxEvtIoRead: status %!STATUS!", status); + + return; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +BOOLEAN +PLxEvtProgramReadDma( + IN WDFDMATRANSACTION Transaction, + IN WDFDEVICE Device, + IN WDFCONTEXT Context, + IN WDF_DMA_DIRECTION Direction, + IN PSCATTER_GATHER_LIST SgList + ) +/*++ + +Routine Description: + + The framework calls a driver's EvtProgramDma event callback function + when the driver calls WdfDmaTransactionExecute and the system has + enough map registers to do the transfer. The callback function must + program the hardware to start the transfer. A single transaction + initiated by calling WdfDmaTransactionExecute may result in multiple + calls to this function if the buffer is too large and there aren't + enough map registers to do the whole transfer. + + +Arguments: + +Return Value: + +--*/ +{ + PDEVICE_EXTENSION devExt; + size_t offset; + PDMA_TRANSFER_ELEMENT dteVA; + ULONG_PTR dteLA; + BOOLEAN errors; + ULONG i; + + UNREFERENCED_PARAMETER( Context ); + UNREFERENCED_PARAMETER( Direction ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + "--> PLxEvtProgramReadDma"); + + // + // Initialize locals + // + devExt = PLxGetDeviceContext(Device); + errors = FALSE; + + // + // Get the number of bytes as the offset to the beginning of this + // Dma operations transfer location in the buffer. + // + offset = WdfDmaTransactionGetBytesTransferred(Transaction); + + // + // Setup the pointer to the next DMA_TRANSFER_ELEMENT + // for both virtual and physical address references. + // + dteVA = (PDMA_TRANSFER_ELEMENT) devExt->ReadCommonBufferBase; + dteLA = (devExt->ReadCommonBufferBaseLA.LowPart + + sizeof(DMA_TRANSFER_ELEMENT)); + + // + // Translate the System's SCATTER_GATHER_LIST elements + // into the device's DMA_TRANSFER_ELEMENT elements. + // + for (i=0; i < SgList->NumberOfElements; i++) { + + // + // Construct this DTE. + // + // NOTE: The LocalAddress is the offset into the SRAM from + // where this Read will start. + // + dteVA->PciAddressLow = SgList->Elements[i].Address.LowPart; + dteVA->PciAddressHigh = SgList->Elements[i].Address.HighPart; + dteVA->TransferSize = SgList->Elements[i].Length; + + dteVA->LocalAddress = (ULONG) offset; + + dteVA->DescPtr.DescLocation = DESC_PTR_DESC_LOCATION__PCI; + dteVA->DescPtr.TermCountInt = FALSE; + dteVA->DescPtr.LastElement = FALSE; + dteVA->DescPtr.DirOfTransfer = DESC_PTR_DIRECTION__FROM_DEVICE; + dteVA->DescPtr.Address = DESC_PTR_ADDR( dteLA ); + + // + // Increment the DmaTransaction length by this element length + // + offset += SgList->Elements[i].Length; + + // + // If at end of SgList, then set LastElement bit in final NTE. + // + if (i == SgList->NumberOfElements - 1) { + + dteVA->DescPtr.LastElement = TRUE; + + //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + // "\tDTE[%d] : Addr #%X%08X Len %5d, Local %08X, " + // "Loc(%d), Last(%d), TermInt(%d), ToPci(%d)\n", + // i, + // dteVA->PciAddressHigh, + // dteVA->PciAddressLow, + // dteVA->TransferSize, + // dteVA->LocalAddress, + // dteVA->DescPtr.DescLocation, + // dteVA->DescPtr.LastElement, + // dteVA->DescPtr.TermCountInt, + // dteVA->DescPtr.DirOfTransfer ); + break; + } + + //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + // "\tDTE[%d] : Addr #%X%08X Len %5d, Local %08X, " + // "Loc(%d), Last(%d), TermInt(%d), ToPci(%d)\n", + // i, + // dteVA->PciAddressHigh, + // dteVA->PciAddressLow, + // dteVA->TransferSize, + // dteVA->LocalAddress, + // dteVA->DescPtr.DescLocation, + // dteVA->DescPtr.LastElement, + // dteVA->DescPtr.TermCountInt, + // dteVA->DescPtr.DirOfTransfer ); + + // + // Adjust the next DMA_TRANSFER_ELEMEMT + // + dteVA++; + dteLA += sizeof(DMA_TRANSFER_ELEMENT); + } + + // + // Start the DMA operation. + // Acquire this device's InterruptSpinLock. + // + WdfInterruptAcquireLock( devExt->Interrupt ); + + // + // DMA 1 Mode Register - (DMAMODE1) + // Enable Scatter/Gather Mode, Interrupt On Done, + // and route Ints to PCI. + // + { + union { + DMA_MODE bits; + ULONG ulong; + } dmaMode; + + dmaMode.ulong = + READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma1_Mode ); + + dmaMode.bits.SgModeEnable = TRUE; + dmaMode.bits.DoneIntEnable = TRUE; + dmaMode.bits.IntToPci = TRUE; + + dmaMode.bits.ClearCountMode = TRUE; + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma1_Mode, + dmaMode.ulong ); + } + + // + // Interrupt CSR Register - (INTCSR) + // Enable PCI Ints and DMA Channel 1 Ints. + // + { + union { + INT_CSR bits; + ULONG ulong; + } intCSR; + + intCSR.ulong = + READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr ); + + intCSR.bits.PciIntEnable = TRUE; + intCSR.bits.DmaChan1IntEnable = TRUE; + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr, + intCSR.ulong ); + } + + // + // DMA 1 Descriptor Pointer Register - (DMADPR1) + // Write the base LOGICAL address of the DMA_TRANSFER_ELEMENT list. + // + { + union { + DESC_PTR bits; + ULONG ulong; + } ptr; + + ptr.bits.DescLocation = DESC_PTR_DESC_LOCATION__PCI; + ptr.bits.TermCountInt = TRUE; + ptr.bits.Address = + DESC_PTR_ADDR( devExt->ReadCommonBufferBaseLA.LowPart ); + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma1_Desc_Ptr, + ptr.ulong ); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + " PLxEvtProgramReadDma: Start a Read DMA operation"); + + // + // DMA 1 CSR Register - (DMACSR1) + // Start the DMA operation: Set Enable and Start bits. + // + { + union { + DMA_CSR bits; + UCHAR uchar; + } dmaCSR; + + dmaCSR.uchar = + READ_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma1_Csr ); + + dmaCSR.bits.Enable = TRUE; + dmaCSR.bits.Start = TRUE; + + WRITE_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma1_Csr, + dmaCSR.uchar ); + } + + // + // Release our interrupt spinlock + // + WdfInterruptReleaseLock( devExt->Interrupt ); + + // + // NOTE: This shows how to process errors which occur in the + // PFN_WDF_PROGRAM_DMA function in general. + // Basically the DmaTransaction must be deleted and + // the Request must be completed. + // + if (errors) { + NTSTATUS status; + + // + // Must abort the transaction before deleting. + // + (VOID) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status); + ASSERT(NT_SUCCESS(status)); + + PLxReadRequestComplete( Transaction, STATUS_INVALID_DEVICE_STATE ); + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "<-- PLxEvtProgramReadDma: errors ****"); + return FALSE; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + "<-- PLxEvtProgramReadDma"); + + return TRUE; +} + +VOID +PLxReadRequestComplete( + IN WDFDMATRANSACTION DmaTransaction, + IN NTSTATUS Status + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + WDFREQUEST request; + size_t bytesTransferred; + + // + // Get the associated request from the transaction. + // + request = WdfDmaTransactionGetRequest(DmaTransaction); + + ASSERT(request); + + // + // Get the final bytes transferred count. + // + bytesTransferred = WdfDmaTransactionGetBytesTransferred( DmaTransaction ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, + "PLxReadRequestComplete: Request %p, Status %!STATUS!, " + "bytes transferred %d\n", + request, Status, (int) bytesTransferred ); + + WdfDmaTransactionRelease(DmaTransaction); + + // + // Complete this Request. + // + WdfRequestCompleteWithInformation( request, Status, bytesTransferred); + +} + diff --git a/general/PLX9x5x/sys/Reg9656.h b/general/PLX9x5x/sys/Reg9656.h new file mode 100644 index 00000000..9165403b --- /dev/null +++ b/general/PLX9x5x/sys/Reg9656.h @@ -0,0 +1,259 @@ +#ifndef __REG9656_H_ +#define __REG9656_H_ + +//***************************************************************************** +// +// File Name: Reg9656.h +// +// Description: This file defines all the PLX 9656 chip Registers. +// +// NOTE: These definitions are for memory-mapped register access only. +// +//***************************************************************************** + +//----------------------------------------------------------------------------- +// PCI Device/Vendor Ids. +//----------------------------------------------------------------------------- +#define PLX_PCI_VENDOR_ID 0x10B5 +#define PLX_PCI_DEVICE_ID 0x9601 + +//----------------------------------------------------------------------------- +// Expected size of the PCI9656RDK-Lite on-board SRAM +//----------------------------------------------------------------------------- +#define PCI9656_SRAM_SIZE (0x20000) + +//----------------------------------------------------------------------------- +// Maximum DMA transfer size (in bytes). +// +// NOTE: This value is rather abritrary for this drive, +// but must be between [0 - PCI9656_SRAM_SIZE] in value. +// You can play with the value to see how requests are sequenced as a +// set of one or more DMA operations. +//----------------------------------------------------------------------------- +#define PCI9656_MAXIMUM_TRANSFER_LENGTH (8*1024) + +//----------------------------------------------------------------------------- +// The DMA_TRANSFER_ELEMENTS (the 9656's hardware scatter/gather list element) +// must be aligned on a 16-byte boundry. This is because the lower 4 bits of +// the DESC_PTR.Address contain bit fields not included in the "next" address. +//----------------------------------------------------------------------------- +#define PCI9656_DTE_ALIGNMENT_16 FILE_OCTA_ALIGNMENT + +//----------------------------------------------------------------------------- +// Number of DMA channels supported by PLX Chip +//----------------------------------------------------------------------------- +#define PCI9656_DMA_CHANNELS (2) + +//----------------------------------------------------------------------------- +// DMA Transfer Element (DTE) +// +// NOTE: This structure is modeled after the DMAPADRx, DMALADRx, DMASIZx and +// DMAADPRx registers. See DataBook Registers description: 11-74 to 11-77. +//----------------------------------------------------------------------------- +typedef struct _DESC_PTR_ { + + unsigned int DescLocation : 1 ; // TRUE - Desc in PCI (host) memory + unsigned int LastElement : 1 ; // TRUE - last NTE in chain + unsigned int TermCountInt : 1 ; // TRUE - Interrupt on term count. + unsigned int DirOfTransfer : 1 ; // see defines below + unsigned int Address : 28 ; + +} DESC_PTR; + +#define DESC_PTR_DESC_LOCATION__LOCAL (0) +#define DESC_PTR_DESC_LOCATION__PCI (1) + +#define DESC_PTR_DIRECTION__TO_DEVICE (0) +#define DESC_PTR_DIRECTION__FROM_DEVICE (1) + +typedef struct _DMA_TRANSFER_ELEMENT { + + unsigned int PciAddressLow ; + unsigned int LocalAddress ; + unsigned int TransferSize ; + DESC_PTR DescPtr ; + unsigned int PciAddressHigh ; + unsigned int pad [3] ; + +} DMA_TRANSFER_ELEMENT, * PDMA_TRANSFER_ELEMENT; + +#define DESC_PTR_ADDR_SHIFT (4) +#define DESC_PTR_ADDR(a) (((ULONG) a) >> DESC_PTR_ADDR_SHIFT) + + +//----------------------------------------------------------------------------- +// Define the Interrupt Command Status Register (CSR) +//----------------------------------------------------------------------------- +typedef struct _INT_CSR_ { + unsigned int EnableIntSources : 2; // bit 0-1 + unsigned int GenPciBusSerrInt : 1; // bit 2 + unsigned int MailboxIntEnable : 1; // bit 3 + unsigned int PowerMgmtIntEnable : 1; // bit 4 + unsigned int PowerMgmtInt : 1; // bit 5 + unsigned int DM_WriteParityCheck : 1; // bit 6 + unsigned int DM_WriteParityCheckErr : 1; // bit 7 + unsigned int PciIntEnable : 1; // bit 8 + unsigned int PciDoorbellIntEnable : 1; // bit 9 + unsigned int PciAbortIntEnable : 1; // bit 10 + unsigned int LocalIntInputEnable : 1; // bit 11 + unsigned int RetryAbortEnable : 1; // bit 12 + unsigned int PciDoorbellIntActive : 1; // bit 13 + unsigned int PciAbortIntActive : 1; // bit 14 + unsigned int LocalIntInputActive : 1; // bit 15 + unsigned int LocalIntOutputEnable : 1; // bit 16 + unsigned int LocalDoorbellIntEnable : 1; // bit 17 + unsigned int DmaChan0IntEnable : 1; // bit 18 + unsigned int DmaChan1IntEnable : 1; // bit 19 + unsigned int LocalDoorbellIntActive : 1; // bit 20 + unsigned int DmaChan0IntActive : 1; // bit 21 + unsigned int DmaChan1IntActive : 1; // bit 22 + unsigned int BistIntActive : 1; // bit 23 + unsigned int DM_WasBusMastOnAbort : 1; // bit 24 + unsigned int Dma0_WasBusMastOnAbort : 1; // bit 25 + unsigned int Dma1_WasBusMastOnAbort : 1; // bit 26 + unsigned int AbortAfter256Retries : 1; // bit 27 + unsigned int DataInMailbox0 : 1; // bit 28 + unsigned int DataInMailbox1 : 1; // bit 29 + unsigned int DataInMailbox2 : 1; // bit 30 + unsigned int DataInMailbox3 : 1; // bit 31 +} INT_CSR; + + +//----------------------------------------------------------------------------- +// Define the EEPROM CSR (CNTRL) +//----------------------------------------------------------------------------- +typedef struct _EEPROM_CSR_ { + unsigned int PciReadCmdForDma : 4; // bit 0-3 + unsigned int PciWriteCmdForDma : 4; // bit 4-7 + unsigned int PciMemReadCmdForDM : 4; // bit 8-11 + unsigned int PciMemWriteCmdForDM : 4; // bit 12-15 + unsigned int GPIO_Output : 1; // bit 16 + unsigned int GPIO_Input : 1; // bit 17 + unsigned int User_i_Select : 1; // bit 18 + unsigned int User_o_Select : 1; // bit 19 + unsigned int LINT_o_IntStatus : 1; // bit 20 + unsigned int TeaIntStatus : 1; // bit 21 + unsigned int reserved : 2; // bit 22-23 + unsigned int SerialEEPROMClockOut : 1; // bit 24 + unsigned int SerialEEPROMChipSelect : 1; // bit 25 + unsigned int SerialEEPROMDataIn : 1; // bit 26 + unsigned int SerialEEPROMDataOut : 1; // bit 27 + unsigned int SerialEEPROMPresent : 1; // bit 28 + unsigned int ReloadConfigRegisters : 1; // bit 29 + unsigned int SoftwareReset : 1; // bit 30 + unsigned int EEDOInputEnable : 1; // bit 31 +} EEPROM_CSR; + +//----------------------------------------------------------------------------- +// Define the DMA Mode Register +//----------------------------------------------------------------------------- +typedef struct _DMA_MODE_ { + unsigned int LocalBusDataWidth : 2 ; // bit 0-1 + unsigned int WaitStateCounter : 4 ; // bit 2-5 + unsigned int TaRdyInputEnable : 1 ; // bit 6 + unsigned int BurstEnable : 1 ; // bit 7 + unsigned int LocalBurstEnable : 1 ; // bit 8 + unsigned int SgModeEnable : 1 ; // bit 9 + unsigned int DoneIntEnable : 1 ; // bit 10 + unsigned int LocalAddressMode : 1 ; // bit 11 + unsigned int DemandMode : 1 ; // bit 12 + unsigned int MWIEnable : 1 ; // bit 13 + unsigned int EOTEnable : 1 ; // bit 14 + unsigned int TermModeSelect : 1 ; // bit 15 + unsigned int ClearCountMode : 1 ; // bit 16 + unsigned int IntToPci : 1 ; // bit 17 + unsigned int DACChainLoad : 1 ; // bit 18 + unsigned int EOTEndLink : 1 ; // bit 19 + unsigned int RingMgmtValidMode : 1 ; // bit 20 + unsigned int RingMgmtValidStop : 1 ; // bit 21 + unsigned int reserved : 10; // bit 22-31 +} DMA_MODE; + +//----------------------------------------------------------------------------- +// Define the DMA Command Status Register (CSR) +//----------------------------------------------------------------------------- +#pragma warning(disable:4214) // bit field types other than int warning + +typedef struct _DMA_CSR_ { + unsigned char Enable : 1; // bit 0 + unsigned char Start : 1; // bit 1 + unsigned char Abort : 1; // bit 2 + unsigned char Clear : 1; // bit 3 + unsigned char Done : 1; // bit 4 + unsigned char pad : 1; // bit 5-7 +} DMA_CSR; + +#pragma warning(default:4214) + +//----------------------------------------------------------------------------- +// PCI9659_REGS structure +//----------------------------------------------------------------------------- +typedef struct _PCI9656_REGS_ { + + unsigned int Space0_Range ; // 0x000 + unsigned int Space0_Remap ; // 0x004 + unsigned int Local_DMA_Arbit ; // 0x008 + unsigned int Endian_Desc ; // 0x00C + unsigned int Exp_XP_ROM_Range ; // 0x010 + unsigned int Exp_ROM_Remap ; // 0x014 + unsigned int Space0_ROM_Desc ; // 0x018 + unsigned int DM_Range ; // 0x01C + unsigned int DM_Mem_Base ; // 0x020 + unsigned int DM_IO_Base ; // 0x024 + unsigned int DM_PCI_Mem_Remap ; // 0x028 + + unsigned int pad1 [7] ; // range [0x02C - 0x044] + + unsigned int Mailbox2 ; // 0x048 + unsigned int Mailbox3 ; // 0x04C + unsigned int Mailbox4 ; // 0x050 + unsigned int Mailbox5 ; // 0x054 + unsigned int Mailbox6 ; // 0x058 + unsigned int Mailbox7 ; // 0x05C + + unsigned int Local_Doorbell ; // 0x060 + unsigned int PCI_Doorbell ; // 0x064 + INT_CSR Int_Csr ; // 0x068 + EEPROM_CSR EEPROM_Ctrl_Stat ; // 0x06C + unsigned int Perm_Vendor_Id ; // 0x070 + unsigned int Revision_Id ; // 0x074 + + unsigned int pad2 [2] ; // range [0x078 - 0x07C] + + DMA_MODE Dma0_Mode ; // 0x080 + unsigned int Dma0_PCI_Addr ; // 0x084 + unsigned int Dma0_Local_Addr ; // 0x088 + unsigned int Dma0_Count ; // 0x08C + DESC_PTR Dma0_Desc_Ptr ; // 0x090 + + DMA_MODE Dma1_Mode ; // 0x094 + unsigned int Dma1_PCI_Addr ; // 0x098 + unsigned int Dma1_Local_Addr ; // 0x09C + unsigned int Dma1_Count ; // 0x0A0 + DESC_PTR Dma1_Desc_Ptr ; // 0x0A4 + + DMA_CSR Dma0_Csr ; // 0x0A8 + DMA_CSR Dma1_Csr ; // 0x0A9 + + unsigned char pad3 [2] ; // pad to next 32-bit boundry + + unsigned int Dma_Arbit ; // 0x0AC + unsigned int Dma_Threshold ; // 0x0B0 + + unsigned int Dma0_PCI_DAC ; // 0x0B4 + unsigned int Dma1_PCI_DAC ; // 0x0B8 + + unsigned int pad4 [13] ; // range [0x0BC - 0x0EC] + + unsigned int Space1_Range ; // 0x0F0 + unsigned int Space1_Remap ; // 0x0F4 + unsigned int Space1_Desc ; // 0x0F8 + unsigned int DM_DAC ; // 0x0FC + + unsigned int Arbiter_Cntl ; // 0x100 + unsigned int Abort_Address ; // 0x104 + +} PCI9656_REGS, * PPCI9656_REGS; + + +#endif // __REG9656_H_ diff --git a/general/PLX9x5x/sys/Write.c b/general/PLX9x5x/sys/Write.c new file mode 100644 index 00000000..3e4d8b53 --- /dev/null +++ b/general/PLX9x5x/sys/Write.c @@ -0,0 +1,525 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Write.c + +Abstract: + + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" + +#include "Write.tmh" + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +VOID +PLxEvtIoWrite( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + Called by the framework as soon as it receives a write request. + If the device is not ready, fail the request. + Otherwise get scatter-gather list for this request and send the + packet to the hardware for DMA. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + Request - Handle to a framework request object. + + Length - Length of the IO operation + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + +--*/ +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + PDEVICE_EXTENSION devExt = NULL; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, + "--> PLxEvtIoWrite: Request %p", Request); + + // + // Get the DevExt from the Queue handle + // + devExt = PLxGetDeviceContext(WdfIoQueueGetDevice(Queue)); + + // + // Validate the Length parameter. + // + if (Length > PCI9656_SRAM_SIZE) { + status = STATUS_INVALID_BUFFER_SIZE; + goto CleanUp; + } + + // + // Following code illustrates two different ways of initializing a DMA + // transaction object. If ASSOC_WRITE_REQUEST_WITH_DMA_TRANSACTION is + // defined in the sources file, the first section will be used. + // +#ifdef ASSOC_WRITE_REQUEST_WITH_DMA_TRANSACTION + + // + // This section illustrates how to create and initialize + // a DmaTransaction using a WDF Request. + // This type of coding pattern would probably be the most commonly used + // for handling client Requests. + // + status = WdfDmaTransactionInitializeUsingRequest( + devExt->WriteDmaTransaction, + Request, + PLxEvtProgramWriteDma, + WdfDmaDirectionWriteToDevice ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfDmaTransactionInitializeUsingRequest failed: " + "%!STATUS!", status); + goto CleanUp; + } +#else + // + // This section illustrates how to create and initialize + // a DmaTransaction via direct parameters (e.g. not using a WDF Request). + // This type of coding pattern might be used for driver-initiated DMA + // operations (e.g. DMA operations not based on driver client requests.) + // + // NOTE: This example unpacks the WDF Request in order to get a set of + // parameters for the call to WdfDmaTransactionInitialize. While + // this is completely legimate, the represenative usage pattern + // for WdfDmaTransactionIniitalize would have the driver create/ + // initialized a DmaTransactin without a WDF Request. A simple + // example might be where the driver needs to DMA the devices's + // firmware to it during device initialization. There would be + // no WDF Request; the driver would supply the parameters for + // WdfDmaTransactionInitialize directly. + // + { + PTRANSACTION_CONTEXT transContext; + PMDL mdl; + PVOID virtualAddress; + ULONG length; + + // + // Initialize this new DmaTransaction with direct parameters. + // + status = WdfRequestRetrieveInputWdmMdl(Request, &mdl); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfRequestRetrieveInputWdmMdl failed: %!STATUS!", status); + goto CleanUp; + } + + virtualAddress = MmGetMdlVirtualAddress(mdl); + length = MmGetMdlByteCount(mdl); + + _Analysis_assume_(length > 0); + status = WdfDmaTransactionInitialize( devExt->WriteDmaTransaction, + PLxEvtProgramWriteDma, + WdfDmaDirectionWriteToDevice, + mdl, + virtualAddress, + length ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfDmaTransactionInitialize failed: %!STATUS!", status); + goto CleanUp; + } + + // + // Retreive this DmaTransaction's context ptr (aka TRANSACTION_CONTEXT) + // and fill it in with info. + // + transContext = PLxGetTransactionContext( devExt->WriteDmaTransaction ); + transContext->Request = Request; + } +#endif + +#if 0 //FYI + // + // Modify the MaximumLength for this DmaTransaction only. + // + // Note: The new length must be less than or equal to that set when + // the DmaEnabler was created. + // + { + ULONG length = devExt->MaximumTransferLength / 2; + + //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, + // "Setting a new MaxLen %d", length); + + WdfDmaTransactionSetMaximumLength( devExt->WriteDmaTransaction, length ); + } +#endif + + // + // Execute this DmaTransaction transaction. + // + status = WdfDmaTransactionExecute( devExt->WriteDmaTransaction, + WDF_NO_CONTEXT); + + if(!NT_SUCCESS(status)) { + + // + // Couldn't execute this DmaTransaction, so fail Request. + // + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfDmaTransactionExecute failed: %!STATUS!", status); + goto CleanUp; + } + + // + // Indicate that Dma transaction has been started successfully. The request + // will be complete by the Dpc routine when the DMA transaction completes. + // + status = STATUS_SUCCESS; + +CleanUp: + + // + // If there are errors, then clean up and complete the Request. + // + if (!NT_SUCCESS(status)) { + WdfDmaTransactionRelease(devExt->WriteDmaTransaction); + WdfRequestComplete(Request, status); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, + "<-- PLxEvtIoWrite: %!STATUS!", status); + + return; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +BOOLEAN +PLxEvtProgramWriteDma( + IN WDFDMATRANSACTION Transaction, + IN WDFDEVICE Device, + IN PVOID Context, + IN WDF_DMA_DIRECTION Direction, + IN PSCATTER_GATHER_LIST SgList + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + PDEVICE_EXTENSION devExt; + size_t offset; + PDMA_TRANSFER_ELEMENT dteVA; + ULONG_PTR dteLA; + BOOLEAN errors; + ULONG i; + + UNREFERENCED_PARAMETER( Context ); + UNREFERENCED_PARAMETER( Direction ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, + "--> PLxEvtProgramWriteDma"); + + // + // Initialize locals + // + devExt = PLxGetDeviceContext(Device); + errors = FALSE; + + // + // Get the number of bytes as the offset to the beginning of this + // Dma operations transfer location in the buffer. + // + offset = WdfDmaTransactionGetBytesTransferred(Transaction); + + // + // Setup the pointer to the next DMA_TRANSFER_ELEMENT + // for both virtual and physical address references. + // + dteVA = (PDMA_TRANSFER_ELEMENT) devExt->WriteCommonBufferBase; + dteLA = (devExt->WriteCommonBufferBaseLA.LowPart + + sizeof(DMA_TRANSFER_ELEMENT)); + + // + // Translate the System's SCATTER_GATHER_LIST elements + // into the device's DMA_TRANSFER_ELEMENT elements. + // + for (i=0; i < SgList->NumberOfElements; i++) { + + // + // Construct this DTE. + // + // NOTE: The LocalAddress is the offset into the SRAM from + // where this Write will start. + // + dteVA->PciAddressLow = SgList->Elements[i].Address.LowPart; + dteVA->PciAddressHigh = SgList->Elements[i].Address.HighPart; + dteVA->TransferSize = SgList->Elements[i].Length; + + dteVA->LocalAddress = (ULONG) offset; + + dteVA->DescPtr.DescLocation = DESC_PTR_DESC_LOCATION__PCI; + dteVA->DescPtr.TermCountInt = FALSE; + dteVA->DescPtr.LastElement = FALSE; + dteVA->DescPtr.DirOfTransfer = DESC_PTR_DIRECTION__TO_DEVICE; + dteVA->DescPtr.Address = DESC_PTR_ADDR( dteLA ); + + // + // Increment the DmaTransaction length by this element length + // + offset += SgList->Elements[i].Length; + + // + // If at end of SgList, then set LastElement bit in final NTE. + // + if (i == SgList->NumberOfElements - 1) { + + dteVA->DescPtr.LastElement = TRUE; + +#if 0 // set to 1 for recording the details + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, + "\tDTE[%d] : Addr #%X%08X Len %5d, Local %08X, " + "Loc(%d), Last(%d), TermInt(%d), ToPci(%d)\n", + i, + dteVA->PciAddressHigh, + dteVA->PciAddressLow, + dteVA->TransferSize, + dteVA->LocalAddress, + dteVA->DescPtr.DescLocation, + dteVA->DescPtr.LastElement, + dteVA->DescPtr.TermCountInt, + dteVA->DescPtr.DirOfTransfer ); +#endif + break; + } + +#if 0 // set to 1 for recording the details + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, + "\tDTE[%d] : Addr #%X%08X Len %5d, Local %08X, " + "Loc(%d), Last(%d), TermInt(%d), ToPci(%d)\n", + i, + dteVA->PciAddressHigh, + dteVA->PciAddressLow, + dteVA->TransferSize, + dteVA->LocalAddress, + dteVA->DescPtr.DescLocation, + dteVA->DescPtr.LastElement, + dteVA->DescPtr.TermCountInt, + dteVA->DescPtr.DirOfTransfer ); +#endif + + // + // Adjust the next DMA_TRANSFER_ELEMEMT + // + dteVA++; + dteLA += sizeof(DMA_TRANSFER_ELEMENT); + } + + // + // Start the DMA operation. + // Acquire this device's InterruptSpinLock. + // + WdfInterruptAcquireLock( devExt->Interrupt ); + + // + // DMA 0 Mode Register - (DMAMODE0) + // Enable Scatter/Gather Mode, Interrupt On Done, + // and route Ints to PCI. + // + { + union { + DMA_MODE bits; + ULONG ulong; + } dmaMode; + + dmaMode.ulong = + READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma0_Mode ); + + dmaMode.bits.SgModeEnable = TRUE; + dmaMode.bits.DoneIntEnable = TRUE; + dmaMode.bits.IntToPci = TRUE; + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma0_Mode, + dmaMode.ulong ); + } + + // + // Interrupt CSR Register - (INTCSR) + // Enable PCI Ints and DMA Channel 0 Ints. + // + { + union { + INT_CSR bits; + ULONG ulong; + } intCSR; + + intCSR.ulong = + READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr ); + + intCSR.bits.PciIntEnable = TRUE; + intCSR.bits.DmaChan0IntEnable = TRUE; + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr, + intCSR.ulong ); + } + + // + // DMA 0 Descriptor Pointer Register - (DMADPR0) + // Write the base LOGICAL address of the DMA_TRANSFER_ELEMENT list. + // + { + union { + DESC_PTR bits; + ULONG ulong; + } ptr; + + ptr.bits.DescLocation = DESC_PTR_DESC_LOCATION__PCI; + ptr.bits.TermCountInt = TRUE; + ptr.bits.Address = + DESC_PTR_ADDR( devExt->WriteCommonBufferBaseLA.LowPart ); + + WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Dma0_Desc_Ptr, + ptr.ulong ); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, + " PLxEvtProgramWriteDma: Start a Write DMA operation"); + + // + // DMA 0 CSR Register - (DMACSR0) + // Start the DMA operation: Set Enable and Start bits. + // + { + union { + DMA_CSR bits; + UCHAR uchar; + } dmaCSR; + + dmaCSR.uchar = + READ_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma0_Csr ); + + dmaCSR.bits.Enable = TRUE; + dmaCSR.bits.Start = TRUE; + + WRITE_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma0_Csr, + dmaCSR.uchar ); + } + + // + // Release our interrupt spinlock + // + WdfInterruptReleaseLock( devExt->Interrupt ); + + // + // NOTE: This shows how to process errors which occur in the + // PFN_WDF_PROGRAM_DMA function in general. + // Basically the DmaTransaction must be deleted and + // the Request must be completed. + // + if (errors) { + // + // Must abort the transaction before deleting it. + // + NTSTATUS status; + + (VOID) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status); + ASSERT(NT_SUCCESS(status)); + PLxWriteRequestComplete( Transaction, STATUS_INVALID_DEVICE_STATE ); + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "<-- PLxEvtProgramWriteDma: error ****"); + return FALSE; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, + "<-- PLxEvtProgramWriteDma"); + + return TRUE; +} + + +VOID +PLxWriteRequestComplete( + IN WDFDMATRANSACTION DmaTransaction, + IN NTSTATUS Status + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + WDFREQUEST request; + size_t bytesTransferred; + + // + // Initialize locals + // + +#ifdef ASSOC_WRITE_REQUEST_WITH_DMA_TRANSACTION + + request = WdfDmaTransactionGetRequest(DmaTransaction); + +#else + // + // If CreateDirect was used then there will be no assoc. Request. + // + { + PTRANSACTION_CONTEXT transContext = PLxGetTransactionContext(DmaTransaction); + + request = transContext->Request; + transContext->Request = NULL; + + } +#endif + + // + // Get the final bytes transferred count. + // + bytesTransferred = WdfDmaTransactionGetBytesTransferred( DmaTransaction ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, + "PLxWriteRequestComplete: Request %p, Status %!STATUS!, " + "bytes transferred %d\n", + request, Status, (int) bytesTransferred ); + + WdfDmaTransactionRelease(DmaTransaction); + + WdfRequestCompleteWithInformation( request, Status, bytesTransferred); + +} + diff --git a/general/PLX9x5x/sys/pci9x5x.inx b/general/PLX9x5x/sys/pci9x5x.inx new file mode 100644 index 00000000..3904344b --- /dev/null +++ b/general/PLX9x5x/sys/pci9x5x.inx @@ -0,0 +1,97 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +; KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR +; PURPOSE. +; +;Module Name: +; +; Pci9x5x.INF +; +;Abstract: +; INF file for the PLx PCI9x5xRDK-Lite driver. +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFT% +DriverVer=03/20/2003,5.00.3788 +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 +HKR,,DeviceCharacteristics,0x10001,0x100 ;Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)" ;Allow generic all access to system and built-in Admin. + + +; ================= Device Install section ===================== + +[ControlFlags] +ExcludeFromSelect=* + +[Manufacturer] +%MSFT%=MSFT,NT$ARCH$ + +[SourceDisksFiles] +Pci9x5x.sys=1 + +[SourceDisksNames] +1=%DISK_NAME%, + +; For Win2K +[MSFT] +; DisplayName Section DeviceId +; ----------- ------- -------- +%Pci9056.DRVDESC%= Pci9x5x_Inst, PCI\VEN_10b5&DEV_5601 +%Pci9656.DRVDESC%= Pci9x5x_Inst, PCI\VEN_10b5&DEV_9601 + +; For XP and later +[MSFT.NT$ARCH$] +; DisplayName Section DeviceId +; ----------- ------- -------- +%Pci9056.DRVDESC%= Pci9x5x_Inst, PCI\VEN_10b5&DEV_5601 +%Pci9656.DRVDESC%= Pci9x5x_Inst, PCI\VEN_10b5&DEV_9601 + +[Pci9x5x_Inst.NT] +CopyFiles=Pci9x5x.CopyFiles + +[Pci9x5x.CopyFiles] +Pci9x5x.sys + +[Pci9x5x_Inst.NT.Services] +AddService=Pci9x5x,0x00000002,Pci9x5x_Service + +[Pci9x5x_Service] +DisplayName = %Pci9x5x.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\Pci9x5x.sys + +[Pci9x5x_Inst.NT.Wdf] +KmdfService = Pci9x5x, Pci9x5x_wdfsect +[Pci9x5x_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +[Strings] +MSFT = "Microsoft" +ClassName = "Sample Device" +Pci9x5x.SVCDESC = "Sample Driver Service for the PCI9x5xRDK-Lite adapter" +Pci9056.DRVDESC = "Sample Driver for the PCI9056RDK-Lite adapter" +Pci9656.DRVDESC = "Sample Driver for the PCI9656RDK-Lite adapter" +DISK_NAME = "Pci9x5x Sample Install Disk" diff --git a/general/PLX9x5x/sys/precompsrc.c b/general/PLX9x5x/sys/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/general/PLX9x5x/sys/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/general/PLX9x5x/sys/trace.h b/general/PLX9x5x/sys/trace.h new file mode 100644 index 00000000..a9726b3a --- /dev/null +++ b/general/PLX9x5x/sys/trace.h @@ -0,0 +1,66 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + TRACE.h + +Abstract: + + Header file for the debug tracing related function defintions and macros. + +Environment: + + Kernel mode + + +--*/ + +#include // For TRACE_LEVEL definitions + +// +// If software tracing is defined in the sources file.. +// WPP_DEFINE_CONTROL_GUID specifies the GUID used for this driver. +// *** REPLACE THE GUID WITH YOUR OWN UNIQUE ID *** +// WPP_DEFINE_BIT allows setting debug bit masks to selectively print. +// The names defined in the WPP_DEFINE_BIT call define the actual names +// that are used to control the level of tracing for the control guid +// specified. +// +// Name of the logger is PLX9x5x and the guid is +// {CA630800-D4D4-4457-8983-DFBBFCAC5542} +// (0xca630800, 0xd4d4, 0x4457, 0x89, 0x83, 0xdf, 0xbb, 0xfc, 0xac, 0x55, 0x42); +// + +#define WPP_CHECK_FOR_NULL_STRING //to prevent exceptions due to NULL strings + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(PLX9x5xTraceGuid, (ca630800, D4D4, 4457,8983, DFBBFCAC5542),\ + WPP_DEFINE_BIT(DBG_INIT) /* bit 0 = 0x00000001 */ \ + WPP_DEFINE_BIT(DBG_PNP) /* bit 1 = 0x00000002 */ \ + WPP_DEFINE_BIT(DBG_POWER) /* bit 2 = 0x00000004 */ \ + WPP_DEFINE_BIT(DBG_WMI) /* bit 3 = 0x00000008 */ \ + WPP_DEFINE_BIT(DBG_CREATE_CLOSE) /* bit 4 = 0x00000010 */ \ + WPP_DEFINE_BIT(DBG_IOCTLS) /* bit 5 = 0x00000020 */ \ + WPP_DEFINE_BIT(DBG_WRITE) /* bit 6 = 0x00000040 */ \ + WPP_DEFINE_BIT(DBG_READ) /* bit 7 = 0x00000080 */ \ + WPP_DEFINE_BIT(DBG_DPC) /* bit 8 = 0x00000100 */ \ + WPP_DEFINE_BIT(DBG_INTERRUPT) /* bit 9 = 0x00000200 */ \ + WPP_DEFINE_BIT(DBG_LOCKS) /* bit 10 = 0x00000400 */ \ + WPP_DEFINE_BIT(DBG_QUEUEING) /* bit 11 = 0x00000800 */ \ + WPP_DEFINE_BIT(DBG_HW_ACCESS) /* bit 12 = 0x00001000 */ \ + /* You can have up to 32 defines. If you want more than that,\ + you have to provide another trace control GUID */\ + ) + + +#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) WPP_LEVEL_LOGGER(flags) +#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) + + diff --git a/general/PLX9x5x/test/plx.cpp b/general/PLX9x5x/test/plx.cpp new file mode 100644 index 00000000..c5bae510 --- /dev/null +++ b/general/PLX9x5x/test/plx.cpp @@ -0,0 +1,1104 @@ +/*++ + +Copyright (c) Microsoft Corporation + +Module Name: + + plx.cpp + +Abstract: + + This module implements the PLX class which tests the DMA of PLX devices. + + Example usage: + plx.exe /wr /wb=100 # write-then-read once with a buffer of 100 bytes + plx.exe /thread # repeat write-then-read for default 5000 millisecs. + plx.exe /thread /time=1000 # repeat write-then-read for 1000 millisecs. + + NOTE: The /quite option will suppress most non-error messages. + NOTE: The options and parameters are case sensitive. + +Environment: + + User Mode Win2k or Later + +--*/ + +#define INITGUID + +#include "plx.hpp" + +// +// Define the spin count to be used for critical sections. The value +// specified below is arbitrary. Change it based on your requirements. +// +#define SPIN_COUNT_FOR_CS 0x4000 + +int g_TimeUp =0; + +DWORD WINAPI +ReadThreadProc( + LPVOID lpParameter + ) +{ + ULONG bytes; + PTHREAD_CONTEXT Context = (PTHREAD_CONTEXT)lpParameter; + + while(!g_TimeUp) { + + if(ReadFile(Context->hDevice, + Context->Buffer, + Context->BufferSize, + &bytes, + NULL)) { + + if (!Context->quite) { + printf("Read sucessful.\n"); + } + + } else { + + printf("Read failed.\n"); + ExitProcess(1); + } + } + ExitThread(0); +} + +DWORD WINAPI +WriteThreadProc( + LPVOID lpParameter + ) +{ + ULONG bytes; + PTHREAD_CONTEXT Context = (PTHREAD_CONTEXT)lpParameter; + + while(!g_TimeUp) { + + if(WriteFile(Context->hDevice, + Context->Buffer, + Context->BufferSize, + &bytes, + NULL)) { + + if (!Context->quite) { + printf("Write sucessful.\n"); + } + + } else { + + printf("Write failed.\n"); + ExitProcess(1); + } + } + ExitThread(0); +} + +int __cdecl +main( + _In_ int argc, + _In_reads_(argc) char* argv[] + ) +{ + PLX Plx; + BOOL status = TRUE; + ULONG test = MENU_TEST; + + if (!Plx.Initialize()) { + printf("Failied to Initialize Test class.\n"); + printf("exit(%u)\n", Plx.Status); + exit(Plx.Status); + } + + if(argc > 1) { + + for(int i=1; (i < argc) && status; i++) { + + char delims[] = "-/="; + char delims2[] = "="; + char *command; + char *data; + char *state = NULL; + + data = NULL; + + #pragma prefast(suppress:6385, "i < argc-1 before it is incremented below"); + command = strtok_s(argv[i], delims, &state); + if(command == NULL) { + status = FALSE; + break; + } + + if(strcmp(command, "rb") == 0) { + + data = strtok_s(NULL, delims2, &state); + + if (!data && i < argc-1) { + data = argv[++i]; + } + + ULONG size = atol(data); + if (size > 0) { + Plx.SetReadBufferSize(size); + } else { + status = FALSE; + } + + } else if(strcmp(command, "wb") == 0) { + + ULONG size = 0; + data = strtok_s(NULL, delims2, &state); + + if (!data && i < argc-1) { + data = argv[++i]; + } + if (data) { + size = atol(data); + } + + if (size > 0) { + Plx.SetWriteBufferSize(size); + } else { + status = FALSE; + } + + } else if (strcmp(command, "bs") == 0) { + data = strtok_s(NULL, delims2, &state); + + if (!data && i < argc-1) { + data = argv[++i]; + } + + ULONG size = atol(data); + if(size > 0) { + Plx.SetWriteBufferSize(size); + } else { + status = FALSE; + } + + } else if(strcmp(command, "wt") == 0) { + + test = WRITE_TEST; + + } else if(strcmp(command, "rt") == 0) { + + test = READ_TEST; + + } else if(strcmp(command, "quite") == 0) { + + Plx.Quite = TRUE; + + } else if(strcmp(command, "thread") == 0) { + + test = THREAD_TEST; + + } else if (strcmp(command, "time") == 0) { + + data = strtok_s(NULL, delims, &state); + + if(!data && i < argc-1) { + #pragma prefast(suppress:6385, "i < argc-1 before it is incremented"); + data = argv[++i]; + } + ULONG size = (NULL != data) ? atol(data) : 0; + Plx.SetThreadLifeTime(size); + + } else { + status = FALSE; + } + + if (!Plx.Quite) { + if (data) { + printf("Arg %d: Command: %s Parameter: %s\n", i, command, data); + } else { + printf("Arg %d: Command: %s\n", i, command); + } + } + } + } + + if (status) { + + switch (test) { + case READ_TEST: + Plx.ReadTest(); + break; + + case WRITE_TEST: + Plx.WriteTest(); + break; + + case READ_WRITE_TEST: + Plx.ReadWriteTest(); + break; + + case THREAD_TEST: + Plx.ThreadedReadWriteTest(); + break; + + case MENU_TEST: + default: + Plx.Menu(); + } + + } else { + + printf("Invalid command line parameter.\n"); + Plx.Status = 1; + } + + printf("exit(%u)\n", Plx.Status); + + exit( Plx.Status ); +} + +PLX::PLX() +{ + ReadBuffer = WriteBuffer = NULL; + hDevInfo = pDeviceInterfaceDetail = NULL; + hDevice = INVALID_HANDLE_VALUE; + console = TRUE; + Contexts = NULL; + Threads = NULL; + ProcessorCount = 0; + CSInitialized = FALSE; + ThreadTimer = 5000; // 5000 milliseconds (5 seconds) + + Quite = FALSE; + Status = 0; +} + +PLX::~PLX() +{ + + if (CSInitialized) { + DeleteCriticalSection(&CriticalSection); + } + if (hDevInfo) { + SetupDiDestroyDeviceInfoList(hDevInfo); + } + + if (pDeviceInterfaceDetail) { + free(pDeviceInterfaceDetail); + } + + if (Contexts) { + _Analysis_assume_(ProcessorCount <= ThreadCount); + for(int i = 0; i < ProcessorCount; i++) { + if (Contexts[i].Buffer) { + delete Contexts[i].Buffer; + } + } + + delete Contexts; + Contexts = NULL; + } + + if (hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + hDevice = INVALID_HANDLE_VALUE; + } +} + +BOOL +PLX::Initialize() +{ + BOOL retValue = TRUE; + + retValue = SetBufferSizes(DEFAULT_READ_BUFFER_SIZE); + if (!retValue) { + return retValue; + } + + retValue = GetDevicePath(); + if (!retValue) { + return retValue; + } + + if (!CSInitialized) { + retValue = InitializeCriticalSectionAndSpinCount(&CriticalSection, SPIN_COUNT_FOR_CS); + if (!retValue) { + printf("InitializeCritialSection failed.\n"); + Status = GetLastError(); + return retValue; + } + CSInitialized = TRUE; + } + + return retValue; +} + +void +PLX::Menu() +{ + int menu = -1; + + while(menu != 0 && pDeviceInterfaceDetail) { + printf("\n" + " 1- Read from device\n" + " 2- Write from device\n" + " 3- Read/Write from device\n" + " 4- Read/Write Thread Test\n" + " 5- Select new device\n" + " 6- Change Buffer Size\n" + " 7- Compare Read/Write Buffers\n" + " 8- Display Read/Write Buffers\n" + " 9- Change Thread Lifetime\n" + "10- Command Line Options\n" + " 0- Quit\n"); + + if (scanf_s("%d", &menu) == 0) { + break; + } + + switch(menu) { + case READ_TEST: // 1 + ReadTest(); + break; + + case WRITE_TEST: // 2 + WriteTest(); + break; + + case READ_WRITE_TEST: // 3 + ReadWriteTest(); + break; + + case THREAD_TEST: // 4 + ThreadedReadWriteTest(); + break; + + case DEVICE_PATH: // 5 + GetDevicePath(); + break; + + case SET_SIZE: // 6 + ULONG size; + printf("\nEnter new buffer size: "); + if (scanf_s("%u", &size) != 0) { + SetBufferSizes(size); + } + break; + + case COMPARE_BUFFERS: // 7 + CompareReadWriteBuffers(); + break; + + case DISPLAY_BUFFERS: // 8 + DisplayReadWriteBuffers(); + break; + + case THREAD_TIME: // 9 + printf("\nEnter new Thread Lifetime (ms): "); + if (scanf_s("%u", &ThreadTimer) == 0) { + break; + } + break; + + case COMMAND_LINE: // 10 + printf("Command Line Options\n" + " Set Read Buffer Size: '/rb=xx'\n" + " Set Write Buffer Size: '/wb=xx'\n" + " Set Both Buffer Sizes: '/bs=xx'\n" + " Perform Write Test: '/wt'\n" + " Perform Read Test: '/rt'\n" + " Perform Read/Write Test: '/wr'\n" + " Perform Read/Write Thread Test: '/thread'\n"); + break; + + default: + break; + } + } +} + +BOOL +PLX::ThreadedReadWriteTest() +{ + BOOL status = TRUE; + DWORD_PTR pAffinity, sAffinity; + int i; + + HANDLE hThread; + HANDLE hProcess = GetCurrentProcess(); + + GetProcessAffinityMask(hProcess, &pAffinity, &sAffinity); + ProcessorCount = 0; + + while(pAffinity) { + ProcessorCount++; + pAffinity = pAffinity >> 1; + } + + if (ProcessorCount == 1) { + ThreadCount = DEFAULT_THREAD_COUNT; + } else { + ThreadCount = ProcessorCount; + } + + Contexts = new THREAD_CONTEXT[ThreadCount]; + Threads = new HANDLE[ThreadCount]; + + if (Contexts == NULL || Threads == NULL) { + return FALSE; + } + + if (hDevice == INVALID_HANDLE_VALUE) { + status = GetDeviceHandle(); + } + + if (!Quite) { + printf("Creating %d threads...\n", ThreadCount); + } + + pAffinity = 1; + + for(i = 0; i < ThreadCount; i++) { + + if ((i % 2) == 0) { + + // + // Create Read Thread + // + Contexts[i].hDevice = hDevice; + Contexts[i].BufferSize = ReadBufferSize; + Contexts[i].Buffer = new UCHAR[ReadBufferSize]; + Contexts[i].quite = Quite; + + hThread = CreateThread(NULL, + 0, + ReadThreadProc, + &Contexts[i], + 0, + NULL); + + if (NULL == hThread) { + printf( "Failed to create thread %d\n", i ); + this->Status = 1; + break; + + } else { + Threads[i] = hThread; + } + + } else { + + // + // Create Write Thread + // + Contexts[i].hDevice = hDevice; + Contexts[i].BufferSize = WriteBufferSize; + Contexts[i].Buffer = new UCHAR[WriteBufferSize]; + Contexts[i].quite = Quite; + + hThread = CreateThread(NULL, + 0, + WriteThreadProc, + &Contexts[i], + 0, + NULL); + + if (NULL == hThread) { + printf( "Failed to create thread %d\n", i ); + this->Status = 1; + break; + + } else { + Threads[i] = hThread; + } + } + + // + // Set Affinity + // + SetThreadAffinityMask(Threads[i], pAffinity); + + pAffinity = pAffinity << 1; + } + + if (i != ThreadCount) { + + // + // some create thread failed, bail out + // + printf( "Some CreateThread was failed, stop\n" ); + g_TimeUp = 1; + + } else { + + // + // wait till either all quit or time is due + // + DWORD error; + + error = WaitForMultipleObjects(i, Threads, TRUE, ThreadTimer); + + if (error == WAIT_TIMEOUT) { + + // + // Stop the threads if time is up + // + g_TimeUp = 2; + error = WaitForMultipleObjects(i, Threads, TRUE, 100000) ; + + if (error) { + printf("WaitForMultipleObjects[%d] error %u\n", i, error); + } + } else { + if (error) { + printf("WaitForMultipleObjects[%d] error %u\n", i, error); + } + } + } + + if (Contexts) { + for (i = 0; i < ThreadCount; i++) { + if (Contexts[i].Buffer) { + delete Contexts[i].Buffer; + } + } + + delete Contexts; + Contexts = NULL; + } + + if (hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + hDevice = INVALID_HANDLE_VALUE; + } + + return status; +} + +BOOL +PLX::ReadTest() +{ + BOOL status = TRUE; + ULONG bytes = 0; + + if (!ReadBuffer) { + status = FALSE; + } + + if ((status == TRUE) && (hDevice == INVALID_HANDLE_VALUE)) { + status = GetDeviceHandle(); + } + + if (status) { + if (ReadFile(hDevice, + ReadBuffer, + ReadBufferSize, + &bytes, + NULL)){ + + EnterCriticalSection(&CriticalSection); + if (!Quite) { + printf("Read sucessful.\n"); + } + LeaveCriticalSection(&CriticalSection); + + } else { + + EnterCriticalSection(&CriticalSection); + printf("Read failed.\n"); + this->Status = 1; + LeaveCriticalSection(&CriticalSection); + } + } + + if (hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + hDevice = INVALID_HANDLE_VALUE; + } + + return status; +} + +BOOL +PLX::WriteTest() +{ + ULONG bytes = 0; + BOOL status = TRUE; + + if (!WriteBuffer) { + status = FALSE; + } + + if ((status == TRUE) && (hDevice == INVALID_HANDLE_VALUE)) { + status = GetDeviceHandle(); + } + + if (status) { + FillMemory(WriteBuffer, WriteBufferSize, 0xAB); + + if (WriteFile(hDevice, + WriteBuffer, + WriteBufferSize, + &bytes, + NULL)) { + + EnterCriticalSection(&CriticalSection); + if (!Quite) { + printf("Write sucessful.\n"); + } + LeaveCriticalSection(&CriticalSection); + + } else { + + EnterCriticalSection(&CriticalSection); + printf("Write failed.\n"); + this->Status = 1; + LeaveCriticalSection(&CriticalSection); + } + } + + if (hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + hDevice = INVALID_HANDLE_VALUE; + } + + return status; +} + +BOOL +PLX::ReadWriteTest() +{ + return (WriteTest() && ReadTest() && CompareReadWriteBuffers()); +} + +BOOL +PLX::CompareReadWriteBuffers() +{ + BOOL status = TRUE; + ULONG size; + PUCHAR WTraverse; + PUCHAR RTraverse; + + if (ReadBufferSize <= WriteBufferSize) { + size = ReadBufferSize; + } else { + size = WriteBufferSize; + } + + WTraverse = WriteBuffer; + RTraverse = ReadBuffer; + + for(ULONG i = 0; i < size; i++) { + if (*WTraverse++ != *RTraverse++) { + status = FALSE; + } + } + + if (status) { + if (!Quite) { + printf("Buffers are identical\n"); + } + } else { + printf("Buffers not identical\n"); + this->Status = 1; + } + + return status; +} + +void +PLX::DisplayReadWriteBuffers() +{ + if (!Quite) { + + PUCHAR WTraverse; + PUCHAR RTraverse; + + WTraverse = WriteBuffer; + RTraverse = ReadBuffer; + + printf("Write: "); + for(ULONG i = 0; i < WriteBufferSize; i++) { + printf("%X ", *WTraverse++); + } + + printf("\n\n\nRead: "); + for(ULONG i = 0; i < ReadBufferSize; i++) { + printf("%X ", *RTraverse++); + } + printf("\n"); + } +} + +BOOL +PLX::SetReadBufferSize(ULONG size) +{ + BOOL status = TRUE; + + if (ReadBuffer) { + free(ReadBuffer); + } + + ReadBufferSize = size; + + ReadBuffer = (PUCHAR)malloc(ReadBufferSize); + + if (!ReadBuffer) { + status = FALSE; + } + + return status; +} + +BOOL +PLX::SetWriteBufferSize(ULONG size) +{ + BOOL status = TRUE; + + if (WriteBuffer) { + free(WriteBuffer); + } + + WriteBufferSize = size; + + WriteBuffer = (PUCHAR)malloc(WriteBufferSize); + + if (!WriteBuffer) { + status = FALSE; + } + + return status; +} + +BOOL +PLX::SetBufferSizes(ULONG size) +{ + BOOL status; + + status = SetReadBufferSize(size); + if (status) { + status = SetWriteBufferSize(size); + } + + return status; +} + +BOOL +PLX::GetDevicePath() +{ + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; + SP_DEVINFO_DATA DeviceInfoData; + + ULONG size; + int count, i, index; + BOOL status = TRUE; + TCHAR *DeviceName = NULL; + TCHAR *DeviceLocation = NULL; + + // + // Retreive the device information for all PLX devices. + // + hDevInfo = SetupDiGetClassDevs(&GUID_PLX_INTERFACE, + NULL, + NULL, + DIGCF_DEVICEINTERFACE | + DIGCF_PRESENT); + + // + // Initialize the SP_DEVICE_INTERFACE_DATA Structure. + // + DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + // + // Determine how many devices are present. + // + count = 0; + while(SetupDiEnumDeviceInterfaces(hDevInfo, + NULL, + &GUID_PLX_INTERFACE, + count++, //Cycle through the available devices. + &DeviceInterfaceData) + ); + + // + // Since the last call fails when all devices have been enumerated, + // decrement the count to get the true device count. + // + count--; + + // + // If the count is zero then there are no devices present. + // + if (count == 0) { + printf("No PLX devices are present and enabled in the system.\n"); + this->Status = 1; + return FALSE; + } + + // + // Initialize the appropriate data structures in preparation for + // the SetupDi calls. + // + DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + // + // Loop through the device list to allow user to choose + // a device. If there is only one device, select it + // by default. + // + i = 0; + while (SetupDiEnumDeviceInterfaces(hDevInfo, + NULL, + (LPGUID)&GUID_PLX_INTERFACE, + i, + &DeviceInterfaceData)) { + + // + // Determine the size required for the DeviceInterfaceData + // + SetupDiGetDeviceInterfaceDetail(hDevInfo, + &DeviceInterfaceData, + NULL, + 0, + &size, + NULL); + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + printf("SetupDiGetDeviceInterfaceDetail failed, Error: %u", GetLastError()); + this->Status = 1; + return FALSE; + } + + pDeviceInterfaceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(size); + + if (!pDeviceInterfaceDetail) { + printf("Insufficient memory.\n"); + this->Status = 1; + return FALSE; + } + + // + // Initialize structure and retrieve data. + // + pDeviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + status = SetupDiGetDeviceInterfaceDetail(hDevInfo, + &DeviceInterfaceData, + pDeviceInterfaceDetail, + size, + NULL, + &DeviceInfoData); + + free(pDeviceInterfaceDetail); + + if (!status) { + printf("SetupDiGetDeviceInterfaceDetail failed, Error: %u", GetLastError()); + this->Status = 1; + return status; + } + + // + // Get the Device Name + // Calls to SetupDiGetDeviceRegistryProperty require two consecutive + // calls, first to get required buffer size and second to get + // the data. + // + SetupDiGetDeviceRegistryProperty(hDevInfo, + &DeviceInfoData, + SPDRP_DEVICEDESC, + NULL, + (PBYTE)DeviceName, + 0, + &size); + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + printf("SetupDiGetDeviceRegistryProperty failed, Error: %u", GetLastError()); + this->Status = 1; + return FALSE; + } + + DeviceName = (TCHAR*) malloc(size); + if (!DeviceName) { + printf("Insufficient memory.\n"); + this->Status = 1; + return FALSE; + } + + status = SetupDiGetDeviceRegistryProperty(hDevInfo, + &DeviceInfoData, + SPDRP_DEVICEDESC, + NULL, + (PBYTE)DeviceName, + size, + NULL); + if (!status) { + printf("SetupDiGetDeviceRegistryProperty failed, Error: %u", + GetLastError()); + free(DeviceName); + this->Status = 1; + return status; + } + + // + // Now retrieve the Device Location. + // + SetupDiGetDeviceRegistryProperty(hDevInfo, + &DeviceInfoData, + SPDRP_LOCATION_INFORMATION, + NULL, + (PBYTE)DeviceLocation, + 0, + &size); + + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + DeviceLocation = (TCHAR*) malloc(size); + + if (DeviceLocation != NULL) { + + status = SetupDiGetDeviceRegistryProperty(hDevInfo, + &DeviceInfoData, + SPDRP_LOCATION_INFORMATION, + NULL, + (PBYTE)DeviceLocation, + size, + NULL); + if (!status) { + free(DeviceLocation); + DeviceLocation = NULL; + } + } + + } else { + DeviceLocation = NULL; + } + + // + // If there is more than one device print description. + // + if (count > 1 && console) { + printf("%d- ", i); + } + + printf("%s\n", DeviceName); + + if (DeviceLocation) { + printf(" %s\n", DeviceLocation); + } + + free(DeviceName); + DeviceName = NULL; + + if (DeviceLocation) { + free(DeviceLocation); + DeviceLocation = NULL; + } + + i++; // Cycle through the available devices. + } + + // + // Select device. + // + index = 0; + if (count > 1) { + printf("\nSelect Device: "); + + if (scanf_s("%d", &index) == 0) { + return ERROR_INVALID_DATA; + } + } + + // + // Get information for specific device. + // + status = SetupDiEnumDeviceInterfaces(hDevInfo, + NULL, + (LPGUID)&GUID_PLX_INTERFACE, + index, + &DeviceInterfaceData); + + if (!status) { + printf("SetupDiEnumDeviceInterfaces failed, Error: %u", GetLastError()); + return status; + } + + // + // Determine the size required for the DeviceInterfaceData + // + SetupDiGetDeviceInterfaceDetail(hDevInfo, + &DeviceInterfaceData, + NULL, + 0, + &size, + NULL); + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + printf("SetupDiGetDeviceInterfaceDetail failed, Error: %u", GetLastError()); + this->Status = 1; + return FALSE; + } + + pDeviceInterfaceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(size); + + if (!pDeviceInterfaceDetail) { + printf("Insufficient memory.\n"); + this->Status = 1; + return FALSE; + } + + // + // Initialize structure and retrieve data. + // + pDeviceInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + status = SetupDiGetDeviceInterfaceDetail(hDevInfo, + &DeviceInterfaceData, + pDeviceInterfaceDetail, + size, + NULL, + &DeviceInfoData); + if (!status) { + printf("SetupDiGetDeviceInterfaceDetail failed, Error: %u", GetLastError()); + this->Status = 1; + return status; + } + + return status; +} + +BOOL +PLX::GetDeviceHandle() +{ + BOOL status = TRUE; + + if (pDeviceInterfaceDetail == NULL) { + status = GetDevicePath(); + } + if (pDeviceInterfaceDetail == NULL) { + status = FALSE; + } + + if (status) { + + // + // Get handle to device. + // + hDevice = CreateFile(pDeviceInterfaceDetail->DevicePath, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (hDevice == INVALID_HANDLE_VALUE) { + status = FALSE; + printf("CreateFile failed. Error:%u", GetLastError()); + this->Status = 1; + } + } + + return status; +} + +void +PLX::SetThreadLifeTime(ULONG time) +{ + ThreadTimer = time; +} + diff --git a/general/PLX9x5x/test/plx.hpp b/general/PLX9x5x/test/plx.hpp new file mode 100644 index 00000000..fe87fea1 --- /dev/null +++ b/general/PLX9x5x/test/plx.hpp @@ -0,0 +1,146 @@ +/*++ + +Copyright (c) 2003 Microsoft Corporation + +Module Name: + + plx.hpp + +Abstract: + + This module defines the PLX class and the + values neccessary for Window operation and IOCTL control. + +Environment: + + User Mode Win2k or Later + +--*/ + +#pragma once + +#include +#include + +#include +#include +#include +#include "public.h" + +// +// scanf_s is not available in the DDK build environment. +// So redefining it to use scanf +// +#if defined(DDKBUILD) + +#define scanf_s scanf + +#endif + + +#define DEFAULT_READ_BUFFER_SIZE 1024 +#define DEFAULT_WRITE_BUFFER_SIZE 1024 + +#define DEFAULT_THREAD_COUNT 2 + +typedef struct _THREAD_CONTEXT +{ + HANDLE hDevice; + BOOL quite; + ULONG BufferSize; + PUCHAR Buffer; + +} THREAD_CONTEXT, *PTHREAD_CONTEXT; + +typedef enum { + + MENU_TEST = 0, + READ_TEST = 1, + WRITE_TEST = 2, + READ_WRITE_TEST = 3, + THREAD_TEST = 4, + DEVICE_PATH = 5, + SET_SIZE = 6, + COMPARE_BUFFERS = 7, + DISPLAY_BUFFERS = 8, + THREAD_TIME = 9, + COMMAND_LINE = 10, + +} COMMAND; + +class PLX +{ + +public: + PLX(); + ~PLX(); + + BOOL + Initialize(); + + void + Menu(); + + BOOL + GetDevicePath(); + + BOOL + ReadTest(); + + BOOL + WriteTest(); + + BOOL + ReadWriteTest(); + + BOOL + CompareReadWriteBuffers(); + + BOOL + SetReadBufferSize(ULONG size); + + BOOL + SetWriteBufferSize(ULONG size); + + BOOL + SetBufferSizes(ULONG size); + + void + DisplayReadWriteBuffers(); + + BOOL + ThreadedReadWriteTest(); + + void + SetThreadLifeTime(ULONG time); + + BOOL Quite; + ULONG Status; + +private: + + BOOL + GetDeviceHandle(); + + HDEVINFO hDevInfo; + PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceInterfaceDetail; + HANDLE hDevice; + + ULONG ReadBufferSize; + ULONG WriteBufferSize; + _Field_size_bytes_(ReadBufferSize) PUCHAR ReadBuffer; + _Field_size_bytes_(WriteBufferSize) PUCHAR WriteBuffer; + + _Field_size_(ThreadCount) HANDLE *Threads; + _Field_size_(ThreadCount) PTHREAD_CONTEXT Contexts; + int ProcessorCount; + int ThreadCount; + + CRITICAL_SECTION CriticalSection; + BOOL CSInitialized; + + ULONG ThreadTimer; + + BOOL console; +}; + diff --git a/general/PLX9x5x/test/plx.vcxproj b/general/PLX9x5x/test/plx.vcxproj new file mode 100644 index 00000000..3dcbd6f3 --- /dev/null +++ b/general/PLX9x5x/test/plx.vcxproj @@ -0,0 +1,184 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {BF101CB1-B147-40AC-8F84-13AC122A2D37} + $(MSBuildProjectName) + Debug + Win32 + {F475764B-7D1A-49E4-8FA0-B4153BB184E4} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + true + + + + plx + + + plx + + + plx + + + plx + + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(PreprocessorDefinitions);BUILT_IN_DDK=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/PLX9x5x/test/plx.vcxproj.Filters b/general/PLX9x5x/test/plx.vcxproj.Filters new file mode 100644 index 00000000..b74b5937 --- /dev/null +++ b/general/PLX9x5x/test/plx.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {0B47F4CC-B263-41C6-86AF-E5D9448601A6} + + + h;hpp;hxx;hm;inl;inc;xsd + {802D6182-3D0F-4A06-9D94-F6E77BA24FFD} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {F55A15F1-EBAB-45A9-AD99-9AA00FA8DA01} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/PLX9x5x/test/test.cmd b/general/PLX9x5x/test/test.cmd new file mode 100644 index 00000000..7c744dbf --- /dev/null +++ b/general/PLX9x5x/test/test.cmd @@ -0,0 +1,2 @@ +start obj\i386\plx.exe /thread /time 10000 + diff --git a/general/SystemDma/wdm/ReadMe.md b/general/SystemDma/wdm/ReadMe.md new file mode 100644 index 00000000..6b15c480 --- /dev/null +++ b/general/SystemDma/wdm/ReadMe.md @@ -0,0 +1,14 @@ +System DMA +========== + +This sample demonstrates the usage of V3 System DMA. It shows how a driver could use a system DMA controller supported by Windows to write data to a hardware location using DMA. + +The sample consists of a legacy device driver and a Win32 console mode test application. The test application opens a handle to the device exposed by the driver and makes a DeviceIoControl call to initiate the example system DMA. To understand how the V3 system DMA calls are invoked please study SDmaWrite() in SDma.c. + +**Note**  This sample driver is not a PnP driver. This is a minimal driver meant to demonstrate an OS feature. Neither it nor its sample programs are intended for use in a production environment. Rather, they are intended for educational purposes and as a skeleton driver. + +Run the sample +-------------- + +To test this driver, copy the test app, SystemDmaApp.exe, and the driver to the same directory, and run the application. The application will automatically load the driver if it's not already loaded and interact with the driver. When you exit the app, the driver will be stopped, unloaded and removed. Because no system DMA controller exists for Windows which uses the advertised DRQ, the sample driver will not proceed any further than failing to acquire a system DMA adapter. + diff --git a/general/SystemDma/wdm/SystemDma.sln b/general/SystemDma/wdm/SystemDma.sln new file mode 100644 index 00000000..1df1c8b0 --- /dev/null +++ b/general/SystemDma/wdm/SystemDma.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{91E3EEC7-4EBF-4BE0-A947-5C7C164EF77D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{6C0D6BEB-2BA2-4769-BD10-476085814CAE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SystemDmaApp", "exe\SystemDmaApp.vcxproj", "{3B7F8119-C721-4322-894C-0D3B49A38FFB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDma", "sys\SDma.vcxproj", "{42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3B7F8119-C721-4322-894C-0D3B49A38FFB}.Debug|Win32.ActiveCfg = Debug|Win32 + {3B7F8119-C721-4322-894C-0D3B49A38FFB}.Debug|Win32.Build.0 = Debug|Win32 + {3B7F8119-C721-4322-894C-0D3B49A38FFB}.Release|Win32.ActiveCfg = Release|Win32 + {3B7F8119-C721-4322-894C-0D3B49A38FFB}.Release|Win32.Build.0 = Release|Win32 + {3B7F8119-C721-4322-894C-0D3B49A38FFB}.Debug|x64.ActiveCfg = Debug|x64 + {3B7F8119-C721-4322-894C-0D3B49A38FFB}.Debug|x64.Build.0 = Debug|x64 + {3B7F8119-C721-4322-894C-0D3B49A38FFB}.Release|x64.ActiveCfg = Release|x64 + {3B7F8119-C721-4322-894C-0D3B49A38FFB}.Release|x64.Build.0 = Release|x64 + {42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE}.Debug|Win32.ActiveCfg = Debug|Win32 + {42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE}.Debug|Win32.Build.0 = Debug|Win32 + {42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE}.Release|Win32.ActiveCfg = Release|Win32 + {42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE}.Release|Win32.Build.0 = Release|Win32 + {42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE}.Debug|x64.ActiveCfg = Debug|x64 + {42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE}.Debug|x64.Build.0 = Debug|x64 + {42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE}.Release|x64.ActiveCfg = Release|x64 + {42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3B7F8119-C721-4322-894C-0D3B49A38FFB} = {91E3EEC7-4EBF-4BE0-A947-5C7C164EF77D} + {42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE} = {6C0D6BEB-2BA2-4769-BD10-476085814CAE} + EndGlobalSection +EndGlobal diff --git a/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj b/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj new file mode 100644 index 00000000..25803eb1 --- /dev/null +++ b/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj @@ -0,0 +1,196 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {3B7F8119-C721-4322-894C-0D3B49A38FFB} + $(MSBuildProjectName) + Debug + Win32 + {91239967-1454-4D74-8730-73372B2E0C94} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + SystemDmaApp + + + SystemDmaApp + + + SystemDmaApp + + + SystemDmaApp + + + + true + Level4 + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + 0x04000000 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + 0x04000000 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + 0x04000000 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + 0x04000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj.Filters b/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj.Filters new file mode 100644 index 00000000..6d4320c9 --- /dev/null +++ b/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {83BDC4AF-E38F-41A3-AE52-10470851D503} + + + h;hpp;hxx;hm;inl;inc;xsd + {3C82C17E-76AD-4F6A-A677-A4591BB22A00} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {0D5328D1-97B0-4961-BF49-FB8CA3B73228} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/SystemDma/wdm/exe/install.c b/general/SystemDma/wdm/exe/install.c new file mode 100644 index 00000000..68497c7d --- /dev/null +++ b/general/SystemDma/wdm/exe/install.c @@ -0,0 +1,537 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + install.c + +Abstract: + + Win32 routines to dynamically load and unload a Windows NT kernel-mode + driver using the Service Control Manager APIs. + +Environment: + + User mode only + +--*/ + + +#include +#include +#include +#include +#include +#include "sdma.h" + +BOOLEAN +InstallDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceExe + ); + + +BOOLEAN +RemoveDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +StartDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +StopDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +InstallDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceExe + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + SC_HANDLE schService; + DWORD err; + + // + // NOTE: This creates an entry for a standalone driver. If this + // is modified for use with a driver that requires a Tag, + // Group, and/or Dependencies, it may be necessary to + // query the registry for existing driver information + // (in order to determine a unique Tag, etc.). + // + + // + // Create a new a service object. + // + + schService = CreateService(SchSCManager, // handle of service control manager database + DriverName, // address of name of service to start + DriverName, // address of display name + SERVICE_ALL_ACCESS, // type of access to service + SERVICE_KERNEL_DRIVER, // type of service + SERVICE_DEMAND_START, // when to start service + SERVICE_ERROR_NORMAL, // severity if service fails to start + ServiceExe, // address of name of binary file + NULL, // service does not belong to a group + NULL, // no tag requested + NULL, // no dependency names + NULL, // use LocalSystem account + NULL // no password for service account + ); + + if (schService == NULL) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_EXISTS) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + printf("CreateService failed! Error = %d \n", (int)err ); + + // + // Indicate an error. + // + + return FALSE; + } + } + + // + // Close the service object. + // + + CloseServiceHandle(schService); + + // + // Indicate success. + // + + return TRUE; + +} // InstallDriver + +BOOLEAN +ManageDriver( + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceName, + _In_ USHORT Function + ) +{ + + SC_HANDLE schSCManager; + + BOOLEAN rCode = TRUE; + + // + // Insure (somewhat) that the driver and service names are valid. + // + + if (!DriverName || !ServiceName) { + + printf("Invalid Driver or Service provided to ManageDriver() \n"); + + return FALSE; + } + + // + // Connect to the Service Control Manager and open the Services database. + // + + schSCManager = OpenSCManager(NULL, // local machine + NULL, // local database + SC_MANAGER_ALL_ACCESS // access required + ); + + if (!schSCManager) { + + printf("Open SC Manager failed! Error = %d \n", (int)GetLastError()); + + return FALSE; + } + + // + // Do the requested function. + // + + switch( Function ) { + + case DRIVER_FUNC_INSTALL: + + // + // Install the driver service. + // + + if (InstallDriver(schSCManager, + DriverName, + ServiceName + )) { + + // + // Start the driver service (i.e. start the driver). + // + + rCode = StartDriver(schSCManager, + DriverName + ); + + } else { + + // + // Indicate an error. + // + + rCode = FALSE; + } + + break; + + case DRIVER_FUNC_REMOVE: + + // + // Stop the driver. + // + + StopDriver(schSCManager, + DriverName + ); + + // + // Remove the driver service. + // + + RemoveDriver(schSCManager, + DriverName + ); + + // + // Ignore all errors. + // + + rCode = TRUE; + + break; + + default: + + printf("Unknown ManageDriver() function. \n"); + + rCode = FALSE; + + break; + } + + // + // Close handle to service control manager. + // + + CloseServiceHandle(schSCManager); + + return rCode; + +} // ManageDriver + + +BOOLEAN +RemoveDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + BOOLEAN rCode; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", (int)GetLastError()); + + // + // Indicate error. + // + + return FALSE; + } + + // + // Mark the service for deletion from the service control manager database. + // + + if (DeleteService(schService)) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + printf("DeleteService failed! Error = %d \n", (int)GetLastError()); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + CloseServiceHandle(schService); + + return rCode; + +} // RemoveDriver + + + +BOOLEAN +StartDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + DWORD err; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", (int)GetLastError()); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Start the execution of the service (i.e. start the driver). + // + + if (!StartService(schService, // service identifier + 0, // number of arguments + NULL // pointer to arguments + )) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_ALREADY_RUNNING) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + printf("StartService failure! Error = %d \n", (int)err ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + return FALSE; + } + + } + + // + // Close the service object. + // + + CloseServiceHandle(schService); + + return TRUE; + +} // StartDriver + + + +BOOLEAN +StopDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + BOOLEAN rCode = TRUE; + SC_HANDLE schService; + SERVICE_STATUS serviceStatus; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", (int)GetLastError()); + + return FALSE; + } + + // + // Request that the service stop. + // + + if (ControlService(schService, + SERVICE_CONTROL_STOP, + &serviceStatus + )) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + printf("ControlService failed! Error = %d \n", (int)GetLastError() ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + CloseServiceHandle (schService); + + return rCode; + +} // StopDriver + +BOOLEAN +SetupDriverName( + _Inout_updates_bytes_all_(BufferLength) PCHAR DriverLocation, + _In_ ULONG BufferLength + ) +{ + HANDLE fileHandle; + DWORD driverLocLen = 0; + + // + // Get the current directory. + // + + driverLocLen = GetCurrentDirectory(BufferLength, + DriverLocation + ); + + if (driverLocLen == 0 || driverLocLen < BufferLength) { + + printf("GetCurrentDirectory failed! Error = %d \n", (int)GetLastError()); + + return FALSE; + } + + DriverLocation[ driverLocLen - 1 ] = '\0'; + + // + // Setup path name to driver file. + // + if (FAILED( StringCbCat(DriverLocation, BufferLength, "\\"DRIVER_NAME".sys") )) { + return FALSE; + } + + // + // Insure driver file is in the specified directory. + // + + if ((fileHandle = CreateFile(DriverLocation, + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + )) == INVALID_HANDLE_VALUE) { + + + printf("%s.sys is not loaded.\n", DRIVER_NAME); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Close open file handle. + // + + if (fileHandle) { + + CloseHandle(fileHandle); + } + + // + // Indicate success. + // + + return TRUE; + + +} // SetupDriverName + + + diff --git a/general/SystemDma/wdm/exe/testapp.c b/general/SystemDma/wdm/exe/testapp.c new file mode 100644 index 00000000..0462f544 --- /dev/null +++ b/general/SystemDma/wdm/exe/testapp.c @@ -0,0 +1,180 @@ +/*++ + +Copyright (c) 1990-98 Microsoft Corporation All Rights Reserved + +Module Name: + + testapp.c + +Abstract: + +Environment: + + Win32 console multi-threaded application + +--*/ +#include +#include +#include +#include +#include +#include +#include "..\sys\sdma.h" + + +BOOLEAN +ManageDriver( + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceName, + _In_ USHORT Function + ); + +BOOLEAN +SetupDriverName( + _Inout_updates_bytes_all_(BufferLength) PCHAR DriverLocation, + _In_ ULONG BufferLength + ); + +char OutputBuffer[100]; +char InputBuffer[100]; + +VOID __cdecl +main( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] + ) +{ + HANDLE hDevice; + BOOL bRc; + ULONG bytesReturned; + DWORD errNum = 0; + TCHAR driverLocation[MAX_PATH]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + // + // open the device + // + + if ((hDevice = CreateFile( "\\\\.\\DmaTest", + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE) { + + errNum = GetLastError(); + + if (errNum != ERROR_FILE_NOT_FOUND) { + + printf("CreateFile failed! ERROR_FILE_NOT_FOUND = %d\n", (int)errNum); + + return ; + } + + // + // The driver is not started yet so let us the install the driver. + // First setup full path to driver name. + // + + if (!SetupDriverName(driverLocation, sizeof(driverLocation))) { + + return ; + } + + if (!ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_INSTALL + )) { + + printf("Unable to install driver. \n"); + + // + // Error - remove driver. + // + + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + return; + } + + hDevice = CreateFile( "\\\\.\\DmaTest", + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if ( hDevice == INVALID_HANDLE_VALUE ){ + printf ( "Error: CreatFile Failed : %d\n", (int)GetLastError()); + return; + } + + } + +#if 0 + // + // Printing Input & Output buffer pointers and size + // + + printf("InputBuffer Pointer = %p, BufLength = %d\n", (ULONG *)InputBuffer, + sizeof(InputBuffer)); + printf("OutputBuffer Pointer = %p BufLength = %d\n", (ULONG *)OutputBuffer, + sizeof(OutputBuffer)); +#endif + + // + // Performing METHOD_BUFFERED + // + + StringCbCopy(InputBuffer, sizeof(InputBuffer), + "This String is from User Application; using METHOD_BUFFERED"); + + printf("\nCalling DeviceIoControl METHOD_BUFFERED:\n"); + + memset(OutputBuffer, 0, sizeof(OutputBuffer)); + + bRc = DeviceIoControl ( hDevice, + (DWORD) IOCTL_SDMA_WRITE, + &InputBuffer, + (DWORD) strlen ( InputBuffer )+1, + &OutputBuffer, + sizeof( OutputBuffer), + &bytesReturned, + NULL + ); + + if ( !bRc ) + { + printf ( "Error in DeviceIoControl : %d", (int)GetLastError()); + return; + + } + printf(" OutBuffer (%d): %s\n", bytesReturned, OutputBuffer); + + CloseHandle ( hDevice ); + + // + // Unload the driver. Ignore any errors. + // + + driverLocation[ 259 ] = '\0'; + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + + // + // close the handle to the device. + // + +} + + diff --git a/general/SystemDma/wdm/sys/SDma.vcxproj b/general/SystemDma/wdm/sys/SDma.vcxproj new file mode 100644 index 00000000..fb50d847 --- /dev/null +++ b/general/SystemDma/wdm/sys/SDma.vcxproj @@ -0,0 +1,140 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {42AC88BC-11CC-4FC0-B2EF-50B6CA8DDBCE} + $(MSBuildProjectName) + Debug + Win32 + {BB0473B0-289C-4FA0-9E81-C6EA8C79707D} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + SDma + + + SDma + + + SDma + + + SDma + + + + true + Level4 + + + + + + + true + Level4 + + + + + + + true + Level4 + + + + + + + true + Level4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/SystemDma/wdm/sys/SDma.vcxproj.Filters b/general/SystemDma/wdm/sys/SDma.vcxproj.Filters new file mode 100644 index 00000000..259eadd5 --- /dev/null +++ b/general/SystemDma/wdm/sys/SDma.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {7F208255-EDD4-4E3B-83EC-EBB1682D21C8} + + + h;hpp;hxx;hm;inl;inc;xsd + {B5677F44-0B89-43F0-91FB-984AB0713883} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {82A0D909-8651-4045-8729-2AF8321AB2BB} + + + inf;inv;inx;mof;mc; + {7D13F81D-2971-47FA-9382-E4CF95208FA4} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/SystemDma/wdm/sys/sdma.c b/general/SystemDma/wdm/sys/sdma.c new file mode 100644 index 00000000..629d43d5 --- /dev/null +++ b/general/SystemDma/wdm/sys/sdma.c @@ -0,0 +1,1364 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation All Rights Reserved + +Module Name: + + sdma.c + +Abstract: + + The purpose of this driver is to demonstrate V3 system DMA. + +Environment: + + Kernel mode only. + +--*/ + + +// +// Include files. +// + +#include // various NT definitions +#include + +#include "sdma.h" + +#define NT_DEVICE_NAME L"\\Device\\SDMA" +#define DOS_DEVICE_NAME L"\\DosDevices\\DmaTest" + +#if DBG +#define SDMA_KDPRINT(_x_) \ + DbgPrint("SDMA.SYS: ");\ + DbgPrint _x_; + +#else +#define SDMA_KDPRINT(_x_) +#endif + +// +// These are the states a PDO or FDO transition upon +// receiving a specific PnP Irp. Refer to the PnP Device States +// diagram in DDK documentation for better understanding. +// + +typedef enum _DEVICE_PNP_STATE { + + NotStarted = 0, // Not started yet + Started, // Device has received the START_DEVICE IRP + StopPending, // Device has received the QUERY_STOP IRP + Stopped, // Device has received the STOP_DEVICE IRP + RemovePending, // Device has received the QUERY_REMOVE IRP + SurpriseRemovePending, // Device has received the SURPRISE_REMOVE IRP + Deleted, // Device has received the REMOVE_DEVICE IRP + UnKnown // Unknown state + +} DEVICE_PNP_STATE; + +typedef struct _COMMON_DEVICE_DATA +{ + // A back pointer to the device object for which this is the extension + + PDEVICE_OBJECT Self; + + // This flag helps distinguish between PDO and FDO + + BOOLEAN IsFDO; + + // We track the state of the device with every PnP Irp + // that affects the device through these two variables. + + DEVICE_PNP_STATE DevicePnPState; + + DEVICE_PNP_STATE PreviousPnPState; + + + ULONG DebugLevel; + + // Stores the current system power state + + SYSTEM_POWER_STATE SystemPowerState; + + // Stores current device power state + + DEVICE_POWER_STATE DevicePowerState; + + ULONG ulVariationFlags; + +} COMMON_DEVICE_DATA, *PCOMMON_DEVICE_DATA; + +// +// The device extension of the bus itself. From whence the PDO's are born. +// + +typedef struct _FDO_DEVICE_DATA +{ + COMMON_DEVICE_DATA CommonData; + + PDEVICE_OBJECT UnderlyingPDO; + + // The underlying bus PDO and the actual device object to which our + // FDO is attached + + PDEVICE_OBJECT NextLowerDriver; + + // List of PDOs created so far + + LIST_ENTRY ListOfPDOs; + + // The PDOs currently enumerated. + + ULONG NumPDOs; + + // A synchronization for access to the device extension. + + FAST_MUTEX Mutex; + + // + // The number of IRPs sent from the bus to the underlying device object + // + + ULONG OutstandingIO; // Biased to 1 + + // + // On remove device plug & play request we must wait until all outstanding + // requests have been completed before we can actually delete the device + // object. This event is when the Outstanding IO count goes to zero + // + + KEVENT RemoveEvent; + + // + // This event is set when the Outstanding IO count goes to 1. + // + + KEVENT StopEvent; + + // The name returned from IoRegisterDeviceInterface, + // which is used as a handle for IoSetDeviceInterfaceState. + + UNICODE_STRING InterfaceName; + +} FDO_DEVICE_DATA, *PFDO_DEVICE_DATA; + +// +// Define minimum and maximum macros. +// + +#define Minimum(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#define Maximum(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) + +// +// Define the structure that we'll use to communicate with our +// AllocateAdapterChannel callback. +// + +typedef struct _SDMA_CALLBACK_CONTEXT { + IO_ALLOCATION_ACTION Action; + ULONG NumberOfMapRegisters; + PVOID MapRegisterBase; + KEVENT CallBackEvent; + KEVENT CompletionEvent; +} SDMA_CALLBACK_CONTEXT, *PSDMA_CALLBACK_CONTEXT; + +// +// Device driver routine declarations. +// + +DRIVER_INITIALIZE DriverEntry; + +_Dispatch_type_(IRP_MJ_CREATE) +_Dispatch_type_(IRP_MJ_CLOSE) +DRIVER_DISPATCH SDmaCreateClose; + +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) +DRIVER_DISPATCH SDmaDeviceControl; + +DRIVER_UNLOAD SDmaUnloadDriver; + +VOID +PrintIrpInfo( + PIRP Irp + ); +VOID +PrintChars( + _In_reads_(CountChars) PCHAR BufferAddress, + _In_ size_t CountChars + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, DriverEntry ) +#pragma alloc_text( PAGE, SDmaCreateClose) +#pragma alloc_text( PAGE, SDmaDeviceControl) +#pragma alloc_text( PAGE, SDmaUnloadDriver) +#pragma alloc_text( PAGE, PrintIrpInfo) +#pragma alloc_text( PAGE, PrintChars) +#endif // ALLOC_PRAGMA + + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + This routine is called by the Operating System to initialize the driver. + + It creates the device object, fills in the dispatch entry points and + completes the initialization. + +Arguments: + DriverObject - a pointer to the object that represents this device + driver. + + RegistryPath - a pointer to our Services key in the registry. + +Return Value: + STATUS_SUCCESS if initialized; an error otherwise. + +--*/ + +{ + NTSTATUS ntStatus; + UNICODE_STRING ntUnicodeString; // NT Device Name "\Device\SDMA" + UNICODE_STRING ntWin32NameString; // Win32 Name "\DosDevices\DmaTest" + PDEVICE_OBJECT deviceObject = NULL; // ptr to device object + + UNREFERENCED_PARAMETER(RegistryPath); + + RtlInitUnicodeString( &ntUnicodeString, NT_DEVICE_NAME ); + + ntStatus = IoCreateDevice( + DriverObject, // Our Driver Object + 0, // We don't use a device extension + &ntUnicodeString, // Device name "\Device\SDMA" + FILE_DEVICE_UNKNOWN, // Device type + FILE_DEVICE_SECURE_OPEN, // Device characteristics + FALSE, // Not an exclusive device + &deviceObject ); // Returned ptr to Device Object + + if ( !NT_SUCCESS( ntStatus ) ) + { + SDMA_KDPRINT(("Couldn't create the device object\n")); + return ntStatus; + } + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = SDmaCreateClose; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = SDmaCreateClose; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SDmaDeviceControl; + DriverObject->DriverUnload = SDmaUnloadDriver; + + // + // Initialize a Unicode String containing the Win32 name + // for our device. + // + + RtlInitUnicodeString( &ntWin32NameString, DOS_DEVICE_NAME ); + + // + // Create a symbolic link between our device name and the Win32 name + // + + ntStatus = IoCreateSymbolicLink( + &ntWin32NameString, &ntUnicodeString ); + + if ( !NT_SUCCESS( ntStatus ) ) + { + // + // Delete everything that this routine has allocated. + // + SDMA_KDPRINT(("Couldn't create symbolic link\n")); + IoDeleteDevice( deviceObject ); + } + + + return ntStatus; +} + + +NTSTATUS +SDmaCreateClose( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + This routine is called by the I/O system when the driver is opened or + closed. + + No action is performed other than completing the request successfully. + +Arguments: + + DeviceObject - a pointer to the object that represents the device + that I/O is to be done on. + + Irp - a pointer to the I/O Request Packet for this request. + +Return Value: + + NT status code + +--*/ + +{ + UNREFERENCED_PARAMETER(DeviceObject); + + PAGED_CODE(); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + return STATUS_SUCCESS; +} + +VOID +SDmaUnloadDriver( + _In_ PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine is called by the I/O system to unload the driver. + + Any resources previously allocated must be freed. + +Arguments: + + DriverObject - a pointer to the object that represents our driver. + +Return Value: + + None +--*/ + +{ + PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; + UNICODE_STRING uniWin32NameString; + + PAGED_CODE(); + + // + // Create counted string version of our Win32 device name. + // + + RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME ); + + + // + // Delete the link from our device name to a name in the Win32 namespace. + // + + IoDeleteSymbolicLink( &uniWin32NameString ); + + if ( deviceObject != NULL ) + { + IoDeleteDevice( deviceObject ); + } + + + +} + +// +// These helper routines allow MDL creation and destruction +// as well as providing simple DMA callback routines for +// channel allocation and DMA completion. +// + +ULONG +MDL_SPAN( + _In_ PMDL Mdl, + _In_ PVOID CurrentVa, + ULONG Length + ) + +/*++ + +Routine Description: + + This function determines the number of pages spanned by the given MDL. + We have to account for the case where the MDL is actually the head of + a chain. + +Arguments: + + Mdl - Supplies the MDL being checked. + + CurrentVa - Provides our starting address within the first MDL. + + Length - Supplies the total number of bytes from the given MDL chain that + are associated with this transfer. + +Return Value: + + This function returns the number of pages spanned by the specified + transfer. + +--*/ + +{ + PVOID VirtualAddress; + ULONG Span; + ULONG RemainingLength; + ULONG ChunkLength; + ULONG MdlOffset; + + if (Length == 0) + { + return 0; + } + + if (Mdl->Next == NULL) + { + return ADDRESS_AND_SIZE_TO_SPAN_PAGES(CurrentVa, Length); + } + + Span = 0; + RemainingLength = Length; + VirtualAddress = CurrentVa; + + MdlOffset = (ULONG) (((ULONG_PTR) CurrentVa) + - ((ULONG_PTR) MmGetMdlVirtualAddress(Mdl))); + + while (Mdl != NULL) + { + ChunkLength = Minimum(MmGetMdlByteCount(Mdl) - MdlOffset, + RemainingLength); + + Span += ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, ChunkLength); + + Mdl = Mdl->Next; + MdlOffset = 0; + RemainingLength -= ChunkLength; + + if (Mdl != NULL) + { + VirtualAddress = MmGetMdlVirtualAddress(Mdl); + } + + } + + return Span; +} + +VOID +SDmaFreeMdl( + _In_opt_ PMDL MdlHead + ) + +/*++ + +Routine Description: + + This function frees an MDL that was allocated earlier with a call to + DmaUnitAllocateMdl. + +Arguments: + + MdlHead - Supplies the MDL to free. This could be the head of a chain + of MDLs. + +Return Value: + + None. + +--*/ + +{ + PMDL Mdl; + PMDL NextMdl; + + Mdl = MdlHead; + + while (Mdl != NULL) + { + if ((Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) != 0) + { + MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl); + } + + MmFreePagesFromMdl(Mdl); + + NextMdl = Mdl->Next; + ExFreePool(Mdl); + + Mdl = NextMdl; + } + + return; +} + +VOID SDmaFillMdl( + _In_ PMDL Mdl, + _In_ PVOID CurrentVa, + _In_ ULONG Length, + _In_ UCHAR Fill + ) + +/*++ + +Routine Description: + + This function fills an MDL with a data byte. + +Arguments: + + Mdl - Supplies the MDL that describes the system pages associated with + this transfer. + + CurrentVa - Provides the starting address of the transfer within + the given MDL. + + Length - Supplies the length of the transfer in bytes. + + Fill - The byte to write into the MDL. + +Return Value: + + None + +--*/ + +{ + ULONG MdlOffset; + ULONG PageOffset; + ULONG ChunkLength; + PCHAR VirtualAddress; + ULONG RemainingLength; + + RemainingLength = Length; + while (Mdl != NULL) + { + + MdlOffset = (ULONG) (((ULONG_PTR) CurrentVa) + - ((ULONG_PTR) MmGetMdlVirtualAddress(Mdl))); + + ASSERT((Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) != 0); + + PageOffset = BYTE_OFFSET(CurrentVa); + VirtualAddress = PAGE_ALIGN(((PCHAR) Mdl->MappedSystemVa) + MdlOffset); + + while ((MdlOffset != MmGetMdlByteCount(Mdl)) + && (RemainingLength != 0)) + { + + ChunkLength = Minimum(MmGetMdlByteCount(Mdl) - MdlOffset, + RemainingLength); + + ChunkLength = Minimum(ChunkLength, PAGE_SIZE - PageOffset); + + ASSERT((PageOffset + ChunkLength) <= PAGE_SIZE); + + RtlFillMemory(((PCHAR) VirtualAddress) + PageOffset, ChunkLength, + Fill); + + PageOffset = 0; + RemainingLength -= ChunkLength; + MdlOffset += ChunkLength; + VirtualAddress = VirtualAddress + ChunkLength; + } + + Mdl = Mdl->Next; + } +} + +VOID SDmaPrintMdl( + _In_ PMDL Mdl, + _In_ PVOID CurrentVa, + _In_ ULONG Length, + _In_ PCHAR TitleStr + ) + +/*++ + +Routine Description: + + This function prints MDL information to the debugger. + +Arguments: + + Mdl - Supplies the MDL to be displayed. + + CurrentVa - Provides the starting address of the transfer within + the given MDL. + + Length - Supplies the length of the transfer in bytes. + + TitleStr - A text tile to print describing this MDL. + +Return Value: + + None + +--*/ + +{ + ULONG MdlOffset; + PCHAR VirtualAddress; + ULONG MdlCount = 0; + ULONG TotalLength = 0; + PPFN_NUMBER PageFrame; + ULONG NumPages = 0; + ULONG PageCount = 0; + PMDL MdlHead; + PHYSICAL_ADDRESS PageAddress; + ULONG RemainingLength; + ULONG ChunkLength; + + MdlOffset = (ULONG) (((ULONG_PTR) CurrentVa) + - ((ULONG_PTR) MmGetMdlVirtualAddress(Mdl))); + + MdlHead = Mdl; + NumPages = MDL_SPAN(MdlHead, CurrentVa, Length); + while (Mdl != NULL) + { + + TotalLength += Minimum(MmGetMdlByteCount(Mdl) - MdlOffset, + Length); + + Mdl = Mdl->Next; + MdlCount++; + } + + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "########### %s ########\n", + TitleStr == NULL ? "MDL" : TitleStr ); + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "Mdl Head = 0x%p\n", MdlHead); + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "Mdl Count = %d\n", MdlCount); + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "Total Byte Count = %d\n", TotalLength); + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "Total Physical Pages = %d\n", NumPages); + + Mdl = MdlHead; + MdlCount = 0; + RemainingLength = Length; + while (Mdl != NULL) + { + + MdlOffset = (ULONG) (((ULONG_PTR) CurrentVa) + - ((ULONG_PTR) MmGetMdlVirtualAddress(Mdl))); + + ASSERT((Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) != 0); + + VirtualAddress = PAGE_ALIGN(((PCHAR) Mdl->MappedSystemVa) + MdlOffset); + ChunkLength = Minimum(MmGetMdlByteCount(Mdl) - MdlOffset, + RemainingLength); + + NumPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, ChunkLength); + + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "########################################\n"); + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "Mdl #%d: 0x%p\n", MdlCount, Mdl); + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, " Mdl Offset = %d\n", MdlOffset); + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, " Mdl Length = %d\n", Mdl->ByteCount); + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, " Mdl Base VA = 0x%p\n", VirtualAddress); + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "------------ PHYSICAL PAGES ------------\n"); + PageFrame = MmGetMdlPfnArray( Mdl ); + + for (PageCount = 0; PageCount < NumPages; PageCount++) + { + PageAddress.QuadPart = (ULONGLONG)(*(PageFrame + PageCount)) << PAGE_SHIFT; + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, " Page #%d: 0x%I64x\n", PageCount, PageAddress.QuadPart); + } + + Mdl = Mdl->Next; + RemainingLength -= ChunkLength; + MdlCount++; + } + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "########################################\n"); +} + +PMDL +SDmaAllocateMdl( + _Out_ PVOID *CurrentVa, + _Out_ PULONG Length, + _In_ BOOLEAN ForceContiguous + ) + +/*++ + +Routine Description: + + This function allocates a single MDL. + +Arguments: + + CurrentVa - Supplies the location where we should store the starting + virtual address of the transfer within the allocated MDL. + + Length - Supplies the location where we should store the length of + the requested transfer. + +Return Value: + + This function returns a pointer to a single MDL. + +--*/ + +{ + PMDL MdlHead; + PMDL Mdl; + ULONG Span; + ULONG RequestBytes; + PHYSICAL_ADDRESS LowAddress; + PHYSICAL_ADDRESS HighAddress; + PHYSICAL_ADDRESS SkipAddress; + PVOID MappingAddress; + ULONG TransferLength; + ULONG Offset; + + *Length = 0; + *CurrentVa = NULL; + LowAddress.QuadPart = 0; + HighAddress.QuadPart = 0xffffffffffffffffI64; + SkipAddress.QuadPart = 0; + + // + // Add one MDL. + // + + MdlHead = NULL; + TransferLength = 0; + + { + Span = 2; + RequestBytes = Span << PAGE_SHIFT; + + if (ForceContiguous) + { + Mdl = MmAllocatePagesForMdlEx(LowAddress, + HighAddress, + SkipAddress, + RequestBytes, + MmCached, + MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS + ); + } + else + { + Mdl = MmAllocatePagesForMdl(LowAddress, + HighAddress, + SkipAddress, + RequestBytes + ); + } + + if (Mdl == NULL) + { + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "MmAllocatePagesForMdl failed. \n" ); + goto Cleanup; + } + + if (MmGetMdlByteCount(Mdl) < RequestBytes) + { + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "MmAllocatePagesForMdl allocated less than requested bytes. \n" ); + goto Cleanup; + } + + // + // Set the byte count and byte offset appropriately for this MDL. + // Note that this is safe since we allocated enough memory to cover + // the entire page span up above. + // + + Mdl->ByteCount = RequestBytes; + Mdl->ByteOffset = 0; + + MappingAddress = MmMapLockedPagesSpecifyCache( + Mdl, + KernelMode, + MmCached, + NULL, + FALSE, + HighPagePriority); + + if (MappingAddress == NULL) + { + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "MmMapLockedPagesSpecifyCache failed. \n" ); + goto Cleanup; + } + + if (MdlHead == NULL) + { + MdlHead = Mdl; + } + + TransferLength += RequestBytes; + } + + Offset = 0; + + *CurrentVa = + (PVOID) (((ULONG_PTR) MmGetMdlVirtualAddress(MdlHead)) + Offset); + + *Length = TransferLength - Offset; + + return MdlHead; + + // + // We'll branch to this point when we need to free the active MDL and + // the MDL that we've built up to this point. + // + +Cleanup: + + if (Mdl != NULL) + { + MmFreePagesFromMdl(Mdl); + ExFreePool(Mdl); + } + + if (MdlHead != NULL) + { + SDmaFreeMdl(MdlHead); + } + + return NULL; +} + +_Function_class_(DRIVER_CONTROL) +IO_ALLOCATION_ACTION +SDmaAdapterControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID MapRegisterBase, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This is the AdapterControl routine that we'll use to complete calls + to AllocateAdapterChannelEx. + +Arguments: + + DeviceObject - Supplies the FDO associated with this AllocateAdapterChannelEx + call. + + Irp - Unused. + + MapRegisterBase - Supplies the handle to our allocated map registers. + + Context - Supplies the callback context that was passed to + AllocateAdapterChannelEx. In our case this will be a + context structure. + +Return Value: + + This function returns the Action parameter from the given callback + context structure. + +Environment: + + DISPATCH_LEVEL. + +--*/ + +{ + IO_ALLOCATION_ACTION Action; + PSDMA_CALLBACK_CONTEXT CallbackContext; + + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(DeviceObject); + + CallbackContext = (PSDMA_CALLBACK_CONTEXT) Context; + + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "AllocateAdapterChannelEx called back." + " MapRegisterBase = 0x%IX\n", (ULONG_PTR)MapRegisterBase + ); + + // + // Make a note of the allocated map register base and return the handle + // associated with this map register allocation to the caller. + // + CallbackContext->MapRegisterBase = MapRegisterBase; + + Action = CallbackContext->Action; + + KeSetEvent(&(CallbackContext->CallBackEvent), IO_NO_INCREMENT, FALSE); + + return Action; +} + +_Function_class_(DMA_COMPLETION_ROUTINE) +VOID +SDmaCompletion( + IN PDMA_ADAPTER DmaAdapter, + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context, + IN DMA_COMPLETION_STATUS Status + ) + +/*++ + +Routine Description: + + This is the DMA completion routine that we'll use to complete calls + to MapTransferEx. + +Arguments: + + DeviceObject - Supplies the FDO associated with this MapTransferEx + call. + + Context - Supplies the callback context that was passed to + MapTransferEx. In our case this will be a + context structure. + +Return Value: + + None. + +Environment: + + DISPATCH_LEVEL. + +--*/ + +{ + PSDMA_CALLBACK_CONTEXT CallbackContext; + + UNREFERENCED_PARAMETER(DmaAdapter); + UNREFERENCED_PARAMETER(DeviceObject); + + CallbackContext = (PSDMA_CALLBACK_CONTEXT) Context; + + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "MapTransferEx called back. Status = %d\n", + Status + ); + + KeSetEvent(&(CallbackContext->CompletionEvent), IO_NO_INCREMENT, FALSE); +} + +NTSTATUS +SDmaWrite( + _In_ PFDO_DEVICE_DATA pFdoData + ) + +/*++ + +Routine Description: + + This function initiates simple system DMA against a + non-existent DMA controller. The code is provided + only for example purposes and is not intended as product + code. + +Arguments: + + pFdoData - Supplies the FDO extension + +Return Value: + + This function returns a NTSTATUS value. + +--*/ + +{ + KIRQL Irql; + ULONG DmaRequestLine = 0x1000; + NTSTATUS Status = STATUS_SUCCESS; + PDMA_ADAPTER pWriteAdapter = NULL; + ULONG AllocatableMapRegisters = 0, RequestedMapRegisters = 2; + DEVICE_DESCRIPTION Description; + SDMA_CALLBACK_CONTEXT WriteContext; + CHAR WriteDmaTransferContext[ DMA_TRANSFER_CONTEXT_SIZE_V1 ]; + PMDL Mdl0 = NULL; + PVOID CurrentVa = NULL; + ULONG Length = 0; + + // + // Allocate a system DMA adapter to write data to a device + // (for instance the Tx register of a UART) using system DMA. + // + + ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + + RtlZeroMemory(&Description, sizeof(Description)); + Description.Version = DEVICE_DESCRIPTION_VERSION3; + Description.DmaAddressWidth = 32; + Description.DmaRequestLine = DmaRequestLine; + Description.DmaChannel = DmaRequestLine; + Description.InterfaceType = ACPIBus; + Description.Master = FALSE; + Description.ScatterGather = TRUE; + Description.MaximumLength = RequestedMapRegisters << PAGE_SHIFT; + + pWriteAdapter = IoGetDmaAdapter(pFdoData->UnderlyingPDO, + &Description, + &AllocatableMapRegisters); + + // + // This is the expected return path on all systems, since no + // system DMA controller supports request line 0x1000. + // + if ( pWriteAdapter == NULL ) + { + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, "IoGetDmaAdapter failed for request line %d. \n", Description.DmaRequestLine ); + Status = STATUS_NO_MATCH; + goto Exit; + } + + RtlZeroMemory( &WriteContext, sizeof( WriteContext ) ); + WriteContext.Action = KeepObject; + WriteContext.NumberOfMapRegisters = AllocatableMapRegisters; + KeInitializeEvent(&(WriteContext.CallBackEvent), NotificationEvent, FALSE); + KeInitializeEvent(&(WriteContext.CompletionEvent), NotificationEvent, FALSE); + + pWriteAdapter->DmaOperations->InitializeDmaTransferContext( + pWriteAdapter, + (PVOID)&(WriteDmaTransferContext)); + + DbgPrintEx(DPFLTR_IHVBUS_ID, 0, + "%p WRITE V3 System DmaAdapter created: 32 bit, Scatter-Gather capable, %d MapRegs (%d requested)\n", + pWriteAdapter, AllocatableMapRegisters, RequestedMapRegisters + ); + + // + // Call AllocateAdapterChannelEx for the write adapter. + // + KeRaiseIrql(DISPATCH_LEVEL, &Irql); + + Status = pWriteAdapter->DmaOperations->AllocateAdapterChannelEx( + pWriteAdapter, + pFdoData->CommonData.Self, + &(WriteDmaTransferContext), + AllocatableMapRegisters, + 0, + SDmaAdapterControl, + (PVOID) &WriteContext, + NULL); + KeLowerIrql(Irql); + + if (Status != STATUS_SUCCESS) + { + goto Exit; + } + + // + // Wait for the channel to be allocated to our driver. A real driver would + // not block this IRP to wait for AllocateAdapterChannelEx to call + // back the AdapterControl routine. Instead all the steps below (including + // the MapTransferEx) would be staged from with the AdapterControl + // routine. + // + + KeWaitForSingleObject(&(WriteContext.CallBackEvent), Executive, KernelMode, FALSE, NULL); + + // + // We have acquired a channel for Write adapter successfully. + // + + // + // Build a fake MDL to represent user data. + // Mdl0 is the address for the Write channel, and is the + // data (likely from usermode in real world scenarios) that + // will be written to the device address (this address may + // be the Tx register of a UART device for instance). + // + + Mdl0 = SDmaAllocateMdl(&CurrentVa, &Length, FALSE); + + // + // Fill the MDL with 0xDA bytes. + // + + SDmaPrintMdl( Mdl0, CurrentVa, Length, "Mdl0" ); + SDmaFillMdl( Mdl0, CurrentVa, Length, 0xDA ); + + // + // Signal the DMA engine to write the entire length of the + // MDL using V3 system DMA. + // We just use a 0 offset since our CurrentVa starts at the + // beginning of the MDL. + // + + pWriteAdapter->DmaOperations->MapTransferEx( + pWriteAdapter, + Mdl0, + WriteContext.MapRegisterBase, + 0, + 0x70006200, // Physical address of hardware + // device + // (e.g. UART Tx register) + &Length, + TRUE, + NULL, + 0, + SDmaCompletion, + &WriteContext); + + // + // Wait for DMA to complete on the channel. A real driver + // would not block this IRP to wait but would instead handle the + // FlushAdapterBuffersEx/FreeAdapterChannel steps during the DMA + // completion routine. + // + + KeWaitForSingleObject(&(WriteContext.CompletionEvent), Executive, KernelMode, FALSE, NULL); + + // + // Copy the data in the map buffers (if any) back to the MDL. + // + + pWriteAdapter->DmaOperations->FlushAdapterBuffersEx( + pWriteAdapter, + Mdl0, + WriteContext.MapRegisterBase, + 0, + Length, + TRUE); + + // + // Now we can free the MDL associated with this transfer. + // + + SDmaFreeMdl(Mdl0); + + // + // Return the channel back to the HAL. If this action is + // not taken, then nothing else in the OS will be able to + // use this channel again until it is freed. This includes + // our own driver handling another IOCTL. + // + + KeRaiseIrql(DISPATCH_LEVEL, &Irql); + pWriteAdapter->DmaOperations->FreeAdapterChannel( pWriteAdapter ); + KeLowerIrql(Irql); + +Exit: + + // + // Free the DMA adapter. Typically a driver will not Get and Put + // a DMA adapter for each DMA transaction, but instead will perform + // these actions once upon device driver construction and destruction. + // + + ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); + + if (pWriteAdapter != NULL) + { + pWriteAdapter->DmaOperations->PutDmaAdapter(pWriteAdapter); + } + + // + // Send our return status. + // + + return Status; +} + +NTSTATUS +SDmaDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to perform a device I/O + control function. + +Arguments: + + DeviceObject - a pointer to the object that represents the device + that I/O is to be done on. + + Irp - a pointer to the I/O Request Packet for this request. + +Return Value: + + NT status code + +--*/ + +{ + PIO_STACK_LOCATION irpSp;// Pointer to current stack location + NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success + ULONG inBufLength; // Input buffer length + ULONG outBufLength; // Output buffer length + PCHAR inBuf; // pointer to Input buffer + PCHAR data = "This String is from Device Driver !!!"; + size_t datalen = strlen(data)+1;//Length of data including null + PFDO_DEVICE_DATA fdoData; + + PAGED_CODE(); + + fdoData = (PFDO_DEVICE_DATA) DeviceObject->DeviceExtension; + irpSp = IoGetCurrentIrpStackLocation( Irp ); + inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength; + outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; + + if (!inBufLength || !outBufLength) + { + ntStatus = STATUS_INVALID_PARAMETER; + goto End; + } + + // + // Determine which I/O control code was specified. + // + + switch ( irpSp->Parameters.DeviceIoControl.IoControlCode ) + { + case IOCTL_SDMA_WRITE: + + // + // In this method the I/O manager allocates a buffer large enough to + // to accommodate larger of the user input buffer and output buffer, + // assigns the address to Irp->AssociatedIrp.SystemBuffer, and + // copies the content of the user input buffer into this SystemBuffer + // + + SDMA_KDPRINT(("Called IOCTL_SDMA_WRITE\n")); + PrintIrpInfo(Irp); + + // + // Input buffer and output buffer is same in this case, read the + // content of the buffer before writing to it + // + + inBuf = Irp->AssociatedIrp.SystemBuffer; +#if 0 + outBuf = Irp->AssociatedIrp.SystemBuffer; +#endif + + // + // Read the data from the buffer + // + + SDMA_KDPRINT(("\tData from User :")); + // + // We are using the following function to print characters instead + // of DebugPrint with %s format because the string we get may or + // may not be null terminated. + // + PrintChars(inBuf, inBufLength); + + // + // Call the example V3 system DMA routine. The routine + // is expected to fail because no system DMA controller + // will match the one requested within the routine. + // + + SDmaWrite( fdoData ); + +#if 0 + // + // Write to the buffer over-writes the input buffer content + // + + RtlCopyBytes(outBuf, data, outBufLength); + + SDMA_KDPRINT(("\tData to User : ")); + PrintChars(outBuf, datalen ); +#endif + + // + // Assign the length of the data copied to IoStatus.Information + // of the Irp and complete the Irp. + // + + Irp->IoStatus.Information = (outBufLengthParameters.DeviceIoControl.IoControlCode)); + break; + } + +End: + // + // Finish the I/O operation by simply completing the packet and returning + // the same status as in the packet itself. + // + + Irp->IoStatus.Status = ntStatus; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + return ntStatus; +} + +VOID +PrintIrpInfo( + PIRP Irp) +{ + PIO_STACK_LOCATION irpSp; + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + PAGED_CODE(); + + SDMA_KDPRINT(("\tIrp->AssociatedIrp.SystemBuffer = 0x%p\n", + Irp->AssociatedIrp.SystemBuffer)); + SDMA_KDPRINT(("\tIrp->UserBuffer = 0x%p\n", Irp->UserBuffer)); + SDMA_KDPRINT(("\tirpSp->Parameters.DeviceIoControl.Type3InputBuffer = 0x%p\n", + irpSp->Parameters.DeviceIoControl.Type3InputBuffer)); + SDMA_KDPRINT(("\tirpSp->Parameters.DeviceIoControl.InputBufferLength = %d\n", + irpSp->Parameters.DeviceIoControl.InputBufferLength)); + SDMA_KDPRINT(("\tirpSp->Parameters.DeviceIoControl.OutputBufferLength = %d\n", + irpSp->Parameters.DeviceIoControl.OutputBufferLength )); + + UNREFERENCED_PARAMETER(irpSp); + return; +} + +VOID +PrintChars( + _In_reads_(CountChars) PCHAR BufferAddress, + _In_ size_t CountChars + ) +{ + PAGED_CODE(); + + if (CountChars) { + + while (CountChars--) { + + if (*BufferAddress > 31 + && *BufferAddress != 127) { + + KdPrint (( "%c", *BufferAddress) ); + + } else { + + KdPrint(( ".") ); + + } + BufferAddress++; + } + KdPrint (("\n")); + } + return; +} + + diff --git a/general/SystemDma/wdm/sys/sdma.h b/general/SystemDma/wdm/sys/sdma.h new file mode 100644 index 00000000..d59b4077 --- /dev/null +++ b/general/SystemDma/wdm/sys/sdma.h @@ -0,0 +1,38 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + SDMA.H + +Abstract: + + + Defines the IOCTL codes that will be used by this driver. The IOCTL code + contains a command identifier, plus other information about the device, + the type of access with which the file must have been opened, + and the type of buffering. + +Environment: + + Kernel mode only. + +--*/ + +// +// Device type -- in the "User Defined" range." +// +#define SDMA_TYPE 40000 +// +// The IOCTL function codes from 0x800 to 0xFFF are for customer use. +// + +#define IOCTL_SDMA_WRITE \ + CTL_CODE( SDMA_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS ) + +#define DRIVER_FUNC_INSTALL 0x01 +#define DRIVER_FUNC_REMOVE 0x02 + +#define DRIVER_NAME "SDMA" + diff --git a/general/SystemDma/wdm/sys/sdma.rc b/general/SystemDma/wdm/sys/sdma.rc new file mode 100644 index 00000000..42cb81b9 --- /dev/null +++ b/general/SystemDma/wdm/sys/sdma.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Sample V3 System DMA Driver" +#define VER_INTERNALNAME_STR "SDMA.sys" + +#include "common.ver" diff --git a/general/cancel/ReadMe.md b/general/cancel/ReadMe.md new file mode 100644 index 00000000..5baab9b9 --- /dev/null +++ b/general/cancel/ReadMe.md @@ -0,0 +1,27 @@ +Cancel-Safe IRP Queue Sample +============================ + +This sample demonstrates the use of the cancel-safe queue routines [**IoCsqInitialize**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549054), [**IoCsqInsertIrp**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549066), [**IoCsqRemoveIrp**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549070), [**IoCsqRemoveNextIrp**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549072). These routines were introduced in Windows XP for queuing IRPs in the driver's internal device queue. By using these routines, driver developers do not have to worry about IRP cancellation race conditions. A common problem with cancellation of IRPs in a driver is synchronization between the cancel lock or the InterlockedExchange in the I/O Manager with the driver's queue lock. The **IoCsq*Xxx*** routines abstract the cancel logic while allowing the driver to implement the queue and associated synchronization. + +The sample is accompanied by a simple multithreaded Win32 console application to stress-test the driver's cancel and cleanup routines. + +This driver is written for an hypothetical data-acquisition device that requires polling at a regular interval. The device has some settling period between two successive reads. On a user request, the driver reads data and records the time. When the next read request comes in, the driver checks the interval to see if it's reading the device too soon. If so, it pends the IRP and sleeps for a while, and then tries again. On arrival, IRPs are queued in a cancel-safe queue and a semaphore is signaled. A polling thread that waits indefinitely on the semaphore wakes up to the signal and processes queued IRPs sequentially. + +The building and installation instructions given here apply to Windows 2000 and later versions of Windows. + +This sample driver is not a Plug and Play driver. This is a minimal driver meant to demonstrate a feature of the operating system. Neither this driver nor its sample programs are intended for use in a production environment. Instead, they are intended for educational purposes and as a skeleton driver. + +Look in the Startio directory for another version of the sample driver that shows how to use cancel-safe IRP queues to implement I/O queuing functionality similar to the [**IoStartPacket**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff550370) and [**IoStartNextPacket**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff550358) routines. The same test application works with this driver as well. + +For more information, see [Cancel-Safe IRP Queues](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540755). + + +Run the sample +-------------- + +To test this driver, run Testapp.exe, which is a simple Win32 multithreaded console application. The driver will automatically load and start. When you exit the application, the driver will stop and be removed. + +`Usage: testapp ` + +**Note**  The `NumberOfThreads` command-line parameter is limited to a maximum of 10 threads; the default value if no parameter is specified is 1. The main thread waits for user input. If you press Q, the application exits gracefully; otherwise, it exits the process abruptly and forces all the threads to be terminated and all pending I/O operations to be canceled. Other threads perform I/O asynchronously in a loop. After every overlapped read, the thread goes into an alertable sleep and wakes as soon as the completion routine runs, which occurs when the driver completes the read IRP. You should run multiple instances of the application to stress test the driver. + diff --git a/general/cancel/cancel.sln b/general/cancel/cancel.sln new file mode 100644 index 00000000..a9836742 --- /dev/null +++ b/general/cancel/cancel.sln @@ -0,0 +1,59 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{DBD7C2B7-C00F-4FFE-84D4-B05886013873}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{0B6A483D-D5F5-4F95-96E7-281FD7EA600F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Startio", "Startio", "{234DCAD4-F351-4964-9C3B-0B59C944B70D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancel", "sys\cancel.vcxproj", "{3363DCA3-7873-4ECB-BA98-F243C2E8FDA0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "canclapp", "exe\canclapp.vcxproj", "{C8925B47-FB65-4E3E-89E4-2B45E3C10509}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancel", "startio\cancel.vcxproj", "{1392C861-BA6F-4423-8C33-A8C771BAF473}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3363DCA3-7873-4ECB-BA98-F243C2E8FDA0}.Debug|Win32.ActiveCfg = Debug|Win32 + {3363DCA3-7873-4ECB-BA98-F243C2E8FDA0}.Debug|Win32.Build.0 = Debug|Win32 + {3363DCA3-7873-4ECB-BA98-F243C2E8FDA0}.Release|Win32.ActiveCfg = Release|Win32 + {3363DCA3-7873-4ECB-BA98-F243C2E8FDA0}.Release|Win32.Build.0 = Release|Win32 + {3363DCA3-7873-4ECB-BA98-F243C2E8FDA0}.Debug|x64.ActiveCfg = Debug|x64 + {3363DCA3-7873-4ECB-BA98-F243C2E8FDA0}.Debug|x64.Build.0 = Debug|x64 + {3363DCA3-7873-4ECB-BA98-F243C2E8FDA0}.Release|x64.ActiveCfg = Release|x64 + {3363DCA3-7873-4ECB-BA98-F243C2E8FDA0}.Release|x64.Build.0 = Release|x64 + {C8925B47-FB65-4E3E-89E4-2B45E3C10509}.Debug|Win32.ActiveCfg = Debug|Win32 + {C8925B47-FB65-4E3E-89E4-2B45E3C10509}.Debug|Win32.Build.0 = Debug|Win32 + {C8925B47-FB65-4E3E-89E4-2B45E3C10509}.Release|Win32.ActiveCfg = Release|Win32 + {C8925B47-FB65-4E3E-89E4-2B45E3C10509}.Release|Win32.Build.0 = Release|Win32 + {C8925B47-FB65-4E3E-89E4-2B45E3C10509}.Debug|x64.ActiveCfg = Debug|x64 + {C8925B47-FB65-4E3E-89E4-2B45E3C10509}.Debug|x64.Build.0 = Debug|x64 + {C8925B47-FB65-4E3E-89E4-2B45E3C10509}.Release|x64.ActiveCfg = Release|x64 + {C8925B47-FB65-4E3E-89E4-2B45E3C10509}.Release|x64.Build.0 = Release|x64 + {1392C861-BA6F-4423-8C33-A8C771BAF473}.Debug|Win32.ActiveCfg = Debug|Win32 + {1392C861-BA6F-4423-8C33-A8C771BAF473}.Debug|Win32.Build.0 = Debug|Win32 + {1392C861-BA6F-4423-8C33-A8C771BAF473}.Release|Win32.ActiveCfg = Release|Win32 + {1392C861-BA6F-4423-8C33-A8C771BAF473}.Release|Win32.Build.0 = Release|Win32 + {1392C861-BA6F-4423-8C33-A8C771BAF473}.Debug|x64.ActiveCfg = Debug|x64 + {1392C861-BA6F-4423-8C33-A8C771BAF473}.Debug|x64.Build.0 = Debug|x64 + {1392C861-BA6F-4423-8C33-A8C771BAF473}.Release|x64.ActiveCfg = Release|x64 + {1392C861-BA6F-4423-8C33-A8C771BAF473}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3363DCA3-7873-4ECB-BA98-F243C2E8FDA0} = {DBD7C2B7-C00F-4FFE-84D4-B05886013873} + {C8925B47-FB65-4E3E-89E4-2B45E3C10509} = {0B6A483D-D5F5-4F95-96E7-281FD7EA600F} + {1392C861-BA6F-4423-8C33-A8C771BAF473} = {234DCAD4-F351-4964-9C3B-0B59C944B70D} + EndGlobalSection +EndGlobal diff --git a/general/cancel/exe/canclapp.vcxproj b/general/cancel/exe/canclapp.vcxproj new file mode 100644 index 00000000..63333b9c --- /dev/null +++ b/general/cancel/exe/canclapp.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {C8925B47-FB65-4E3E-89E4-2B45E3C10509} + $(MSBuildProjectName) + Debug + Win32 + {1EB28B67-703E-42CB-AEEF-4871909ACBE4} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + canclapp + + + canclapp + + + canclapp + + + canclapp + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + MultiThreaded + MultiThreadedDebug + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + 0x0400000 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + MultiThreaded + MultiThreadedDebug + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + 0x0400000 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + MultiThreaded + MultiThreadedDebug + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + 0x0400000 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + MultiThreaded + MultiThreadedDebug + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + 0x0400000 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/cancel/exe/canclapp.vcxproj.Filters b/general/cancel/exe/canclapp.vcxproj.Filters new file mode 100644 index 00000000..fa02af34 --- /dev/null +++ b/general/cancel/exe/canclapp.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {8E80D4A1-56AB-4705-840F-3971E3C35CC4} + + + h;hpp;hxx;hm;inl;inc;xsd + {F05A42DC-3C1C-4B0B-BB8A-980D8BDD8C12} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {DB77EDC2-6072-41D9-B58F-42B9EECC5702} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/cancel/exe/install.c b/general/cancel/exe/install.c new file mode 100644 index 00000000..f19ff558 --- /dev/null +++ b/general/cancel/exe/install.c @@ -0,0 +1,480 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + install.c + +Abstract: + + Win32 routines to dynamically load and unload a Windows NT kernel-mode + driver using the Service Control Manager APIs. + +Environment: + + User mode only + +--*/ + + +#include +#include +#include +#include + +#include "testapp.h" + + +BOOLEAN +InstallDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceExe + ); + + +BOOLEAN +RemoveDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +StartDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +StopDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +InstallDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceExe + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + SC_HANDLE schService; + DWORD err; + + // + // NOTE: This creates an entry for a standalone driver. If this + // is modified for use with a driver that requires a Tag, + // Group, and/or Dependencies, it may be necessary to + // query the registry for existing driver information + // (in order to determine a unique Tag, etc.). + // + + // + // Create a new a service object. + // + + schService = CreateService(SchSCManager, // handle of service control manager database + DriverName, // address of name of service to start + DriverName, // address of display name + SERVICE_ALL_ACCESS, // type of access to service + SERVICE_KERNEL_DRIVER, // type of service + SERVICE_DEMAND_START, // when to start service + SERVICE_ERROR_NORMAL, // severity if service fails to start + ServiceExe, // address of name of binary file + NULL, // service does not belong to a group + NULL, // no tag requested + NULL, // no dependency names + NULL, // use LocalSystem account + NULL // no password for service account + ); + + if (schService == NULL) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_EXISTS) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + printf("CreateService failed! Error = %d \n", err ); + + // + // Indicate an error. + // + + return FALSE; + } + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + // + // Indicate success. + // + + return TRUE; + +} // InstallDriver + +BOOLEAN +ManageDriver( + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceName, + _In_ USHORT Function + ) +{ + + SC_HANDLE schSCManager; + + BOOLEAN rCode = TRUE; + + // + // Insure (somewhat) that the driver and service names are valid. + // + + if (!DriverName || !ServiceName) { + + printf("Invalid Driver or Service provided to ManageDriver() \n"); + + return FALSE; + } + + // + // Connect to the Service Control Manager and open the Services database. + // + + schSCManager = OpenSCManager(NULL, // local machine + NULL, // local database + SC_MANAGER_ALL_ACCESS // access required + ); + + if (!schSCManager) { + + printf("Open SC Manager failed! Error = %d \n", GetLastError()); + + return FALSE; + } + + // + // Do the requested function. + // + + switch( Function ) { + + case DRIVER_FUNC_INSTALL: + + // + // Install the driver service. + // + + if (InstallDriver(schSCManager, + DriverName, + ServiceName + )) { + + // + // Start the driver service (i.e. start the driver). + // + + rCode = StartDriver(schSCManager, + DriverName + ); + + } else { + + // + // Indicate an error. + // + + rCode = FALSE; + } + + break; + + case DRIVER_FUNC_REMOVE: + + // + // Stop the driver. + // + + StopDriver(schSCManager, + DriverName + ); + + // + // Remove the driver service. + // + + RemoveDriver(schSCManager, + DriverName + ); + + // + // Ignore all errors. + // + + rCode = TRUE; + + break; + + default: + + printf("Unknown ManageDriver() function. \n"); + + rCode = FALSE; + + break; + } + + // + // Close handle to service control manager. + // + + if (schSCManager) { + + CloseServiceHandle(schSCManager); + } + + return rCode; + +} // ManageDriver + + +BOOLEAN +RemoveDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + BOOLEAN rCode; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", GetLastError()); + + // + // Indicate error. + // + + return FALSE; + } + + // + // Mark the service for deletion from the service control manager database. + // + + if (DeleteService(schService)) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + printf("DeleteService failed! Error = %d \n", GetLastError()); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + return rCode; + +} // RemoveDriver + + + +BOOLEAN +StartDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + DWORD err; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", GetLastError()); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Start the execution of the service (i.e. start the driver). + // + + if (!StartService(schService, // service identifier + 0, // number of arguments + NULL // pointer to arguments + )) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_ALREADY_RUNNING) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + printf("StartService failure! Error = %d \n", err ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + return FALSE; + } + + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + return TRUE; + +} // StartDriver + + + +BOOLEAN +StopDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + BOOLEAN rCode = TRUE; + SC_HANDLE schService; + SERVICE_STATUS serviceStatus; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", GetLastError()); + + return FALSE; + } + + // + // Request that the service stop. + // + + if (ControlService(schService, + SERVICE_CONTROL_STOP, + &serviceStatus + )) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + printf("ControlService failed! Error = %d \n", GetLastError() ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle (schService); + } + + return rCode; + +} // StopDriver + + + + diff --git a/general/cancel/exe/testapp.c b/general/cancel/exe/testapp.c new file mode 100644 index 00000000..f6c27fe9 --- /dev/null +++ b/general/cancel/exe/testapp.c @@ -0,0 +1,333 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + testapp.c + +Abstract: + +Environment: + + User mode Win32 console application + +--*/ + +// +// Annotation to indicate to prefast that this is nondriver user-mode code. +// + +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include +#include +#include +#include +#include +#include + +#include "testapp.h" + +// +// Globals +// + +HANDLE hDevice; +BOOLEAN ExitFlag = FALSE; +HANDLE hThreads[MAXTHREADS]; + +// +// function prototypes +// + +VOID CALLBACK CompletionRoutine( + DWORD errorcode, + DWORD bytesTransfered, + LPOVERLAPPED ov + ); + +DWORD +WINAPI Reader( + PVOID + ); + +BOOLEAN +SetupDriverName( + _Inout_updates_all_(BufferLength) PCHAR DriverLocation, + _In_ ULONG BufferLength + ); + +// +// Main function +// + +VOID __cdecl +main( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] + ) +{ + ULONG i, Id; + ULONG NumberOfThreads = 1; + DWORD errNum = 0; + TCHAR driverLocation[MAX_PATH] = {'\0'}; + + + if (argc >= 2 && (argv[1][0] == '-' || isalpha((unsigned char)argv[1][0]))) + { + puts("Usage:testapp \n"); + return; + } + else if (argc >= 2 && ((NumberOfThreads = atoi(argv[1])) > MAXTHREADS)) + { + printf("Invalid option:Only a maximun of %d threads allowed.\n", + MAXTHREADS); + return; + + } + + // + // Try to connect to driver. If this fails, try to load the driver + // dynamically. + // + + if ((hDevice = CreateFile("\\\\.\\CancelSamp", + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL + )) == INVALID_HANDLE_VALUE) { + + errNum = GetLastError(); + + if (errNum != ERROR_FILE_NOT_FOUND) { + + printf("CreateFile failed! Error = %d\n", errNum); + + return ; + } + + // + // Setup full path to driver name. + // + + if (!SetupDriverName(driverLocation, sizeof(driverLocation))) { + + return ; + } + + + // + // Install driver. + // + + if (!ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_INSTALL + )) { + + printf("Unable to install driver. \n"); + + // + // Error - remove driver. + // + + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + return; + } + // + // Try to open the newly installed driver. + // + + hDevice = CreateFile( "\\\\.\\CancelSamp", + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + if ( hDevice == INVALID_HANDLE_VALUE ){ + printf ( "Error: CreatFile Failed : %d\n", GetLastError()); + return; + } + } + + printf("Number of threads : %d\n", NumberOfThreads); + + + printf("Enter 'q' to exit gracefully:"); + + for(i=0; i < NumberOfThreads; i++) + { + hThreads[i] = CreateThread( NULL, // security attributes + 0, // initial stack size + Reader, // Main() function + NULL, // arg to Reader thread + 0, // creation flags + (LPDWORD)&Id); // returned thread id + + if ( NULL == hThreads[i] ) { + printf( " Error CreateThread[%d] Failed: %d\n", i, GetLastError()); + ExitProcess ( 1 ); + } + + } + + + if (getchar() == 'q') + { + ExitFlag = TRUE; + + WaitForMultipleObjects( NumberOfThreads, hThreads, TRUE, INFINITE); + + for(i=0; i < NumberOfThreads; i++) + CloseHandle(hThreads[i]); + + } + + CloseHandle(hDevice); + + // + // Unload the driver. Ignore any errors. + // + + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + ExitProcess(1); + +} + + +DWORD WINAPI Reader(PVOID dummy ) +{ + ULONG data; + OVERLAPPED ov; + + UNREFERENCED_PARAMETER(dummy); + + while(!ExitFlag) + { + ZeroMemory( &ov, sizeof(ov) ); + ov.Offset = 0; + ov.OffsetHigh = 0; + + if (!ReadFileEx(hDevice, (PVOID)&data, sizeof(ULONG), &ov, CompletionRoutine)) + { + printf ( "Error: Read Failed: %d\n", GetLastError()); + ExitProcess ( 1 ); + } + SleepEx(INFINITE, TRUE); + } + + printf("Exiting thread %d \n", GetCurrentThreadId()); + ExitThread(0); +} + + +VOID CALLBACK CompletionRoutine( + DWORD errorcode, + DWORD bytesTransfered, + LPOVERLAPPED ov + ) +{ + + UNREFERENCED_PARAMETER(errorcode); + UNREFERENCED_PARAMETER(ov); + + fprintf(stdout, "Thread %d read: %d bytes\n", + GetCurrentThreadId(), bytesTransfered); + return; +} + + +BOOLEAN +SetupDriverName( + _Inout_updates_all_(BufferLength) PCHAR DriverLocation, + _In_ ULONG BufferLength + ) +{ + HANDLE fileHandle; + DWORD driverLocLen = 0; + + // + // Get the current directory. + // + + driverLocLen = GetCurrentDirectory(BufferLength, + DriverLocation + ); + + if (driverLocLen == 0) { + + printf("GetCurrentDirectory failed! Error = %d \n", GetLastError()); + + return FALSE; + } + + // + // Setup path name to driver file. + // + if (FAILED( StringCbCat(DriverLocation, BufferLength, "\\"DRIVER_NAME".sys") )) { + return FALSE; + } + + // + // Insure driver file is in the specified directory. + // + + if ((fileHandle = CreateFile(DriverLocation, + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + )) == INVALID_HANDLE_VALUE) { + + + printf("%s.sys is not loaded.\n", DRIVER_NAME); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Close open file handle. + // + + if (fileHandle) { + + CloseHandle(fileHandle); + } + + // + // Indicate success. + // + + return TRUE; + + +} // SetupDriverName + + + diff --git a/general/cancel/exe/testapp.h b/general/cancel/exe/testapp.h new file mode 100644 index 00000000..fa63d0b9 --- /dev/null +++ b/general/cancel/exe/testapp.h @@ -0,0 +1,14 @@ + +#define DRIVER_FUNC_INSTALL 0x01 +#define DRIVER_FUNC_REMOVE 0x02 + +#define MAXTHREADS 10 +#define DRIVER_NAME "cancel" + +BOOLEAN +ManageDriver( + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceName, + _In_ USHORT Function + ); + diff --git a/general/cancel/startio/cancel.c b/general/cancel/startio/cancel.c new file mode 100644 index 00000000..808a8941 --- /dev/null +++ b/general/cancel/startio/cancel.c @@ -0,0 +1,965 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + + +Module Name: + + cancel.c + +Abstract: Demonstrates the use of new Cancel-Safe queue + APIs to perform queuing of IRPs without worrying about + any synchronization issues between cancel lock in the I/O + manager and the driver's queue lock. + + This driver is written for an hypothetical data acquisition + device that requires polling at a regular interval. + The device has some settling period between two reads. + Upon user request the driver reads data and records the time. + When the next read request comes in, it checks the interval + to see if it's reading the device too soon. If so, it pends + the IRP and sleeps for while and tries again. + +Environment: + + Kernel mode + +--*/ + +#include "cancel.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, CsampCreateClose) +#pragma alloc_text(PAGE, CsampUnload) +#pragma alloc_text(PAGE, CsampRead) +#endif // ALLOC_PRAGMA + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + registryPath - pointer to a unicode string representing the path, + to driver-specific key in the registry. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING unicodeDeviceName; + UNICODE_STRING unicodeDosDeviceName; + PDEVICE_OBJECT deviceObject; + PDEVICE_EXTENSION devExtension; + UNICODE_STRING sddlString; + + UNREFERENCED_PARAMETER (RegistryPath); + + CSAMP_KDPRINT(("DriverEntry Enter \n")); + + + (void) RtlInitUnicodeString(&unicodeDeviceName, CSAMP_DEVICE_NAME_U); + + // + // We will create a secure deviceobject so that only processes running + // in admin and local system account can access the device. Refer + // "Security Descriptor String Format" section in the platform + // SDK documentation to understand the format of the sddl string. + // We need to do because this is a legacy driver and there is no INF + // involved in installing the driver. For PNP drivers, security descriptor + // is typically specified for the FDO in the INF file. + // + + (void) RtlInitUnicodeString(&sddlString, L"D:P(A;;GA;;;SY)(A;;GA;;;BA)"); + + status = IoCreateDeviceSecure( + DriverObject, + sizeof(DEVICE_EXTENSION), + &unicodeDeviceName, + FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, + (BOOLEAN) FALSE, + &sddlString, + (LPCGUID)&GUID_DEVCLASS_CANCEL_SAMPLE, + &deviceObject + ); + + + if (!NT_SUCCESS(status)) + { + return status; + } + + // + // Allocate and initialize a Unicode String containing the Win32 name + // for our device. + // + + (void)RtlInitUnicodeString(&unicodeDosDeviceName, CSAMP_DOS_DEVICE_NAME_U); + + + status = IoCreateSymbolicLink( + (PUNICODE_STRING) &unicodeDosDeviceName, + (PUNICODE_STRING) &unicodeDeviceName + ); + + if (!NT_SUCCESS(status)) + { + IoDeleteDevice(deviceObject); + return status; + } + + devExtension = deviceObject->DeviceExtension; + + DriverObject->MajorFunction[IRP_MJ_CREATE]= + DriverObject->MajorFunction[IRP_MJ_CLOSE] = CsampCreateClose; + DriverObject->MajorFunction[IRP_MJ_READ] = CsampRead; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = CsampCleanup; + + DriverObject->DriverUnload = CsampUnload; + + // + // Set the flag signifying that we will do buffered I/O. This causes NT + // to allocate a buffer on a ReadFile operation which will then be copied + // back to the calling application by the I/O subsystem + // + + deviceObject->Flags |= DO_BUFFERED_IO; + + // + // Initialize the spinlock. This is used to serailize + // access to the device. + // + + KeInitializeSpinLock(&devExtension->DeviceLock); + + // + // This is used to serailize access to the queue. + // + + KeInitializeSpinLock(&devExtension->QueueLock); + + + // + //Initialize the Dpc object + // + + KeInitializeDpc(&devExtension->PollingDpc, + CsampPollingTimerDpc, + (PVOID)deviceObject); + + // + // Initialize the timer object + // + + KeInitializeTimer(&devExtension->PollingTimer); + + // + // Initialize the pending Irp devicequeue + // + + InitializeListHead(&devExtension->PendingIrpQueue); + + // + // 10 is multiplied because system time is specified in 100ns units + // + + devExtension->PollingInterval.QuadPart = Int32x32To64( + CSAMP_RETRY_INTERVAL, -10); + // + // Note down system time + // + + KeQuerySystemTime (&devExtension->LastPollTime); + + IoCsqInitializeEx(&devExtension->CancelSafeQueue, + CsampInsertIrp, + CsampRemoveIrp, + CsampPeekNextIrp, + CsampAcquireLock, + CsampReleaseLock, + CsampCompleteCanceledIrp); + + CSAMP_KDPRINT(("DriverEntry Exit = %x\n", status)); + + ASSERT(NT_SUCCESS(status)); + + return status; +} + + +_Use_decl_annotations_ +NTSTATUS +CsampCreateClose( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + Process the Create and close IRPs sent to this device. + +Arguments: + + DeviceObject - pointer to a device object. + + Irp - pointer to an I/O Request Packet. + +Return Value: + + NT Status code + +--*/ +{ + PIO_STACK_LOCATION irpStack; + NTSTATUS status = STATUS_SUCCESS; + PFILE_CONTEXT fileContext; + + UNREFERENCED_PARAMETER(DeviceObject); + + PAGED_CODE (); + + CSAMP_KDPRINT(("CsampCreateClose Enter\n")); + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + ASSERT(irpStack->FileObject != NULL); + + switch(irpStack->MajorFunction) + { + case IRP_MJ_CREATE: + + // + // The dispatch routine for IRP_MJ_CREATE is called when a + // file object associated with the device is created. + // This is typically because of a call to CreateFile() in + // a user-mode program or because a higher-level driver is + // layering itself over a lower-level driver. A driver is + // required to supply a dispatch routine for IRP_MJ_CREATE. + // + + fileContext = ExAllocatePoolWithQuotaTag(NonPagedPool, + sizeof(FILE_CONTEXT), + TAG); + + if (NULL == fileContext) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + IoInitializeRemoveLock(&fileContext->FileRundownLock, TAG, 0, 0); + + // + // Make sure nobody is using the FsContext scratch area. + // + ASSERT(irpStack->FileObject->FsContext == NULL); + + // + // Store the context in the FileObject's scratch area. + // + irpStack->FileObject->FsContext = (PVOID) fileContext; + + CSAMP_KDPRINT(("IRP_MJ_CREATE\n")); + break; + + case IRP_MJ_CLOSE: + + // + // The IRP_MJ_CLOSE dispatch routine is called when a file object + // opened on the driver is being removed from the system; that is, + // all file object handles have been closed and the reference count + // of the file object is down to 0. Certain types of drivers do not + // need to handle IRP_MJ_CLOSE, mainly drivers of devices that must + // be available for the system to continue running. In general, this + // is the place that a driver should "undo" whatever has been done + // in the routine for IRP_MJ_CREATE. + // + + fileContext = irpStack->FileObject->FsContext; + + ExFreePoolWithTag(fileContext, TAG); + + CSAMP_KDPRINT(("IRP_MJ_CLOSE\n")); + break; + + default: + CSAMP_KDPRINT((" Invalid CreateClose Parameter\n")); + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Save Status for return and complete Irp + // + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + CSAMP_KDPRINT((" CsampCreateClose Exit = %x\n", status)); + + return status; +} + +_Use_decl_annotations_ +NTSTATUS +CsampRead( + PDEVICE_OBJECT DeviceObject, + PIRP Irp +) + /*++ + Routine Description: + + Read disptach routine + + Arguments: + + DeviceObject - pointer to a device object. + Irp - pointer to current Irp + + Return Value: + + NT status code. +--*/ +{ + NTSTATUS status; + PDEVICE_EXTENSION devExtension; + PIO_STACK_LOCATION irpStack; + LARGE_INTEGER currentTime; + PVOID readBuffer; + PFILE_CONTEXT fileContext; + BOOLEAN inCriticalRegion; + + PAGED_CODE(); + + CSAMP_KDPRINT(("--->CsampReadReport irp 0x%p\n", Irp)); + + // + // Get a pointer to the device extension. + // + devExtension = DeviceObject->DeviceExtension; + inCriticalRegion = FALSE; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + ASSERT(irpStack->FileObject != NULL); + + fileContext = irpStack->FileObject->FsContext; + + status = IoAcquireRemoveLock(&fileContext->FileRundownLock, Irp); + if (!NT_SUCCESS(status)) { + // + // Lock is in a removed state. That means we have already received + // cleaned up request for this handle. + // + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return status; + } + + // + // First make sure there is enough room. + // + if (irpStack->Parameters.Read.Length < sizeof(INPUT_DATA)) + { + Irp->IoStatus.Status = status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = 0; + IoReleaseRemoveLock(&fileContext->FileRundownLock, Irp); + IoCompleteRequest (Irp, IO_NO_INCREMENT); + return status; + } + + // + // Simple little random polling time generator. + // FOR TESTING: + // Initialize the data to mod 2 of some random number. + // With this value you can control the number of times the + // Irp will be queued before completion. Check + // CsampPollDevice routine to know how this works. + // + + KeQuerySystemTime(¤tTime); + + readBuffer = Irp->AssociatedIrp.SystemBuffer; + + *((PULONG)readBuffer) = ((currentTime.LowPart/13)%2); + + // + // If the thread is suspended right after the queue is marked busy due to + // insert, it will prevent I/Os from other threads being processed leading + // to denial of service (DOS) attack. So disable thread suspension by + // entering critical region. + // + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + KeEnterCriticalRegion(); + inCriticalRegion = TRUE; + + // + // Try inserting the IRP in the queue. If the device is busy, + // the IRP will get queued and the following function will + // return SUCCESS. If the device is not busy, it will set the + // IRP to DeviceExtension->CurrentIrp and return UNSUCCESSFUL. + // + if (!NT_SUCCESS(IoCsqInsertIrpEx(&devExtension->CancelSafeQueue, + Irp, NULL, NULL))) { + IoMarkIrpPending(Irp); + + CsampInitiateIo(DeviceObject); + } else { + // + // Do not touch the IRP once it has been queued because another thread + // could remove the IRP and complete it before this one gets to run. + // + // DO_NOTHING(); + } + if (inCriticalRegion == TRUE) { + KeLeaveCriticalRegion(); + } + // + // We don't hold the lock for IRP that's pending in the list because this + // lock is meant to rundown currently dispatching threads when the cleanup + // is handled. + // + IoReleaseRemoveLock(&fileContext->FileRundownLock, Irp); + + CSAMP_KDPRINT(("<---CsampReadReport\n")); + + return STATUS_PENDING; +} + +VOID +CsampInitiateIo( + _In_ PDEVICE_OBJECT DeviceObject +) + /*++ + Routine Description: + + Performs the actual I/O operations. + + Arguments: + + DeviceObject - pointer to a device object. + + Return Value: + + NT status code. + + +--*/ + +{ + NTSTATUS status; + PDEVICE_EXTENSION devExtension = DeviceObject->DeviceExtension; + PIRP irp = NULL; + + CSAMP_KDPRINT(("--> CsampInitiateIo\n")); + + irp = devExtension->CurrentIrp; + + for(;;) { + + ASSERT(irp != NULL && irp == devExtension->CurrentIrp); + + status = CsampPollDevice(DeviceObject, irp); + if (status == STATUS_PENDING) + { + // + // Oops, polling too soon. Start the timer to retry the operation. + // + KeSetTimer(&devExtension->PollingTimer, + devExtension->PollingInterval, + &devExtension->PollingDpc); + break; + } + else + { + // + // Read device is successful. Now complete the IRP and service + // the next one from the queue. + // + irp->IoStatus.Status = status; + CSAMP_KDPRINT(("completing irp :0x%p\n", irp)); + IoCompleteRequest (irp, IO_NO_INCREMENT); + + irp = IoCsqRemoveNextIrp(&devExtension->CancelSafeQueue, NULL); + + if (irp == NULL) { + break; + } + } + + } + + CSAMP_KDPRINT(("<---CsampInitiateIo\n")); + + return; +} + +_Use_decl_annotations_ +VOID +CsampPollingTimerDpc( + PKDPC Dpc, + PVOID Context, + PVOID SystemArgument1, + PVOID SystemArgument2 +) + /*++ + Routine Description: + + CustomTimerDpc routine to process Irp that are + waiting in the PendingIrpQueue + + Arguments: + + DeviceObject - pointer to DPC object + Context - pointer to device object + SystemArgument1 - undefined + SystemArgument2 - undefined + + Return Value: +--*/ +{ + PDEVICE_OBJECT deviceObject; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + CSAMP_KDPRINT(("---> CsampPollingTimerDpc\n")); + + _Analysis_assume_(Context != NULL); + + deviceObject = (PDEVICE_OBJECT)Context; + + CsampInitiateIo(deviceObject); + + CSAMP_KDPRINT(("<--- CsampPollingTimerDpc\n")); +} + +_Use_decl_annotations_ +NTSTATUS +CsampCleanup( + PDEVICE_OBJECT DeviceObject, + PIRP Irp +) +/*++ + +Routine Description: + This dispatch routine is called when the last handle (in + the whole system) to a file object is closed. In other words, the open + handle count for the file object goes to 0. A driver that holds pending + IRPs internally must implement a routine for IRP_MJ_CLEANUP. When the + routine is called, the driver should cancel all the pending IRPs that + belong to the file object identified by the IRP_MJ_CLEANUP call. In other + words, it should cancel all the IRPs that have the same file-object pointer + as the one supplied in the current I/O stack location of the IRP for the + IRP_MJ_CLEANUP call. Of course, IRPs belonging to other file objects should + not be canceled. Also, if an outstanding IRP is completed immediately, the + driver does not have to cancel it. + +Arguments: + + DeviceObject -- pointer to the device object + Irp -- pointer to the requesing Irp + +Return Value: + + STATUS_SUCCESS -- if the poll succeeded, +--*/ +{ + + PDEVICE_EXTENSION devExtension; + PIRP pendingIrp; + PIO_STACK_LOCATION irpStack; + PFILE_CONTEXT fileContext; + NTSTATUS status; + + CSAMP_KDPRINT(("--->CsampCleanupIrp\n")); + + devExtension = DeviceObject->DeviceExtension; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + ASSERT(irpStack->FileObject != NULL); + + fileContext = irpStack->FileObject->FsContext; + + // + // This acquire cannot fail because you cannot get more than one + // cleanup for the same handle. + // + status = IoAcquireRemoveLock(&fileContext->FileRundownLock, Irp); + ASSERT(NT_SUCCESS(status)); + + // + // Wait for all the threads that are currently dispatching to exit and + // prevent any threads dispatching I/O on the same handle beyond this point. + // + IoReleaseRemoveLockAndWait(&fileContext->FileRundownLock, Irp); + + pendingIrp = IoCsqRemoveNextIrp(&devExtension->CancelSafeQueue, + irpStack->FileObject); + while(pendingIrp) + { + // + // Cancel the IRP + // + pendingIrp->IoStatus.Information = 0; + pendingIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(pendingIrp, IO_NO_INCREMENT); + + pendingIrp = IoCsqRemoveNextIrp(&devExtension->CancelSafeQueue, + irpStack->FileObject); + } + + // + // Finally complete the cleanup IRP + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + CSAMP_KDPRINT(("<---CsampCleanupIrp\n")); + + return STATUS_SUCCESS; +} + +_Use_decl_annotations_ +NTSTATUS +CsampPollDevice( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + Pools for data + +Arguments: + + DeviceObject -- pointer to the device object + Irp -- pointer to the requesing Irp + + +Return Value: + + STATUS_SUCCESS -- if the poll succeeded, + STATUS_TIMEOUT -- if the poll failed (timeout), + or the checksum was incorrect + STATUS_PENDING -- if polled too soon + +--*/ +{ + PINPUT_DATA pInput; + + UNREFERENCED_PARAMETER(DeviceObject); + + pInput = (PINPUT_DATA)Irp->AssociatedIrp.SystemBuffer; + +#ifdef REAL + + RtlZeroMemory(pInput, sizeof(INPUT_DATA)); + + // + // If currenttime is less than the lasttime polled plus + // minimum time required for the device to settle + // then don't poll and return STATUS_PENDING + // + + KeQuerySystemTime(¤tTime); + if (currentTime->QuadPart < (TimeBetweenPolls + + devExtension->LastPollTime.QuadPart)) + { + return STATUS_PENDING; + } + + // + // Read/Write to the port here. + // Fill the INPUT structure + // + + // + // Note down the current time as the last polled time + // + + KeQuerySystemTime(&devExtension->LastPollTime); + + + return STATUS_SUCCESS; +#else + + // + // With this conditional statement + // you can control the number of times the + // irp should be queued before completing. + // + + if (pInput->Data-- <= 0) + { + Irp->IoStatus.Information = sizeof(INPUT_DATA); + return STATUS_SUCCESS; + } + return STATUS_PENDING; + + #endif + +} + +VOID +CsampUnload( + _In_ PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + Free all the allocated resources, etc. + +Arguments: + + DriverObject - pointer to a driver object. + +Return Value: + + VOID +--*/ +{ + PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; + UNICODE_STRING uniWin32NameString; + PDEVICE_EXTENSION devExtension = deviceObject->DeviceExtension; + + PAGED_CODE(); + + CSAMP_KDPRINT(("--->CsampUnload\n")); + + // + // The OS (XP and beyond) forces any DPCs that are already + // running to run to completion, even after the driver unload , + // routine returns, but before unmapping the driver image from + // memory. + // This driver makes an assumption that I/O request are going to + // come only from usermode app and as long as there are active + // IRPs in the driver, the driver will not get unloaded. + // NOTE: If a driver can get I/O request directly from another + // driver without having an explicit handle, you should wait on an + // event signalled by the DPC to make sure that DPC doesn't access + // the resources that you are going to free here. + // + KeCancelTimer(&devExtension->PollingTimer); + + + // + // Create counted string version of our Win32 device name. + // + + RtlInitUnicodeString(&uniWin32NameString, CSAMP_DOS_DEVICE_NAME_U); + + // + // Delete the link from our device name to a name in the Win32 namespace. + // + + IoDeleteSymbolicLink(&uniWin32NameString); + + IoDeleteDevice(deviceObject); + + CSAMP_KDPRINT(("<---CsampUnload\n")); + return; +} + +NTSTATUS CsampInsertIrp ( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp, + _In_ PVOID InsertContext + ) +{ + PDEVICE_EXTENSION devExtension; + + UNREFERENCED_PARAMETER(InsertContext); + + devExtension = CONTAINING_RECORD(Csq, + DEVICE_EXTENSION, CancelSafeQueue); + // + // Suppressing because the address below csq is valid since it's + // part of DEVICE_EXTENSION structure. + // +#pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'devExtension->CurrentIrp") + if (!devExtension->CurrentIrp) { + devExtension->CurrentIrp = Irp; + return STATUS_UNSUCCESSFUL; + } + + + InsertTailList(&devExtension->PendingIrpQueue, + &Irp->Tail.Overlay.ListEntry); + return STATUS_SUCCESS; +} + +VOID CsampRemoveIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp + ) +{ + UNREFERENCED_PARAMETER(Csq); + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); +} + + +PIRP CsampPeekNextIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp, + _In_ PVOID PeekContext + ) +{ + PDEVICE_EXTENSION devExtension; + PIRP nextIrp = NULL; + PLIST_ENTRY nextEntry; + PLIST_ENTRY listHead; + PIO_STACK_LOCATION irpStack; + + devExtension = CONTAINING_RECORD(Csq, + DEVICE_EXTENSION, CancelSafeQueue); + + listHead = &devExtension->PendingIrpQueue; + + // + // If the IRP is NULL, we will start peeking from the listhead, else + // we will start from that IRP onwards. This is done under the + // assumption that new IRPs are always inserted at the tail. + // + + if (Irp == NULL) { + nextEntry = listHead->Flink; + } else { + nextEntry = Irp->Tail.Overlay.ListEntry.Flink; + } + + + while(nextEntry != listHead) { + + nextIrp = CONTAINING_RECORD(nextEntry, IRP, Tail.Overlay.ListEntry); + + irpStack = IoGetCurrentIrpStackLocation(nextIrp); + + // + // If context is present, continue until you find a matching one. + // Else you break out as you got next one. + // + + if (PeekContext) { + if (irpStack->FileObject == (PFILE_OBJECT) PeekContext) { + break; + } + } else { + break; + } + nextIrp = NULL; + nextEntry = nextEntry->Flink; + } + + // + // Check if this is from start packet. + // + + if (PeekContext == NULL) { + devExtension->CurrentIrp = nextIrp; + } + + return nextIrp; +} + +// +// CsampAcquireLock modifies the execution level of the current processor. +// +// KeAcquireSpinLock raises the execution level to Dispatch Level and stores +// the current execution level in the Irql parameter to be restored at a later +// time. KeAcqurieSpinLock also requires us to be running at no higher than +// Dispatch level when it is called. +// +// The annotations reflect these changes and requirments. +// + +_IRQL_raises_(DISPATCH_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_Acquires_lock_(CONTAINING_RECORD(Csq,DEVICE_EXTENSION, CancelSafeQueue)->QueueLock) +VOID CsampAcquireLock( + _In_ PIO_CSQ Csq, + _Out_ _At_(*Irql, _Post_ _IRQL_saves_) PKIRQL Irql + ) +{ + PDEVICE_EXTENSION devExtension; + + devExtension = CONTAINING_RECORD(Csq, + DEVICE_EXTENSION, CancelSafeQueue); + // + // Suppressing because the address below csq is valid since it's + // part of DEVICE_EXTENSION structure. + // +#pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'devExtension->QueueLock'") + KeAcquireSpinLock(&devExtension->QueueLock, Irql); +} + +// +// CsampReleaseLock modifies the execution level of the current processor. +// +// KeReleaseSpinLock assumes we already hold the spin lock and are therefore +// running at Dispatch level. It will use the Irql parameter saved in a +// previous call to KeAcquireSpinLock to return the thread back to it's original +// execution level. +// +// The annotations reflect these changes and requirments. +// + +_IRQL_requires_(DISPATCH_LEVEL) +_Releases_lock_(CONTAINING_RECORD(Csq,DEVICE_EXTENSION, CancelSafeQueue)->QueueLock) +VOID CsampReleaseLock( + _In_ PIO_CSQ Csq, + _In_ _IRQL_restores_ KIRQL Irql + ) +{ + PDEVICE_EXTENSION devExtension; + + devExtension = CONTAINING_RECORD(Csq, + DEVICE_EXTENSION, CancelSafeQueue); + // + // Suppressing because the address below csq is valid since it's + // part of DEVICE_EXTENSION structure. + // +#pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'devExtension->QueueLock'") + KeReleaseSpinLock(&devExtension->QueueLock, Irql); +} + +VOID CsampCompleteCanceledIrp( + _In_ PIO_CSQ pCsq, + _In_ PIRP Irp + ) +{ + UNREFERENCED_PARAMETER(pCsq); + + CSAMP_KDPRINT(("Cancelled IRP: 0x%p\n", Irp)); + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); +} + diff --git a/general/cancel/startio/cancel.h b/general/cancel/startio/cancel.h new file mode 100644 index 00000000..8f04ab75 --- /dev/null +++ b/general/cancel/startio/cancel.h @@ -0,0 +1,166 @@ +#ifndef __CANCEL_H +#define __CANCEL_H + +#include + +// +// Since this driver is a legacy driver and gets installed as a service +// (without an INF file), we will define a class guid for use in +// IoCreateDeviceSecure function. This would allow the system to store +// Security, DeviceType, Characteristics and Exclusivity information of the +// deviceobject in the registery under +// HKLM\SYSTEM\CurrentControlSet\Control\Class\ClassGUID\Properties. +// This information can be overrided by an Administrators giving them the ability +// to control access to the device beyond what is initially allowed +// by the driver developer. +// + + +// {5D006E1A-2631-466c-B8A0-32FD498E4424} - generated using guidgen.exe +DEFINE_GUID (GUID_DEVCLASS_CANCEL_SAMPLE, + 0x5d006e1a, 0x2631, 0x466c, 0xb8, 0xa0, 0x32, 0xfd, 0x49, 0x8e, 0x44, 0x24); + +// +// GUID definition are required to be outside of header inclusion pragma to +// avoid error during precompiled headers. +// +#include +#include // for IoCreateDeviceSecure +#include + +// Debugging macros + +#if DBG +#define CSAMP_KDPRINT(_x_) \ + DbgPrint("CANCEL.SYS: ");\ + DbgPrint _x_; + +#define TRAP() DbgBreakPoint() + +#else + +#define CSAMP_KDPRINT(_x_) + +#define TRAP() + +#endif + +#define CSAMP_DEVICE_NAME_U L"\\Device\\CANCELSAMP" +#define CSAMP_DOS_DEVICE_NAME_U L"\\DosDevices\\CancelSamp" +#define CSAMP_RETRY_INTERVAL 500*1000 //500 ms +#define TAG (ULONG)'MASC' + +typedef struct _INPUT_DATA{ + + ULONG Data; //device data is stored here + +} INPUT_DATA, *PINPUT_DATA; + +typedef struct _DEVICE_EXTENSION{ + + // Irps waiting to be processed are queued here + LIST_ENTRY PendingIrpQueue; + + // SpinLock to protect access to the queue + KSPIN_LOCK QueueLock; + + // SpinLock to provide exclusive access to the port + KSPIN_LOCK DeviceLock; + + // Pointer to current device IRP. Exclusive access to this + // field is also provided by the QueueLock. + PIRP CurrentIrp; + + // Customtimer DPC object + KDPC PollingDpc; + + // Time at which the device was last polled + LARGE_INTEGER LastPollTime; + + // Polling timer object + KTIMER PollingTimer; + + // Polling interval (retry interval) + LARGE_INTEGER PollingInterval; + + IO_CSQ CancelSafeQueue; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +typedef struct _FILE_CONTEXT{ + // + // Lock to rundown threads that are dispatching I/Os on a file handle + // while the cleanup for that handle is in progress. + // + IO_REMOVE_LOCK FileRundownLock; +} FILE_CONTEXT, *PFILE_CONTEXT; + +DRIVER_INITIALIZE DriverEntry; + +_Dispatch_type_(IRP_MJ_CREATE) +_Dispatch_type_(IRP_MJ_CLOSE) +DRIVER_DISPATCH CsampCreateClose; + +_Dispatch_type_(IRP_MJ_CLEANUP) +DRIVER_DISPATCH CsampCleanup; + +_Dispatch_type_(IRP_MJ_READ) +DRIVER_DISPATCH CsampRead; + +DRIVER_DISPATCH CsampPollDevice; + +DRIVER_UNLOAD CsampUnload; + +KDEFERRED_ROUTINE CsampPollingTimerDpc; + +VOID +CsampInitiateIo( + _In_ PDEVICE_OBJECT DeviceObject +); + +NTSTATUS +CsampInsertIrp ( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp, + _In_ PVOID InsertContext + ); + +VOID +CsampRemoveIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp + ); + +PIRP +CsampPeekNextIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp, + _In_ PVOID PeekContext + ); + +_IRQL_raises_(DISPATCH_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_Acquires_lock_(CONTAINING_RECORD(Csq,DEVICE_EXTENSION, CancelSafeQueue)->QueueLock) +VOID +CsampAcquireLock( + _In_ PIO_CSQ Csq, + _Out_ _At_(*Irql, _Post_ _IRQL_saves_) PKIRQL Irql + ); + +_IRQL_requires_(DISPATCH_LEVEL) +_Releases_lock_(CONTAINING_RECORD(Csq,DEVICE_EXTENSION, CancelSafeQueue)->QueueLock) +VOID +CsampReleaseLock( + _In_ PIO_CSQ Csq, + _In_ _IRQL_restores_ KIRQL Irql + ); + +VOID +CsampCompleteCanceledIrp( + _In_ PIO_CSQ pCsq, + _In_ PIRP Irp + ); + +#endif + + diff --git a/general/cancel/startio/cancel.rc b/general/cancel/startio/cancel.rc new file mode 100644 index 00000000..2bd08155 --- /dev/null +++ b/general/cancel/startio/cancel.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Sample Cancel Driver" +#define VER_INTERNALNAME_STR "cancel.sys" + +#include "common.ver" diff --git a/general/cancel/startio/cancel.vcxproj b/general/cancel/startio/cancel.vcxproj new file mode 100644 index 00000000..8cdbf0ce --- /dev/null +++ b/general/cancel/startio/cancel.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {1392C861-BA6F-4423-8C33-A8C771BAF473} + $(MSBuildProjectName) + Debug + Win32 + {2FA77D27-524D-4C63-81CD-62E054676D96} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + cancel + + + cancel + + + cancel + + + cancel + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + true + Level4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/cancel/startio/cancel.vcxproj.Filters b/general/cancel/startio/cancel.vcxproj.Filters new file mode 100644 index 00000000..015f96b7 --- /dev/null +++ b/general/cancel/startio/cancel.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {DBC52094-6726-43E4-8D3A-1E52EB8592CA} + + + h;hpp;hxx;hm;inl;inc;xsd + {12A618AD-2EC7-4EF1-B1F8-F14652DE50D0} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {01F4B40C-7AC7-4573-A236-526D211FE249} + + + inf;inv;inx;mof;mc; + {C63E5134-5856-4331-9141-C85F75D697B9} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/cancel/sys/cancel.c b/general/cancel/sys/cancel.c new file mode 100644 index 00000000..58bf72e9 --- /dev/null +++ b/general/cancel/sys/cancel.c @@ -0,0 +1,945 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + + +Module Name: + + cancel.c + +Abstract: Demonstrates the use of new Cancel-Safe queue + APIs to perform queuing of IRPs without worrying about + any synchronization issues between cancel lock in the I/O + manager and the driver's queue lock. + + This driver is written for an hypothetical data acquisition + device that requires polling at a regular interval. + The device has some settling period between two reads. + Upon user request the driver reads data and records the time. + When the next read request comes in, it checks the interval + to see if it's reading the device too soon. If so, it pends + the IRP and sleeps for while and tries again. + + Upon arrival, IRPs are queued in a cancel-safe queue and a + semaphore is signaled. A polling thread indefinitely waits on the + semaphore to process queued IRPs sequentially. + + This sample is adapted from the original cancel + sample (KB Q188276) available in MSDN. + +Environment: + + Kernel mode + +--*/ + +#include "cancel.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, DriverEntry ) +#pragma alloc_text( PAGE, CsampCreateClose) +#pragma alloc_text( PAGE, CsampUnload) +#pragma alloc_text( PAGE, CsampRead) +#endif // ALLOC_PRAGMA + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + registryPath - pointer to a unicode string representing the path, + to driver-specific key in the registry. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING unicodeDeviceName; + UNICODE_STRING unicodeDosDeviceName; + PDEVICE_OBJECT deviceObject; + PDEVICE_EXTENSION devExtension; + HANDLE threadHandle; + UNICODE_STRING sddlString; + + UNREFERENCED_PARAMETER (RegistryPath); + + CSAMP_KDPRINT(("DriverEntry Enter \n")); + + + (void) RtlInitUnicodeString(&unicodeDeviceName, CSAMP_DEVICE_NAME_U); + + (void) RtlInitUnicodeString( &sddlString, L"D:P(A;;GA;;;SY)(A;;GA;;;BA)"); + + // + // We will create a secure deviceobject so that only processes running + // in admin and local system account can access the device. Refer + // "Security Descriptor String Format" section in the platform + // SDK documentation to understand the format of the sddl string. + // We need to do because this is a legacy driver and there is no INF + // involved in installing the driver. For PNP drivers, security descriptor + // is typically specified for the FDO in the INF file. + // + + status = IoCreateDeviceSecure( + DriverObject, + sizeof(DEVICE_EXTENSION), + &unicodeDeviceName, + FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, + (BOOLEAN) FALSE, + &sddlString, + (LPCGUID)&GUID_DEVCLASS_CANCEL_SAMPLE, + &deviceObject + ); + if (!NT_SUCCESS(status)) + { + return status; + } + + DbgPrint("DeviceObject %p\n", deviceObject); + + // + // Allocate and initialize a Unicode String containing the Win32 name + // for our device. + // + + (void)RtlInitUnicodeString( &unicodeDosDeviceName, CSAMP_DOS_DEVICE_NAME_U ); + + + status = IoCreateSymbolicLink( + (PUNICODE_STRING) &unicodeDosDeviceName, + (PUNICODE_STRING) &unicodeDeviceName + ); + + if (!NT_SUCCESS(status)) + { + IoDeleteDevice(deviceObject); + return status; + } + + devExtension = deviceObject->DeviceExtension; + + DriverObject->MajorFunction[IRP_MJ_CREATE]= + DriverObject->MajorFunction[IRP_MJ_CLOSE] = CsampCreateClose; + DriverObject->MajorFunction[IRP_MJ_READ] = CsampRead; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = CsampCleanup; + + DriverObject->DriverUnload = CsampUnload; + + // + // Set the flag signifying that we will do buffered I/O. This causes NT + // to allocate a buffer on a ReadFile operation which will then be copied + // back to the calling application by the I/O subsystem + // + + deviceObject->Flags |= DO_BUFFERED_IO; + + // + // This is used to serailize access to the queue. + // + + KeInitializeSpinLock(&devExtension->QueueLock); + + KeInitializeSemaphore(&devExtension->IrpQueueSemaphore, 0, MAXLONG ); + + // + // Initialize the pending Irp devicequeue + // + + InitializeListHead( &devExtension->PendingIrpQueue ); + + // + // Initialize the cancel safe queue + // + IoCsqInitialize( &devExtension->CancelSafeQueue, + CsampInsertIrp, + CsampRemoveIrp, + CsampPeekNextIrp, + CsampAcquireLock, + CsampReleaseLock, + CsampCompleteCanceledIrp ); + // + // 10 is multiplied because system time is specified in 100ns units + // + + devExtension->PollingInterval.QuadPart = Int32x32To64( + CSAMP_RETRY_INTERVAL, -10); + // + // Note down system time + // + + KeQuerySystemTime (&devExtension->LastPollTime); + + // + // Start the polling thread. + // + + devExtension->ThreadShouldStop = FALSE; + + status = PsCreateSystemThread(&threadHandle, + (ACCESS_MASK)0, + NULL, + (HANDLE) 0, + NULL, + CsampPollingThread, + deviceObject ); + + if ( !NT_SUCCESS( status )) + { + IoDeleteSymbolicLink( &unicodeDosDeviceName ); + IoDeleteDevice( deviceObject ); + return status; + } + + // + // Convert the Thread object handle into a pointer to the Thread object + // itself. Then close the handle. + // + + ObReferenceObjectByHandle(threadHandle, + THREAD_ALL_ACCESS, + NULL, + KernelMode, + &devExtension->ThreadObject, + NULL ); + + ZwClose(threadHandle); + + CSAMP_KDPRINT(("DriverEntry Exit = %x\n", status)); + + ASSERT(NT_SUCCESS(status)); + + return status; +} + + +_Use_decl_annotations_ +NTSTATUS +CsampCreateClose( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + Process the Create and close IRPs sent to this device. + +Arguments: + + DeviceObject - pointer to a device object. + + Irp - pointer to an I/O Request Packet. + +Return Value: + + NT Status code + +--*/ +{ + PIO_STACK_LOCATION irpStack; + NTSTATUS status = STATUS_SUCCESS; + PFILE_CONTEXT fileContext; + + UNREFERENCED_PARAMETER(DeviceObject); + + PAGED_CODE (); + + CSAMP_KDPRINT(("CsampCreateClose Enter\n")); + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + ASSERT(irpStack->FileObject != NULL); + + switch(irpStack->MajorFunction) + { + case IRP_MJ_CREATE: + + // + // The dispatch routine for IRP_MJ_CREATE is called when a + // file object associated with the device is created. + // This is typically because of a call to CreateFile() in + // a user-mode program or because a another driver is + // layering itself over a this driver. A driver is + // required to supply a dispatch routine for IRP_MJ_CREATE. + // + fileContext = ExAllocatePoolWithQuotaTag(NonPagedPool, + sizeof(FILE_CONTEXT), + TAG); + + if (NULL == fileContext) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + IoInitializeRemoveLock(&fileContext->FileRundownLock, TAG, 0, 0); + + // + // Make sure nobody is using the FsContext scratch area. + // + ASSERT(irpStack->FileObject->FsContext == NULL); + + // + // Store the context in the FileObject's scratch area. + // + irpStack->FileObject->FsContext = (PVOID) fileContext; + + CSAMP_KDPRINT(("IRP_MJ_CREATE\n")); + break; + + case IRP_MJ_CLOSE: + // + // The IRP_MJ_CLOSE dispatch routine is called when a file object + // opened on the driver is being removed from the system; that is, + // all file object handles have been closed and the reference count + // of the file object is down to 0. + // + fileContext = irpStack->FileObject->FsContext; + + ExFreePoolWithTag(fileContext, TAG); + + CSAMP_KDPRINT(("IRP_MJ_CLOSE\n")); + break; + + default: + CSAMP_KDPRINT((" Invalid CreateClose Parameter\n")); + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Save Status for return and complete Irp + // + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + CSAMP_KDPRINT((" CsampCreateClose Exit = %x\n", status)); + + return status; +} + + +_Use_decl_annotations_ +NTSTATUS +CsampRead( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + /*++ + Routine Description: + + Read disptach routine + + Arguments: + + DeviceObject - pointer to a device object. + Irp - pointer to current Irp + + Return Value: + + NT status code. + +--*/ +{ + NTSTATUS status; + PDEVICE_EXTENSION devExtension; + PIO_STACK_LOCATION irpStack; + LARGE_INTEGER currentTime; + PFILE_CONTEXT fileContext; + PVOID readBuffer; + BOOLEAN inCriticalRegion; + + PAGED_CODE(); + + CSAMP_KDPRINT(("CsampRead Enter:0x%p\n", Irp)); + + devExtension = DeviceObject->DeviceExtension; + inCriticalRegion = FALSE; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + ASSERT(irpStack->FileObject != NULL); + + fileContext = irpStack->FileObject->FsContext; + + status = IoAcquireRemoveLock(&fileContext->FileRundownLock, Irp); + if (!NT_SUCCESS(status)) { + // + // Lock is in a removed state. That means we have already received + // cleaned up request for this handle. + // + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return status; + } + + // + // First make sure there is enough room. + // + if (irpStack->Parameters.Read.Length < sizeof(INPUT_DATA)) + { + Irp->IoStatus.Status = status = STATUS_BUFFER_TOO_SMALL; + Irp->IoStatus.Information = 0; + IoReleaseRemoveLock(&fileContext->FileRundownLock, Irp); + IoCompleteRequest (Irp, IO_NO_INCREMENT); + return status; + } + + // + // FOR TESTING: + // Initialize the data to mod 2 of some random number. + // With this value you can control the number of times the + // Irp will be queued before completion. Check + // CsampPollDevice routine to know how this works. + // + + KeQuerySystemTime(¤tTime); + + readBuffer = Irp->AssociatedIrp.SystemBuffer; + + *((PULONG)readBuffer) = ((currentTime.LowPart/13)%2); + + // + // To avoid the thread from being suspended after it has queued the IRP and + // before it signalled the semaphore, we will enter critical region. + // + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + KeEnterCriticalRegion(); + inCriticalRegion = TRUE; + + // + // Queue the IRP and return STATUS_PENDING after signalling the + // polling thread. + // Note: IoCsqInsertIrp marks the IRP pending. + // + IoCsqInsertIrp(&devExtension->CancelSafeQueue, Irp, NULL); + + // + // Do not touch the IRP once it has been queued because another thread + // could remove the IRP and complete it before this one gets to run. + // + + // + // A semaphore remains signaled as long as its count is greater than + // zero, and non-signaled when the count is zero. Following function + // increments the semaphore count by 1. + // + + KeReleaseSemaphore(&devExtension->IrpQueueSemaphore, + 0,// No priority boost + 1,// Increment semaphore by 1 + FALSE );// No WaitForXxx after this call + if (inCriticalRegion == TRUE) { + KeLeaveCriticalRegion(); + } + // + // We don't hold the lock for IRP that's pending in the list because this + // lock is meant to rundown currently dispatching threads when the cleanup + // is handled. + // + IoReleaseRemoveLock(&fileContext->FileRundownLock, Irp); + + return STATUS_PENDING; +} + +VOID +CsampPollingThread( + _In_ PVOID Context + ) +/*++ + +Routine Description: + + This is the main thread that removes IRP from the queue + and peforms I/O on it. + +Arguments: + + Context -- pointer to the device object + +--*/ +{ + PDEVICE_OBJECT DeviceObject = Context; + PDEVICE_EXTENSION DevExtension = DeviceObject->DeviceExtension; + PIRP Irp; + NTSTATUS Status; + + KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY ); + + // + // Now enter the main IRP-processing loop + // + for(;;) + { + // + // Wait indefinitely for an IRP to appear in the work queue or for + // the Unload routine to stop the thread. Every successful return + // from the wait decrements the semaphore count by 1. + // + KeWaitForSingleObject(&DevExtension->IrpQueueSemaphore, + Executive, + KernelMode, + FALSE, + NULL ); + + // + // See if thread was awakened because driver is unloading itself... + // + + if ( DevExtension->ThreadShouldStop ) { + PsTerminateSystemThread( STATUS_SUCCESS ); + } + + // + // Remove a pending IRP from the queue. + // + Irp = IoCsqRemoveNextIrp(&DevExtension->CancelSafeQueue, NULL); + + if (!Irp) { + CSAMP_KDPRINT(("Oops, a queued irp got cancelled\n")); + continue; // go back to waiting + } + + for(;;) { + // + // Perform I/O + // + Status = CsampPollDevice(DeviceObject, Irp); + if (Status == STATUS_PENDING) { + + // + // Device is not ready, so sleep for a while and try again. + // + KeDelayExecutionThread(KernelMode, FALSE, + &DevExtension->PollingInterval); + + } else { + + // + // I/O is successful, so complete the Irp. + // + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + break; + } + + } + // + // Go back to the top of the loop to see if there's another request waiting. + // + } // end of while-loop +} + +_Use_decl_annotations_ +NTSTATUS +CsampPollDevice( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + Polls for data + +Arguments: + + DeviceObject -- pointer to the device object + Irp -- pointer to the requesing Irp + + +Return Value: + + STATUS_SUCCESS -- if the poll succeeded, + STATUS_TIMEOUT -- if the poll failed (timeout), + or the checksum was incorrect + STATUS_PENDING -- if polled too soon + +--*/ +{ + PINPUT_DATA pInput; + + UNREFERENCED_PARAMETER( DeviceObject ); + + pInput = (PINPUT_DATA)Irp->AssociatedIrp.SystemBuffer; + +#ifdef REAL + + RtlZeroMemory( pInput, sizeof(INPUT_DATA) ); + + // + // If currenttime is less than the lasttime polled plus + // minimum time required for the device to settle + // then don't poll and return STATUS_PENDING + // + + KeQuerySystemTime(¤tTime); + if (currentTime->QuadPart < (TimeBetweenPolls + + devExtension->LastPollTime.QuadPart)) + { + return STATUS_PENDING; + } + + // + // Read/Write to the port here. + // Fill the INPUT structure + // + + // + // Note down the current time as the last polled time + // + + KeQuerySystemTime(&devExtension->LastPollTime); + + + return STATUS_SUCCESS; +#else + + // + // With this conditional statement + // you can control the number of times the + // i/o should be retried before completing. + // + + if (pInput->Data-- <= 0) + { + Irp->IoStatus.Information = sizeof(INPUT_DATA); + return STATUS_SUCCESS; + } + return STATUS_PENDING; + + #endif + +} + +_Use_decl_annotations_ +NTSTATUS +CsampCleanup( + PDEVICE_OBJECT DeviceObject, + PIRP Irp +) +/*++ + +Routine Description: + This dispatch routine is called when the last handle (in + the whole system) to a file object is closed. In other words, the open + handle count for the file object goes to 0. A driver that holds pending + IRPs internally must implement a routine for IRP_MJ_CLEANUP. When the + routine is called, the driver should cancel all the pending IRPs that + belong to the file object identified by the IRP_MJ_CLEANUP call. In other + words, it should cancel all the IRPs that have the same file-object pointer + as the one supplied in the current I/O stack location of the IRP for the + IRP_MJ_CLEANUP call. Of course, IRPs belonging to other file objects should + not be canceled. Also, if an outstanding IRP is completed immediately, the + driver does not have to cancel it. + +Arguments: + + DeviceObject -- pointer to the device object + Irp -- pointer to the requesing Irp + +Return Value: + + STATUS_SUCCESS -- if the poll succeeded, +--*/ +{ + + PDEVICE_EXTENSION devExtension; + PIRP pendingIrp; + PIO_STACK_LOCATION irpStack; + PFILE_CONTEXT fileContext; + NTSTATUS status; + + CSAMP_KDPRINT(("CsampCleanupIrp enter\n")); + + devExtension = DeviceObject->DeviceExtension; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + ASSERT(irpStack->FileObject != NULL); + + fileContext = irpStack->FileObject->FsContext; + + // + // This acquire cannot fail because you cannot get more than one + // cleanup for the same handle. + // + status = IoAcquireRemoveLock(&fileContext->FileRundownLock, Irp); + ASSERT(NT_SUCCESS(status)); + + // + // Wait for all the threads that are currently dispatching to exit and + // prevent any threads dispatching I/O on the same handle beyond this point. + // + IoReleaseRemoveLockAndWait(&fileContext->FileRundownLock, Irp); + + pendingIrp = IoCsqRemoveNextIrp(&devExtension->CancelSafeQueue, + irpStack->FileObject); + + while(pendingIrp) + { + // + // Cancel the IRP + // + pendingIrp->IoStatus.Information = 0; + pendingIrp->IoStatus.Status = STATUS_CANCELLED; + CSAMP_KDPRINT(("Cleanup cancelled irp\n")); + IoCompleteRequest(pendingIrp, IO_NO_INCREMENT); + + pendingIrp = IoCsqRemoveNextIrp(&devExtension->CancelSafeQueue, + irpStack->FileObject); + } + + // + // Finally complete the cleanup IRP + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + CSAMP_KDPRINT(("CsampCleanupIrp exit\n")); + + return STATUS_SUCCESS; + +} + +VOID +CsampUnload( + _In_ PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + Free all the allocated resources, etc. + +Arguments: + + DriverObject - pointer to a driver object. + +Return Value: + + VOID +--*/ +{ + PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; + UNICODE_STRING uniWin32NameString; + PDEVICE_EXTENSION devExtension = deviceObject->DeviceExtension; + + PAGED_CODE(); + + CSAMP_KDPRINT(("CsampUnload Enter\n")); + + // + // Set the Stop flag + // + devExtension->ThreadShouldStop = TRUE; + + // + // Make sure the thread wakes up + // +#pragma prefast(suppress: __WARNING_ERROR, "Passing TRUE as last parameter of KeReleaseSemaphore is just a hint that a wait is next.") + KeReleaseSemaphore(&devExtension->IrpQueueSemaphore, + 0, // No priority boost + 1, // Increment semaphore by 1 + TRUE );// WaitForXxx after this call + + // + // Wait for the thread to terminate + // + KeWaitForSingleObject(devExtension->ThreadObject, + Executive, + KernelMode, + FALSE, + NULL ); + + ObDereferenceObject(devExtension->ThreadObject); + + // + // Create counted string version of our Win32 device name. + // + + RtlInitUnicodeString( &uniWin32NameString, CSAMP_DOS_DEVICE_NAME_U ); + + IoDeleteSymbolicLink( &uniWin32NameString ); + + IoDeleteDevice( deviceObject ); + + CSAMP_KDPRINT(("CsampUnload Exit\n")); + return; +} + +VOID CsampInsertIrp ( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp + ) +{ + PDEVICE_EXTENSION devExtension; + + devExtension = CONTAINING_RECORD(Csq, + DEVICE_EXTENSION, CancelSafeQueue); + + InsertTailList(&devExtension->PendingIrpQueue, + &Irp->Tail.Overlay.ListEntry); +} + +VOID CsampRemoveIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp + ) +{ + UNREFERENCED_PARAMETER(Csq); + + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); +} + + +PIRP CsampPeekNextIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp, + _In_ PVOID PeekContext + ) +{ + PDEVICE_EXTENSION devExtension; + PIRP nextIrp = NULL; + PLIST_ENTRY nextEntry; + PLIST_ENTRY listHead; + PIO_STACK_LOCATION irpStack; + + devExtension = CONTAINING_RECORD(Csq, + DEVICE_EXTENSION, CancelSafeQueue); + + listHead = &devExtension->PendingIrpQueue; + + // + // If the IRP is NULL, we will start peeking from the listhead, else + // we will start from that IRP onwards. This is done under the + // assumption that new IRPs are always inserted at the tail. + // + + if (Irp == NULL) { + nextEntry = listHead->Flink; + } else { + nextEntry = Irp->Tail.Overlay.ListEntry.Flink; + } + + while(nextEntry != listHead) { + + nextIrp = CONTAINING_RECORD(nextEntry, IRP, Tail.Overlay.ListEntry); + + irpStack = IoGetCurrentIrpStackLocation(nextIrp); + + // + // If context is present, continue until you find a matching one. + // Else you break out as you got next one. + // + + if (PeekContext) { + if (irpStack->FileObject == (PFILE_OBJECT) PeekContext) { + break; + } + } else { + break; + } + nextIrp = NULL; + nextEntry = nextEntry->Flink; + } + + return nextIrp; + +} + +// +// CsampAcquireLock modifies the execution level of the current processor. +// +// KeAcquireSpinLock raises the execution level to Dispatch Level and stores +// the current execution level in the Irql parameter to be restored at a later +// time. KeAcqurieSpinLock also requires us to be running at no higher than +// Dispatch level when it is called. +// +// The annotations reflect these changes and requirments. +// + +_IRQL_raises_(DISPATCH_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_Acquires_lock_(CONTAINING_RECORD(Csq,DEVICE_EXTENSION, CancelSafeQueue)->QueueLock) +VOID CsampAcquireLock( + _In_ PIO_CSQ Csq, + _Out_ _At_(*Irql, _Post_ _IRQL_saves_) PKIRQL Irql + ) +{ + PDEVICE_EXTENSION devExtension; + + devExtension = CONTAINING_RECORD(Csq, + DEVICE_EXTENSION, CancelSafeQueue); + // + // Suppressing because the address below csq is valid since it's + // part of DEVICE_EXTENSION structure. + // +#pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'devExtension->QueueLock'") + KeAcquireSpinLock(&devExtension->QueueLock, Irql); +} + +// +// CsampReleaseLock modifies the execution level of the current processor. +// +// KeReleaseSpinLock assumes we already hold the spin lock and are therefore +// running at Dispatch level. It will use the Irql parameter saved in a +// previous call to KeAcquireSpinLock to return the thread back to it's original +// execution level. +// +// The annotations reflect these changes and requirments. +// + +_IRQL_requires_(DISPATCH_LEVEL) +_Releases_lock_(CONTAINING_RECORD(Csq,DEVICE_EXTENSION, CancelSafeQueue)->QueueLock) +VOID CsampReleaseLock( + _In_ PIO_CSQ Csq, + _In_ _IRQL_restores_ KIRQL Irql + ) +{ + PDEVICE_EXTENSION devExtension; + + devExtension = CONTAINING_RECORD(Csq, + DEVICE_EXTENSION, CancelSafeQueue); + // + // Suppressing because the address below csq is valid since it's + // part of DEVICE_EXTENSION structure. + // +#pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'devExtension->QueueLock'") + KeReleaseSpinLock(&devExtension->QueueLock, Irql); +} + +VOID CsampCompleteCanceledIrp( + _In_ PIO_CSQ pCsq, + _In_ PIRP Irp + ) +{ + + UNREFERENCED_PARAMETER(pCsq); + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + CSAMP_KDPRINT(("cancelled irp\n")); + IoCompleteRequest(Irp, IO_NO_INCREMENT); +} + diff --git a/general/cancel/sys/cancel.h b/general/cancel/sys/cancel.h new file mode 100644 index 00000000..4f63d2af --- /dev/null +++ b/general/cancel/sys/cancel.h @@ -0,0 +1,181 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + cancel.h + +Abstract: + +Environment: + + Kernel mode only. + + +Revision History: + +--*/ + +#include + +// +// Since this driver is a legacy driver and gets installed as a service +// (without an INF file), we will define a class guid for use in +// IoCreateDeviceSecure function. This would allow the system to store +// Security, DeviceType, Characteristics and Exclusivity information of the +// deviceobject in the registery under +// HKLM\SYSTEM\CurrentControlSet\Control\Class\ClassGUID\Properties. +// This information can be overrided by an Administrators giving them the ability +// to control access to the device beyond what is initially allowed +// by the driver developer. +// + +// {5D006E1A-2631-466c-B8A0-32FD498E4424} - generated using guidgen.exe +DEFINE_GUID (GUID_DEVCLASS_CANCEL_SAMPLE, + 0x5d006e1a, 0x2631, 0x466c, 0xb8, 0xa0, 0x32, 0xfd, 0x49, 0x8e, 0x44, 0x24); + +// +// GUID definition are required to be outside of header inclusion pragma to avoid +// error during precompiled headers. +// + +#ifndef __CANCEL_H +#define __CANCEL_H + +// +// GUID definition are required to be outside of header inclusion pragma to +// avoid error during precompiled headers. +// +#include +#include // for IoCreateDeviceSecure +#include + +// Debugging macros + +#if DBG +#define CSAMP_KDPRINT(_x_) \ + DbgPrint("CANCEL.SYS: ");\ + DbgPrint _x_; +#else + +#define CSAMP_KDPRINT(_x_) + +#endif + +#define CSAMP_DEVICE_NAME_U L"\\Device\\CANCELSAMP" +#define CSAMP_DOS_DEVICE_NAME_U L"\\DosDevices\\CancelSamp" +#define CSAMP_RETRY_INTERVAL 500*1000 //500 ms +#define TAG (ULONG)'MASC' + +typedef struct _INPUT_DATA{ + + ULONG Data; //device data is stored here + +} INPUT_DATA, *PINPUT_DATA; + +typedef struct _DEVICE_EXTENSION{ + + BOOLEAN ThreadShouldStop; + + // Irps waiting to be processed are queued here + LIST_ENTRY PendingIrpQueue; + + // SpinLock to protect access to the queue + KSPIN_LOCK QueueLock; + + IO_CSQ CancelSafeQueue; + + // Time at which the device was last polled + LARGE_INTEGER LastPollTime; + + // Polling interval (retry interval) + LARGE_INTEGER PollingInterval; + + KSEMAPHORE IrpQueueSemaphore; + + PETHREAD ThreadObject; +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +typedef struct _FILE_CONTEXT{ + // + // Lock to rundown threads that are dispatching I/Os on a file handle + // while the cleanup for that handle is in progress. + // + IO_REMOVE_LOCK FileRundownLock; +} FILE_CONTEXT, *PFILE_CONTEXT; + +DRIVER_INITIALIZE DriverEntry; + +_Dispatch_type_(IRP_MJ_CREATE) +_Dispatch_type_(IRP_MJ_CLOSE) +DRIVER_DISPATCH CsampCreateClose; + +_Dispatch_type_(IRP_MJ_CLEANUP) +DRIVER_DISPATCH CsampCleanup; + +_Dispatch_type_(IRP_MJ_READ) +DRIVER_DISPATCH CsampRead; + +DRIVER_DISPATCH CsampPollDevice; + +DRIVER_UNLOAD CsampUnload; + +KSTART_ROUTINE CsampPollingThread; + +VOID +CsampPollingThread( + _In_ PVOID Context + ); + +VOID +CsampInsertIrp ( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp + ); + +VOID +CsampRemoveIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp + ); + +PIRP +CsampPeekNextIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp, + _In_ PVOID PeekContext + ); + +_IRQL_raises_(DISPATCH_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_Acquires_lock_(CONTAINING_RECORD(Csq,DEVICE_EXTENSION, CancelSafeQueue)->QueueLock) +VOID +CsampAcquireLock( + _In_ PIO_CSQ Csq, + _Out_ _At_(*Irql, _Post_ _IRQL_saves_) PKIRQL Irql + ); + +_IRQL_requires_(DISPATCH_LEVEL) +_Releases_lock_(CONTAINING_RECORD(Csq,DEVICE_EXTENSION, CancelSafeQueue)->QueueLock) +VOID +CsampReleaseLock( + _In_ PIO_CSQ Csq, + _In_ _IRQL_restores_ KIRQL Irql + ); + +VOID +CsampCompleteCanceledIrp( + _In_ PIO_CSQ pCsq, + _In_ PIRP Irp + ); + +#endif + + + diff --git a/general/cancel/sys/cancel.rc b/general/cancel/sys/cancel.rc new file mode 100644 index 00000000..2bd08155 --- /dev/null +++ b/general/cancel/sys/cancel.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Sample Cancel Driver" +#define VER_INTERNALNAME_STR "cancel.sys" + +#include "common.ver" diff --git a/general/cancel/sys/cancel.vcxproj b/general/cancel/sys/cancel.vcxproj new file mode 100644 index 00000000..6227a13d --- /dev/null +++ b/general/cancel/sys/cancel.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {3363DCA3-7873-4ECB-BA98-F243C2E8FDA0} + $(MSBuildProjectName) + Debug + Win32 + {3003AE7E-3AA2-458A-B349-64E4A921061B} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + cancel + + + cancel + + + cancel + + + cancel + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + true + Level4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/cancel/sys/cancel.vcxproj.Filters b/general/cancel/sys/cancel.vcxproj.Filters new file mode 100644 index 00000000..bdf2e015 --- /dev/null +++ b/general/cancel/sys/cancel.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {6BC3B1BF-F875-48B1-9E49-07CB6AE2FED1} + + + h;hpp;hxx;hm;inl;inc;xsd + {646CC6F7-384E-44CB-B55D-2CF7427232F2} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A3ADD9F6-E06C-4974-AD86-F265759792E2} + + + inf;inv;inx;mof;mc; + {4BC75BD1-ADE8-40F5-91AF-0002A740EF8C} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/echo/kmdf/ReadMe.md b/general/echo/kmdf/ReadMe.md new file mode 100644 index 00000000..05f41f49 --- /dev/null +++ b/general/echo/kmdf/ReadMe.md @@ -0,0 +1,84 @@ +KMDF Echo Sample +================ + +The ECHO (KMDF) sample demonstrates how to use a sequential queue to serialize read and write requests presented to the driver. + +It also shows how to synchronize execution of these events with other asynchronous events such as request cancellation and DPC. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Related technologies +-------------------- + +[Kernel-Mode Driver Framework](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544396) + +Code Tour +--------- + +DriverEntry - Creates a framework driver object. + +EvtDeviceAdd: Creates a device and registers self managed I/O callbacks so that it can start and stop the periodic timer when the device is entering and leaving D0 state. It registers a device interface so that application can find the device and send I/O. For managing I/O requests, the driver creates a default queue to receive only read & write requests. All other requests sent to the driver will be failed by the framework. Then the driver creates a periodic timer to simulate asynchronous event. The purpose of this timer would be to complete the currently pending request. + +In the AutoSync version of the sample, the queue is created with WdfSynchronizationScopeQueue so that I/O callbacks including cancel routine are synchronized with a queue-level lock. Since timer is parented to queue and by default timer objects are created with AutomaticSerialization set to **TRUE**, timer DPC callbacks will be serialized with EvtIoRead, EvtIoWrite and Cancel Routine. + +In the DriverSync version of the sample, the queue is created with WdfSynchronizationScopeNone, so that the framework does not provide any synchronization. The driver synchronizes the I/O callbacks, cancel routine and the timer DPC using a spinlock that it creates for this purpose. + +EvtIoWrite: Allocates an internal buffer as big as the size of buffer in the write request and copies the data from the request buffer to internal buffer. The internal buffer address is saved in the queue context. If the driver receives another write request, it will free this one and allocate a new buffer to match the size of the incoming request. After copying the data, it will mark the request cancelable and return. The request will be eventually completed either by the timer or by the cancel routine if the application exits. + +EvtIoRead: Retrieves request memory buffer and copies the data from the buffer created by the write handler to the request buffer, and marks the request cancelable. The request will be completed by the timer DPC callback. + +Since the queue is a sequential queue, only one request is outstanding in the driver. + +Testing +------- + +**Usage:** + +Echoapp.exe --- Send single write and read request synchronously + +Echoapp.exe -Async --- Send 100 reads and writes asynchronously + +Exit the app anytime by pressing Ctrl-C + +File Manifest +------------- + +File + +Description + +Echo.htm + +Documentation for this sample (this file). + +***(The AutoSync and DriverSync versions of the sample each have their own version of the following files)*** + +Driver.h, Driver.c + +DriverEntry and Events on the Driver Object. + +Device.h, Device.c + +Events on the Device Object. + +Queue.h, Queue.c + +Contains Events on the I/O Queue Objects. + +Echo.inx + +File that describes the installation of this driver. The build process converts this into an INF file. + +Makefile.inc + +A makefile that defines custom build actions. This includes the conversion of the .INX file into a .INF file + +Makefile + +This file merely redirects to the real makefile that is shared by all the driver components of the Windows NT DDK. + +Sources + +Generic file that lists source files and all the build options. + diff --git a/general/echo/kmdf/driver/AutoSync/device.c b/general/echo/kmdf/driver/AutoSync/device.c new file mode 100644 index 00000000..ee20447f --- /dev/null +++ b/general/echo/kmdf/driver/AutoSync/device.c @@ -0,0 +1,210 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + device.c - Device handling events for example driver. + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#include "driver.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, EchoDeviceCreate) +#pragma alloc_text (PAGE, EchoEvtDeviceSelfManagedIoSuspend) +#endif + + +NTSTATUS +EchoDeviceCreate( + PWDFDEVICE_INIT DeviceInit + ) +/*++ + +Routine Description: + + Worker routine called to create a device and its software resources. + +Arguments: + + DeviceInit - Pointer to an opaque init structure. Memory for this + structure will be freed by the framework when the WdfDeviceCreate + succeeds. So don't access the structure after that point. + +Return Value: + + NTSTATUS + +--*/ +{ + WDF_OBJECT_ATTRIBUTES deviceAttributes; + PDEVICE_CONTEXT deviceContext; + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDFDEVICE device; + NTSTATUS status; + + PAGED_CODE(); + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); + + // + // Register pnp/power callbacks so that we can start and stop the timer as the device + // gets started and stopped. + // + pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = EchoEvtDeviceSelfManagedIoStart; + pnpPowerCallbacks.EvtDeviceSelfManagedIoSuspend = EchoEvtDeviceSelfManagedIoSuspend; + + #pragma prefast(suppress: 28024, "Function used for both Init and Restart Callbacks") + pnpPowerCallbacks.EvtDeviceSelfManagedIoRestart = EchoEvtDeviceSelfManagedIoStart; + + // + // Register the PnP and power callbacks. Power policy related callbacks will be registered + // later in SotwareInit. + // + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT); + + status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device); + + if (NT_SUCCESS(status)) { + // + // Get the device context and initialize it. WdfObjectGet_DEVICE_CONTEXT is an + // inline function generated by WDF_DECLARE_CONTEXT_TYPE macro in the + // device.h header file. This function will do the type checking and return + // the device context. If you pass a wrong object handle + // it will return NULL and assert if run under framework verifier mode. + // + deviceContext = WdfObjectGet_DEVICE_CONTEXT(device); + deviceContext->PrivateDeviceData = 0; + + // + // Create a device interface so that application can find and talk + // to us. + // + status = WdfDeviceCreateDeviceInterface( + device, + &GUID_DEVINTERFACE_ECHO, + NULL // ReferenceString + ); + + if (NT_SUCCESS(status)) { + // + // Initialize the I/O Package and any Queues + // + status = EchoQueueInitialize(device); + } + } + + return status; +} + + +NTSTATUS +EchoEvtDeviceSelfManagedIoStart( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + This event is called by the Framework when the device is started + or restarted after a suspend operation. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - Failures will result in the device stack being torn down. + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(WdfDeviceGetDefaultQueue(Device)); + LARGE_INTEGER DueTime; + + KdPrint(("--> EchoEvtDeviceSelfManagedIoInit\n")); + + // + // Restart the queue and the periodic timer. We stopped them before going + // into low power state. + // + WdfIoQueueStart(WdfDeviceGetDefaultQueue(Device)); + + DueTime.QuadPart = WDF_REL_TIMEOUT_IN_MS(100); + + WdfTimerStart(queueContext->Timer, DueTime.QuadPart); + + KdPrint(( "<-- EchoEvtDeviceSelfManagedIoInit\n")); + + return STATUS_SUCCESS; +} + +NTSTATUS +EchoEvtDeviceSelfManagedIoSuspend( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + This event is called by the Framework when the device is stopped + for resource rebalance or suspended when the system is entering + Sx state. + + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - The driver is not allowed to fail this function. If it does, the + device stack will be torn down. + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(WdfDeviceGetDefaultQueue(Device)); + + PAGED_CODE(); + + KdPrint(("--> EchoEvtDeviceSelfManagedIoSuspend\n")); + + // + // Before we stop the timer we should make sure there are no outstanding + // i/o. We need to do that because framework cannot suspend the device + // if there are requests owned by the driver. There are two ways to solve + // this issue: 1) We can wait for the outstanding I/O to be complete by the + // periodic timer 2) Register EvtIoStop callback on the queue and acknowledge + // the request to inform the framework that it's okay to suspend the device + // with outstanding I/O. In this sample we will use the 1st approach + // because it's pretty easy to do. We will restart the queue when the + // device is restarted. + // + WdfIoQueueStopSynchronously(WdfDeviceGetDefaultQueue(Device)); + + // + // Stop the watchdog timer and wait for DPC to run to completion if it's already fired. + // + WdfTimerStop(queueContext->Timer, TRUE); + + KdPrint(( "<-- EchoEvtDeviceSelfManagedIoSuspend\n")); + + return STATUS_SUCCESS; +} + + + diff --git a/general/echo/kmdf/driver/AutoSync/device.h b/general/echo/kmdf/driver/AutoSync/device.h new file mode 100644 index 00000000..f29c7908 --- /dev/null +++ b/general/echo/kmdf/driver/AutoSync/device.h @@ -0,0 +1,48 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + device.h + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#include "public.h" + +// +// The device context performs the same job as +// a WDM device extension in the driver frameworks +// +typedef struct _DEVICE_CONTEXT +{ + ULONG PrivateDeviceData; // just a placeholder + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +// +// This macro will generate an inline function called WdfObjectGet_DEVICE_CONTEXT +// which will be used to get a pointer to the device context memory +// in a type safe manner. +// +WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT) + +// +// Function to initialize the device and its callbacks +// +NTSTATUS +EchoDeviceCreate( + PWDFDEVICE_INIT DeviceInit + ); + +// +// Device events +// +EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT EchoEvtDeviceSelfManagedIoStart; +EVT_WDF_DEVICE_SELF_MANAGED_IO_SUSPEND EchoEvtDeviceSelfManagedIoSuspend; + diff --git a/general/echo/kmdf/driver/AutoSync/driver.c b/general/echo/kmdf/driver/AutoSync/driver.c new file mode 100644 index 00000000..60a02692 --- /dev/null +++ b/general/echo/kmdf/driver/AutoSync/driver.c @@ -0,0 +1,202 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This driver demonstrates use of a default I/O Queue, its + request start events, cancellation event, and a synchronized DPC. + + To demonstrate asynchronous operation, the I/O requests are not completed + immediately, but stored in the drivers private data structure, and a timer + DPC will complete it next time the DPC runs. + + During the time the request is waiting for the DPC to run, it is + made cancellable by the call WdfRequestMarkCancelable. This + allows the test program to cancel the request and exit instantly. + + This rather complicated set of events is designed to demonstrate + the driver frameworks synchronization of access to a device driver + data structure, and a pointer which can be a proxy for device hardware + registers or resources. + + This common data structure, or resource is accessed by new request + events arriving, the DPC that completes it, and cancel processing. + + Notice the lack of specific lock/unlock operations. + + Even though this example utilizes a serial queue, a parallel queue + would not need any additional explicit synchronization, just a + strategy for managing multiple requests outstanding. + +--*/ + +#include "driver.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (INIT, EchoPrintDriverVersion) +#pragma alloc_text (PAGE, EchoEvtDeviceAdd) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. DriverEntry specifies the other entry + points in the function driver, such as EvtDevice and DriverUnload. + +Parameters Description: + + DriverObject - represents the instance of the function driver that is loaded + into memory. DriverEntry must initialize members of DriverObject before it + returns to the caller. DriverObject is allocated by the system before the + driver is loaded, and it is released by the system after the system unloads + the function driver from memory. + + RegistryPath - represents the driver specific path in the Registry. + The function driver can use the path to store driver related data between + reboots. The path does not store hardware instance specific data. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ +{ + WDF_DRIVER_CONFIG config; + NTSTATUS status; + + WDF_DRIVER_CONFIG_INIT(&config, + EchoEvtDeviceAdd + ); + + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + WDF_NO_HANDLE); + if (!NT_SUCCESS(status)) { + KdPrint(("Error: WdfDriverCreate failed 0x%x\n", status)); + return status; + } + +#if DBG + EchoPrintDriverVersion(); +#endif + + return status; +} + +NTSTATUS +EchoEvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + EvtDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. We create and initialize a device object to + represent a new instance of the device. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + KdPrint(("Enter EchoEvtDeviceAdd\n")); + + status = EchoDeviceCreate(DeviceInit); + + return status; +} + +NTSTATUS +EchoPrintDriverVersion( + ) +/*++ +Routine Description: + + This routine shows how to retrieve framework version string and + also how to find out to which version of framework library the + client driver is bound to. + +Arguments: + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status; + WDFSTRING string; + UNICODE_STRING us; + WDF_DRIVER_VERSION_AVAILABLE_PARAMS ver; + + // + // 1) Retreive version string and print that in the debugger. + // + status = WdfStringCreate(NULL, WDF_NO_OBJECT_ATTRIBUTES, &string); + if (!NT_SUCCESS(status)) { + KdPrint(("Error: WdfStringCreate failed 0x%x\n", status)); + return status; + } + + status = WdfDriverRetrieveVersionString(WdfGetDriver(), string); + if (!NT_SUCCESS(status)) { + // + // No need to worry about delete the string object because + // by default it's parented to the driver and it will be + // deleted when the driverobject is deleted when the DriverEntry + // returns a failure status. + // + KdPrint(("Error: WdfDriverRetrieveVersionString failed 0x%x\n", status)); + return status; + } + + WdfStringGetUnicodeString(string, &us); + KdPrint(("Echo Sample %wZ\n", &us)); + + WdfObjectDelete(string); + string = NULL; // To avoid referencing a deleted object. + + // + // 2) Find out to which version of framework this driver is bound to. + // + WDF_DRIVER_VERSION_AVAILABLE_PARAMS_INIT(&ver, 1, 0); + if (WdfDriverIsVersionAvailable(WdfGetDriver(), &ver) == TRUE) { + KdPrint(("Yes, framework version is 1.0\n")); + }else { + KdPrint(("No, framework verison is not 1.0\n")); + } + + return STATUS_SUCCESS; +} + diff --git a/general/echo/kmdf/driver/AutoSync/driver.h b/general/echo/kmdf/driver/AutoSync/driver.h new file mode 100644 index 00000000..9398ab30 --- /dev/null +++ b/general/echo/kmdf/driver/AutoSync/driver.h @@ -0,0 +1,34 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + driver.h + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#define INITGUID + +#include +#include + +#include "device.h" +#include "queue.h" + +// +// WDFDRIVER Events +// + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_DEVICE_ADD EchoEvtDeviceAdd; + +NTSTATUS +EchoPrintDriverVersion( + ); + diff --git a/general/echo/kmdf/driver/AutoSync/echo.inx b/general/echo/kmdf/driver/AutoSync/echo.inx new file mode 100644 index 00000000..fa0e4f6e --- /dev/null +++ b/general/echo/kmdf/driver/AutoSync/echo.inx @@ -0,0 +1,104 @@ +;/*++ +; +;Copyright (c) 1990-2000 Microsoft Corporation +; +;Module Name: +; ECHO.INF +; +;Abstract: +; INF file for installing the Driver Frameworks ECHO Driver +; +;Installation Notes: +; Using Devcon: Type "devcon install ECHO.inf root\ECHO" to install +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFT% +DriverVer=03/20/2003,5.00.3788 +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +ECHO.sys = 1,, + +;***************************************** +; ECHO Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%ECHO.DeviceDesc%=ECHO_Device, root\ECHO + +[ECHO_Device.NT] +CopyFiles=Drivers_Dir + +[Drivers_Dir] +ECHO.sys + + +;-------------- Service installation +[ECHO_Device.NT.Services] +AddService = ECHO,%SPSVCINST_ASSOCSERVICE%, ECHO_Service_Inst + +; -------------- ECHO driver install sections +[ECHO_Service_Inst] +DisplayName = %ECHO.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\ECHO.sys + +; +;--- ECHO_Device Coinstaller installation ------ +; + +[DestinationDirs] +ECHO_Device_CoInstaller_CopyFiles = 11 + +[ECHO_Device.NT.CoInstallers] +AddReg=ECHO_Device_CoInstaller_AddReg +CopyFiles=ECHO_Device_CoInstaller_CopyFiles + +[ECHO_Device_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[ECHO_Device_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames + +[ECHO_Device.NT.Wdf] +KmdfService = ECHO, ECHO_wdfsect +[ECHO_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +MSFT = "Microsoft" +StdMfg = "(Standard system devices)" +DiskId1 = "WDF Sample ECHO Installation Disk #1" +ECHO.DeviceDesc = "Sample WDF ECHO Driver" +ECHO.SVCDESC = "Sample WDF ECHO Service" +ClassName = "Sample Device" diff --git a/general/echo/kmdf/driver/AutoSync/echo.vcxproj b/general/echo/kmdf/driver/AutoSync/echo.vcxproj new file mode 100644 index 00000000..fcaaf0ac --- /dev/null +++ b/general/echo/kmdf/driver/AutoSync/echo.vcxproj @@ -0,0 +1,168 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {C8F9A776-3675-459B-A0A3-BA17D003C70B} + $(MSBuildProjectName) + 1 + Debug + Win32 + {8063717F-2826-44B9-BCF2-23ACCAF2C1FA} + + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + $(InfArch) + true + .\$(IntDir)\echo.inf + + + + echo + + + echo + + + echo + + + echo + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/echo/kmdf/driver/AutoSync/echo.vcxproj.Filters b/general/echo/kmdf/driver/AutoSync/echo.vcxproj.Filters new file mode 100644 index 00000000..ba649423 --- /dev/null +++ b/general/echo/kmdf/driver/AutoSync/echo.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C301082A-56B5-43D9-AF50-C90CDBB830DA} + + + h;hpp;hxx;hm;inl;inc;xsd + {E8E22922-FDB4-496F-9D8B-0E5930795BAC} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {78CA938F-ABD5-4BA1-A021-7E751E09B367} + + + inf;inv;inx;mof;mc; + {A5E77DC1-258D-4E61-AA8D-1879410AE785} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/echo/kmdf/driver/AutoSync/queue.c b/general/echo/kmdf/driver/AutoSync/queue.c new file mode 100644 index 00000000..cb02a965 --- /dev/null +++ b/general/echo/kmdf/driver/AutoSync/queue.c @@ -0,0 +1,532 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + queue.c + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#include "driver.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, EchoQueueInitialize) +#pragma alloc_text (PAGE, EchoTimerCreate) +#endif + +NTSTATUS +EchoQueueInitialize( + WDFDEVICE Device + ) +/*++ + +Routine Description: + + + The I/O dispatch callbacks for the frameworks device object + are configured in this function. + + A single default I/O Queue is configured for serial request + processing, and a driver context memory allocation is created + to hold our structure QUEUE_CONTEXT. + + This memory may be used by the driver automatically synchronized + by the Queue's presentation lock. + + The lifetime of this memory is tied to the lifetime of the I/O + Queue object, and we register an optional destructor callback + to release any private allocations, and/or resources. + + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS + +--*/ +{ + WDFQUEUE queue; + NTSTATUS status; + PQUEUE_CONTEXT queueContext; + WDF_IO_QUEUE_CONFIG queueConfig; + WDF_OBJECT_ATTRIBUTES queueAttributes; + + PAGED_CODE(); + + // + // Configure a default queue so that requests that are not + // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto + // other queues get dispatched here. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( + &queueConfig, + WdfIoQueueDispatchSequential + ); + + queueConfig.EvtIoRead = EchoEvtIoRead; + queueConfig.EvtIoWrite = EchoEvtIoWrite; + + // + // Fill in a callback for destroy, and our QUEUE_CONTEXT size + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_CONTEXT); + + // + // Set synchronization scope on queue and have the timer to use queue as + // the parent object so that queue and timer callbacks are synchronized + // with the same lock. + // + queueAttributes.SynchronizationScope = WdfSynchronizationScopeQueue; + + queueAttributes.EvtDestroyCallback = EchoEvtIoQueueContextDestroy; + + status = WdfIoQueueCreate( + Device, + &queueConfig, + &queueAttributes, + &queue + ); + + if( !NT_SUCCESS(status) ) { + KdPrint(("WdfIoQueueCreate failed 0x%x\n",status)); + return status; + } + + // Get our Driver Context memory from the returned Queue handle + queueContext = QueueGetContext(queue); + + queueContext->Buffer = NULL; + queueContext->Timer = NULL; + + queueContext->CurrentRequest = NULL; + queueContext->CurrentStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // Create the Queue timer + // + status = EchoTimerCreate(&queueContext->Timer, TIMER_PERIOD, queue); + if (!NT_SUCCESS(status)) { + KdPrint(("Error creating timer 0x%x\n",status)); + return status; + } + + return status; +} + + +NTSTATUS +EchoTimerCreate( + IN WDFTIMER* Timer, + IN ULONG Period, + IN WDFQUEUE Queue + ) +/*++ + +Routine Description: + + Subroutine to create periodic timer. By associating the timerobject with + the queue, we are basically telling the framework to serialize the queue + callbacks with the dpc callback. By doing so, we don't have to worry + about protecting queue-context structure from multiple threads accessing + it simultaneously. + +Arguments: + + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status; + WDF_TIMER_CONFIG timerConfig; + WDF_OBJECT_ATTRIBUTES timerAttributes; + + PAGED_CODE(); + + // + // Create a WDFTIMER object + // + WDF_TIMER_CONFIG_INIT_PERIODIC(&timerConfig, EchoEvtTimerFunc, Period); + + timerConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes); + timerAttributes.ParentObject = Queue; // Synchronize with the I/O Queue + + Status = WdfTimerCreate(&timerConfig, + &timerAttributes, + Timer // Output handle + ); + + return Status; +} + + + +VOID +EchoEvtIoQueueContextDestroy( + WDFOBJECT Object +) +/*++ + +Routine Description: + + This is called when the Queue that our driver context memory + is associated with is destroyed. + +Arguments: + + Context - Context that's being freed. + +Return Value: + + VOID + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(Object); + + // + // Release any resources pointed to in the queue context. + // + // The body of the queue context will be released after + // this callback handler returns + // + + // + // If Queue context has an I/O buffer, release it + // + if( queueContext->Buffer != NULL ) { + ExFreePool(queueContext->Buffer); + } + + return; +} + + +VOID +EchoEvtRequestCancel( + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + + Called when an I/O request is cancelled after the driver has marked + the request cancellable. This callback is automatically synchronized + with the I/O callbacks since we have chosen to use frameworks Device + level locking. + +Arguments: + + Request - Request being cancelled. + +Return Value: + + VOID + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(WdfRequestGetIoQueue(Request)); + + KdPrint(("EchoEvtRequestCancel called on Request 0x%p\n", Request)); + + // + // The following is race free by the callside or DPC side + // synchronizing completion by calling + // WdfRequestMarkCancelable(Queue, Request, FALSE) before + // completion and not calling WdfRequestComplete if the + // return status == STATUS_CANCELLED. + // + WdfRequestCompleteWithInformation(Request, STATUS_CANCELLED, 0L); + + // + // This book keeping is synchronized by the common + // Queue presentation lock + // + ASSERT(queueContext->CurrentRequest == Request); + queueContext->CurrentRequest = NULL; + + return; +} + +VOID +EchoEvtIoRead( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + This event is called when the framework receives IRP_MJ_READ request. + It will copy the content from the queue-context buffer to the request buffer. + If the driver hasn't received any write request earlier, the read returns zero. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + + Request - Handle to a framework request object. + + Length - number of bytes to be read. + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS Status; + PQUEUE_CONTEXT queueContext = QueueGetContext(Queue); + WDFMEMORY memory; + + _Analysis_assume_(Length > 0); + + KdPrint(("EchoEvtIoRead Called! Queue 0x%p, Request 0x%p Length %d\n", + Queue,Request,Length)); + // + // No data to read + // + if( (queueContext->Buffer == NULL) ) { + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, (ULONG_PTR)0L); + return; + } + _Analysis_assume_(queueContext->Length > 0); + + // + // Read what we have + // + if( queueContext->Length < Length ) { + Length = queueContext->Length; + } + + // + // Get the request memory + // + Status = WdfRequestRetrieveOutputMemory(Request, &memory); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoRead Could not get request memory buffer 0x%x\n", Status)); + WdfVerifierDbgBreakPoint(); + WdfRequestCompleteWithInformation(Request, Status, 0L); + return; + } + + // Copy the memory out + Status = WdfMemoryCopyFromBuffer( memory, // destination + 0, // offset into the destination memory + queueContext->Buffer, + Length ); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoRead: WdfMemoryCopyFromBuffer failed 0x%x\n", Status)); + WdfRequestComplete(Request, Status); + return; + } + + // Set transfer information + WdfRequestSetInformation(Request, (ULONG_PTR)Length); + + // Mark the request is cancelable + WdfRequestMarkCancelable(Request, EchoEvtRequestCancel); + + + // Defer the completion to another thread from the timer dpc + queueContext->CurrentRequest = Request; + queueContext->CurrentStatus = Status; + + return; +} + +VOID +EchoEvtIoWrite( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + This event is invoked when the framework receives IRP_MJ_WRITE request. + This routine allocates memory buffer, copies the data from the request to it, + and stores the buffer pointer in the queue-context with the length variable + representing the buffers length. The actual completion of the request + is defered to the periodic timer dpc. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + + Request - Handle to a framework request object. + + Length - number of bytes to be read. + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS Status; + WDFMEMORY memory; + PQUEUE_CONTEXT queueContext = QueueGetContext(Queue); + + _Analysis_assume_(Length > 0); + + KdPrint(("EchoEvtIoWrite Called! Queue 0x%p, Request 0x%p Length %d\n", + Queue,Request,Length)); + + if( Length > MAX_WRITE_LENGTH ) { + KdPrint(("EchoEvtIoWrite Buffer Length to big %d, Max is %d\n", + Length,MAX_WRITE_LENGTH)); + WdfRequestCompleteWithInformation(Request, STATUS_BUFFER_OVERFLOW, 0L); + return; + } + + // Get the memory buffer + Status = WdfRequestRetrieveInputMemory(Request, &memory); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoWrite Could not get request memory buffer 0x%x\n", + Status)); + WdfVerifierDbgBreakPoint(); + WdfRequestComplete(Request, Status); + return; + } + + // Release previous buffer if set + if( queueContext->Buffer != NULL ) { + ExFreePool(queueContext->Buffer); + queueContext->Buffer = NULL; + queueContext->Length = 0L; + } + + queueContext->Buffer = ExAllocatePoolWithTag(NonPagedPool, Length, 'sam1'); + if( queueContext->Buffer == NULL ) { + KdPrint(("EchoEvtIoWrite: Could not allocate %d byte buffer\n", Length)); + WdfRequestComplete(Request, STATUS_INSUFFICIENT_RESOURCES); + return; + } + + + // Copy the memory in + Status = WdfMemoryCopyToBuffer( memory, + 0, // offset into the source memory + queueContext->Buffer, + Length ); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoWrite WdfMemoryCopyToBuffer failed 0x%x\n", Status)); + WdfVerifierDbgBreakPoint(); + + ExFreePool(queueContext->Buffer); + queueContext->Buffer = NULL; + queueContext->Length = 0L; + + WdfRequestComplete(Request, Status); + return; + } + + + queueContext->Length = (ULONG) Length; + + // Set transfer information + WdfRequestSetInformation(Request, (ULONG_PTR)Length); + + // Specify the request is cancelable + WdfRequestMarkCancelable(Request, EchoEvtRequestCancel); + + // Defer the completion to another thread from the timer dpc + queueContext->CurrentRequest = Request; + queueContext->CurrentStatus = Status; + + return; +} + + +VOID +EchoEvtTimerFunc( + IN WDFTIMER Timer + ) +/*++ + +Routine Description: + + This is the TimerDPC the driver sets up to complete requests. + This function is registered when the WDFTIMER object is created, and + will automatically synchronize with the I/O Queue callbacks + and cancel routine. + +Arguments: + + Timer - Handle to a framework Timer object. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS Status; + WDFREQUEST Request; + WDFQUEUE queue; + PQUEUE_CONTEXT queueContext ; + + queue = WdfTimerGetParentObject(Timer); + queueContext = QueueGetContext(queue); + + // + // DPC is automatically synchronized to the Queue lock, + // so this is race free without explicit driver managed locking. + // + Request = queueContext->CurrentRequest; + if( Request != NULL ) { + + // + // Attempt to remove cancel status from the request. + // + // The request is not completed if it is already cancelled + // since the EchoEvtIoCancel function has run, or is about to run + // and we are racing with it. + // + Status = WdfRequestUnmarkCancelable(Request); + if( Status != STATUS_CANCELLED ) { + + queueContext->CurrentRequest = NULL; + Status = queueContext->CurrentStatus; + + KdPrint(("CustomTimerDPC Completing request 0x%p, Status 0x%x \n", Request,Status)); + + WdfRequestComplete(Request, Status); + } + else { + KdPrint(("CustomTimerDPC Request 0x%p is STATUS_CANCELLED, not completing\n", + Request)); + } + } + + return; +} + + diff --git a/general/echo/kmdf/driver/AutoSync/queue.h b/general/echo/kmdf/driver/AutoSync/queue.h new file mode 100644 index 00000000..a20e0375 --- /dev/null +++ b/general/echo/kmdf/driver/AutoSync/queue.h @@ -0,0 +1,64 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + queue.h + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +// Set max write length for testing +#define MAX_WRITE_LENGTH 1024*40 + +// Set timer period in ms +#define TIMER_PERIOD 1000*2 + +// +// This is the context that can be placed per queue +// and would contain per queue information. +// +typedef struct _QUEUE_CONTEXT { + + // Here we allocate a buffer from a test write so it can be read back + PVOID Buffer; + ULONG Length; + + // Timer DPC for this queue + WDFTIMER Timer; + + // Virtual I/O + WDFREQUEST CurrentRequest; + NTSTATUS CurrentStatus; + +} QUEUE_CONTEXT, *PQUEUE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext) + +NTSTATUS +EchoQueueInitialize( + WDFDEVICE hDevice + ); + +EVT_WDF_IO_QUEUE_CONTEXT_DESTROY_CALLBACK EchoEvtIoQueueContextDestroy; + +// +// Events from the IoQueue object +// +EVT_WDF_REQUEST_CANCEL EchoEvtRequestCancel; +EVT_WDF_IO_QUEUE_IO_READ EchoEvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE EchoEvtIoWrite; + +NTSTATUS +EchoTimerCreate( + IN WDFTIMER* pTimer, + IN ULONG Period, + IN WDFQUEUE Queue + ); + +EVT_WDF_TIMER EchoEvtTimerFunc; diff --git a/general/echo/kmdf/driver/DriverSync/device.c b/general/echo/kmdf/driver/DriverSync/device.c new file mode 100644 index 00000000..eb37eaf2 --- /dev/null +++ b/general/echo/kmdf/driver/DriverSync/device.c @@ -0,0 +1,223 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + device.c - Device handling events for example driver. + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#include "driver.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, EchoDeviceCreate) +#pragma alloc_text (PAGE, EchoEvtDeviceSelfManagedIoSuspend) +#endif + + +NTSTATUS +EchoDeviceCreate( + PWDFDEVICE_INIT DeviceInit + ) +/*++ + +Routine Description: + + Worker routine called to create a device and its software resources. + +Arguments: + + DeviceInit - Pointer to an opaque init structure. Memory for this + structure will be freed by the framework when the WdfDeviceCreate + succeeds. So don't access the structure after that point. + +Return Value: + + NTSTATUS + +--*/ +{ + WDF_OBJECT_ATTRIBUTES attributes; + PDEVICE_CONTEXT deviceContext; + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDFDEVICE device; + NTSTATUS status; + + PAGED_CODE(); + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); + + // + // Register pnp/power callbacks so that we can start and stop the timer as the device + // gets started and stopped. + // + pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = EchoEvtDeviceSelfManagedIoStart; + pnpPowerCallbacks.EvtDeviceSelfManagedIoSuspend = EchoEvtDeviceSelfManagedIoSuspend; + #pragma prefast(suppress: 28024, "Function used for both Init and Restart Callbacks") + pnpPowerCallbacks.EvtDeviceSelfManagedIoRestart = EchoEvtDeviceSelfManagedIoStart; + + // + // Register the PnP and power callbacks. Power policy related callbacks will be registered + // later in SotwareInit. + // + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT); + WdfDeviceInitSetRequestAttributes(DeviceInit, &attributes); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT); + + // + // By not setting the synchronization scope and using the default, there is + // no locking between any of the callbacks in this driver. + // + // We will create a sequential queue so all of the EvtIoXxx callbacks are + // serialized against each other (at least until the request is completed), + // but the cancel routine and the timer DPC are not synchronized against the + // queue's EvtIoXxx callbacks. + // + // attributes.SynchronizationScope = ... + + status = WdfDeviceCreate(&DeviceInit, &attributes, &device); + + if (NT_SUCCESS(status)) { + // + // Get the device context and initialize it. WdfObjectGet_DEVICE_CONTEXT is an + // inline function generated by WDF_DECLARE_CONTEXT_TYPE macro in the + // device.h header file. This function will do the type checking and return + // the device context. If you pass a wrong object handle + // it will return NULL and assert if run under framework verifier mode. + // + deviceContext = WdfObjectGet_DEVICE_CONTEXT(device); + deviceContext->PrivateDeviceData = 0; + + // + // Create a device interface so that application can find and talk + // to us. + // + status = WdfDeviceCreateDeviceInterface( + device, + &GUID_DEVINTERFACE_ECHO, + NULL // ReferenceString + ); + + if (NT_SUCCESS(status)) { + // + // Initialize the I/O Package and any Queues + // + status = EchoQueueInitialize(device); + } + } + + return status; +} + + +NTSTATUS +EchoEvtDeviceSelfManagedIoStart( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + This event is called by the Framework when the device is started + or restarted after a suspend operation. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - Failures will result in the device stack being torn down. + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(WdfDeviceGetDefaultQueue(Device)); + LARGE_INTEGER DueTime; + + KdPrint(("--> EchoEvtDeviceSelfManagedIoInit\n")); + + // + // Restart the queue and the periodic timer. We stopped them before going + // into low power state. + // + WdfIoQueueStart(WdfDeviceGetDefaultQueue(Device)); + + DueTime.QuadPart = WDF_REL_TIMEOUT_IN_MS(100); + + WdfTimerStart(queueContext->Timer, DueTime.QuadPart); + + KdPrint(( "<-- EchoEvtDeviceSelfManagedIoInit\n")); + + return STATUS_SUCCESS; +} + +NTSTATUS +EchoEvtDeviceSelfManagedIoSuspend( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + This event is called by the Framework when the device is stopped + for resource rebalance or suspended when the system is entering + Sx state. + + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - The driver is not allowed to fail this function. If it does, the + device stack will be torn down. + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(WdfDeviceGetDefaultQueue(Device)); + + PAGED_CODE(); + + KdPrint(("--> EchoEvtDeviceSelfManagedIoSuspend\n")); + + // + // Before we stop the timer we should make sure there are no outstanding + // i/o. We need to do that because framework cannot suspend the device + // if there are requests owned by the driver. There are two ways to solve + // this issue: 1) We can wait for the outstanding I/O to be complete by the + // periodic timer 2) Register EvtIoStop callback on the queue and acknowledge + // the request to inform the framework that it's okay to suspend the device + // with outstanding I/O. In this sample we will use the 1st approach + // because it's pretty easy to do. We will restart the queue when the + // device is restarted. + // + WdfIoQueueStopSynchronously(WdfDeviceGetDefaultQueue(Device)); + + // + // Stop the watchdog timer and wait for DPC to run to completion if it's already fired. + // + WdfTimerStop(queueContext->Timer, TRUE); + + KdPrint(( "<-- EchoEvtDeviceSelfManagedIoSuspend\n")); + + return STATUS_SUCCESS; +} + + + diff --git a/general/echo/kmdf/driver/DriverSync/device.h b/general/echo/kmdf/driver/DriverSync/device.h new file mode 100644 index 00000000..f29c7908 --- /dev/null +++ b/general/echo/kmdf/driver/DriverSync/device.h @@ -0,0 +1,48 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + device.h + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#include "public.h" + +// +// The device context performs the same job as +// a WDM device extension in the driver frameworks +// +typedef struct _DEVICE_CONTEXT +{ + ULONG PrivateDeviceData; // just a placeholder + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +// +// This macro will generate an inline function called WdfObjectGet_DEVICE_CONTEXT +// which will be used to get a pointer to the device context memory +// in a type safe manner. +// +WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT) + +// +// Function to initialize the device and its callbacks +// +NTSTATUS +EchoDeviceCreate( + PWDFDEVICE_INIT DeviceInit + ); + +// +// Device events +// +EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT EchoEvtDeviceSelfManagedIoStart; +EVT_WDF_DEVICE_SELF_MANAGED_IO_SUSPEND EchoEvtDeviceSelfManagedIoSuspend; + diff --git a/general/echo/kmdf/driver/DriverSync/driver.c b/general/echo/kmdf/driver/DriverSync/driver.c new file mode 100644 index 00000000..f1e216f2 --- /dev/null +++ b/general/echo/kmdf/driver/DriverSync/driver.c @@ -0,0 +1,201 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This driver demonstrates use of a default I/O Queue, its + request start events, cancellation event, and a synchronized DPC. + + To demonstrate asynchronous operation, the I/O requests are not completed + immediately, but stored in the drivers private data structure, and a timer + DPC will complete it next time the DPC runs. + + During the time the request is waiting for the DPC to run, it is + made cancellable by the call WdfRequestMarkCancelable. This + allows the test program to cancel the request and exit instantly. + + This rather complicated set of events is designed to demonstrate + the driver frameworks synchronization of access to a device driver + data structure, and a pointer which can be a proxy for device hardware + registers or resources. + + This common data structure, or resource is accessed by new request + events arriving, the DPC that completes it, and cancel processing. + + Notice the lack of specific lock/unlock operations. + + Even though this example utilizes a serial queue, a parallel queue + would not need any additional explicit synchronization, just a + strategy for managing multiple requests outstanding. + +--*/ + +#include "driver.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (INIT, EchoPrintDriverVersion) +#pragma alloc_text (PAGE, EchoEvtDeviceAdd) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. DriverEntry specifies the other entry + points in the function driver, such as EvtDevice and DriverUnload. + +Parameters Description: + + DriverObject - represents the instance of the function driver that is loaded + into memory. DriverEntry must initialize members of DriverObject before it + returns to the caller. DriverObject is allocated by the system before the + driver is loaded, and it is released by the system after the system unloads + the function driver from memory. + + RegistryPath - represents the driver specific path in the Registry. + The function driver can use the path to store driver related data between + reboots. The path does not store hardware instance specific data. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ +{ + WDF_DRIVER_CONFIG config; + NTSTATUS status; + + WDF_DRIVER_CONFIG_INIT(&config, + EchoEvtDeviceAdd + ); + + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + WDF_NO_HANDLE); + if (!NT_SUCCESS(status)) { + KdPrint(("Error: WdfDriverCreate failed 0x%x\n", status)); + return status; + } + +#if DBG + EchoPrintDriverVersion(); +#endif + + return status; +} + +NTSTATUS +EchoEvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + EvtDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. We create and initialize a device object to + represent a new instance of the device. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + KdPrint(("Enter EchoEvtDeviceAdd\n")); + + status = EchoDeviceCreate(DeviceInit); + + return status; +} + +NTSTATUS +EchoPrintDriverVersion( + ) +/*++ +Routine Description: + + This routine shows how to retrieve framework version string and + also how to find out to which version of framework library the + client driver is bound to. + +Arguments: + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status; + WDFSTRING string; + UNICODE_STRING us; + WDF_DRIVER_VERSION_AVAILABLE_PARAMS ver; + + // + // 1) Retreive version string and print that in the debugger. + // + status = WdfStringCreate(NULL, WDF_NO_OBJECT_ATTRIBUTES, &string); + if (!NT_SUCCESS(status)) { + KdPrint(("Error: WdfStringCreate failed 0x%x\n", status)); + return status; + } + + status = WdfDriverRetrieveVersionString(WdfGetDriver(), string); + if (!NT_SUCCESS(status)) { + // + // No need to worry about delete the string object because + // by default it's parented to the driver and it will be + // deleted when the driverobject is deleted when the DriverEntry + // returns a failure status. + // + KdPrint(("Error: WdfDriverRetrieveVersionString failed 0x%x\n", status)); + return status; + } + + WdfStringGetUnicodeString(string, &us); + KdPrint(("Echo Sample %wZ\n", &us)); + + WdfObjectDelete(string); + string = NULL; // To avoid referencing a deleted object. + + // + // 2) Find out to which version of framework this driver is bound to. + // + WDF_DRIVER_VERSION_AVAILABLE_PARAMS_INIT(&ver, 1, 0); + if (WdfDriverIsVersionAvailable(WdfGetDriver(), &ver) == TRUE) { + KdPrint(("Yes, framework version is 1.0\n")); + }else { + KdPrint(("No, framework verison is not 1.0\n")); + } + + return STATUS_SUCCESS; +} + diff --git a/general/echo/kmdf/driver/DriverSync/driver.h b/general/echo/kmdf/driver/DriverSync/driver.h new file mode 100644 index 00000000..5bf8853f --- /dev/null +++ b/general/echo/kmdf/driver/DriverSync/driver.h @@ -0,0 +1,48 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + driver.h + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#define INITGUID + +#include +#include + +#include "device.h" +#include "queue.h" + +typedef struct _REQUEST_CONTEXT { + // + // Count to use when trying to claim completion ownership of a cancelable + // request when clearing the cancel routine. If the caller can clear the + // cancel routine successfully, the caller is *NOT* responsible for decrementing + // the count if the request is going to be completed immediately (and a + // cancel routine is not going to be set in the future). + // + LONG CancelCompletionOwnershipCount; + +} REQUEST_CONTEXT, *PREQUEST_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REQUEST_CONTEXT, RequestGetContext); + +// +// WDFDRIVER Events +// + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_DEVICE_ADD EchoEvtDeviceAdd; + +NTSTATUS +EchoPrintDriverVersion( + ); + diff --git a/general/echo/kmdf/driver/DriverSync/echo_2.inx b/general/echo/kmdf/driver/DriverSync/echo_2.inx new file mode 100644 index 00000000..af09757f --- /dev/null +++ b/general/echo/kmdf/driver/DriverSync/echo_2.inx @@ -0,0 +1,105 @@ +;/*++ +; +;Copyright (c) 1990-2000 Microsoft Corporation +; +;Module Name: +; ECHO_2.INF +; +;Abstract: +; INF file for installing the Driver Frameworks ECHO Driver (DriverSync version) +; +;Installation Notes: +; Using Devcon: Type "devcon install ECHO_2.inf root\ECHO_2" to install +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFT% +DriverVer=03/20/2003,5.00.3788 +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +ECHO_2.sys = 1,, + +;***************************************** +; ECHO Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%ECHO.DeviceDesc%=ECHO_Device, root\ECHO_2 + +[ECHO_Device.NT] +CopyFiles=Drivers_Dir + +[Drivers_Dir] +ECHO_2.sys + + +;-------------- Service installation +[ECHO_Device.NT.Services] +AddService = ECHO_2,%SPSVCINST_ASSOCSERVICE%, ECHO_Service_Inst + +; -------------- ECHO driver install sections +[ECHO_Service_Inst] +DisplayName = %ECHO.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\ECHO_2.sys + +; +;--- ECHO_Device Coinstaller installation ------ +; + +[DestinationDirs] +ECHO_Device_CoInstaller_CopyFiles = 11 + +[ECHO_Device.NT.CoInstallers] +AddReg=ECHO_Device_CoInstaller_AddReg +CopyFiles=ECHO_Device_CoInstaller_CopyFiles + +[ECHO_Device_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[ECHO_Device_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames + +[ECHO_Device.NT.Wdf] +KmdfService = ECHO_2, ECHO_wdfsect + +[ECHO_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +MSFT = "Microsoft" +StdMfg = "(Standard system devices)" +DiskId1 = "WDF Sample ECHO Installation Disk #1 (DriverSync)" +ECHO.DeviceDesc = "Sample WDF ECHO Driver (DriverSync)" +ECHO.SVCDESC = "Sample WDF ECHO Service (DriverSync)" +ClassName = "Sample Device" diff --git a/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj b/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj new file mode 100644 index 00000000..a96b9b73 --- /dev/null +++ b/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {968447B1-9A4F-4D2D-B81B-7BCB9240F5E3} + $(MSBuildProjectName) + 1 + Debug + Win32 + {0F2B6094-D739-411B-B15C-D0ABD3ACF20E} + + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + $(InfArch) + true + .\$(IntDir)\echo_2.inf + + + + echo_2 + + + echo_2 + + + echo_2 + + + echo_2 + + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\exe + %(AdditionalIncludeDirectories);..\..\inc + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj.Filters b/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj.Filters new file mode 100644 index 00000000..2fac5833 --- /dev/null +++ b/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {5AF6C435-D6C0-4812-A1B9-A231D60FF6A3} + + + h;hpp;hxx;hm;inl;inc;xsd + {52F80479-DCAD-49B9-9A2F-BB7363EEEBAF} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {1EC49F54-AC4E-447D-B4EA-B9859C16E917} + + + inf;inv;inx;mof;mc; + {9CFF80B0-15ED-408B-B629-91208E5966C3} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/echo/kmdf/driver/DriverSync/queue.c b/general/echo/kmdf/driver/DriverSync/queue.c new file mode 100644 index 00000000..89d47fa2 --- /dev/null +++ b/general/echo/kmdf/driver/DriverSync/queue.c @@ -0,0 +1,816 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + queue.c + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#include "driver.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, EchoQueueInitialize) +#pragma alloc_text (PAGE, EchoTimerCreate) +#endif + +LONG +EchoInterlockedIncrementFloor( + LONG volatile *Target, + LONG Floor + ) +/*++ + +Routine Description: + This routine will interlock increment a value only if the current value + is greater then the floor value. + + The volatile keyword on the Target pointer is absolutely required, otherwise + the compiler might rearrange pointer dereferences and that cannot happen. + +Arguments: + Target - the value that will be pontetially incrmented + + Floor - the value in which the Target value must be greater then if it is + to be incremented + +Return Value: + The current value of Target. To detect failure, the return value will be + <= Floor + 1. It is +1 because we cannot increment from the Floor value + itself, so Floor+1 cannot be a successful return value. + + --*/ +{ + LONG oldValue, currentValue; + + currentValue = *Target; + + do { + if (currentValue <= Floor) { + return currentValue; + } + + oldValue = currentValue; + + // + // currentValue will be the value that used to be Target if the exchange + // was made or its current value if the exchange was not made. + // + currentValue = InterlockedCompareExchange(Target, oldValue + 1, oldValue); + + // + // If oldValue == currentValue, then no one updated Target in between + // the deref at the top and the InterlockecCompareExchange afterward + // and we have successfully incremented the value and can exit the loop. + // + } while (oldValue != currentValue); + + // + // Since InterlockedIncrement returns the new incremented value of Target, + // we should do the same here. + // + return oldValue + 1; +} + +FORCEINLINE +LONG +EchoInterlockedIncrementGTZero( + IN OUT LONG volatile *Target + ) +/*++ + +Routine Description: + Increment the value only if it is currently > 0. + +Arguments: + Target - the value to be incremented. NOTE: the volatile keyword is requreid + +Return Value: + Upon success, a value > 0. Upon failure, a value <= 0. + + --*/ +{ + return EchoInterlockedIncrementFloor(Target, 0); +} + +NTSTATUS +EchoQueueInitialize( + WDFDEVICE Device + ) +/*++ + +Routine Description: + + + The I/O dispatch callbacks for the frameworks device object + are configured in this function. + + A single default I/O Queue is configured for serial request + processing, and a driver context memory allocation is created + to hold our structure QUEUE_CONTEXT. + + This memory may be used by the driver automatically synchronized + by the Queue's presentation lock. + + The lifetime of this memory is tied to the lifetime of the I/O + Queue object, and we register an optional destructor callback + to release any private allocations, and/or resources. + + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS + +--*/ +{ + WDFQUEUE queue; + NTSTATUS status; + PQUEUE_CONTEXT queueContext; + WDF_IO_QUEUE_CONFIG queueConfig; + WDF_OBJECT_ATTRIBUTES attributes; + + PAGED_CODE(); + + // + // Configure a default queue so that requests that are not + // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto + // other queues get dispatched here. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( + &queueConfig, + WdfIoQueueDispatchSequential + ); + + queueConfig.EvtIoRead = EchoEvtIoRead; + queueConfig.EvtIoWrite = EchoEvtIoWrite; + + // + // Fill in a callback for destroy, and our QUEUE_CONTEXT size + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, QUEUE_CONTEXT); + attributes.EvtDestroyCallback = EchoEvtIoQueueContextDestroy; + + status = WdfIoQueueCreate( + Device, + &queueConfig, + &attributes, + &queue + ); + + if( !NT_SUCCESS(status) ) { + KdPrint(("WdfIoQueueCreate failed 0x%x\n",status)); + return status; + } + + // Get our Driver Context memory from the returned Queue handle + queueContext = QueueGetContext(queue); + + queueContext->Buffer = NULL; + queueContext->Timer = NULL; + + queueContext->CurrentRequest = NULL; + queueContext->CurrentStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // Create the SpinLock. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = queue; + + status = WdfSpinLockCreate(&attributes, &queueContext->SpinLock); + if (!NT_SUCCESS(status)) { + KdPrint(("WdfSpinLockCreate failed 0x%x\n",status)); + return status; + } + + // + // Create the Queue timer + // + status = EchoTimerCreate(&queueContext->Timer, TIMER_PERIOD, queue); + if (!NT_SUCCESS(status)) { + KdPrint(("Error creating timer 0x%x\n",status)); + return status; + } + + return status; +} + + +NTSTATUS +EchoTimerCreate( + IN WDFTIMER* Timer, + IN ULONG Period, + IN WDFQUEUE Queue + ) +/*++ + +Routine Description: + + Subroutine to create periodic timer. + +Arguments: + + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status; + WDF_TIMER_CONFIG timerConfig; + WDF_OBJECT_ATTRIBUTES timerAttributes; + + PAGED_CODE(); + + // + // Create a WDFTIMER object + // + WDF_TIMER_CONFIG_INIT_PERIODIC(&timerConfig, EchoEvtTimerFunc, Period); + + WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes); + + // + // We are explicitly *not* serializing against the queue's lock, we will do + // that on our own. + // + timerAttributes.ParentObject = Queue; + + Status = WdfTimerCreate( + &timerConfig, + &timerAttributes, + Timer // Output handle + ); + + return Status; +} + + + +VOID +EchoEvtIoQueueContextDestroy( + WDFOBJECT Object +) +/*++ + +Routine Description: + + This is called when the Queue that our driver context memory + is associated with is destroyed. + +Arguments: + + Context - Context that's being freed. + +Return Value: + + VOID + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(Object); + + // + // Release any resources pointed to in the queue context. + // + // The body of the queue context will be released after + // this callback handler returns + // + + // + // If Queue context has an I/O buffer, release it + // + if( queueContext->Buffer != NULL ) { + ExFreePool(queueContext->Buffer); + queueContext->Buffer = NULL; + } + + return; +} + +BOOLEAN +EchoDecrementRequestCancelOwnershipCount( + PREQUEST_CONTEXT RequestContext + ) +/*++ + +Routine Description: + Decrements the cancel ownership count for the request. When the count + reaches zero ownership has been acquired. + +Arguments: + RequestContext - the context which holds the count + +Return Value: + TRUE if the caller can complete the request, FALSE otherwise + + --*/ +{ + LONG result; + + result = InterlockedDecrement( + &RequestContext->CancelCompletionOwnershipCount + ); + + ASSERT(result >= 0); + + if (result == 0) { + return TRUE; + } + else { + return FALSE; + } +} + +BOOLEAN +EchoIncrementRequestCancelOwnershipCount( + PREQUEST_CONTEXT RequestContext + ) +/*++ + +Routine Description: + Attempts to increment the request ownership count so that it cannot be + completed until the count has been decremented + +Arguments: + RequestContext - context which holds the count + +Return Value: + TRUE if the count was incremented, FALSE otherwise + + --*/ +{ + // + // See comments in EchoInterlockedIncrementFloor as to why <= 1 is failure + // + if (EchoInterlockedIncrementGTZero( + &RequestContext->CancelCompletionOwnershipCount + ) <= 1) { + return FALSE; + } + else { + return TRUE; + } +} + +VOID +EchoEvtRequestCancel( + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + + Called when an I/O request is cancelled after the driver has marked + the request cancellable. This callback is not automatically synchronized + with the I/O callbacks since we have chosen not to use frameworks Device + or Queue level locking. + +Arguments: + + Request - Request being cancelled. + +Return Value: + + VOID + +--*/ +{ + PQUEUE_CONTEXT queueContext; + PREQUEST_CONTEXT requestContext; + WDFQUEUE queue; + BOOLEAN completeRequest; + + KdPrint(("EchoEvtRequestCancel called on Request 0x%p\n", Request)); + + queue = WdfRequestGetIoQueue(Request); + + requestContext = RequestGetContext(Request); + queueContext = QueueGetContext(queue); + + // + // This book keeping is synchronized by the common + // Queue presentation lock which we are now acquiring + // + WdfSpinLockAcquire(queueContext->SpinLock); + + completeRequest = EchoDecrementRequestCancelOwnershipCount(requestContext); + + if (completeRequest) { + ASSERT(queueContext->CurrentRequest == Request); + queueContext->CurrentRequest = NULL; + } + else { + queueContext->CurrentStatus = STATUS_CANCELLED; + } + + WdfSpinLockRelease(queueContext->SpinLock); + + // + // Complete the request outside of holding any locks + // + if (completeRequest) { + WdfRequestCompleteWithInformation(Request, STATUS_CANCELLED, 0L); + } + + return; +} + +VOID +EchoSetCurrentRequest( + WDFREQUEST Request, + WDFQUEUE Queue + ) +{ + NTSTATUS status; + PQUEUE_CONTEXT queueContext; + PREQUEST_CONTEXT requestContext; + + requestContext = RequestGetContext(Request); + queueContext = QueueGetContext(Queue); + + // + // Set the ownership count to one. When a caller wants to claim ownership, + // they will interlock decrement the count. When the count reaches zero, + // ownership has been acquired and the caller may complete the request. + // + requestContext->CancelCompletionOwnershipCount = 1; + + // + // Defer the completion to another thread from the timer dpc + // + WdfSpinLockAcquire(queueContext->SpinLock); + + queueContext->CurrentRequest = Request; + queueContext->CurrentStatus = STATUS_SUCCESS; + + // + // Set the cancel routine under the lock, otherwise if we set it outside + // of the lock, the timer could run and attempt to mark the request + // uncancelable before we can mark it cancelable on this thread. Use + // WdfRequestMarkCancelableEx here to prevent to deadlock with ourselves + // (cancel routine tries to acquire the queue object lock). + // + status = WdfRequestMarkCancelableEx(Request, EchoEvtRequestCancel); + if (!NT_SUCCESS(status)) { + queueContext->CurrentRequest = NULL; + } + + WdfSpinLockRelease(queueContext->SpinLock); + + // + // Complete the request with an error when unable to mark it cancelable. + // + if (!NT_SUCCESS(status)) { + WdfRequestCompleteWithInformation(Request, status, 0L); + } +} + +VOID +EchoEvtIoRead( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + This event is called when the framework receives IRP_MJ_READ request. + It will copy the content from the queue-context buffer to the request buffer. + If the driver hasn't received any write request earlier, the read returns zero. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + Request - Handle to a framework request object. + + Length - number of bytes to be read. + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + + +Return Value: + + VOID + +--*/ +{ + NTSTATUS Status; + PQUEUE_CONTEXT queueContext = QueueGetContext(Queue); + WDFMEMORY memory; + + _Analysis_assume_(Length > 0); + + KdPrint(("EchoEvtIoRead Called! Queue 0x%p, Request 0x%p Length %d\n", + Queue,Request,Length)); + // + // No data to read + // + if( (queueContext->Buffer == NULL) ) { + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, (ULONG_PTR)0L); + return; + } + + _Analysis_assume_(queueContext->Length > 0); + + // + // Read what we have + // + if( queueContext->Length < Length ) { + Length = queueContext->Length; + } + + // + // Get the request memory + // + Status = WdfRequestRetrieveOutputMemory(Request, &memory); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoRead Could not get request memory buffer 0x%x\n",Status)); + WdfVerifierDbgBreakPoint(); + WdfRequestCompleteWithInformation(Request, Status, 0L); + return; + } + + // Copy the memory out + Status = WdfMemoryCopyFromBuffer( memory, // destination + 0, // offset into the destination memory + queueContext->Buffer, + Length ); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoRead: WdfMemoryCopyFromBuffer failed 0x%x\n", Status)); + WdfRequestComplete(Request, Status); + return; + } + + // Set transfer information + WdfRequestSetInformation(Request, (ULONG_PTR)Length); + + // + // Mark the request is cancelable. This must be the last thing we do because + // the cancel routine can run immediately after we set it. This means that + // CurrentRequest and CurrentStatus must be initialized before we mark the + // request cancelable. + // + EchoSetCurrentRequest(Request, Queue); + + return; +} + +VOID +EchoEvtIoWrite( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + This event is invoked when the framework receives IRP_MJ_WRITE request. + This routine allocates memory buffer, copies the data from the request to it, + and stores the buffer pointer in the queue-context with the length variable + representing the buffers length. The actual completion of the request + is defered to the periodic timer dpc. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + Request - Handle to a framework request object. + + Length - number of bytes to be read. + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS Status; + WDFMEMORY memory; + PQUEUE_CONTEXT queueContext = QueueGetContext(Queue); + + _Analysis_assume_(Length > 0); + + KdPrint(("EchoEvtIoWrite Called! Queue 0x%p, Request 0x%p Length %d\n", + Queue,Request,Length)); + + if( Length > MAX_WRITE_LENGTH ) { + KdPrint(("EchoEvtIoWrite Buffer Length to big %d, Max is %d\n", + Length,MAX_WRITE_LENGTH)); + WdfRequestCompleteWithInformation(Request, STATUS_BUFFER_OVERFLOW, 0L); + return; + } + + // Get the memory buffer + Status = WdfRequestRetrieveInputMemory(Request, &memory); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoWrite Could not get request memory buffer 0x%x\n", + Status)); + WdfVerifierDbgBreakPoint(); + WdfRequestComplete(Request, Status); + return; + } + + // Release previous buffer if set + if( queueContext->Buffer != NULL ) { + ExFreePool(queueContext->Buffer); + queueContext->Buffer = NULL; + queueContext->Length = 0L; + } + + queueContext->Buffer = ExAllocatePoolWithTag(NonPagedPool, Length, 'sam1'); + if( queueContext->Buffer == NULL ) { + KdPrint(("EchoEvtIoWrite: Could not allocate %d byte buffer\n",Length)); + WdfRequestComplete(Request, STATUS_INSUFFICIENT_RESOURCES); + return; + } + + + // Copy the memory in + Status = WdfMemoryCopyToBuffer( memory, + 0, // offset into the source memory + queueContext->Buffer, + Length ); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoWrite WdfMemoryCopyToBuffer failed 0x%x\n", Status)); + WdfVerifierDbgBreakPoint(); + ExFreePool(queueContext->Buffer); + queueContext->Buffer = NULL; + queueContext->Length = 0L; + WdfRequestComplete(Request, Status); + return; + } + + queueContext->Length = (ULONG) Length; + + // Set transfer information + WdfRequestSetInformation(Request, (ULONG_PTR)Length); + + + // + // Mark the request is cancelable. This must be the last thing we do because + // the cancel routine can run immediately after we set it. This means that + // CurrentRequest and CurrentStatus must be initialized before we mark the + // request cancelable. + // + EchoSetCurrentRequest(Request, Queue); + + return; +} + + +VOID +EchoEvtTimerFunc( + IN WDFTIMER Timer + ) +/*++ + +Routine Description: + + This is the TimerDPC the driver sets up to complete requests. + This function is registered when the WDFTIMER object is created. + + This function does *NOT* automatically synchronize with the I/O Queue + callbacks and cancel routine, we must do it ourself in the routine. + +Arguments: + + Timer - Handle to a framework Timer object. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS status; + WDFREQUEST request; + WDFQUEUE queue; + PQUEUE_CONTEXT queueContext; + PREQUEST_CONTEXT requestContext; + BOOLEAN cancel, completeRequest; + + // + // Default to failure. status is initialized so that the compiler does not + // think we are using an uninitialized value when completing the request. + // + status = STATUS_UNSUCCESSFUL; + cancel = FALSE; + completeRequest = FALSE; + + queue = (WDFQUEUE) WdfTimerGetParentObject(Timer); + queueContext = QueueGetContext(queue); + requestContext = NULL; + + // + // We must synchronize with the cancel routine which will be taking the + // request out of the context under this lock. + // + WdfSpinLockAcquire(queueContext->SpinLock); + + request = queueContext->CurrentRequest; + + if (request != NULL) { + requestContext = RequestGetContext(request); + + if (EchoIncrementRequestCancelOwnershipCount(requestContext)) { + cancel = TRUE; + } + else { + // + // What has happened is that the cancel routine has executed and + // has already claimed cancel ownership of the request, but has not + // yet acquired the object lock and cleared the CurrentRequest field + // in queueContext. In this case, do nothing and let the cancel + // routine run to completion and complete the request. + // + } + } + + WdfSpinLockRelease(queueContext->SpinLock); + + // + // If we could not claim cancel ownership, we are done. + // + if (cancel == FALSE) { + return; + } + + // + // The request handle and requestContext are valid until we release + // the cancel ownership count we already acquired. + // + status = WdfRequestUnmarkCancelable(request); + if (status != STATUS_CANCELLED) { + KdPrint(("CustomTimerDPC successfully cleared cancel routine on " + "request 0x%p, Status 0x%x \n", request,status)); + + // + // Since we successfully removed the cancel routine (and we are not + // currently racing with it), there is no need to use an interlocked + // decrement to lower the cancel ownership count. + // + + // + // 2 is the initial count we set when we initialized CancelCompletionOwnershipCount + // plus the call to EchoIncrementRequestCancelOwnershipCount() + // + ASSERT(requestContext->CancelCompletionOwnershipCount == 2); + requestContext->CancelCompletionOwnershipCount -=2; + + completeRequest = TRUE; + } + else { + completeRequest = EchoDecrementRequestCancelOwnershipCount( + requestContext + ); + + if (completeRequest) { + KdPrint( + ("CustomTimerDPC Request 0x%p is STATUS_CANCELLED, but " + "claimed completion ownership\n", request)); + } + else { + KdPrint( + ("CustomTimerDPC Request 0x%p is STATUS_CANCELLED, not " + "completing", request)); + } + } + + if (completeRequest) { + KdPrint(("CustomTimerDPC Completing request 0x%p, Status 0x%x \n", + request,status)); + + // + // Clear the current request out of the queue context and complete + // the request. + // + WdfSpinLockAcquire(queueContext->SpinLock); + queueContext->CurrentRequest = NULL; + status = queueContext->CurrentStatus; + WdfSpinLockRelease(queueContext->SpinLock); + + WdfRequestComplete(request, status); + } +} + diff --git a/general/echo/kmdf/driver/DriverSync/queue.h b/general/echo/kmdf/driver/DriverSync/queue.h new file mode 100644 index 00000000..1985a6c7 --- /dev/null +++ b/general/echo/kmdf/driver/DriverSync/queue.h @@ -0,0 +1,67 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + queue.h + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +// Set max write length for testing +#define MAX_WRITE_LENGTH 1024*40 + +// Set timer period in ms +#define TIMER_PERIOD 1000*10 + +// +// This is the context that can be placed per queue +// and would contain per queue information. +// +typedef struct _QUEUE_CONTEXT { + + // Here we allocate a buffer from a test write so it can be read back + PVOID Buffer; + ULONG Length; + + // Timer DPC for this queue + WDFTIMER Timer; + + // Virtual I/O + WDFREQUEST CurrentRequest; + NTSTATUS CurrentStatus; + + // SpinLock to synchronize I/O callbacks. + WDFSPINLOCK SpinLock; + +} QUEUE_CONTEXT, *PQUEUE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext) + +NTSTATUS +EchoQueueInitialize( + WDFDEVICE hDevice + ); + +EVT_WDF_IO_QUEUE_CONTEXT_DESTROY_CALLBACK EchoEvtIoQueueContextDestroy; + +// +// Events from the IoQueue object +// +EVT_WDF_REQUEST_CANCEL EchoEvtRequestCancel; +EVT_WDF_IO_QUEUE_IO_READ EchoEvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE EchoEvtIoWrite; + +NTSTATUS +EchoTimerCreate( + IN WDFTIMER* pTimer, + IN ULONG Period, + IN WDFQUEUE Queue + ); + +EVT_WDF_TIMER EchoEvtTimerFunc; diff --git a/general/echo/kmdf/exe/echoapp.cpp b/general/echo/kmdf/exe/echoapp.cpp new file mode 100644 index 00000000..9649a407 --- /dev/null +++ b/general/echo/kmdf/exe/echoapp.cpp @@ -0,0 +1,700 @@ +/*++ + +Copyright (c) Microsoft Corporation + +Module Name: + + ioctl.cpp + +Abstract: + + A simple asynch test for usb driver. + + +Environment: + + user mode only + +--*/ + + +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + +#define INITGUID + +#include +#include +#include +#include +#include +#include "public.h" + +#define NUM_ASYNCH_IO 100 +#define BUFFER_SIZE (40*1024) + +#define READER_TYPE 1 +#define WRITER_TYPE 2 + +#define MAX_DEVPATH_LENGTH 256 + +BOOLEAN G_PerformAsyncIo; +BOOLEAN G_LimitedLoops; +ULONG G_AsyncIoLoopsNum; +CHAR G_DevicePath[MAX_DEVPATH_LENGTH]; + + +ULONG +AsyncIo( + PVOID ThreadParameter + ); + +BOOLEAN +PerformWriteReadTest( + IN HANDLE hDevice, + IN ULONG TestLength + ); + +BOOL +GetDevicePath( + IN LPGUID InterfaceGuid, + _Out_writes_(BufLen) PCHAR DevicePath, + _In_ size_t BufLen + ); + + +int __cdecl +main( + _In_ int argc, + _In_reads_(argc) char* argv[] + ) +{ + HANDLE hDevice = INVALID_HANDLE_VALUE; + HANDLE th1 = NULL; + BOOLEAN result = TRUE; + + + if (argc > 1) { + if(!_strnicmp (argv[1], "-Async", 6) ) { + G_PerformAsyncIo = TRUE; + if (argc > 2) { + G_AsyncIoLoopsNum = atoi(argv[2]); + G_LimitedLoops = TRUE; + } + else { + G_LimitedLoops = FALSE; + } + + } else { + printf("Usage:\n"); + printf(" Echoapp.exe --- Send single write and read request synchronously\n"); + printf(" Echoapp.exe -Async --- Send reads and writes asynchronously without terminating\n"); + printf(" Echoapp.exe -Async --- Send reads and writes asynchronously\n"); + printf("Exit the app anytime by pressing Ctrl-C\n"); + result = FALSE; + goto exit; + } + } + + if ( !GetDevicePath( + (LPGUID) &GUID_DEVINTERFACE_ECHO, + G_DevicePath, + sizeof(G_DevicePath)/sizeof(G_DevicePath[0])) ) + { + result = FALSE; + goto exit; + } + + printf("DevicePath: %s\n", G_DevicePath); + + hDevice = CreateFile(G_DevicePath, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if (hDevice == INVALID_HANDLE_VALUE) { + printf("Failed to open device. Error %d\n",GetLastError()); + result = FALSE; + goto exit; + } + + printf("Opened device successfully\n"); + + if(G_PerformAsyncIo) { + + printf("Starting AsyncIo\n"); + + // + // Create a reader thread + // + th1 = CreateThread( NULL, // Default Security Attrib. + 0, // Initial Stack Size, + (LPTHREAD_START_ROUTINE) AsyncIo, // Thread Func + (LPVOID)READER_TYPE, + 0, // Creation Flags + NULL ); // Don't need the Thread Id. + + if (th1 == NULL) { + printf("Couldn't create reader thread - error %d\n", GetLastError()); + result = FALSE; + goto exit; + } + + // + // Use this thread for peforming write. + // + result = (BOOLEAN)AsyncIo((PVOID)WRITER_TYPE); + + }else { + // + // Write pattern buffers and read them back, then verify them + // + result = PerformWriteReadTest(hDevice, 512); + if(!result) { + goto exit; + } + + result = PerformWriteReadTest(hDevice, 30*1024); + if(!result) { + goto exit; + } + + } + +exit: + + if (th1 != NULL) { + WaitForSingleObject(th1, INFINITE); + CloseHandle(th1); + } + + if (hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + } + + return ((result == TRUE) ? 0 : 1); + +} + +PUCHAR +CreatePatternBuffer( + IN ULONG Length + ) +{ + unsigned int i; + PUCHAR p, pBuf; + + pBuf = (PUCHAR)malloc(Length); + if( pBuf == NULL ) { + printf("Could not allocate %d byte buffer\n",Length); + return NULL; + } + + p = pBuf; + + for(i=0; i < Length; i++ ) { + *p = (UCHAR)i; + p++; + } + + return pBuf; +} + +BOOLEAN +VerifyPatternBuffer( + _In_reads_bytes_(Length) PUCHAR pBuffer, + _In_ ULONG Length + ) +{ + unsigned int i; + PUCHAR p = pBuffer; + + for( i=0; i < Length; i++ ) { + + if( *p != (UCHAR)(i & 0xFF) ) { + printf("Pattern changed. SB 0x%x, Is 0x%x\n", + (UCHAR)(i & 0xFF), *p); + return FALSE; + } + + p++; + } + + return TRUE; +} + +BOOLEAN +PerformWriteReadTest( + IN HANDLE hDevice, + IN ULONG TestLength + ) +/* +*/ +{ + ULONG bytesReturned =0; + PUCHAR WriteBuffer = NULL, + ReadBuffer = NULL; + BOOLEAN result = TRUE; + + WriteBuffer = CreatePatternBuffer(TestLength); + if( WriteBuffer == NULL ) { + + result = FALSE; + goto Cleanup; + } + + ReadBuffer = (PUCHAR)malloc(TestLength); + if( ReadBuffer == NULL ) { + + printf("PerformWriteReadTest: Could not allocate %d " + "bytes ReadBuffer\n",TestLength); + + result = FALSE; + goto Cleanup; + + } + + // + // Write the pattern to the device + // + bytesReturned = 0; + + if (!WriteFile ( hDevice, + WriteBuffer, + TestLength, + &bytesReturned, + NULL)) { + + printf ("PerformWriteReadTest: WriteFile failed: " + "Error %d\n", GetLastError()); + + result = FALSE; + goto Cleanup; + + } else { + + if( bytesReturned != TestLength ) { + + printf("bytes written is not test length! Written %d, " + "SB %d\n",bytesReturned, TestLength); + + result = FALSE; + goto Cleanup; + } + + printf ("%d Pattern Bytes Written successfully\n", + bytesReturned); + } + + bytesReturned = 0; + + if ( !ReadFile (hDevice, + ReadBuffer, + TestLength, + &bytesReturned, + NULL)) { + + printf ("PerformWriteReadTest: ReadFile failed: " + "Error %d\n", GetLastError()); + + result = FALSE; + goto Cleanup; + + } else { + + if( bytesReturned != TestLength ) { + + printf("bytes Read is not test length! Read %d, " + "SB %d\n",bytesReturned, TestLength); + + // + // Note: Is this a Failure Case?? + // + result = FALSE; + goto Cleanup; + } + + printf ("%d Pattern Bytes Read successfully\n",bytesReturned); + } + + // + // Now compare + // + if( !VerifyPatternBuffer(ReadBuffer, TestLength) ) { + + printf("Verify failed\n"); + + result = FALSE; + goto Cleanup; + } + + printf("Pattern Verified successfully\n"); + +Cleanup: + + // + // Free WriteBuffer if non NULL. + // + if (WriteBuffer) { + free (WriteBuffer); + } + + // + // Free ReadBuffer if non NULL + // + if (ReadBuffer) { + free (ReadBuffer); + } + + return result; +} + + + +ULONG +AsyncIo( + PVOID ThreadParameter + ) +{ + HANDLE hDevice = INVALID_HANDLE_VALUE; + HANDLE hCompletionPort = NULL; + OVERLAPPED *pOvList = NULL; + PUCHAR buf = NULL; + ULONG numberOfBytesTransferred; + OVERLAPPED *completedOv; + ULONG_PTR i; + ULONG ioType = (ULONG)(ULONG_PTR)ThreadParameter; + ULONG_PTR key; + ULONG error; + BOOLEAN result = TRUE; + ULONG maxPendingRequests = NUM_ASYNCH_IO; + ULONG remainingRequestsToSend = 0; + ULONG remainingRequestsToReceive = 0; + + hDevice = CreateFile(G_DevicePath, + GENERIC_WRITE|GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL ); + + + if (hDevice == INVALID_HANDLE_VALUE) { + printf("Cannot open %s error %d\n", G_DevicePath, GetLastError()); + result = FALSE; + goto Error; + } + + hCompletionPort = CreateIoCompletionPort(hDevice, NULL, 1, 0); + if (hCompletionPort == NULL) { + printf("Cannot open completion port %d \n",GetLastError()); + result = FALSE; + goto Error; + } + + // + // We will only have NUM_ASYNCH_IO or G_AsyncIoLoopsNum pending at any + // time (whichever is less) + // + if (G_LimitedLoops == TRUE) { + remainingRequestsToReceive = G_AsyncIoLoopsNum; + if (G_AsyncIoLoopsNum > NUM_ASYNCH_IO) { + // + // After we send the initial NUM_ASYNCH_IO, we will have additional + // (G_AsyncIoLoopsNum - NUM_ASYNCH_IO) I/Os to send + // + maxPendingRequests = NUM_ASYNCH_IO; + remainingRequestsToSend = G_AsyncIoLoopsNum - NUM_ASYNCH_IO; + } + else { + maxPendingRequests = G_AsyncIoLoopsNum; + remainingRequestsToSend = 0; + + } + } + + pOvList = (OVERLAPPED *)malloc(maxPendingRequests * sizeof(OVERLAPPED)); + if (pOvList == NULL) { + printf("Cannot allocate overlapped array \n"); + result = FALSE; + goto Error; + } + + buf = (PUCHAR)malloc(maxPendingRequests * BUFFER_SIZE); + if (buf == NULL) { + printf("Cannot allocate buffer \n"); + result = FALSE; + goto Error; + } + + ZeroMemory(pOvList, maxPendingRequests * sizeof(OVERLAPPED)); + ZeroMemory(buf, maxPendingRequests * BUFFER_SIZE); + + // + // Issue asynch I/O + // + + for (i = 0; i < maxPendingRequests; i++) { + if (ioType == READER_TYPE) { + if ( ReadFile( hDevice, + buf + (i* BUFFER_SIZE), + BUFFER_SIZE, + NULL, + &pOvList[i]) == 0) { + + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + printf(" %dth Read failed %d \n",i, GetLastError()); + result = FALSE; + goto Error; + } + } + + } else { + if ( WriteFile( hDevice, + buf + (i* BUFFER_SIZE), + BUFFER_SIZE, + NULL, + &pOvList[i]) == 0) { + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + printf(" %dth Write failed %d \n",i, GetLastError()); + result = FALSE; + goto Error; + } + } + } + } + + // + // Wait for the I/Os to complete. If one completes then reissue the I/O + // + + WHILE (1) { + + if ( GetQueuedCompletionStatus(hCompletionPort, &numberOfBytesTransferred, &key, &completedOv, INFINITE) == 0) { + printf("GetQueuedCompletionStatus failed %d\n", GetLastError()); + result = FALSE; + goto Error; + } + + // + // Read successfully completed. If we're doing unlimited I/Os then Issue another one. + // + + if (ioType == READER_TYPE) { + + i = completedOv - pOvList; + printf("Number of bytes read by request number %d is %d\n", i, numberOfBytesTransferred); + + // + // If we're done with the I/Os, then exit + // + if (G_LimitedLoops == TRUE) { + if ((--remainingRequestsToReceive) == 0) { + break; + } + + if (remainingRequestsToSend == 0) { + continue; + } + else { + remainingRequestsToSend--; + } + } + + + if ( ReadFile( hDevice, + buf + (i * BUFFER_SIZE), + BUFFER_SIZE, + NULL, + completedOv) == 0) { + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + printf("%dth Read failed %d \n", i, GetLastError()); + result = FALSE; + goto Error; + } + } + } else { + + i = completedOv - pOvList; + + printf("Number of bytes written by request number %d is %d\n", i, numberOfBytesTransferred); + + // + // If we're done with the I/Os, then exit + // + if (G_LimitedLoops == TRUE) { + if ((--remainingRequestsToReceive) == 0) { + break; + } + + if (remainingRequestsToSend == 0) { + continue; + } + else { + remainingRequestsToSend--; + } + } + + + if ( WriteFile( hDevice, + buf + (i * BUFFER_SIZE), + BUFFER_SIZE, + NULL, + completedOv) == 0) { + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + + printf("%dth write failed %d \n", i, GetLastError()); + result = FALSE; + goto Error; + } + } + } + } + +Error: + if(hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + } + + if(hCompletionPort) { + CloseHandle(hCompletionPort); + } + + if(buf) { + free(buf); + } + if(pOvList) { + free(pOvList); + } + + return (ULONG)result; + +} + + +BOOL +GetDevicePath( + IN LPGUID InterfaceGuid, + _Out_writes_(BufLen) PCHAR DevicePath, + _In_ size_t BufLen + ) +{ + HDEVINFO HardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; + PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = NULL; + ULONG Length, RequiredLength = 0; + BOOL bResult; + HRESULT hr; + + HardwareDeviceInfo = SetupDiGetClassDevs( + InterfaceGuid, + NULL, + NULL, + (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); + + if (HardwareDeviceInfo == INVALID_HANDLE_VALUE) { + printf("SetupDiGetClassDevs failed!\n"); + return FALSE; + } + + DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + bResult = SetupDiEnumDeviceInterfaces(HardwareDeviceInfo, + 0, + InterfaceGuid, + 0, + &DeviceInterfaceData); + + if (bResult == FALSE) { + + LPVOID lpMsgBuf; + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &lpMsgBuf, + 0, + NULL + )) { + + printf("SetupDiEnumDeviceInterfaces failed: %s", (LPTSTR)lpMsgBuf); + LocalFree(lpMsgBuf); + } + + printf("SetupDiEnumDeviceInterfaces failed.\n"); + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + return FALSE; + } + + SetupDiGetDeviceInterfaceDetail( + HardwareDeviceInfo, + &DeviceInterfaceData, + NULL, + 0, + &RequiredLength, + NULL + ); + + DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, RequiredLength); + + if (DeviceInterfaceDetailData == NULL) { + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + printf("Failed to allocate memory.\n"); + return FALSE; + } + + DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + Length = RequiredLength; + + bResult = SetupDiGetDeviceInterfaceDetail( + HardwareDeviceInfo, + &DeviceInterfaceData, + DeviceInterfaceDetailData, + Length, + &RequiredLength, + NULL); + + if (bResult == FALSE) { + + LPVOID lpMsgBuf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &lpMsgBuf, + 0, + NULL + ); + + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + printf("Error in SetupDiGetDeviceInterfaceDetail: %s\n", (LPTSTR)lpMsgBuf); + LocalFree(DeviceInterfaceDetailData); + LocalFree(lpMsgBuf); + return FALSE; + } + + hr = StringCchCopy(DevicePath, + BufLen, + DeviceInterfaceDetailData->DevicePath) ; + + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); // It must be executed in both success and failure traces + LocalFree(DeviceInterfaceDetailData); + + return ( !FAILED(hr) ); // Result depends on StringCchCopy() +} + diff --git a/general/echo/kmdf/exe/echoapp.vcxproj b/general/echo/kmdf/exe/echoapp.vcxproj new file mode 100644 index 00000000..11b3eb4c --- /dev/null +++ b/general/echo/kmdf/exe/echoapp.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {684264A6-91C1-4046-AD23-BD823E13EB60} + $(MSBuildProjectName) + Debug + Win32 + {C342DB0F-934F-4A0A-90A7-02650E906732} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + echoapp + + + echoapp + + + echoapp + + + echoapp + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/echo/kmdf/exe/echoapp.vcxproj.Filters b/general/echo/kmdf/exe/echoapp.vcxproj.Filters new file mode 100644 index 00000000..7e14cf46 --- /dev/null +++ b/general/echo/kmdf/exe/echoapp.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {A120A502-9177-430F-BAEB-BBE51661EDDD} + + + h;hpp;hxx;hm;inl;inc;xsd + {967C5907-C8A7-46F3-B2B7-E3A0BFF110EE} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {53BEED44-2F74-4E8F-B7B8-D0BDDB11BEB6} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/echo/kmdf/exe/public.h b/general/echo/kmdf/exe/public.h new file mode 100644 index 00000000..d632951d --- /dev/null +++ b/general/echo/kmdf/exe/public.h @@ -0,0 +1,30 @@ +/*++ +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + public.h + +Abstract: + + This module contains the common declarations shared by driver + and user applications. + + +Environment: + + user and kernel + +--*/ + +#define WHILE(a) \ +__pragma(warning(suppress:4127)) while(a) + +// +// Define an Interface Guid so that app can find the device and talk to it. +// + +DEFINE_GUID (GUID_DEVINTERFACE_ECHO, + 0xcdc35b6e, 0xbe4, 0x4936, 0xbf, 0x5f, 0x55, 0x37, 0x38, 0xa, 0x7c, 0x1a); +// {CDC35B6E-0BE4-4936-BF5F-5537380A7C1A} + diff --git a/general/echo/kmdf/kmdfecho.sln b/general/echo/kmdf/kmdfecho.sln new file mode 100644 index 00000000..b9c25c6c --- /dev/null +++ b/general/echo/kmdf/kmdfecho.sln @@ -0,0 +1,63 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{1DD2F948-0799-49E3-A880-35A6215F8479}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AutoSync", "AutoSync", "{B52DE63E-ED02-41DD-9DAB-53ACE0286663}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{AE9E09B7-46C1-4AA3-9411-F125D9188EFD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DriverSync", "DriverSync", "{F01C6D5C-982E-4AE2-8DDC-0666F3905135}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echoapp", "exe\echoapp.vcxproj", "{684264A6-91C1-4046-AD23-BD823E13EB60}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo", "driver\AutoSync\echo.vcxproj", "{C8F9A776-3675-459B-A0A3-BA17D003C70B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo_2", "driver\DriverSync\echo_2.vcxproj", "{968447B1-9A4F-4D2D-B81B-7BCB9240F5E3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {684264A6-91C1-4046-AD23-BD823E13EB60}.Debug|Win32.ActiveCfg = Debug|Win32 + {684264A6-91C1-4046-AD23-BD823E13EB60}.Debug|Win32.Build.0 = Debug|Win32 + {684264A6-91C1-4046-AD23-BD823E13EB60}.Release|Win32.ActiveCfg = Release|Win32 + {684264A6-91C1-4046-AD23-BD823E13EB60}.Release|Win32.Build.0 = Release|Win32 + {684264A6-91C1-4046-AD23-BD823E13EB60}.Debug|x64.ActiveCfg = Debug|x64 + {684264A6-91C1-4046-AD23-BD823E13EB60}.Debug|x64.Build.0 = Debug|x64 + {684264A6-91C1-4046-AD23-BD823E13EB60}.Release|x64.ActiveCfg = Release|x64 + {684264A6-91C1-4046-AD23-BD823E13EB60}.Release|x64.Build.0 = Release|x64 + {C8F9A776-3675-459B-A0A3-BA17D003C70B}.Debug|Win32.ActiveCfg = Debug|Win32 + {C8F9A776-3675-459B-A0A3-BA17D003C70B}.Debug|Win32.Build.0 = Debug|Win32 + {C8F9A776-3675-459B-A0A3-BA17D003C70B}.Release|Win32.ActiveCfg = Release|Win32 + {C8F9A776-3675-459B-A0A3-BA17D003C70B}.Release|Win32.Build.0 = Release|Win32 + {C8F9A776-3675-459B-A0A3-BA17D003C70B}.Debug|x64.ActiveCfg = Debug|x64 + {C8F9A776-3675-459B-A0A3-BA17D003C70B}.Debug|x64.Build.0 = Debug|x64 + {C8F9A776-3675-459B-A0A3-BA17D003C70B}.Release|x64.ActiveCfg = Release|x64 + {C8F9A776-3675-459B-A0A3-BA17D003C70B}.Release|x64.Build.0 = Release|x64 + {968447B1-9A4F-4D2D-B81B-7BCB9240F5E3}.Debug|Win32.ActiveCfg = Debug|Win32 + {968447B1-9A4F-4D2D-B81B-7BCB9240F5E3}.Debug|Win32.Build.0 = Debug|Win32 + {968447B1-9A4F-4D2D-B81B-7BCB9240F5E3}.Release|Win32.ActiveCfg = Release|Win32 + {968447B1-9A4F-4D2D-B81B-7BCB9240F5E3}.Release|Win32.Build.0 = Release|Win32 + {968447B1-9A4F-4D2D-B81B-7BCB9240F5E3}.Debug|x64.ActiveCfg = Debug|x64 + {968447B1-9A4F-4D2D-B81B-7BCB9240F5E3}.Debug|x64.Build.0 = Debug|x64 + {968447B1-9A4F-4D2D-B81B-7BCB9240F5E3}.Release|x64.ActiveCfg = Release|x64 + {968447B1-9A4F-4D2D-B81B-7BCB9240F5E3}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {684264A6-91C1-4046-AD23-BD823E13EB60} = {1DD2F948-0799-49E3-A880-35A6215F8479} + {C8F9A776-3675-459B-A0A3-BA17D003C70B} = {B52DE63E-ED02-41DD-9DAB-53ACE0286663} + {968447B1-9A4F-4D2D-B81B-7BCB9240F5E3} = {F01C6D5C-982E-4AE2-8DDC-0666F3905135} + {B52DE63E-ED02-41DD-9DAB-53ACE0286663} = {AE9E09B7-46C1-4AA3-9411-F125D9188EFD} + {F01C6D5C-982E-4AE2-8DDC-0666F3905135} = {AE9E09B7-46C1-4AA3-9411-F125D9188EFD} + EndGlobalSection +EndGlobal diff --git a/general/echo/umdf/Comsup.cpp b/general/echo/umdf/Comsup.cpp new file mode 100644 index 00000000..fd298470 --- /dev/null +++ b/general/echo/umdf/Comsup.cpp @@ -0,0 +1,344 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + ComSup.cpp + +Abstract: + + This module contains implementations for the functions and methods + used for providing COM support. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" + +#include "comsup.tmh" + +// +// Implementation of CUnknown methods. +// + +CUnknown::CUnknown( + VOID + ) : m_ReferenceCount(1) +/*++ + + Routine Description: + + Constructor for an instance of the CUnknown class. This simply initializes + the reference count of the object to 1. The caller is expected to + call Release() if it wants to delete the object once it has been allocated. + + Arguments: + + None + + Return Value: + + None + +--*/ +{ + // do nothing. +} + +HRESULT +STDMETHODCALLTYPE +CUnknown::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method provides the basic support for query interface on CUnknown. + If the interface requested is IUnknown it references the object and + returns an interface pointer. Otherwise it returns an error. + + Arguments: + + InterfaceId - the IID being requested + + Object - a location to store the interface pointer to return. + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + if (IsEqualIID(InterfaceId, __uuidof(IUnknown))) + { + *Object = QueryIUnknown(); + return S_OK; + } + else + { + *Object = NULL; + return E_NOINTERFACE; + } +} + +IUnknown * +CUnknown::QueryIUnknown( + VOID + ) +/*++ + + Routine Description: + + This helper method references the object and returns a pointer to the + object's IUnknown interface. + + This allows other methods to convert a CUnknown pointer into an IUnknown + pointer without a typecast and without calling QueryInterface and dealing + with the return value. + + Arguments: + + None + + Return Value: + + A pointer to the object's IUnknown interface. + +--*/ +{ + AddRef(); + return static_cast(this); +} + +ULONG +STDMETHODCALLTYPE +CUnknown::AddRef( + VOID + ) +/*++ + + Routine Description: + + This method adds one to the object's reference count. + + Arguments: + + None + + Return Value: + + The new reference count. The caller should only use this for debugging + as the object's actual reference count can change while the caller + examines the return value. + +--*/ +{ + return InterlockedIncrement(&m_ReferenceCount); +} + +ULONG +STDMETHODCALLTYPE +CUnknown::Release( + VOID + ) +/*++ + + Routine Description: + + This method subtracts one to the object's reference count. If the count + goes to zero, this method deletes the object. + + Arguments: + + None + + Return Value: + + The new reference count. If the caller uses this value it should only be + to check for zero (i.e. this call caused or will cause deletion) or + non-zero (i.e. some other call may have caused deletion, but this one + didn't). + +--*/ +{ + ULONG count = InterlockedDecrement(&m_ReferenceCount); + + if (count == 0) + { + delete this; + } + return count; +} + +// +// Implementation of CClassFactory methods. +// + +// +// Define storage for the factory's static lock count variable. +// + +LONG CClassFactory::s_LockCount = 0; + +IClassFactory * +CClassFactory::QueryIClassFactory( + VOID + ) +/*++ + + Routine Description: + + This helper method references the object and returns a pointer to the + object's IClassFactory interface. + + This allows other methods to convert a CClassFactory pointer into an + IClassFactory pointer without a typecast and without dealing with the + return value QueryInterface. + + Arguments: + + None + + Return Value: + + A referenced pointer to the object's IClassFactory interface. + +--*/ +{ + AddRef(); + return static_cast(this); +} + +HRESULT +CClassFactory::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method attempts to retrieve the requested interface from the object. + + If the interface is found then the reference count on that interface (and + thus the object itself) is incremented. + + Arguments: + + InterfaceId - the interface the caller is requesting. + + Object - a location to store the interface pointer. + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + // + // This class only supports IClassFactory so check for that. + // + + if (IsEqualIID(InterfaceId, __uuidof(IClassFactory))) + { + *Object = QueryIClassFactory(); + return S_OK; + } + else + { + // + // See if the base class supports the interface. + // + + return CUnknown::QueryInterface(InterfaceId, Object); + } +} + +HRESULT +STDMETHODCALLTYPE +CClassFactory::CreateInstance( + _In_opt_ IUnknown * /* OuterObject */, + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This COM method is the factory routine - it creates instances of the driver + callback class and returns the specified interface on them. + + Arguments: + + OuterObject - only used for aggregation, which our driver callback class + does not support. + + InterfaceId - the interface ID the caller would like to get from our + new object. + + Object - a location to store the referenced interface pointer to the new + object. + + Return Value: + + Status. + +--*/ +{ + HRESULT hr; + + PCMyDriver driver; + + *Object = NULL; + + hr = CMyDriver::CreateInstance(&driver); + + if (SUCCEEDED(hr)) + { + hr = driver->QueryInterface(InterfaceId, Object); + driver->Release(); + } + + return hr; +} + +HRESULT +STDMETHODCALLTYPE +CClassFactory::LockServer( + _In_ BOOL Lock + ) +/*++ + + Routine Description: + + This COM method can be used to keep the DLL in memory. However since the + driver's DllCanUnloadNow function always returns false, this has little + effect. Still it tracks the number of lock and unlock operations. + + Arguments: + + Lock - Whether the caller wants to lock or unlock the "server" + + Return Value: + + S_OK + +--*/ +{ + if (Lock) + { + InterlockedIncrement(&s_LockCount); + } + else + { + InterlockedDecrement(&s_LockCount); + } + return S_OK; +} + diff --git a/general/echo/umdf/Comsup.h b/general/echo/umdf/Comsup.h new file mode 100644 index 00000000..b96fd982 --- /dev/null +++ b/general/echo/umdf/Comsup.h @@ -0,0 +1,215 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + ComSup.h + +Abstract: + + This module contains classes and functions use for providing COM support + code. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// Forward type declarations. They are here rather than in internal.h as +// you only need them if you choose to use these support classes. +// + +typedef class CUnknown *PCUnknown; +typedef class CClassFactory *PCClassFactory; + +// +// Base class to implement IUnknown. You can choose to derive your COM +// classes from this class, or simply implement IUnknown in each of your +// classes. +// + +class CUnknown : public IUnknown +{ + +// +// Private data members and methods. These are only accessible by the methods +// of this class. +// +private: + + // + // The reference count for this object. Initialized to 1 in the + // constructor. + // + + LONG m_ReferenceCount; + +// +// Protected data members and methods. These are accessible by the subclasses +// but not by other classes. +// +protected: + + // + // The constructor and destructor are protected to ensure that only the + // subclasses of CUnknown can create and destroy instances. + // + + CUnknown( + VOID + ); + + // + // The destructor MUST be virtual. Since any instance of a CUnknown + // derived class should only be deleted from within CUnknown::Release, + // the destructor MUST be virtual or only CUnknown::~CUnknown will get + // invoked on deletion. + // + // If you see that your CMyDevice specific destructor is never being + // called, make sure you haven't deleted the virtual destructor here. + // + + virtual + ~CUnknown( + VOID + ) + { + // Do nothing + } + +// +// Public Methods. These are accessible by any class. +// +public: + + IUnknown * + QueryIUnknown( + VOID + ); + +// +// COM Methods. +// +public: + + // + // IUnknown methods + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ); + + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ); + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); +}; + +// +// Class factory support class. Create an instance of this from your +// DllGetClassObject method and modify the implementation to create +// an instance of your driver event handler class. +// + +class CClassFactory : public CUnknown, public IClassFactory +{ +// +// Private data members and methods. These are only accessible by the methods +// of this class. +// +private: + + // + // The lock count. This is shared across all instances of IClassFactory + // and can be queried through the public IsLocked method. + // + + static LONG s_LockCount; + +// +// Public Methods. These are accessible by any class. +// +public: + + IClassFactory * + QueryIClassFactory( + VOID + ); + +// +// COM Methods. +// +public: + + // + // IUnknown methods + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + _At_(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + // + // IClassFactory methods. + // + + virtual + HRESULT + STDMETHODCALLTYPE + CreateInstance( + _In_opt_ IUnknown *OuterObject, + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + virtual + HRESULT + STDMETHODCALLTYPE + LockServer( + _In_ BOOL Lock + ); +}; diff --git a/general/echo/umdf/Device.cpp b/general/echo/umdf/Device.cpp new file mode 100644 index 00000000..77110e8e --- /dev/null +++ b/general/echo/umdf/Device.cpp @@ -0,0 +1,415 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Device.cpp + +Abstract: + + This module contains the implementation of the sample driver's + device callback object. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "initguid.h" + +#include "device.tmh" + +DEFINE_GUID (GUID_DEVINTERFACE_ECHO, + 0xcdc35b6e, 0xbe4, 0x4936, 0xbf, 0x5f, 0x55, 0x37, 0x38, 0xa, 0x7c, 0x1a); +// {CDC35B6E-0BE4-4936-BF5F-5537380A7C1A} + +HRESULT +CMyDevice::CreateInstance( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit, + _Out_ PCMyDevice *Device + ) +/*++ + + Routine Description: + + This method creates and initializs an instance of the driver's + device callback object. + + Arguments: + + FxDeviceInit - the settings for the device. + + Device - a location to store the referenced pointer to the device object. + + Return Value: + + Status + +--*/ +{ + PCMyDevice device; + HRESULT hr; + + // + // Allocate a new instance of the device class. + // + + device = new CMyDevice(); + + if (NULL == device) + { + return E_OUTOFMEMORY; + } + + // + // Initialize the instance. + // + + hr = device->Initialize(FxDriver, FxDeviceInit); + + if (SUCCEEDED(hr)) + { + *Device = device; + } + else + { + device->Release(); + } + + return hr; +} + +HRESULT +CMyDevice::Initialize( + _In_ IWDFDriver * FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit + ) +/*++ + + Routine Description: + + This method initializes the device callback object and creates the + partner device object. + + The method should perform any device-specific configuration that: + * could fail (these can't be done in the constructor) + * must be done before the partner object is created -or- + * can be done after the partner object is created and which aren't + influenced by any device-level parameters the parent (the driver + in this case) might set. + + Arguments: + + FxDeviceInit - the settings for this device. + + Return Value: + + status. + +--*/ +{ + IWDFDevice *fxDevice = NULL; + IWDFDeviceInitialize2 *fxDeviceInit2; + HRESULT hr; + + // + // Configure things like the locking model before we go to create our + // partner device. + // + + // + // Set no locking unless you need an automatic callbacks synchronization + // + + FxDeviceInit->SetLockingConstraint(None); + + // + // TODO: If you're writing a filter driver then indicate that here. + // + // FxDeviceInit->SetFilter(); + // + + // + // TODO: Any per-device initialization which must be done before + // creating the partner object. + // + + // + // Create a new FX device object and assign the new callback object to + // handle any device level events that occur. + // + + // + // Set retrieval mode to direct I/O. This needs to be done before the call + // to CreateDevice. + // + hr = FxDeviceInit->QueryInterface(IID_PPV_ARGS(&fxDeviceInit2)); + + if (SUCCEEDED(hr)) + { + // + // WdfDeviceIoBufferedOrDirect for read/write and ioctrl operations. + // UMDF defaults to direct-I/O when the device is not running in a shared + // wudfhost process, and it defaults to buffered-I/O otherwise. Direct I/O + // is not allowed when the device is pooled. + // + // + fxDeviceInit2->SetIoTypePreference(WdfDeviceIoBufferRetrievalDeferred, + WdfDeviceIoBufferedOrDirect, + WdfDeviceIoBufferedOrDirect); + + SAFE_RELEASE(fxDeviceInit2); + + // + // QueryIUnknown references the IUnknown interface that it returns + // (which is the same as referencing the device). We pass that to + // CreateDevice, which takes its own reference if everything works. + // + { + IUnknown *unknown = this->QueryIUnknown(); + + hr = FxDriver->CreateDevice(FxDeviceInit, unknown, &fxDevice); + + unknown->Release(); + } + } + + // + // If that succeeded then set our FxDevice member variable. + // + + if (SUCCEEDED(hr)) + { + m_FxDevice = fxDevice; + + // + // Drop the reference we got from CreateDevice. Since this object + // is partnered with the framework object they have the same + // lifespan - there is no need for an additional reference. + // + + fxDevice->Release(); + } + + return hr; +} + +HRESULT +CMyDevice::Configure( + VOID + ) +/*++ + + Routine Description: + + This method is called after the device callback object has been initialized + and returned to the driver. It would setup the device's queues and their + corresponding callback objects. + + Arguments: + + FxDevice - the framework device object for which we're handling events. + + Return Value: + + status + +--*/ +{ + PCMyQueue defaultQueue; + + HRESULT hr; + + hr = CMyQueue::CreateInstance(m_FxDevice, &defaultQueue); + + if (FAILED(hr)) + { + return hr; + } + + hr = defaultQueue->Configure(); + + if (SUCCEEDED(hr)) + { + // + // In case of success store defaultQueue in our member + // The reference is transferred to m_DefaultQueue + // + + m_Queue = defaultQueue; + } + else + { + // + // In case of failure release the reference + // + + defaultQueue->Release(); + } + + if (SUCCEEDED(hr)) + { + hr = m_FxDevice->CreateDeviceInterface(&GUID_DEVINTERFACE_ECHO, + NULL); + } + + return hr; +} + +HRESULT +CMyDevice::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method is called to get a pointer to one of the object's callback + interfaces. + + Since the sample driver doesn't support any of the device events, this + method simply calls the base class's BaseQueryInterface. + + If the sample is extended to include device event interfaces then this + method must be changed to check the IID and return pointers to them as + appropriate. + + Arguments: + + InterfaceId - the interface being requested + + Object - a location to store the interface pointer if successful + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + HRESULT hr; + + if (IsEqualIID(InterfaceId, __uuidof(IPnpCallbackSelfManagedIo))) { + *Object = QueryIPnpCallbackSelfManagedIo(); + hr = S_OK; + } else { + hr = CUnknown::QueryInterface(InterfaceId, Object); + } + + return hr; +} + +HRESULT +CMyDevice::OnSelfManagedIoInit( + _In_ IWDFDevice * pWdfDevice + ) +/*++ + + Routine Description: + + This method is called to allow driver to initialize any resources + that driver might need to process I/O. + + Echo driver needs a thread to process completions. We initialize + this thread here + + Arguments: + + pWdfDevice - framework device object for which to initialze resources + + Return Value: + + S_OK in case of success + HRESULT correponding to error returned by CreateThread, in case of failure + +--*/ +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pWdfDevice); + + + m_ThreadHandle = CreateThread( NULL, // Default Security Attrib. + 0, // Initial Stack Size, + CMyQueue::CompletionThread, // Thread Func + (LPVOID)m_Queue, // Arg to Thread Func is Queue + 0, // Creation Flags + NULL ); // Don't need the Thread Id. + + if (m_ThreadHandle == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + return hr; +} + +void +CMyDevice::OnSelfManagedIoCleanup( + _In_ IWDFDevice * pWdfDevice + ) +/*++ + + Routine Description: + + This method is called to allow driver to cleanup any resources + that driver allocated to process I/O. + + It is critical that, in this routine driver wait for all of the + threads which it created to exit. Otherwise those threads could + continue to execute when framework unloads the driver which + would lead to a crash. + + Echo driver created a thread to handle completions. We wait for + that thread to exit in this routine + + Arguments: + + pWdfDevice - framework device object for which to cleanup resources + + Return Value: + + None + +--*/ +{ + // + // Kill the thread and + // wait for the thread to die. + // + + UNREFERENCED_PARAMETER(pWdfDevice); + + if (m_ThreadHandle) { + + // + // Ask queue to set terminate flag which will make + // the thread exit + // + m_Queue->SetExitThread(); + + // + // Wait for the thread to exit + // + + WaitForSingleObject(m_ThreadHandle, INFINITE); + + // + // Close the thread handle + // + + CloseHandle(m_ThreadHandle); + m_ThreadHandle = NULL; + } + + // + // Release the reference we took on the queue callback object + // to keep it alive until the thread exits + // + + SAFE_RELEASE(m_Queue); +} + diff --git a/general/echo/umdf/Device.h b/general/echo/umdf/Device.h new file mode 100644 index 00000000..70147c11 --- /dev/null +++ b/general/echo/umdf/Device.h @@ -0,0 +1,217 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions for the UMDF Echo sample + driver's device callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +#include "queue.h" + +// +// Class for the iotrace driver. +// + +class CMyDevice : + public CUnknown, + public IPnpCallbackSelfManagedIo +{ + +// +// Private data members. +// +private: + + IWDFDevice *m_FxDevice; + + // + // Completion Thread handle used by queue callback object + // + HANDLE m_ThreadHandle; + + // + // Our queue callback object + // Strong reference - since we pass it to the thread we create + // + CMyQueue *m_Queue; + +// +// Private methods. +// + +private: + + CMyDevice( + VOID + ) + { + m_FxDevice = NULL; + } + + HRESULT + Initialize( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ); + + IPnpCallbackSelfManagedIo * + QueryIPnpCallbackSelfManagedIo( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + +// +// Public methods +// +public: + + // + // The factory method used to create an instance of this driver. + // + + static + HRESULT + CreateInstance( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit, + _Out_ PCMyDevice *Device + ); + + HRESULT + Configure( + VOID + ); + +// +// COM methods +// +public: + + // + // IUnknown methods. + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + _At_(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + // + // IPnpCallbackSelfManagedIo methods + // + + // + // We implement this interface to create and tear down + // our completion thread + // + // It is critical that we wait for all the threads we create + // to exit during OnSelfManagedIoCleanup, otherwise thread + // may continue to execute when framework unloads the driver, + // leading to a crash + // + // We don't manage any I/O separate from the queue, so apart + // from OnSelfManagedIoInit and OnSelfManagedIoCleanup, other + // methods have token implementations + // + + virtual + void + STDMETHODCALLTYPE + OnSelfManagedIoCleanup( + _In_ IWDFDevice * pWdfDevice + ); + + virtual + void + STDMETHODCALLTYPE + OnSelfManagedIoFlush( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoInit( + _In_ IWDFDevice * pWdfDevice + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoSuspend( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoRestart( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoStop( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + + return S_OK; + } +}; diff --git a/general/echo/umdf/Driver.cpp b/general/echo/umdf/Driver.cpp new file mode 100644 index 00000000..1428a08a --- /dev/null +++ b/general/echo/umdf/Driver.cpp @@ -0,0 +1,220 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Driver.cpp + +Abstract: + + This module contains the implementation of the UMDF Sample's + core driver callback object. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "driver.tmh" + +HRESULT +CMyDriver::CreateInstance( + _Out_ PCMyDriver *Driver + ) +/*++ + + Routine Description: + + This static method is invoked in order to create and initialize a new + instance of the driver class. The caller should arrange for the object + to be released when it is no longer in use. + + Arguments: + + Driver - a location to store a referenced pointer to the new instance + + Return Value: + + S_OK if successful, or error otherwise. + +--*/ +{ + PCMyDriver driver; + HRESULT hr; + + // + // Allocate the callback object. + // + + driver = new CMyDriver(); + + if (NULL == driver) + { + return E_OUTOFMEMORY; + } + + // + // Initialize the callback object. + // + + hr = driver->Initialize(); + + if (SUCCEEDED(hr)) + { + // + // Store a pointer to the new, initialized object in the output + // parameter. + // + + *Driver = driver; + } + else + { + + // + // Release the reference on the driver object to get it to delete + // itself. + // + + driver->Release(); + } + + return hr; +} + +HRESULT +CMyDriver::Initialize( + VOID + ) +/*++ + + Routine Description: + + This method is called to initialize a newly created driver callback object + before it is returned to the creator. Unlike the constructor, the + Initialize method contains operations which could potentially fail. + + Arguments: + + None + + Return Value: + + None + +--*/ +{ + return S_OK; +} + +HRESULT +CMyDriver::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Interface + ) +/*++ + + Routine Description: + + This method returns a pointer to the requested interface on the callback + object.. + + Arguments: + + InterfaceId - the IID of the interface to query/reference + + Interface - a location to store the interface pointer. + + Return Value: + + S_OK if the interface is supported. + E_NOINTERFACE if it is not supported. + +--*/ +{ + if (IsEqualIID(InterfaceId, __uuidof(IDriverEntry))) + { + *Interface = QueryIDriverEntry(); + return S_OK; + } + else + { + return CUnknown::QueryInterface(InterfaceId, Interface); + } +} + +HRESULT +CMyDriver::OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ) +/*++ + + Routine Description: + + The FX invokes this method when it wants to install our driver on a device + stack. This method creates a device callback object, then calls the Fx + to create an Fx device object and associate the new callback object with + it. + + Arguments: + + FxWdfDriver - the Fx driver object. + + FxDeviceInit - the initialization information for the device. + + Return Value: + + status + +--*/ +{ + HRESULT hr; + + PCMyDevice device = NULL; + + // + // TODO: Do any per-device initialization (reading settings from the + // registry for example) that's necessary before creating your + // device callback object here. Otherwise you can leave such + // initialization to the initialization of the device event + // handler. + // + + // + // Create a new instance of our device callback object + // + + hr = CMyDevice::CreateInstance(FxWdfDriver, FxDeviceInit, &device); + + // + // TODO: Change any per-device settings that the object exposes before + // calling Configure to let it complete its initialization. + // + + // + // If that succeeded then call the device's construct method. This + // allows the device to create any queues or other structures that it + // needs now that the corresponding fx device object has been created. + // + + if (SUCCEEDED(hr)) + { + hr = device->Configure(); + } + + // + // Release the reference on the device callback object now that it's been + // associated with an fx device object. + // + + if (NULL != device) + { + device->Release(); + } + + return hr; +} diff --git a/general/echo/umdf/Driver.h b/general/echo/umdf/Driver.h new file mode 100644 index 00000000..643ea5a5 --- /dev/null +++ b/general/echo/umdf/Driver.h @@ -0,0 +1,149 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Driver.h + +Abstract: + + This module contains the type definitions for the UMDF sample's + driver callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// This class handles driver events for the sample. In particular +// it supports the OnDeviceAdd event, which occurs when the driver is called +// to setup per-device handlers for a new device stack. +// + +class CMyDriver : public CUnknown, public IDriverEntry +{ +// +// Private data members. +// +private: + +// +// Private methods. +// +private: + + // + // Returns a refernced pointer to the IDriverEntry interface. + // + + IDriverEntry * + QueryIDriverEntry( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + HRESULT + Initialize( + VOID + ); + +// +// Public methods +// +public: + + // + // The factory method used to create an instance of this driver. + // + + static + HRESULT + CreateInstance( + _Out_ PCMyDriver *Driver + ); + +// +// COM methods +// +public: + + // + // IDriverEntry methods + // + + virtual + HRESULT + STDMETHODCALLTYPE + OnInitialize( + _In_ IWDFDriver *FxWdfDriver + ) + { + UNREFERENCED_PARAMETER( FxWdfDriver ); + + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ); + + virtual + VOID + STDMETHODCALLTYPE + OnDeinitialize( + _In_ IWDFDriver *FxWdfDriver + ) + { + UNREFERENCED_PARAMETER( FxWdfDriver ); + + return; + } + + // + // IUnknown methods. + // + // We have to implement basic ones here that redirect to the + // base class becuase of the multiple inheritance. + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + _At_(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); +}; diff --git a/general/echo/umdf/Echo.rc b/general/echo/umdf/Echo.rc new file mode 100644 index 00000000..2a26d85c --- /dev/null +++ b/general/echo/umdf/Echo.rc @@ -0,0 +1,21 @@ +//--------------------------------------------------------------------------- +// Echo.rc +// +// Copyright (c) Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include + +// +// TODO: Change the file description and file names to match your binary. +// + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WDF:UMDF Echo User-Mode Driver Sample" +#define VER_INTERNALNAME_STR "UMDFEcho" +#define VER_ORIGINALFILENAME_STR "UMDFEcho.dll" + +#include "common.ver" diff --git a/general/echo/umdf/Queue.cpp b/general/echo/umdf/Queue.cpp new file mode 100644 index 00000000..f366fe31 --- /dev/null +++ b/general/echo/umdf/Queue.cpp @@ -0,0 +1,545 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + queue.cpp + +Abstract: + + This file implements the I/O queue interface and performs + the read/write/ioctl operations. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + + +#include "internal.h" + +// +// IUnknown implementation +// + +// +// Queue destructor. +// Free up the buffer, wait for thread to terminate and +// delete critical section. +// + + +CMyQueue::~CMyQueue( + VOID + ) +/*++ + +Routine Description: + + + IUnknown implementation of Release + +Arguments: + + +Return Value: + + ULONG (reference count after Release) + +--*/ +{ + if (m_Buffer) { + delete [] m_Buffer; + } + + if (m_InitCritSec) { + ::DeleteCriticalSection(&m_Crit); + } +} + + +// +// Initialize +HRESULT +CMyQueue::CreateInstance( + _In_ IWDFDevice *FxDevice, + _Out_ PCMyQueue *Queue + ) +/*++ + +Routine Description: + + + CreateInstance creates an instance of the queue object. + +Arguments: + + ppUkwn - OUT parameter is an IUnknown interface to the queue object + +Return Value: + + HRESULT indicating success or failure + +--*/ +{ + CMyQueue *pMyQueue = new CMyQueue; + HRESULT hr; + + if (pMyQueue == NULL) { + return E_OUTOFMEMORY; + } + + hr = pMyQueue->Initialize(FxDevice); + + if (SUCCEEDED(hr)) + { + *Queue = pMyQueue; + } + else + { + pMyQueue->Release(); + } + return hr; +} + +HRESULT +CMyQueue::Initialize( + _In_ IWDFDevice *FxDevice + ) +{ + IWDFIoQueue *fxQueue; + HRESULT hr; + + // + // Initialize the critical section before we continue + // + + if (!InitializeCriticalSectionAndSpinCount(&m_Crit,0x80000400)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Exit; + } + m_InitCritSec = TRUE; + + // + // Create the framework queue + // + + { + IUnknown *unknown = QueryIUnknown(); + hr = FxDevice->CreateIoQueue(unknown, + TRUE, + WdfIoQueueDispatchSequential, + TRUE, + FALSE, + &fxQueue); + unknown->Release(); + } + + if (FAILED(hr)) + { + goto Exit; + } + + m_FxQueue = fxQueue; + + fxQueue->Release(); + +Exit: + return hr; +} + +HRESULT +STDMETHODCALLTYPE +CMyQueue::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + +Routine Description: + + + Query Interface + +Arguments: + + Follows COM specifications + +Return Value: + + HRESULT indicating success or failure + +--*/ +{ + HRESULT hr; + + + if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackWrite))) { + *Object = QueryIQueueCallbackWrite(); + hr = S_OK; + } else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackRead))) { + *Object = QueryIQueueCallbackRead(); + hr = S_OK; + } else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackDeviceIoControl))) { + *Object = QueryIQueueCallbackDeviceIoControl(); + hr = S_OK; + } else { + hr = CUnknown::QueryInterface(InterfaceId, Object); + } + + return hr; +} + +VOID +STDMETHODCALLTYPE +CMyQueue::OnDeviceIoControl( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T InputBufferSizeInBytes, + _In_ SIZE_T OutputBufferSizeInBytes + ) +/*++ + +Routine Description: + + + DeviceIoControl dispatch routine + +Arguments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + ControlCode - IO Control Code + InputBufferSizeInBytes - Length of input buffer + OutputBufferSizeInBytes - Length of output buffer + + Always succeeds DeviceIoIoctl +Return Value: + + VOID + +--*/ +{ + + UNREFERENCED_PARAMETER(pWdfQueue); + UNREFERENCED_PARAMETER(ControlCode); + UNREFERENCED_PARAMETER(InputBufferSizeInBytes); + UNREFERENCED_PARAMETER(OutputBufferSizeInBytes); + + pWdfRequest->Complete(S_OK); + return; +} + +VOID +STDMETHODCALLTYPE +CMyQueue::OnWrite( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T BytesToWrite + ) +/*++ + +Routine Description: + + + Write dispatch routine + IQueueCallbackWrite + +Arguments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + BytesToWrite - Length of bytes in the write buffer + + Allocate and copy data to local buffer +Return Value: + + VOID + +--*/ +{ + + HRESULT hr; + IWDFMemory* pRequestMemory = NULL; + IWDFIoRequest2 * pWdfRequest2 = NULL; + + UNREFERENCED_PARAMETER(pWdfQueue); + + // + // Handle Zero length writes. + // + + if (!BytesToWrite) { + pWdfRequest->CompleteWithInformation(S_OK, 0); + return; + } + + if( BytesToWrite > MAX_WRITE_LENGTH ) { + + pWdfRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_MORE_DATA), 0); + return; + } + + // Release previous buffer if set + + if( m_Buffer != NULL ) { + delete [] m_Buffer; + m_Buffer = NULL; + m_Length = 0L; + } + + // Allocate Buffer + + m_Buffer = new UCHAR[BytesToWrite]; + if (m_Buffer == NULL) { + pWdfRequest->Complete(E_OUTOFMEMORY); + m_Length = 0L; + return; + } + + // Get memory object + hr = pWdfRequest->QueryInterface(IID_PPV_ARGS(&pWdfRequest2)); + + if (FAILED(hr)) { + goto Exit; + } + + hr = pWdfRequest2->RetrieveInputMemory(&pRequestMemory); + + if (FAILED(hr)) { + goto Exit; + } + + // Copy from memory object to our buffer + + hr = pRequestMemory->CopyToBuffer(0, m_Buffer, BytesToWrite); + + if (FAILED(hr)) { + goto Exit; + } + + // + // Release memory object. + // + SAFE_RELEASE(pRequestMemory); + + // + // Save the information so that we can use it + // to complete the request later. + // + + Lock(); + + m_Length = (ULONG) BytesToWrite; + m_XferredBytes = m_Length; + m_CurrentRequest = pWdfRequest2; + + Unlock(); + +Exit: + + if (FAILED(hr)) { + if (pWdfRequest2) { + pWdfRequest2->CompleteWithInformation(hr, 0); + } + delete [] m_Buffer; + m_Buffer = NULL; + SAFE_RELEASE(pRequestMemory); + } + + // + // This is an early release. pWdfRequest2 will be released, when the request is completed + // + SAFE_RELEASE(pWdfRequest2); + + return; +} + +VOID +STDMETHODCALLTYPE +CMyQueue::OnRead( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T SizeInBytes + ) +/*++ + +Routine Description: + + + Read dispatch routine + IQueueCallbackRead + +Arguments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + SizeInBytes - Length of bytes in the read buffer + + Copy available data into the read buffer +Return Value: + + VOID + +--*/ +{ + IWDFMemory* pRequestMemory = NULL; + IWDFIoRequest2 * pWdfRequest2 = NULL; + HRESULT hr; + + UNREFERENCED_PARAMETER(pWdfQueue); + + // + // Handle Zero length reads. + // + + if (!SizeInBytes) { + pWdfRequest->CompleteWithInformation(S_OK, 0); + return; + } + + if (m_Buffer == NULL) { + pWdfRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), SizeInBytes); + return; + } + + if (m_Length < SizeInBytes) { + SizeInBytes = m_Length; + } + + // + // Get memory object + // + + hr = pWdfRequest->QueryInterface(IID_PPV_ARGS(&pWdfRequest2)); + + if (FAILED(hr)) { + goto Exit; + } + + hr = pWdfRequest2->RetrieveOutputMemory(&pRequestMemory ); + + if (FAILED(hr)) { + goto Exit; + } + + // Copy from buffer to memory object + + hr = pRequestMemory->CopyFromBuffer(0, m_Buffer, SizeInBytes); + + if (FAILED(hr)) { + goto Exit; + } + + // + // Release memory object. + // + + SAFE_RELEASE(pRequestMemory); + + // + // Save the information so that we can use it + // to complete the request later. + // + + Lock(); + + m_CurrentRequest = pWdfRequest2; + m_XferredBytes = SizeInBytes; + + Unlock(); + +Exit: + + if (FAILED(hr)) { + if (pWdfRequest2) { + pWdfRequest2->CompleteWithInformation(hr, 0); + } + SAFE_RELEASE(pRequestMemory); + } + + // + // This is an early release. pWdfRequest2 will be released, when the request is completed + // + SAFE_RELEASE(pWdfRequest2); + + return; +} + +DWORD +CMyQueue::CompletionThread( + PVOID ThreadParameter + ) +/*++ + +Routine Description: + + + This routine is called from the thread started to complete + I/O requests. It sleeps for TIMER_PERIOD and then completes + the current request. Note that it has to release the lock + before it calls the request complete method. + +Arguments: + + ThreadParameter - This is a pointer to the Queue object. + +Return Value: + + VOID + +--*/ +{ + CMyQueue *pQueue = (CMyQueue *)ThreadParameter; + IWDFIoRequest2 *request; + SIZE_T bytesXferred = 0; + + for (;;) { + + // + // Block for a fixed time and then complete the request. + // + + Sleep(TIMER_PERIOD); + + pQueue->Lock(); + + // + // Process the current request. + // + + request = pQueue->m_CurrentRequest; + + if (request) { + bytesXferred = pQueue->m_XferredBytes; + } + + // + // Reset values. + // + + pQueue->m_CurrentRequest = NULL; + pQueue->m_XferredBytes = 0; + + + pQueue->Unlock(); + + if (request) { + request->CompleteWithInformation(S_OK, bytesXferred); + } + + // + // If thread needs to be terminated + // + + if (pQueue->m_ExitThread) { + ExitThread(0); + } + + } + +} diff --git a/general/echo/umdf/Queue.h b/general/echo/umdf/Queue.h new file mode 100644 index 00000000..c4b28c97 --- /dev/null +++ b/general/echo/umdf/Queue.h @@ -0,0 +1,213 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + queue.h + +Abstract: + + This file defines the queue callback interface. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// Set max write length for testing +#define MAX_WRITE_LENGTH (40*1024) + +// Set timer period in ms +#define TIMER_PERIOD 100 + +// +// Queue Callback Object. +// + +class CMyQueue : + public IQueueCallbackDeviceIoControl, + public IQueueCallbackRead, + public IQueueCallbackWrite, + public CUnknown +{ + PVOID m_Buffer; // Current buffer + ULONG m_Length; // Length of the buffer + SIZE_T m_XferredBytes; // Amount of bytes transferred for the current request + IWDFIoRequest2 *m_CurrentRequest; // Current request + CRITICAL_SECTION m_Crit; // Lock to protect updates to CMyQueue fields + BOOLEAN m_ExitThread; // If TRUE Terminate thread. + BOOLEAN m_InitCritSec; // If TRUE lock initialized + + IWDFIoQueue *m_FxQueue; + + CMyQueue() : + m_Buffer(NULL), + m_Length (0), + m_CurrentRequest(NULL), + m_XferredBytes(0), + m_ExitThread(FALSE), + m_InitCritSec(FALSE), + m_FxQueue(NULL) + { + } + + virtual ~CMyQueue(); + + _Acquires_lock_(this->m_Crit) + __inline + void + Lock( + ) + { + ::EnterCriticalSection(&m_Crit); + } + + _Releases_lock_(this->m_Crit) + __inline + void + Unlock( + ) + { + ::LeaveCriticalSection(&m_Crit); + } + + HRESULT + Initialize( + _In_ IWDFDevice *FxDevice + ); + +public: + + // + // Completion thread routine. + // + + static DWORD CompletionThread( PVOID ThreadParameter); + + // + // Sets the flag to make thread exit + // + + void + SetExitThread() + { + m_ExitThread = TRUE; + } + + static + HRESULT + CreateInstance( + _In_ IWDFDevice *FxDevice, + _Out_ PCMyQueue *Queue + ); + + HRESULT + Configure( + VOID + ) + { + return S_OK; + } + + + IQueueCallbackDeviceIoControl * + QueryIQueueCallbackDeviceIoControl( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + IQueueCallbackRead * + QueryIQueueCallbackRead( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + IQueueCallbackWrite * + QueryIQueueCallbackWrite( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + // + // IUnknown + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) { + return CUnknown::AddRef(); + } + + _At_(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) { + return CUnknown::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + // + // Wdf Callbacks + // + + // IQueueCallbackDeviceIoControl + // + virtual + VOID + STDMETHODCALLTYPE + OnDeviceIoControl( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T InputBufferSizeInBytes, + _In_ SIZE_T OutputBufferSizeInBytes + ); + + // IQueueCallbackWrite + // + virtual + VOID + STDMETHODCALLTYPE + OnWrite( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T NumOfBytesToWrite + ); + + // IQueueCallbackRead + // + virtual + VOID + STDMETHODCALLTYPE + OnRead( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T NumOfBytesToRead + ); +}; diff --git a/general/echo/umdf/ReadMe.md b/general/echo/umdf/ReadMe.md new file mode 100644 index 00000000..750d34df --- /dev/null +++ b/general/echo/umdf/ReadMe.md @@ -0,0 +1,124 @@ +Echo Sample (UMDF Version 1) +============================ + +This sample demonstrates how to use User-Mode Driver Framework (UMDF) version 1 to write a driver and demonstrates best practices. + +It also demonstrates the use of a default Serial Dispatch I/O Queue, its request start events, cancellation event, and synchronizing with another thread. The preferred I/O retrieval mode is set to Direct I/O. So, whenever a request is received by the framework, UMDF looks at the size of the buffer and determines, whether it should copy the buffer (if the length is less than 2 full pages) or map it (if the length is greater or equal to 2 full pages). + +This sample driver is a minimal driver meant to demonstrate the usage of the User-Mode Driver Framework. It is not intended for use in a production environment. + + +Related technologies +-------------------- + +[User-Mode Driver Framework](http://msdn.microsoft.com/en-us/library/windows/hardware/ff560456) + +Testing +------- + +To test the Echo driver, you can run echoapp.exe which is built from src\\general\\echo\\exe. + +First install the device as described above. Then run echoapp.exe. + +``` {.syntax xml:space="preserve"} +D:\>echoapp /? +Usage: +Echoapp.exe --- Send single write and read request synchronously +Echoapp.exe -Async --- Send 100 reads and writes asynchronously +Exit the app anytime by pressing Ctrl-C + +D:\>echoapp +DevicePath: \\?\root#sample#0000#{cdc35b6e-0be4-4936-bf5f-5537380a7c1a} +Opened device successfully +512 Pattern Bytes Written successfully +512 Pattern Bytes Read successfully +Pattern Verified successfully + +D:\>echoapp -Async +DevicePath: \\?\root#sample#0000#{cdc35b6e-0be4-4936-bf5f-5537380a7c1a} +Opened device successfully +Starting AsyncIo +Number of bytes written by request number 0 is 1024 +Number of bytes read by request number 0 is 1024 +Number of bytes read by request number 1 is 1024 +Number of bytes written by request number 2 is 1024 +Number of bytes read by request number 2 is 1024 +Number of bytes written by request number 3 is 1024 +Number of bytes read by request number 3 is 1024 +Number of bytes written by request number 4 is 1024 +Number of bytes read by request number 4 is 1024 +Number of bytes written by request number 5 is 1024 +Number of bytes read by request number 5 is 1024 +Number of bytes written by request number 6 is 1024 +Number of bytes read by request number 6 is 1024 +Number of bytes written by request number 7 is 1024 +Number of bytes read by request number 7 is 1024 +Number of bytes written by request number 8 is 1024 +Number of bytes read by request number 8 is 1024 +Number of bytes written by request number 9 is 1024 +Number of bytes read by request number 9 is 1024 +Number of bytes written by request number 10 is 1024 +Number of bytes read by request number 10 is 1024 +Number of bytes written by request number 11 is 1024 +... +``` + +Note that the reads and writes are performed by independent threads in the echo test application. As a result the order of the output may not exactly match what you see above. + +File Manifest +------------- + +File + +Description + +comsup.cpp & comsup.h + +COM Support code - specifically base classes which provide implementations for the standard COM interfaces IUnknown and IClassFactory which are used throughout this sample. + +The implementation of IClassFactory is designed to create instances of the CMyDriver class. If you should change the name of your base driver class, you would also need to modify this file. + +dllsup.cpp + +DLL Support code - provides the DLL's entry point as well as the single required export (DllGetClassObject). + +These depend on comsup.cpp to perform the necessary class creation. + +exports.def + +This file lists the functions that the driver DLL exports. + +internal.h + +This is the main header file for this driver. + +Driver.cpp and Driver.h + +DriverEntry and events on the driver object. + +Device.cpp and Device.h + +The Events on the device object. + +Queue.cpp and Queue.h + +Contains Events on the I/O Queue Objects. + +Echo.rc + +Resource file for the driver. + +WUDFEchoDriver.inx + +File that describes the installation of this driver. The build process converts this into an INF file. + +makefile.inc + +A makefile that defines custom build actions. This includes the conversion of the .INX file into a .INF file + +echodriver.ctl + +This file lists the WPP trace control GUID(s) for the sample driver. This file can be used with the tracelog command's -guid flag to enable the collection of these trace events within an established trace session. + +These GUIDs must remain in sync with the trace control GUIDs defined in internal.h. + diff --git a/general/echo/umdf/WUDFEchoDriver.inx b/general/echo/umdf/WUDFEchoDriver.inx new file mode 100644 index 00000000..6279783e --- /dev/null +++ b/general/echo/umdf/WUDFEchoDriver.inx @@ -0,0 +1,87 @@ +; +; WUDFEchoDriver.inf +; + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFTWUDF% +CatalogFile=WUDF.cat +DriverVer=03/20/2003,5.00.3788 + +[Manufacturer] +%MSFTWUDF%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%EchoDeviceName%=Echo_Install,WUDF\Echo + +[ClassInstall32] +AddReg=SampleClass_RegistryAdd + +[SampleClass_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"-10" + +[SourceDisksFiles] +WUDFEchoDriver.dll=1 +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== WUDF Echo Test Driver ================================== + +[Echo_Install.NT] +CopyFiles=UMDriverCopy + +[Echo_Install.NT.hw] + +[Echo_Install.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[Echo_Install.NT.CoInstallers] +AddReg = CoInstallers_AddReg +CopyFiles = CoInstallers_CopyFiles + +[Echo_Install.NT.Wdf] +UmdfService=WUDFEchoDriver,WUDFEchoDriver_Install +UmdfServiceOrder=WUDFEchoDriver + +; if device can do either direct i/o or buffered transfer mode, +; umdf defaults to direct i/o if devices is not pooled. +UmdfHostProcessSharing=ProcessSharingDisabled + +[WUDFEchoDriver_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID={7AB7DCF5-D1D4-4085-9547-1DB968CCA720} +ServiceBinary=%12%\UMDF\WUDFEchoDriver.dll + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +[CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WudfUpdate_$UMDFCOINSTALLERVERSION$.dll" + +[CoInstallers_CopyFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to drivers\UMDF +CoInstallers_CopyFiles=11 + +[UMDriverCopy] +WUDFEchoDriver.dll + +; =================== Generic ================================== + +[Strings] +MSFTWUDF="Microsoft Internal (WUDF)" +MediaDescription="Microsoft WUDF Sample Driver Installation Media" +ClassName="Sample Device" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" +EchoDeviceName="Sample WUDF Echo Driver" diff --git a/general/echo/umdf/WUDFEchoDriver.vcxproj b/general/echo/umdf/WUDFEchoDriver.vcxproj new file mode 100644 index 00000000..242f1dda --- /dev/null +++ b/general/echo/umdf/WUDFEchoDriver.vcxproj @@ -0,0 +1,256 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {9E7A6816-063C-4560-A31F-C5472D1CE345} + $(MSBuildProjectName) + 1 + Debug + Win32 + {164B0F80-BB88-48C9-A03F-3F3937D8CCB6} + + + + Windows10 + False + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + internal.h + + + $(InfArch) + true + .\$(IntDir)\WudfEchoDriver.inf + + + true + true + internal.h + + + + WUDFEchoDriver + + + WUDFEchoDriver + + + WUDFEchoDriver + + + WUDFEchoDriver + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + 0x0A00 + 0x0A000000 + + + 0x0A00 + 0x0A000000 + + + 0x0A00 + 0x0A000000 + + + 0x0A00 + 0x0A000000 + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/echo/umdf/WUDFEchoDriver.vcxproj.Filters b/general/echo/umdf/WUDFEchoDriver.vcxproj.Filters new file mode 100644 index 00000000..b702fb21 --- /dev/null +++ b/general/echo/umdf/WUDFEchoDriver.vcxproj.Filters @@ -0,0 +1,54 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {0FBE107F-4905-4E97-BFF9-8C2A6B03AED8} + + + h;hpp;hxx;hm;inl;inc;xsd + {9534FE43-2493-4430-A335-97FC2BE106BD} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A96A2015-2537-47ED-B43D-8C379EDEC69F} + + + inf;inv;inx;mof;mc; + {203FBCD2-49E2-491E-9D3F-03A028ED6CEE} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/echo/umdf/dllsup.cpp b/general/echo/umdf/dllsup.cpp new file mode 100644 index 00000000..e7200a28 --- /dev/null +++ b/general/echo/umdf/dllsup.cpp @@ -0,0 +1,176 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + dllsup.cpp + +Abstract: + + This module contains the implementation of the UMDF Echo Sample + Driver's entry point and its exported functions for providing COM support. + + This module can be copied without modification to a new UMDF driver. It + depends on some of the code in comsup.cpp & comsup.h to handle DLL + registration and creating the first class factory. + + This module is dependent on the following defines: + + MYDRIVER_TRACING_ID - A wide string passed to WPP when initializing + tracing. For example the echo driver uses + L"Microsoft\\UMDF\\Echo" + + MYDRIVER_CLASS_ID - A GUID encoded in struct format used to + initialize the driver's ClassID. + + These are defined in internal.h for the sample. If you choose + to use a different primary include file, you should ensure they are + defined there as well. + +Environment: + + WDF User-Mode Driver Framework (WDF:UMDF) + +--*/ + +#include "internal.h" +#include "dllsup.tmh" + +const GUID CLSID_MyDriverCoClass = MYDRIVER_CLASS_ID; + +BOOL +WINAPI +DllMain( + HINSTANCE ModuleHandle, + DWORD Reason, + PVOID /* Reserved */ + ) +/*++ + + Routine Description: + + This is the entry point and exit point for the I/O trace driver. This + does very little as the I/O trace driver has minimal global data. + + This method initializes tracing. + + Arguments: + + ModuleHandle - the DLL handle for this module. + + Reason - the reason this entry point was called. + + Reserved - unused + + Return Value: + + TRUE + +--*/ +{ + + UNREFERENCED_PARAMETER(ModuleHandle); + + if (DLL_PROCESS_ATTACH == Reason) + { + // + // Initialize tracing. + // + + WPP_INIT_TRACING(MYDRIVER_TRACING_ID); + } + else if (DLL_PROCESS_DETACH == Reason) + { + // + // Cleanup tracing. + // + + WPP_CLEANUP(); + } + + return TRUE; +} + +HRESULT +STDAPICALLTYPE +DllGetClassObject( + _In_ REFCLSID ClassId, + _In_ REFIID InterfaceId, + _Outptr_ LPVOID *Interface + ) +/*++ + + Routine Description: + + This routine is called by COM in order to instantiate the + driver callback object and do an initial query interface on it. + + This method only creates an instance of the driver's class factory, as this + is the minimum required to support UMDF. + + Arguments: + + ClassId - the CLSID of the object being "gotten" + + InterfaceId - the interface the caller wants from that object. + + Interface - a location to store the referenced interface pointer + + Return Value: + + S_OK if the function succeeds or error indicating the cause of the + failure. + +--*/ +{ + PCClassFactory factory; + + HRESULT hr = S_OK; + + *Interface = NULL; + + // + // If the CLSID doesn't match that of our "coclass" (defined in the IDL + // file) then we can't create the object the caller wants. This may + // indicate that the COM registration is incorrect, and another CLSID + // is referencing this drvier. + // + + if (IsEqualCLSID(ClassId, CLSID_MyDriverCoClass) == false) + { + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Called to create instance of unrecognized class (%!GUID!)", + &ClassId + ); + + return CLASS_E_CLASSNOTAVAILABLE; + } + + // + // Create an instance of the class factory for the caller. + // + + factory = new CClassFactory(); + + if (NULL == factory) + { + hr = E_OUTOFMEMORY; + } + + // + // Query the object we created for the interface the caller wants. After + // that we release the object. This will drive the reference count to + // 1 (if the QI succeeded an referenced the object) or 0 (if the QI failed). + // In the later case the object is automatically deleted. + // + + if (SUCCEEDED(hr)) + { + hr = factory->QueryInterface(InterfaceId, Interface); + factory->Release(); + } + + return hr; +} diff --git a/general/echo/umdf/echo.sln b/general/echo/umdf/echo.sln new file mode 100644 index 00000000..853b7d55 --- /dev/null +++ b/general/echo/umdf/echo.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFEchoDriver", "WUDFEchoDriver.vcxproj", "{9E7A6816-063C-4560-A31F-C5472D1CE345}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9E7A6816-063C-4560-A31F-C5472D1CE345}.Debug|Win32.ActiveCfg = Debug|Win32 + {9E7A6816-063C-4560-A31F-C5472D1CE345}.Debug|Win32.Build.0 = Debug|Win32 + {9E7A6816-063C-4560-A31F-C5472D1CE345}.Release|Win32.ActiveCfg = Release|Win32 + {9E7A6816-063C-4560-A31F-C5472D1CE345}.Release|Win32.Build.0 = Release|Win32 + {9E7A6816-063C-4560-A31F-C5472D1CE345}.Debug|x64.ActiveCfg = Debug|x64 + {9E7A6816-063C-4560-A31F-C5472D1CE345}.Debug|x64.Build.0 = Debug|x64 + {9E7A6816-063C-4560-A31F-C5472D1CE345}.Release|x64.ActiveCfg = Release|x64 + {9E7A6816-063C-4560-A31F-C5472D1CE345}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/general/echo/umdf/echodriver.ctl b/general/echo/umdf/echodriver.ctl new file mode 100644 index 00000000..a0ce2089 --- /dev/null +++ b/general/echo/umdf/echodriver.ctl @@ -0,0 +1 @@ +d93fb470-afb1-4af8-860e-75f726c66f6b WudfEchoDriverTraceGuid diff --git a/general/echo/umdf/exports.def b/general/echo/umdf/exports.def new file mode 100644 index 00000000..ec564639 --- /dev/null +++ b/general/echo/umdf/exports.def @@ -0,0 +1,10 @@ +; Echo.def : Declares the module parameters. + +; +; TODO: Change the library name here to match your binary name. +; + +LIBRARY "WUDFEchoDriver.DLL" + +EXPORTS + DllGetClassObject PRIVATE diff --git a/general/echo/umdf/internal.h b/general/echo/umdf/internal.h new file mode 100644 index 00000000..8e5c2d60 --- /dev/null +++ b/general/echo/umdf/internal.h @@ -0,0 +1,114 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Internal.h + +Abstract: + + This module contains the local type definitions for the UMDF Echo + driver sample. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +// +// Include the WUDF DDI +// + +#include "wudfddi.h" + +// +// Use specstrings for in/out annotation of function parameters. +// + +#include "specstrings.h" + +// +// Forward definitions of classes in the other header files. +// + +typedef class CMyDriver *PCMyDriver; +typedef class CMyDevice *PCMyDevice; +typedef class CMyQueue *PCMyQueue; + +// +// Define the tracing flags. +// +// TODO: Choose a different trace control GUID +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + MyDriverTraceControl, (d93fb470,afb1,4af8,860e,75f726c66f6b), \ + \ + WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ + ) + +#define WPP_FLAG_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAG_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); +// end_wpp +// + +// +// Driver specific #defines +// +// TODO: Change these values to be appropriate for your driver. +// + +#define MYDRIVER_TRACING_ID L"Microsoft\\UMDF\\Echo" +#define MYDRIVER_CLASS_ID {0x7ab7dcf5, 0xd1d4, 0x4085, {0x95, 0x47, 0x1d, 0xb9, 0x68, 0xcc, 0xa7, 0x20}} + +// +// Include the type specific headers. +// + +#include "comsup.h" +#include "driver.h" +#include "device.h" +#include "queue.h" + +__forceinline +#ifdef _PREFAST_ +__declspec(noreturn) +#endif +VOID +WdfTestNoReturn( + VOID + ) +{ + // do nothing. +} + +#define WUDF_TEST_DRIVER_ASSERT(p) \ +{ \ + if ( !(p) ) \ + { \ + DebugBreak(); \ + WdfTestNoReturn(); \ + } \ +} + +#define SAFE_RELEASE(p) {if ((p)) { (p)->Release(); (p) = NULL; }} diff --git a/general/echo/umdf2/ReadMe.md b/general/echo/umdf2/ReadMe.md new file mode 100644 index 00000000..e037a8b5 --- /dev/null +++ b/general/echo/umdf2/ReadMe.md @@ -0,0 +1,77 @@ +Echo Sample (UMDF Version 2) +============================ + +The ECHO (UMDF version 2) sample demonstrates how to use a sequential queue to serialize read and write requests presented to the driver. + +It also shows how to synchronize execution of these events with other asynchronous events such as request cancellation and DPC. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + + +Related technologies +-------------------- + +[User-Mode Driver Framework](http://msdn.microsoft.com/en-us/library/windows/hardware/ff560456) + + +Download and extract the sample +------------------------------- + +Click the download button on this page. Click **Save**, and then click **Open Folder**. Right click the zip file, and choose **Extract All**. Specify or browse to a folder for the extracted files. For example, you could extract to c:\\umdf2echo. + +Open the driver solution in Visual Studio +----------------------------------------- + +Navigate to the folder that has the extracted sample. Double click the solution file (umdf2echo.sln). In Microsoft Visual Studio, locate Solution Explorer. (If this is not already open, choose **Solution Explorer** from the **View** menu.) In Solution Explorer, you can see one solution that contains 3 projects. There is a driver project (Driver-\>AutoSync-\>echo), an application project (Exe-\>echoapp), and a package project named **package** (lower case). + +Set the configuration and platform in Visual Studio +--------------------------------------------------- + +In Visual Studio, in Solution Explorer, right click **Solution**, and choose **Configuration Manager**. Set the configuration and the platform. Make sure that the configuration and platform are the same for both the driver project and the package project. Do not check the **Deploy** boxes. Because this solution uses UMDF version 2, you cannot select a configuration earlier than Windows 8.1. + + +Locate the built driver package +------------------------------- + +In File Explorer, navigate to the folder that contains your built driver package. The location of this folder varies depending on what you set for configuration and platform. For example, if your settings are Win8.1 Debug and x64, the package is in your solution folder under x64\\Win8.1Debug\\Package. + +Run the sample +-------------- + +The computer where you install the driver is called the *target computer* or the *test computer*. Typically this is a separate computer from where you develop and build the driver package. The computer where you develop and build the driver is called the *host computer*. + +The process of moving the driver package to the target computer and installing the driver is called *deploying the driver*. You can deploy a driver sample automatically or manually. + +### Automatic deployment (root enumerated) + +Before you automatically deploy a driver, you must provision the target computer. For instructions, see [Configuring a Computer for Driver Deployment, Testing, and Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/). + +1. On the host computer, in Visual Studio, in Solution Explorer, right click **package** (lower case), and choose **Properties**. Navigate to **Configuration Properties \> Driver Install \> Deployment**. +2. Check **Enable deployment**, and check **Remove previous driver versions before deployment**. For **Target Computer Name**, select the name of a target computer that you provisioned previously. Select **Hardware ID Driver Update**, and enter **root\\ECHO** for the hardware ID. Click **OK**. +3. On the **Build** menu, choose **Build Solution**. + +### Manual deployment (root enumerated) + +Before you manually deploy a driver, you must turn on test signing and install a certificate on the target computer. You also need to copy the [DevCon](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544707) tool to the target computer. For instructions, see [Preparing a Computer for Manual Driver Deployment](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265571). + +1. Copy all of the files in your driver package to a folder on the target computer (for example, c:\\umdf2echoPkg). +2. On the target computer, open a Command Prompt window as Administrator. Navigate to your driver package folder, and enter the following command: + + **devcon install echoum.inf root\\ECHO** + +### View the root enumerated driver in Device Manager + +On the target computer, in a Command Prompt window, enter **devmgmt** to open Device Manager. In Device Manager, on the **View** menu, choose **Devices by type**. In the device tree, locate **Sample WDF ECHO Driver** (for example, this might be under the **Sample Device** node). + +In Device Manager, on the **View** menu, choose **Devices by connection**. Locate **Sample WDF ECHO Driver** as a child of the root node of the device tree. + +Build the sample using MSBuild +------------------------------ + +As an alternative to building the driver sample in Visual Studio, you can build it in a Visual Studio Command Prompt window. In Visual Studio, on the **Tools** menu, choose **Visual Studio Command Prompt**. In the Visual Studio Command Prompt window, navigate to the folder that has the solution file, umdf2echo.sln. Use the MSBuild command to build the solution. Here is an example: + +**msbuild /p:configuration=â€Win8 Release†/p:platform=â€Win32†umdf2echo.sln** + +For more information about using MSBuild to build a driver package, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + diff --git a/general/echo/umdf2/driver/AutoSync/device.c b/general/echo/umdf2/driver/AutoSync/device.c new file mode 100644 index 00000000..48afdb66 --- /dev/null +++ b/general/echo/umdf2/driver/AutoSync/device.c @@ -0,0 +1,202 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + device.c - Device handling events for example driver. + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#include "driver.h" + +NTSTATUS +EchoDeviceCreate( + PWDFDEVICE_INIT DeviceInit + ) +/*++ + +Routine Description: + + Worker routine called to create a device and its software resources. + +Arguments: + + DeviceInit - Pointer to an opaque init structure. Memory for this + structure will be freed by the framework when the WdfDeviceCreate + succeeds. So don't access the structure after that point. + +Return Value: + + NTSTATUS + +--*/ +{ + WDF_OBJECT_ATTRIBUTES deviceAttributes; + PDEVICE_CONTEXT deviceContext; + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDFDEVICE device; + NTSTATUS status; + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); + + // + // Register pnp/power callbacks so that we can start and stop the timer as the device + // gets started and stopped. + // + pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = EchoEvtDeviceSelfManagedIoStart; + pnpPowerCallbacks.EvtDeviceSelfManagedIoSuspend = EchoEvtDeviceSelfManagedIoSuspend; + + #pragma prefast(suppress: 28024, "Function used for both Init and Restart Callbacks") + pnpPowerCallbacks.EvtDeviceSelfManagedIoRestart = EchoEvtDeviceSelfManagedIoStart; + + // + // Register the PnP and power callbacks. Power policy related callbacks will be registered + // later in SotwareInit. + // + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT); + + status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device); + + if (NT_SUCCESS(status)) { + // + // Get the device context and initialize it. WdfObjectGet_DEVICE_CONTEXT is an + // inline function generated by WDF_DECLARE_CONTEXT_TYPE macro in the + // device.h header file. This function will do the type checking and return + // the device context. If you pass a wrong object handle + // it will return NULL and assert if run under framework verifier mode. + // + deviceContext = WdfObjectGet_DEVICE_CONTEXT(device); + deviceContext->PrivateDeviceData = 0; + + // + // Create a device interface so that application can find and talk + // to us. + // + status = WdfDeviceCreateDeviceInterface( + device, + &GUID_DEVINTERFACE_ECHO, + NULL // ReferenceString + ); + + if (NT_SUCCESS(status)) { + // + // Initialize the I/O Package and any Queues + // + status = EchoQueueInitialize(device); + } + } + + return status; +} + + +NTSTATUS +EchoEvtDeviceSelfManagedIoStart( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + This event is called by the Framework when the device is started + or restarted after a suspend operation. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - Failures will result in the device stack being torn down. + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(WdfDeviceGetDefaultQueue(Device)); + LARGE_INTEGER DueTime; + + KdPrint(("--> EchoEvtDeviceSelfManagedIoInit\n")); + + // + // Restart the queue and the periodic timer. We stopped them before going + // into low power state. + // + WdfIoQueueStart(WdfDeviceGetDefaultQueue(Device)); + + DueTime.QuadPart = WDF_REL_TIMEOUT_IN_MS(100); + + WdfTimerStart(queueContext->Timer, DueTime.QuadPart); + + KdPrint(( "<-- EchoEvtDeviceSelfManagedIoInit\n")); + + return STATUS_SUCCESS; +} + +NTSTATUS +EchoEvtDeviceSelfManagedIoSuspend( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + This event is called by the Framework when the device is stopped + for resource rebalance or suspended when the system is entering + Sx state. + + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - The driver is not allowed to fail this function. If it does, the + device stack will be torn down. + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(WdfDeviceGetDefaultQueue(Device)); + + PAGED_CODE(); + + KdPrint(("--> EchoEvtDeviceSelfManagedIoSuspend\n")); + + // + // Before we stop the timer we should make sure there are no outstanding + // i/o. We need to do that because framework cannot suspend the device + // if there are requests owned by the driver. There are two ways to solve + // this issue: 1) We can wait for the outstanding I/O to be complete by the + // periodic timer 2) Register EvtIoStop callback on the queue and acknowledge + // the request to inform the framework that it's okay to suspend the device + // with outstanding I/O. In this sample we will use the 1st approach + // because it's pretty easy to do. We will restart the queue when the + // device is restarted. + // + WdfIoQueueStopSynchronously(WdfDeviceGetDefaultQueue(Device)); + + // + // Stop the watchdog timer and wait for DPC to run to completion if it's already fired. + // + WdfTimerStop(queueContext->Timer, TRUE); + + KdPrint(( "<-- EchoEvtDeviceSelfManagedIoSuspend\n")); + + return STATUS_SUCCESS; +} + + + diff --git a/general/echo/umdf2/driver/AutoSync/device.h b/general/echo/umdf2/driver/AutoSync/device.h new file mode 100644 index 00000000..f29c7908 --- /dev/null +++ b/general/echo/umdf2/driver/AutoSync/device.h @@ -0,0 +1,48 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + device.h + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#include "public.h" + +// +// The device context performs the same job as +// a WDM device extension in the driver frameworks +// +typedef struct _DEVICE_CONTEXT +{ + ULONG PrivateDeviceData; // just a placeholder + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +// +// This macro will generate an inline function called WdfObjectGet_DEVICE_CONTEXT +// which will be used to get a pointer to the device context memory +// in a type safe manner. +// +WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT) + +// +// Function to initialize the device and its callbacks +// +NTSTATUS +EchoDeviceCreate( + PWDFDEVICE_INIT DeviceInit + ); + +// +// Device events +// +EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT EchoEvtDeviceSelfManagedIoStart; +EVT_WDF_DEVICE_SELF_MANAGED_IO_SUSPEND EchoEvtDeviceSelfManagedIoSuspend; + diff --git a/general/echo/umdf2/driver/AutoSync/driver.c b/general/echo/umdf2/driver/AutoSync/driver.c new file mode 100644 index 00000000..34fdd776 --- /dev/null +++ b/general/echo/umdf2/driver/AutoSync/driver.c @@ -0,0 +1,192 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + driver.c + +Abstract: + + This driver demonstrates use of a default I/O Queue, its + request start events, cancellation event, and a synchronized DPC. + + To demonstrate asynchronous operation, the I/O requests are not completed + immediately, but stored in the drivers private data structure, and a timer + will complete it next time the Timer callback runs. + + During the time the request is waiting for the timer callback to run, it is + made cancellable by the call WdfRequestMarkCancelable. This + allows the test program to cancel the request and exit instantly. + + This rather complicated set of events is designed to demonstrate + the driver frameworks synchronization of access to a device driver + data structure, and a pointer which can be a proxy for device hardware + registers or resources. + + This common data structure, or resource is accessed by new request + events arriving, the Timer callback that completes it, and cancel processing. + + Notice the lack of specific lock/unlock operations. + + Even though this example utilizes a serial queue, a parallel queue + would not need any additional explicit synchronization, just a + strategy for managing multiple requests outstanding. + +--*/ + +#include "driver.h" + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. DriverEntry specifies the other entry + points in the function driver, such as EvtDevice and DriverUnload. + +Parameters Description: + + DriverObject - represents the instance of the function driver that is loaded + into memory. DriverEntry must initialize members of DriverObject before it + returns to the caller. DriverObject is allocated by the system before the + driver is loaded, and it is released by the system after the system unloads + the function driver from memory. + + RegistryPath - represents the driver specific path in the Registry. + The function driver can use the path to store driver related data between + reboots. The path does not store hardware instance specific data. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ +{ + WDF_DRIVER_CONFIG config; + NTSTATUS status; + + WDF_DRIVER_CONFIG_INIT(&config, + EchoEvtDeviceAdd + ); + + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + WDF_NO_HANDLE); + if (!NT_SUCCESS(status)) { + KdPrint(("Error: WdfDriverCreate failed 0x%x\n", status)); + return status; + } + +#if DBG + EchoPrintDriverVersion(); +#endif + + return status; +} + +NTSTATUS +EchoEvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + EvtDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. We create and initialize a device object to + represent a new instance of the device. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(Driver); + + KdPrint(("Enter EchoEvtDeviceAdd\n")); + + status = EchoDeviceCreate(DeviceInit); + + return status; +} + +NTSTATUS +EchoPrintDriverVersion( + ) +/*++ +Routine Description: + + This routine shows how to retrieve framework version string and + also how to find out to which version of framework library the + client driver is bound to. + +Arguments: + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status; + WDFSTRING string; + UNICODE_STRING us; + WDF_DRIVER_VERSION_AVAILABLE_PARAMS ver; + + // + // 1) Retreive version string and print that in the debugger. + // + status = WdfStringCreate(NULL, WDF_NO_OBJECT_ATTRIBUTES, &string); + if (!NT_SUCCESS(status)) { + KdPrint(("Error: WdfStringCreate failed 0x%x\n", status)); + return status; + } + + status = WdfDriverRetrieveVersionString(WdfGetDriver(), string); + if (!NT_SUCCESS(status)) { + // + // No need to worry about delete the string object because + // by default it's parented to the driver and it will be + // deleted when the driverobject is deleted when the DriverEntry + // returns a failure status. + // + KdPrint(("Error: WdfDriverRetrieveVersionString failed 0x%x\n", status)); + return status; + } + + WdfStringGetUnicodeString(string, &us); + KdPrint(("Echo Sample %wZ\n", &us)); + + WdfObjectDelete(string); + string = NULL; // To avoid referencing a deleted object. + + // + // 2) Find out to which version of framework this driver is bound to. + // + WDF_DRIVER_VERSION_AVAILABLE_PARAMS_INIT(&ver, 1, 0); + if (WdfDriverIsVersionAvailable(WdfGetDriver(), &ver) == TRUE) { + KdPrint(("Yes, framework version is 1.0\n")); + }else { + KdPrint(("No, framework verison is not 1.0\n")); + } + + return STATUS_SUCCESS; +} + diff --git a/general/echo/umdf2/driver/AutoSync/driver.h b/general/echo/umdf2/driver/AutoSync/driver.h new file mode 100644 index 00000000..b1a5b40a --- /dev/null +++ b/general/echo/umdf2/driver/AutoSync/driver.h @@ -0,0 +1,46 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + driver.h + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#define INITGUID + +#include +#include +#include "device.h" +#include "queue.h" + +#ifndef ASSERT +#if DBG +#define ASSERT( exp ) \ + ((!(exp)) ? \ + (KdPrint(( "\n*** Assertion failed: " #exp "\n\n")), \ + DebugBreak(), \ + FALSE) : \ + TRUE) +#else +#define ASSERT( exp ) +#endif // DBG +#endif // ASSERT + +// +// WDFDRIVER Events +// + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_DEVICE_ADD EchoEvtDeviceAdd; + +NTSTATUS +EchoPrintDriverVersion( + ); + diff --git a/general/echo/umdf2/driver/AutoSync/echo.vcxproj b/general/echo/umdf2/driver/AutoSync/echo.vcxproj new file mode 100644 index 00000000..defa7b4d --- /dev/null +++ b/general/echo/umdf2/driver/AutoSync/echo.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {95360722-8B66-4DD4-957A-DF8B7CA700FB} + $(MSBuildProjectName) + 2 + Debug + Win32 + {F03689D2-F1BC-4D18-B99E-286CA22656D5} + + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + $(InfArch) + true + .\$(IntDir)\EchoUm.inf + + + + echo + + + echo + + + echo + + + echo + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalIncludeDirectories);..\..\exe + + + + + %(AdditionalIncludeDirectories);..\..\exe + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/echo/umdf2/driver/AutoSync/echo.vcxproj.Filters b/general/echo/umdf2/driver/AutoSync/echo.vcxproj.Filters new file mode 100644 index 00000000..75ab6a0f --- /dev/null +++ b/general/echo/umdf2/driver/AutoSync/echo.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {EF21C647-A675-40F9-981A-364CC77BFA4D} + + + h;hpp;hxx;hm;inl;inc;xsd + {DC1E303A-60D0-45E3-AD8E-FA903A23C932} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {04F193A8-1C9D-45EC-ADDB-918233FB151D} + + + inf;inv;inx;mof;mc; + {C0846D78-C3A3-42B0-AFDD-5D33B6AAE456} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/echo/umdf2/driver/AutoSync/echoum.inx b/general/echo/umdf2/driver/AutoSync/echoum.inx new file mode 100644 index 00000000..287c05df --- /dev/null +++ b/general/echo/umdf2/driver/AutoSync/echoum.inx @@ -0,0 +1,89 @@ +;/*++ +; +;Copyright (c) 1990-2000 Microsoft Corporation +; +;Module Name: +; EchoUm.INF +; +;Abstract: +; INF file for installing the Usermode Driver Frameworks Echo Driver +; +;Installation Notes: +; Using Devcon: Type "devcon install EchoUm.inf root\ECHO" to install +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFT% +DriverVer=03/20/2003,5.00.3788 +CatalogFile=wudf.cat + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +Echo.dll = 1,, + +;***************************************** +; ECHO Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%ECHO.DeviceDesc%=ECHO_Device, root\ECHO + +;---------------- copy files + +[ECHO_Device.NT] +CopyFiles=UMDriverCopy + +[UMDriverCopy] +ECHO.dll + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to driversMdf + +;-------------- Service installation +[ECHO_Device.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +;-------------- WDF specific section ------------- +[ECHO_Device.NT.Wdf] +UmdfService=Echo, Echo_Install +UmdfServiceOrder=Echo + +[Echo_Install] +UmdfLibraryVersion=$UMDFVERSION$ +ServiceBinary=%12%\UMDF\echo.dll + +[Strings] +MSFT = "Microsoft" +StdMfg = "(Standard system devices)" +DiskId1 = "WDF Sample ECHO Installation Disk #1" +ECHO.DeviceDesc = "Sample UMDF v2 ECHO Driver" +ClassName = "Sample Device" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" \ No newline at end of file diff --git a/general/echo/umdf2/driver/AutoSync/queue.c b/general/echo/umdf2/driver/AutoSync/queue.c new file mode 100644 index 00000000..3162a683 --- /dev/null +++ b/general/echo/umdf2/driver/AutoSync/queue.c @@ -0,0 +1,541 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + queue.c + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +#include "driver.h" + +NTSTATUS +EchoQueueInitialize( + WDFDEVICE Device + ) +/*++ + +Routine Description: + + + The I/O dispatch callbacks for the frameworks device object + are configured in this function. + + A single default I/O Queue is configured for serial request + processing, and a driver context memory allocation is created + to hold our structure QUEUE_CONTEXT. + + This memory may be used by the driver automatically synchronized + by the Queue's presentation lock. + + The lifetime of this memory is tied to the lifetime of the I/O + Queue object, and we register an optional destructor callback + to release any private allocations, and/or resources. + + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS + +--*/ +{ + WDFQUEUE queue; + NTSTATUS status; + PQUEUE_CONTEXT queueContext; + WDF_IO_QUEUE_CONFIG queueConfig; + WDF_OBJECT_ATTRIBUTES queueAttributes; + + // + // Configure a default queue so that requests that are not + // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto + // other queues get dispatched here. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( + &queueConfig, + WdfIoQueueDispatchSequential + ); + + queueConfig.EvtIoRead = EchoEvtIoRead; + queueConfig.EvtIoWrite = EchoEvtIoWrite; + + // + // Fill in a callback for destroy, and our QUEUE_CONTEXT size + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_CONTEXT); + + // + // Set synchronization scope on queue and have the timer to use queue as + // the parent object so that queue and timer callbacks are synchronized + // with the same lock. + // + queueAttributes.SynchronizationScope = WdfSynchronizationScopeQueue; + + queueAttributes.EvtDestroyCallback = EchoEvtIoQueueContextDestroy; + + status = WdfIoQueueCreate( + Device, + &queueConfig, + &queueAttributes, + &queue + ); + + if( !NT_SUCCESS(status) ) { + KdPrint(("WdfIoQueueCreate failed 0x%x\n",status)); + return status; + } + + // Get our Driver Context memory from the returned Queue handle + queueContext = QueueGetContext(queue); + + queueContext->WriteMemory = NULL; + queueContext->Timer = NULL; + + queueContext->CurrentRequest = NULL; + queueContext->CurrentStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // Create the Queue timer + // + status = EchoTimerCreate(&queueContext->Timer, queue); + if (!NT_SUCCESS(status)) { + KdPrint(("Error creating timer 0x%x\n",status)); + return status; + } + + return status; +} + + +NTSTATUS +EchoTimerCreate( + IN WDFTIMER* Timer, + IN WDFQUEUE Queue + ) +/*++ + +Routine Description: + + Subroutine to create timer. By associating the timerobject with + the queue, we are basically telling the framework to serialize the queue + callbacks with the timer callback. By doing so, we don't have to worry + about protecting queue-context structure from multiple threads accessing + it simultaneously. + +Arguments: + + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status; + WDF_TIMER_CONFIG timerConfig; + WDF_OBJECT_ATTRIBUTES timerAttributes; + + // + // Create a WDFTIMER object + // + WDF_TIMER_CONFIG_INIT(&timerConfig, EchoEvtTimerFunc); + + // + // WDF_OBJECT_ATTRIBUTES_INIT sets AutomaticSerialization to TRUE by default + // + WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes); + timerAttributes.ParentObject = Queue; // Synchronize with the I/O Queue + timerAttributes.ExecutionLevel = WdfExecutionLevelPassive; + + // + // Create a non-periodic timer since WDF does not allow periodic timer + // with autosynchronization at passive level + // + Status = WdfTimerCreate(&timerConfig, + &timerAttributes, + Timer // Output handle + ); + + return Status; +} + + + +VOID +EchoEvtIoQueueContextDestroy( + WDFOBJECT Object +) +/*++ + +Routine Description: + + This is called when the Queue that our driver context memory + is associated with is destroyed. + +Arguments: + + Context - Context that's being freed. + +Return Value: + + VOID + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(Object); + + // + // Release any resources pointed to in the queue context. + // + // The body of the queue context will be released after + // this callback handler returns + // + + // + // If Queue context has an I/O buffer, release it + // + if( queueContext->WriteMemory != NULL ) { + WdfObjectDelete(queueContext->WriteMemory); + queueContext->WriteMemory = NULL; + } + + return; +} + + +VOID +EchoEvtRequestCancel( + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + + Called when an I/O request is cancelled after the driver has marked + the request cancellable. This callback is automatically synchronized + with the I/O callbacks since we have chosen to use frameworks Device + level locking. + +Arguments: + + Request - Request being cancelled. + +Return Value: + + VOID + +--*/ +{ + PQUEUE_CONTEXT queueContext = QueueGetContext(WdfRequestGetIoQueue(Request)); + + KdPrint(("EchoEvtRequestCancel called on Request 0x%p\n", Request)); + + // + // The following is race free by the callside or DPC side + // synchronizing completion by calling + // WdfRequestMarkCancelable(Queue, Request, FALSE) before + // completion and not calling WdfRequestComplete if the + // return status == STATUS_CANCELLED. + // + WdfRequestCompleteWithInformation(Request, STATUS_CANCELLED, 0L); + + // + // This book keeping is synchronized by the common + // Queue presentation lock + // + ASSERT(queueContext->CurrentRequest == Request); + queueContext->CurrentRequest = NULL; + + return; +} + +VOID +EchoEvtIoRead( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + This event is called when the framework receives IRP_MJ_READ request. + It will copy the content from the queue-context buffer to the request buffer. + If the driver hasn't received any write request earlier, the read returns zero. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + + Request - Handle to a framework request object. + + Length - number of bytes to be read. + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS Status; + PQUEUE_CONTEXT queueContext = QueueGetContext(Queue); + WDFMEMORY memory; + size_t writeMemoryLength; + + _Analysis_assume_(Length > 0); + + KdPrint(("EchoEvtIoRead Called! Queue 0x%p, Request 0x%p Length %d\n", + Queue,Request,Length)); + // + // No data to read + // + if( (queueContext->WriteMemory == NULL) ) { + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, (ULONG_PTR)0L); + return; + } + + // + // Read what we have + // + WdfMemoryGetBuffer(queueContext->WriteMemory, &writeMemoryLength); + _Analysis_assume_(writeMemoryLength > 0); + + if( writeMemoryLength < Length ) { + Length = writeMemoryLength; + } + + // + // Get the request memory + // + Status = WdfRequestRetrieveOutputMemory(Request, &memory); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoRead Could not get request memory buffer 0x%x\n", Status)); + WdfVerifierDbgBreakPoint(); + WdfRequestCompleteWithInformation(Request, Status, 0L); + return; + } + + // Copy the memory out + Status = WdfMemoryCopyFromBuffer( memory, // destination + 0, // offset into the destination memory + WdfMemoryGetBuffer(queueContext->WriteMemory, NULL), + Length ); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoRead: WdfMemoryCopyFromBuffer failed 0x%x\n", Status)); + WdfRequestComplete(Request, Status); + return; + } + + // Set transfer information + WdfRequestSetInformation(Request, (ULONG_PTR)Length); + + // Mark the request is cancelable + WdfRequestMarkCancelable(Request, EchoEvtRequestCancel); + + + // Defer the completion to another thread from the timer dpc + queueContext->CurrentRequest = Request; + queueContext->CurrentStatus = Status; + + return; +} + +VOID +EchoEvtIoWrite( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + This event is invoked when the framework receives IRP_MJ_WRITE request. + This routine allocates memory buffer, copies the data from the request to it, + and stores the buffer pointer in the queue-context with the length variable + representing the buffers length. The actual completion of the request + is defered to the periodic timer dpc. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + + Request - Handle to a framework request object. + + Length - number of bytes to be read. + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS Status; + WDFMEMORY memory; + PQUEUE_CONTEXT queueContext = QueueGetContext(Queue); + PVOID writeBuffer = NULL; + + _Analysis_assume_(Length > 0); + + KdPrint(("EchoEvtIoWrite Called! Queue 0x%p, Request 0x%p Length %d\n", + Queue,Request,Length)); + + if( Length > MAX_WRITE_LENGTH ) { + KdPrint(("EchoEvtIoWrite Buffer Length to big %d, Max is %d\n", + Length,MAX_WRITE_LENGTH)); + WdfRequestCompleteWithInformation(Request, STATUS_BUFFER_OVERFLOW, 0L); + return; + } + + // Get the memory buffer + Status = WdfRequestRetrieveInputMemory(Request, &memory); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoWrite Could not get request memory buffer 0x%x\n", + Status)); + WdfVerifierDbgBreakPoint(); + WdfRequestComplete(Request, Status); + return; + } + + // Release previous buffer if set + if( queueContext->WriteMemory != NULL ) { + WdfObjectDelete(queueContext->WriteMemory); + queueContext->WriteMemory = NULL; + } + + Status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES, + NonPagedPoolNx, + 'sam1', + Length, + &queueContext->WriteMemory, + &writeBuffer + ); + + if(!NT_SUCCESS(Status)) { + KdPrint(("EchoEvtIoWrite: Could not allocate %d byte buffer\n", Length)); + WdfRequestComplete(Request, STATUS_INSUFFICIENT_RESOURCES); + return; + } + + + // Copy the memory in + Status = WdfMemoryCopyToBuffer( memory, + 0, // offset into the source memory + writeBuffer, + Length ); + if( !NT_SUCCESS(Status) ) { + KdPrint(("EchoEvtIoWrite WdfMemoryCopyToBuffer failed 0x%x\n", Status)); + WdfVerifierDbgBreakPoint(); + + WdfObjectDelete(queueContext->WriteMemory); + queueContext->WriteMemory = NULL; + + WdfRequestComplete(Request, Status); + return; + } + + // Set transfer information + WdfRequestSetInformation(Request, (ULONG_PTR)Length); + + // Specify the request is cancelable + WdfRequestMarkCancelable(Request, EchoEvtRequestCancel); + + // Defer the completion to another thread from the timer dpc + queueContext->CurrentRequest = Request; + queueContext->CurrentStatus = Status; + + return; +} + + +VOID +EchoEvtTimerFunc( + IN WDFTIMER Timer + ) +/*++ + +Routine Description: + + This is the TimerDPC the driver sets up to complete requests. + This function is registered when the WDFTIMER object is created, and + will automatically synchronize with the I/O Queue callbacks + and cancel routine. + +Arguments: + + Timer - Handle to a framework Timer object. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS Status; + WDFREQUEST Request; + WDFQUEUE queue; + PQUEUE_CONTEXT queueContext ; + + queue = WdfTimerGetParentObject(Timer); + queueContext = QueueGetContext(queue); + + // + // DPC is automatically synchronized to the Queue lock, + // so this is race free without explicit driver managed locking. + // + Request = queueContext->CurrentRequest; + if( Request != NULL ) { + + // + // Attempt to remove cancel status from the request. + // + // The request is not completed if it is already cancelled + // since the EchoEvtIoCancel function has run, or is about to run + // and we are racing with it. + // + Status = WdfRequestUnmarkCancelable(Request); + if( Status != STATUS_CANCELLED ) { + + queueContext->CurrentRequest = NULL; + Status = queueContext->CurrentStatus; + + KdPrint(("CustomTimerDPC Completing request 0x%p, Status 0x%x \n", Request,Status)); + + WdfRequestComplete(Request, Status); + } + else { + KdPrint(("CustomTimerDPC Request 0x%p is STATUS_CANCELLED, not completing\n", + Request)); + } + } + + // + // Restart the Timer since WDF does not allow periodic timer + // with autosynchronization at passive level + // + WdfTimerStart(Timer, WDF_REL_TIMEOUT_IN_MS(TIMER_PERIOD)); + + return; +} + + diff --git a/general/echo/umdf2/driver/AutoSync/queue.h b/general/echo/umdf2/driver/AutoSync/queue.h new file mode 100644 index 00000000..580c5b41 --- /dev/null +++ b/general/echo/umdf2/driver/AutoSync/queue.h @@ -0,0 +1,62 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + queue.h + +Abstract: + + This is a C version of a very simple sample driver that illustrates + how to use the driver framework and demonstrates best practices. + +--*/ + +// Set max write length for testing +#define MAX_WRITE_LENGTH 1024*40 + +// Set timer period in ms +#define TIMER_PERIOD 1000*2 + +// +// This is the context that can be placed per queue +// and would contain per queue information. +// +typedef struct _QUEUE_CONTEXT { + + // Here we allocate a buffer from a test write so it can be read back + WDFMEMORY WriteMemory; + + // Timer DPC for this queue + WDFTIMER Timer; + + // Virtual I/O + WDFREQUEST CurrentRequest; + NTSTATUS CurrentStatus; + +} QUEUE_CONTEXT, *PQUEUE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext) + +NTSTATUS +EchoQueueInitialize( + WDFDEVICE hDevice + ); + +EVT_WDF_IO_QUEUE_CONTEXT_DESTROY_CALLBACK EchoEvtIoQueueContextDestroy; + +// +// Events from the IoQueue object +// +EVT_WDF_REQUEST_CANCEL EchoEvtRequestCancel; +EVT_WDF_IO_QUEUE_IO_READ EchoEvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE EchoEvtIoWrite; + +NTSTATUS +EchoTimerCreate( + IN WDFTIMER* pTimer, + IN WDFQUEUE Queue + ); + +EVT_WDF_TIMER EchoEvtTimerFunc; diff --git a/general/echo/umdf2/exe/echoapp.cpp b/general/echo/umdf2/exe/echoapp.cpp new file mode 100644 index 00000000..9649a407 --- /dev/null +++ b/general/echo/umdf2/exe/echoapp.cpp @@ -0,0 +1,700 @@ +/*++ + +Copyright (c) Microsoft Corporation + +Module Name: + + ioctl.cpp + +Abstract: + + A simple asynch test for usb driver. + + +Environment: + + user mode only + +--*/ + + +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + +#define INITGUID + +#include +#include +#include +#include +#include +#include "public.h" + +#define NUM_ASYNCH_IO 100 +#define BUFFER_SIZE (40*1024) + +#define READER_TYPE 1 +#define WRITER_TYPE 2 + +#define MAX_DEVPATH_LENGTH 256 + +BOOLEAN G_PerformAsyncIo; +BOOLEAN G_LimitedLoops; +ULONG G_AsyncIoLoopsNum; +CHAR G_DevicePath[MAX_DEVPATH_LENGTH]; + + +ULONG +AsyncIo( + PVOID ThreadParameter + ); + +BOOLEAN +PerformWriteReadTest( + IN HANDLE hDevice, + IN ULONG TestLength + ); + +BOOL +GetDevicePath( + IN LPGUID InterfaceGuid, + _Out_writes_(BufLen) PCHAR DevicePath, + _In_ size_t BufLen + ); + + +int __cdecl +main( + _In_ int argc, + _In_reads_(argc) char* argv[] + ) +{ + HANDLE hDevice = INVALID_HANDLE_VALUE; + HANDLE th1 = NULL; + BOOLEAN result = TRUE; + + + if (argc > 1) { + if(!_strnicmp (argv[1], "-Async", 6) ) { + G_PerformAsyncIo = TRUE; + if (argc > 2) { + G_AsyncIoLoopsNum = atoi(argv[2]); + G_LimitedLoops = TRUE; + } + else { + G_LimitedLoops = FALSE; + } + + } else { + printf("Usage:\n"); + printf(" Echoapp.exe --- Send single write and read request synchronously\n"); + printf(" Echoapp.exe -Async --- Send reads and writes asynchronously without terminating\n"); + printf(" Echoapp.exe -Async --- Send reads and writes asynchronously\n"); + printf("Exit the app anytime by pressing Ctrl-C\n"); + result = FALSE; + goto exit; + } + } + + if ( !GetDevicePath( + (LPGUID) &GUID_DEVINTERFACE_ECHO, + G_DevicePath, + sizeof(G_DevicePath)/sizeof(G_DevicePath[0])) ) + { + result = FALSE; + goto exit; + } + + printf("DevicePath: %s\n", G_DevicePath); + + hDevice = CreateFile(G_DevicePath, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if (hDevice == INVALID_HANDLE_VALUE) { + printf("Failed to open device. Error %d\n",GetLastError()); + result = FALSE; + goto exit; + } + + printf("Opened device successfully\n"); + + if(G_PerformAsyncIo) { + + printf("Starting AsyncIo\n"); + + // + // Create a reader thread + // + th1 = CreateThread( NULL, // Default Security Attrib. + 0, // Initial Stack Size, + (LPTHREAD_START_ROUTINE) AsyncIo, // Thread Func + (LPVOID)READER_TYPE, + 0, // Creation Flags + NULL ); // Don't need the Thread Id. + + if (th1 == NULL) { + printf("Couldn't create reader thread - error %d\n", GetLastError()); + result = FALSE; + goto exit; + } + + // + // Use this thread for peforming write. + // + result = (BOOLEAN)AsyncIo((PVOID)WRITER_TYPE); + + }else { + // + // Write pattern buffers and read them back, then verify them + // + result = PerformWriteReadTest(hDevice, 512); + if(!result) { + goto exit; + } + + result = PerformWriteReadTest(hDevice, 30*1024); + if(!result) { + goto exit; + } + + } + +exit: + + if (th1 != NULL) { + WaitForSingleObject(th1, INFINITE); + CloseHandle(th1); + } + + if (hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + } + + return ((result == TRUE) ? 0 : 1); + +} + +PUCHAR +CreatePatternBuffer( + IN ULONG Length + ) +{ + unsigned int i; + PUCHAR p, pBuf; + + pBuf = (PUCHAR)malloc(Length); + if( pBuf == NULL ) { + printf("Could not allocate %d byte buffer\n",Length); + return NULL; + } + + p = pBuf; + + for(i=0; i < Length; i++ ) { + *p = (UCHAR)i; + p++; + } + + return pBuf; +} + +BOOLEAN +VerifyPatternBuffer( + _In_reads_bytes_(Length) PUCHAR pBuffer, + _In_ ULONG Length + ) +{ + unsigned int i; + PUCHAR p = pBuffer; + + for( i=0; i < Length; i++ ) { + + if( *p != (UCHAR)(i & 0xFF) ) { + printf("Pattern changed. SB 0x%x, Is 0x%x\n", + (UCHAR)(i & 0xFF), *p); + return FALSE; + } + + p++; + } + + return TRUE; +} + +BOOLEAN +PerformWriteReadTest( + IN HANDLE hDevice, + IN ULONG TestLength + ) +/* +*/ +{ + ULONG bytesReturned =0; + PUCHAR WriteBuffer = NULL, + ReadBuffer = NULL; + BOOLEAN result = TRUE; + + WriteBuffer = CreatePatternBuffer(TestLength); + if( WriteBuffer == NULL ) { + + result = FALSE; + goto Cleanup; + } + + ReadBuffer = (PUCHAR)malloc(TestLength); + if( ReadBuffer == NULL ) { + + printf("PerformWriteReadTest: Could not allocate %d " + "bytes ReadBuffer\n",TestLength); + + result = FALSE; + goto Cleanup; + + } + + // + // Write the pattern to the device + // + bytesReturned = 0; + + if (!WriteFile ( hDevice, + WriteBuffer, + TestLength, + &bytesReturned, + NULL)) { + + printf ("PerformWriteReadTest: WriteFile failed: " + "Error %d\n", GetLastError()); + + result = FALSE; + goto Cleanup; + + } else { + + if( bytesReturned != TestLength ) { + + printf("bytes written is not test length! Written %d, " + "SB %d\n",bytesReturned, TestLength); + + result = FALSE; + goto Cleanup; + } + + printf ("%d Pattern Bytes Written successfully\n", + bytesReturned); + } + + bytesReturned = 0; + + if ( !ReadFile (hDevice, + ReadBuffer, + TestLength, + &bytesReturned, + NULL)) { + + printf ("PerformWriteReadTest: ReadFile failed: " + "Error %d\n", GetLastError()); + + result = FALSE; + goto Cleanup; + + } else { + + if( bytesReturned != TestLength ) { + + printf("bytes Read is not test length! Read %d, " + "SB %d\n",bytesReturned, TestLength); + + // + // Note: Is this a Failure Case?? + // + result = FALSE; + goto Cleanup; + } + + printf ("%d Pattern Bytes Read successfully\n",bytesReturned); + } + + // + // Now compare + // + if( !VerifyPatternBuffer(ReadBuffer, TestLength) ) { + + printf("Verify failed\n"); + + result = FALSE; + goto Cleanup; + } + + printf("Pattern Verified successfully\n"); + +Cleanup: + + // + // Free WriteBuffer if non NULL. + // + if (WriteBuffer) { + free (WriteBuffer); + } + + // + // Free ReadBuffer if non NULL + // + if (ReadBuffer) { + free (ReadBuffer); + } + + return result; +} + + + +ULONG +AsyncIo( + PVOID ThreadParameter + ) +{ + HANDLE hDevice = INVALID_HANDLE_VALUE; + HANDLE hCompletionPort = NULL; + OVERLAPPED *pOvList = NULL; + PUCHAR buf = NULL; + ULONG numberOfBytesTransferred; + OVERLAPPED *completedOv; + ULONG_PTR i; + ULONG ioType = (ULONG)(ULONG_PTR)ThreadParameter; + ULONG_PTR key; + ULONG error; + BOOLEAN result = TRUE; + ULONG maxPendingRequests = NUM_ASYNCH_IO; + ULONG remainingRequestsToSend = 0; + ULONG remainingRequestsToReceive = 0; + + hDevice = CreateFile(G_DevicePath, + GENERIC_WRITE|GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL ); + + + if (hDevice == INVALID_HANDLE_VALUE) { + printf("Cannot open %s error %d\n", G_DevicePath, GetLastError()); + result = FALSE; + goto Error; + } + + hCompletionPort = CreateIoCompletionPort(hDevice, NULL, 1, 0); + if (hCompletionPort == NULL) { + printf("Cannot open completion port %d \n",GetLastError()); + result = FALSE; + goto Error; + } + + // + // We will only have NUM_ASYNCH_IO or G_AsyncIoLoopsNum pending at any + // time (whichever is less) + // + if (G_LimitedLoops == TRUE) { + remainingRequestsToReceive = G_AsyncIoLoopsNum; + if (G_AsyncIoLoopsNum > NUM_ASYNCH_IO) { + // + // After we send the initial NUM_ASYNCH_IO, we will have additional + // (G_AsyncIoLoopsNum - NUM_ASYNCH_IO) I/Os to send + // + maxPendingRequests = NUM_ASYNCH_IO; + remainingRequestsToSend = G_AsyncIoLoopsNum - NUM_ASYNCH_IO; + } + else { + maxPendingRequests = G_AsyncIoLoopsNum; + remainingRequestsToSend = 0; + + } + } + + pOvList = (OVERLAPPED *)malloc(maxPendingRequests * sizeof(OVERLAPPED)); + if (pOvList == NULL) { + printf("Cannot allocate overlapped array \n"); + result = FALSE; + goto Error; + } + + buf = (PUCHAR)malloc(maxPendingRequests * BUFFER_SIZE); + if (buf == NULL) { + printf("Cannot allocate buffer \n"); + result = FALSE; + goto Error; + } + + ZeroMemory(pOvList, maxPendingRequests * sizeof(OVERLAPPED)); + ZeroMemory(buf, maxPendingRequests * BUFFER_SIZE); + + // + // Issue asynch I/O + // + + for (i = 0; i < maxPendingRequests; i++) { + if (ioType == READER_TYPE) { + if ( ReadFile( hDevice, + buf + (i* BUFFER_SIZE), + BUFFER_SIZE, + NULL, + &pOvList[i]) == 0) { + + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + printf(" %dth Read failed %d \n",i, GetLastError()); + result = FALSE; + goto Error; + } + } + + } else { + if ( WriteFile( hDevice, + buf + (i* BUFFER_SIZE), + BUFFER_SIZE, + NULL, + &pOvList[i]) == 0) { + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + printf(" %dth Write failed %d \n",i, GetLastError()); + result = FALSE; + goto Error; + } + } + } + } + + // + // Wait for the I/Os to complete. If one completes then reissue the I/O + // + + WHILE (1) { + + if ( GetQueuedCompletionStatus(hCompletionPort, &numberOfBytesTransferred, &key, &completedOv, INFINITE) == 0) { + printf("GetQueuedCompletionStatus failed %d\n", GetLastError()); + result = FALSE; + goto Error; + } + + // + // Read successfully completed. If we're doing unlimited I/Os then Issue another one. + // + + if (ioType == READER_TYPE) { + + i = completedOv - pOvList; + printf("Number of bytes read by request number %d is %d\n", i, numberOfBytesTransferred); + + // + // If we're done with the I/Os, then exit + // + if (G_LimitedLoops == TRUE) { + if ((--remainingRequestsToReceive) == 0) { + break; + } + + if (remainingRequestsToSend == 0) { + continue; + } + else { + remainingRequestsToSend--; + } + } + + + if ( ReadFile( hDevice, + buf + (i * BUFFER_SIZE), + BUFFER_SIZE, + NULL, + completedOv) == 0) { + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + printf("%dth Read failed %d \n", i, GetLastError()); + result = FALSE; + goto Error; + } + } + } else { + + i = completedOv - pOvList; + + printf("Number of bytes written by request number %d is %d\n", i, numberOfBytesTransferred); + + // + // If we're done with the I/Os, then exit + // + if (G_LimitedLoops == TRUE) { + if ((--remainingRequestsToReceive) == 0) { + break; + } + + if (remainingRequestsToSend == 0) { + continue; + } + else { + remainingRequestsToSend--; + } + } + + + if ( WriteFile( hDevice, + buf + (i * BUFFER_SIZE), + BUFFER_SIZE, + NULL, + completedOv) == 0) { + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + + printf("%dth write failed %d \n", i, GetLastError()); + result = FALSE; + goto Error; + } + } + } + } + +Error: + if(hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + } + + if(hCompletionPort) { + CloseHandle(hCompletionPort); + } + + if(buf) { + free(buf); + } + if(pOvList) { + free(pOvList); + } + + return (ULONG)result; + +} + + +BOOL +GetDevicePath( + IN LPGUID InterfaceGuid, + _Out_writes_(BufLen) PCHAR DevicePath, + _In_ size_t BufLen + ) +{ + HDEVINFO HardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; + PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = NULL; + ULONG Length, RequiredLength = 0; + BOOL bResult; + HRESULT hr; + + HardwareDeviceInfo = SetupDiGetClassDevs( + InterfaceGuid, + NULL, + NULL, + (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); + + if (HardwareDeviceInfo == INVALID_HANDLE_VALUE) { + printf("SetupDiGetClassDevs failed!\n"); + return FALSE; + } + + DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + bResult = SetupDiEnumDeviceInterfaces(HardwareDeviceInfo, + 0, + InterfaceGuid, + 0, + &DeviceInterfaceData); + + if (bResult == FALSE) { + + LPVOID lpMsgBuf; + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &lpMsgBuf, + 0, + NULL + )) { + + printf("SetupDiEnumDeviceInterfaces failed: %s", (LPTSTR)lpMsgBuf); + LocalFree(lpMsgBuf); + } + + printf("SetupDiEnumDeviceInterfaces failed.\n"); + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + return FALSE; + } + + SetupDiGetDeviceInterfaceDetail( + HardwareDeviceInfo, + &DeviceInterfaceData, + NULL, + 0, + &RequiredLength, + NULL + ); + + DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, RequiredLength); + + if (DeviceInterfaceDetailData == NULL) { + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + printf("Failed to allocate memory.\n"); + return FALSE; + } + + DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + Length = RequiredLength; + + bResult = SetupDiGetDeviceInterfaceDetail( + HardwareDeviceInfo, + &DeviceInterfaceData, + DeviceInterfaceDetailData, + Length, + &RequiredLength, + NULL); + + if (bResult == FALSE) { + + LPVOID lpMsgBuf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &lpMsgBuf, + 0, + NULL + ); + + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + printf("Error in SetupDiGetDeviceInterfaceDetail: %s\n", (LPTSTR)lpMsgBuf); + LocalFree(DeviceInterfaceDetailData); + LocalFree(lpMsgBuf); + return FALSE; + } + + hr = StringCchCopy(DevicePath, + BufLen, + DeviceInterfaceDetailData->DevicePath) ; + + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); // It must be executed in both success and failure traces + LocalFree(DeviceInterfaceDetailData); + + return ( !FAILED(hr) ); // Result depends on StringCchCopy() +} + diff --git a/general/echo/umdf2/exe/echoapp.vcxproj b/general/echo/umdf2/exe/echoapp.vcxproj new file mode 100644 index 00000000..c8b2000b --- /dev/null +++ b/general/echo/umdf2/exe/echoapp.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {2744B9D7-C918-4979-AF41-3DC6B305BA72} + $(MSBuildProjectName) + Debug + Win32 + {725C57DF-D40D-4503-ADBE-A694D0AC15A8} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + echoapp + + + echoapp + + + echoapp + + + echoapp + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/echo/umdf2/exe/echoapp.vcxproj.Filters b/general/echo/umdf2/exe/echoapp.vcxproj.Filters new file mode 100644 index 00000000..1b67a921 --- /dev/null +++ b/general/echo/umdf2/exe/echoapp.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {A27E5252-132E-4B45-BC34-E21958A5A7B8} + + + h;hpp;hxx;hm;inl;inc;xsd + {21C62558-1F78-4174-BB6A-42FE9F273912} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {D51D868F-D9FD-45A5-B7C1-50CBFF474959} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/echo/umdf2/exe/public.h b/general/echo/umdf2/exe/public.h new file mode 100644 index 00000000..d632951d --- /dev/null +++ b/general/echo/umdf2/exe/public.h @@ -0,0 +1,30 @@ +/*++ +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + public.h + +Abstract: + + This module contains the common declarations shared by driver + and user applications. + + +Environment: + + user and kernel + +--*/ + +#define WHILE(a) \ +__pragma(warning(suppress:4127)) while(a) + +// +// Define an Interface Guid so that app can find the device and talk to it. +// + +DEFINE_GUID (GUID_DEVINTERFACE_ECHO, + 0xcdc35b6e, 0xbe4, 0x4936, 0xbf, 0x5f, 0x55, 0x37, 0x38, 0xa, 0x7c, 0x1a); +// {CDC35B6E-0BE4-4936-BF5F-5537380A7C1A} + diff --git a/general/echo/umdf2/umdf2echo.sln b/general/echo/umdf2/umdf2echo.sln new file mode 100644 index 00000000..f7467745 --- /dev/null +++ b/general/echo/umdf2/umdf2echo.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{5B8D0286-7445-4569-84C3-D5616997F792}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AutoSync", "AutoSync", "{DAEA5A04-51AB-46D0-B95C-25DA1FEB0B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{0E50C291-4877-4FE5-A66C-EDEFF747DA75}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echoapp", "exe\echoapp.vcxproj", "{2744B9D7-C918-4979-AF41-3DC6B305BA72}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo", "driver\AutoSync\echo.vcxproj", "{95360722-8B66-4DD4-957A-DF8B7CA700FB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2744B9D7-C918-4979-AF41-3DC6B305BA72}.Debug|Win32.ActiveCfg = Debug|Win32 + {2744B9D7-C918-4979-AF41-3DC6B305BA72}.Debug|Win32.Build.0 = Debug|Win32 + {2744B9D7-C918-4979-AF41-3DC6B305BA72}.Release|Win32.ActiveCfg = Release|Win32 + {2744B9D7-C918-4979-AF41-3DC6B305BA72}.Release|Win32.Build.0 = Release|Win32 + {2744B9D7-C918-4979-AF41-3DC6B305BA72}.Debug|x64.ActiveCfg = Debug|x64 + {2744B9D7-C918-4979-AF41-3DC6B305BA72}.Debug|x64.Build.0 = Debug|x64 + {2744B9D7-C918-4979-AF41-3DC6B305BA72}.Release|x64.ActiveCfg = Release|x64 + {2744B9D7-C918-4979-AF41-3DC6B305BA72}.Release|x64.Build.0 = Release|x64 + {95360722-8B66-4DD4-957A-DF8B7CA700FB}.Debug|Win32.ActiveCfg = Debug|Win32 + {95360722-8B66-4DD4-957A-DF8B7CA700FB}.Debug|Win32.Build.0 = Debug|Win32 + {95360722-8B66-4DD4-957A-DF8B7CA700FB}.Release|Win32.ActiveCfg = Release|Win32 + {95360722-8B66-4DD4-957A-DF8B7CA700FB}.Release|Win32.Build.0 = Release|Win32 + {95360722-8B66-4DD4-957A-DF8B7CA700FB}.Debug|x64.ActiveCfg = Debug|x64 + {95360722-8B66-4DD4-957A-DF8B7CA700FB}.Debug|x64.Build.0 = Debug|x64 + {95360722-8B66-4DD4-957A-DF8B7CA700FB}.Release|x64.ActiveCfg = Release|x64 + {95360722-8B66-4DD4-957A-DF8B7CA700FB}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {2744B9D7-C918-4979-AF41-3DC6B305BA72} = {5B8D0286-7445-4569-84C3-D5616997F792} + {95360722-8B66-4DD4-957A-DF8B7CA700FB} = {DAEA5A04-51AB-46D0-B95C-25DA1FEB0B70} + {DAEA5A04-51AB-46D0-B95C-25DA1FEB0B70} = {0E50C291-4877-4FE5-A66C-EDEFF747DA75} + EndGlobalSection +EndGlobal diff --git a/general/echo/umdfSocketEcho/Driver/Connection.cpp b/general/echo/umdfSocketEcho/Driver/Connection.cpp new file mode 100644 index 00000000..e28d0c56 --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/Connection.cpp @@ -0,0 +1,263 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + Connection.cpp + +Abstract: + + Module for the socket connection specfic routines in the driver. + Makes Connection to the server given server host and port address. + +Environment: + + User mode only + + +--*/ + +#include "internal.h" +#include "connection.tmh" + + +CConnection::CConnection() +/*++ + +Routine Description: + + Constructor for connection object + +Arguments: + + None + +Return Value: + + VOID + +--*/ +{ + + // Initialize the socket member as Invalid + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + m_socket = INVALID_SOCKET; +} + +HRESULT +CConnection::Connect( + IN IWDFDevice *pDevice + ) +/*++ + +Routine Description: + + This routine is for the initialization of the connection object associated with + the File Object . It is invoked from the dispatch OnCreateFile on the default + queue callback of the driver. It socket connection to the client. + +Arguments: + + pDevice = Wdf Device Object + +Return Value: + + S_OK if success , error HRESULT otherwise + +--*/ +{ + + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + + HRESULT hr = S_OK; + + addrinfoW* info = NULL ; + + PWSTR hostStr = NULL; + + PWSTR portStr = NULL; + + // + // Reads the host and port strings stored in the device context. + // + + DeviceContext *pContext = NULL; + + hr = pDevice->RetrieveContext((void**)&pContext); + + if ( FAILED(hr) ) + { + + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: unable to retrieve context from wdf device object %!hresult!", + hr + ); + goto Clean0; + + } + + hostStr = pContext->hostStr; + + portStr = pContext->portStr; + + // + // lookup hostname with addrinfo hints; + // + + addrinfoW hints; + + ZeroMemory(&hints,sizeof(hints)); + + hints.ai_family = AF_INET; + + hints.ai_socktype = SOCK_STREAM; + + hints.ai_protocol = IPPROTO_TCP; + + int n = GetAddrInfoW(hostStr, portStr, &hints, &info); + + if (n != 0) + { + DWORD err = WSAGetLastError(); + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Unable to find address/port of host %!winerr!", + err + ); + hr = HRESULT_FROM_WIN32(err); + goto Clean0; + } + + // + // Create a socket with this infomation recvd in getaddrinfo + // + m_socket = socket(info->ai_family,info->ai_socktype,info->ai_protocol); + + if (m_socket == INVALID_SOCKET) + { + DWORD err = WSAGetLastError(); + + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Unable to create socket %!winerr!", + err + ); + + hr = HRESULT_FROM_WIN32(err); + + goto Clean0; + } + + // + // If that succeeds , proceed to connect to the socket + // + + + ATLASSERT(info->ai_addrlen <= 0x7fffffff); + + int nret = connect(m_socket,info->ai_addr,(int)info->ai_addrlen); + + if (nret == SOCKET_ERROR) + { + DWORD err = WSAGetLastError(); + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Unable to connect to host %!winerr!", + err + ); + hr = HRESULT_FROM_WIN32(err); + + goto Clean0; + } + + +Clean0: + + if (info != NULL) + { + FreeAddrInfoW(info); + + } + + if (FAILED(hr) && m_socket != INVALID_SOCKET) + { + closesocket(m_socket); + m_socket = INVALID_SOCKET; + } + + return hr; + +} + +HANDLE +CConnection::GetSocketHandle( + ) +/*++ + +Routine Description: + + Function returns the socket handle associated with this connection object + +Arguments: + + None + +Return Value: + + Socket handle if valid socket + INVALID_HANDLE_VALUE otherwise + +--*/ +{ + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + if ( INVALID_SOCKET != m_socket ) + { + return (HANDLE)m_socket ; + } + else + { + return INVALID_HANDLE_VALUE; + } + +} + + +VOID +CConnection::Close() +/*++ + +Routine Description: + + Closes the socket connection to the server associated with this connection object + +Arguments: + + None + +Return Value: + + None +--*/ +{ + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + if (m_socket != INVALID_SOCKET) + { + closesocket(m_socket); + m_socket = INVALID_SOCKET; + } + +} diff --git a/general/echo/umdfSocketEcho/Driver/FileContext.h b/general/echo/umdfSocketEcho/Driver/FileContext.h new file mode 100644 index 00000000..dbdda2e4 --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/FileContext.h @@ -0,0 +1,30 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + filecontext.h + +Abstract: + + This header file defines the structure type for file context associated with the file object + +Environment: + + user mode only + +Revision History: + +--*/ + + +#pragma once + +typedef struct _FileContext +{ + CConnection *pConnection ; + + CComPtr pFileTarget; + +}FileContext; diff --git a/general/echo/umdfSocketEcho/Driver/Queue.cpp b/general/echo/umdfSocketEcho/Driver/Queue.cpp new file mode 100644 index 00000000..242925d4 --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/Queue.cpp @@ -0,0 +1,580 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + queue.cpp + +Abstract: + + This file implements the I/O queue interface and performs + the read/write/ioctl operations. + +Environment: + + user mode only + +Revision History: + +--*/ + +#include "internal.h" + +#include "queue.tmh" + +CMyQueue::CMyQueue( + ) : + m_FxQueue(NULL), + m_Device(NULL) +{ +} + +// +// Queue destructor. +// + +CMyQueue::~CMyQueue( + VOID + ) +{ + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); +} + +// +// Initialize +// + +HRESULT +CMyQueue::Initialize( + _In_ CMyDevice * Device + ) +/*++ + +Routine Description: + + Queue Initialize helper routine. + This routine will Create a default parallel queue associated with the Fx device object + and pass the IUnknown for this queue + +Aruments: + Device - Device object pointer + +Return Value: + + S_OK if Initialize succeeds + +--*/ +{ + + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + + CComPtr fxQueue; + + HRESULT hr; + + m_Device = Device; + + // + // Create the I/O Queue object. + // + + { + CComPtr pUnk; + + HRESULT hrQI = this->QueryInterface(__uuidof(IUnknown),(void**)&pUnk); + + WUDF_SAMPLE_DRIVER_ASSERT(SUCCEEDED(hrQI)); + + hr = m_Device->GetFxDevice()->CreateIoQueue( + pUnk, + TRUE, + WdfIoQueueDispatchParallel, + TRUE, + FALSE, + &fxQueue + ); + } + + if (FAILED(hr)) + { + Trace( + TRACE_LEVEL_ERROR, + "Failed to initialize driver queue %!hresult!", + hr + ); + goto Exit; + } + + m_FxQueue = fxQueue; + + +Exit: + + return hr; +} + +HRESULT +CMyQueue::Configure( + VOID + ) +/*++ + +Routine Description: + + Queue configuration function . + It is called after queue object has been succesfully initialized. + +Aruments: + + NONE + + Return Value: + + S_OK if succeeds. + +--*/ +{ + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + + HRESULT hr = S_OK; + + return hr; +} + + +STDMETHODIMP_(void) +CMyQueue::OnCreateFile( + _In_ IWDFIoQueue* pWdfQueue, + _In_ IWDFIoRequest* pWdfRequest, + _In_ IWDFFile* pWdfFileObject + ) + +/*++ + +Routine Description: + + Create callback from the framework for this default parallel queue + + The create request will create a socket connection , create a file i/o target associated + with the socket handle for this connection and store in the file object context. + +Aruments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + pWdfFileObject - WDF file object for this create + + Return Value: + + VOID + +--*/ +{ + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + + HRESULT hr = S_OK; + + CComPtr spFileHandleTargetFactory; + + CComPtr pFileTarget; + + CComPtr pDevice; + + HANDLE SocketHandle = NULL; + + pWdfQueue->GetDevice(&pDevice); + + FileContext *pContext = NULL; + + // + // Create new connection object + // + + CConnection *pConnection = new CConnection(); + + if (NULL == pConnection ) + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Could not create connection object %!hresult!", + hr + ); + goto Exit; + } + + // + // Connect to the socket server + // + + hr = pConnection->Connect(pDevice); + + if (FAILED(hr)) + { + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Could not connect %!hresult!", + hr + ); + + goto Exit; + + } + + // + // If that succeeds, get socket handle for the connection + // + + if ( NULL == (SocketHandle = pConnection->GetSocketHandle()) ) + { + hr = E_FAIL; + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Unable to obtain valid Socket Handle %!hresult!", + hr + ); + goto Exit; + } + + // + // Create file context for this file object + // + + pContext = new FileContext; + + if (NULL == pContext) + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Could not create file context %!hresult!", + hr + ); + goto Exit; + + } + + // + // QI for IWDFFileHandleTargetFactory from the framework device object. + // Note UmdfDispatcher in Wdf Section in the Inf + // + + hr = pDevice->QueryInterface(IID_PPV_ARGS(&spFileHandleTargetFactory)); + + if (FAILED(hr)) + { + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Unable to obtain target factory for creating FileHandle based I/O target %!hresult!", + hr + ); + goto Exit; + } + + // + // If that succeeds, Create a File Handle I/O Target and associate the socket handle with this target + // + + hr = spFileHandleTargetFactory->CreateFileHandleTarget(SocketHandle ,&pFileTarget); + + if (FAILED(hr)) + { + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Unable to create framework I/O target %!hresult!", + hr + ); + goto Exit; + } + + + pContext->pFileTarget = pFileTarget; + + pContext->pConnection = pConnection; + + hr = pWdfFileObject->AssignContext(NULL,(void*)pContext); + + if (FAILED(hr)) + { + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Unable to Assign Context to this File Object %!hresult!", + hr + ); + goto Exit; + } + + + +Exit: + + if (FAILED(hr)) + { + + if ( pFileTarget ) + { + pFileTarget->DeleteWdfObject(); + } + + if (pConnection != NULL) + { + delete pConnection; + pConnection = NULL; + } + + if (pContext != NULL) + { + delete pContext; + pContext = NULL; + } + + } + + pWdfRequest->Complete(hr); + +} + + +STDMETHODIMP_ (void) +CMyQueue::OnWrite( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T BytesToWrite + ) +/*++ + +Routine Description: + + Write callback from the framework for this default parallel queue + + The write request needs to be sent to the file handle i/o target associated with this fileobject + +Aruments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + BytesToWrite - Lenth of bytes in the write buffer + + Return Value: + + VOID + +--*/ +{ + UNREFERENCED_PARAMETER(pWdfQueue); + UNREFERENCED_PARAMETER(BytesToWrite); + + // Call helper function to send request to i/o target + + SendRequestToFileTarget(pWdfRequest); + + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + + return; +} + +STDMETHODIMP_ (void) +CMyQueue::OnRead( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T BytesToRead + ) +/*++ + +Routine Description: + + Read callback from the framework for this default parallel queue + + The read request needs to be sent to the file handle i/o target associated with this fileobject + +Aruments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + BytesToRead - Lenth of bytes in the read buffer + + +Return Value: + + VOID + +--*/ +{ + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + + UNREFERENCED_PARAMETER(pWdfQueue); + UNREFERENCED_PARAMETER(BytesToRead); + + // + // Call helper function to send request to i/o target + // + + SendRequestToFileTarget(pWdfRequest); + + return; +} + +STDMETHODIMP_(void) +CMyQueue::OnCompletion( + _In_ IWDFIoRequest* pWdfRequest, + _In_ IWDFIoTarget* pTarget, + _In_ IWDFRequestCompletionParams* pCompletionParams, + _In_ void* pContext +) +/*++ + +Routine Description: + + This routine is invoked when the request is completed by the lower stack location, + in this case the win32 i/o target associated with the file object of this request + + + Arguments: + + pWdfRequest - wdf request + pTarget - wdf target to which request was earlier sent + pCompletionParams - wdf request completion parameters + pContext - Context information , if any + + +Return Value: + + None +--*/ +{ + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + + UNREFERENCED_PARAMETER(pTarget); + UNREFERENCED_PARAMETER(pContext); + + // Complete request from the driver + pWdfRequest->CompleteWithInformation( + pCompletionParams->GetCompletionStatus(), + pCompletionParams->GetInformation()); +} + +VOID +CMyQueue::SendRequestToFileTarget( + _In_ IWDFIoRequest* pWdfRequest +) +/*++ + +Routine Description: + + This is a helper functiom to send R/W requests to the win32 file i/o target + associated with the socket connection for this request. + First, filecontext is retrieved which has the file i/o target where this request needs to be sent. + + +Arguments: + + pWdfRequest - wdf request + +Return Value: + + None + +--*/ +{ + + HRESULT hr; + + FileContext *pContext = NULL; + CComPtr pWdfFile = NULL; + + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + + // + // Get the file object for this request + // + + pWdfRequest->GetFileObject(&pWdfFile); + + // + // Retrieve Context from file object + // + + hr = pWdfFile->RetrieveContext((void**)&pContext); + + if (pContext == NULL) + { + if ( SUCCEEDED(hr) ) + { + hr = E_FAIL; + Trace(TRACE_LEVEL_ERROR, + " No Context associated with this file object %!hresult!", + hr); + } + goto Exit; + } + + // + // If that succeeds, set completion callback for the request + // + pWdfRequest->SetCompletionCallback(CComQIPtr(this), + NULL); + + // + // Do not modify the request, format using current type + // + + pWdfRequest->FormatUsingCurrentType(); + + // + // Send the request to the win32 i/o target . This was created in OnCreateFile + // + + hr = pWdfRequest->Send(pContext->pFileTarget, + 0, + 0); +Exit: + + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, + "Could not send request to i/o target %!hresult!", + hr); + pWdfRequest->Complete(hr); + } + + return ; +} + +STDMETHODIMP_(void) +CMyQueue::OnCleanup( + _In_ IWDFObject* /*pWdfObject*/ + ) +{ + // + // CMyQueue has a reference to framework device object via m_FxQueue. + // Framework queue object has a reference to CMyQueue object via the callbacks. + // This leads to circular reference and both the objects can't be destroyed until this circular reference is broken. + // To break the circular reference we release the reference to the framework queue object here in OnCleanup. + // + m_FxQueue = NULL; +} diff --git a/general/echo/umdfSocketEcho/Driver/Queue.h b/general/echo/umdfSocketEcho/Driver/Queue.h new file mode 100644 index 00000000..952e1e0d --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/Queue.h @@ -0,0 +1,83 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + queue.h + +Abstract: + + This file defines the queue callback interface. + +Environment: + + user mode only + +Revision History: + +--*/ + +#pragma once + +// +// Queue Callback Object. +// + +class ATL_NO_VTABLE CMyQueue : + public CComObjectRootEx, + public IQueueCallbackCreate, + public IQueueCallbackRead, + public IQueueCallbackWrite, + public IRequestCallbackRequestCompletion, + public IObjectCleanup +{ +public: + +DECLARE_NOT_AGGREGATABLE(CMyQueue) + +BEGIN_COM_MAP(CMyQueue) + COM_INTERFACE_ENTRY(IQueueCallbackCreate) + COM_INTERFACE_ENTRY(IQueueCallbackRead) + COM_INTERFACE_ENTRY(IQueueCallbackWrite) + COM_INTERFACE_ENTRY(IRequestCallbackRequestCompletion) + COM_INTERFACE_ENTRY(IObjectCleanup) +END_COM_MAP() + +public: + //IQueueCallbackRead + STDMETHOD_(void,OnRead)(_In_ IWDFIoQueue* pWdfQueue,_In_ IWDFIoRequest* pWdfRequest,_In_ SIZE_T NumOfBytesToRead); + + //IQueueCallbackWrite + STDMETHOD_(void,OnWrite)(_In_ IWDFIoQueue* pWdfQueue,_In_ IWDFIoRequest* pWdfRequest,_In_ SIZE_T NumOfBytesToWrite); + + //IQueueCallbackCreate + STDMETHOD_(void,OnCreateFile)(_In_ IWDFIoQueue* pWdfQueue,_In_ IWDFIoRequest* pWDFRequest,_In_ IWDFFile* pWdfFileObject); + + // IRequestCallbackRequestCompletion + STDMETHOD_(void,OnCompletion)(_In_ IWDFIoRequest* pWdfRequest,_In_ IWDFIoTarget* pTarget,_In_ IWDFRequestCompletionParams* pCompletionParams,_In_ void* pContext); + + //IObjectCleanup + STDMETHOD_(void,OnCleanup)(_In_ IWDFObject* pWdfObject); + +public: + CMyQueue(); + ~CMyQueue(); + + STDMETHOD(Initialize)(_In_ CMyDevice * Device); + + HRESULT + Configure( + ); + +private: + CComPtr m_FxQueue; + + // + // Unreferenced pointer to the parent device. + // + + CMyDevice * m_Device; + + VOID SendRequestToFileTarget( _In_ IWDFIoRequest* pWdfRequest); +}; diff --git a/general/echo/umdfSocketEcho/Driver/SocketEcho.inx b/general/echo/umdfSocketEcho/Driver/SocketEcho.inx new file mode 100644 index 00000000..d9ed27ea --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/SocketEcho.inx @@ -0,0 +1,89 @@ +; +; SocketEcho.inf +; + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFT% +CatalogFile=wudf.cat +DriverVer=03/20/2003,5.00.3788 + +[Manufacturer] +%MSFTWUDF%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%SocketEchoName%=SocketEcho_Install,WUDF\SocketEcho + +[ClassInstall32] +AddReg=SampleClass_RegistryAdd + +[SampleClass_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"-10" + +[SourceDisksFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1 +SocketEcho.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== WUDF SocketEcho Test Driver ================================== + +[SocketEcho_Install] +CopyFiles=UMDFDriverCopy + +[SocketEcho_Install.hw] +AddReg=SocketEcho_AddReg + +[SocketEcho_Install.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[SocketEcho_Install.CoInstallers] +AddReg = SocketEcho_Install.CoInstallers_AddReg +CopyFiles = CoInstallers_CopyFiles + +[SocketEcho_Install.CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WudfUpdate_$UMDFCOINSTALLERVERSION$.dll" + + + +[CoInstallers_CopyFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll + +[SocketEcho_Install.Wdf] +UmdfService=SocketEcho, SocketEcho_Driver_Install +UmdfServiceOrder=SocketEcho +UmdfDispatcher=FileHandle + +[SocketEcho_AddReg] +HKR,"SocketEcho","Host",0x00000000,"localhost" +HKR,"SocketEcho","Port",0x00000000,"6000" + +[WUDFRD_ServiceInstall] +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\WUDFRd.sys + +[SocketEcho_Driver_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID="{83B87D35-76B8-4920-B43C-3BDE6B0EC5B8}" +ServiceBinary="%12%\UMDF\SocketEcho.dll" + +[DestinationDirs] +UMDFDriverCopy=12,UMDF + +[UMDFDriverCopy] +SocketEcho.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME + +; =================== Generic ================================== + +[Strings] +MSFT="Microsoft" +MSFTWUDF="Microsoft Internal (WUDF)" +MediaDescription="Microsoft WUDF Sample Driver Installation Media" +ClassName="Sample Device" +SocketEchoName="Sample WUDF SocketEcho Driver" diff --git a/general/echo/umdfSocketEcho/Driver/SocketEcho.rc b/general/echo/umdfSocketEcho/Driver/SocketEcho.rc new file mode 100644 index 00000000..cc27b15f --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/SocketEcho.rc @@ -0,0 +1,21 @@ +//--------------------------------------------------------------------------- +// Skeleton.rc +// +// Copyright (c) Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include + +// +// TODO: Change the file description and file names to match your binary. +// + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WDF:UMDF Sample WUDF SocketEcho Driver" +#define VER_INTERNALNAME_STR "SocketEcho" +#define VER_ORIGINALFILENAME_STR "SocketEcho.dll" + +#include "common.ver" diff --git a/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj b/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj new file mode 100644 index 00000000..d9930170 --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj @@ -0,0 +1,245 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3} + $(MSBuildProjectName) + 1 + 1 + Debug + Win32 + {DA04694B-6179-416F-83FF-53A671E51B26} + + + + Windows10 + False + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + internal.h + + + $(InfArch) + true + .\$(IntDir)\SocketEcho.inf + + + true + true + internal.h + + + + SocketEcho + + + SocketEcho + + + SocketEcho + + + SocketEcho + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + Dynamic + + + Dynamic + + + Dynamic + + + Dynamic + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ws2_32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ws2_32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ws2_32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ws2_32.lib + exports.def + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj.Filters b/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj.Filters new file mode 100644 index 00000000..539cbe3b --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj.Filters @@ -0,0 +1,54 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {BEDB1CCE-4E58-4AE0-B5B6-C54C6159232D} + + + h;hpp;hxx;hm;inl;inc;xsd + {07B8152F-B3FA-4EA1-BBCD-EABDD1B7FCAB} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {61C11142-602D-496E-B9EB-516ED5D7685B} + + + inf;inv;inx;mof;mc; + {2BB65E51-CB92-4484-AD29-5ED2A6007684} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/echo/umdfSocketEcho/Driver/connection.h b/general/echo/umdfSocketEcho/Driver/connection.h new file mode 100644 index 00000000..2b7e23c6 --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/connection.h @@ -0,0 +1,32 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + Connection.h + +Abstract: + + Header file for the socketecho connection class + +Environment: + + User mode only + + +--*/ +#pragma once + +class CConnection +{ +public: + CConnection(); + HRESULT Connect(IN IWDFDevice *pDevice); + VOID Close(); + HANDLE GetSocketHandle( ); + +private: + SOCKET m_socket; +}; + diff --git a/general/echo/umdfSocketEcho/Driver/device.cpp b/general/echo/umdfSocketEcho/Driver/device.cpp new file mode 100644 index 00000000..9c3e4db1 --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/device.cpp @@ -0,0 +1,469 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Device.cpp + +Abstract: + + This module contains the implementation of the UMDF socketecho sample + driver's device callback object. + + It does not implement either of the PNP interfaces so once the device + is setup, it won't ever get any callbacks until the device is removed. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "device.tmh" + +const GUID GUID_DEVINTERFACE_SOCKETECHO = + {0xcdc35b6e, 0xbe4, 0x4936, { 0xbf, 0x5f, 0x55, 0x37, 0x38, 0xa, 0x7c, 0x1a }}; + + +HRESULT +CMyDevice::Initialize( + _In_ IWDFDriver* FxDriver, + _In_ IWDFDeviceInitialize* FxDeviceInit + ) +/*++ + + Routine Description: + + This method initializes the device callback object and creates the + partner device object. + + The method should perform any device-specific configuration that: + * could fail (these can't be done in the constructor) + * must be done before the partner object is created -or- + * can be done after the partner object is created and which aren't + influenced by any device-level parameters the parent (the driver + in this case) might set. + + Arguments: + + FxDeviceInit - the settings for this device. + FxDriver - IWDF Driver for this device. + + Return Value: + + status. + +--*/ +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC!"); + + CComPtr fxDevice; + HRESULT hr; + BOOL bFilter = FALSE; + + // + // Configure things like the locking model before we go to create our + // partner device. + // + + // + // Set the locking model + // + + FxDeviceInit->SetLockingConstraint(None); + + // + // Mark filter if we are a filter + // + + if (bFilter) + { + FxDeviceInit->SetFilter(); + } + + // + // TODO: Any per-device initialization which must be done before + // creating the partner object. + // + + // + // Create a new FX device object and assign the new callback object to + // handle any device level events that occur. + // + + // + // QueryIUnknown references the IUnknown interface that it returns + // (which is the same as referencing the device). We pass that to + // CreateDevice, which takes its own reference if everything works. + // + + CComPtr pUnk; + HRESULT hrQI = this->QueryInterface(__uuidof(IUnknown),(void**)&pUnk); + WUDF_SAMPLE_DRIVER_ASSERT(SUCCEEDED(hrQI)); + + hr = FxDriver->CreateDevice(FxDeviceInit, pUnk, &fxDevice); + + // + // If that succeeded then set our FxDevice member variable. + // + + if (SUCCEEDED(hr)) + { + m_FxDevice = fxDevice; + } + + return hr; +} + +HRESULT +CMyDevice::Configure( + VOID + ) +/*++ + + Routine Description: + + This method is called after the device callback object has been initialized + and returned to the driver. It would setup the device's queues and their + corresponding callback objects. + + Arguments: + + None + + Return Value: + + status + +--*/ +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC!"); + + HRESULT hr; + CComObject * defaultQueue = NULL; + + // + // Create a new instance of our queue callback object + // + hr = CComObject::CreateInstance(&defaultQueue); + + if (SUCCEEDED(hr)) + { + defaultQueue->AddRef(); + hr = defaultQueue->Initialize(this); + } + + if (SUCCEEDED(hr)) + { + hr = defaultQueue->Configure(); + } + + // + // Create and Enable Device Interface for this device. + // + if (SUCCEEDED(hr)) + { + hr = m_FxDevice->CreateDeviceInterface(&GUID_DEVINTERFACE_SOCKETECHO, + NULL); + } + if (SUCCEEDED(hr)) + { + hr = m_FxDevice->AssignDeviceInterfaceState(&GUID_DEVINTERFACE_SOCKETECHO, + NULL, + TRUE); + } + + if (SUCCEEDED(hr)) + { + hr = ReadAndAssignPropertyStoreValue(); + } + + // + // Release the reference we took on the queue callback object. + // The framework took its own references on the object's callback interfaces + // when we called m_FxDevice->CreateIoQueue, and will manage the object's lifetime. + // + SAFE_RELEASE(defaultQueue); + + return hr; +} + +STDMETHODIMP_(void) +CMyDevice::OnCloseFile( + _In_ IWDFFile* pWdfFileObject + ) +/*++ + + Routine Description: + + This method is called when an app closes the file handle to this device. + This will free the context memory associated with this file object, close + the connection object associated with this file object and delete the file + handle i/o target object associated with this file object. + + Arguments: + + pWdfFileObject - the framework file object for which close is handled. + + Return Value: + + None + +--*/ +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC!"); + + HRESULT hr = S_OK ; + FileContext *pContext = NULL; + + hr = pWdfFileObject->RetrieveContext((void**)&pContext); + + if (SUCCEEDED(hr) && (pContext != NULL ) ) + { + pContext->pConnection->Close(); + pContext->pFileTarget->DeleteWdfObject(); + + delete pContext->pConnection; + delete pContext; + } + + return ; +} + + +STDMETHODIMP_(void) +CMyDevice::OnCleanupFile( + _In_ IWDFFile* pWdfFileObject + ) +/*++ + + Routine Description: + + This method is when app with open handle device terminates. + + Arguments: + + pWdfFileObject - the framework file object for which close is handled. + + Return Value: + + None + +--*/ +{ + UNREFERENCED_PARAMETER(pWdfFileObject); +} + +STDMETHODIMP_(void) +CMyDevice::OnCleanup( + _In_ IWDFObject* pWdfObject + ) +/*++ + + Routine Description: + + This device callback method is invoked by the framework when the WdfObject + is about to be released by the framework. This will free the context memory + associated with the device object. + + Arguments: + + pWdfObject - the framework device object for which OnCleanup. + + Return Value: + + None + +--*/ +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC!"); + + HRESULT hr ; + DeviceContext *pContext = NULL; + + WUDF_SAMPLE_DRIVER_ASSERT(pWdfObject == m_FxDevice); + + hr = pWdfObject->RetrieveContext((void**)&pContext); + + if (SUCCEEDED(hr) && (pContext != NULL)) + { + // hostStr is allocated through StrDup, and thus need be freed through LocalFree + // + if (pContext->hostStr != NULL) + { + LocalFree( pContext->hostStr ); + } + + if (pContext->portStr != NULL) + { + LocalFree( pContext->portStr ); + } + + delete pContext; + } +// +//CMyDevice has a reference to framework device object via m_Device. +//Framework device object has a reference to CMyDevice object via the callbacks. +//This leads to circular reference and both the objects can't be destroyed until this circular reference is broken. +//To break the circular reference we release the reference to the framework device object here in OnCleanup. + + m_FxDevice = NULL; +} + +HRESULT +CMyDevice::ReadAndAssignPropertyStoreValue( + VOID + ) +/*++ + + Routine Description: + Helper function for reading property store values and storing them in the + device level context. + + Arguments: + + pWdfFileObject - the framework file object for which close is handled. + + Return Value: + + None + +--*/ +{ + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC!"); + + CComPtr pPropStore; + WDF_PROPERTY_STORE_DISPOSITION disposition; + PROPVARIANT val; + HRESULT hr ; + + PropVariantInit(&val); + + DeviceContext *pContext = new DeviceContext; + if (pContext == NULL) + { + hr = E_OUTOFMEMORY; + Trace(TRACE_LEVEL_ERROR, + L"ERROR: Could not create device context object %!hresult!", + hr); + + goto CleanUp; + } + + pContext->hostStr = NULL; + pContext->portStr = NULL; + + // + // Retreive property store for reading drivers custom settings as specified + // in the INF + // + hr = m_FxDevice->RetrieveDevicePropertyStore(L"SocketEcho", + WdfPropertyStoreNormal, + &pPropStore, + &disposition); + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, + "Failed to retrieve device property store for reading custom " + "settings as specified in the INF %!hresult!", + hr); + + goto CleanUp; + } + + // + // Get the key for this device with Named value "host" + // + hr = pPropStore->GetNamedValue(L"Host", &val); + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, + "Failed to get \"Host\" key value %!hresult!", + hr); + + goto CleanUp; + } + + if (val.vt != VT_LPWSTR) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION); + Trace(TRACE_LEVEL_ERROR, + "Unexpected string format for value in \"Host\" key %!hresult!", + hr); + + goto CleanUp; + } + + pContext->hostStr = StrDup(val.pwszVal); + + // + // Clear property variant for reading next key + // + PropVariantClear(&val); + + // + // Get the key for this device with Named value "Port" + // + hr = pPropStore->GetNamedValue(L"Port", &val); + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, + "Failed to get \"Port\" key value %!hresult!", + hr); + + goto CleanUp; + } + + if (val.vt != VT_LPWSTR) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_CONFIGURATION); + Trace(TRACE_LEVEL_ERROR, + "Unexpected string format for value in \"Port\" key %!hresult!", + hr); + + goto CleanUp; + } + + pContext->portStr = StrDup(val.pwszVal); + + hr = m_FxDevice->AssignContext(NULL, (void*)pContext); + if (FAILED(hr)) + { + Trace(TRACE_LEVEL_ERROR, + "Failed to assign property store value to device %!hresult!", + hr); + + // + // Fall through to clean up and exit ... + // + } + +CleanUp: + + PropVariantClear(&val); + + if (FAILED(hr)) + { + if (pContext != NULL) + { + // hostStr is allocated through StrDup, and thus need be freed through LocalFree + // + if (pContext->hostStr != NULL) + { + LocalFree( pContext->hostStr ); + } + + if (pContext->portStr != NULL) + { + LocalFree( pContext->portStr ); + } + + delete pContext; + } + } + + return hr; +} + diff --git a/general/echo/umdfSocketEcho/Driver/device.h b/general/echo/umdfSocketEcho/Driver/device.h new file mode 100644 index 00000000..176f10e6 --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/device.h @@ -0,0 +1,70 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions for the UMDF Skeleton sample + driver's device callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// Class for the iotrace driver. +// + +class ATL_NO_VTABLE CMyDevice : + public CComObjectRootEx, + public IFileCallbackCleanup, + public IFileCallbackClose, + public IObjectCleanup +{ +public: + +DECLARE_NOT_AGGREGATABLE(CMyDevice) + +BEGIN_COM_MAP(CMyDevice) + COM_INTERFACE_ENTRY(IFileCallbackCleanup) + COM_INTERFACE_ENTRY(IFileCallbackClose) + COM_INTERFACE_ENTRY(IObjectCleanup) +END_COM_MAP() + +public: + + //IFileCallbackCleanup + STDMETHOD_(void,OnCleanupFile)(_In_ IWDFFile* pWdfFileObject); + //IFileCallbackClose + STDMETHOD_(void,OnCloseFile)(_In_ IWDFFile* pWdfFileObject); + //IObjectCleanup + STDMETHOD_(void,OnCleanup)(_In_ IWDFObject* pWdfObject); + +public: + + STDMETHOD(Initialize)(_In_ IWDFDriver* pWdfDriver, _In_ IWDFDeviceInitialize* pWdfDeviceInit); + + HRESULT + Configure( + ); + + IWDFDevice * + GetFxDevice( + ) + { + return m_FxDevice; + } + +private: + CComPtr m_FxDevice; + HRESULT ReadAndAssignPropertyStoreValue(); + +}; diff --git a/general/echo/umdfSocketEcho/Driver/devicecontext.h b/general/echo/umdfSocketEcho/Driver/devicecontext.h new file mode 100644 index 00000000..2bf10d2e --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/devicecontext.h @@ -0,0 +1,32 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + devicecontext.h + +Abstract: + + This header file defines the structure type for device context associated with the device object + +Environment: + + user mode only + +Revision History: + +--*/ + + +#pragma once + + +typedef struct _DeviceContext +{ + PWSTR hostStr; + + PWSTR portStr; + +}DeviceContext; + diff --git a/general/echo/umdfSocketEcho/Driver/dllsup.cpp b/general/echo/umdfSocketEcho/Driver/dllsup.cpp new file mode 100644 index 00000000..5ec3eb33 --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/dllsup.cpp @@ -0,0 +1,111 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + dllsup.cpp + +Abstract: + + This module contains the implementation of the UMDF Socktecho Sample + Driver's entry point and its exported functions for providing COM support. + + This module can be copied without modification to a new UMDF driver. It + depends on some of the code in comsup.cpp & comsup.h to handle DLL + registration and creating the first class factory. + + This module is dependent on the following defines: + + MYDRIVER_TRACING_ID - A wide string passed to WPP when initializing + tracing. For example the socktecho uses + L"Microsoft\\UMDF\\Socketecho" + + MYDRIVER_CLASS_ID - A GUID encoded in struct format used to + initialize the driver's ClassID. + + These are defined in internal.h for the sample. If you choose + to use a different primary include file, you should ensure they are + defined there as well. + +Environment: + + WDF User-Mode Driver Framework (WDF:UMDF) + +--*/ + +#include "internal.h" +#include "dllsup.tmh" + +const GUID CLSID_MyDriverCoClass = MYDRIVER_CLASS_ID; + +class CSocketEchoModule : public CAtlDllModuleT< CSocketEchoModule > +{ +}; + + +OBJECT_ENTRY_AUTO(CLSID_MyDriverCoClass, CMyDriver) + + +CSocketEchoModule _AtlModule; + +BOOL +WINAPI +DllMain( + HINSTANCE ModuleHandle, + DWORD Reason, + PVOID Reserved + ) +/*++ + + Routine Description: + + This is the entry point and exit point for the I/O trace driver. This + does very little as the I/O trace driver has minimal global data. + + This method initializes tracing. + + Arguments: + + ModuleHandle - the DLL handle for this module. + + Reason - the reason this entry point was called. + + Reserved - unused + + Return Value: + + TRUE + +--*/ +{ + + UNREFERENCED_PARAMETER( ModuleHandle ); + + if (DLL_PROCESS_ATTACH == Reason) + { + // + // Initialize tracing. + // + + WPP_INIT_TRACING(MYDRIVER_TRACING_ID); + + } + else if (DLL_PROCESS_DETACH == Reason) + { + // + // Cleanup tracing. + // + + WPP_CLEANUP(); + } + + return _AtlModule.DllMain(Reason, Reserved); +; +} + +_Check_return_ +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} diff --git a/general/echo/umdfSocketEcho/Driver/driver.cpp b/general/echo/umdfSocketEcho/Driver/driver.cpp new file mode 100644 index 00000000..4f93691c --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/driver.cpp @@ -0,0 +1,174 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Driver.cpp + +Abstract: + + This module contains the implementation of the UMDF Socketecho Sample's + core driver callback object. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "driver.tmh" + +STDMETHODIMP +CMyDriver::OnInitialize( + _In_ IWDFDriver* pWdfDriver + ) + + +/*++ + + Routine Description: + + This routine is invoked by the framework at driver load . + This method will invoke the Winsock Library for using + Winsock API in this driver. + + Arguments: + + pWdfDriver - Framework driver object + + Return Value: + + S_OK if successful, or error otherwise. + +--*/ + +{ + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + UNREFERENCED_PARAMETER(pWdfDriver); + + WORD sockVersion; + WSADATA wsaData; + + sockVersion = MAKEWORD(2, 0); + + int result = WSAStartup(sockVersion, &wsaData); + + if (result != 0) + { + DWORD err = WSAGetLastError(); + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Failed to initialize Winsock 2.0 %!winerr!", + err + ); + return HRESULT_FROM_WIN32(err); + } + + return S_OK; +} + +STDMETHODIMP_(void) +CMyDriver::OnDeinitialize( + _In_ IWDFDriver* pWdfDriver + ) + +/*++ + Routine Description: + + The FX invokes this method when it unloads the driver. + This routine will Cleanup Winsock library + + Arguments: + + pWdfDriver - the Fx driver object. + + Return Value: + + None + + + --*/ + +{ + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + + UNREFERENCED_PARAMETER(pWdfDriver); + + WSACleanup(); +} + +STDMETHODIMP +CMyDriver::OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ) +/*++ + + Routine Description: + + The FX invokes this method when it wants to install our driver on a device + stack. This method creates a device callback object, then calls the Fx + to create an Fx device object and associate the new callback object with + it. + + Arguments: + + FxWdfDriver - the Fx driver object. + + FxDeviceInit - the initialization information for the device. + + Return Value: + + status + +--*/ +{ + Trace( + TRACE_LEVEL_INFORMATION, + "%!FUNC!" + ); + + HRESULT hr; + + CComObject * device = NULL; + + // + // Create a new instance of our device callback object + // + + hr = CComObject::CreateInstance(&device); + + if (SUCCEEDED(hr)) + { + device->AddRef(); + hr = device->Initialize(FxWdfDriver, FxDeviceInit); + } + + // + // If that succeeded then call the device's configure method. This + // allows the device to create any queues or other structures that it + // needs now that the corresponding fx device object has been created. + // + + if (SUCCEEDED(hr)) + { + hr = device->Configure(); + } + + // + // Release the reference we took on the device callback object. + // The framework took its own references on the object's callback interfaces + // when we called FxWdfDriver->CreateDevice, and will manage the object's lifetime. + // + SAFE_RELEASE(device); + + return hr; +} diff --git a/general/echo/umdfSocketEcho/Driver/driver.h b/general/echo/umdfSocketEcho/Driver/driver.h new file mode 100644 index 00000000..6affa20f --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/driver.h @@ -0,0 +1,53 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Driver.h + +Abstract: + + This module contains the type definitions for the UMDF Socketecho sample's + driver callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// This class handles driver events for the socktecho sample. In particular +// it supports the OnDeviceAdd event, which occurs when the driver is called +// to setup per-device handlers for a new device stack. +// + +extern const GUID CLSID_MyDriverCoClass; + +class ATL_NO_VTABLE CMyDriver : + public CComObjectRootEx, + public CComCoClass, + public IDriverEntry +{ +public: + +DECLARE_NOT_AGGREGATABLE(CMyDriver) + +DECLARE_CLASSFACTORY(); + +DECLARE_NO_REGISTRY(); + +BEGIN_COM_MAP(CMyDriver) + COM_INTERFACE_ENTRY(IDriverEntry) +END_COM_MAP() + +public: + // IDriverEntry + STDMETHOD(OnInitialize)(_In_ IWDFDriver* pWdfDriver); + STDMETHOD(OnDeviceAdd)(_In_ IWDFDriver* pWdfDriver, _In_ IWDFDeviceInitialize* pWdfDeviceInit); + STDMETHOD_(void,OnDeinitialize)(_In_ IWDFDriver* pWdfDriver); +}; + diff --git a/general/echo/umdfSocketEcho/Driver/exports.def b/general/echo/umdfSocketEcho/Driver/exports.def new file mode 100644 index 00000000..2c0b7d49 --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/exports.def @@ -0,0 +1,6 @@ +; Socketecho.def : Declares the module parameters. + +LIBRARY "SocketEcho" + +EXPORTS + DllGetClassObject PRIVATE diff --git a/general/echo/umdfSocketEcho/Driver/internal.h b/general/echo/umdfSocketEcho/Driver/internal.h new file mode 100644 index 00000000..a7875468 --- /dev/null +++ b/general/echo/umdfSocketEcho/Driver/internal.h @@ -0,0 +1,117 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Internal.h + +Abstract: + + This module contains the local type definitions for the UMDF Socketecho sample + driver sample. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +// +// Include the winsock headers before any other windows headers. +// +#include +#include + +// +// Include the WUDF DDI +// + +#include "wudfddi.h" + +// +// Use specstrings for in/out annotation of function parameters. +// + +#include "specstrings.h" + +// +// Define the tracing flags. +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + MyDriverTraceControl, (64316518,DFE2,42B6,8786,4995E5EC435), \ + \ + WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ + ) + +#define WPP_FLAG_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAG_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); +// end_wpp +// + +// +// Driver specific #defines +// + +#define MYDRIVER_TRACING_ID L"Microsoft\\UMDF\\SocketEcho" +#define MYDRIVER_CLASS_ID { 0x83B87D35, 0x76B8, 0x4920, {0xB4, 0x3C, 0x3B, 0xDE, 0x6B, 0x0E, 0xC5, 0xB8} } + +#ifndef SAFE_RELEASE +#define SAFE_RELEASE(p) {if ((p)) { (p)->Release(); (p) = NULL; }} +#endif + +__forceinline +#ifdef _PREFAST_ +__declspec(noreturn) +#endif +VOID +WdfTestNoReturn( + VOID + ) +{ + // do nothing. +} + +#define WUDF_SAMPLE_DRIVER_ASSERT(p) \ +{ \ + if ( !(p) ) \ + { \ + DebugBreak(); \ + WdfTestNoReturn(); \ + } \ +} + +// +// Include the type specific headers. +// +#include +#include + +#include "connection.h" +#include "filecontext.h" +#include "devicecontext.h" +#include "driver.h" +#include "device.h" +#include "queue.h" + +_Analysis_mode_(_Analysis_operator_new_null_) + diff --git a/general/echo/umdfSocketEcho/Exe/internal.h b/general/echo/umdfSocketEcho/Exe/internal.h new file mode 100644 index 00000000..ff1cd863 --- /dev/null +++ b/general/echo/umdfSocketEcho/Exe/internal.h @@ -0,0 +1,18 @@ +// internal.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include +_Analysis_mode_(_Analysis_code_type_user_code_); +#include +#include +#include +#include +#include +#include +#include + +#include "socketechoserver.h" diff --git a/general/echo/umdfSocketEcho/Exe/socketechoserver.cpp b/general/echo/umdfSocketEcho/Exe/socketechoserver.cpp new file mode 100644 index 00000000..bfe5f546 --- /dev/null +++ b/general/echo/umdfSocketEcho/Exe/socketechoserver.cpp @@ -0,0 +1,512 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + socketserver.cpp + +Abstract: + + A simple socket server application that listens on a specified port and echoes back data + received. + +Environment: + + User Mode + +--*/ + +#include "internal.h" + + +DWORD +Run( + LPVOID lpThreadParameter + ) + /*++ + +Routine Description: + + This routine is invoked for each thread created for a new connection accepted by the server. + The rcv and send to socket happen in this thread routine. + + +Arguments: + + lpThreadParameter , The Thread parameter which contains socket information + +Return Value: + + Thread Exit Code + + +--*/ +{ + #define DeleteBufferExitThread(dwExitCode) \ + delete[] buffer; \ + buffer = NULL; \ + ExitThread(dwExitCode); + + #define DeleteBufferReturn(dwExitCode) \ + delete[] buffer; \ + buffer = NULL; \ + return dwExitCode; + + int count =0; + + char *buffer = new char[DATA_LENGTH]; + if (NULL == buffer) + { + ExitThread(1); + } + + DWORD Event; + + // + // Look at socket information from thread arg. + // + + + CEchoServer *pThreadData = (CEchoServer*)lpThreadParameter; + if (pThreadData==NULL) + { + DeleteBufferExitThread(1); + } + + SOCKET sClient = pThreadData->m_socket; + HANDLE NetworkEvent = pThreadData->m_NetworkEvent; + WSANETWORKEVENTS NetworkEvents; + printf("Client Start: 0x%Ix\n", sClient); + int actual = 0; + for(;;) + { + if ((Event = WSAWaitForMultipleEvents( + 1, + &NetworkEvent, + FALSE, + WSA_INFINITE, + FALSE)) == WSA_WAIT_FAILED) + { + printf("WSAWaitForMultipleEvents failed with error %d\n", WSAGetLastError()); + DeleteBufferReturn(0); + } + + if (WSAEnumNetworkEvents(sClient ,NetworkEvent, &NetworkEvents) == SOCKET_ERROR) + { + printf("WSAEnumNetworkEvents failed with error %d\n", WSAGetLastError()); + DeleteBufferReturn(0); + } + + if (NetworkEvents.lNetworkEvents & FD_READ) + { + if (NetworkEvents.lNetworkEvents & FD_READ && NetworkEvents.iErrorCode[FD_READ_BIT] != 0) + { + printf("FD_READ failed with error %d\n", NetworkEvents.iErrorCode[FD_READ_BIT]); + } + else + { + + actual = recv(sClient,buffer,DATA_LENGTH*sizeof(char),0); + // + // socket connection has been reset ,so bail out . + // + if (actual == 0 || actual == WSAECONNRESET ) + { + printf(" Could not get data , Error : 0x%lx \n",WSAGetLastError()); + break; // socket shut-down + + } + printf("FD_READ read buffer on client 0x%Ix with length %d \n",sClient,actual); + count = send(sClient, (const char*)buffer,actual,0); + if ( count == SOCKET_ERROR ) + { + if ( WSAGetLastError()== WSAEWOULDBLOCK ) + { + printf(" Could not send data as resource is unavaliable , do not retry until next Write event \n"); + } + else + { + printf(" Could not send data , Error : 0x%lx \n",WSAGetLastError()); + break; + } + } + else + { + printf("FD_WRITE write buffer on client 0x%Ix with length %d \n",sClient,count); + } + } + } + // + // if there is a write network event and there is data to write , write that + // + if (NetworkEvents.lNetworkEvents & FD_WRITE) + { + if (NetworkEvents.lNetworkEvents & FD_WRITE && NetworkEvents.iErrorCode[FD_WRITE_BIT] != 0) + { + printf("FD_WRITE failed with error %d\n", NetworkEvents.iErrorCode[FD_WRITE_BIT]); + } + else + { + count = send(sClient, (const char*)buffer,actual,0); + if ( count == SOCKET_ERROR ) + { + if ( WSAGetLastError()== WSAEWOULDBLOCK ) + { + printf(" Could not send data as resource is unavaliable , do not retry until next Write event "); + } + else + { + printf(" Could not send data , Error : 0x%lx \n",WSAGetLastError()); + break; + } + } + else + { + printf("FD_WRITE write buffer on client 0x%Ix with length %d \n",sClient,count); + } + actual = 0; + } + } + if (NetworkEvents.lNetworkEvents & FD_CLOSE) + { + shutdown(sClient,FD_READ|FD_WRITE); + printf(" recived a close from client : 0x%Ix \n",sClient); + closesocket(sClient); + DeleteBufferExitThread(0); + } + } + + DeleteBufferReturn(1); + +} + +CEchoServer::CEchoServer( + SOCKET socketclient + ) +/*++ + +Routine Description: + + This is the constructor routine for CEchoServer class. This is called for each instance of new + connection accepted by the server . + +Arguments: + + Socket received from the accept + +Return Value: + + None . + +--*/ +{ + m_socket = socketclient; + m_NetworkEvent = WSACreateEvent(); + printf("socket created : 0x%Ix \n", m_socket); + +} + +void +CEchoServer::Start() +/*++ + +Routine Description: + + This routine is to Start the thread which will rcv and send the data recieved on this instance of socket connection. + + +Arguments: + + None. + +Return Value: + + None. +--*/ +{ + + + if(WSAEventSelect( + m_socket, + m_NetworkEvent, + FD_READ|FD_WRITE|FD_CLOSE)== SOCKET_ERROR) + { + printf("Error in Event Select,Cannot start Server thread for this socket \n"); + closesocket(m_socket); + goto Exit; + } +// +// Create thread to read/write data to this socket +// + + HANDLE hRunThread = CreateThread( + NULL, // Default Security Attrib. + 0, // Initial Stack Size, + (LPTHREAD_START_ROUTINE) Run, // Thread Func + this, // Arg to Thread Func. + 0, // Creation Flags + NULL // Don't need the Thread Id. + ); + if (NULL == hRunThread) + { + printf(" Could not create socket server run thread : 0x%lx \n", GetLastError()); + closesocket(m_socket); + goto Exit; + } + +Exit: + + return ; + } + +void +SocketServerMain( + _In_ unsigned short uPort + ) +/*++ + +Routine Description: + + This routine is the main entry for the app when the app is configured to + be a socket server. + It creates a a listening socket for incoming conenctions. + + +Arguments: + + uPort - Port Number that the socket server binds to + +Return Value: + + None. +--*/ +{ + + + SOCKET ListenSocket; + int iResult; + #pragma warning( suppress: 24002 ) // suppress warning for IPv6 ,currently IPv4 specific + sockaddr_in service ; + + // Initialize Winsock 2.2 + WSADATA wsaData; + iResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if ( NO_ERROR != iResult ) + { + printf("Error at WSAStartup() \n"); + goto Exit; + } + // + // Create a SOCKET for listening for incoming connection requests. + // + ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if ( INVALID_SOCKET == ListenSocket) + { + printf("Error at socket(): %ld\n ", WSAGetLastError()); + goto Cleanup; + } + // The sockaddr_in structure specifies the address family, + // IP address, and port for the socket that is being bound. + service.sin_family = AF_INET; + // + // Suppress overflow warning. + // inet_pton is annotated to write sizeof(IN6_ADDR) bytes to pAddrBuf, + // but it only writes sizeof(IN_ADDR) bytes when Family is AF_INET (IPv4). + // https://msdn.microsoft.com/en-us/library/windows/desktop/cc805844(v=vs.85).aspx + // + #pragma warning( suppress: 26000 ) + iResult = inet_pton(AF_INET, "127.0.0.1", &service.sin_addr); + if (iResult != 1) + { + printf("Error at inet_pton(): %ld\n ", WSAGetLastError()); + closesocket(ListenSocket); + goto Cleanup; + } + service.sin_port = htons(uPort); + if (SOCKET_ERROR == bind( + ListenSocket, + (SOCKADDR*) &service, + sizeof(service) ) ) + { + printf("bind() failed. \n"); + closesocket(ListenSocket); + goto Cleanup; + } + + // + // Listen for incoming connection requests + // on the created socket upto MAX_CONNECTIONS + // + if ( SOCKET_ERROR == listen( + ListenSocket, + MAX_CONNECTIONS ) ) + { + printf("Error listening on socket.\n"); + } + printf("Listening on socket...\n"); + + // + // Set Socket RCVBUF and SNDBUF size to DATA_LENGTH , so large requests are not fragmented . + // + int iOptVal; + int iOptLen = sizeof(int); + + if (getsockopt(ListenSocket, SOL_SOCKET, SO_RCVBUF, (char*)&iOptVal, &iOptLen) != SOCKET_ERROR) + { + printf("SO_RCVBUF value: %ld\n", iOptVal); + } + iOptVal = DATA_LENGTH; + iOptLen = sizeof(int); + if (setsockopt(ListenSocket, SOL_SOCKET, SO_RCVBUF, (char*)&iOptVal, iOptLen) != SOCKET_ERROR) + { + printf("Set SO_RCVBUF: ON\n"); + } + iOptLen = sizeof(int); + if (getsockopt(ListenSocket, SOL_SOCKET, SO_RCVBUF, (char*)&iOptVal, &iOptLen) != SOCKET_ERROR) + { + printf("SO_RCVBUF Value: %ld\n", iOptVal); + } + iOptLen = sizeof(int); + if (getsockopt(ListenSocket, SOL_SOCKET, SO_SNDBUF, (char*)&iOptVal, &iOptLen) != SOCKET_ERROR) + { + printf("SO_SNDBUF value: %ld\n", iOptVal); + } + iOptVal = DATA_LENGTH; + iOptLen = sizeof(int); + if (setsockopt(ListenSocket, SOL_SOCKET, SO_SNDBUF, (char*)&iOptVal, iOptLen) != SOCKET_ERROR) + { + printf("Set SO_SNDBUF: ON\n"); + } + iOptLen = sizeof(int); + if (getsockopt(ListenSocket, SOL_SOCKET, SO_SNDBUF, (char*)&iOptVal, &iOptLen) != SOCKET_ERROR) + { + printf("SO_SNDBUF Value: %ld\n", iOptVal); + } + +// +// Loop the server to start accepting connections from clients on this socket +// + + for(;;) + { + CEchoServer *client = new CEchoServer(accept(ListenSocket,NULL,NULL)); + + if (client) + { + printf("Client connected.\n"); + client->Start(); // Start receiving/sending data on the socket + } + } + +Cleanup: + // + // Invoke Winsock Cleanup + // + + WSACleanup(); + + Exit: + return; + +} +void +Usage() + +/*++ + +Routine Description: + + This routine is invoked to display the usage of this application + +Arguments: + + None. + +Return Value: + + None . +--*/ + +{ + printf("\n\n Usage: \n"); + printf(" ------ \n\n"); + printf(" socketechoapp Display Usage \n"); + printf(" socketechoapp -h Display Usage\n"); + printf(" socketechoapp -p Start the app as server listening on default port\n"); + printf(" socketechoapp -p [port#] Start the app as server listening on this port \n"); + + + +} + + +/* */ +void __cdecl +main( + _In_ int argc, + _In_reads_(argc) char* argv[] + ) + +/*++ + +Routine Description: + + + +Arguments: + + None. + +Return Value: + + None. +--*/ +{ + unsigned short argIndex = 1 ; + unsigned short uPort = DEFAULT_PORT_ADDRESS ; + + + if (argc < 2) + { + Usage(); + goto Exit; + } + +// +// look at second arg and check for either -h which indicates user asked for help in Usage +// of this commandline +// + + if (!strcmp(*(argv+argIndex),"-h")) + { + Usage(); + goto Exit; + } +// +// check if its -p and proceed with otherwise show usage +// + else if (!strcmp(*(argv+argIndex),"-p")) + { + // + // look at third arg, which should be the port# + // + if ( ++argIndex < argc ) + { + uPort = (unsigned short)atoi(*(argv+(argIndex))); + } + SocketServerMain(uPort); + + } + else + { + Usage(); + goto Exit; + } + +Exit: + return; + +} + + diff --git a/general/echo/umdfSocketEcho/Exe/socketechoserver.h b/general/echo/umdfSocketEcho/Exe/socketechoserver.h new file mode 100644 index 00000000..f71bc48b --- /dev/null +++ b/general/echo/umdfSocketEcho/Exe/socketechoserver.h @@ -0,0 +1,48 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + sockechoserver.h + +Abstract: + + Header file for the socket server module of the socketecho application + +Environment: + + User mode only + +--*/ + +#pragma once + + +#define MAX_CONNECTIONS 5 +#define DEFAULT_PORT_ADDRESS 6000 +#define DATA_LENGTH 1024*40 + +void +SocketServerMain( + _In_ unsigned short uPort + ); + + // + // Class definition for CEchoServer Class + // +class CEchoServer +{ + + public: + + SOCKET m_socket; + HANDLE m_NetworkEvent; + + + + CEchoServer(SOCKET socketclient); + void Start(); + +}; + diff --git a/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj b/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj new file mode 100644 index 00000000..ea299161 --- /dev/null +++ b/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {4237BF5F-1426-45DD-96E0-74DEADFA24C6} + $(MSBuildProjectName) + Debug + Win32 + {13151EE8-4C58-4284-BA01-C4B9431C6B06} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + socketechoserver + + + socketechoserver + + + socketechoserver + + + socketechoserver + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);user32.lib;Ws2_32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);user32.lib;Ws2_32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);user32.lib;Ws2_32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);user32.lib;Ws2_32.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj.Filters b/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj.Filters new file mode 100644 index 00000000..035fba1a --- /dev/null +++ b/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {1BEC8228-FE60-4512-B036-885F56208A4B} + + + h;hpp;hxx;hm;inl;inc;xsd + {E4060FEA-373E-4AE6-94B7-FF878D406EAE} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {181042B8-D282-46BA-B9D7-BDEE33402D00} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/echo/umdfSocketEcho/ReadMe.md b/general/echo/umdfSocketEcho/ReadMe.md new file mode 100644 index 00000000..12515365 --- /dev/null +++ b/general/echo/umdfSocketEcho/ReadMe.md @@ -0,0 +1,184 @@ +UMDF SocketEcho Sample (UMDF Version 1) +======================================= + +The UMDF SocketEcho sample demonstrates how to use the User-Mode Driver Framework (UMDF) to write a driver and demonstrates best practices. + +This sample also demonstrates how to use a default parallel dispatch I/O queue, use a Microsoft Win32 dispatcher, and handle a socket handle by using a Win32 file I/O target. + +Related technologies +-------------------- + +[User-Mode Driver Framework](http://msdn.microsoft.com/en-us/library/windows/hardware/ff560456) + +Code Tour +--------- + +Parts of this code sample are generated from the ATL Project Wizard in Microsoft Visual Studio 2005. This sample driver is a minimal driver that is intended to demonstrate how to use UMDF. It is not intended for use in a production environment. + +CMyDriver::OnInitialize in driver.cpp is called by the framework when the driver loads. This method initiates use of the Winsock Library. CMyDriver::OnDeviceAdd in driver.cpp is called by the framework to install the driver on a device stack. OnDeviceAdd creates a device callback object, and then calls IWDFDriver::CreateDevice to create an framework device object and to associate the device callback object with the framework device object. + +CMyQueue::OnCreateFile in queue.cpp is called by the framework to create a socket connection, create a file i/o target that is associated with the socket handle for this connection, and store the socket handle in the file object context. + +Installation +------------ + +In Visual Studio, you can press F5 to build the sample and then deploy it to a target machine. For more information, see [Deploying a Driver to a Test Computer](http://msdn.microsoft.com/en-us/library/windows/hardware/hh454834). Alternatively, you can install the sample from the command line. + +To test this sample, you must have a test computer that is running Windows Vista or later. This test computer can be a second computer or, if necessary, your development computer. + +To install the UMDF Echo sample driver from the command line, do the following: + +1. Copy the driver binary and the socketecho.inf file to a directory on your test computer (for example, C:\\ socketechoSample.) + +2. Copy the UMDF coinstaller, WUDFUpdate\_*MMmmmm*.dll, from the \\redist\\wdf\\\ directory to the same directory (for example, C:\\socketechoSample). + + **Note**   + + You can obtain redistributable framework updates by downloading the *wdfcoinstaller.msi* package from [WDK 8 Redistributable Components](http://go.microsoft.com/fwlink/p/?LinkID=226396). This package performs a silent install into the directory of your Windows Driver Kit (WDK) installation. You will see no confirmation that the installation has completed. You can verify that the redistributables have been installed on top of the WDK by ensuring there is a redist\\wdf directory under the root directory of the WDK, %ProgramFiles(x86)%\\Windows Kits\\8.0. + +3. + + Navigate to the directory that contains the INF file and binaries (for example, cd /d c:\\socketechoSample), and run DevCon.exe as follows: + + **devcon.exe install socketecho.inf WUDF\\socketecho** + + You can find DevCon.exe in the \\tools directory of the WDK (for example, \\tools\\devcon\\i386\\devcon.exe). + +To update the socketecho driver after you make any changes, do the following: + +1. Increment the version number in the INF file. This change is not necessary, but it will help ensure that Plug and Play (PnP) selects your new driver as a better match for the device. + +2. Copy the updated driver binary and the socketecho.inf file to a directory on your test computer (for example, C:\\ socketechoSample.) + +3. Navigate to the directory that contains the INF file and binaries (for example, cd /d c:\\ socketechoSample), and run devcon.exe as follows: + + devcon.exe update socketecho.inf WUDF\\socketecho + +To test this sample drivers on a checked operating system that you have installed (in contrast to the standard retail installations), you must modify the INF file to use the checked version of the UMDF co-installer. That is, you must do the following: + +1. In the INX file, replace all occurrences of WudfUpdate\_*MMmmmm*.dll with WudfUpdate\_*MMmmmm*\_chk.dll. + +2. Copy the WudfUpdate\_*MMmmmm*\_chk.dll file from the \\redist\\wdf\\\ directory to your driver package instead of WudfUpdate\_*MMmmmm*.dll. + +3. If WdfCoinstaller*MMmmmm*.dll or WinUsbCoinstaller.dll is included in your driver package, repeat step 1 and step 2 for them. + +Testing +------- + +To test the SocketEcho driver, you can run socketechoserver.exe, which is built from the src\\general\\echo\\umdfSocketEcho\\Exe directory, and echoapp.exe, which is built from the Kernel-Mode Driver Framework (KMDF) samples in the src\\general\\echo\\kmdf directory. + +First, you must install the device as described earlier. Then, run socketechoserver.exe from a Command Prompt window. + +D:\\\>socketechoserver -h + +Usage: + +------ + +socketechoserver Display Usage + +socketechoserver -h Display Usage + +socketechoserver -p Start the app as server listening on default port + +socketechoserver -p [port\#] Start the app as server listening on this port + +D:\\\>socketechoserver -p + +Listening on socket... + +In another Command Prompt window, run echoapp.exe. + +D:\\\>echoapp + +DevicePath: \\\\?\\root\#sample\#0000\#{ e5e65b0c-82c8-4689-96d4-f77837971990} + +Opened device successfully + +512 Pattern Bytes Written successfully + +512 Pattern Bytes Read successfully + +Pattern Verified successfully + +D:\\\>echoapp -Async + +DevicePath: \\\\?\\root\#sample\#0000\#{cdc35b6e-0be4-4936-bf5f-5537380a7c1a} + +Opened device successfully + +Starting AsyncIo + +Number of bytes written by request number 0 is 1024 + +Number of bytes read by request number 0 is 1024 + +Number of bytes read by request number 1 is 1024 + +Number of bytes written by request number 2 is 1024 + +Number of bytes read by request number 2 is 1024 + +Number of bytes written by request number 3 is 1024 + +Number of bytes read by request number 3 is 1024 + +Number of bytes written by request number 4 is 1024 + +Number of bytes read by request number 4 is 1024 + +Number of bytes written by request number 5 is 1024 + +Number of bytes read by request number 5 is 1024 + +Number of bytes written by request number 6 is 1024 + +Number of bytes read by request number 6 is 1024 + +Number of bytes written by request number 7 is 1024 + +Number of bytes read by request number 7 is 1024 + +Number of bytes written by request number 8 is 1024 + +Number of bytes read by request number 8 is 1024 + +Number of bytes written by request number 9 is 1024 + +Number of bytes read by request number 9 is 1024 + +Number of bytes written by request number 10 is 1024 + +Number of bytes read by request number 10 is 1024 + +Number of bytes written by request number 11 is 1024 + +... + +Note that independent threads perform the reads and writes in the echo test application. As a result, the order of the output might not exactly match what you see in the preceding output. + +File Manifest +------------- + + ++++ + + + + + + + + + + + +
File +Description

Socketecho.htm

+

The documentation for this sample.

Dllsup.cpp

+

The DLL support code that provides the DLL's entry point and the single required export (DllGetClassObject).

+ + diff --git a/general/echo/umdfSocketEcho/umdfsocketecho.sln b/general/echo/umdfSocketEcho/umdfsocketecho.sln new file mode 100644 index 00000000..9cd8f76d --- /dev/null +++ b/general/echo/umdfSocketEcho/umdfsocketecho.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{C4B24CED-B58F-47D1-8FC0-778610EF84CF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{AFCDA28A-1D07-410D-BA77-47E637336CA6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SocketEcho", "Driver\SocketEcho.vcxproj", "{ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "socketechoserver", "Exe\socketechoserver.vcxproj", "{4237BF5F-1426-45DD-96E0-74DEADFA24C6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3}.Debug|Win32.ActiveCfg = Debug|Win32 + {ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3}.Debug|Win32.Build.0 = Debug|Win32 + {ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3}.Release|Win32.ActiveCfg = Release|Win32 + {ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3}.Release|Win32.Build.0 = Release|Win32 + {ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3}.Debug|x64.ActiveCfg = Debug|x64 + {ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3}.Debug|x64.Build.0 = Debug|x64 + {ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3}.Release|x64.ActiveCfg = Release|x64 + {ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3}.Release|x64.Build.0 = Release|x64 + {4237BF5F-1426-45DD-96E0-74DEADFA24C6}.Debug|Win32.ActiveCfg = Debug|Win32 + {4237BF5F-1426-45DD-96E0-74DEADFA24C6}.Debug|Win32.Build.0 = Debug|Win32 + {4237BF5F-1426-45DD-96E0-74DEADFA24C6}.Release|Win32.ActiveCfg = Release|Win32 + {4237BF5F-1426-45DD-96E0-74DEADFA24C6}.Release|Win32.Build.0 = Release|Win32 + {4237BF5F-1426-45DD-96E0-74DEADFA24C6}.Debug|x64.ActiveCfg = Debug|x64 + {4237BF5F-1426-45DD-96E0-74DEADFA24C6}.Debug|x64.Build.0 = Debug|x64 + {4237BF5F-1426-45DD-96E0-74DEADFA24C6}.Release|x64.ActiveCfg = Release|x64 + {4237BF5F-1426-45DD-96E0-74DEADFA24C6}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {ABEFFAA1-36CF-4F78-9A6B-10EAEC11B3E3} = {C4B24CED-B58F-47D1-8FC0-778610EF84CF} + {4237BF5F-1426-45DD-96E0-74DEADFA24C6} = {AFCDA28A-1D07-410D-BA77-47E637336CA6} + EndGlobalSection +EndGlobal diff --git a/general/event/ReadMe.md b/general/event/ReadMe.md new file mode 100644 index 00000000..8b8bebaa --- /dev/null +++ b/general/event/ReadMe.md @@ -0,0 +1,25 @@ +Hardware Event Sample +===================== + +This sample demonstrates two different ways a Windows kernel-mode driver can notify an application about a hardware event. One way uses an event-based method, and the other uses an IRP-based method. Because the sample driver is not talking to any real hardware, it uses a timer DPC to simulate hardware events. The test application informs the driver whether it wants to be notified by signaling an event or by completing the pending IRP. Additionally, the test application specifies a relative time at which the DPC timer must fire. + +*Event-based approach:* The application calls the [**CreateEvent**](http://msdn.microsoft.com/en-us/library/windows/hardware/ms682396) function to create an event. It then passes the event handle to the driver in an I/O control request that uses a private IOCTL code, IOCTL\_REGISTER\_EVENT. Because the driver is a monolithic, top-level driver, its IRP dispatch routines run in the application process context and, as a result, the event handle is still valid in the driver. The driver dereferences the user-mode handle into system space and saves the event object pointer for later use. Next, the driver queues a custom timer DPC. When the DPC fires, the driver signals the event by calling the [**KeSetEvent**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff553253) routine at DISPATCH\_LEVEL, and deletes the references to the event object. You can't use this approach if your driver is not a monolithic, top-level driver; that is because a driver can't guarantee the process context in a multi-level driver stack if the driver is not at the top of the stack. + +*Pending IRP-based approach:* The application makes a synchronous IOCTL\_REGISTER\_EVENT request. The driver sets the status of the device I/O control request to IRP pending, queues a timer DPC, and returns STATUS\_PENDING. When the timer fires to indicate a hardware event, the driver completes the pending IRP to notify the application about the hardware event. + +There are two advantages of IRP-based approach over the event-based approach. First, the driver can send a message to the application along with the event notification. Second, the driver routines don't have to run in the context of the process that made the request. Instead, the application can send a synchronous or asynchronous (overlapped) I/O control request to the driver. + +**Note**  This sample driver is not a Plug and Play driver. This is a minimal driver meant to demonstrate a feature of the operating system. Neither this driver nor its sample programs are intended for use in a production environment. Rather, they are intended for educational purposes and as a skeleton driver. + + +Run the sample +-------------- + +To test this driver, copy the test application, event.exe, and the driver to the same directory, and run the application. The application will automatically load the driver, if it's not already loaded, and interact with the driver. When you exit the app, the driver will be stopped, unloaded, and removed. + +To run the test application, enter the following command in the command window: + +`C:\>event.exe <0|1>` + +The first command-line parameter, `Delay`, equals the time, in seconds, to delay the event signal. For the second command-line parameter, specify 0 for IRP-based notification and 1 for event-based notification. + diff --git a/general/event/eventsample.sln b/general/event/eventsample.sln new file mode 100644 index 00000000..1b533282 --- /dev/null +++ b/general/event/eventsample.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{AD60DF57-AE60-4857-9CAE-41B96E1E0789}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Wdm", "Wdm", "{1403EA42-4F69-4B63-B15F-3447F44A500A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "event", "exe\event.vcxproj", "{19EFCD7C-6C7A-427A-84D2-A62D9073146A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "event", "wdm\event.vcxproj", "{99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {19EFCD7C-6C7A-427A-84D2-A62D9073146A}.Debug|Win32.ActiveCfg = Debug|Win32 + {19EFCD7C-6C7A-427A-84D2-A62D9073146A}.Debug|Win32.Build.0 = Debug|Win32 + {19EFCD7C-6C7A-427A-84D2-A62D9073146A}.Release|Win32.ActiveCfg = Release|Win32 + {19EFCD7C-6C7A-427A-84D2-A62D9073146A}.Release|Win32.Build.0 = Release|Win32 + {19EFCD7C-6C7A-427A-84D2-A62D9073146A}.Debug|x64.ActiveCfg = Debug|x64 + {19EFCD7C-6C7A-427A-84D2-A62D9073146A}.Debug|x64.Build.0 = Debug|x64 + {19EFCD7C-6C7A-427A-84D2-A62D9073146A}.Release|x64.ActiveCfg = Release|x64 + {19EFCD7C-6C7A-427A-84D2-A62D9073146A}.Release|x64.Build.0 = Release|x64 + {99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B}.Debug|Win32.ActiveCfg = Debug|Win32 + {99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B}.Debug|Win32.Build.0 = Debug|Win32 + {99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B}.Release|Win32.ActiveCfg = Release|Win32 + {99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B}.Release|Win32.Build.0 = Release|Win32 + {99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B}.Debug|x64.ActiveCfg = Debug|x64 + {99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B}.Debug|x64.Build.0 = Debug|x64 + {99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B}.Release|x64.ActiveCfg = Release|x64 + {99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {19EFCD7C-6C7A-427A-84D2-A62D9073146A} = {AD60DF57-AE60-4857-9CAE-41B96E1E0789} + {99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B} = {1403EA42-4F69-4B63-B15F-3447F44A500A} + EndGlobalSection +EndGlobal diff --git a/general/event/exe/event.vcxproj b/general/event/exe/event.vcxproj new file mode 100644 index 00000000..1ec7b921 --- /dev/null +++ b/general/event/exe/event.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {19EFCD7C-6C7A-427A-84D2-A62D9073146A} + $(MSBuildProjectName) + Debug + Win32 + {B020889A-CDA3-4F73-AA14-45C800A49925} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + event + + + event + + + event + + + event + + + + true + Level4 + %(AdditionalIncludeDirectories);..\wdm + + + + + %(AdditionalIncludeDirectories);..\wdm + + + %(AdditionalIncludeDirectories);..\wdm + + + 0x400000 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\wdm + + + + + %(AdditionalIncludeDirectories);..\wdm + + + %(AdditionalIncludeDirectories);..\wdm + + + 0x400000 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\wdm + + + + + %(AdditionalIncludeDirectories);..\wdm + + + %(AdditionalIncludeDirectories);..\wdm + + + 0x400000 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\wdm + + + + + %(AdditionalIncludeDirectories);..\wdm + + + %(AdditionalIncludeDirectories);..\wdm + + + 0x400000 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/event/exe/event.vcxproj.Filters b/general/event/exe/event.vcxproj.Filters new file mode 100644 index 00000000..7cdaec8b --- /dev/null +++ b/general/event/exe/event.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {F62E2250-4375-47E7-8996-8B59DCFCFCF8} + + + h;hpp;hxx;hm;inl;inc;xsd + {B34D45F9-19AD-4EA0-A3AA-ABDD239D4829} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {F9F32DF2-C4E0-45E6-9964-FE9DAA47CC64} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/event/exe/eventtest.c b/general/event/exe/eventtest.c new file mode 100644 index 00000000..e1b03204 --- /dev/null +++ b/general/event/exe/eventtest.c @@ -0,0 +1,266 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + EventTest.c + +Abstract: + + Simple console test app for the event.sys driver. + +Enviroment: + + User Mode + +Revision History: + +--*/ + +// +// INCLUDES +// +#include +#include +#include +#include +#include +#include +#include +#include "public.h" + +BOOLEAN +ManageDriver( + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceName, + _In_ USHORT Function + ); + +BOOLEAN +SetupDriverName( + _Inout_updates_bytes_all_(BufferLength) PCHAR DriverLocation, + _In_ ULONG BufferLength + ); + +#define USAGE() {\ + printf("event <0/1>\n");\ + printf("\twhere = time to delay the event signal in seconds.\n");\ + printf("\t 0 for IRP based and 1 for event based notification.\n");\ +} + +// +// MAIN +// +VOID __cdecl +main( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] + ) +{ + BOOL bStatus; + HANDLE hDevice; + ULONG ulReturnedLength; + REGISTER_EVENT registerEvent; + FLOAT fDelay = 3; + UINT type = EVENT_BASED; + DWORD errNum = 0; + TCHAR driverLocation[MAX_PATH] = { 0 }; + + + if ( (argc < 3) || (argv[1] == NULL) || (argv[2] == NULL) ) { + USAGE(); + exit(1); + } + + if (sscanf_s( argv[1], "%f", &fDelay ) == 0) { + printf("sscanf_s failed\n"); + exit(1); + } + + if (sscanf_s( argv[2], "%d", &type ) == 0) { + printf("sscanf_s failed\n"); + exit(1); + } + + // + // open the device + // + if ((hDevice = CreateFile( + "\\\\.\\Event_Sample", // lpFileName + GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess + FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode + NULL, // lpSecurityAttributes + OPEN_EXISTING, // dwCreationDistribution + 0, // dwFlagsAndAttributes + NULL // hTemplateFile + )) == INVALID_HANDLE_VALUE) { + + errNum = GetLastError(); + + if (errNum != ERROR_FILE_NOT_FOUND) { + + printf("CreateFile failed! ERROR_FILE_NOT_FOUND = %d\n", errNum); + + return ; + } + + // + // The driver is not started yet so let us the install driver. + // First setup full path to driver name. + // + + if (!SetupDriverName(driverLocation, sizeof(driverLocation) )) { + + return ; + } + + if (!ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_INSTALL + )) { + + printf("Unable to install driver. \n"); + + // + // Error - remove driver. + // + + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + return; + } + + // + // Now open the device again. + // + hDevice = CreateFile( + "\\\\.\\Event_Sample", // lpFileName + GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess + FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode + NULL, // lpSecurityAttributes + OPEN_EXISTING, // dwCreationDistribution + 0, // dwFlagsAndAttributes + NULL // hTemplateFile + ); + + if ( hDevice == INVALID_HANDLE_VALUE ){ + printf ( "Error: CreatFile Failed : %d\n", GetLastError()); + return; + } + + } + + // + // set the event signal delay. Use relative time for this sample + // + registerEvent.DueTime.QuadPart = -((LONGLONG)(fDelay * 10.0E6)); + registerEvent.Type = type; + + if (type == EVENT_BASED) { + // + // + // + registerEvent.hEvent = CreateEvent( + NULL, // lpEventAttributes + TRUE, // bManualReset + FALSE, // bInitialState +#ifdef DBG + "TEST_EVENT" // use WinObj to view named events for DBG +#else + NULL // lpName +#endif + ); + + + if ( !registerEvent.hEvent ) { + printf("CreateEvent error = %d\n", GetLastError() ); + } else { + + printf("Event HANDLE = %p\n", registerEvent.hEvent ); + printf("Press any key to exit.\n"); + while( !_kbhit() ) { + bStatus = DeviceIoControl( + hDevice, // Handle to device + IOCTL_REGISTER_EVENT, // IO Control code + ®isterEvent, // Input Buffer to driver. + SIZEOF_REGISTER_EVENT, // Length of input buffer in bytes. + NULL, // Output Buffer from driver. + 0, // Length of output buffer in bytes. + &ulReturnedLength, // Bytes placed in buffer. + NULL // synchronous call + ); + + if ( !bStatus ) { + printf("Ioctl failed with code %d\n", GetLastError() ); + break; + } else { + printf("Waiting for Event...\n"); + + WaitForSingleObject(registerEvent.hEvent, + INFINITE ); + + printf("Event signalled.\n\n"); + + ResetEvent( registerEvent.hEvent); + //printf("Event reset.\n"); + } + } + } + + }else if (type == IRP_BASED) { + + printf("Press any key to exit.\n"); + registerEvent.hEvent = NULL; + registerEvent.Type = IRP_BASED; + + while( !_kbhit() ) { + bStatus = DeviceIoControl( + hDevice, // Handle to device + IOCTL_REGISTER_EVENT, // IO Control code + ®isterEvent, // Input Buffer to driver. + SIZEOF_REGISTER_EVENT, // Length of input buffer in bytes. + NULL, // Output Buffer from driver. + 0, // Length of output buffer in bytes. + &ulReturnedLength, // Bytes placed in buffer. + NULL // synchronous call + ); + + if ( !bStatus ) { + printf("Ioctl failed with code %d\n", GetLastError() ); + break; + } + printf("Event occurred.\n\n"); + printf("\nRegistering event again....\n\n"); + } + + }else { //unknown type + USAGE(); + } + + // + // close the handle to the device. + // + CloseHandle(hDevice); + + // + // Unload the driver if loaded. Ignore any errors. + // + if (driverLocation[0] != (TCHAR)0) { + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + } + + return; +} diff --git a/general/event/exe/install.c b/general/event/exe/install.c new file mode 100644 index 00000000..b52178e5 --- /dev/null +++ b/general/event/exe/install.c @@ -0,0 +1,558 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + install.c + +Abstract: + + Win32 routines to dynamically load and unload a Windows NT kernel-mode + driver using the Service Control Manager APIs. + +Environment: + + User mode only + +--*/ + + +#include +#include +#include +#include +#include +#include "public.h" + +BOOLEAN +InstallDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceExe + ); + + +BOOLEAN +RemoveDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +StartDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +StopDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +InstallDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceExe + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + SC_HANDLE schService; + DWORD err; + + // + // NOTE: This creates an entry for a standalone driver. If this + // is modified for use with a driver that requires a Tag, + // Group, and/or Dependencies, it may be necessary to + // query the registry for existing driver information + // (in order to determine a unique Tag, etc.). + // + + // + // Create a new a service object. + // + + schService = CreateService(SchSCManager, // handle of service control manager database + DriverName, // address of name of service to start + DriverName, // address of display name + SERVICE_ALL_ACCESS, // type of access to service + SERVICE_KERNEL_DRIVER, // type of service + SERVICE_DEMAND_START, // when to start service + SERVICE_ERROR_NORMAL, // severity if service fails to start + ServiceExe, // address of name of binary file + NULL, // service does not belong to a group + NULL, // no tag requested + NULL, // no dependency names + NULL, // use LocalSystem account + NULL // no password for service account + ); + + if (schService == NULL) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_EXISTS) { + + // + // Ignore this error. + // + + return TRUE; + + } else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) { + // + // Previous instance of the service is not fully deleted so sleep + // and try again. + // + printf("Previous instance of the service is not fully deleted. Try again...\n"); + return FALSE; + } + else { + + printf("CreateService failed! Error = %d \n", err ); + + // + // Indicate an error. + // + + return FALSE; + } + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + // + // Indicate success. + // + + return TRUE; + +} // InstallDriver + +BOOLEAN +ManageDriver( + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceName, + _In_ USHORT Function + ) +{ + + SC_HANDLE schSCManager; + + BOOLEAN rCode = TRUE; + + // + // Insure (somewhat) that the driver and service names are valid. + // + + if (!DriverName || !ServiceName) { + + printf("Invalid Driver or Service provided to ManageDriver() \n"); + + return FALSE; + } + + // + // Connect to the Service Control Manager and open the Services database. + // + + schSCManager = OpenSCManager(NULL, // local machine + NULL, // local database + SC_MANAGER_ALL_ACCESS // access required + ); + + if (!schSCManager) { + + printf("Open SC Manager failed! Error = %d \n", GetLastError()); + + return FALSE; + } + + // + // Do the requested function. + // + + switch( Function ) { + + case DRIVER_FUNC_INSTALL: + + // + // Install the driver service. + // + + if (InstallDriver(schSCManager, + DriverName, + ServiceName + )) { + + // + // Start the driver service (i.e. start the driver). + // + + rCode = StartDriver(schSCManager, + DriverName + ); + + } else { + + // + // Indicate an error. + // + + rCode = FALSE; + } + + break; + + case DRIVER_FUNC_REMOVE: + + // + // Stop the driver. + // + + StopDriver(schSCManager, + DriverName + ); + + // + // Remove the driver service. + // + + RemoveDriver(schSCManager, + DriverName + ); + + // + // Ignore all errors. + // + + rCode = TRUE; + + break; + + default: + + printf("Unknown ManageDriver() function. \n"); + + rCode = FALSE; + + break; + } + + // + // Close handle to service control manager. + // + + if (schSCManager) { + + CloseServiceHandle(schSCManager); + } + + return rCode; + +} // ManageDriver + + +BOOLEAN +RemoveDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + BOOLEAN rCode; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", GetLastError()); + + // + // Indicate error. + // + + return FALSE; + } + + // + // Mark the service for deletion from the service control manager database. + // + + if (DeleteService(schService)) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + printf("DeleteService failed! Error = %d \n", GetLastError()); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + return rCode; + +} // RemoveDriver + + + +BOOLEAN +StartDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + DWORD err; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", GetLastError()); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Start the execution of the service (i.e. start the driver). + // + + if (!StartService(schService, // service identifier + 0, // number of arguments + NULL // pointer to arguments + )) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_ALREADY_RUNNING) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + printf("StartService failure! Error = %d \n", err ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + return FALSE; + } + + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + return TRUE; + +} // StartDriver + + + +BOOLEAN +StopDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + BOOLEAN rCode = TRUE; + SC_HANDLE schService; + SERVICE_STATUS serviceStatus; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", GetLastError()); + + return FALSE; + } + + // + // Request that the service stop. + // + + if (ControlService(schService, + SERVICE_CONTROL_STOP, + &serviceStatus + )) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + printf("ControlService failed! Error = %d \n", GetLastError() ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle (schService); + } + + return rCode; + +} // StopDriver + +BOOLEAN +SetupDriverName( + _Inout_updates_bytes_all_(BufferLength) PCHAR DriverLocation, + _In_ ULONG BufferLength + ) +{ + HANDLE fileHandle; + DWORD driverLocLen = 0; + + // + // Get the current directory. + // + + driverLocLen = GetCurrentDirectory(BufferLength, + DriverLocation + ); + + if (driverLocLen == 0) { + + printf("GetCurrentDirectory failed! Error = %d \n", GetLastError()); + + return FALSE; + } + + // + // Setup path name to driver file. + // + if (FAILED( StringCbCat(DriverLocation, BufferLength, "\\"DRIVER_NAME".sys") )) { + return FALSE; + } + + // + // Insure driver file is in the specified directory. + // + + if ((fileHandle = CreateFile(DriverLocation, + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + )) == INVALID_HANDLE_VALUE) { + + + printf("%s.sys is not loaded.\n", DRIVER_NAME); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Close open file handle. + // + + if (fileHandle) { + + CloseHandle(fileHandle); + } + + // + // Indicate success. + // + + return TRUE; + + +} // SetupDriverName + + + diff --git a/general/event/wdm/event.c b/general/event/wdm/event.c new file mode 100644 index 00000000..64bae443 --- /dev/null +++ b/general/event/wdm/event.c @@ -0,0 +1,1070 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Event.c + +Abstract: + + The purpose of this sample is to demonstrate how a kernel-mode driver can notify + an user-app about a device event. There are several different techniques. This sample + will demonstrate two very commonly used techniques. + + 1) Using an event: + The application creates an event object using CreateEvent(). + The app passes the event handle to the driver in a private IOCTL. + The driver is running in the app's thread context during the IOCTL so + there is a valid user-mode handle at that time. + The driver dereferences the user-mode handle into system space & saves + the event object pointer for later use. + The driver signals the event via KeSetEvent() at IRQL <= DISPATCH_LEVEL. + The driver deletes the references to the event object. + + 2) Pending Irp: This technique is useful if you want to send a message + to the app along with the notification. In this, an application sends + a synchronous or asynchronous (overlapped) ioctl to the driver. The driver + would then pend the IRP until the device event occurs. When the hardware + event occurs, the driver will complete the IRP. This will cause the thread that + sent the request to come out of DeviceIoControl call if it's synchronous or signal + the event that the thread is waiting on in the usermode it's has done a + OVERLAPPED call. Another advantage of this technique over the event model + is that the driver doesn't have to be in the context of the process that + sent the IOCTL request. You can't guarantee the process context in multi-level + drivers. + + 3) Using WMI to fire events. Check the wmifilter sample in the DDK. + + 4) Using PNP custom notification scheme. Walter Oney's book describes this. + Can be used only in PNP drivers. + + 4) Named events: In that an app creates a named event in the usermode + and driver opens that in kernel and signal it. This technique is deprecated + by the kb article (Q228785) + + This sample demonstrates the first two techniques. This sample is an + improvised version of the event sample available in the KB article + Q176415 + + +Enviroment: + + Kernel Mode Only + +Revision History: + +--*/ + +#include +#include "public.h" //common to app and driver +#include "event.h" // private to driver + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, EventCreateClose) +#pragma alloc_text (PAGE, EventUnload) +#endif + +_Use_decl_annotations_ +NTSTATUS +DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine gets called by the system to initialize the driver. + +Arguments: + + DriverObject - the system supplied driver object. + RegistryPath - the system supplied registry path for this driver. + +Return Value: + + NTSTATUS + +--*/ + +{ + + + + PDEVICE_OBJECT deviceObject; + PDEVICE_EXTENSION deviceExtension; + UNICODE_STRING ntDeviceName; + UNICODE_STRING symbolicLinkName; + NTSTATUS status; + + UNREFERENCED_PARAMETER(RegistryPath); + + DebugPrint(("==>DriverEntry\n")); + + // + // Create the device object + // + RtlInitUnicodeString(&ntDeviceName, NTDEVICE_NAME_STRING); + + status = IoCreateDevice(DriverObject, // DriverObject + sizeof(DEVICE_EXTENSION), // DeviceExtensionSize + &ntDeviceName, // DeviceName + FILE_DEVICE_UNKNOWN, // DeviceType + FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics + FALSE, // Not Exclusive + &deviceObject // DeviceObject + ); + + if (!NT_SUCCESS(status)) { + DebugPrint(("\tIoCreateDevice returned 0x%x\n", status)); + return(status); + } + + // + // Set up dispatch entry points for the driver. + // + DriverObject->MajorFunction[IRP_MJ_CREATE] = EventCreateClose; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = EventCreateClose; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = EventCleanup; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = EventDispatchIoControl; + DriverObject->DriverUnload = EventUnload; + + // + // Create a symbolic link for userapp to interact with the driver. + // + RtlInitUnicodeString(&symbolicLinkName, SYMBOLIC_NAME_STRING); + status = IoCreateSymbolicLink(&symbolicLinkName, &ntDeviceName); + + if (!NT_SUCCESS(status)) { + IoDeleteDevice(deviceObject); + DebugPrint(("\tIoCreateSymbolicLink returned 0x%x\n", status)); + return(status); + } + + // + // Initialize the device extension. + // + deviceExtension = deviceObject->DeviceExtension; + + InitializeListHead(&deviceExtension->EventQueueHead); + + KeInitializeSpinLock(&deviceExtension->QueueLock); + + deviceExtension->Self = deviceObject; + + // + // Establish user-buffer access method. + // + deviceObject->Flags |= DO_BUFFERED_IO; + + DebugPrint(("<==DriverEntry\n")); + + ASSERT(NT_SUCCESS(status)); + + return status; +} + +_Use_decl_annotations_ +VOID +EventUnload( + PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine gets called to remove the driver from the system. + +Arguments: + + DriverObject - the system supplied driver object. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; + PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension; + UNICODE_STRING symbolicLinkName; + + DebugPrint(("==>Unload\n")); + + PAGED_CODE(); + + if (!IsListEmpty(&deviceExtension->EventQueueHead)) { + ASSERTMSG("Event Queue is not empty\n", FALSE); + } + + // + // Delete the user-mode symbolic link and deviceobjct. + // + RtlInitUnicodeString(&symbolicLinkName, SYMBOLIC_NAME_STRING); + IoDeleteSymbolicLink(&symbolicLinkName); + IoDeleteDevice(deviceObject); + + return; +} + +_Use_decl_annotations_ +NTSTATUS +EventCreateClose( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + This device control dispatcher handles create & close IRPs. + +Arguments: + + DeviceObject - Context for the activity. + Irp - The device control argument block. + +Return Value: + + NTSTATUS + +--*/ +{ + PIO_STACK_LOCATION irpStack; + NTSTATUS status; + PFILE_CONTEXT fileContext; + + UNREFERENCED_PARAMETER(DeviceObject); + + PAGED_CODE(); + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + ASSERT(irpStack->FileObject != NULL); + + switch (irpStack->MajorFunction) + { + case IRP_MJ_CREATE: + DebugPrint(("IRP_MJ_CREATE\n")); + + fileContext = ExAllocatePoolWithQuotaTag(NonPagedPool, + sizeof(FILE_CONTEXT), + TAG); + + if (NULL == fileContext) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + IoInitializeRemoveLock(&fileContext->FileRundownLock, TAG, 0, 0); + + // + // Make sure nobody is using the FsContext scratch area. + // + ASSERT(irpStack->FileObject->FsContext == NULL); + + // + // Store the context in the FileObject's scratch area. + // + irpStack->FileObject->FsContext = (PVOID) fileContext; + + status = STATUS_SUCCESS; + break; + + case IRP_MJ_CLOSE: + DebugPrint(("IRP_MJ_CLOSE\n")); + + fileContext = irpStack->FileObject->FsContext; + + ExFreePoolWithTag(fileContext, TAG); + + status = STATUS_SUCCESS; + break; + + default: + ASSERT(FALSE); // should never hit this + status = STATUS_NOT_IMPLEMENTED; + break; + } + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return status; + +} + +_Use_decl_annotations_ +NTSTATUS +EventCleanup( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + This device control dispatcher handles Cleanup IRP. + +Arguments: + + DeviceObject - Context for the activity. + Irp - The device control argument block. + +Return Value: + + NTSTATUS + +--*/ +{ + PIO_STACK_LOCATION irpStack; + NTSTATUS status ; + KIRQL oldIrql; + PLIST_ENTRY thisEntry, nextEntry, listHead; + PNOTIFY_RECORD notifyRecord; + PDEVICE_EXTENSION deviceExtension; + LIST_ENTRY cleanupList; + PFILE_CONTEXT fileContext; + + DebugPrint(("==>EventCleanup\n")); + + deviceExtension = DeviceObject->DeviceExtension; + irpStack = IoGetCurrentIrpStackLocation(Irp); + + ASSERT(irpStack->FileObject != NULL); + + fileContext = irpStack->FileObject->FsContext; + + // + // This acquire cannot fail because you cannot get more than one + // cleanup for the same handle. + // + status = IoAcquireRemoveLock(&fileContext->FileRundownLock, Irp); + ASSERT(NT_SUCCESS(status)); + + // + // Wait for all the threads that are currently dispatching to exit and + // prevent any threads dispatching I/O on the same handle beyond this point. + // + IoReleaseRemoveLockAndWait(&fileContext->FileRundownLock, Irp); + + InitializeListHead(&cleanupList); + + // + // Walk the list and remove all the pending notification records + // that belong to this filehandle. + // + + KeAcquireSpinLock(&deviceExtension->QueueLock, &oldIrql); + + listHead = &deviceExtension->EventQueueHead; + + for (thisEntry = listHead->Flink; + thisEntry != listHead; + thisEntry = nextEntry) + { + nextEntry = thisEntry->Flink; + + notifyRecord = CONTAINING_RECORD(thisEntry, NOTIFY_RECORD, ListEntry); + + if (irpStack->FileObject == notifyRecord->FileObject) { + + // + // KeCancelTimer returns if the timer is successfully cancelled. + // If it returns FALSE, there are two possibilities. Either the + // TimerDpc has just run and waiting to acquire the lock or it + // has run to completion. We wouldn't be here if it had run to + // completion because we wouldn't found the record in the list. + // So the only possibility is that it's waiting to acquire the lock. + // In that case, we will just let the DPC to complete the request + // and free the record. + // + if (KeCancelTimer(¬ifyRecord->Timer)) { + + DebugPrint(("\tCanceled timer\n")); + RemoveEntryList(thisEntry); + + switch (notifyRecord->Type) { + case IRP_BASED: + // + // Clear the cancel-routine and check the return value to + // see whether it was cleared by us or by the I/O manager. + // + if (IoSetCancelRoutine (notifyRecord->Message.PendingIrp, NULL) != NULL) { + + // + // We cleared it and as a result we own the IRP and + // nobody can cancel it anymore. We will queue the IRP + // in the local cleanup list so that we can complete + // all the IRPs outside the lock to avoid deadlocks in + // the completion routine of the driver above us re-enters + // our driver. + // + InsertTailList(&cleanupList, + ¬ifyRecord->Message.PendingIrp->Tail.Overlay.ListEntry); + ExFreePoolWithTag(notifyRecord, TAG); + + } else { + // + // The I/O manager cleared it and called the cancel-routine. + // Cancel routine is probably waiting to acquire the lock. + // So reinitialze the ListEntry so that it doesn't crash + // when it tries to remove the entry from the list and + // set the CancelRoutineFreeMemory to indicate that it should + // free the notification record. + // + InitializeListHead(¬ifyRecord->ListEntry); + notifyRecord->CancelRoutineFreeMemory = TRUE; + } + break; + + case EVENT_BASED: + ObDereferenceObject(notifyRecord->Message.Event); + ExFreePoolWithTag(notifyRecord, TAG); + break; + default: break; + + } + } + } + } + + KeReleaseSpinLock(&deviceExtension->QueueLock, oldIrql); + + // + // Walk through the cleanup list and cancel all + // the IRPs. + // + while (!IsListEmpty(&cleanupList)) + { + PIRP pendingIrp; + // + // Complete the IRP + // + thisEntry = RemoveHeadList(&cleanupList); + pendingIrp = CONTAINING_RECORD(thisEntry, IRP, Tail.Overlay.ListEntry); + + DebugPrint(("\t canceled IRP %p\n", pendingIrp)); + + pendingIrp->Tail.Overlay.DriverContext[3] = NULL; + pendingIrp->IoStatus.Information = 0; + pendingIrp->IoStatus.Status = STATUS_CANCELLED; + + IoCompleteRequest(pendingIrp, IO_NO_INCREMENT); + } + + // + // Finally complete the cleanup Irp + // + Irp->IoStatus.Status = status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + DebugPrint(("<== EventCleanup\n")); + return status; + +} + +_Use_decl_annotations_ +NTSTATUS +EventDispatchIoControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + This device control dispatcher handles IOCTLs. + +Arguments: + + DeviceObject - Context for the activity. + Irp - The device control argument block. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PIO_STACK_LOCATION irpStack; + PREGISTER_EVENT registerEvent; + NTSTATUS status; + PFILE_CONTEXT fileContext; + + DebugPrint(("==> EventDispatchIoControl\n")); + + irpStack = IoGetCurrentIrpStackLocation(Irp); + + ASSERT(irpStack->FileObject != NULL); + + fileContext = irpStack->FileObject->FsContext; + + status = IoAcquireRemoveLock(&fileContext->FileRundownLock, Irp); + if (!NT_SUCCESS(status)) { + // + // Lock is in a removed state. That means we have already received + // cleaned up request for this handle. + // + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return status; + } + + switch (irpStack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_REGISTER_EVENT: + + DebugPrint(("\tIOCTL_REGISTER_EVENT\n")); + + // + // First validate the parameters. + // + if (irpStack->Parameters.DeviceIoControl.InputBufferLength < + SIZEOF_REGISTER_EVENT) { + status = STATUS_INVALID_PARAMETER; + break; + } + + registerEvent = (PREGISTER_EVENT)Irp->AssociatedIrp.SystemBuffer; + + switch (registerEvent->Type) { + case IRP_BASED: + status = RegisterIrpBasedNotification(DeviceObject, Irp); + break; + case EVENT_BASED: + status = RegisterEventBasedNotification(DeviceObject, Irp); + break; + default: + ASSERTMSG("\tUnknow notification type from user-mode\n", FALSE); + status = STATUS_INVALID_PARAMETER; + break; + } + + break; + + default: + ASSERT(FALSE); // should never hit this + status = STATUS_NOT_IMPLEMENTED; + break; + + } // switch IoControlCode + + if (status != STATUS_PENDING) { + // + // complete the Irp + // + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + + // + // We don't hold the lock for IRP that's pending in the list because this + // lock is meant to rundown currently dispatching threads when the cleanup + // is handled. + // + IoReleaseRemoveLock(&fileContext->FileRundownLock, Irp); + + DebugPrint(("<== EventDispatchIoControl\n")); + return status; +} + +_Use_decl_annotations_ +VOID +EventCancelRoutine( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + The cancel routine. It will remove the IRP from the queue + and will complete it. The cancel spin lock is already acquired + when this routine is called. This routine is not required if + you are just using the event based notification. + +Arguments: + + DeviceObject - pointer to the device object. + + Irp - pointer to the IRP to be cancelled. + + +Return Value: + + VOID. + +--*/ +{ + PDEVICE_EXTENSION deviceExtension; + KIRQL oldIrql ; + PNOTIFY_RECORD notifyRecord; + + DebugPrint (("==>EventCancelRoutine irp %p\n", Irp)); + + deviceExtension = DeviceObject->DeviceExtension; + + // + // Release the cancel spinlock + // + IoReleaseCancelSpinLock(Irp->CancelIrql); + + // + // Acquire the queue spinlock + // + KeAcquireSpinLock(&deviceExtension->QueueLock, &oldIrql); + + notifyRecord = Irp->Tail.Overlay.DriverContext[3]; + ASSERT(NULL != notifyRecord); + ASSERT(IRP_BASED == notifyRecord->Type); + + RemoveEntryList(¬ifyRecord->ListEntry); + + // + // Clear the pending Irp field because we complete the IRP no matter whether + // we succeed or fail to cancel the timer. TimerDpc will check this field + // before dereferencing the IRP. + // + notifyRecord->Message.PendingIrp = NULL; + + if (KeCancelTimer(¬ifyRecord->Timer)) { + DebugPrint(("\t canceled timer\n")); + ExFreePoolWithTag(notifyRecord, TAG); + notifyRecord = NULL; + } else { + // + // Here the possibilities are: + // 1) DPC is fired and waiting to acquire the lock. + // 2) DPC has run to completion. + // 3) DPC has been cancelled by the cleanup routine. + // By checking the CancelRoutineFreeMemory, we can figure out whether + // dpc is waiting to acquire the lock and access the notifyRecord memory. + // + if (notifyRecord->CancelRoutineFreeMemory == FALSE) { + // + // This is case 1 where the DPC is waiting to run. + // + InitializeListHead(¬ifyRecord->ListEntry); + } else { + // + // This is either 2 or 3. + // + ExFreePoolWithTag(notifyRecord, TAG); + notifyRecord = NULL; + } + + } + + KeReleaseSpinLock(&deviceExtension->QueueLock, oldIrql); + + DebugPrint (("\t canceled IRP %p\n", Irp)); + Irp->Tail.Overlay.DriverContext[3] = NULL; + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + DebugPrint (("<==EventCancelRoutine irp %p\n", Irp)); + return; + +} + +_Use_decl_annotations_ +VOID +CustomTimerDPC( + PKDPC Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This is the DPC associated with this drivers Timer object setup in ioctl routine. + +Arguments: + + Dpc - our DPC object associated with our Timer + DeferredContext - Context for the DPC that we setup in DriverEntry + SystemArgument1 - + SystemArgument2 - + +Return Value: + + Nothing. + +--*/ +{ + PNOTIFY_RECORD notifyRecord = DeferredContext; + PDEVICE_EXTENSION deviceExtension; + PIRP irp; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + DebugPrint(("==> CustomTimerDPC \n")); + + ASSERT(notifyRecord != NULL); // can't be NULL + _Analysis_assume_(notifyRecord != NULL); + + deviceExtension = notifyRecord->DeviceExtension; + + KeAcquireSpinLockAtDpcLevel(&deviceExtension->QueueLock); + + RemoveEntryList(¬ifyRecord->ListEntry); + + switch (notifyRecord->Type) { + case IRP_BASED: + irp = notifyRecord->Message.PendingIrp; + if (irp != NULL) { + if (IoSetCancelRoutine(irp, NULL) != NULL) { + + irp->Tail.Overlay.DriverContext[3] = NULL; + + // + // Drop the lock before completing the request. + // + KeReleaseSpinLockFromDpcLevel(&deviceExtension->QueueLock); + + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = 0; + IoCompleteRequest(irp, IO_NO_INCREMENT); + + KeAcquireSpinLockAtDpcLevel(&deviceExtension->QueueLock); + + } else { + // + // Cancel routine will run as soon as we release the lock. + // So let it complete the request and free the record. + // + InitializeListHead(¬ifyRecord->ListEntry); + notifyRecord->CancelRoutineFreeMemory = TRUE; + notifyRecord = NULL; + } + } else { + // + // Cancel routine has run and completed the IRP. So just free + // the record. + // + ASSERT(notifyRecord->CancelRoutineFreeMemory == FALSE); + } + + break; + + case EVENT_BASED: + // + // Signal the Event created in user-mode. + // + KeSetEvent(notifyRecord->Message.Event, 0, FALSE); + + // + // Dereference the object as we are done with it. + // + ObDereferenceObject(notifyRecord->Message.Event); + + break; + + default: + ASSERT(FALSE); + break; + } + + KeReleaseSpinLockFromDpcLevel(&deviceExtension->QueueLock); + + // + // Free the memory outside the lock for better performance. + // + if (notifyRecord != NULL) { + ExFreePoolWithTag(notifyRecord, TAG); + notifyRecord = NULL; + } + + DebugPrint(("<== CustomTimerDPC\n")); + + return; +} + +_Use_decl_annotations_ +NTSTATUS +RegisterIrpBasedNotification( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + This routine queues a IRP based notification record to be + handled by a DPC. + + +Arguments: + + DeviceObject - Context for the activity. + Irp - The device control argument block. + +Return Value: + + NTSTATUS - If the status is not STATUS_PENDING, the caller + will complete the request. + + +--*/ +{ + PDEVICE_EXTENSION deviceExtension; + PNOTIFY_RECORD notifyRecord; + PIO_STACK_LOCATION irpStack; + KIRQL oldIrql; + PREGISTER_EVENT registerEvent; + + DebugPrint(("\tRegisterIrpBasedNotification\n")); + + irpStack = IoGetCurrentIrpStackLocation(Irp); + deviceExtension = DeviceObject->DeviceExtension; + registerEvent = (PREGISTER_EVENT)Irp->AssociatedIrp.SystemBuffer; + + // + // Allocate a record and save all the event context. + // + + notifyRecord = ExAllocatePoolWithQuotaTag(NonPagedPool, + sizeof(NOTIFY_RECORD), + TAG); + + if (NULL == notifyRecord) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + InitializeListHead(¬ifyRecord->ListEntry); + + notifyRecord->FileObject = irpStack->FileObject; + notifyRecord->DeviceExtension = deviceExtension; + notifyRecord->Type = IRP_BASED; + notifyRecord->Message.PendingIrp = Irp; + + // + // Start the timer to run the CustomTimerDPC in DueTime seconds to + // simulate an interrupt (which would queue a DPC). + // The user's event object is signaled or the IRP is completed in the DPC to + // notify the hardware event. + // + + // ensure relative time for this sample + + if (registerEvent->DueTime.QuadPart > 0) { + registerEvent->DueTime.QuadPart = -(registerEvent->DueTime.QuadPart); + } + + KeInitializeDpc(¬ifyRecord->Dpc, // Dpc + CustomTimerDPC, // DeferredRoutine + notifyRecord // DeferredContext + ); + + KeInitializeTimer(¬ifyRecord->Timer); + + // + // We will set the cancel routine and TimerDpc within the + // lock so that they don't modify the list before we are + // completely done. + // + KeAcquireSpinLock(&deviceExtension->QueueLock, &oldIrql); + + // + // Set the cancel routine. This is required if the app decides to + // exit or cancel the event prematurely. + // + IoSetCancelRoutine (Irp, EventCancelRoutine); + + // + // Before we queue the IRP, we must check to see if it's cancelled. + // + if (Irp->Cancel) { + + // + // Clear the cancel-routine automically and check the return value. + // We will complete the IRP here if we succeed in clearing it. If + // we fail then we will let the cancel-routine complete it. + // + if (IoSetCancelRoutine (Irp, NULL) != NULL) { + + // + // We are able to successfully clear the routine. Either the + // the IRP is cancelled before we set the cancel-routine or + // we won the race with I/O manager in clearing the routine. + // Return STATUS_CANCELLED so that the caller can complete + // the request. + + KeReleaseSpinLock(&deviceExtension->QueueLock, oldIrql); + + ExFreePoolWithTag(notifyRecord, TAG); + + return STATUS_CANCELLED; + } else { + // + // The IRP got cancelled after we set the cancel-routine and the + // I/O manager won the race in clearing it and called the cancel + // routine. So queue the request so that cancel-routine can dequeue + // and complete it. Note the cancel-routine cannot run until we + // drop the queue lock. + // + } + } + + IoMarkIrpPending(Irp); + + InsertTailList(&deviceExtension->EventQueueHead, + ¬ifyRecord->ListEntry); + + notifyRecord->CancelRoutineFreeMemory = FALSE; + + // + // We will save the record pointer in the IRP so that we can get to + // it directly in the CancelRoutine. + // + Irp->Tail.Overlay.DriverContext[3] = notifyRecord; + + KeSetTimer(¬ifyRecord->Timer, // Timer + registerEvent->DueTime, // DueTime + ¬ifyRecord->Dpc // Dpc + ); + + KeReleaseSpinLock(&deviceExtension->QueueLock, oldIrql); + + // + // We will return pending as we have marked the IRP pending. + // + return STATUS_PENDING;; + +} + +_Use_decl_annotations_ +NTSTATUS +RegisterEventBasedNotification( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + This routine queues a event based notification record + to be handled by a DPC. + +Arguments: + + DeviceObject - Context for the activity. + Irp - The device control argument block. + +Return Value: + + NTSTATUS - If the status is not STATUS_PENDING, the caller + will complete the request. + +--*/ +{ + PDEVICE_EXTENSION deviceExtension; + PNOTIFY_RECORD notifyRecord; + NTSTATUS status; + PIO_STACK_LOCATION irpStack; + PREGISTER_EVENT registerEvent; + KIRQL oldIrql; + + DebugPrint(("\tRegisterEventBasedNotification\n")); + + deviceExtension = DeviceObject->DeviceExtension; + + irpStack = IoGetCurrentIrpStackLocation(Irp); + registerEvent = (PREGISTER_EVENT)Irp->AssociatedIrp.SystemBuffer; + + // + // Allocate a record and save all the event context. + // + notifyRecord = ExAllocatePoolWithQuotaTag(NonPagedPool, + sizeof(NOTIFY_RECORD), + TAG); + + if (NULL == notifyRecord) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + InitializeListHead(¬ifyRecord->ListEntry); + + notifyRecord->FileObject = irpStack->FileObject; + notifyRecord->DeviceExtension = deviceExtension; + notifyRecord->Type = EVENT_BASED; + + // + // Get the object pointer from the handle. Note we must be in the context + // of the process that created the handle. + // + status = ObReferenceObjectByHandle(registerEvent->hEvent, + SYNCHRONIZE | EVENT_MODIFY_STATE, + *ExEventObjectType, + Irp->RequestorMode, + ¬ifyRecord->Message.Event, + NULL + ); + + if (!NT_SUCCESS(status)) { + + DebugPrint(("\tUnable to reference User-Mode Event object, Error = 0x%x\n", status)); + ExFreePoolWithTag(notifyRecord, TAG); + return status; + } + + // + // Start the timer to run the CustomTimerDPC in DueTime seconds to + // simulate an interrupt (which would queue a DPC). + // The user's event object is signaled or the IRP is completed in the DPC to + // notify the hardware event. + // + if (registerEvent->DueTime.QuadPart > 0) { + registerEvent->DueTime.QuadPart = -(registerEvent->DueTime.QuadPart); + } + + KeInitializeDpc(¬ifyRecord->Dpc, // Dpc + CustomTimerDPC, // DeferredRoutine + notifyRecord // DeferredContext + ); + + KeInitializeTimer(¬ifyRecord->Timer); + + KeAcquireSpinLock(&deviceExtension->QueueLock, &oldIrql); + + InsertTailList(&deviceExtension->EventQueueHead, + ¬ifyRecord->ListEntry); + + KeReleaseSpinLock(&deviceExtension->QueueLock, oldIrql); + + KeSetTimer(¬ifyRecord->Timer, // Timer + registerEvent->DueTime, // DueTime + ¬ifyRecord->Dpc // Dpc + ); + return STATUS_SUCCESS; +} + + diff --git a/general/event/wdm/event.h b/general/event/wdm/event.h new file mode 100644 index 00000000..1de7db76 --- /dev/null +++ b/general/event/wdm/event.h @@ -0,0 +1,99 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + + +Module Name: + + Event.h + +--*/ + +#ifndef __EVENT__ +#define __EVENT__ + +// +// DEFINES +// + +#define NTDEVICE_NAME_STRING L"\\Device\\Event_Sample" +#define SYMBOLIC_NAME_STRING L"\\DosDevices\\Event_Sample" +#define TAG (ULONG)'TEVE' + +#if DBG +#define DebugPrint(_x_) \ + DbgPrint("EVENT.SYS: ");\ + DbgPrint _x_; + +#else + +#define DebugPrint(_x_) + +#endif + +// +// DATA +// +typedef struct _DEVICE_EXTENSION { + PDEVICE_OBJECT Self; + LIST_ENTRY EventQueueHead; // where all the user notification requests are queued + KSPIN_LOCK QueueLock; +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + + +typedef struct _NOTIFY_RECORD{ + NOTIFY_TYPE Type; + LIST_ENTRY ListEntry; + union { + PKEVENT Event; + PIRP PendingIrp; + } Message; + KDPC Dpc; + KTIMER Timer; + PFILE_OBJECT FileObject; + PDEVICE_EXTENSION DeviceExtension; + BOOLEAN CancelRoutineFreeMemory; +} NOTIFY_RECORD, *PNOTIFY_RECORD; + +typedef struct _FILE_CONTEXT{ + // + // Lock to rundown threads that are dispatching I/Os on a file handle + // while the cleanup for that handle is in progress. + // + IO_REMOVE_LOCK FileRundownLock; +} FILE_CONTEXT, *PFILE_CONTEXT; + +// +// Function prototypes +// + + +DRIVER_INITIALIZE DriverEntry; + +_Dispatch_type_(IRP_MJ_CREATE) +_Dispatch_type_(IRP_MJ_CLOSE) +DRIVER_DISPATCH EventCreateClose; + +_Dispatch_type_(IRP_MJ_CLEANUP) +DRIVER_DISPATCH EventCleanup; + +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) +DRIVER_DISPATCH EventDispatchIoControl; + +DRIVER_UNLOAD EventUnload; + +DRIVER_CANCEL EventCancelRoutine; + +KDEFERRED_ROUTINE CustomTimerDPC; + +DRIVER_DISPATCH RegisterEventBasedNotification; + +DRIVER_DISPATCH RegisterIrpBasedNotification; + + +#endif // __EVENT__ diff --git a/general/event/wdm/event.rc b/general/event/wdm/event.rc new file mode 100644 index 00000000..5ffe2ea7 --- /dev/null +++ b/general/event/wdm/event.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Sample Event Driver" +#define VER_INTERNALNAME_STR "event.sys" + +#include "common.ver" + diff --git a/general/event/wdm/event.vcxproj b/general/event/wdm/event.vcxproj new file mode 100644 index 00000000..3dd391d5 --- /dev/null +++ b/general/event/wdm/event.vcxproj @@ -0,0 +1,156 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {99CD8B5D-2961-44A5-ACB4-CC1CCCEE096B} + $(MSBuildProjectName) + Debug + Win32 + {463697D8-488D-488E-A7DF-ECB602F46DD2} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + event + + + event + + + event + + + event + + + + true + Level4 + + + + + true + Level4 + + + + + true + Level4 + + + + + true + Level4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/event/wdm/event.vcxproj.Filters b/general/event/wdm/event.vcxproj.Filters new file mode 100644 index 00000000..c876bb5b --- /dev/null +++ b/general/event/wdm/event.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {68A8B3BF-BA74-401E-A54C-A5D84A227969} + + + h;hpp;hxx;hm;inl;inc;xsd + {F899D68E-E4E7-4168-9636-254BF3CDC360} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {0DFB698C-60E9-4C46-A9DD-76D3E4A63C45} + + + inf;inv;inx;mof;mc; + {705F4A1B-5DF2-4182-BCEA-69C62EC7ED04} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/event/wdm/public.h b/general/event/wdm/public.h new file mode 100644 index 00000000..9e08960c --- /dev/null +++ b/general/event/wdm/public.h @@ -0,0 +1,48 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Event.h + +--*/ + +#ifndef __PUBLIC__ +#define __PUBLIC__ + + +#include "devioctl.h" +#include + +typedef enum { + IRP_BASED , + EVENT_BASED +} NOTIFY_TYPE; + +typedef struct _REGISTER_EVENT +{ + NOTIFY_TYPE Type; + HANDLE hEvent; + LARGE_INTEGER DueTime; // requested DueTime in 100-nanosecond units + +} REGISTER_EVENT , *PREGISTER_EVENT ; + +#define SIZEOF_REGISTER_EVENT sizeof(REGISTER_EVENT ) + + +#define IOCTL_REGISTER_EVENT \ + CTL_CODE( FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS ) + + +#define DRIVER_FUNC_INSTALL 0x01 +#define DRIVER_FUNC_REMOVE 0x02 + +#define DRIVER_NAME "event" + +#endif // __PUBLIC__ diff --git a/general/filehistory/ReadMe.md b/general/filehistory/ReadMe.md new file mode 100644 index 00000000..58ebbb46 --- /dev/null +++ b/general/filehistory/ReadMe.md @@ -0,0 +1,23 @@ +File History Sample +================== + +The FileHistory sample is a console application that starts the file history service, if it is stopped, and schedules regular backups. The application requires, as a command-line parameter, the path name of a storage device to use as the default backup target. + +This sample application uses the [File History API](http://msdn.microsoft.com/en-us/library/windows/hardware/hh829789), which is available starting with Windows 8.1. The File History API enables third parties to automatically configure the File History feature on a Windows platform and customize it in accordance with their unique needs. + + +Run the sample +-------------- + +The name of the built sample application is Fhsetup.exe. To run this application, open a command window and enter a command that has the following format: + +`fhsetup ` + +The `path` command-line parameter is the path name of a storage device to use as the default backup target. The following are examples: + +`fhsetup D:\` + +`fhsetup \\server\share` + +If the specified target is inaccessible, read-only, an invalid drive type (such as a CD), already being used for file history, or part of the protected namespace, the application fails the request and does not enable file history on the target. + diff --git a/general/filehistory/exe/fhsetup.cpp b/general/filehistory/exe/fhsetup.cpp new file mode 100644 index 00000000..41edfd02 --- /dev/null +++ b/general/filehistory/exe/fhsetup.cpp @@ -0,0 +1,271 @@ +// +// File History Sample Setup Tool +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// + +#include + +HRESULT ScheduleBackups() +/*++ + +Routine Description: + + This function starts the File History service if it is stopped + and schedules regular backups. + +Arguments: + + None + +Return Value: + + S_OK if successful + HRESULT from underlying functions + +--*/ +{ + HRESULT backupHr = S_OK; + HRESULT pipeHr = S_OK; + FH_SERVICE_PIPE_HANDLE pipe = NULL; + + pipeHr = FhServiceOpenPipe(TRUE, &pipe); + if (SUCCEEDED(pipeHr)) + { + backupHr = FhServiceReloadConfiguration(pipe); + pipeHr = FhServiceClosePipe(pipe); + } + + // The HRESULT from the backup operation is more important than + // the HRESULT from pipe operations + return FAILED(backupHr) ? backupHr : pipeHr; +} + +HRESULT ConfigureFileHistory( + _In_ PWSTR TargetPath + ) +/*++ + +Routine Description: + + This function configures a target for File History. + It will only succeed if the user has never configured File History + before and there is no File History data on the target. + +Arguments: + + TargetPath - + The path of the File History target + +Return Value: + + S_OK if successful + E_INVALIDARG if TargetPath is NULL + E_FAIL if configuration failed because File History is disabled by + group policy or the target is not valid + HRESULT from underlying functions + +--*/ +{ + HRESULT hr = S_OK; + FH_BACKUP_STATUS backupStatus; + FH_DEVICE_VALIDATION_RESULT validationResult; + CComPtr configMgr; + CComBSTR targetPath; + CComBSTR targetName; + + // TargetPath must not be NULL + if (TargetPath == NULL) + { + hr = E_INVALIDARG; + goto Cleanup; + } + + // Copy the target path into a local variable and set the target name + // to an empty string to allow the config manager to set a name + _ATLTRY + { + targetPath = TargetPath; + targetName = L""; + } + _ATLCATCH(e) + { + hr = e; + goto Cleanup; + } + + // The configuration manager is used to create and load configuration + // files, get/set the backup status, validate a target, etc + hr = configMgr.CoCreateInstance(CLSID_FhConfigMgr); + if (FAILED(hr)) + { + wprintf(L"Error: CoCreateInstance failed (0x%X)\n", hr); + goto Cleanup; + } + + // Create a new default configuration file - do not overwrite if one + // already exists + wprintf(L"Creating default configuration\n"); + hr = configMgr->CreateDefaultConfiguration(FALSE); + if (FAILED(hr)) + { + if (hr == FHCFG_E_CONFIG_ALREADY_EXISTS) + { + wprintf(L"Error: File History has previously been configured\n"); + } + else + { + wprintf(L"Error: CreateDefaultConfiguration failed (0x%X)\n", hr); + } + goto Cleanup; + } + + // Check the backup status + // If File History is disabled by group policy, quit + wprintf(L"Getting backup status\n"); + hr = configMgr->GetBackupStatus(&backupStatus); + if (FAILED(hr)) + { + wprintf(L"Error: GetBackupStatus failed (0x%X)\n", hr); + goto Cleanup; + } + if (backupStatus == FH_STATUS_DISABLED_BY_GP) + { + wprintf(L"Error: File History is disabled by group policy\n"); + hr = E_FAIL; + goto Cleanup; + } + + // Make sure the target is valid to be used for File History + wprintf(L"Validating target\n"); + hr = configMgr->ValidateTarget(targetPath, &validationResult); + if (FAILED(hr)) + { + wprintf(L"Error: ValidateTarget failed (0x%X)\n", hr); + goto Cleanup; + } + if (validationResult != FH_VALID_TARGET) + { + // If the target is inaccessible, read-only, an invalid drive type + // (such as a CD), already being used for File History, or part of + // the protected namespace - don't enable File History + wprintf(L"Error: %ws is not a valid target\n", targetPath.m_str); + hr = E_FAIL; + goto Cleanup; + } + + // Provision the target to be used for File History and set + // it as the default target + wprintf(L"Provisioning and setting target\n"); + hr = configMgr->ProvisionAndSetNewTarget(targetPath, targetName); + if (FAILED(hr)) + { + wprintf(L"Error: ProvisionAndSetNewTarget failed (0x%X)\n", hr); + goto Cleanup; + } + + // Enable File History + wprintf(L"Enabling File History\n"); + hr = configMgr->SetBackupStatus(FH_STATUS_ENABLED); + if (FAILED(hr)) + { + wprintf(L"Error: SetBackupStatus failed (0x%X)\n", hr); + goto Cleanup; + } + + // Save the configuration to disk + wprintf(L"Saving configuration\n"); + hr = configMgr->SaveConfiguration(); + if (FAILED(hr)) + { + wprintf(L"Error: SaveConfiguration failed (0x%X)\n", hr); + goto Cleanup; + } + + // Tell the File History service to schedule backups + wprintf(L"Scheduling regular backups\n"); + hr = ScheduleBackups(); + if (FAILED(hr)) + { + wprintf(L"Error: ScheduleBackups failed (0x%X)\n", hr); + goto Cleanup; + } + + // Recommend the target to other Homegroup members + wprintf(L"Recommending target to Homegroup\n"); + HRESULT hrRecommend = configMgr->ChangeDefaultTargetRecommendation(TRUE); + if (FAILED(hrRecommend)) + { + wprintf(L"Warning: Failed to recommend target to Homegroup (0x%X)\n", hrRecommend); + } + + wprintf(L"Success! File History is now enabled\n"); + +Cleanup: + return hr; +} + +int __cdecl wmain( + _In_ int Argc, + _In_reads_(Argc) PWSTR Argv[] + ) +/*++ + +Routine Description: + + This is the main entry point of the console application. + +Arguments: + + Argc - the number of command line arguments + Argv - command line arguments + +Return Value: + + exit code + +--*/ +{ + HRESULT hr = S_OK; + BOOL comInitialized = FALSE; + + wprintf(L"\nFile History Sample Setup Tool\n"); + wprintf(L"Copyright (C) Microsoft Corporation. All rights reserved.\n\n"); + + // If there are fewer than 2 command-line arguments, print the correct + // usage and exit + if (Argc < 2) + { + wprintf(L"Usage: fhsetup \n\n"); + wprintf(L"Examples:\n"); + wprintf(L" fhsetup D:\\\n"); + wprintf(L" fhsetup \\\\server\\share\\\n\n"); + goto Cleanup; + } + + // COM is needed to use the Config Manager + wprintf(L"Initializing COM...\n"); + hr = CoInitialize(NULL); + if (FAILED(hr)) + { + wprintf(L"Error: CoInitialize failed (0x%X)\n", hr); + goto Cleanup; + } + comInitialized = TRUE; + + hr = ConfigureFileHistory(Argv[1]); + if (FAILED(hr)) + { + wprintf(L"File History configuration failed (0x%X)\n", hr); + goto Cleanup; + } + +Cleanup: + // If COM was initialized, make sure it is uninitialized + if (comInitialized) + { + CoUninitialize(); + comInitialized = FALSE; + } + + return 0; +} diff --git a/general/filehistory/exe/fhsetup.h b/general/filehistory/exe/fhsetup.h new file mode 100644 index 00000000..eeb241c9 --- /dev/null +++ b/general/filehistory/exe/fhsetup.h @@ -0,0 +1,27 @@ +// +// File History Sample Setup Tool +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// +// Define CLSID_FhConfigMgr. +// We must include initguid.h before using DEFINE_GUID otherwise +// DEFINE_GUID will declare CLSID_FhConfigMgr as extern. +// + +#include +DEFINE_GUID(CLSID_FhConfigMgr,0xED43BB3C,0x09E9,0x498a,0x9D,0xF6,0x21,0x77,0x24,0x4C,0x6D,0xB4); \ No newline at end of file diff --git a/general/filehistory/exe/fhsetup.vcxproj b/general/filehistory/exe/fhsetup.vcxproj new file mode 100644 index 00000000..29151c50 --- /dev/null +++ b/general/filehistory/exe/fhsetup.vcxproj @@ -0,0 +1,191 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {5ED52C3F-4CA6-4E04-82DD-404CA008EE8B} + $(MSBuildProjectName) + Debug + Win32 + {19CB999A-197D-4EAB-85F1-C4230C32B26E} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + fhsetup + Dynamic + + + fhsetup + Dynamic + + + fhsetup + Dynamic + + + fhsetup + Dynamic + + + + true + Level4 + Sync + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(AdditionalDependencies);shell32.lib;shlwapi.lib;Ole32.lib;Oleaut32.lib;User32.lib;fhsvcctl.lib + + + + + true + Level4 + Sync + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(AdditionalDependencies);shell32.lib;shlwapi.lib;Ole32.lib;Oleaut32.lib;User32.lib;fhsvcctl.lib + + + + + true + Level4 + Sync + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(AdditionalDependencies);shell32.lib;shlwapi.lib;Ole32.lib;Oleaut32.lib;User32.lib;fhsvcctl.lib + + + + + true + Level4 + Sync + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_INC_PATH) + + + %(AdditionalDependencies);shell32.lib;shlwapi.lib;Ole32.lib;Oleaut32.lib;User32.lib;fhsvcctl.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/filehistory/exe/fhsetup.vcxproj.Filters b/general/filehistory/exe/fhsetup.vcxproj.Filters new file mode 100644 index 00000000..8b773bab --- /dev/null +++ b/general/filehistory/exe/fhsetup.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {86ABC263-8E1E-48CB-8D3E-368CE3C22885} + + + h;hpp;hxx;hm;inl;inc;xsd + {E466B712-D4BD-4268-8FDB-FB7BDDAC7FB4} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {C3E2505A-EA9E-4B2A-B2B1-1AF968556961} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/filehistory/filehistory.sln b/general/filehistory/filehistory.sln new file mode 100644 index 00000000..055780ce --- /dev/null +++ b/general/filehistory/filehistory.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fhsetup", "exe\fhsetup.vcxproj", "{5ED52C3F-4CA6-4E04-82DD-404CA008EE8B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5ED52C3F-4CA6-4E04-82DD-404CA008EE8B}.Debug|Win32.ActiveCfg = Debug|Win32 + {5ED52C3F-4CA6-4E04-82DD-404CA008EE8B}.Debug|Win32.Build.0 = Debug|Win32 + {5ED52C3F-4CA6-4E04-82DD-404CA008EE8B}.Release|Win32.ActiveCfg = Release|Win32 + {5ED52C3F-4CA6-4E04-82DD-404CA008EE8B}.Release|Win32.Build.0 = Release|Win32 + {5ED52C3F-4CA6-4E04-82DD-404CA008EE8B}.Debug|x64.ActiveCfg = Debug|x64 + {5ED52C3F-4CA6-4E04-82DD-404CA008EE8B}.Debug|x64.Build.0 = Debug|x64 + {5ED52C3F-4CA6-4E04-82DD-404CA008EE8B}.Release|x64.ActiveCfg = Release|x64 + {5ED52C3F-4CA6-4E04-82DD-404CA008EE8B}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/general/installwdf/Install.cpp b/general/installwdf/Install.cpp new file mode 100644 index 00000000..cd89e284 --- /dev/null +++ b/general/installwdf/Install.cpp @@ -0,0 +1,509 @@ +#include +#include +#include +#include + +// +// These defines control which packages need to be updated. In order to +// to customize this for your drivers needs, edit needed packages to 1 and +// unneeded packages to 0. +// +// If your UMDF driver uses USB functionality, you also need KMDF and WinUSB +// +#define INSTALL_KMDF (TRUE) +#define INSTALL_WINUSB (TRUE) +#define INSTALL_UMDF (TRUE) + +// +// Reference strings that are used to build our MSU package name. These will +// change between releases of the framework and will need to be updated +// +#define WINUSB_UPDATE_NAME L"WinUSB_1.9.msu" + +// +// MSU names are of the format: +// -.- +// Win-..msu +// +#define MSU_FORMAT_STRING L"%s-%d.%d-Win-%d.%d.msu" + +#define WUSA_EXE L"%windir%\\system32\\wusa.exe" + +#define WUSA_EXE_ARGUMENTS L"/quiet /norestart" + +#define WDF_MAJOR_VERSION 1 +#define WDF_MINOR_VERSION 11 + +DWORD +ApplyUpdate( + PCWSTR MSUName +) +{ + DWORD error = ERROR_SUCCESS; + size_t cmdLengthBytes; + size_t applicationLengthBytes; + BOOL ok; + PROCESS_INFORMATION pInfo; + STARTUPINFOW startInfo; + PWCHAR applicationName = NULL; + PWCHAR commandLine = NULL; + HRESULT hr; + + ZeroMemory(&startInfo,sizeof(startInfo)) ; + startInfo.cb = sizeof(STARTUPINFO) ; + + ZeroMemory(&pInfo,sizeof(pInfo)); + // + // Check that the update package exists + // + error = GetFileAttributes(MSUName); + + if (error == INVALID_FILE_ATTRIBUTES) { + error = GetLastError(); + wprintf(L"Error: Could not find update file %s; error %x\n", MSUName, error); + goto exit; + } + + // + // Invoke wusa + // + applicationName = (PWCHAR) LocalAlloc(LPTR, (MAX_PATH + 1)*sizeof(WCHAR)); + if (applicationName == NULL) { + error = ERROR_INSTALL_FAILURE; + wprintf(L"Failed to allocate applicationName buffer\n"); + goto exit; + } + + applicationName[0] = L'\0'; + + applicationLengthBytes = ExpandEnvironmentStrings(WUSA_EXE, + applicationName, + MAX_PATH+1); + + if ((applicationLengthBytes == 0) || + (applicationLengthBytes > MAX_PATH+1)) { + wprintf(L"Could not expland %s\n", WUSA_EXE); + error = ERROR_INSTALL_FAILURE; + goto exit; + } + + applicationLengthBytes = sizeof(WCHAR) * applicationLengthBytes; + + hr = StringCbLength(MSUName, + MAX_PATH * sizeof(WCHAR), + &cmdLengthBytes); + + if (hr != S_OK) { + + error = ERROR_INSTALL_FAILURE; + wprintf(L"StringCbLength failed MSUName, %x\n", + hr); + goto exit; + } + // + // Add enough padding for 2 \". The size returned by sizeof() includes + // the terminating L'\0' + // + cmdLengthBytes = applicationLengthBytes + cmdLengthBytes + sizeof(WUSA_EXE_ARGUMENTS) + 3*sizeof(WCHAR); + + commandLine = (PWCHAR) LocalAlloc(LPTR, cmdLengthBytes ); + + if (commandLine == NULL) { + wprintf(L"Failed to allocate applicationName buffer\n"); + error = ERROR_INSTALL_FAILURE; + goto exit; + } + + hr = StringCbPrintf(commandLine, + cmdLengthBytes, + L"%s \"%s\" %s", + applicationName, + MSUName, + WUSA_EXE_ARGUMENTS); + if (hr != S_OK) { + + error = ERROR_INSTALL_FAILURE; + wprintf(L"StringCbPrintf failed for applicationParameters, %x\n", + hr); + goto exit; + } + + wprintf(L"Invoking: %s\n", commandLine); + + ok = CreateProcess(applicationName, // name of executable module + commandLine, // command line string + NULL, // SD + NULL, // SD + TRUE, // handle inheritance option + 0, // creation flags CREATE_NO_WINDOW + NULL, // new environment block + NULL, // current directory name + &startInfo, // startup information + &pInfo // process information + ); + + if (ok == FALSE) { + + error = GetLastError(); + wprintf(L"Create process failed : %x\n", + error); + goto exit; + + } else { + + // + // Wait until child process exits. + // + + error = WaitForSingleObject( pInfo.hProcess, INFINITE ); + + if ( error != WAIT_OBJECT_0 ) { + // + // It can't hurt to add this + // + TerminateProcess(pInfo.hProcess, (UINT)-1); + } + + GetExitCodeProcess(pInfo.hProcess, &error); + + // + // The possible return values for wusa.exe are: + // 1)ERROR_SUCCESS (0) : installation was successfull + // 2)ERROR_SUCCESS_REBOOT_REQUIRED (3010) : installation was successful, + // however a reboot is required, so that the binaries will be + // loaded to memory + // 3)S_FALSE (1) (Vista) OR WU_S_ALREADY_INSTALLED (240006) (Win7): + // No action was taken (i.e. files were already installed) + // 4)Everything else (e.g. ERROR_INSTALL_FAILURE) is an error + // + + switch (error) { + case S_FALSE: + case WU_S_ALREADY_INSTALLED: + wprintf(L"The package was already installed in the system\n"); + error = ERROR_SUCCESS; + break; + case ERROR_SUCCESS: + wprintf(L"The package was installed successfully\n"); + break; + case ERROR_SUCCESS_REBOOT_REQUIRED: + wprintf(L"The package was installed successfully but requires a reboot\n"); + break; + case ERROR_SERVICE_DISABLED: + case WU_E_WU_DISABLED: + + // + // If the "Windows Update" service is disabled, then wusa + // returns ERROR_SERVICE_DISABLED + // + + wprintf(L"The \"Windows Update\" service is disabled. It " + L"has to be enabled for the installation to succeed." + L"\n"); + break; + default: + wprintf(L"The update process returned error code :%x. ", + error); + wprintf(L"For additional information please look at the log " + L"files %%windir%%\\windowsupdate.log and " + L"%%windir%%\\Logs\\CBS\\CBS.log\n"); + break; + } + + CloseHandle(pInfo.hProcess); + CloseHandle(pInfo.hThread); + } + +exit: + if (commandLine != NULL) { + LocalFree(commandLine); + commandLine = NULL; + } + + if (applicationName != NULL) { + LocalFree(applicationName); + applicationName = NULL; + } + + return error; +} + +BOOL +PromptRestart() +{ + HANDLE hToken; // handle to process token + TOKEN_PRIVILEGES tkp; // pointer to token structure + BOOL fResult; // system shutdown flag + + // Get the current process token handle so we can get shutdown + // privilege. + + if (!OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) + return FALSE; + + // Get the LUID for shutdown privilege. + + LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, + &tkp.Privileges[0].Luid); + + tkp.PrivilegeCount = 1; // one privilege to set + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + // Get shutdown privilege for this process. + + AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, + (PTOKEN_PRIVILEGES) NULL, 0); + + // Cannot test the return value of AdjustTokenPrivileges. + + if (GetLastError() != ERROR_SUCCESS) { + return FALSE; + } + + // Display the shutdown dialog box and start the countdown. + + #pragma prefast(suppress:28159, "Ignore the suggestion against system shutdown.") + fResult = InitiateSystemShutdownEx( + NULL, // shut down local computer + NULL, // message for user + 0, // time-out period, in seconds + FALSE, // ask user to close apps + TRUE, // reboot after shutdown + SHTDN_REASON_FLAG_PLANNED // shutdown reason + | SHTDN_REASON_MAJOR_SOFTWARE + | SHTDN_REASON_MINOR_UPGRADE); + + // Disable shutdown privilege. + + tkp.Privileges[0].Attributes = 0; + AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, + (PTOKEN_PRIVILEGES) NULL, 0); + + return fResult; + +} + +DWORD +UpdateWdf( + VOID + ) +{ + BOOL ok; + OSVERSIONINFO curOsvi; + OSVERSIONINFOEX targetOsvi; + DWORDLONG dwlConditionMask = 0; + WCHAR MSUName[MAX_PATH]; + BOOL rebootNeeded = FALSE; + DWORD error = ERROR_SUCCESS; + HRESULT hr; + + // + // Make sure updates are valid for this operating system. + // Vista SP1/SP2; Win7 RTM + // TODO: what happens on Vista RTM/SP3+/Win7 SP1+ + // + + ZeroMemory(&targetOsvi, sizeof(OSVERSIONINFOEX)); + dwlConditionMask = 0; + + targetOsvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + targetOsvi.dwMajorVersion = 6; + targetOsvi.dwMinorVersion = 0; + targetOsvi.wServicePackMajor = 0; + + VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION, VER_LESS_EQUAL ); + VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION, VER_LESS_EQUAL ); + VER_SET_CONDITION( dwlConditionMask, VER_SERVICEPACKMAJOR, VER_LESS_EQUAL ); + + ok = VerifyVersionInfo( &targetOsvi, + VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, + dwlConditionMask); + + if (ok) { + wprintf(L"Error: Updates are not supported on OS before Vista or Vista RTM\n"); + error = ERROR_OLD_WIN_VERSION; + goto exit; + } + + // + // No need to update on Win8+ + // + ZeroMemory(&targetOsvi, sizeof(OSVERSIONINFOEX)); + dwlConditionMask = 0; + + targetOsvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + targetOsvi.dwMajorVersion = 6; + targetOsvi.dwMinorVersion = 2; + + VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL ); + VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL ); + + ok = VerifyVersionInfo( &targetOsvi, + VER_MAJORVERSION | VER_MINORVERSION, + dwlConditionMask); + + if (ok) { + wprintf(L"Updates are not needed to Windows 8, they are already inbox\n"); + error = ERROR_SUCCESS; + goto exit; + } + + // + // Create MSU name + // + + ZeroMemory(&curOsvi, sizeof(OSVERSIONINFO)); + curOsvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + +#pragma warning( push ) +#pragma warning( disable : 4996 ) // 'GetVersionEx': was declared deprecated + ok = GetVersionEx(&curOsvi); + + if (ok == FALSE) { + + error = GetLastError(); + wprintf(L"GetVersionEx failed: %x\n", error); + goto exit; + } + + // + // We want to apply the updates in a specific order. This is because UMDF + // is potentially dependent on WinUSB. WinUSB is dependent on KMDF. If we + // fail to apply an update, we want to make sure the machine is in a good + // state. This we apply required framework updates first. + // KMDF > WinUSB > UMDF + // + +#if INSTALL_KMDF + hr = StringCchPrintf(MSUName, + MAX_PATH, + MSU_FORMAT_STRING, + L"kmdf", + WDF_MAJOR_VERSION, + WDF_MINOR_VERSION, + curOsvi.dwMajorVersion, + curOsvi.dwMinorVersion); + + if (hr != S_OK) { + wprintf(L"StringCchPrintf for KMDF MSU failed: %x\n", hr); + error = ERROR_INSTALL_FAILURE; + goto exit; + } + + error = ApplyUpdate(MSUName); + + if (error == ERROR_SUCCESS_REBOOT_REQUIRED) { + rebootNeeded = TRUE; + } else if (error != ERROR_SUCCESS) { + goto exit; + } +#endif // INSTALL_KMDF +#pragma warning( pop ) // 'GetVersionEx': was declared deprecated + +#if (INSTALL_WINUSB) + // + // WinUSB update only applies to Vista + // + + ZeroMemory(&targetOsvi, sizeof(OSVERSIONINFOEX)); + dwlConditionMask = 0; + + targetOsvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + targetOsvi.dwMajorVersion = 6; + targetOsvi.dwMinorVersion = 0; + + VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION, VER_EQUAL ); + VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION, VER_EQUAL ); + + ok = VerifyVersionInfo( &targetOsvi, + VER_MAJORVERSION | VER_MINORVERSION, + dwlConditionMask); + + if (ok) { + error = ApplyUpdate(WINUSB_UPDATE_NAME); + + if (error == ERROR_SUCCESS_REBOOT_REQUIRED) { + rebootNeeded = TRUE; + } else if (error != ERROR_SUCCESS) { + goto exit; + } + } +#endif // INSTALL_WINUSB + +#if INSTALL_UMDF + hr = StringCchPrintf(MSUName, + MAX_PATH, + MSU_FORMAT_STRING, + L"umdf", + WDF_MAJOR_VERSION, + WDF_MINOR_VERSION, + curOsvi.dwMajorVersion, + curOsvi.dwMinorVersion); + + if (hr != S_OK) { + wprintf(L"StringCchPrintf for UMDF MSU failed: %x\n", hr); + error = ERROR_INSTALL_FAILURE; + goto exit; + } + + error = ApplyUpdate(MSUName); + + if (error == ERROR_SUCCESS_REBOOT_REQUIRED) { + rebootNeeded = TRUE; + } else if (error != ERROR_SUCCESS) { + goto exit; + } +#endif // INSTALL_UMDF + + // + // If we have made it to this point there have been no fatal errors. If + // there were fatal errors, these should be caught and we would've jumped + // to exit. + // + // We must account for the fact the latest update applied did not require + // a reboot but earlier updates did. + // + if (rebootNeeded == TRUE) { + + error = ERROR_SUCCESS_REBOOT_REQUIRED; + goto exit; + } + + error = ERROR_SUCCESS; + +exit: + + return error; +} + +int __cdecl +wmain( + _In_ int argc, + _In_reads_(argc) char* argv[] + ) +{ + DWORD updateStatus; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + updateStatus = UpdateWdf(); + + if (updateStatus == ERROR_SUCCESS_REBOOT_REQUIRED) { + + int msgboxID = MessageBox( + NULL, + L"A restart is needed for these changes to take effect\nRestart now?", + L"Restart Required", + MB_ICONEXCLAMATION | MB_YESNO + ); + + if (msgboxID == IDYES) + { + PromptRestart(); + } + } + + return updateStatus; +} diff --git a/general/installwdf/InstallWdf.vcxproj b/general/installwdf/InstallWdf.vcxproj new file mode 100644 index 00000000..566e44b1 --- /dev/null +++ b/general/installwdf/InstallWdf.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {242C8F74-1471-4876-ABE9-F9B3798F7537} + $(MSBuildProjectName) + Debug + Win32 + {EC6592EB-25EF-40EB-8E93-87FABB3C6085} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + InstallWdf + + + InstallWdf + + + InstallWdf + + + InstallWdf + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/installwdf/InstallWdf.vcxproj.Filters b/general/installwdf/InstallWdf.vcxproj.Filters new file mode 100644 index 00000000..aded6d8a --- /dev/null +++ b/general/installwdf/InstallWdf.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {5F54DABF-B262-4C6D-922E-4458AB12ED8C} + + + h;hpp;hxx;hm;inl;inc;xsd + {7D22CB0D-9813-4E12-B539-5F8A9BCC71F1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {13558A71-E018-47D4-B9A5-45A8C27B32B1} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/installwdf/ReadMe.md b/general/installwdf/ReadMe.md new file mode 100644 index 00000000..429957d5 --- /dev/null +++ b/general/installwdf/ReadMe.md @@ -0,0 +1,9 @@ +WDF Installation Package +======================== + +This sample contains example code that demonstrates how to install WDF packages on a system. This code can be used as-is to install the needed WDF components onto a user system. This code can also be reworked into an existing setup application to provide a better experience. + + +Related technologies +-------------------- +[Installation Components for Framework-based Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544208) diff --git a/general/installwdf/installwdf.sln b/general/installwdf/installwdf.sln new file mode 100644 index 00000000..e520c1dc --- /dev/null +++ b/general/installwdf/installwdf.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InstallWdf", "InstallWdf.vcxproj", "{242C8F74-1471-4876-ABE9-F9B3798F7537}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {242C8F74-1471-4876-ABE9-F9B3798F7537}.Debug|Win32.ActiveCfg = Debug|Win32 + {242C8F74-1471-4876-ABE9-F9B3798F7537}.Debug|Win32.Build.0 = Debug|Win32 + {242C8F74-1471-4876-ABE9-F9B3798F7537}.Release|Win32.ActiveCfg = Release|Win32 + {242C8F74-1471-4876-ABE9-F9B3798F7537}.Release|Win32.Build.0 = Release|Win32 + {242C8F74-1471-4876-ABE9-F9B3798F7537}.Debug|x64.ActiveCfg = Debug|x64 + {242C8F74-1471-4876-ABE9-F9B3798F7537}.Debug|x64.Build.0 = Debug|x64 + {242C8F74-1471-4876-ABE9-F9B3798F7537}.Release|x64.ActiveCfg = Release|x64 + {242C8F74-1471-4876-ABE9-F9B3798F7537}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/general/ioctl/wdm/ReadMe.md b/general/ioctl/wdm/ReadMe.md new file mode 100644 index 00000000..94191a33 --- /dev/null +++ b/general/ioctl/wdm/ReadMe.md @@ -0,0 +1,17 @@ +IOCTL +===== + +This sample demonstrates the usage of four different types of IOCTLs (METHOD\_IN\_DIRECT, METHOD\_OUT\_DIRECT, METHOD\_NEITHER, and METHOD\_BUFFERED). + +The sample shows how the user input and output buffers specified in the **DeviceIoControl** function call are handled, in each case, by the I/O subsystem and the driver. + +The sample consists of a legacy device driver and a Win32 console test application. The test application opens a handle to the device exposed by the driver and makes all four different **DeviceIoControl** calls, one after another. To understand how the IRP fields are set the I/O manager, you should run the checked build version of the driver and look at the debug output. + +**Note**  This sample driver is not a Plug and Play driver. This is a minimal driver meant to demonstrate a feature of the operating system. Neither this driver nor its sample programs are intended for use in a production environment. Instead, they are intended for educational purposes and as a skeleton driver. + + +Run the sample +-------------- + +To test this driver, copy the test app, Ioctlapp.exe, and the driver to the same directory, and run the application. The application will automatically load the driver, if it's not already loaded, and interact with the driver. When you exit the application, the driver will be stopped, unloaded and removed. + diff --git a/general/ioctl/wdm/exe/install.c b/general/ioctl/wdm/exe/install.c new file mode 100644 index 00000000..4b77f0aa --- /dev/null +++ b/general/ioctl/wdm/exe/install.c @@ -0,0 +1,550 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + install.c + +Abstract: + + Win32 routines to dynamically load and unload a Windows NT kernel-mode + driver using the Service Control Manager APIs. + +Environment: + + User mode only + +--*/ + + +#include +#include +#include +#include +#include +#include "sioctl.h" + +BOOLEAN +InstallDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceExe + ); + + +BOOLEAN +RemoveDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +StartDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +StopDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ); + +BOOLEAN +InstallDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceExe + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + SC_HANDLE schService; + DWORD err; + + // + // NOTE: This creates an entry for a standalone driver. If this + // is modified for use with a driver that requires a Tag, + // Group, and/or Dependencies, it may be necessary to + // query the registry for existing driver information + // (in order to determine a unique Tag, etc.). + // + + // + // Create a new a service object. + // + + schService = CreateService(SchSCManager, // handle of service control manager database + DriverName, // address of name of service to start + DriverName, // address of display name + SERVICE_ALL_ACCESS, // type of access to service + SERVICE_KERNEL_DRIVER, // type of service + SERVICE_DEMAND_START, // when to start service + SERVICE_ERROR_NORMAL, // severity if service fails to start + ServiceExe, // address of name of binary file + NULL, // service does not belong to a group + NULL, // no tag requested + NULL, // no dependency names + NULL, // use LocalSystem account + NULL // no password for service account + ); + + if (schService == NULL) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_EXISTS) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + printf("CreateService failed! Error = %d \n", err ); + + // + // Indicate an error. + // + + return FALSE; + } + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + // + // Indicate success. + // + + return TRUE; + +} // InstallDriver + +BOOLEAN +ManageDriver( + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceName, + _In_ USHORT Function + ) +{ + + SC_HANDLE schSCManager; + + BOOLEAN rCode = TRUE; + + // + // Insure (somewhat) that the driver and service names are valid. + // + + if (!DriverName || !ServiceName) { + + printf("Invalid Driver or Service provided to ManageDriver() \n"); + + return FALSE; + } + + // + // Connect to the Service Control Manager and open the Services database. + // + + schSCManager = OpenSCManager(NULL, // local machine + NULL, // local database + SC_MANAGER_ALL_ACCESS // access required + ); + + if (!schSCManager) { + + printf("Open SC Manager failed! Error = %d \n", GetLastError()); + + return FALSE; + } + + // + // Do the requested function. + // + + switch( Function ) { + + case DRIVER_FUNC_INSTALL: + + // + // Install the driver service. + // + + if (InstallDriver(schSCManager, + DriverName, + ServiceName + )) { + + // + // Start the driver service (i.e. start the driver). + // + + rCode = StartDriver(schSCManager, + DriverName + ); + + } else { + + // + // Indicate an error. + // + + rCode = FALSE; + } + + break; + + case DRIVER_FUNC_REMOVE: + + // + // Stop the driver. + // + + StopDriver(schSCManager, + DriverName + ); + + // + // Remove the driver service. + // + + RemoveDriver(schSCManager, + DriverName + ); + + // + // Ignore all errors. + // + + rCode = TRUE; + + break; + + default: + + printf("Unknown ManageDriver() function. \n"); + + rCode = FALSE; + + break; + } + + // + // Close handle to service control manager. + // + + if (schSCManager) { + + CloseServiceHandle(schSCManager); + } + + return rCode; + +} // ManageDriver + + +BOOLEAN +RemoveDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + BOOLEAN rCode; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", GetLastError()); + + // + // Indicate error. + // + + return FALSE; + } + + // + // Mark the service for deletion from the service control manager database. + // + + if (DeleteService(schService)) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + printf("DeleteService failed! Error = %d \n", GetLastError()); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + return rCode; + +} // RemoveDriver + + + +BOOLEAN +StartDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + DWORD err; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", GetLastError()); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Start the execution of the service (i.e. start the driver). + // + + if (!StartService(schService, // service identifier + 0, // number of arguments + NULL // pointer to arguments + )) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_ALREADY_RUNNING) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + printf("StartService failure! Error = %d \n", err ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + return FALSE; + } + + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + return TRUE; + +} // StartDriver + + + +BOOLEAN +StopDriver( + _In_ SC_HANDLE SchSCManager, + _In_ LPCTSTR DriverName + ) +{ + BOOLEAN rCode = TRUE; + SC_HANDLE schService; + SERVICE_STATUS serviceStatus; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + printf("OpenService failed! Error = %d \n", GetLastError()); + + return FALSE; + } + + // + // Request that the service stop. + // + + if (ControlService(schService, + SERVICE_CONTROL_STOP, + &serviceStatus + )) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + printf("ControlService failed! Error = %d \n", GetLastError() ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle (schService); + } + + return rCode; + +} // StopDriver + +BOOLEAN +SetupDriverName( + _Inout_updates_bytes_all_(BufferLength) PCHAR DriverLocation, + _In_ ULONG BufferLength + ) +{ + HANDLE fileHandle; + DWORD driverLocLen = 0; + + // + // Get the current directory. + // + + driverLocLen = GetCurrentDirectory(BufferLength, + DriverLocation + ); + + if (driverLocLen == 0) { + + printf("GetCurrentDirectory failed! Error = %d \n", GetLastError()); + + return FALSE; + } + + // + // Setup path name to driver file. + // + if (FAILED( StringCbCat(DriverLocation, BufferLength, "\\"DRIVER_NAME".sys") )) { + return FALSE; + } + + // + // Insure driver file is in the specified directory. + // + + if ((fileHandle = CreateFile(DriverLocation, + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + )) == INVALID_HANDLE_VALUE) { + + + printf("%s.sys is not loaded.\n", DRIVER_NAME); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Close open file handle. + // + + if (fileHandle) { + + CloseHandle(fileHandle); + } + + // + // Indicate success. + // + + return TRUE; + + +} // SetupDriverName + + + diff --git a/general/ioctl/wdm/exe/ioctlapp.vcxproj b/general/ioctl/wdm/exe/ioctlapp.vcxproj new file mode 100644 index 00000000..976df289 --- /dev/null +++ b/general/ioctl/wdm/exe/ioctlapp.vcxproj @@ -0,0 +1,196 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {76D71F31-1E96-453B-B624-603110936517} + $(MSBuildProjectName) + Debug + Win32 + {F11B90FF-7C0F-4187-A69C-B3D2C6FA36BD} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + ioctlapp + + + ioctlapp + + + ioctlapp + + + ioctlapp + + + + true + Level4 + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + 0x04000000 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + 0x04000000 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + 0x04000000 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + 0x04000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/ioctl/wdm/exe/ioctlapp.vcxproj.Filters b/general/ioctl/wdm/exe/ioctlapp.vcxproj.Filters new file mode 100644 index 00000000..6698bc65 --- /dev/null +++ b/general/ioctl/wdm/exe/ioctlapp.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {A6129EF0-0D42-48B8-B0C4-C484D8CB1BEC} + + + h;hpp;hxx;hm;inl;inc;xsd + {7C1B93C7-3641-4D37-AF76-8B4E72FB0E36} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {4E2B024A-1EF5-41B1-A019-C74272815D2A} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/ioctl/wdm/exe/testapp.c b/general/ioctl/wdm/exe/testapp.c new file mode 100644 index 00000000..5a62faa0 --- /dev/null +++ b/general/ioctl/wdm/exe/testapp.c @@ -0,0 +1,261 @@ +/*++ + +Copyright (c) 1990-98 Microsoft Corporation All Rights Reserved + +Module Name: + + testapp.c + +Abstract: + +Environment: + + Win32 console multi-threaded application + +--*/ +#include +#include +#include +#include +#include +#include +#include "..\sys\sioctl.h" + + +BOOLEAN +ManageDriver( + _In_ LPCTSTR DriverName, + _In_ LPCTSTR ServiceName, + _In_ USHORT Function + ); + +BOOLEAN +SetupDriverName( + _Inout_updates_bytes_all_(BufferLength) PCHAR DriverLocation, + _In_ ULONG BufferLength + ); + +char OutputBuffer[100]; +char InputBuffer[100]; + +VOID __cdecl +main( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] + ) +{ + HANDLE hDevice; + BOOL bRc; + ULONG bytesReturned; + DWORD errNum = 0; + TCHAR driverLocation[MAX_PATH]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + // + // open the device + // + + if ((hDevice = CreateFile( "\\\\.\\IoctlTest", + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE) { + + errNum = GetLastError(); + + if (errNum != ERROR_FILE_NOT_FOUND) { + + printf("CreateFile failed! ERROR_FILE_NOT_FOUND = %d\n", errNum); + + return ; + } + + // + // The driver is not started yet so let us the install the driver. + // First setup full path to driver name. + // + + if (!SetupDriverName(driverLocation, sizeof(driverLocation))) { + + return ; + } + + if (!ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_INSTALL + )) { + + printf("Unable to install driver. \n"); + + // + // Error - remove driver. + // + + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + return; + } + + hDevice = CreateFile( "\\\\.\\IoctlTest", + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if ( hDevice == INVALID_HANDLE_VALUE ){ + printf ( "Error: CreatFile Failed : %d\n", GetLastError()); + return; + } + + } + + // + // Printing Input & Output buffer pointers and size + // + + printf("InputBuffer Pointer = %p, BufLength = %d\n", InputBuffer, + sizeof(InputBuffer)); + printf("OutputBuffer Pointer = %p BufLength = %d\n", OutputBuffer, + sizeof(OutputBuffer)); + // + // Performing METHOD_BUFFERED + // + + StringCbCopy(InputBuffer, sizeof(InputBuffer), + "This String is from User Application; using METHOD_BUFFERED"); + + printf("\nCalling DeviceIoControl METHOD_BUFFERED:\n"); + + memset(OutputBuffer, 0, sizeof(OutputBuffer)); + + bRc = DeviceIoControl ( hDevice, + (DWORD) IOCTL_SIOCTL_METHOD_BUFFERED, + &InputBuffer, + (DWORD) strlen ( InputBuffer )+1, + &OutputBuffer, + sizeof( OutputBuffer), + &bytesReturned, + NULL + ); + + if ( !bRc ) + { + printf ( "Error in DeviceIoControl : %d", GetLastError()); + return; + + } + printf(" OutBuffer (%d): %s\n", bytesReturned, OutputBuffer); + + // + // Performing METHOD_NIETHER + // + + printf("\nCalling DeviceIoControl METHOD_NEITHER\n"); + + StringCbCopy(InputBuffer, sizeof(InputBuffer), + "This String is from User Application; using METHOD_NEITHER"); + memset(OutputBuffer, 0, sizeof(OutputBuffer)); + + bRc = DeviceIoControl ( hDevice, + (DWORD) IOCTL_SIOCTL_METHOD_NEITHER, + &InputBuffer, + (DWORD) strlen ( InputBuffer )+1, + &OutputBuffer, + sizeof( OutputBuffer), + &bytesReturned, + NULL + ); + + if ( !bRc ) + { + printf ( "Error in DeviceIoControl : %d\n", GetLastError()); + return; + + } + + printf(" OutBuffer (%d): %s\n", bytesReturned, OutputBuffer); + + // + // Performing METHOD_IN_DIRECT + // + + printf("\nCalling DeviceIoControl METHOD_IN_DIRECT\n"); + + StringCbCopy(InputBuffer, sizeof(InputBuffer), + "This String is from User Application; using METHOD_IN_DIRECT"); + StringCbCopy(OutputBuffer, sizeof(OutputBuffer), + "This String is from User Application in OutBuffer; using METHOD_IN_DIRECT"); + + bRc = DeviceIoControl ( hDevice, + (DWORD) IOCTL_SIOCTL_METHOD_IN_DIRECT, + &InputBuffer, + (DWORD) strlen ( InputBuffer )+1, + &OutputBuffer, + sizeof( OutputBuffer), + &bytesReturned, + NULL + ); + + if ( !bRc ) + { + printf ( "Error in DeviceIoControl : : %d", GetLastError()); + return; + } + + printf(" Number of bytes transfered from OutBuffer: %d\n", + bytesReturned); + + // + // Performing METHOD_OUT_DIRECT + // + + printf("\nCalling DeviceIoControl METHOD_OUT_DIRECT\n"); + StringCbCopy(InputBuffer, sizeof(InputBuffer), + "This String is from User Application; using METHOD_OUT_DIRECT"); + memset(OutputBuffer, 0, sizeof(OutputBuffer)); + bRc = DeviceIoControl ( hDevice, + (DWORD) IOCTL_SIOCTL_METHOD_OUT_DIRECT, + &InputBuffer, + (DWORD) strlen ( InputBuffer )+1, + &OutputBuffer, + sizeof( OutputBuffer), + &bytesReturned, + NULL + ); + + if ( !bRc ) + { + printf ( "Error in DeviceIoControl : : %d", GetLastError()); + return; + } + + printf(" OutBuffer (%d): %s\n", bytesReturned, OutputBuffer); + + CloseHandle ( hDevice ); + + // + // Unload the driver. Ignore any errors. + // + + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + + // + // close the handle to the device. + // + +} + + diff --git a/general/ioctl/wdm/ioctl.sln b/general/ioctl/wdm/ioctl.sln new file mode 100644 index 00000000..ba787f9d --- /dev/null +++ b/general/ioctl/wdm/ioctl.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{91C983A9-7E97-4964-A924-F019A135772F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{9D017694-F9BE-450B-BF96-67F46D45BE81}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ioctlapp", "exe\ioctlapp.vcxproj", "{76D71F31-1E96-453B-B624-603110936517}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sioctl", "sys\sioctl.vcxproj", "{EFC79CFC-9D17-4830-90C1-1825B6CFE1AD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {76D71F31-1E96-453B-B624-603110936517}.Debug|Win32.ActiveCfg = Debug|Win32 + {76D71F31-1E96-453B-B624-603110936517}.Debug|Win32.Build.0 = Debug|Win32 + {76D71F31-1E96-453B-B624-603110936517}.Release|Win32.ActiveCfg = Release|Win32 + {76D71F31-1E96-453B-B624-603110936517}.Release|Win32.Build.0 = Release|Win32 + {76D71F31-1E96-453B-B624-603110936517}.Debug|x64.ActiveCfg = Debug|x64 + {76D71F31-1E96-453B-B624-603110936517}.Debug|x64.Build.0 = Debug|x64 + {76D71F31-1E96-453B-B624-603110936517}.Release|x64.ActiveCfg = Release|x64 + {76D71F31-1E96-453B-B624-603110936517}.Release|x64.Build.0 = Release|x64 + {EFC79CFC-9D17-4830-90C1-1825B6CFE1AD}.Debug|Win32.ActiveCfg = Debug|Win32 + {EFC79CFC-9D17-4830-90C1-1825B6CFE1AD}.Debug|Win32.Build.0 = Debug|Win32 + {EFC79CFC-9D17-4830-90C1-1825B6CFE1AD}.Release|Win32.ActiveCfg = Release|Win32 + {EFC79CFC-9D17-4830-90C1-1825B6CFE1AD}.Release|Win32.Build.0 = Release|Win32 + {EFC79CFC-9D17-4830-90C1-1825B6CFE1AD}.Debug|x64.ActiveCfg = Debug|x64 + {EFC79CFC-9D17-4830-90C1-1825B6CFE1AD}.Debug|x64.Build.0 = Debug|x64 + {EFC79CFC-9D17-4830-90C1-1825B6CFE1AD}.Release|x64.ActiveCfg = Release|x64 + {EFC79CFC-9D17-4830-90C1-1825B6CFE1AD}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {76D71F31-1E96-453B-B624-603110936517} = {91C983A9-7E97-4964-A924-F019A135772F} + {EFC79CFC-9D17-4830-90C1-1825B6CFE1AD} = {9D017694-F9BE-450B-BF96-67F46D45BE81} + EndGlobalSection +EndGlobal diff --git a/general/ioctl/wdm/sys/sioctl.c b/general/ioctl/wdm/sys/sioctl.c new file mode 100644 index 00000000..7eae2971 --- /dev/null +++ b/general/ioctl/wdm/sys/sioctl.c @@ -0,0 +1,744 @@ +/*++ + +Copyright (c) 1990-98 Microsoft Corporation All Rights Reserved + +Module Name: + + sioctl.c + +Abstract: + + Purpose of this driver is to demonstrate how the four different types + of IOCTLs can be used, and how the I/O manager handles the user I/O + buffers in each case. This sample also helps to understand the usage of + some of the memory manager functions. + +Environment: + + Kernel mode only. + +--*/ + + +// +// Include files. +// + +#include // various NT definitions +#include + +#include "sioctl.h" + +#define NT_DEVICE_NAME L"\\Device\\SIOCTL" +#define DOS_DEVICE_NAME L"\\DosDevices\\IoctlTest" + +#if DBG +#define SIOCTL_KDPRINT(_x_) \ + DbgPrint("SIOCTL.SYS: ");\ + DbgPrint _x_; + +#else +#define SIOCTL_KDPRINT(_x_) +#endif + +// +// Device driver routine declarations. +// + +DRIVER_INITIALIZE DriverEntry; + +_Dispatch_type_(IRP_MJ_CREATE) +_Dispatch_type_(IRP_MJ_CLOSE) +DRIVER_DISPATCH SioctlCreateClose; + +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) +DRIVER_DISPATCH SioctlDeviceControl; + +DRIVER_UNLOAD SioctlUnloadDriver; + +VOID +PrintIrpInfo( + PIRP Irp + ); +VOID +PrintChars( + _In_reads_(CountChars) PCHAR BufferAddress, + _In_ size_t CountChars + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, DriverEntry ) +#pragma alloc_text( PAGE, SioctlCreateClose) +#pragma alloc_text( PAGE, SioctlDeviceControl) +#pragma alloc_text( PAGE, SioctlUnloadDriver) +#pragma alloc_text( PAGE, PrintIrpInfo) +#pragma alloc_text( PAGE, PrintChars) +#endif // ALLOC_PRAGMA + + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + This routine is called by the Operating System to initialize the driver. + + It creates the device object, fills in the dispatch entry points and + completes the initialization. + +Arguments: + DriverObject - a pointer to the object that represents this device + driver. + + RegistryPath - a pointer to our Services key in the registry. + +Return Value: + STATUS_SUCCESS if initialized; an error otherwise. + +--*/ + +{ + NTSTATUS ntStatus; + UNICODE_STRING ntUnicodeString; // NT Device Name "\Device\SIOCTL" + UNICODE_STRING ntWin32NameString; // Win32 Name "\DosDevices\IoctlTest" + PDEVICE_OBJECT deviceObject = NULL; // ptr to device object + + UNREFERENCED_PARAMETER(RegistryPath); + + RtlInitUnicodeString( &ntUnicodeString, NT_DEVICE_NAME ); + + ntStatus = IoCreateDevice( + DriverObject, // Our Driver Object + 0, // We don't use a device extension + &ntUnicodeString, // Device name "\Device\SIOCTL" + FILE_DEVICE_UNKNOWN, // Device type + FILE_DEVICE_SECURE_OPEN, // Device characteristics + FALSE, // Not an exclusive device + &deviceObject ); // Returned ptr to Device Object + + if ( !NT_SUCCESS( ntStatus ) ) + { + SIOCTL_KDPRINT(("Couldn't create the device object\n")); + return ntStatus; + } + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = SioctlCreateClose; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = SioctlCreateClose; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SioctlDeviceControl; + DriverObject->DriverUnload = SioctlUnloadDriver; + + // + // Initialize a Unicode String containing the Win32 name + // for our device. + // + + RtlInitUnicodeString( &ntWin32NameString, DOS_DEVICE_NAME ); + + // + // Create a symbolic link between our device name and the Win32 name + // + + ntStatus = IoCreateSymbolicLink( + &ntWin32NameString, &ntUnicodeString ); + + if ( !NT_SUCCESS( ntStatus ) ) + { + // + // Delete everything that this routine has allocated. + // + SIOCTL_KDPRINT(("Couldn't create symbolic link\n")); + IoDeleteDevice( deviceObject ); + } + + + return ntStatus; +} + + +NTSTATUS +SioctlCreateClose( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +/*++ + +Routine Description: + + This routine is called by the I/O system when the SIOCTL is opened or + closed. + + No action is performed other than completing the request successfully. + +Arguments: + + DeviceObject - a pointer to the object that represents the device + that I/O is to be done on. + + Irp - a pointer to the I/O Request Packet for this request. + +Return Value: + + NT status code + +--*/ + +{ + UNREFERENCED_PARAMETER(DeviceObject); + + PAGED_CODE(); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + return STATUS_SUCCESS; +} + +VOID +SioctlUnloadDriver( + _In_ PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine is called by the I/O system to unload the driver. + + Any resources previously allocated must be freed. + +Arguments: + + DriverObject - a pointer to the object that represents our driver. + +Return Value: + + None +--*/ + +{ + PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; + UNICODE_STRING uniWin32NameString; + + PAGED_CODE(); + + // + // Create counted string version of our Win32 device name. + // + + RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME ); + + + // + // Delete the link from our device name to a name in the Win32 namespace. + // + + IoDeleteSymbolicLink( &uniWin32NameString ); + + if ( deviceObject != NULL ) + { + IoDeleteDevice( deviceObject ); + } + + + +} + +NTSTATUS +SioctlDeviceControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to perform a device I/O + control function. + +Arguments: + + DeviceObject - a pointer to the object that represents the device + that I/O is to be done on. + + Irp - a pointer to the I/O Request Packet for this request. + +Return Value: + + NT status code + +--*/ + +{ + PIO_STACK_LOCATION irpSp;// Pointer to current stack location + NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success + ULONG inBufLength; // Input buffer length + ULONG outBufLength; // Output buffer length + PCHAR inBuf, outBuf; // pointer to Input and output buffer + PCHAR data = "This String is from Device Driver !!!"; + size_t datalen = strlen(data)+1;//Length of data including null + PMDL mdl = NULL; + PCHAR buffer = NULL; + + UNREFERENCED_PARAMETER(DeviceObject); + + PAGED_CODE(); + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength; + outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; + + if (!inBufLength || !outBufLength) + { + ntStatus = STATUS_INVALID_PARAMETER; + goto End; + } + + // + // Determine which I/O control code was specified. + // + + switch ( irpSp->Parameters.DeviceIoControl.IoControlCode ) + { + case IOCTL_SIOCTL_METHOD_BUFFERED: + + // + // In this method the I/O manager allocates a buffer large enough to + // to accommodate larger of the user input buffer and output buffer, + // assigns the address to Irp->AssociatedIrp.SystemBuffer, and + // copies the content of the user input buffer into this SystemBuffer + // + + SIOCTL_KDPRINT(("Called IOCTL_SIOCTL_METHOD_BUFFERED\n")); + PrintIrpInfo(Irp); + + // + // Input buffer and output buffer is same in this case, read the + // content of the buffer before writing to it + // + + inBuf = Irp->AssociatedIrp.SystemBuffer; + outBuf = Irp->AssociatedIrp.SystemBuffer; + + // + // Read the data from the buffer + // + + SIOCTL_KDPRINT(("\tData from User :")); + // + // We are using the following function to print characters instead + // DebugPrint with %s format because we string we get may or + // may not be null terminated. + // + PrintChars(inBuf, inBufLength); + + // + // Write to the buffer over-writes the input buffer content + // + + RtlCopyBytes(outBuf, data, outBufLength); + + SIOCTL_KDPRINT(("\tData to User : ")); + PrintChars(outBuf, datalen ); + + // + // Assign the length of the data copied to IoStatus.Information + // of the Irp and complete the Irp. + // + + Irp->IoStatus.Information = (outBufLengthParameters.DeviceIoControl.Type3InputBuffer; + outBuf = Irp->UserBuffer; + + // + // Access the buffers directly if only if you are running in the + // context of the calling process. Only top level drivers are + // guaranteed to have the context of process that made the request. + // + + try { + // + // Before accessing user buffer, you must probe for read/write + // to make sure the buffer is indeed an userbuffer with proper access + // rights and length. ProbeForRead/Write will raise an exception if it's otherwise. + // + ProbeForRead( inBuf, inBufLength, sizeof( UCHAR ) ); + + // + // Since the buffer access rights can be changed or buffer can be freed + // anytime by another thread of the same process, you must always access + // it within an exception handler. + // + + SIOCTL_KDPRINT(("\tData from User :")); + PrintChars(inBuf, inBufLength); + + } + except(EXCEPTION_EXECUTE_HANDLER) + { + + ntStatus = GetExceptionCode(); + SIOCTL_KDPRINT(( + "Exception while accessing inBuf 0X%08X in METHOD_NEITHER\n", + ntStatus)); + break; + } + + + // + // If you are accessing these buffers in an arbitrary thread context, + // say in your DPC or ISR, if you are using it for DMA, or passing these buffers to the + // next level driver, you should map them in the system process address space. + // First allocate an MDL large enough to describe the buffer + // and initilize it. Please note that on a x86 system, the maximum size of a buffer + // that an MDL can describe is 65508 KB. + // + + mdl = IoAllocateMdl(inBuf, inBufLength, FALSE, TRUE, NULL); + if (!mdl) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + try + { + + // + // Probe and lock the pages of this buffer in physical memory. + // You can specify IoReadAccess, IoWriteAccess or IoModifyAccess + // Always perform this operation in a try except block. + // MmProbeAndLockPages will raise an exception if it fails. + // + MmProbeAndLockPages(mdl, UserMode, IoReadAccess); + } + except(EXCEPTION_EXECUTE_HANDLER) + { + + ntStatus = GetExceptionCode(); + SIOCTL_KDPRINT(( + "Exception while locking inBuf 0X%08X in METHOD_NEITHER\n", + ntStatus)); + IoFreeMdl(mdl); + break; + } + + // + // Map the physical pages described by the MDL into system space. + // Note: double mapping the buffer this way causes lot of + // system overhead for large size buffers. + // + + buffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority ); + + if (!buffer) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + MmUnlockPages(mdl); + IoFreeMdl(mdl); + break; + } + + // + // Now you can safely read the data from the buffer. + // + SIOCTL_KDPRINT(("\tData from User (SystemAddress) : ")); + PrintChars(buffer, inBufLength); + + // + // Once the read is over unmap and unlock the pages. + // + + MmUnlockPages(mdl); + IoFreeMdl(mdl); + + // + // The same steps can be followed to access the output buffer. + // + + mdl = IoAllocateMdl(outBuf, outBufLength, FALSE, TRUE, NULL); + if (!mdl) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + + try { + // + // Probe and lock the pages of this buffer in physical memory. + // You can specify IoReadAccess, IoWriteAccess or IoModifyAccess. + // + + MmProbeAndLockPages(mdl, UserMode, IoWriteAccess); + } + except(EXCEPTION_EXECUTE_HANDLER) + { + + ntStatus = GetExceptionCode(); + SIOCTL_KDPRINT(( + "Exception while locking outBuf 0X%08X in METHOD_NEITHER\n", + ntStatus)); + IoFreeMdl(mdl); + break; + } + + + buffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority ); + + if (!buffer) { + MmUnlockPages(mdl); + IoFreeMdl(mdl); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + // + // Write to the buffer + // + + RtlCopyBytes(buffer, data, outBufLength); + + SIOCTL_KDPRINT(("\tData to User : %s\n", buffer)); + PrintChars(buffer, datalen); + + MmUnlockPages(mdl); + + // + // Free the allocated MDL + // + + IoFreeMdl(mdl); + + // + // Assign the length of the data copied to IoStatus.Information + // of the Irp and complete the Irp. + // + + Irp->IoStatus.Information = (outBufLengthAssociatedIrp.SystemBuffer and copies the content of user input buffer + // into the SystemBuffer. For the user output buffer, the I/O manager + // probes to see whether the virtual address is readable in the callers + // access mode, locks the pages in memory and passes the pointer to + // MDL describing the buffer in Irp->MdlAddress. + // + + SIOCTL_KDPRINT(("Called IOCTL_SIOCTL_METHOD_IN_DIRECT\n")); + + PrintIrpInfo(Irp); + + inBuf = Irp->AssociatedIrp.SystemBuffer; + + SIOCTL_KDPRINT(("\tData from User in InputBuffer: ")); + PrintChars(inBuf, inBufLength); + + // + // To access the output buffer, just get the system address + // for the buffer. For this method, this buffer is intended for transfering data + // from the application to the driver. + // + + buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); + + if (!buffer) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + SIOCTL_KDPRINT(("\tData from User in OutputBuffer: ")); + PrintChars(buffer, outBufLength); + + // + // Return total bytes read from the output buffer. + // Note OutBufLength = MmGetMdlByteCount(Irp->MdlAddress) + // + + Irp->IoStatus.Information = MmGetMdlByteCount(Irp->MdlAddress); + + // + // NOTE: Changes made to the SystemBuffer are not copied + // to the user input buffer by the I/O manager + // + + break; + + case IOCTL_SIOCTL_METHOD_OUT_DIRECT: + + // + // In this type of transfer, the I/O manager allocates a system buffer + // large enough to accommodate the User input buffer, sets the buffer address + // in Irp->AssociatedIrp.SystemBuffer and copies the content of user input buffer + // into the SystemBuffer. For the output buffer, the I/O manager + // probes to see whether the virtual address is writable in the callers + // access mode, locks the pages in memory and passes the pointer to MDL + // describing the buffer in Irp->MdlAddress. + // + + + SIOCTL_KDPRINT(("Called IOCTL_SIOCTL_METHOD_OUT_DIRECT\n")); + + PrintIrpInfo(Irp); + + + inBuf = Irp->AssociatedIrp.SystemBuffer; + + SIOCTL_KDPRINT(("\tData from User : ")); + PrintChars(inBuf, inBufLength); + + // + // To access the output buffer, just get the system address + // for the buffer. For this method, this buffer is intended for transfering data + // from the driver to the application. + // + + buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); + + if (!buffer) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // Write data to be sent to the user in this buffer + // + + RtlCopyBytes(buffer, data, outBufLength); + + SIOCTL_KDPRINT(("\tData to User : ")); + PrintChars(buffer, datalen); + + Irp->IoStatus.Information = (outBufLengthParameters.DeviceIoControl.IoControlCode)); + break; + } + +End: + // + // Finish the I/O operation by simply completing the packet and returning + // the same status as in the packet itself. + // + + Irp->IoStatus.Status = ntStatus; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + return ntStatus; +} + +VOID +PrintIrpInfo( + PIRP Irp) +{ + PIO_STACK_LOCATION irpSp; + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + PAGED_CODE(); + + SIOCTL_KDPRINT(("\tIrp->AssociatedIrp.SystemBuffer = 0x%p\n", + Irp->AssociatedIrp.SystemBuffer)); + SIOCTL_KDPRINT(("\tIrp->UserBuffer = 0x%p\n", Irp->UserBuffer)); + SIOCTL_KDPRINT(("\tirpSp->Parameters.DeviceIoControl.Type3InputBuffer = 0x%p\n", + irpSp->Parameters.DeviceIoControl.Type3InputBuffer)); + SIOCTL_KDPRINT(("\tirpSp->Parameters.DeviceIoControl.InputBufferLength = %d\n", + irpSp->Parameters.DeviceIoControl.InputBufferLength)); + SIOCTL_KDPRINT(("\tirpSp->Parameters.DeviceIoControl.OutputBufferLength = %d\n", + irpSp->Parameters.DeviceIoControl.OutputBufferLength )); + return; +} + +VOID +PrintChars( + _In_reads_(CountChars) PCHAR BufferAddress, + _In_ size_t CountChars + ) +{ + PAGED_CODE(); + + if (CountChars) { + + while (CountChars--) { + + if (*BufferAddress > 31 + && *BufferAddress != 127) { + + KdPrint (( "%c", *BufferAddress) ); + + } else { + + KdPrint(( ".") ); + + } + BufferAddress++; + } + KdPrint (("\n")); + } + return; +} + + diff --git a/general/ioctl/wdm/sys/sioctl.h b/general/ioctl/wdm/sys/sioctl.h new file mode 100644 index 00000000..33c0ff4b --- /dev/null +++ b/general/ioctl/wdm/sys/sioctl.h @@ -0,0 +1,47 @@ +/*++ + +Copyright (c) 1997 Microsoft Corporation + +Module Name: + + SIOCTL.H + +Abstract: + + + Defines the IOCTL codes that will be used by this driver. The IOCTL code + contains a command identifier, plus other information about the device, + the type of access with which the file must have been opened, + and the type of buffering. + +Environment: + + Kernel mode only. + +--*/ + +// +// Device type -- in the "User Defined" range." +// +#define SIOCTL_TYPE 40000 +// +// The IOCTL function codes from 0x800 to 0xFFF are for customer use. +// +#define IOCTL_SIOCTL_METHOD_IN_DIRECT \ + CTL_CODE( SIOCTL_TYPE, 0x900, METHOD_IN_DIRECT, FILE_ANY_ACCESS ) + +#define IOCTL_SIOCTL_METHOD_OUT_DIRECT \ + CTL_CODE( SIOCTL_TYPE, 0x901, METHOD_OUT_DIRECT , FILE_ANY_ACCESS ) + +#define IOCTL_SIOCTL_METHOD_BUFFERED \ + CTL_CODE( SIOCTL_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS ) + +#define IOCTL_SIOCTL_METHOD_NEITHER \ + CTL_CODE( SIOCTL_TYPE, 0x903, METHOD_NEITHER , FILE_ANY_ACCESS ) + + +#define DRIVER_FUNC_INSTALL 0x01 +#define DRIVER_FUNC_REMOVE 0x02 + +#define DRIVER_NAME "SIoctl" + diff --git a/general/ioctl/wdm/sys/sioctl.rc b/general/ioctl/wdm/sys/sioctl.rc new file mode 100644 index 00000000..a93374ac --- /dev/null +++ b/general/ioctl/wdm/sys/sioctl.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Sample IOCTL Driver" +#define VER_INTERNALNAME_STR "SIOCTL.sys" + +#include "common.ver" diff --git a/general/ioctl/wdm/sys/sioctl.vcxproj b/general/ioctl/wdm/sys/sioctl.vcxproj new file mode 100644 index 00000000..7340c780 --- /dev/null +++ b/general/ioctl/wdm/sys/sioctl.vcxproj @@ -0,0 +1,140 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {EFC79CFC-9D17-4830-90C1-1825B6CFE1AD} + $(MSBuildProjectName) + Debug + Win32 + {733A9BE6-EB74-47AD-9701-2DB93FD4B2AC} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + sioctl + + + sioctl + + + sioctl + + + sioctl + + + + true + Level4 + + + + + + + true + Level4 + + + + + + + true + Level4 + + + + + + + true + Level4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/ioctl/wdm/sys/sioctl.vcxproj.Filters b/general/ioctl/wdm/sys/sioctl.vcxproj.Filters new file mode 100644 index 00000000..3b5a4633 --- /dev/null +++ b/general/ioctl/wdm/sys/sioctl.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {68FEC55D-22E4-4CC8-86A7-C923C2AB8F07} + + + h;hpp;hxx;hm;inl;inc;xsd + {9D595192-13EA-4968-AFE4-63BECD7EDD64} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {644100AF-059B-48EE-B8B9-280BA8724FF6} + + + inf;inv;inx;mof;mc; + {41B8EECC-BA42-433E-9150-6D2D385CC021} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/obcallback/ReadMe.md b/general/obcallback/ReadMe.md new file mode 100644 index 00000000..def34eac --- /dev/null +++ b/general/obcallback/ReadMe.md @@ -0,0 +1,42 @@ +ObCallback Callback Registration Driver +======================================= + +The ObCallback sample driver demonstrates the use of registered callbacks for process protection. The driver registers control callbacks which are called at process creation. + + +Design and Operation +-------------------- + +The sample exercises both the [**PsSetCreateProcessNotifyRoutineEx**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff559951) and the [**ObRegisterCallbacks**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff558692) routines. The first example uses the **ObRegisterCallbacks** routine and a callback to restrict requested access rights during a open process action. The second example uses the **PsSetCreateProcessNotifyRoutineEx** routine to reject a process creation by examining the command line. + +The following is a command line usage scenario to exercise access restriction: + +``` {.syntax xml:space="preserve"} +C:\> obcallbacktestctrl.exe -? (for command line help) +C:\> obcallbacktestctrl.exe -install (installs the kernel driver) +C:\> obcallbacktestctrl.exe -name notepad (specifies that the string “notepad†will be watched as a protected executable) + (now you can start up “notepad.exeâ€) +C:\> notepad + +C:\> tlist (locate the process ID of notepad.exe) + +C:\> kill –f 2329 (attempt to kill off the notepad.exe with a PID of 2329) +process notepad.exe (2329) – ‘Untitled – Notepad’ could not be killed + +C:\> obcallbacktestctrl.exe -deprotect (remove the protections on the notepad process) + +C:\> kill –f 2329 (attempt to kill off the process – which will succeed) +C:\> obcallbacktestctrl.exe -uninstall (uninstall the kernel driver) + + +``` + +The following is another sample test you can run to prevent a process from being created: + +``` {.syntax xml:space="preserve"} +C:\> obcallbacktestctrl.exe -install (installs the kernel driver) +C:\> obcallbacktestctrl.exe -reject notepad (specifies that the string “notepad†will be watched and prevented from starting as a process) + +C:\> notepad (now you can start up “notepad.exeâ€) +Access is denied. +``` diff --git a/general/obcallback/control/ObCallbackTestCtrl.vcxproj b/general/obcallback/control/ObCallbackTestCtrl.vcxproj new file mode 100644 index 00000000..d9ea7f08 --- /dev/null +++ b/general/obcallback/control/ObCallbackTestCtrl.vcxproj @@ -0,0 +1,236 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {8B053BEE-EA21-4D12-984B-6C93FE6D4992} + $(MSBuildProjectName) + Debug + Win32 + {BED8786D-3B67-42D0-AC7B-D7D2F45E551E} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + ObCallbackTestCtrl + 0x0A00 + 0x0A000000 + + + ObCallbackTestCtrl + 0x0A00 + 0x0A000000 + + + ObCallbackTestCtrl + 0x0A00 + 0x0A000000 + + + ObCallbackTestCtrl + 0x0A00 + 0x0A000000 + + + + Disabled + true + Level4 + + + + + Disabled + true + Level4 + + + + + Disabled + true + Level4 + + + + + Disabled + true + Level4 + + + + + %(AdditionalOptions) /LARGEADDRESSAWARE + %(AdditionalDependencies);ntdll.lib;kernel32.lib;advapi32.lib + + + + + %(AdditionalOptions) /LARGEADDRESSAWARE + %(AdditionalDependencies);ntdll.lib;kernel32.lib;advapi32.lib + + + + + %(AdditionalOptions) /LARGEADDRESSAWARE + %(AdditionalDependencies);ntdll.lib;kernel32.lib;advapi32.lib + + + + + %(AdditionalOptions) /LARGEADDRESSAWARE + %(AdditionalDependencies);ntdll.lib;kernel32.lib;advapi32.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Create + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/obcallback/control/ObCallbackTestCtrl.vcxproj.Filters b/general/obcallback/control/ObCallbackTestCtrl.vcxproj.Filters new file mode 100644 index 00000000..b9258c52 --- /dev/null +++ b/general/obcallback/control/ObCallbackTestCtrl.vcxproj.Filters @@ -0,0 +1,28 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {5FCF0F0F-49FE-446D-9A27-490CBE156E8F} + + + h;hpp;hxx;hm;inl;inc;xsd + {F9488019-A49A-430B-818A-BB8ADC3106D1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {DBBA22E4-2B2D-4D16-8C9A-26B358D8752E} + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/obcallback/control/common.h b/general/obcallback/control/common.h new file mode 100644 index 00000000..1ba366a2 --- /dev/null +++ b/general/obcallback/control/common.h @@ -0,0 +1,86 @@ + +// Notice: +// +// Use this sample code at your own risk; there is no support from Microsoft for the sample code. +// In addition, this sample code is licensed to you under the terms of the Microsoft Public License +// (http://www.microsoft.com/opensource/licenses.mspx) + +#pragma once + +#pragma warning (disable: 4201) // nonstandard extension used : nameless struct/union + +#include "..\driver\shared.h" + +// +// Logging support macros. +// +// LOG_INFO +// LOG_INFO_FAILURE +// LOG_PASSED +// LOG_ERROR +// + + +#ifdef DEBUG +#define LOG_INFO(fmt, ...) \ + _tprintf(_T("%hs: ") fmt, __FUNCTION__, __VA_ARGS__);_tprintf(_T("\n")); +#define LOG_INFO_FAILURE(fmt, ...) \ + _tprintf(_T("ReportFailure %hs: ") fmt, __FUNCTION__, __VA_ARGS__);_tprintf(_T("\n")); + +#define LOG_PASSED(fmt, ...) \ + _tprintf(_T("\n!!!PASSED: %hs (%hs:%u): ") fmt, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__);_tprintf(_T("\n")); +#define LOG_ERROR(fmt, ...) \ + _tprintf(_T("\n!!!FAILED: %hs (%hs:%u): ") fmt, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__); _tprintf(_T("\n")); + +#else + +#define LOG_INFO(FormatString, ...) +#define LOG_INFO_FAILURE(FormatString, ...) + +#define LOG_PASSED(FormatString, ...) +#define LOG_ERROR(FormatString, ...) + +#endif + + +extern HANDLE TcDeviceHandle; + +BOOL TcInitialize(); +BOOL TcUnInitialize(); +BOOL TcCleanupSCM(); + +BOOL TcInstallDriver(); + +BOOL TcUninstallDriver(); + +BOOL TcRemoveProtection (); + +BOOL TcProcessName ( + _In_ int argc, + _In_reads_(argc) LPCWSTR argv[], + _In_ ULONG ulOperation +); + +BOOL TcUnprotectCallback (); + +BOOL TcProcessNameCallback ( + _In_reads_(NAME_SIZE+1) PCWSTR pnametoprotect, + _In_ ULONG ulOperation +); + +// +// Utility functions +// + +BOOL TcInitializeGlobals(); +BOOL TcLoadDriver(); +BOOL TcUnloadDriver(); + +BOOL TcCreateService(); +BOOL TcDeleteService(); +BOOL TcStartService(); +BOOL TcStopService(); + +BOOL TcOpenDevice(); +BOOL TcCloseDevice(); + diff --git a/general/obcallback/control/main.cpp b/general/obcallback/control/main.cpp new file mode 100644 index 00000000..e5a36224 --- /dev/null +++ b/general/obcallback/control/main.cpp @@ -0,0 +1,320 @@ +/*++ + +Module Name: + + main.cpp + +Abstract: + + Main module for for ps/Ob sample + +Notice: + + Use this sample code at your own risk; there is no support from Microsoft for the sample code. + In addition, this sample code is licensed to you under the terms of the Microsoft Public License + (http://www.microsoft.com/opensource/licenses.mspx) + + +--*/ + +#include "pch.h" +#include "common.h" + +// +// PrintUsage +// + +void TcPrintUsage() +{ + puts ("Usage:"); + puts (""); + puts(" ObCallbackTestCtrl.exe -install -name NameofExe -reject NameofExe -uninstall -deprotect [-?]"); + puts(" -install install driver"); + puts(" -uninstall uninstall driver"); + puts(" -name NameofExe protect/filter access to NameofExe"); + puts(" -reject NameofExe prevents execution of NameofExe"); + puts(" -deprotect unprotect/unfilter"); +} + +// +// wmain() +// + +int _cdecl +wmain ( + _In_ int argc, + _In_reads_(argc) LPCWSTR argv[] +) +{ + int ExitCode = ERROR_SUCCESS; + + if (argc > 1) + { + const wchar_t * arg = argv[1]; + + // initialize globals and logging + if (!TcInitialize()) { + puts("Initialization failed - program exiting"); + ExitCode = ERROR_FUNCTION_FAILED; + goto Exit; + } + + if (0 == wcscmp (arg, L"-install")) { + TcInstallDriver(); + } else + if (0 == wcscmp (arg, L"-uninstall")) { + TcUninstallDriver(); + } else + if ((0 == wcscmp (arg, L"-?")) || (0 == wcscmp (arg, L"-h")) || (0 == wcscmp (arg, L"-help"))) { + TcPrintUsage(); + } else + if (0 == wcscmp (arg, L"-deprotect")) { + TcRemoveProtection(); + } else + if (0 == wcscmp (arg, L"-name")) { + TcProcessName (argc, argv, TDProtectName_Protect); + } else + if (0 == wcscmp (arg, L"-reject")) { + TcProcessName (argc, argv, TDProtectName_Reject); + } else { + puts ("Unknown command!"); + TcPrintUsage(); + } + + } + else + { + TcPrintUsage(); + } + +Exit: + + if (!TcUnInitialize()) { + puts("UnInitialization failed"); + ExitCode = ERROR_FUNCTION_FAILED; + } + + return ExitCode; +} + + + +// +// TcRemoveProtection +// + +BOOL TcRemoveProtection () +{ + BOOL ReturnValue = FALSE; + + LOG_INFO(_T("TcRemoveProtection: Entering")); + + + // + // Open a handle to the device. + // + + ReturnValue = TcOpenDevice(); + if (ReturnValue != TRUE) + { + LOG_INFO_FAILURE (_T("TcOpenDevice failed")); + goto Exit; + } + + + // + // Send the command to the driver + // + ReturnValue = TcUnprotectCallback(); + if (ReturnValue != TRUE) + { + LOG_INFO_FAILURE (_T("TcUnprotectCallback failed")); + goto Exit; + } + +Exit: + + // + // Close our handle to the device. + // + + ReturnValue = TcCloseDevice(); + if (ReturnValue != TRUE) + { + LOG_INFO_FAILURE (_T("TcCloseDevice failed")); + } + + + LOG_INFO(_T("TcRemoveProtection: Exiting")); + + return ReturnValue; +} + + +// +// TcProcessName +// + +BOOL TcProcessName( + _In_ int argc, + _In_reads_(argc) LPCWSTR argv[], + _In_ ULONG ulOperation +) +{ + BOOL ReturnValue = FALSE; + + PCWSTR pwProcessName = NULL; + + LOG_INFO(L"TcProcessName: Entering"); + + + // + // Parse command line. + // + // argv[1] is "-name" so starting from arg #2 that should be the process name to protect + // + + if (argc < 3) { + LOG_INFO_FAILURE (L"TcProcessName: Too few parameters"); + LOG_INFO_FAILURE (L"TcProcessName: Usage -name nameofExe -reject nameofExe"); + ReturnValue = FALSE; + goto Exit; + } + + pwProcessName = argv[2]; + + if (!pwProcessName) { + LOG_INFO_FAILURE (L"TcProcessName: NULL process name to process"); + ReturnValue = FALSE; + goto Exit; + } + + + LOG_INFO(L"Ready to copy process name"); + LOG_INFO(L"Name to pass to driver %ls", pwProcessName); + + + // + // Open a handle to the device. + // + + ReturnValue = TcOpenDevice(); + if (ReturnValue != TRUE) + { + LOG_INFO_FAILURE (L"TcProcessName: TcOpenDevice failed"); + goto Exit; + } + + + // + // Send process name to protect and the command to the driver + // + ReturnValue = TcProcessNameCallback(pwProcessName, ulOperation); + if (ReturnValue != TRUE) + { + LOG_INFO_FAILURE (L"TcProcessName: TcProcessNameCallback failed"); + goto Exit; + } + +Exit: + + // + // Close our handle to the device. + // + + ReturnValue = TcCloseDevice(); + if (ReturnValue != TRUE) + { + LOG_INFO_FAILURE (L"TcProtectProcess: TcCloseDevice failed"); + } + + + LOG_INFO(L"TcProtectProcess: Exiting"); + + return ReturnValue; +} + + + +// +// TcInstallDriver - installs the kernel driver +// + +BOOL TcInstallDriver () +{ + BOOL bRC = TRUE; + + LOG_INFO(L"TcInstallDriver: Entering"); + BOOL Result = TcLoadDriver(); + + if (Result != TRUE) + { + LOG_ERROR (L"TcLoadDriver failed, exiting"); + bRC = FALSE; + goto Exit; + } + +Exit: + + LOG_INFO(L"TcInstallDriver: Exiting"); + return bRC; +} + + +// +// TcUninstallDriver - uninstalls the kernel driver +// + +BOOL TcUninstallDriver () +{ + BOOL bRC = TRUE; + + LOG_INFO(L"TcUninstallDriver: Entering"); + BOOL Result = TcUnloadDriver(); + + if (Result != TRUE) + { + LOG_ERROR (L"TcUnloadDriver failed, exiting"); + bRC = FALSE; + goto Exit; + } + +Exit: + + LOG_INFO(L"TcUninstallDriver: Exiting"); + return bRC; +} + + +// +// TcInitialize +// + +BOOL bLoggingInitialized = FALSE; + +BOOL TcInitialize () +{ + + BOOL Result = TcInitializeGlobals(); + if (Result != TRUE) + { + LOG_ERROR (L"TcInitializeGlobals failed, exiting"); + return FALSE; + } + + LOG_INFO(L"TcInitialize: Entering"); + return TRUE; + +} + +// +// TcUnInitialize +// + +BOOL TcUnInitialize() +{ + if (TcCleanupSCM() == FALSE){ + LOG_ERROR (L"TcUnInitialize failed cleanup of SCM"); + } + return TRUE; +} diff --git a/general/obcallback/control/pch.h b/general/obcallback/control/pch.h new file mode 100644 index 00000000..d165c04f --- /dev/null +++ b/general/obcallback/control/pch.h @@ -0,0 +1,18 @@ + +// Notice: +// +// Use this sample code at your own risk; there is no support from Microsoft for the sample code. +// In addition, this sample code is licensed to you under the terms of the Microsoft Public License +// (http://www.microsoft.com/opensource/licenses.mspx) + +#pragma once + +//#include +//#include +//#include +#include +#include +#include +#include +#include + diff --git a/general/obcallback/control/pchsrc.cpp b/general/obcallback/control/pchsrc.cpp new file mode 100644 index 00000000..17305716 --- /dev/null +++ b/general/obcallback/control/pchsrc.cpp @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/general/obcallback/control/readme.txt b/general/obcallback/control/readme.txt new file mode 100644 index 00000000..694696ad --- /dev/null +++ b/general/obcallback/control/readme.txt @@ -0,0 +1,57 @@ +The sample code exercises both PsSetCreateProcessNotifyRoutineEx() and ObRegisterCallbacks(). +These routines were introduced in Vista SP1 and are present in Windows7. They are available in both 32bit OS and 64bit OS. +The first example uses ObRegisterCallbacks() and a callback to restrict requested access rights during a open process action. +The second example uses PsSetCreateProcessNotifyRoutineEx() to reject a process creation by examining the command line. + +The code once compiled produces two files: ObCallbackTest.sys and ObCallbackTestCtrl.exe + +It is important to change the names of the binaries in the sample code to be unique for your own use. +#define TD_DRIVER_NAME L"ObCallbackTest" +#define TD_DRIVER_NAME_WITH_EXT L"ObCallbackTest.sys" + +#define TD_NT_DEVICE_NAME L"\\Device\\ObCallbackTest" +#define TD_DOS_DEVICES_LINK_NAME L"\\DosDevices\\ObCallbackTest" +#define TD_WIN32_DEVICE_NAME L"\\\\.\\ObCallbackTest" + + + + +For running the code you can use (run as administrator): + + C:\> obcallbacktest.exe -? (for command line help) + C:\> obcallbacktest.exe -install (installs the kernel driver) + C:\> obcallbacktest.exe -name notepad (specifies that the string “notepad” will be watched as a protected executable) + + (now you can start up “notepad.exe”) + C:\> notepad + + (locate the process ID of notepad.exe) + C:\> tlist + + (attempt to kill off the notepad.exe with a PID of 2329) + C:\> kill –f 2329 + process notepad.exe (2329) – ‘Untitled – Notepad’ could not be killed + + (remove the protections on the notepad process) + C:\> obcallbacktest.exe -deprotect + + (attempt to kill off the process – which will succeed) + C:\> kill –f 2329 + + (uninstall the kernel driver) + C:\> obcallbacktest.exe -uninstall + +Another sample test you can run is to prevent a process from being created + + C:\> obcallbacktest.exe -install (installs the kernel driver) + C:\> obcallbacktest.exe -reject notepad (specifies that the string “notepad” will be watched and prevented from starting as a process) + + (now you can start up “notepad.exe”) + C:\> notepad + Access is denied. + + +Use this sample code at your own risk; there is no support from Microsoft for the sample code. In addition, this sample code is licensed to you under the terms of the Microsoft Public License (http://www.microsoft.com/opensource/licenses.mspx). + +May 2009 + diff --git a/general/obcallback/control/utils.cpp b/general/obcallback/control/utils.cpp new file mode 100644 index 00000000..3c1cb617 --- /dev/null +++ b/general/obcallback/control/utils.cpp @@ -0,0 +1,727 @@ +// +// Module: utils.cpp +// +// Helper functions for Ob sample code tests. +// +// Notice: +// +// Use this sample code at your own risk; there is no support from Microsoft for the sample code. +// In addition, this sample code is licensed to you under the terms of the Microsoft Public License +// (http://www.microsoft.com/opensource/licenses.mspx) +// +// + +#include "pch.h" +#include "common.h" + +// +// Globals +// + +SC_HANDLE TcScmHandle = NULL; +HANDLE TcDeviceHandle = INVALID_HANDLE_VALUE; + +WCHAR TcDriverPath[MAX_PATH]; + + +// +// TcUnprotectCallback +// +// Sends unprotect callback ioctl to the driver. +// + +BOOL TcUnprotectCallback () +{ + TD_UNPROTECT_CALLBACK_INPUT UnprotectCallbackInput = {0}; + + DWORD BytesReturned = 0; + + LOG_INFO (L"TcUnprotectCallback: entering"); + + BOOL Result = DeviceIoControl ( + TcDeviceHandle, + TD_IOCTL_UNPROTECT_CALLBACK, + &UnprotectCallbackInput, + sizeof(UnprotectCallbackInput), + NULL, + 0, + &BytesReturned, + NULL + ); + + if (Result == TRUE) + { + LOG_INFO (L"TcUnprotectCallback: succeeded"); + } + else + { + LOG_INFO_FAILURE (L"TcUnprotectCallback: DeviceIoControl failed, last error 0x%x", GetLastError()); + } + + + LOG_INFO (L"TcUnprotectCallback: exiting"); + return Result; +} + + +// +// TcUnprotectCallback +// +// Sends unprotect callback ioctl to the driver. +// + +BOOL TcProcessNameCallback ( + _In_reads_(NAME_SIZE+1) PCWSTR pnametoprotect, + _In_ ULONG ulOperation +) +{ + TD_PROTECTNAME_INPUT ProtectNameCallbackInput = {0}; + BOOL Result = FALSE; + DWORD BytesReturned = 0; + + LOG_INFO (L"TcProtectNameCallback: entering - nametoprotect %ls", pnametoprotect); + + // Copy the name of the exececutible to protect into IOCTL structure + if (!pnametoprotect) { + LOG_INFO_FAILURE (L"TcProcessNameCallback: NULL Protect Name"); + Result = FALSE; + goto Exit; + } + wcsncpy_s(ProtectNameCallbackInput.Name, pnametoprotect, NAME_SIZE); + ProtectNameCallbackInput.Operation = ulOperation; + + + LOG_INFO (L"TcProtectNameCallback: IOCTL sending nametoprotect %ls", ProtectNameCallbackInput.Name); + + Result = DeviceIoControl ( + TcDeviceHandle, + TD_IOCTL_PROTECT_NAME_CALLBACK, + &ProtectNameCallbackInput, + sizeof(ProtectNameCallbackInput), + NULL, + 0, + &BytesReturned, + NULL + ); + + if (Result == TRUE) + { + LOG_INFO (L"TcProcessNameCallback: succeeded"); + } + else + { + LOG_INFO_FAILURE (L"TcProcessNameCallback: DeviceIoControl failed, last error 0x%x", GetLastError()); + } + +Exit: + + LOG_INFO (L"TcProcessNameCallback: exiting"); + return Result; +} + + + +// +// TcInitializeGlobals +// + +BOOL TcInitializeGlobals() +{ + WCHAR SysDir[MAX_PATH]; + BOOL ReturnValue = FALSE; + +#if !defined (_WIN64) + + BOOL Result = FALSE; + BOOL Wow64Process = FALSE; + PVOID OldWowRedirectionValue = NULL; + + Result = IsWow64Process ( + GetCurrentProcess(), + &Wow64Process + ); + + if (Result == FALSE) + { + LOG_INFO_FAILURE (L"IsWow64Process failed, last error 0x%x", GetLastError()); + goto Exit; + } + + if (Wow64Process == TRUE) + { + // + // Disable FS redirection to make sure a 32 bit test process will + // copy our (64 bit) driver to system32\drivers rather than syswow64\drivers. + // + + Result = Wow64DisableWow64FsRedirection (&OldWowRedirectionValue); + + if (Result == FALSE) + { + LOG_INFO_FAILURE (L"Wow64DisableWow64FsRedirection failed, last error 0x%x", GetLastError()); + goto Exit; + } + } + +#endif + + // + // Open the service control manager if not already open + // + + if (TcScmHandle == NULL) { + TcScmHandle = OpenSCManager ( + NULL, + NULL, + SC_MANAGER_ALL_ACCESS + ); + + if (TcScmHandle == NULL) + { + LOG_INFO_FAILURE (L"OpenSCManager failed, last error 0x%x", GetLastError()); + goto Exit; + } + } + // + // Construct driver path. + // + + UINT Size = GetSystemDirectory (SysDir, ARRAYSIZE(SysDir)); + + if (Size == 0) + { + LOG_INFO_FAILURE (L"GetSystemDirectory failed, last error 0x%x", GetLastError()); + goto Exit; + } + + HRESULT hr = StringCchPrintf ( + TcDriverPath, + ARRAYSIZE(TcDriverPath), + L"%ls\\drivers\\%ls.sys", + SysDir, + TD_DRIVER_NAME + ); + + if (FAILED (hr)) + { + LOG_INFO_FAILURE (L"StringCchPrintf failed, hr 0x%08x", hr); + goto Exit; + } + + ReturnValue = TRUE; + +Exit: + return ReturnValue; +} + + +// +// TcUnInitialize +// + +BOOL TcCleanupSCM() +{ + if (TcScmHandle != NULL) { + CloseServiceHandle(TcScmHandle); + TcScmHandle = NULL; + } + + return TRUE; +} + +// +// TcLoadDriver +// + +BOOL TcLoadDriver() +{ + BOOL ReturnValue = FALSE; + + LOG_INFO(L"TcLoadDriver: Entering"); + + // + // First, uninstall and unload the driver. + // + + ReturnValue = TcUnloadDriver(); + + if (ReturnValue != TRUE) + { + LOG_INFO_FAILURE (L"TcUnloadDriver failed"); + goto Exit; + } + + // + // Copy the driver to system32\drivers + // + + ReturnValue = CopyFile (TD_DRIVER_NAME_WITH_EXT, TcDriverPath, FALSE); + + if (ReturnValue == FALSE) + { + LOG_INFO_FAILURE ( + L"CopyFile(%ls, %ls) failed, last error 0x%x", + TD_DRIVER_NAME_WITH_EXT, TcDriverPath, GetLastError() + ); + + goto Exit; + } + + // + // Install the driver. + // + + ReturnValue = TcCreateService(); + + if (ReturnValue == FALSE) + { + LOG_INFO_FAILURE (L"TcCreateService failed"); + goto Exit; + } + + // + // Load the driver. + // + + ReturnValue = TcStartService(); + + if (ReturnValue == FALSE) + { + LOG_INFO_FAILURE (L"TcStartService failed"); + goto Exit; + } + + + ReturnValue = TRUE; + +Exit: + + LOG_INFO(L"TcLoadDriver: Exiting"); + return ReturnValue; +} + + + +// +// TcUnloadDriver +// + +BOOL TcUnloadDriver() +{ + BOOL ReturnValue = FALSE; + + LOG_INFO(L"TcUnloadDriver: Entering"); + + + // + // Unload the driver. + // + + ReturnValue = TcStopService(); + + if (ReturnValue == FALSE) + { + LOG_INFO_FAILURE (L"TcStopService failed"); + goto Exit; + } + + // + // Delete the service. + // + + ReturnValue = TcDeleteService(); + + if (ReturnValue == FALSE) + { + LOG_INFO_FAILURE (L"TcDeleteService failed"); + goto Exit; + } + + ReturnValue = TRUE; + +Exit: + + LOG_INFO(L"TcUnloadDriver: Exiting"); + + return ReturnValue; +} + +// +// TcGetServiceState +// + +BOOL TcGetServiceState ( + _In_ SC_HANDLE ServiceHandle, + _Out_ DWORD* State +) +{ + SERVICE_STATUS_PROCESS ServiceStatus; + DWORD BytesNeeded; + + *State = 0; + + BOOL Result = QueryServiceStatusEx ( + ServiceHandle, + SC_STATUS_PROCESS_INFO, + (LPBYTE)&ServiceStatus, + sizeof(ServiceStatus), + &BytesNeeded + ); + + if (Result == FALSE) + { + LOG_INFO_FAILURE (L"TcGetServiceState: QueryServiceStatusEx failed, last error 0x%x", GetLastError()); + return FALSE; + } + + *State = ServiceStatus.dwCurrentState; + + return TRUE; +} + +// +// Wait for service to enter specified state. +// + +BOOL TcWaitForServiceState ( + _In_ SC_HANDLE ServiceHandle, + _In_ DWORD State +) +{ + for (;;) + { + LOG_INFO (L"TcWaitForServiceState: Waiting for service %p to enter state %u...", (DWORD_PTR)ServiceHandle, State); + + DWORD ServiceState; + BOOL Result = TcGetServiceState (ServiceHandle, &ServiceState); + + if (Result == FALSE) + { + return FALSE; + } + + if (ServiceState == State) + { + break; + } + + Sleep (1000); + } + + return TRUE; +} + +// +// TcCreateService +// + +BOOL TcCreateService() +{ + BOOL ReturnValue = FALSE; + + LOG_INFO(L"TcCreateService: Entering"); + + // + // Create the service + // + + SC_HANDLE ServiceHandle = CreateService ( + TcScmHandle, // handle to SC manager + TD_DRIVER_NAME, // name of service + TD_DRIVER_NAME, // display name + SERVICE_ALL_ACCESS, // access mask + SERVICE_KERNEL_DRIVER, // service type + SERVICE_DEMAND_START, // start type + SERVICE_ERROR_NORMAL, // error control + TcDriverPath, // full path to driver + NULL, // load ordering + NULL, // tag id + NULL, // dependency + NULL, // account name + NULL // password + ); + + DWORD LastError = GetLastError(); + + if (ServiceHandle == NULL && LastError != ERROR_SERVICE_EXISTS) + { + LOG_INFO_FAILURE (L"CreateService failed, last error 0x%x", LastError); + goto Exit; + } + + ReturnValue = TRUE; + +Exit: + + if (ServiceHandle) + { + CloseServiceHandle (ServiceHandle); + } + + LOG_INFO(L"TcCreateService: Exiting"); + + return ReturnValue; +} + +// +// TcStartService +// + +BOOL TcStartService() +{ + BOOL ReturnValue = FALSE; + + // + // Open the service. The function assumes that + // TdCreateService has been called before this one + // and the service is already installed. + // + + SC_HANDLE ServiceHandle = OpenService ( + TcScmHandle, + TD_DRIVER_NAME, + SERVICE_ALL_ACCESS + ); + + if (ServiceHandle == NULL) + { + LOG_INFO_FAILURE (L"TcStartService: OpenService failed, last error 0x%x", GetLastError()); + goto Exit; + } + + // + // Start the service + // + + if (! StartService (ServiceHandle, 0, NULL)) + { + if (GetLastError() != ERROR_SERVICE_ALREADY_RUNNING) + { + LOG_INFO_FAILURE (L"TcStartService: StartService failed, last error 0x%x", GetLastError()); + goto Exit; + } + } + + if (FALSE == TcWaitForServiceState (ServiceHandle, SERVICE_RUNNING)) + { + goto Exit; + } + + ReturnValue = TRUE; + +Exit: + + if (ServiceHandle) + { + CloseServiceHandle (ServiceHandle); + } + + return ReturnValue; +} + + +// +// TcStopService +// + +BOOL TcStopService() +{ + BOOL ReturnValue = FALSE; + + LOG_INFO(L"TcStopService: Entering"); + + // + // Open the service so we can stop it + // + + SC_HANDLE ServiceHandle = OpenService ( + TcScmHandle, + TD_DRIVER_NAME, + SERVICE_ALL_ACCESS + ); + + DWORD LastError = GetLastError(); + + if (ServiceHandle == NULL) + { + if (LastError == ERROR_SERVICE_DOES_NOT_EXIST) + { + ReturnValue = TRUE; + } + else + { + LOG_INFO_FAILURE (L"TcStopService: OpenService failed, last error 0x%x", LastError); + } + + goto Exit; + } + + // + // Stop the service + // + + SERVICE_STATUS ServiceStatus; + + if (FALSE == ControlService (ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus)) + { + LastError = GetLastError(); + + if (LastError != ERROR_SERVICE_NOT_ACTIVE) + { + LOG_INFO_FAILURE (L"TcStopService: ControlService failed, last error 0x%x", LastError); + goto Exit; + } + } + + if (FALSE == TcWaitForServiceState (ServiceHandle, SERVICE_STOPPED)) + { + goto Exit; + } + + ReturnValue = TRUE; + +Exit: + + if (ServiceHandle) + { + CloseServiceHandle (ServiceHandle); + } + + LOG_INFO(L"TcStopService: Exiting"); + + return ReturnValue; +} + +// +// TcDeleteService +// + +BOOL TcDeleteService() +{ + BOOL ReturnValue = FALSE; + + + LOG_INFO(L"TcDeleteService: Entering"); + + // + // Open the service so we can delete it + // + + SC_HANDLE ServiceHandle = OpenService ( + TcScmHandle, + TD_DRIVER_NAME, + SERVICE_ALL_ACCESS + ); + + DWORD LastError = GetLastError(); + + if (ServiceHandle == NULL) + { + if (LastError == ERROR_SERVICE_DOES_NOT_EXIST) + { + ReturnValue = TRUE; + } + else + { + LOG_INFO_FAILURE (L"TcDeleteService: OpenService failed, last error 0x%x", LastError); + } + + goto Exit; + } + + // + // Delete the service + // + + if (! DeleteService (ServiceHandle)) + { + LastError = GetLastError(); + + if (LastError != ERROR_SERVICE_MARKED_FOR_DELETE) + { + LOG_INFO_FAILURE (L"TcDeleteService: DeleteService failed, last error 0x%x", LastError); + goto Exit; + } + } + + ReturnValue = TRUE; + +Exit: + + if (ServiceHandle) + { + CloseServiceHandle (ServiceHandle); + } + + LOG_INFO(L"TcDeleteService: Exiting"); + + return ReturnValue; +} + +// +// TcOpenDevice +// + +BOOL TcOpenDevice() +{ + BOOL ReturnValue = FALSE; + + LOG_INFO(L"TcOpenDevice: Entering"); + + + // + // Open the device if not already opened + // + if (TcDeviceHandle == INVALID_HANDLE_VALUE) { + TcDeviceHandle = CreateFile ( + TD_WIN32_DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + + if (TcDeviceHandle == INVALID_HANDLE_VALUE) + { + LOG_INFO_FAILURE (L"TcOpenDevice: CreateFile(%ls) failed, last error 0x%x", TD_WIN32_DEVICE_NAME, GetLastError()); + goto Exit; + } + } + + + ReturnValue = TRUE; + +Exit: + + LOG_INFO(L"TcOpenDevice: Exiting"); + return ReturnValue; +} + +// +// TcOpenDevice +// + +BOOL TcCloseDevice() +{ + BOOL ReturnValue = FALSE; + + LOG_INFO(L"TcCloseDevice: Entering"); + + // + // Close our handle to the device. + // + + if (TcDeviceHandle != INVALID_HANDLE_VALUE) + { + CloseHandle (TcDeviceHandle); + TcDeviceHandle = INVALID_HANDLE_VALUE; + } + + ReturnValue = TRUE; + + LOG_INFO(L"TcCloseDevice: Exiting"); + return ReturnValue; +} + diff --git a/general/obcallback/driver/ObCallbackTest.vcxproj b/general/obcallback/driver/ObCallbackTest.vcxproj new file mode 100644 index 00000000..e781eb3a --- /dev/null +++ b/general/obcallback/driver/ObCallbackTest.vcxproj @@ -0,0 +1,210 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {C696D115-0970-4E9C-8FED-31A99E039ED5} + $(MSBuildProjectName) + Debug + Win32 + {F7152E7D-34B2-4B88-A5D9-17D68B786DFC} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + ObCallbackTest + + + ObCallbackTest + + + ObCallbackTest + + + ObCallbackTest + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalIncludeDirectories) + Disabled + true + Level4 + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalDependencies) + %(AdditionalOptions) /INTEGRITYCHECK + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalIncludeDirectories) + Disabled + true + Level4 + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalDependencies) + %(AdditionalOptions) /INTEGRITYCHECK + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalIncludeDirectories) + Disabled + true + Level4 + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalDependencies) + %(AdditionalOptions) /INTEGRITYCHECK + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalIncludeDirectories) + Disabled + true + Level4 + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalDependencies) + %(AdditionalOptions) /INTEGRITYCHECK + + + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Create + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/obcallback/driver/ObCallbackTest.vcxproj.Filters b/general/obcallback/driver/ObCallbackTest.vcxproj.Filters new file mode 100644 index 00000000..6fb49bed --- /dev/null +++ b/general/obcallback/driver/ObCallbackTest.vcxproj.Filters @@ -0,0 +1,35 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {5962AF0C-70AA-4F9A-870A-9CA844475924} + + + h;hpp;hxx;hm;inl;inc;xsd + {B9BBA35B-98FC-4830-9A01-41A3227755C9} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {75DE6DD6-44CF-4D1B-BC2C-1EF556200FC9} + + + inf;inv;inx;mof;mc; + {593EF94F-8BBC-4C56-999C-5D894DD5456F} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/obcallback/driver/callback.c b/general/obcallback/driver/callback.c new file mode 100644 index 00000000..39ecd1b4 --- /dev/null +++ b/general/obcallback/driver/callback.c @@ -0,0 +1,479 @@ + +// Callback functions for Ob sample code tests. +// +// Notice: +// +// Use this sample code at your own risk; there is no support from Microsoft for the sample code. +// In addition, this sample code is licensed to you under the terms of the Microsoft Public License +// (http://www.microsoft.com/opensource/licenses.mspx) +// +// + + +#include "pch.h" +#include "tdriver.h" + +// +// Globals +// + +KGUARDED_MUTEX TdCallbacksMutex; +BOOLEAN bCallbacksInstalled = FALSE; + + +#define CB_PROCESS_TERMINATE 0x0001 +#define CB_THREAD_TERMINATE 0x0001 + +// The following are for setting up callbacks for Process and Thread filtering +PVOID pCBRegistrationHandle = NULL; + +OB_CALLBACK_REGISTRATION CBObRegistration = { 0 }; +OB_OPERATION_REGISTRATION CBOperationRegistrations[2] = { { 0 }, { 0 } }; +UNICODE_STRING CBAltitude = {0}; +TD_CALLBACK_REGISTRATION CBCallbackRegistration = {0}; + +// Here is the protected process +WCHAR TdwProtectName[NAME_SIZE+1] = {0}; +PVOID TdProtectedTargetProcess = NULL; +HANDLE TdProtectedTargetProcessId = {0}; + + +// +// TdDeleteProtectNameCallback +// +NTSTATUS TdDeleteProtectNameCallback () +{ + NTSTATUS Status = STATUS_SUCCESS; + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdDeleteProtectNameCallback entering\n"); + + KeAcquireGuardedMutex (&TdCallbacksMutex); + + // if the callbacks are active - remove them + if (bCallbacksInstalled == TRUE) { + ObUnRegisterCallbacks(pCBRegistrationHandle); + pCBRegistrationHandle = NULL; + bCallbacksInstalled = FALSE; + } + + + KeReleaseGuardedMutex (&TdCallbacksMutex); + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdDeleteProtectNameCallback exiting - status 0x%x\n", Status + ); + + return Status; +} + + +// +// TdProtectNameCallback +// + +NTSTATUS TdProtectNameCallback ( + _In_ PTD_PROTECTNAME_INPUT pProtectName +) +{ + NTSTATUS Status = STATUS_SUCCESS; + + if (!pProtectName) { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, + "ObCallbackTest: TdProtectNameCallback: name to protect/filter NULL pointer\n" + ); + } + else { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdProtectNameCallback: entering name to protect/filter %ls\n", pProtectName->Name + ); + } + KeAcquireGuardedMutex (&TdCallbacksMutex); + + // Need to copy out the name and then set the flag to filter + // This will allow process creation to watch for the process to be created and get the PID + // and then prevent any other process from opening up that PID to terminate + + memcpy(TdwProtectName, pProtectName->Name, sizeof(TdwProtectName)); + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: name copied %ls\n", TdwProtectName + ); + + // Need to enable the OB callbacks + // once the process is matched to a newly created process, the callbacks will protect the process + if (bCallbacksInstalled == FALSE) { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdProtectNameCallback: installing callbacks\n" + ); + + // Setup the Ob Registration calls + + CBOperationRegistrations[0].ObjectType = PsProcessType; + CBOperationRegistrations[0].Operations |= OB_OPERATION_HANDLE_CREATE; + CBOperationRegistrations[0].Operations |= OB_OPERATION_HANDLE_DUPLICATE; + CBOperationRegistrations[0].PreOperation = CBTdPreOperationCallback; + CBOperationRegistrations[0].PostOperation = CBTdPostOperationCallback; + + CBOperationRegistrations[1].ObjectType = PsThreadType; + CBOperationRegistrations[1].Operations |= OB_OPERATION_HANDLE_CREATE; + CBOperationRegistrations[1].Operations |= OB_OPERATION_HANDLE_DUPLICATE; + CBOperationRegistrations[1].PreOperation = CBTdPreOperationCallback; + CBOperationRegistrations[1].PostOperation = CBTdPostOperationCallback; + + + RtlInitUnicodeString (&CBAltitude, L"1000"); + + CBObRegistration.Version = OB_FLT_REGISTRATION_VERSION; + CBObRegistration.OperationRegistrationCount = 2; + CBObRegistration.Altitude = CBAltitude; + CBObRegistration.RegistrationContext = &CBCallbackRegistration; + CBObRegistration.OperationRegistration = CBOperationRegistrations; + + + Status = ObRegisterCallbacks ( + &CBObRegistration, + &pCBRegistrationHandle // save the registration handle to remove callbacks later + ); + + if (!NT_SUCCESS (Status)) { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, + "ObCallbackTest: installing OB callbacks failed status 0x%x\n", Status + ); + KeReleaseGuardedMutex (&TdCallbacksMutex); // Release the lock before exit + goto Exit; + } + bCallbacksInstalled = TRUE; + + } + + + KeReleaseGuardedMutex (&TdCallbacksMutex); + + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdProtectNameCallback: name to protect/filter %ls\n", TdwProtectName + ); + +Exit: + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdProtectNameCallback: exiting status 0x%x\n", Status + ); + return Status; +} + + +// +// TdCheckProcessMatch - function to test a command line to see if the process is to be protected +// +NTSTATUS TdCheckProcessMatch ( + _In_ PCUNICODE_STRING pustrCommand, + _In_ PEPROCESS Process, + _In_ HANDLE ProcessId +) +{ + NTSTATUS Status = STATUS_UNSUCCESSFUL; + WCHAR CommandLineBuffer[NAME_SIZE + 1] = {0}; // force a NULL termination + USHORT CommandLineBytes = 0; + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdCheckProcessMatch: entering\n"); + + if (!pustrCommand || !pustrCommand->Buffer) { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, + "ObCallbackTest: TdCheckProcessMatch: no Command line provided\n" + ); + Status = FALSE; + goto Exit; + } + else { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdCheckProcessMatch: checking for %ls\n", TdwProtectName + ); + } + + KeAcquireGuardedMutex (&TdCallbacksMutex); + + + // Make sure that the CommandLineBuffer is NULL terminated + if (pustrCommand->Length < (NAME_SIZE * sizeof(WCHAR))) + CommandLineBytes = pustrCommand->Length; + else + CommandLineBytes = NAME_SIZE * sizeof(WCHAR); + + if (CommandLineBytes) { + memcpy(CommandLineBuffer, pustrCommand->Buffer, CommandLineBytes); + + // now check if the process to protect is in the command line + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdCheckProcessMatch: command line %ls\n", CommandLineBuffer + ); + + if (NULL != wcsstr (CommandLineBuffer, TdwProtectName)) { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdCheckProcessMatch: match FOUND\n" + ); + + // Set the process to watch + TdProtectedTargetProcess = Process; + TdProtectedTargetProcessId = ProcessId; + + Status = STATUS_SUCCESS; + } + } + else { + Status = FALSE; // no command line buffer provided + } + + KeReleaseGuardedMutex (&TdCallbacksMutex); + + +Exit: + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdCheckProcessMatch: leaving status 0x%x\n", Status + ); + return Status; +} + + +// +// CBTdPreOperationCallback +// +OB_PREOP_CALLBACK_STATUS +CBTdPreOperationCallback ( + _In_ PVOID RegistrationContext, + _Inout_ POB_PRE_OPERATION_INFORMATION PreInfo +) +{ + PTD_CALLBACK_REGISTRATION CallbackRegistration; + + ACCESS_MASK AccessBitsToClear = 0; + ACCESS_MASK AccessBitsToSet = 0; + ACCESS_MASK InitialDesiredAccess = 0; + ACCESS_MASK OriginalDesiredAccess = 0; + + + PACCESS_MASK DesiredAccess = NULL; + + LPCWSTR ObjectTypeName = NULL; + LPCWSTR OperationName = NULL; + + // Not using driver specific values at this time + CallbackRegistration = (PTD_CALLBACK_REGISTRATION)RegistrationContext; + + + TD_ASSERT (PreInfo->CallContext == NULL); + + // Only want to filter attempts to access protected process + // all other processes are left untouched + + if (PreInfo->ObjectType == *PsProcessType) { + // + // Ignore requests for processes other than our target process. + // + + // if (TdProtectedTargetProcess != NULL && + // TdProtectedTargetProcess != PreInfo->Object) + if (TdProtectedTargetProcess != PreInfo->Object) + { + goto Exit; + } + + // + // Also ignore requests that are trying to open/duplicate the current + // process. + // + + if (PreInfo->Object == PsGetCurrentProcess()) { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: CBTdPreOperationCallback: ignore process open/duplicate from the protected process itself\n"); + goto Exit; + } + + ObjectTypeName = L"PsProcessType"; + AccessBitsToClear = CB_PROCESS_TERMINATE; + AccessBitsToSet = 0; + } + else if (PreInfo->ObjectType == *PsThreadType) { + HANDLE ProcessIdOfTargetThread = PsGetThreadProcessId ((PETHREAD)PreInfo->Object); + + // + // Ignore requests for threads belonging to processes other than our + // target process. + // + + // if (CallbackRegistration->TargetProcess != NULL && + // CallbackRegistration->TargetProcessId != ProcessIdOfTargetThread) + if (TdProtectedTargetProcessId != ProcessIdOfTargetThread) { + goto Exit; + } + + // + // Also ignore requests for threads belonging to the current processes. + // + + if (ProcessIdOfTargetThread == PsGetCurrentProcessId()) { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: CBTdPreOperationCallback: ignore thread open/duplicate from the protected process itself\n"); + goto Exit; + } + + ObjectTypeName = L"PsThreadType"; + AccessBitsToClear = CB_THREAD_TERMINATE; + AccessBitsToSet = 0; + } + else { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, + "ObCallbackTest: CBTdPreOperationCallback: unexpected object type\n"); + goto Exit; + } + + switch (PreInfo->Operation) { + case OB_OPERATION_HANDLE_CREATE: + DesiredAccess = &PreInfo->Parameters->CreateHandleInformation.DesiredAccess; + OriginalDesiredAccess = PreInfo->Parameters->CreateHandleInformation.OriginalDesiredAccess; + + OperationName = L"OB_OPERATION_HANDLE_CREATE"; + break; + + case OB_OPERATION_HANDLE_DUPLICATE: + DesiredAccess = &PreInfo->Parameters->DuplicateHandleInformation.DesiredAccess; + OriginalDesiredAccess = PreInfo->Parameters->DuplicateHandleInformation.OriginalDesiredAccess; + + OperationName = L"OB_OPERATION_HANDLE_DUPLICATE"; + break; + + default: + TD_ASSERT (FALSE); + break; + } + + InitialDesiredAccess = *DesiredAccess; + + // Filter only if request made outside of the kernel + if (PreInfo->KernelHandle != 1) { + *DesiredAccess &= ~AccessBitsToClear; + *DesiredAccess |= AccessBitsToSet; + } + + // + // Set call context. + // + + TdSetCallContext (PreInfo, CallbackRegistration); + + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "ObCallbackTest: CBTdPreOperationCallback: PROTECTED process %p (ID 0x%p)\n", + TdProtectedTargetProcess, + (PVOID)TdProtectedTargetProcessId + ); + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: CBTdPreOperationCallback\n" + " Client Id: %p:%p\n" + " Object: %p\n" + " Type: %ls\n" + " Operation: %ls (KernelHandle=%d)\n" + " OriginalDesiredAccess: 0x%x\n" + " DesiredAccess (in): 0x%x\n" + " DesiredAccess (out): 0x%x\n", + PsGetCurrentProcessId(), + PsGetCurrentThreadId(), + PreInfo->Object, + ObjectTypeName, + OperationName, + PreInfo->KernelHandle, + OriginalDesiredAccess, + InitialDesiredAccess, + *DesiredAccess + ); + +Exit: + + return OB_PREOP_SUCCESS; +} + +// +// TdPostOperationCallback +// + +VOID +CBTdPostOperationCallback ( + _In_ PVOID RegistrationContext, + _In_ POB_POST_OPERATION_INFORMATION PostInfo + ) +{ + PTD_CALLBACK_REGISTRATION CallbackRegistration = (PTD_CALLBACK_REGISTRATION)RegistrationContext; + + TdCheckAndFreeCallContext (PostInfo, CallbackRegistration); + + if (PostInfo->ObjectType == *PsProcessType) { + // + // Ignore requests for processes other than our target process. + // + + if (CallbackRegistration->TargetProcess != NULL && + CallbackRegistration->TargetProcess != PostInfo->Object + ) { + return; + } + + // + // Also ignore requests that are trying to open/duplicate the current + // process. + // + + if (PostInfo->Object == PsGetCurrentProcess()) { + return; + } + } + else if (PostInfo->ObjectType == *PsThreadType) { + HANDLE ProcessIdOfTargetThread = PsGetThreadProcessId ((PETHREAD)PostInfo->Object); + + // + // Ignore requests for threads belonging to processes other than our + // target process. + // + + if (CallbackRegistration->TargetProcess != NULL && + CallbackRegistration->TargetProcessId != ProcessIdOfTargetThread + ) { + return; + } + + // + // Also ignore requests for threads belonging to the current processes. + // + + if (ProcessIdOfTargetThread == PsGetCurrentProcessId()) { + return; + } + } + else { + TD_ASSERT (FALSE); + } + +} + diff --git a/general/obcallback/driver/pch.h b/general/obcallback/driver/pch.h new file mode 100644 index 00000000..9cab74cc --- /dev/null +++ b/general/obcallback/driver/pch.h @@ -0,0 +1,13 @@ + +// Notice: +// +// Use this sample code at your own risk; there is no support from Microsoft for the sample code. +// In addition, this sample code is licensed to you under the terms of the Microsoft Public License +// (http://www.microsoft.com/opensource/licenses.mspx) + +#pragma once + +#include +#include + + diff --git a/general/obcallback/driver/pchsrc.c b/general/obcallback/driver/pchsrc.c new file mode 100644 index 00000000..17305716 --- /dev/null +++ b/general/obcallback/driver/pchsrc.c @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/general/obcallback/driver/shared.h b/general/obcallback/driver/shared.h new file mode 100644 index 00000000..cea7f5f9 --- /dev/null +++ b/general/obcallback/driver/shared.h @@ -0,0 +1,95 @@ +/*++ + +Module Name: + + shared.h + +Abstract: + + This contains declarations shared by the Ob/Ps callback test driver and + the user mode test app. + + +// Notice: +// +// Use this sample code at your own risk; there is no support from Microsoft for the sample code. +// In addition, this sample code is licensed to you under the terms of the Microsoft Public License +// (http://www.microsoft.com/opensource/licenses.mspx) + +--*/ + +#pragma once + +#pragma warning(disable:4214) // bit field types other than int +#pragma warning(disable:4201) // nameless struct/union + +// +// TD_ASSERT +// +// This macro is identical to NT_ASSERT but works in fre builds as well. +// +// It is used for error checking in the driver in cases where +// we can't easily report the error to the user mode app, or the +// error is so severe that we should break in immediately to +// investigate. +// +// It's better than DbgBreakPoint because it provides additional info +// that can be dumped with .exr -1, and individual asserts can be disabled +// from kd using 'ahi' command. +// + +#define TD_ASSERT(_exp) \ + ((!(_exp)) ? \ + (__annotation(L"Debug", L"AssertFail", L#_exp), \ + DbgRaiseAssertionFailure(), FALSE) : \ + TRUE) + +// +// Driver and device names +// It is important to change the names of the binaries +// in the sample code to be unique for your own use. +// + +#define TD_DRIVER_NAME L"ObCallbackTest" +#define TD_DRIVER_NAME_WITH_EXT L"ObCallbackTest.sys" + +#define TD_NT_DEVICE_NAME L"\\Device\\ObCallbackTest" +#define TD_DOS_DEVICES_LINK_NAME L"\\DosDevices\\ObCallbackTest" +#define TD_WIN32_DEVICE_NAME L"\\\\.\\ObCallbackTest" + + +#define NAME_SIZE 200 + +#define TD_INVALID_CALLBACK_ID ((ULONG)-1) + +// +// IOCTLs exposed by the driver. +// + +// #define TD_IOCTL_REGISTER_CALLBACK CTL_CODE (FILE_DEVICE_UNKNOWN, (0x800 + 0), METHOD_BUFFERED, FILE_SPECIAL_ACCESS) +// #define TD_IOCTL_UNREGISTER_CALLBACK CTL_CODE (FILE_DEVICE_UNKNOWN, (0x800 + 1), METHOD_BUFFERED, FILE_SPECIAL_ACCESS) +#define TD_IOCTL_PROTECT_NAME_CALLBACK CTL_CODE (FILE_DEVICE_UNKNOWN, (0x800 + 2), METHOD_BUFFERED, FILE_SPECIAL_ACCESS) +#define TD_IOCTL_UNPROTECT_CALLBACK CTL_CODE (FILE_DEVICE_UNKNOWN, (0x800 + 3), METHOD_BUFFERED, FILE_SPECIAL_ACCESS) + + +#define TDProtectName_Protect 0 // name of programs to proect and filter out the desiredAccess on Process Open +#define TDProtectName_Reject 1 // name of programs to reject during ProcessCreate + +// +// Structures used by TD_IOCTL_PROTECTNAME +// + +typedef struct _TD_PROTECTNAME_INPUT { + ULONG Operation; + WCHAR Name[NAME_SIZE+1]; // what is the filename to protect - extra wchar for forced NULL +} +TD_PROTECTNAME_INPUT, *PTD_PROTECTNAME_INPUT; + +// +// Structures used by TD_IOCTL_UNPROTECT_CALLBACK +// + +typedef struct _TD_UNPROTECT_CALLBACK_INPUT { + ULONG UnusedParameter; +} +TD_UNPROTECT_CALLBACK_INPUT, *PTD_UNPROTECT_CALLBACK_INPUT; diff --git a/general/obcallback/driver/tdriver.c b/general/obcallback/driver/tdriver.c new file mode 100644 index 00000000..641635ee --- /dev/null +++ b/general/obcallback/driver/tdriver.c @@ -0,0 +1,532 @@ +/*++ + +Module Name: + + tdriver.c + +Abstract: + + Main module for the Ob and Ps sample code + +Notice: + Use this sample code at your own risk; there is no support from Microsoft for the sample code. + In addition, this sample code is licensed to you under the terms of the Microsoft Public License + (http://www.microsoft.com/opensource/licenses.mspx) + + +--*/ + +#include "pch.h" +#include "tdriver.h" + +// +// Process notify routines. +// + +BOOLEAN TdProcessNotifyRoutineSet2 = FALSE; + +// allow filter the requested access +BOOLEAN TdbProtectName = FALSE; +BOOLEAN TdbRejectName = FALSE; + +// +// Function declarations +// +DRIVER_INITIALIZE DriverEntry; + +_Dispatch_type_(IRP_MJ_CREATE) DRIVER_DISPATCH TdDeviceCreate; +_Dispatch_type_(IRP_MJ_CLOSE) DRIVER_DISPATCH TdDeviceClose; +_Dispatch_type_(IRP_MJ_CLEANUP) DRIVER_DISPATCH TdDeviceCleanup; +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH TdDeviceControl; + +DRIVER_UNLOAD TdDeviceUnload; + +VOID +TdCreateProcessNotifyRoutine2 ( + _Inout_ PEPROCESS Process, + _In_ HANDLE ProcessId, + _In_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo + ) +{ + NTSTATUS Status = STATUS_SUCCESS; + + if (CreateInfo != NULL) + { + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdCreateProcessNotifyRoutine2: process %p (ID 0x%p) created, creator %Ix:%Ix\n" + " command line %wZ\n" + " file name %wZ (FileOpenNameAvailable: %d)\n", + Process, + (PVOID)ProcessId, + (ULONG_PTR)CreateInfo->CreatingThreadId.UniqueProcess, + (ULONG_PTR)CreateInfo->CreatingThreadId.UniqueThread, + CreateInfo->CommandLine, + CreateInfo->ImageFileName, + CreateInfo->FileOpenNameAvailable + ); + + // Search for matching process to protect only if filtering + if (TdbProtectName) { + if (CreateInfo->CommandLine != NULL) + { + Status = TdCheckProcessMatch(CreateInfo->CommandLine, Process, ProcessId); + + if (Status == STATUS_SUCCESS) { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "ObCallbackTest: TdCreateProcessNotifyRoutine2: PROTECTING process %p (ID 0x%p)\n", + Process, + (PVOID)ProcessId + ); + } + } + + } + + // Search for matching process to reject process creation + if (TdbRejectName) { + if (CreateInfo->CommandLine != NULL) + { + Status = TdCheckProcessMatch(CreateInfo->CommandLine, Process, ProcessId); + + if (Status == STATUS_SUCCESS) { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "ObCallbackTest: TdCreateProcessNotifyRoutine2: REJECTING process %p (ID 0x%p)\n", + Process, + (PVOID)ProcessId + ); + + CreateInfo->CreationStatus = STATUS_ACCESS_DENIED; + } + } + + } + } + else + { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "ObCallbackTest: TdCreateProcessNotifyRoutine2: process %p (ID 0x%p) destroyed\n", + Process, + (PVOID)ProcessId + ); + } +} + +// +// DriverEntry +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath +) +{ + NTSTATUS Status; + UNICODE_STRING NtDeviceName = RTL_CONSTANT_STRING (TD_NT_DEVICE_NAME); + UNICODE_STRING DosDevicesLinkName = RTL_CONSTANT_STRING (TD_DOS_DEVICES_LINK_NAME); + PDEVICE_OBJECT Device = NULL; + BOOLEAN SymLinkCreated = FALSE; + USHORT CallbackVersion; + + UNREFERENCED_PARAMETER (RegistryPath); + + DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "ObCallbackTest: DriverEntry: Driver loaded. Use ed nt!Kd_IHVDRIVER_Mask f (or 7) to enable more traces\n"); + + CallbackVersion = ObGetFilterVersion(); + + DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "ObCallbackTest: DriverEntry: Callback version 0x%hx\n", CallbackVersion); + + // + // Initialize globals. + // + + KeInitializeGuardedMutex (&TdCallbacksMutex); + + // + // Create our device object. + // + + Status = IoCreateDevice ( + DriverObject, // pointer to driver object + 0, // device extension size + &NtDeviceName, // device name + FILE_DEVICE_UNKNOWN, // device type + 0, // device characteristics + FALSE, // not exclusive + &Device); // returned device object pointer + + if (! NT_SUCCESS(Status)) + { + goto Exit; + } + + TD_ASSERT (Device == DriverObject->DeviceObject); + + // + // Set dispatch routines. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = TdDeviceCreate; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = TdDeviceClose; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = TdDeviceCleanup; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = TdDeviceControl; + DriverObject->DriverUnload = TdDeviceUnload; + + // + // Create a link in the Win32 namespace. + // + + Status = IoCreateSymbolicLink (&DosDevicesLinkName, &NtDeviceName); + + if (! NT_SUCCESS(Status)) + { + goto Exit; + } + + SymLinkCreated = TRUE; + + // + // Set process create routines. + // + + Status = PsSetCreateProcessNotifyRoutineEx ( + TdCreateProcessNotifyRoutine2, + FALSE + ); + + if (! NT_SUCCESS(Status)) + { + DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "ObCallbackTest: DriverEntry: PsSetCreateProcessNotifyRoutineEx(2) returned 0x%x\n", Status); + goto Exit; + } + + TdProcessNotifyRoutineSet2 = TRUE; + +Exit: + + if (!NT_SUCCESS (Status)) + { + if (TdProcessNotifyRoutineSet2 == TRUE) + { + Status = PsSetCreateProcessNotifyRoutineEx ( + TdCreateProcessNotifyRoutine2, + TRUE + ); + + TD_ASSERT (Status == STATUS_SUCCESS); + + TdProcessNotifyRoutineSet2 = FALSE; + } + + if (SymLinkCreated == TRUE) + { + IoDeleteSymbolicLink (&DosDevicesLinkName); + } + + if (Device != NULL) + { + IoDeleteDevice (Device); + } + } + + return Status; +} + +// +// Function: +// +// TdDeviceUnload +// +// Description: +// +// This function handles driver unloading. All this driver needs to do +// is to delete the device object and the symbolic link between our +// device name and the Win32 visible name. +// + +VOID +TdDeviceUnload ( + _In_ PDRIVER_OBJECT DriverObject +) +{ + NTSTATUS Status = STATUS_SUCCESS; + UNICODE_STRING DosDevicesLinkName = RTL_CONSTANT_STRING (TD_DOS_DEVICES_LINK_NAME); + + DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "ObCallbackTest: TdDeviceUnload\n"); + + // + // Unregister process notify routines. + // + + if (TdProcessNotifyRoutineSet2 == TRUE) + { + Status = PsSetCreateProcessNotifyRoutineEx ( + TdCreateProcessNotifyRoutine2, + TRUE + ); + + TD_ASSERT (Status == STATUS_SUCCESS); + + TdProcessNotifyRoutineSet2 = FALSE; + } + + // remove filtering and remove any OB callbacks + TdbProtectName = FALSE; + Status = TdDeleteProtectNameCallback(); + TD_ASSERT (Status == STATUS_SUCCESS); + + // + // Delete the link from our device name to a name in the Win32 namespace. + // + + Status = IoDeleteSymbolicLink (&DosDevicesLinkName); + if (Status != STATUS_INSUFFICIENT_RESOURCES) { + // + // IoDeleteSymbolicLink can fail with STATUS_INSUFFICIENT_RESOURCES. + // + + TD_ASSERT (NT_SUCCESS (Status)); + + } + + + // + // Delete our device object. + // + + IoDeleteDevice (DriverObject->DeviceObject); +} + +// +// Function: +// +// TdDeviceCreate +// +// Description: +// +// This function handles the 'create' irp. +// + + +NTSTATUS +TdDeviceCreate ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp +) +{ + UNREFERENCED_PARAMETER (DeviceObject); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + +// +// Function: +// +// TdDeviceClose +// +// Description: +// +// This function handles the 'close' irp. +// + +NTSTATUS +TdDeviceClose ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp +) +{ + UNREFERENCED_PARAMETER (DeviceObject); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + +// +// Function: +// +// TdDeviceCleanup +// +// Description: +// +// This function handles the 'cleanup' irp. +// + +NTSTATUS +TdDeviceCleanup ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp +) +{ + UNREFERENCED_PARAMETER (DeviceObject); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + +// +// TdControlProtectName +// + +NTSTATUS TdControlProtectName ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp +) +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpStack = NULL; + ULONG InputBufferLength = 0; + PTD_PROTECTNAME_INPUT pProtectNameInput = NULL; + + UNREFERENCED_PARAMETER (DeviceObject); + + + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdControlProtectName: Entering\n"); + + IrpStack = IoGetCurrentIrpStackLocation (Irp); + InputBufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; + + if (InputBufferLength < sizeof (TD_PROTECTNAME_INPUT)) + { + Status = STATUS_BUFFER_OVERFLOW; + goto Exit; + } + + pProtectNameInput = (PTD_PROTECTNAME_INPUT)Irp->AssociatedIrp.SystemBuffer; + + Status = TdProtectNameCallback (pProtectNameInput); + + switch (pProtectNameInput->Operation) { + case TDProtectName_Protect: + // Begin filtering access rights + TdbProtectName = TRUE; + TdbRejectName = FALSE; + break; + + case TDProtectName_Reject: + // Begin reject process creation on match + TdbProtectName = FALSE; + TdbRejectName = TRUE; + break; + } + + +Exit: + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TD_IOCTL_PROTECTNAME: Status %x\n", Status); + + return Status; +} + +// +// TdControlUnprotect +// + +NTSTATUS TdControlUnprotect ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp +) +{ + NTSTATUS Status = STATUS_SUCCESS; + // PIO_STACK_LOCATION IrpStack = NULL; + // ULONG InputBufferLength = 0; + + UNREFERENCED_PARAMETER (DeviceObject); + UNREFERENCED_PARAMETER (Irp); + + // IrpStack = IoGetCurrentIrpStackLocation (Irp); + // InputBufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; + + // No need to check length of passed in parameters as we do not need any information from that + + // do not filter requested access + Status = TdDeleteProtectNameCallback(); + if (Status != STATUS_SUCCESS) { + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TdDeleteProtectNameCallback: status 0x%x\n", Status); + } + TdbProtectName = FALSE; + TdbRejectName = FALSE; + +//Exit: + DbgPrintEx ( + DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, + "ObCallbackTest: TD_IOCTL_UNPROTECT: exiting - status 0x%x\n", Status); + + return Status; +} + + +// +// Function: +// +// TdDeviceControl +// +// Description: +// +// This function handles 'control' irp. +// + +NTSTATUS +TdDeviceControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp +) +{ + PIO_STACK_LOCATION IrpStack; + ULONG Ioctl; + NTSTATUS Status; + + UNREFERENCED_PARAMETER (DeviceObject); + + + Status = STATUS_SUCCESS; + + IrpStack = IoGetCurrentIrpStackLocation (Irp); + Ioctl = IrpStack->Parameters.DeviceIoControl.IoControlCode; + + DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "TdDeviceControl: entering - ioctl code 0x%x\n", Ioctl); + + switch (Ioctl) + { + case TD_IOCTL_PROTECT_NAME_CALLBACK: + + Status = TdControlProtectName (DeviceObject, Irp); + break; + + case TD_IOCTL_UNPROTECT_CALLBACK: + + Status = TdControlUnprotect (DeviceObject, Irp); + break; + + + default: + DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "TdDeviceControl: unrecognized ioctl code 0x%x\n", Ioctl); + break; + } + + // + // Complete the irp and return. + // + + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NO_INCREMENT); + + DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "TdDeviceControl leaving - status 0x%x\n", Status); + return Status; +} diff --git a/general/obcallback/driver/tdriver.h b/general/obcallback/driver/tdriver.h new file mode 100644 index 00000000..b7dedd02 --- /dev/null +++ b/general/obcallback/driver/tdriver.h @@ -0,0 +1,125 @@ +/*++ + +Module Name: + + tdriver.h + +Abstract: + + This module declarations for the Ob/Ps callback test driver. + + +// Notice: +// +// Use this sample code at your own risk; there is no support from Microsoft for the sample code. +// In addition, this sample code is licensed to you under the terms of the Microsoft Public License +// (http://www.microsoft.com/opensource/licenses.mspx) + +--*/ + +#pragma once + +#include "shared.h" + +#define TD_CALLBACK_REGISTRATION_TAG '0bCO' // TD_CALLBACK_REGISTRATION structure. +#define TD_CALL_CONTEXT_TAG '1bCO' // TD_CALL_CONTEXT structure. + + +typedef struct _TD_CALLBACK_PARAMETERS { + ACCESS_MASK AccessBitsToClear; + ACCESS_MASK AccessBitsToSet; +} +TD_CALLBACK_PARAMETERS, *PTD_CALLBACK_PARAMETERS; + +// +// TD_CALLBACK_REGISTRATION +// + +typedef struct _TD_CALLBACK_REGISTRATION { + + // + // Handle returned by ObRegisterCallbacks. + // + + PVOID RegistrationHandle; + + // + // If not NULL, filter only requests to open/duplicate handles to this + // process (or one of its threads). + // + + PVOID TargetProcess; + HANDLE TargetProcessId; + + + // + // Currently each TD_CALLBACK_REGISTRATION has at most one process and one + // thread callback. That is, we can't register more than one callback for + // the same object type with a single ObRegisterCallbacks call. + // + + TD_CALLBACK_PARAMETERS ProcessParams; + TD_CALLBACK_PARAMETERS ThreadParams; + + ULONG RegistrationId; // Index in the global TdCallbacks array. + +} +TD_CALLBACK_REGISTRATION, *PTD_CALLBACK_REGISTRATION; + +// +// TD_CALL_CONTEXT +// + +typedef struct _TD_CALL_CONTEXT +{ + PTD_CALLBACK_REGISTRATION CallbackRegistration; + + OB_OPERATION Operation; + PVOID Object; + POBJECT_TYPE ObjectType; +} +TD_CALL_CONTEXT, *PTD_CALL_CONTEXT; + +extern KGUARDED_MUTEX TdCallbacksMutex; + +NTSTATUS TdDeleteCallback ( + _In_ ULONG RegistrationId +); + +// delete the process/thead OB callbacks +NTSTATUS TdDeleteProtectNameCallback (); + + + +NTSTATUS TdProtectNameCallback( + _In_ PTD_PROTECTNAME_INPUT pProtectName +); + +NTSTATUS TdCheckProcessMatch ( + _In_ PCUNICODE_STRING pustrCommand, + _In_ PEPROCESS Process, + _In_ HANDLE ProcessId +); + +OB_PREOP_CALLBACK_STATUS +CBTdPreOperationCallback ( + _In_ PVOID RegistrationContext, + _Inout_ POB_PRE_OPERATION_INFORMATION PreInfo +); + +VOID +CBTdPostOperationCallback ( + _In_ PVOID RegistrationContext, + _In_ POB_POST_OPERATION_INFORMATION PostInfo +); + +VOID TdSetCallContext ( + _Inout_ POB_PRE_OPERATION_INFORMATION PreInfo, + _In_ PTD_CALLBACK_REGISTRATION CallbackRegistration +); + +VOID TdCheckAndFreeCallContext ( + _Inout_ POB_POST_OPERATION_INFORMATION PostInfo, + _In_ PTD_CALLBACK_REGISTRATION CallbackRegistration +); + diff --git a/general/obcallback/driver/util.c b/general/obcallback/driver/util.c new file mode 100644 index 00000000..c00e96a3 --- /dev/null +++ b/general/obcallback/driver/util.c @@ -0,0 +1,73 @@ +/*++ + +Module Name: + + util.c + +Notice: + Use this sample code at your own risk; there is no support from Microsoft for the sample code. + In addition, this sample code is licensed to you under the terms of the Microsoft Public License + (http://www.microsoft.com/opensource/licenses.mspx) + + +--*/ + +#include "pch.h" +#include "tdriver.h" + +// +// TdSetCallContext +// +// Creates a call context object and stores a pointer to it +// in the supplied OB_PRE_OPERATION_INFORMATION structure. +// +// This function is called from a pre-notification. The created call context +// object then has to be freed in a corresponding post-notification using +// TdCheckAndFreeCallContext. +// + +void TdSetCallContext ( + _Inout_ POB_PRE_OPERATION_INFORMATION PreInfo, + _In_ PTD_CALLBACK_REGISTRATION CallbackRegistration +) +{ + PTD_CALL_CONTEXT CallContext; + + CallContext = (PTD_CALL_CONTEXT) ExAllocatePoolWithTag ( + PagedPool, sizeof(TD_CALL_CONTEXT), TD_CALL_CONTEXT_TAG + ); + + if (CallContext == NULL) + { + return; + } + + RtlZeroMemory (CallContext, sizeof(TD_CALL_CONTEXT)); + + CallContext->CallbackRegistration = CallbackRegistration; + CallContext->Operation = PreInfo->Operation; + CallContext->Object = PreInfo->Object; + CallContext->ObjectType = PreInfo->ObjectType; + + PreInfo->CallContext = CallContext; +} + +void TdCheckAndFreeCallContext ( + _Inout_ POB_POST_OPERATION_INFORMATION PostInfo, + _In_ PTD_CALLBACK_REGISTRATION CallbackRegistration +) +{ + PTD_CALL_CONTEXT CallContext = (PTD_CALL_CONTEXT)PostInfo->CallContext; + + if (CallContext != NULL) + { + TD_ASSERT (CallContext->CallbackRegistration == CallbackRegistration); + + TD_ASSERT (CallContext->Operation == PostInfo->Operation); + TD_ASSERT (CallContext->Object == PostInfo->Object); + TD_ASSERT (CallContext->ObjectType == PostInfo->ObjectType); + + ExFreePoolWithTag (CallContext, TD_CALL_CONTEXT_TAG); + } +} + diff --git a/general/obcallback/obcallback.sln b/general/obcallback/obcallback.sln new file mode 100644 index 00000000..ed5a27b9 --- /dev/null +++ b/general/obcallback/obcallback.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Control", "Control", "{D429EFC9-09E8-495C-81B0-32340C75C2BA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{5DA38202-119A-41F8-B5A6-CB8D38191147}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ObCallbackTestCtrl", "control\ObCallbackTestCtrl.vcxproj", "{8B053BEE-EA21-4D12-984B-6C93FE6D4992}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ObCallbackTest", "driver\ObCallbackTest.vcxproj", "{C696D115-0970-4E9C-8FED-31A99E039ED5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8B053BEE-EA21-4D12-984B-6C93FE6D4992}.Debug|Win32.ActiveCfg = Debug|Win32 + {8B053BEE-EA21-4D12-984B-6C93FE6D4992}.Debug|Win32.Build.0 = Debug|Win32 + {8B053BEE-EA21-4D12-984B-6C93FE6D4992}.Release|Win32.ActiveCfg = Release|Win32 + {8B053BEE-EA21-4D12-984B-6C93FE6D4992}.Release|Win32.Build.0 = Release|Win32 + {8B053BEE-EA21-4D12-984B-6C93FE6D4992}.Debug|x64.ActiveCfg = Debug|x64 + {8B053BEE-EA21-4D12-984B-6C93FE6D4992}.Debug|x64.Build.0 = Debug|x64 + {8B053BEE-EA21-4D12-984B-6C93FE6D4992}.Release|x64.ActiveCfg = Release|x64 + {8B053BEE-EA21-4D12-984B-6C93FE6D4992}.Release|x64.Build.0 = Release|x64 + {C696D115-0970-4E9C-8FED-31A99E039ED5}.Debug|Win32.ActiveCfg = Debug|Win32 + {C696D115-0970-4E9C-8FED-31A99E039ED5}.Debug|Win32.Build.0 = Debug|Win32 + {C696D115-0970-4E9C-8FED-31A99E039ED5}.Release|Win32.ActiveCfg = Release|Win32 + {C696D115-0970-4E9C-8FED-31A99E039ED5}.Release|Win32.Build.0 = Release|Win32 + {C696D115-0970-4E9C-8FED-31A99E039ED5}.Debug|x64.ActiveCfg = Debug|x64 + {C696D115-0970-4E9C-8FED-31A99E039ED5}.Debug|x64.Build.0 = Debug|x64 + {C696D115-0970-4E9C-8FED-31A99E039ED5}.Release|x64.ActiveCfg = Release|x64 + {C696D115-0970-4E9C-8FED-31A99E039ED5}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {8B053BEE-EA21-4D12-984B-6C93FE6D4992} = {D429EFC9-09E8-495C-81B0-32340C75C2BA} + {C696D115-0970-4E9C-8FED-31A99E039ED5} = {5DA38202-119A-41F8-B5A6-CB8D38191147} + EndGlobalSection +EndGlobal diff --git a/general/pcidrv/ReadMe.md b/general/pcidrv/ReadMe.md new file mode 100644 index 00000000..72898b5b --- /dev/null +++ b/general/pcidrv/ReadMe.md @@ -0,0 +1,186 @@ +PCIDRV - WDF Driver for PCI Device +================================== + +This sample demonstrates how to write a KMDF driver for a PCI device. The sample works with the Intel 82557/82558 based PCI Ethernet Adapter (10/100) and Intel compatibles. + +This adapter supports scatter-gather DMA, wake on external event (Wait-Wake), and idle power down. The hardware specification is publicly available, and the source code to interface with the hardware is included in the WDK. + + +Overview +-------- + +The following is a list of key KMDF interfaces demonstrated in this sample: + +- Handling PnP & Power Events + +- Registering Device Interface + +- Hardware resource mapping: Port, Memory & Interrupt + +- DMA Interfaces + +- Parallel default queue for write requests. If the write cannot be satisfied immediately, the request is put into a manual parallel queue. + +- Parallel manual queue for Read requests + +- Parallelc default queue for IOCTL requests. If the ioctl cannot be satisfied immediately, the request is put into a manual parallel queue. + +- Request cancelation + +- Handling Interrupt & DPC + +- Watchdog Timer DPC to monitor the device state. + +- Event Tracing & HEXDUMP + +- Reading & Writing to the registry + +Note: This sample provides an example of a minimal driver intended for educational purposes. Neither the driver nor its sample test programs are intended for use in a production environment. + +As stated earlier, this sample is meant to demonstrate how to write a KMDF driver for a generic PCI device and not for PCI network controllers. For network controllers, you should write a monolithic NDIS miniport driver based on the samples given under the src\\network\\ndis directory. + +Note that it is still possible to use a subset of KMDF APIs when writing a NDIS miniport (see src\\network\\ndis\\usbnwifi directory for a sample on how to use KMDF interfaces to talk to USB device in an NDIS miniport). + +The sample driver has been tested on the following Intel Ethernet controllers: + + ++++ + + + + + + + + + + + +
Device Desc +Hardware ID

IBM Netfinity 10/100 Ethernet Adapter

+

PCI\VEN_8086&DEV_1229&SUBSYS_005C1014&REV_05

Intel(R) PRO/100+ Management Adapter with Alert On LAN

+

PCI\VEN_8086&DEV_1229&SUBSYS_000E8086&REV_08

+ +Using this sample as a standalone driver +---------------------------------------- + +``` {.syntax xml:space="preserve"} + --------------------- + | | + | MYPING | <-- Usermode test application + | | + --------------------- + ^ + | UserMode +------------------------------------------------------------------- + | KernelMode + V + --------------------- + | | + | PCIDRV | <-- Installed as a function driver + | | + --------------------- + ^ + | <-----Talk to the hardware using I/O resources + V + --------------- + | H/W NIC | + --------------- + ||||||| + ------- +``` + +You can install the driver as a standalone driver of a custom setup class, called Sample Class using GENPCI.INF. The PCI device is not seen as a network controller and as a result no protocol driver is bound to the device. In order to test the read & write path of the driver, you can use the specially developed ping application, called MYPING. This test application crafts the entire Ethernet frame in usermode and sends it to the driver to be transferred on the wire. In this configuration, you can only ping another machine on the same subnet. The application does all the ARP and AARP resolution in the usermode to get the MAC address of the target machine and sends ICMP ECHO requests. + +The PCIDRV sample acts as a power policy owner of the device and implements all the wait-wake and idle detection logic. + +INSTALLATION +------------ + +The driver can be installed as a Net class driver or as a standalone driver (user defined class). The KMDF versions of the INF files are dynamically generated from .INX file. In addition to the driver files, you have to include the WDF coinstaller DLL from the src\\redist\\wdf folder of the WDK. + +You can obtain redistributable framework updates by downloading the *wdfcoinstaller.msi* package from [WDK 8 Redistributable Components](http://go.microsoft.com/fwlink/p/?LinkID=226396). This package performs a silent install into the directory of your Windows Driver Kit (WDK) installation. You will see no confirmation that the installation has completed. You can verify that the redistributables have been installed on top of the WDK by ensuring there is a redist\\wdf directory under the root directory of the WDK, %ProgramFiles(x86)%\\Windows Kits\\8.0. + +### TESTING + +To test standalone driver configuration: You should use the specially developed ping application, called MYPING that comes with the sample. The Ping.exe provided in the system will not work because in this configuration, the test card is not bound to any network protocol - it's not seen as Net device by the system. Currently the test application doesn't have ability to get an IP address from a network DHCP server. As a result, it is better to connect the network device to a private hub and ping another machine connected to that hub. For example, let us say you have a test machine A and another machine B (development box). + +- Connect machine A and Machine B to a local hub. + +- Assign a static IP address, say 128.0.0.1 to the NIC on machine B. + +- Clear the ARP table on machine B by running **Arp -d** on the command line + +- Now run Myping.exe. This application enumerates GUID\_DEVINTERFACE\_PCIDRV and displays the name of the devices with an index number. This number will be used in identifying the interface when you invoke ping dialog. + +- In the ping dialog specify the following and click okay: + +- Device Index: 1 \<- number displayed in the list window + +- Source Ip Address: 128.0.0.4 \<- You can make up any valid IP address for test Machine A + +- Destination IP Address: 128.0.0.1 \<- IP address of machine B + +- Packet Size: 1428 \<- Default max size of ping payload. Minimum value is 32 bytes. + +If the machine B has more than one adapter and if the second adapter is connected to the internet (Corporate Network), instead of assigning static IP address to the adapter that's connected to the test machine, you can install Internet Connection Sharing (ICS) on it and get an IP address for ICS. This would let you use the test machine to browse the internet when the sample is installed in the miniport configuration and also in the standalone mode without making up or stealing somebody's IP address. For example, let us say the machine B has two adapters NIC1 and NIC2. NIC1 is connected to the CorpNet and NIC2 is connected to the private hub. Install ICS on NIC2 as described below: + +- Select the NIC2 in the Network Connections Applet. + +- Click the **Properties** button. + +- Go to the Advanced Tab and Check the box "Allow Other network users to connect through this computers internet connection" in the Internet Connection Sharing choice. + +- This will assign 192.168.0.1 IP address to NIC2. + +- Now on machine B, you can assume 192.168.0.2 as the local IP address and run Myping.exe . Or, you can install the sample in the miniport configuration and browse the internet. + +Other menu options of myping applications are: + +- Reenumerate All Device: This command lets you terminate active ping threads and close handle to all the device and reenumerate the devices again and display their names with index numbers. This might cause the devices to have new index numbers. + +- Cleanup: This command terminates ping threads and closes handles to all the devices. + +- Clear Display: Clears the window. + +- Verbose: Let you get more debug messages. + +- Exit: Terminate the application. + +**Note**  You can use this application only on a device installed in the standalone configuration. If you run it on a device that's installed as a miniport, you will get an error message. For such devices, you can use the system provided ping.exe. + +RESOURCES +--------- + +For the latest release of the Windows Driver Kit, see http://www.microsoft.com/whdc/. + +If you have questions on using or adapting this sample for your project, you can either contact Microsoft Technical Support or post your questions in the Microsoft driver development newsgroup. + +FILE MANIFEST +------------- + + ++++ + + + + + + + + + + + +
File +Description

KMDF

+

Contains the driver.

KMDF\HW

+

Contains hardware specific code.

+ + diff --git a/general/pcidrv/kmdf/HW/PCIDRV.vcxproj b/general/pcidrv/kmdf/HW/PCIDRV.vcxproj new file mode 100644 index 00000000..ca2cff13 --- /dev/null +++ b/general/pcidrv/kmdf/HW/PCIDRV.vcxproj @@ -0,0 +1,360 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {2221261F-E1E2-4562-A15D-2EF8BF7D878E} + $(MSBuildProjectName) + 1 + Debug + Win32 + {EAA7F2DD-6EAB-48CE-80E1-46A17BF21F72} + + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + $(InfArch) + true + .\$(IntDir)\genpci.inf + + + ".\$(IntDir)\pcidrv.bmf" + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + + + + + PCIDRV + + + PCIDRV + + + PCIDRV + + + PCIDRV + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + %(PreprocessorDefinitions);EVENT_TRACING + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + %(PreprocessorDefinitions);EVENT_TRACING + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + %(PreprocessorDefinitions);EVENT_TRACING + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + %(PreprocessorDefinitions);EVENT_TRACING + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/pcidrv/kmdf/HW/PCIDRV.vcxproj.Filters b/general/pcidrv/kmdf/HW/PCIDRV.vcxproj.Filters new file mode 100644 index 00000000..a15b9d16 --- /dev/null +++ b/general/pcidrv/kmdf/HW/PCIDRV.vcxproj.Filters @@ -0,0 +1,75 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {8FC125AF-15BD-4DCD-9D5E-C7FE9A6ACD75} + + + h;hpp;hxx;hm;inl;inc;xsd + {938C7F7B-13A2-43D1-A1CF-4B02B581687C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {75E794A5-28E0-464A-A171-8AD7F2A1DEAA} + + + inf;inv;inx;mof;mc; + {EDAC3101-0378-4793-BEA6-FC4E036C130B} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/pcidrv/kmdf/HW/e100_557.h b/general/pcidrv/kmdf/HW/e100_557.h new file mode 100644 index 00000000..fb4075df --- /dev/null +++ b/general/pcidrv/kmdf/HW/e100_557.h @@ -0,0 +1,789 @@ +/**************************************************************************** +** COPYRIGHT (C) 1994-1997 INTEL CORPORATION ** +** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON ** +** HTTP://WWW.INTEL.COM/ ** +** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND ** +** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER ** +****************************************************************************/ + +/**************************************************************************** +Module Name: + e100_557.h (82557.h) + +This driver runs on the following hardware: + - 82558 based PCI 10/100Mb ethernet adapters + (aka Intel EtherExpress(TM) PRO Adapters) + +Environment: + Kernel Mode - Or whatever is the equivalent on WinNT + +*****************************************************************************/ + +#ifndef _E100_557_H +#define _E100_557_H + +//------------------------------------------------------------------------- +// D100 Stepping Defines +//------------------------------------------------------------------------- +#define D100_A_STEP 0 // NEVER SHIPPED +#define D100_B_STEP 1 // d100 first shipped silicon +#define D100_C_STEP 2 // d100' (c-step) with vendor/id and hw fix +#define D101_A_STEP 4 // first silicon of d101 + +//------------------------------------------------------------------------- +// E100 Stepping Defines - used in PoMgmt Decisions +//------------------------------------------------------------------------- +#define E100_82557_A_STEP 1 +#define E100_82557_B_STEP 2 +#define E100_82557_C_STEP 3 +#define E100_82558_A_STEP 4 +#define E100_82558_B_STEP 5 +#define E100_82559_A_STEP 6 +#define E100_82559_B_STEP 7 +#define E100_82559_C_STEP 8 +#define E100_82559ER_A_STEP 9 + +//------------------------------------------------------------------------- +// D100 PORT functions -- lower 4 bits +//------------------------------------------------------------------------- +#define PORT_SOFTWARE_RESET 0 +#define PORT_SELFTEST 1 +#define PORT_SELECTIVE_RESET 2 +#define PORT_DUMP 3 + + +//------------------------------------------------------------------------- +// CSR field definitions -- Offsets from CSR base +//------------------------------------------------------------------------- +#define SCB_STATUS_LOW_BYTE 0x0 +#define SCB_STATUS_HIGH_BYTE 0x1 +#define SCB_COMMAND_LOW_BYTE 0x2 +#define SCB_COMMAND_HIGH_BYTE 0x3 +#define SCB_GENERAL_POINTER 0x4 +#define CSR_PORT_LOW_WORD 0x8 +#define CSR_PORT_HIGH_WORD 0x0a +#define CSR_FLASH_CONTROL_REG 0x0c +#define CSR_EEPROM_CONTROL_REG 0x0e +#define CSR_MDI_CONTROL_LOW_WORD 0x10 +#define CSR_MDI_CONTROL_HIGH_WORD 0x12 + + +//------------------------------------------------------------------------- +// SCB Status Word bit definitions +//------------------------------------------------------------------------- +//- Interrupt status fields +#define SCB_STATUS_MASK BIT_12_15 // ACK Mask +#define SCB_STATUS_CX BIT_15 // CU Completed Action Cmd +#define SCB_STATUS_FR BIT_14 // RU Received A Frame +#define SCB_STATUS_CNA BIT_13 // CU Became Inactive (IDLE) +#define SCB_STATUS_RNR BIT_12 // RU Became Not Ready +#define SCB_STATUS_MDI BIT_11 // MDI read or write done +#define SCB_STATUS_SWI BIT_10 // Software generated interrupt + +//- Interrupt ACK fields +#define SCB_ACK_MASK (BIT_9 | BIT_12_15 | BIT_8) // ACK Mask +#define SCB_ALL_INTERRUPT_BITS BIT_8_15 // if all the bits are set, no interrupt to be served +#define SCB_ACK_CX BIT_15 // CU Completed Action Cmd +#define SCB_ACK_FR BIT_14 // RU Received A Frame +#define SCB_ACK_CNA BIT_13 // CU Became Inactive (IDLE) +#define SCB_ACK_RNR BIT_12 // RU Became Not Ready +#define SCB_ACK_MDI BIT_11 // MDI read or write done +#define SCB_ACK_SWI BIT_10 // Software generated interrupt +#define SCB_ACK_ER BIT_9 // Early Receive interrupt +#define SCB_ACK_FCP BIT_8 // Flow Control Pause interrupt + +//- CUS Fields +#define SCB_CUS_MASK (BIT_6 | BIT_7) // CUS 2-bit Mask +#define SCB_CUS_IDLE 0 // CU Idle +#define SCB_CUS_SUSPEND BIT_6 // CU Suspended +#define SCB_CUS_ACTIVE BIT_7 // CU Active + +//- RUS Fields +#define SCB_RUS_IDLE 0 // RU Idle +#define SCB_RUS_MASK BIT_2_5 // RUS 3-bit Mask +#define SCB_RUS_SUSPEND BIT_2 // RU Suspended +#define SCB_RUS_NO_RESOURCES BIT_3 // RU Out Of Resources +#define SCB_RUS_READY BIT_4 // RU Ready +#define SCB_RUS_SUSP_NO_RBDS (BIT_2 | BIT_5) // RU No More RBDs +#define SCB_RUS_NO_RBDS (BIT_3 | BIT_5) // RU No More RBDs +#define SCB_RUS_READY_NO_RBDS (BIT_4 | BIT_5) // RU Ready, No RBDs + + +//------------------------------------------------------------------------- +// SCB Command Word bit definitions +//------------------------------------------------------------------------- +//- CUC fields +#define SCB_CUC_MASK BIT_4_6 // CUC 3-bit Mask +#define SCB_CUC_START BIT_4 // CU Start +#define SCB_CUC_RESUME BIT_5 // CU Resume +#define SCB_CUC_DUMP_ADDR BIT_6 // CU Dump Counters Address +#define SCB_CUC_DUMP_STAT (BIT_4 | BIT_6) // CU Dump statistics counters +#define SCB_CUC_LOAD_BASE (BIT_5 | BIT_6) // Load the CU base +#define SCB_CUC_DUMP_RST_STAT BIT_4_6 // CU Dump and reset statistics counters +#define SCB_CUC_STATIC_RESUME (BIT_5 | BIT_7) // CU Static Resume + +//- RUC fields +#define SCB_RUC_MASK BIT_0_2 // RUC 3-bit Mask +#define SCB_RUC_START BIT_0 // RU Start +#define SCB_RUC_RESUME BIT_1 // RU Resume +#define SCB_RUC_ABORT BIT_2 // RU Abort +#define SCB_RUC_LOAD_HDS (BIT_0 | BIT_2) // Load RFD Header Data Size +#define SCB_RUC_LOAD_BASE (BIT_1 | BIT_2) // Load the RU base +#define SCB_RUC_RBD_RESUME BIT_0_2 // RBD resume + +// Interrupt fields (assuming byte addressing) +#define SCB_INT_MASK BIT_0 // Mask interrupts +#define SCB_SOFT_INT BIT_1 // Generate a software interrupt + + +//------------------------------------------------------------------------- +// EEPROM bit definitions +//------------------------------------------------------------------------- +//- EEPROM control register bits +#define EN_TRNF 0x10 // Enable turnoff +#define EEDO 0x08 // EEPROM data out +#define EEDI 0x04 // EEPROM data in (set for writing data) +#define EECS 0x02 // EEPROM chip select (1=high, 0=low) +#define EESK 0x01 // EEPROM shift clock (1=high, 0=low) + +//- EEPROM opcodes +#define EEPROM_READ_OPCODE 06 +#define EEPROM_WRITE_OPCODE 05 +#define EEPROM_ERASE_OPCODE 07 +#define EEPROM_EWEN_OPCODE 19 // Erase/write enable +#define EEPROM_EWDS_OPCODE 16 // Erase/write disable + +//- EEPROM data locations +#define EEPROM_NODE_ADDRESS_BYTE_0 0 +#define EEPROM_FLAGS_WORD_3 3 +#define EEPROM_FLAG_10MC BIT_0 +#define EEPROM_FLAG_100MC BIT_1 + +//------------------------------------------------------------------------- +// MDI Control register bit definitions +//------------------------------------------------------------------------- +#define MDI_DATA_MASK BIT_0_15 // MDI Data port +#define MDI_REG_ADDR BIT_16_20 // which MDI register to read/write +#define MDI_PHY_ADDR BIT_21_25 // which PHY to read/write +#define MDI_PHY_OPCODE BIT_26_27 // which PHY to read/write +#define MDI_PHY_READY BIT_28 // PHY is ready for another MDI cycle +#define MDI_PHY_INT_ENABLE BIT_29 // Assert INT at MDI cycle completion + + +//------------------------------------------------------------------------- +// MDI Control register opcode definitions +//------------------------------------------------------------------------- +#define MDI_WRITE 1 // Phy Write +#define MDI_READ 2 // Phy read + + +//------------------------------------------------------------------------- +// D100 Action Commands +//------------------------------------------------------------------------- +#define CB_NOP 0 +#define CB_IA_ADDRESS 1 +#define CB_CONFIGURE 2 +#define CB_MULTICAST 3 +#define CB_TRANSMIT 4 +#define CB_LOAD_MICROCODE 5 +#define CB_DUMP 6 +#define CB_DIAGNOSE 7 + + +//------------------------------------------------------------------------- +// Command Block (CB) Field Definitions +//------------------------------------------------------------------------- +//- CB Command Word +#define CB_EL_BIT BIT_15 // CB EL Bit +#define CB_S_BIT BIT_14 // CB Suspend Bit +#define CB_I_BIT BIT_13 // CB Interrupt Bit +#define CB_TX_SF_BIT BIT_3 // TX CB Flexible Mode +#define CB_CMD_MASK BIT_0_2 // CB 3-bit CMD Mask + +//- CB Status Word +#define CB_STATUS_MASK BIT_12_15 // CB Status Mask (4-bits) +#define CB_STATUS_COMPLETE BIT_15 // CB Complete Bit +#define CB_STATUS_OK BIT_13 // CB OK Bit +#define CB_STATUS_UNDERRUN BIT_12 // CB A Bit +#define CB_STATUS_FAIL BIT_11 // CB Fail (F) Bit + +//misc command bits +#define CB_TX_EOF_BIT BIT_15 // TX CB/TBD EOF Bit + +//------------------------------------------------------------------------- +// Config CB Parameter Fields +//------------------------------------------------------------------------- +#define CB_CFIG_BYTE_COUNT 22 // 22 config bytes +#define CB_SHORT_CFIG_BYTE_COUNT 8 // 8 config bytes + +// byte 0 bit definitions +#define CB_CFIG_BYTE_COUNT_MASK BIT_0_5 // Byte count occupies bit 5-0 + +// byte 1 bit definitions +#define CB_CFIG_RXFIFO_LIMIT_MASK BIT_0_4 // RxFifo limit mask +#define CB_CFIG_TXFIFO_LIMIT_MASK BIT_4_7 // TxFifo limit mask + +// byte 3 bit definitions -- +#define CB_CFIG_B3_MWI_ENABLE BIT_0 // Memory Write Invalidate Enable Bit + +// byte 4 bit definitions +#define CB_CFIG_RX_MIN_DMA_MASK BIT_0_6 // Rx minimum DMA count mask + +// byte 5 bit definitions +#define CB_CFIG_TX_MIN_DMA_MASK BIT_0_6 // Tx minimum DMA count mask +#define CB_CFIG_DMBC_EN BIT_7 // Enable Tx/Rx minimum DMA counts + +// byte 6 bit definitions +#define CB_CFIG_LATE_SCB BIT_0 // Update SCB After New Tx Start +#define CB_CFIG_TNO_INT BIT_2 // Tx Not OK Interrupt +#define CB_CFIG_CI_INT BIT_3 // Command Complete Interrupt +#define CB_CFIG_SAVE_BAD_FRAMES BIT_7 // Save Bad Frames Enabled + +// byte 7 bit definitions +#define CB_CFIG_DISC_SHORT_FRAMES BIT_0 // Discard Short Frames +#define CB_CFIG_URUN_RETRY BIT_1_2 // Underrun Retry Count + +// byte 8 bit definitions +#define CB_CFIG_503_MII BIT_0 // 503 vs. MII mode + +// byte 9 bit definitions -- pre-defined all zeros + +// byte 10 bit definitions +#define CB_CFIG_NO_SRCADR BIT_3 // No Source Address Insertion +#define CB_CFIG_PREAMBLE_LEN BIT_4_5 // Preamble Length +#define CB_CFIG_LOOPBACK_MODE BIT_6_7 // Loopback Mode + +// byte 11 bit definitions +#define CB_CFIG_LINEAR_PRIORITY BIT_0_2 // Linear Priority + +// byte 12 bit definitions +#define CB_CFIG_LINEAR_PRI_MODE BIT_0 // Linear Priority mode +#define CB_CFIG_IFS_MASK BIT_4_7 // CSMA level Interframe Spacing mask + +// byte 13 bit definitions -- pre-defined all zeros + +// byte 14 bit definitions -- pre-defined 0xf2 + +// byte 15 bit definitions +#define CB_CFIG_PROMISCUOUS BIT_0 // Promiscuous Mode Enable +#define CB_CFIG_BROADCAST_DIS BIT_1 // Broadcast Mode Disable +#define CB_CFIG_CRS_OR_CDT BIT_7 // CRS Or CDT + +// byte 16 bit definitions -- pre-defined all zeros + +// byte 17 bit definitions -- pre-defined 0x40 + +// byte 18 bit definitions +#define CB_CFIG_STRIPPING BIT_0 // Stripping Disabled +#define CB_CFIG_PADDING BIT_1 // Padding Disabled +#define CB_CFIG_CRC_IN_MEM BIT_2 // Transfer CRC To Memory + +// byte 19 bit definitions +#define CB_CFIG_FORCE_FDX BIT_6 // Force Full Duplex +#define CB_CFIG_FDX_ENABLE BIT_7 // Full Duplex Enabled + +// byte 20 bit definitions +#define CB_CFIG_MULTI_IA BIT_6 // Multiple IA Addr + +// byte 21 bit definitions +#define CB_CFIG_MULTICAST_ALL BIT_3 // Multicast All + + +//------------------------------------------------------------------------- +// Receive Frame Descriptor Fields +//------------------------------------------------------------------------- + +//- RFD Status Bits +#define RFD_RECEIVE_COLLISION BIT_0 // Collision detected on Receive +#define RFD_IA_MATCH BIT_1 // Indv Address Match Bit +#define RFD_RX_ERR BIT_4 // RX_ERR pin on Phy was set +#define RFD_FRAME_TOO_SHORT BIT_7 // Receive Frame Short +#define RFD_DMA_OVERRUN BIT_8 // Receive DMA Overrun +#define RFD_NO_RESOURCES BIT_9 // No Buffer Space +#define RFD_ALIGNMENT_ERROR BIT_10 // Alignment Error +#define RFD_CRC_ERROR BIT_11 // CRC Error +#define RFD_STATUS_OK BIT_13 // RFD OK Bit +#define RFD_STATUS_COMPLETE BIT_15 // RFD Complete Bit + +//- RFD Command Bits +#define RFD_EL_BIT BIT_15 // RFD EL Bit +#define RFD_S_BIT BIT_14 // RFD Suspend Bit +#define RFD_H_BIT BIT_4 // Header RFD Bit +#define RFD_SF_BIT BIT_3 // RFD Flexible Mode + +//- RFD misc bits +#define RFD_EOF_BIT BIT_15 // RFD End-Of-Frame Bit +#define RFD_F_BIT BIT_14 // RFD Buffer Fetch Bit +#define RFD_ACT_COUNT_MASK BIT_0_13 // RFD Actual Count Mask +#define RFD_HEADER_SIZE 0x10 // Size of RFD Header (16 bytes) + +//------------------------------------------------------------------------- +// Receive Buffer Descriptor Fields +//------------------------------------------------------------------------- +#define RBD_EOF_BIT BIT_15 // RBD End-Of-Frame Bit +#define RBD_F_BIT BIT_14 // RBD Buffer Fetch Bit +#define RBD_ACT_COUNT_MASK BIT_0_13 // RBD Actual Count Mask + +#define SIZE_FIELD_MASK BIT_0_13 // Size of the associated buffer +#define RBD_EL_BIT BIT_15 // RBD EL Bit + + +//------------------------------------------------------------------------- +// Size Of Dump Buffer +//------------------------------------------------------------------------- +#define DUMP_BUFFER_SIZE 600 // size of the dump buffer + + +//------------------------------------------------------------------------- +// Self Test Results +//------------------------------------------------------------------------- +#define CB_SELFTEST_FAIL_BIT BIT_12 +#define CB_SELFTEST_DIAG_BIT BIT_5 +#define CB_SELFTEST_REGISTER_BIT BIT_3 +#define CB_SELFTEST_ROM_BIT BIT_2 + +#define CB_SELFTEST_ERROR_MASK ( \ + CB_SELFTEST_FAIL_BIT | CB_SELFTEST_DIAG_BIT | \ + CB_SELFTEST_REGISTER_BIT | CB_SELFTEST_ROM_BIT) + + +//------------------------------------------------------------------------- +// Driver Configuration Default Parameters for the 557 +// Note: If the driver uses any defaults that are different from the chip's +// defaults, it will be noted below +//------------------------------------------------------------------------- +// Byte 0 (byte count) default +#define CB_557_CFIG_DEFAULT_PARM0 CB_CFIG_BYTE_COUNT + +// Byte 1 (fifo limits) default +#define DEFAULT_TX_FIFO_LIMIT 0x08 +#define DEFAULT_RX_FIFO_LIMIT 0x08 +#define CB_557_CFIG_DEFAULT_PARM1 0x88 + +// Byte 2 (IFS) default +#define CB_557_CFIG_DEFAULT_PARM2 0x00 + +// Byte 3 (reserved) default +#define CB_557_CFIG_DEFAULT_PARM3 0x00 + +// Byte 4 (Rx DMA min count) default +#define CB_557_CFIG_DEFAULT_PARM4 0x00 + +// Byte 5 (Tx DMA min count, DMA min count enable) default +#define CB_557_CFIG_DEFAULT_PARM5 0x00 + +// Byte 6 (Late SCB, TNO int, CI int, Save bad frames) default +#define CB_557_CFIG_DEFAULT_PARM6 0x32 + +// Byte 7 (Discard short frames, underrun retry) default +// note: disc short frames will be enabled +#define DEFAULT_UNDERRUN_RETRY 0x01 +#define CB_557_CFIG_DEFAULT_PARM7 0x01 + +// Byte 8 (MII or 503) default +// note: MII will be the default +#define CB_557_CFIG_DEFAULT_PARM8 0x01 + +// Byte 9 - Power management for 82558B, 82559 +#define CB_WAKE_ON_LINK_BYTE9 0x20 +#define CB_WAKE_ON_ARP_PKT_BYTE9 0x40 + +#define CB_557_CFIG_DEFAULT_PARM9 0 + +// Byte 10 (scr addr insertion, preamble, loopback) default +#define CB_557_CFIG_DEFAULT_PARM10 0x2e + +// Byte 11 (linear priority) default +#define CB_557_CFIG_DEFAULT_PARM11 0x00 + +// Byte 12 (IFS,linear priority mode) default +#define CB_557_CFIG_DEFAULT_PARM12 0x60 + +// Byte 13 (reserved) default +#define CB_557_CFIG_DEFAULT_PARM13 0x00 + +// Byte 14 (reserved) default +#define CB_557_CFIG_DEFAULT_PARM14 0xf2 + +// Byte 15 (promiscuous, broadcast, CRS/CDT) default +#define CB_557_CFIG_DEFAULT_PARM15 0xea + +// Byte 16 (reserved) default +#define CB_557_CFIG_DEFAULT_PARM16 0x00 + +// Byte 17 (reserved) default +#define CB_557_CFIG_DEFAULT_PARM17 0x40 + +// Byte 18 (Stripping, padding, Rcv CRC in mem) default +// note: padding will be enabled +#define CB_557_CFIG_DEFAULT_PARM18 0xf2 + +// Byte 19 (reserved) default +// note: full duplex is enabled if FDX# pin is 0 +#define CB_557_CFIG_DEFAULT_PARM19 0x80 + +// Byte 20 (multi-IA) default +#define CB_557_CFIG_DEFAULT_PARM20 0x3f + +// Byte 21 (multicast all) default +#define CB_557_CFIG_DEFAULT_PARM21 0x05 + + +#pragma pack(1) + +//------------------------------------------------------------------------- +// Ethernet Frame Structure +//------------------------------------------------------------------------- +//- Ethernet 6-byte Address +typedef struct _ETH_ADDRESS_STRUC { + UCHAR EthNodeAddress[ETHERNET_ADDRESS_LENGTH]; +} ETH_ADDRESS_STRUC, *PETH_ADDRESS_STRUC; + + +//- Ethernet 14-byte Header +typedef struct _ETH_HEADER_STRUC { + UCHAR Destination[ETHERNET_ADDRESS_LENGTH]; + UCHAR Source[ETHERNET_ADDRESS_LENGTH]; + USHORT TypeLength; +} ETH_HEADER_STRUC, *PETH_HEADER_STRUC; + + +//- Ethernet Buffer (Including Ethernet Header) for Transmits +typedef struct _ETH_TX_BUFFER_STRUC { + ETH_HEADER_STRUC TxMacHeader; + UCHAR TxBufferData[(TCB_BUFFER_SIZE - sizeof(ETH_HEADER_STRUC))]; +} ETH_TX_BUFFER_STRUC, *PETH_TX_BUFFER_STRUC; + +typedef struct _ETH_RX_BUFFER_STRUC { + ETH_HEADER_STRUC RxMacHeader; + UCHAR RxBufferData[(RCB_BUFFER_SIZE - sizeof(ETH_HEADER_STRUC))]; +} ETH_RX_BUFFER_STRUC, *PETH_RX_BUFFER_STRUC; + + + +//------------------------------------------------------------------------- +// 82557 Data Structures +//------------------------------------------------------------------------- + +//------------------------------------------------------------------------- +// Self test +//------------------------------------------------------------------------- +typedef struct _SELF_TEST_STRUC { + ULONG StSignature; // Self Test Signature + ULONG StResults; // Self Test Results +} SELF_TEST_STRUC, *PSELF_TEST_STRUC; + + +//------------------------------------------------------------------------- +// Control/Status Registers (CSR) +//------------------------------------------------------------------------- +typedef struct _CSR_STRUC { + USHORT ScbStatus; // SCB Status register + UCHAR ScbCommandLow; // SCB Command register (low byte) + UCHAR ScbCommandHigh; // SCB Command register (high byte) + ULONG ScbGeneralPointer; // SCB General pointer + ULONG Port; // PORT register + USHORT FlashControl; // Flash Control register + USHORT EepromControl; // EEPROM control register + ULONG MDIControl; // MDI Control Register + ULONG RxDMAByteCount; // Receive DMA Byte count register +} CSR_STRUC, *PCSR_STRUC; + +//------------------------------------------------------------------------- +// Error Counters +//------------------------------------------------------------------------- +typedef struct _ERR_COUNT_STRUC { + ULONG XmtGoodFrames; // Good frames transmitted + ULONG XmtMaxCollisions; // Fatal frames -- had max collisions + ULONG XmtLateCollisions; // Fatal frames -- had a late coll. + ULONG XmtUnderruns; // Transmit underruns (fatal or re-transmit) + ULONG XmtLostCRS; // Frames transmitted without CRS + ULONG XmtDeferred; // Deferred transmits + ULONG XmtSingleCollision; // Transmits that had 1 and only 1 coll. + ULONG XmtMultCollisions; // Transmits that had multiple coll. + ULONG XmtTotalCollisions; // Transmits that had 1+ collisions. + ULONG RcvGoodFrames; // Good frames received + ULONG RcvCrcErrors; // Aligned frames that had a CRC error + ULONG RcvAlignmentErrors; // Receives that had alignment errors + ULONG RcvResourceErrors; // Good frame dropped due to lack of resources + ULONG RcvOverrunErrors; // Overrun errors - bus was busy + ULONG RcvCdtErrors; // Received frames that encountered coll. + ULONG RcvShortFrames; // Received frames that were to short + ULONG CommandComplete; // A005h indicates cmd completion +} ERR_COUNT_STRUC, *PERR_COUNT_STRUC; + + +//------------------------------------------------------------------------- +// Command Block (CB) Generic Header Structure +//------------------------------------------------------------------------- +typedef struct _CB_HEADER_STRUC { + USHORT CbStatus; // Command Block Status + USHORT CbCommand; // Command Block Command + ULONG CbLinkPointer; // Link To Next CB +} CB_HEADER_STRUC, *PCB_HEADER_STRUC; + + +//------------------------------------------------------------------------- +// NOP Command Block (NOP_CB) +//------------------------------------------------------------------------- +typedef struct _NOP_CB_STRUC { + CB_HEADER_STRUC NopCBHeader; +} NOP_CB_STRUC, *PNOP_CB_STRUC; + + +//------------------------------------------------------------------------- +// Individual Address Command Block (IA_CB) +//------------------------------------------------------------------------- +typedef struct _IA_CB_STRUC { + CB_HEADER_STRUC IaCBHeader; + UCHAR IaAddress[ETHERNET_ADDRESS_LENGTH]; +} IA_CB_STRUC, *PIA_CB_STRUC; + + +//------------------------------------------------------------------------- +// Configure Command Block (CONFIG_CB) +//------------------------------------------------------------------------- +typedef struct _CONFIG_CB_STRUC { + CB_HEADER_STRUC ConfigCBHeader; + UCHAR ConfigBytes[CB_CFIG_BYTE_COUNT]; +} CONFIG_CB_STRUC, *PCONFIG_CB_STRUC; + + +//------------------------------------------------------------------------- +// MultiCast Command Block (MULTICAST_CB) +//------------------------------------------------------------------------- +typedef struct _MULTICAST_CB_STRUC { + CB_HEADER_STRUC McCBHeader; + USHORT McCount; // Number of multicast addresses + UCHAR McAddress[(ETHERNET_ADDRESS_LENGTH * MAX_MULTICAST_ADDRESSES)]; +} MULTICAST_CB_STRUC, *PMULTICAST_CB_STRUC; + +//------------------------------------------------------------------------- +// WakeUp Filter Command Block (FILTER_CB) +//------------------------------------------------------------------------- +typedef struct _FILTER_CB_STRUC { + CB_HEADER_STRUC FilterCBHeader; + ULONG Pattern[16]; +}FILTER_CB_STRUC , *PFILTER_CB_STRUC ; + +//------------------------------------------------------------------------- +// Dump Command Block (DUMP_CB) +//------------------------------------------------------------------------- +typedef struct _DUMP_CB_STRUC { + CB_HEADER_STRUC DumpCBHeader; + ULONG DumpAreaAddress; // Dump Buffer Area Address +} DUMP_CB_STRUC, *PDUMP_CB_STRUC; + + +//------------------------------------------------------------------------- +// Dump Area structure definition +//------------------------------------------------------------------------- +typedef struct _DUMP_AREA_STRUC { + UCHAR DumpBuffer[DUMP_BUFFER_SIZE]; +} DUMP_AREA_STRUC, *PDUMP_AREA_STRUC; + + +//------------------------------------------------------------------------- +// Diagnose Command Block (DIAGNOSE_CB) +//------------------------------------------------------------------------- +typedef struct _DIAGNOSE_CB_STRUC { + CB_HEADER_STRUC DiagCBHeader; +} DIAGNOSE_CB_STRUC, *PDIAGNOSE_CB_STRUC; + +//------------------------------------------------------------------------- +// Transmit Command Block (TxCB) +//------------------------------------------------------------------------- +typedef struct _GENERIC_TxCB { + CB_HEADER_STRUC TxCbHeader; + ULONG TxCbTbdPointer; // TBD address + USHORT TxCbCount; // Data Bytes In TCB past header + UCHAR TxCbThreshold; // TX Threshold for FIFO Extender + UCHAR TxCbTbdNumber; + ETH_TX_BUFFER_STRUC TxCbData; + ULONG pad0; + ULONG pad1; + ULONG pad2; + ULONG pad3; +} TXCB_STRUC, *PTXCB_STRUC; + +//------------------------------------------------------------------------- +// Transmit Buffer Descriptor (TBD) +//------------------------------------------------------------------------- +typedef struct _TBD_STRUC { + ULONG TbdBufferAddress; // Physical Transmit Buffer Address + unsigned TbdCount :14; + unsigned :1 ; // always 0 + unsigned EndOfList:1 ; // EL bit in Tbd + unsigned :16; // field that is always 0's in a TBD +} TBD_STRUC, *PTBD_STRUC; + + +//------------------------------------------------------------------------- +// Receive Frame Descriptor (RFD) +//------------------------------------------------------------------------- +typedef struct _RFD_STRUC { + CB_HEADER_STRUC RfdCbHeader; + ULONG RfdRbdPointer; // Receive Buffer Descriptor Addr + USHORT RfdActualCount; // Number Of Bytes Received + USHORT RfdSize; // Number Of Bytes In RFD + ETH_RX_BUFFER_STRUC RfdBuffer; // Data buffer in RFD +} RFD_STRUC, *PRFD_STRUC; + + +//------------------------------------------------------------------------- +// Receive Buffer Descriptor (RBD) +//------------------------------------------------------------------------- +typedef struct _RBD_STRUC { + USHORT RbdActualCount; // Number Of Bytes Received + USHORT RbdFiller; + ULONG RbdLinkAddress; // Link To Next RBD + ULONG RbdRcbAddress; // Receive Buffer Address + USHORT RbdSize; // Receive Buffer Size + USHORT RbdFiller1; +} RBD_STRUC, *PRBD_STRUC; + +#pragma pack() + +//------------------------------------------------------------------------- +// 82557 PCI Register Definitions +// Refer To The PCI Specification For Detailed Explanations +//------------------------------------------------------------------------- +//- Register Offsets +#define PCI_VENDOR_ID_REGISTER 0x00 // PCI Vendor ID Register +#define PCI_DEVICE_ID_REGISTER 0x02 // PCI Device ID Register +#define PCI_CONFIG_ID_REGISTER 0x00 // PCI Configuration ID Register +#define PCI_COMMAND_REGISTER 0x04 // PCI Command Register +#define PCI_STATUS_REGISTER 0x06 // PCI Status Register +#define PCI_REV_ID_REGISTER 0x08 // PCI Revision ID Register +#define PCI_CLASS_CODE_REGISTER 0x09 // PCI Class Code Register +#define PCI_CACHE_LINE_REGISTER 0x0C // PCI Cache Line Register +#define PCI_LATENCY_TIMER 0x0D // PCI Latency Timer Register +#define PCI_HEADER_TYPE 0x0E // PCI Header Type Register +#define PCI_BIST_REGISTER 0x0F // PCI Built-In SelfTest Register +#define PCI_BAR_0_REGISTER 0x10 // PCI Base Address Register 0 +#define PCI_BAR_1_REGISTER 0x14 // PCI Base Address Register 1 +#define PCI_BAR_2_REGISTER 0x18 // PCI Base Address Register 2 +#define PCI_BAR_3_REGISTER 0x1C // PCI Base Address Register 3 +#define PCI_BAR_4_REGISTER 0x20 // PCI Base Address Register 4 +#define PCI_BAR_5_REGISTER 0x24 // PCI Base Address Register 5 +#define PCI_SUBVENDOR_ID_REGISTER 0x2C // PCI SubVendor ID Register +#define PCI_SUBDEVICE_ID_REGISTER 0x2E // PCI SubDevice ID Register +#define PCI_EXPANSION_ROM 0x30 // PCI Expansion ROM Base Register +#define PCI_INTERRUPT_LINE 0x3C // PCI Interrupt Line Register +#define PCI_INTERRUPT_PIN 0x3D // PCI Interrupt Pin Register +#define PCI_MIN_GNT_REGISTER 0x3E // PCI Min-Gnt Register +#define PCI_MAX_LAT_REGISTER 0x3F // PCI Max_Lat Register +#define PCI_NODE_ADDR_REGISTER 0x40 // PCI Node Address Register + + +//------------------------------------------------------------------------- +// PHY 100 MDI Register/Bit Definitions +//------------------------------------------------------------------------- +// MDI register set +#define MDI_CONTROL_REG 0x00 // MDI control register +#define MDI_STATUS_REG 0x01 // MDI Status regiser +#define PHY_ID_REG_1 0x02 // Phy indentification reg (word 1) +#define PHY_ID_REG_2 0x03 // Phy indentification reg (word 2) +#define AUTO_NEG_ADVERTISE_REG 0x04 // Auto-negotiation advertisement +#define AUTO_NEG_LINK_PARTNER_REG 0x05 // Auto-negotiation link partner ability +#define AUTO_NEG_EXPANSION_REG 0x06 // Auto-negotiation expansion +#define AUTO_NEG_NEXT_PAGE_REG 0x07 // Auto-negotiation next page transmit +#define EXTENDED_REG_0 0x10 // Extended reg 0 (Phy 100 modes) +#define EXTENDED_REG_1 0x14 // Extended reg 1 (Phy 100 error indications) +#define NSC_CONG_CONTROL_REG 0x17 // National (TX) congestion control +#define NSC_SPEED_IND_REG 0x19 // National (TX) speed indication +#define PHY_EQUALIZER_REG 0x1A // Register for the Phy Equalizer values + +// MDI Control register bit definitions +#define MDI_CR_COLL_TEST_ENABLE BIT_7 // Collision test enable +#define MDI_CR_FULL_HALF BIT_8 // FDX =1, half duplex =0 +#define MDI_CR_RESTART_AUTO_NEG BIT_9 // Restart auto negotiation +#define MDI_CR_ISOLATE BIT_10 // Isolate PHY from MII +#define MDI_CR_POWER_DOWN BIT_11 // Power down +#define MDI_CR_AUTO_SELECT BIT_12 // Auto speed select enable +#define MDI_CR_10_100 BIT_13 // 0 = 10Mbs, 1 = 100Mbs +#define MDI_CR_LOOPBACK BIT_14 // 0 = normal, 1 = loopback +#define MDI_CR_RESET BIT_15 // 0 = normal, 1 = PHY reset + +// MDI Status register bit definitions +#define MDI_SR_EXT_REG_CAPABLE BIT_0 // Extended register capabilities +#define MDI_SR_JABBER_DETECT BIT_1 // Jabber detected +#define MDI_SR_LINK_STATUS BIT_2 // Link Status -- 1 = link +#define MDI_SR_AUTO_SELECT_CAPABLE BIT_3 // Auto speed select capable +#define MDI_SR_REMOTE_FAULT_DETECT BIT_4 // Remote fault detect +#define MDI_SR_AUTO_NEG_COMPLETE BIT_5 // Auto negotiation complete +#define MDI_SR_10T_HALF_DPX BIT_11 // 10BaseT Half Duplex capable +#define MDI_SR_10T_FULL_DPX BIT_12 // 10BaseT full duplex capable +#define MDI_SR_TX_HALF_DPX BIT_13 // TX Half Duplex capable +#define MDI_SR_TX_FULL_DPX BIT_14 // TX full duplex capable +#define MDI_SR_T4_CAPABLE BIT_15 // T4 capable + +// Auto-Negotiation advertisement register bit definitions +#define NWAY_AD_SELCTOR_FIELD BIT_0_4 // identifies supported protocol +#define NWAY_AD_ABILITY BIT_5_12 // technologies that are supported +#define NWAY_AD_10T_HALF_DPX BIT_5 // 10BaseT Half Duplex capable +#define NWAY_AD_10T_FULL_DPX BIT_6 // 10BaseT full duplex capable +#define NWAY_AD_TX_HALF_DPX BIT_7 // TX Half Duplex capable +#define NWAY_AD_TX_FULL_DPX BIT_8 // TX full duplex capable +#define NWAY_AD_T4_CAPABLE BIT_9 // T4 capable +#define NWAY_AD_REMOTE_FAULT BIT_13 // indicates local remote fault +#define NWAY_AD_RESERVED BIT_14 // reserved +#define NWAY_AD_NEXT_PAGE BIT_15 // Next page (not supported) + +// Auto-Negotiation link partner ability register bit definitions +#define NWAY_LP_SELCTOR_FIELD BIT_0_4 // identifies supported protocol +#define NWAY_LP_ABILITY BIT_5_9 // technologies that are supported +#define NWAY_LP_REMOTE_FAULT BIT_13 // indicates partner remote fault +#define NWAY_LP_ACKNOWLEDGE BIT_14 // acknowledge +#define NWAY_LP_NEXT_PAGE BIT_15 // Next page (not supported) + +// Auto-Negotiation expansion register bit definitions +#define NWAY_EX_LP_NWAY BIT_0 // link partner is NWAY +#define NWAY_EX_PAGE_RECEIVED BIT_1 // link code word received +#define NWAY_EX_NEXT_PAGE_ABLE BIT_2 // local is next page able +#define NWAY_EX_LP_NEXT_PAGE_ABLE BIT_3 // partner is next page able +#define NWAY_EX_PARALLEL_DET_FLT BIT_4 // parallel detection fault +#define NWAY_EX_RESERVED BIT_5_15 // reserved + + +// PHY 100 Extended Register 0 bit definitions +#define PHY_100_ER0_FDX_INDIC BIT_0 // 1 = FDX, 0 = half duplex +#define PHY_100_ER0_SPEED_INDIC BIT_1 // 1 = 100mbs, 0= 10mbs +#define PHY_100_ER0_WAKE_UP BIT_2 // Wake up DAC +#define PHY_100_ER0_RESERVED BIT_3_4 // Reserved +#define PHY_100_ER0_REV_CNTRL BIT_5_7 // Revsion control (A step = 000) +#define PHY_100_ER0_FORCE_FAIL BIT_8 // Force Fail is enabled +#define PHY_100_ER0_TEST BIT_9_13 // Revsion control (A step = 000) +#define PHY_100_ER0_LINKDIS BIT_14 // Link integrity test is disabled +#define PHY_100_ER0_JABDIS BIT_15 // Jabber function is disabled + + +// PHY 100 Extended Register 1 bit definitions +#define PHY_100_ER1_RESERVED BIT_0_8 // Reserved +#define PHY_100_ER1_CH2_DET_ERR BIT_9 // Channel 2 EOF detection error +#define PHY_100_ER1_MANCH_CODE_ERR BIT_10 // Manchester code error +#define PHY_100_ER1_EOP_ERR BIT_11 // EOP error +#define PHY_100_ER1_BAD_CODE_ERR BIT_12 // bad code error +#define PHY_100_ER1_INV_CODE_ERR BIT_13 // invalid code error +#define PHY_100_ER1_DC_BAL_ERR BIT_14 // DC balance error +#define PHY_100_ER1_PAIR_SKEW_ERR BIT_15 // Pair skew error + +// PHY TX Register/Bit definitions +#define PHY_TX_STATUS_CTRL_REG 0x10 +#define PHY_TX_POLARITY_MASK BIT_8 // register 10h bit 8 (the polarity bit) +#define PHY_TX_NORMAL_POLARITY 0 // register 10h bit 8 =0 (normal polarity) + +#define PHY_TX_SPECIAL_CTRL_REG 0x11 +#define AUTO_POLARITY_DISABLE BIT_4 // register 11h bit 4 (0=enable, 1=disable) + +#define PHY_TX_REG_18 0x18 // Error counter register +// National Semiconductor TX phy congestion control register bit definitions +#define NSC_TX_CONG_TXREADY BIT_10 // Makes TxReady an input +#define NSC_TX_CONG_ENABLE BIT_8 // Enables congestion control +#define NSC_TX_CONG_F_CONNECT BIT_5 // Enables congestion control + +// National Semiconductor TX phy speed indication register bit definitions +#define NSC_TX_SPD_INDC_SPEED BIT_6 // 0 = 100mb, 1=10mb + +#endif // _E100_557_H + diff --git a/general/pcidrv/kmdf/HW/e100_equ.h b/general/pcidrv/kmdf/HW/e100_equ.h new file mode 100644 index 00000000..c1e0bbde --- /dev/null +++ b/general/pcidrv/kmdf/HW/e100_equ.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** COPYRIGHT (C) 1994-1997 INTEL CORPORATION ** +** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON ** +** HTTP://WWW.INTEL.COM/ ** +** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND ** +** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER ** +****************************************************************************/ + +/**************************************************************************** +Module Name: + e100_equ.h (equates.h) + +This driver runs on the following hardware: + - 82558 based PCI 10/100Mb ethernet adapters + (aka Intel EtherExpress(TM) PRO Adapters) + +Environment: + Kernel Mode - Or whatever is the equivalent on WinNT + +*****************************************************************************/ + +#ifndef _E100_EQU_H +#define _E100_EQU_H + +//------------------------------------------------------------------------- +// OEM Message Tags +//------------------------------------------------------------------------- +#define stringTag 0xFEFA // Length Byte After String +#define lStringTag 0xFEFB // Length Byte Before String +#define zStringTag 0xFEFC // Zero-Terminated String Tag +#define nStringTag 0xFEFD // No Length Byte Or 0-Term + +//------------------------------------------------------------------------- +// Adapter Types Supported +//------------------------------------------------------------------------- +#define FLASH32_EISA (0 * 4) +#define FLASH32_PCI (1 * 4) +#define D29C_EISA (2 * 4) +#define D29C_PCI (3 * 4) +#define D100_PCI (4 * 4) + +//------------------------------------------------------------------------- +// Phy related constants +//------------------------------------------------------------------------- +#define PHY_503 0 +#define PHY_100_A 0x000003E0 +#define PHY_100_C 0x035002A8 +#define PHY_TX_ID 0x015002A8 +#define PHY_NSC_TX 0x5c002000 +#define PHY_OTHER 0xFFFF + +#define PHY_MODEL_REV_ID_MASK 0xFFF0FFFF +#define PARALLEL_DETECT 0 +#define N_WAY 1 + +#define RENEGOTIATE_TIME 35 // (3.5 Seconds) + +#define CONNECTOR_AUTO 0 +#define CONNECTOR_TPE 1 +#define CONNECTOR_MII 2 + +//------------------------------------------------------------------------- +// Ethernet Frame Sizes +//------------------------------------------------------------------------- +#define ETHERNET_ADDRESS_LENGTH 6 +#define ETHERNET_HEADER_SIZE 14 +#define MINIMUM_ETHERNET_PACKET_SIZE 60 +#define MAXIMUM_ETHERNET_PACKET_SIZE 1514 + +#define MAX_MULTICAST_ADDRESSES 32 +#define TCB_BUFFER_SIZE 0XE0 // 224 +#define COALESCE_BUFFER_SIZE 2048 +#define ETH_MAX_COPY_LENGTH 0x80 // 128 + +// Make receive area 1536 for 16 bit alignment. +//#define RCB_BUFFER_SIZE MAXIMUM_ETHERNET_PACKET_SIZE +#define RCB_BUFFER_SIZE 1520 // 0x5F0 + +//- Area reserved for all Non Transmit command blocks +#define MAX_NON_TX_CB_AREA 512 + +//------------------------------------------------------------------------- +// Ndis/Adapter driver constants +//------------------------------------------------------------------------- +#define MAX_PHYS_DESC 16 +#define MAX_RECEIVE_DESCRIPTORS 1024 // 0x400 +#define NUM_RMD 10 + +//-------------------------------------------------------------------------- +// System wide Equates +//-------------------------------------------------------------------------- +#define MAX_NUMBER_OF_EISA_SLOTS 15 +#define MAX_NUMBER_OF_PCI_SLOTS 15 + +//-------------------------------------------------------------------------- +// Equates Added for NDIS 4 +//-------------------------------------------------------------------------- +#define NUM_BYTES_PROTOCOL_RESERVED_SECTION 16 +#define MAX_NUM_ALLOCATED_RFDS 64 +#define MIN_NUM_RFD 4 +#define MAX_ARRAY_SEND_PACKETS 8 +// limit our receive routine to indicating this many at a time +#define MAX_ARRAY_RECEIVE_PACKETS 16 +#define MAC_RESERVED_SWRFDPTR 0 +#define MAX_PACKETS_TO_ADD 32 + +//------------------------------------------------------------------------- +//- Miscellaneous Equates +//------------------------------------------------------------------------- +#define CR 0x0D // Carriage Return +#define LF 0x0A // Line Feed + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +#define DRIVER_NULL ((ULONG)0xffffffff) +#define DRIVER_ZERO 0 + +//------------------------------------------------------------------------- +// Bit Mask definitions +//------------------------------------------------------------------------- +#define BIT_0 0x0001 +#define BIT_1 0x0002 +#define BIT_2 0x0004 +#define BIT_3 0x0008 +#define BIT_4 0x0010 +#define BIT_5 0x0020 +#define BIT_6 0x0040 +#define BIT_7 0x0080 +#define BIT_8 0x0100 +#define BIT_9 0x0200 +#define BIT_10 0x0400 +#define BIT_11 0x0800 +#define BIT_12 0x1000 +#define BIT_13 0x2000 +#define BIT_14 0x4000 +#define BIT_15 0x8000 +#define BIT_24 0x01000000 +#define BIT_28 0x10000000 + +#define BIT_0_2 0x0007 +#define BIT_0_3 0x000F +#define BIT_0_4 0x001F +#define BIT_0_5 0x003F +#define BIT_0_6 0x007F +#define BIT_0_7 0x00FF +#define BIT_0_8 0x01FF +#define BIT_0_13 0x3FFF +#define BIT_0_15 0xFFFF +#define BIT_1_2 0x0006 +#define BIT_1_3 0x000E +#define BIT_2_5 0x003C +#define BIT_3_4 0x0018 +#define BIT_4_5 0x0030 +#define BIT_4_6 0x0070 +#define BIT_4_7 0x00F0 +#define BIT_5_7 0x00E0 +#define BIT_5_9 0x03E0 +#define BIT_5_12 0x1FE0 +#define BIT_5_15 0xFFE0 +#define BIT_6_7 0x00c0 +#define BIT_7_11 0x0F80 +#define BIT_8_10 0x0700 +#define BIT_9_13 0x3E00 +#define BIT_12_15 0xF000 +#define BIT_8_15 0xFF00 + +#define BIT_16_20 0x001F0000 +#define BIT_21_25 0x03E00000 +#define BIT_26_27 0x0C000000 + +// in order to make our custom oids hopefully somewhat unique +// we will use 0xFF (indicating implementation specific OID) +// A0 (first byte of non zero intel unique identifier) +// C9 (second byte of non zero intel unique identifier) +// XX (the custom OID number - providing 255 possible custom oids) +#define OID_CUSTOM_DRIVER_SET 0xFFA0C901 +#define OID_CUSTOM_DRIVER_QUERY 0xFFA0C902 +#define OID_CUSTOM_ARRAY 0xFFA0C903 +#define OID_CUSTOM_STRING 0xFFA0C904 + +#define CMD_BUS_MASTER BIT_2 + +#endif // _E100_EQU_H + diff --git a/general/pcidrv/kmdf/HW/eeprom.c b/general/pcidrv/kmdf/HW/eeprom.c new file mode 100644 index 00000000..42354f38 --- /dev/null +++ b/general/pcidrv/kmdf/HW/eeprom.c @@ -0,0 +1,306 @@ +/**************************************************************************** +** COPYRIGHT (C) 1994-1997 INTEL CORPORATION ** +** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON ** +** HTTP://WWW.INTEL.COM/ ** +** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND ** +** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER ** +****************************************************************************/ + +/**************************************************************************** +Module Name: + eeprom.c + +This driver runs on the following hardware: + - 82558 based PCI 10/100Mb ethernet adapters + (aka Intel EtherExpress(TM) PRO Adapters) + +Environment: + Kernel Mode - Or whatever is the equivalent on WinNT + +*****************************************************************************/ + +#include "precomp.h" + +#define EEPROM_MAX_SIZE 256 + +//***************************************************************************** +// +// I/O based Read EEPROM Routines +// +//***************************************************************************** + +//----------------------------------------------------------------------------- +// Procedure: EEpromAddressSize +// +// Description: determines the number of bits in an address for the eeprom +// acceptable values are 64, 128, and 256 +// +// Arguments: +// Size -- size of the eeprom +// +// Returns: +// bits in an address for that size eeprom +//----------------------------------------------------------------------------- + +USHORT GetEEpromAddressSize( + IN USHORT Size) +{ + switch (Size) + { + case 64: return 6; + case 128: return 7; + case 256: return 8; + } + + return 0; +} + +//----------------------------------------------------------------------------- +// Procedure: GetEEpromSize +// +// Description: This routine determines the size of the EEPROM. +// +// Arguments: +// Reg - EEPROM word to read. +// +// Returns: +// Size of the EEPROM, or zero if TRACE_LEVEL_ERROR. +//----------------------------------------------------------------------------- + +USHORT GetEEpromSize( + IN PFDO_DATA FdoData, + IN PUCHAR CSRBaseIoAddress) +{ + USHORT x, data; + USHORT size = 1; + + // select EEPROM, reset bits, set EECS + x = FdoData->ReadPort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG)); + + x &= ~(EEDI | EEDO | EESK); + x |= EECS; + FdoData->WritePort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG), x); + + // write the read opcode + ShiftOutBits(FdoData, EEPROM_READ_OPCODE, 3, CSRBaseIoAddress); + + // experiment to discover the size of the eeprom. request register zero + // and wait for the eeprom to tell us it has accepted the entire address. + x = FdoData->ReadPort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG)); + do + { + size *= 2; // each bit of address doubles eeprom size + x |= EEDO; // set bit to detect "dummy zero" + x &= ~EEDI; // address consists of all zeros + + FdoData->WritePort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG), x); + KeStallExecutionProcessor(100); + RaiseClock(FdoData, &x, CSRBaseIoAddress); + LowerClock(FdoData, &x, CSRBaseIoAddress); + + // check for "dummy zero" + x = FdoData->ReadPort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG)); + if (size > EEPROM_MAX_SIZE) + { + size = 0; + break; + } + } + while (x & EEDO); + + // Now read the data (16 bits) in from the selected EEPROM word + data = ShiftInBits(FdoData, CSRBaseIoAddress); + + EEpromCleanup(FdoData, CSRBaseIoAddress); + + return size; +} + +//----------------------------------------------------------------------------- +// Procedure: ReadEEprom +// +// Description: This routine serially reads one word out of the EEPROM. +// +// Arguments: +// Reg - EEPROM word to read. +// +// Returns: +// Contents of EEPROM word (Reg). +//----------------------------------------------------------------------------- + +USHORT ReadEEprom( + IN PFDO_DATA FdoData, + IN PUCHAR CSRBaseIoAddress, + IN USHORT Reg, + IN USHORT AddressSize) +{ + USHORT x; + USHORT data; + + // select EEPROM, reset bits, set EECS + x = FdoData->ReadPort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG)); + + x &= ~(EEDI | EEDO | EESK); + x |= EECS; + FdoData->WritePort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG), x); + + // write the read opcode and register number in that order + // The opcode is 3bits in length, reg is 6 bits long + ShiftOutBits(FdoData, EEPROM_READ_OPCODE, 3, CSRBaseIoAddress); + ShiftOutBits(FdoData, Reg, AddressSize, CSRBaseIoAddress); + + // Now read the data (16 bits) in from the selected EEPROM word + data = ShiftInBits(FdoData, CSRBaseIoAddress); + + EEpromCleanup(FdoData, CSRBaseIoAddress); + return data; +} + +//----------------------------------------------------------------------------- +// Procedure: ShiftOutBits +// +// Description: This routine shifts data bits out to the EEPROM. +// +// Arguments: +// data - data to send to the EEPROM. +// count - number of data bits to shift out. +// +// Returns: (none) +//----------------------------------------------------------------------------- + +VOID ShiftOutBits( + IN PFDO_DATA FdoData, + IN USHORT data, + IN USHORT count, + IN PUCHAR CSRBaseIoAddress) +{ + USHORT x,mask; + + mask = 0x01 << (count - 1); + x = FdoData->ReadPort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG)); + + x &= ~(EEDO | EEDI); + + do + { + x &= ~EEDI; + if(data & mask) + x |= EEDI; + + FdoData->WritePort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG), x); + KeStallExecutionProcessor(100); + RaiseClock(FdoData, &x, CSRBaseIoAddress); + LowerClock(FdoData, &x, CSRBaseIoAddress); + mask = mask >> 1; + } while(mask); + + x &= ~EEDI; + FdoData->WritePort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG), x); +} + +//----------------------------------------------------------------------------- +// Procedure: ShiftInBits +// +// Description: This routine shifts data bits in from the EEPROM. +// +// Arguments: +// +// Returns: +// The contents of that particular EEPROM word +//----------------------------------------------------------------------------- + +USHORT ShiftInBits( + IN PFDO_DATA FdoData, + IN PUCHAR CSRBaseIoAddress) +{ + USHORT x,d,i; + x = FdoData->ReadPort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG)); + + x &= ~( EEDO | EEDI); + d = 0; + + for(i=0; i<16; i++) + { + d = d << 1; + RaiseClock(FdoData, &x, CSRBaseIoAddress); + + x = FdoData->ReadPort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG)); + + x &= ~(EEDI); + if(x & EEDO) + d |= 1; + + LowerClock(FdoData, &x, CSRBaseIoAddress); + } + + return d; +} + +//----------------------------------------------------------------------------- +// Procedure: RaiseClock +// +// Description: This routine raises the EEPOM's clock input (EESK) +// +// Arguments: +// x - Ptr to the EEPROM control register's current value +// +// Returns: (none) +//----------------------------------------------------------------------------- + +VOID RaiseClock( + IN PFDO_DATA FdoData, + IN OUT USHORT *x, + IN PUCHAR CSRBaseIoAddress) +{ + *x = *x | EESK; + FdoData->WritePort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG), *x); + KeStallExecutionProcessor(100); +} + + +//----------------------------------------------------------------------------- +// Procedure: LowerClock +// +// Description: This routine lower's the EEPOM's clock input (EESK) +// +// Arguments: +// x - Ptr to the EEPROM control register's current value +// +// Returns: (none) +//----------------------------------------------------------------------------- + +VOID LowerClock( + IN PFDO_DATA FdoData, + IN OUT USHORT *x, + IN PUCHAR CSRBaseIoAddress) +{ + *x = *x & ~EESK; + FdoData->WritePort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG), *x); + KeStallExecutionProcessor(100); +} + +//----------------------------------------------------------------------------- +// Procedure: EEpromCleanup +// +// Description: This routine returns the EEPROM to an idle state +// +// Arguments: +// +// Returns: (none) +//----------------------------------------------------------------------------- + +VOID EEpromCleanup( + IN PFDO_DATA FdoData, + IN PUCHAR CSRBaseIoAddress) +{ + USHORT x; + x = FdoData->ReadPort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG)); + + x &= ~(EECS | EEDI); + FdoData->WritePort((PUSHORT)(CSRBaseIoAddress + CSR_EEPROM_CONTROL_REG), x); + + RaiseClock(FdoData, &x, CSRBaseIoAddress); + LowerClock(FdoData, &x, CSRBaseIoAddress); +} + + diff --git a/general/pcidrv/kmdf/HW/isrdpc.c b/general/pcidrv/kmdf/HW/isrdpc.c new file mode 100644 index 00000000..ebc2a3a6 --- /dev/null +++ b/general/pcidrv/kmdf/HW/isrdpc.c @@ -0,0 +1,904 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + ISRDPC.C + +Abstract: + + Contains routine to handle interrupts, interrupt DPCs and WatchDogTimer DPC + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" + +#if defined(EVENT_TRACING) +#include "ISRDPC.tmh" +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, NICEvtDeviceD0ExitPreInterruptsDisabled) +#endif + + +BOOLEAN +NICEvtInterruptIsr( + IN WDFINTERRUPT Interrupt, + IN ULONG MessageID + ) +/*++ +Routine Description: + + Interrupt handler for the device. + +Arguments: + + Interupt - Address of the framework interrupt object + MessageID - + +Return Value: + + TRUE if our device is interrupting, FALSE otherwise. + +--*/ +{ + BOOLEAN InterruptRecognized = FALSE; + PFDO_DATA FdoData = NULL; + USHORT IntStatus; + + UNREFERENCED_PARAMETER( MessageID ); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "--> NICEvtInterruptIsr\n"); + + FdoData = FdoGetData(WdfInterruptGetDevice(Interrupt)); + + // + // We process the interrupt if it's not disabled and it's active + // + if (!NIC_INTERRUPT_DISABLED(FdoData) && NIC_INTERRUPT_ACTIVE(FdoData)) + { + InterruptRecognized = TRUE; + + // + // Disable the interrupt (will be re-enabled in NICEvtInterruptDpc + // + NICDisableInterrupt(FdoData); + + // + // Acknowledge the interrupt(s) and get the interrupt status + // + + NIC_ACK_INTERRUPT(FdoData, IntStatus); + + WdfInterruptQueueDpcForIsr( Interrupt ); + + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "<-- NICEvtInterruptIsr\n"); + + return InterruptRecognized; +} + +VOID +NICEvtInterruptDpc( + IN WDFINTERRUPT WdfInterrupt, + IN WDFOBJECT WdfDevice + ) + +/*++ + +Routine Description: + + DPC callback for ISR. + +Arguments: + + WdfInterrupt - Handle to the framework interrupt object + + WdfDevice - Associated device object. + +Return Value: + +--*/ +{ + PFDO_DATA fdoData = NULL; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "--> NICEvtInterruptDpc\n"); + + fdoData = FdoGetData(WdfDevice); + + + WdfSpinLockAcquire(fdoData->RcvLock); + + NICHandleRecvInterrupt(fdoData); + + + WdfSpinLockRelease(fdoData->RcvLock); + + // + // Handle send interrupt + // + + WdfSpinLockAcquire(fdoData->SendLock); + + NICHandleSendInterrupt(fdoData); + + + WdfSpinLockRelease(fdoData->SendLock); + + // + // Check if any queued Sends need to be reprocessed. + // + NICCheckForQueuedSends(fdoData); + + // + // Start the receive unit if it had stopped + // + + WdfSpinLockAcquire(fdoData->RcvLock); + + NICStartRecv(fdoData); + + + WdfSpinLockRelease(fdoData->RcvLock); + + // + // Re-enable the interrupt (disabled in MPIsr) + // + WdfInterruptSynchronize( + WdfInterrupt, + NICEnableInterrupt, + fdoData); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<-- NICEvtInterruptDpc\n"); + +} + +NTSTATUS +NICEvtInterruptEnable( + IN WDFINTERRUPT Interrupt, + IN WDFDEVICE AssociatedDevice + ) +/*++ + +Routine Description: + + This event is called when the Framework moves the device to D0, and after + EvtDeviceD0Entry. The driver should enable its interrupt here. + + This function will be called at the device's assigned interrupt + IRQL (DIRQL.) + +Arguments: + + Interrupt - Handle to a Framework interrupt object. + + AssociatedDevice - Handle to a Framework device object. + +Return Value: + + BOOLEAN - TRUE indicates that the interrupt was successfully enabled. + +--*/ +{ + PFDO_DATA fdoData; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtInterruptEnable\n"); + + fdoData = FdoGetData(AssociatedDevice); + NICEnableInterrupt(Interrupt, fdoData); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtInterruptEnable\n"); + + return STATUS_SUCCESS; +} + +NTSTATUS +NICEvtInterruptDisable( + IN WDFINTERRUPT Interrupt, + IN WDFDEVICE AssociatedDevice + ) +/*++ + +Routine Description: + + This event is called before the Framework moves the device to D1, D2 or D3 + and before EvtDeviceD0Exit. The driver should disable its interrupt here. + + This function will be called at the device's assigned interrupt + IRQL (DIRQL.) + +Arguments: + + Interrupt - Handle to a Framework interrupt object. + + AssociatedDevice - Handle to a Framework device object. + +Return Value: + + STATUS_SUCCESS - indicates success. + +--*/ +{ + PFDO_DATA fdoData; + + UNREFERENCED_PARAMETER(Interrupt); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtInterruptDisable\n"); + + fdoData = FdoGetData(AssociatedDevice); + NICDisableInterrupt(fdoData); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtInterruptDisable\n"); + + return STATUS_SUCCESS; +} + +NTSTATUS +NICEvtDeviceD0EntryPostInterruptsEnabled( + IN WDFDEVICE Device, + IN WDF_POWER_DEVICE_STATE PreviousState + ) +/*++ + +Routine Description: + + This event is called so that driver can do PASSIVE_LEVEL work after + the interrupt is connected and enabled. Here we start the watchdog timer. + Watch dog timer is used to do the initial link detection during + start and then used to make sure the device is not stuck for any reason. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Interrupt - Handle to a Framework interrupt object. + + AssociatedDevice - Handle to a Framework device object. + +Return Value: + + STATUS_SUCCESS - indicates success. + +--*/ +{ + PFDO_DATA fdoData; + + UNREFERENCED_PARAMETER( PreviousState ); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtDeviceD0EntryPostInterruptsEnabled\n"); + + fdoData = FdoGetData(Device); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtDeviceD0EntryPostInterruptsEnabled\n"); + + return STATUS_SUCCESS; + +} + +NTSTATUS +NICEvtDeviceD0ExitPreInterruptsDisabled( + IN WDFDEVICE Device, + IN WDF_POWER_DEVICE_STATE TargetState + ) +/*++ + +Routine Description: + + This event is called so that driver can do PASSIVE_LEVEL work before + the interrupt is disconnected and disabled. + +Arguments: + + Interrupt - Handle to a Framework interrupt object. + + AssociatedDevice - Handle to a Framework device object. + +Return Value: + + STATUS_SUCCESS - indicates success. + +--*/ +{ + PFDO_DATA fdoData; + + UNREFERENCED_PARAMETER(TargetState); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtDeviceD0ExitPreInterruptsDisabled\n"); + + fdoData = FdoGetData(Device); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtDeviceD0ExitPreInterruptsDisabled\n"); + + return STATUS_SUCCESS; + +} + +VOID +NICStartWatchDogTimer( + IN PFDO_DATA FdoData + ) +{ + LARGE_INTEGER dueTime; + + if(!FdoData->CheckForHang){ + + // + // Set the link detection flag to indicate that NICWatchDogEvtTimerFunc + // is first doing link-detection. + // + MP_SET_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION); + FdoData->CheckForHang = FALSE; + FdoData->bLinkDetectionWait = FALSE; + FdoData->bLookForLink = FALSE; + dueTime.QuadPart = NIC_LINK_DETECTION_DELAY; + + } else { + dueTime.QuadPart = NIC_CHECK_FOR_HANG_DELAY; + } + + WdfTimerStart(FdoData->WatchDogTimer, + dueTime.QuadPart + ); + +} + +VOID +NICWatchDogEvtTimerFunc( + IN WDFTIMER Timer + ) +/*++ + +Routine Description: + + This DPC is used to do both link detection during hardware init and + after that for hardware hang detection. + +Arguments: + + +Return Value: + + None + +--*/ +{ + PFDO_DATA FdoData = NULL; + LARGE_INTEGER DueTime; + NTSTATUS status = STATUS_SUCCESS; + + FdoData = FdoGetData(WdfTimerGetParentObject(Timer)); + + DueTime.QuadPart = NIC_CHECK_FOR_HANG_DELAY; + + + if(!FdoData->CheckForHang){ + // + // We are still doing link detection + // + status = NICLinkDetection(FdoData); + if(status == STATUS_PENDING) { + // Wait for 100 ms + FdoData->bLinkDetectionWait = TRUE; + DueTime.QuadPart = NIC_LINK_DETECTION_DELAY; + }else { + FdoData->CheckForHang = TRUE; + } + }else { + // + // Link detection is over, let us check to see + // if the hardware is stuck. + // + if(NICCheckForHang(FdoData)){ + + status = NICReset(FdoData); + if(!NT_SUCCESS(status)){ + goto Exit; + } + } + } + + WdfTimerStart(FdoData->WatchDogTimer, // Timer + DueTime.QuadPart // DueTime + ); + + return; + +Exit: + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "WatchDogTimer is exiting %x\n", status); + return; + +} + +BOOLEAN +NICCheckForHang( + IN PFDO_DATA FdoData + ) +/*++ + +Routine Description: + + CheckForHang handler is called in the context of a timer DPC. + take advantage of this fact when acquiring/releasing spinlocks + +Arguments: + + FdoData Pointer to our adapter + +Return Value: + + TRUE This NIC needs a reset + FALSE Everything is fine + +--*/ +{ + PMP_TCB pMpTcb; + + // + // Just skip this part if the adapter is doing link detection + // + if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION)) + { + return(FALSE); + } + + // + // any nonrecoverable hardware error? + // + if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_NON_RECOVER_ERROR)) + { + TraceEvents(TRACE_LEVEL_WARNING, DBG_DPC, "Non recoverable error - remove\n"); + return (TRUE); + } + + // + // hardware failure? + // + if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_HARDWARE_ERROR)) + { + TraceEvents(TRACE_LEVEL_WARNING, DBG_DPC, "hardware error - reset\n"); + return(TRUE); + } + + // + // Is send stuck? + // + + + WdfSpinLockAcquire(FdoData->SendLock); + + if (FdoData->nBusySend > 0) + { + pMpTcb = FdoData->CurrSendHead; + pMpTcb->Count++; + if (pMpTcb->Count > NIC_SEND_HANG_THRESHOLD) + { + + WdfSpinLockRelease(FdoData->SendLock); + TraceEvents(TRACE_LEVEL_WARNING, DBG_DPC, "Send is stuck - reset\n"); + return(TRUE); + } + } + + + WdfSpinLockRelease(FdoData->SendLock); + + + WdfSpinLockAcquire(FdoData->RcvLock); + + // + // Update the RFD shrink count + // + if (FdoData->CurrNumRfd > FdoData->NumRfd) + { + FdoData->RfdShrinkCount++; + } + + + WdfSpinLockRelease(FdoData->RcvLock); + + NICIndicateMediaState(FdoData); + + return(FALSE); +} + +NTSTATUS +NICReset( + IN PFDO_DATA FdoData + ) +/*++ + +Routine Description: + + Function to reset the device. + +Arguments: + + FdoData Pointer to our adapter + + +Return Value: + + NT Status code. + +Note: + NICReset is called at DPC. Take advantage of this fact + when acquiring or releasing spinlocks + +--*/ +{ + NTSTATUS status; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "---> MPReset\n"); + + + WdfSpinLockAcquire(FdoData->Lock); + + WdfSpinLockAcquire(FdoData->SendLock); + + WdfSpinLockAcquire(FdoData->RcvLock); + + do + { + // + // Is this adapter already doing a reset? + // + if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS)) + { + status = STATUS_SUCCESS; + goto exit; + } + + MP_SET_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS); + + // + // Is this adapter doing link detection? + // + if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION)) + { + TraceEvents(TRACE_LEVEL_WARNING, DBG_DPC, "Reset is pended...\n"); + status = STATUS_SUCCESS; + goto exit; + } + // + // Is this adapter going to be removed + // + if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_NON_RECOVER_ERROR)) + { + status = STATUS_DEVICE_DATA_ERROR; + if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_REMOVE_IN_PROGRESS)) + { + goto exit; + } + + // This is an unrecoverable hardware failure. + // We need to tell NDIS to remove this miniport + MP_SET_FLAG(FdoData, fMP_ADAPTER_REMOVE_IN_PROGRESS); + MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS); + + + WdfSpinLockRelease(FdoData->RcvLock); + + WdfSpinLockRelease(FdoData->SendLock); + + WdfSpinLockRelease(FdoData->Lock); + + // TODO: Log an entry into the eventlog + WdfDeviceSetFailed(FdoData->WdfDevice, WdfDeviceFailedAttemptRestart); + + TraceEvents(TRACE_LEVEL_FATAL, DBG_DPC, "<--- MPReset, status=%x\n", status); + + return status; + } + + + // + // Disable the interrupt and issue a reset to the NIC + // + NICDisableInterrupt(FdoData); + NICIssueSelectiveReset(FdoData); + + + // + // Release all the locks and then acquire back the send lock + // we are going to clean up the send queues + // which may involve calling Ndis APIs + // release all the locks before grabbing the send lock to + // avoid deadlocks + // + + + WdfSpinLockRelease(FdoData->RcvLock); + + WdfSpinLockRelease(FdoData->SendLock); + + WdfSpinLockRelease(FdoData->Lock); + + + WdfSpinLockAcquire(FdoData->SendLock); + + // + // Free the packets on SendQueueList + // + NICFreeQueuedSendPackets(FdoData); + + // + // Free the packets being actively sent & stopped + // + NICFreeBusySendPackets(FdoData); + + + RtlZeroMemory(FdoData->MpTcbMem, FdoData->MpTcbMemSize); + + // + // Re-initialize the send structures + // + NICInitSendBuffers(FdoData); + + + WdfSpinLockRelease(FdoData->SendLock); + + // + // get all the locks again in the right order + // + + + WdfSpinLockAcquire(FdoData->Lock); + + WdfSpinLockAcquire(FdoData->SendLock); + + WdfSpinLockAcquire(FdoData->RcvLock); + + // + // Reset the RFD list and re-start RU + // + NICResetRecv(FdoData); + status = NICStartRecv(FdoData); + if (status != STATUS_SUCCESS) + { + // Are we having failures in a few consecutive resets? + if (FdoData->HwErrCount < NIC_HARDWARE_ERROR_THRESHOLD) + { + // It's not over the threshold yet, let it to continue + FdoData->HwErrCount++; + } + else + { + // This is an unrecoverable hardware failure. + // We need to tell NDIS to remove this miniport + MP_SET_FLAG(FdoData, fMP_ADAPTER_REMOVE_IN_PROGRESS); + MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS); + + + + WdfSpinLockRelease(FdoData->RcvLock); + + WdfSpinLockRelease(FdoData->SendLock); + + WdfSpinLockRelease(FdoData->Lock); + + // TODO: Log an entry into the eventlog + // + // Tell the system that the device has failed. + // + WdfDeviceSetFailed(FdoData->WdfDevice, WdfDeviceFailedAttemptRestart); + + TraceEvents(TRACE_LEVEL_ERROR, DBG_DPC, "<--- MPReset, status=%x\n", status); + return(status); + } + + break; + } + + FdoData->HwErrCount = 0; + MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_HARDWARE_ERROR); + + NICEnableInterrupt(FdoData->WdfInterrupt, FdoData); + + } WHILE (FALSE); + + MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS); + + exit: + + + WdfSpinLockRelease(FdoData->RcvLock); + + WdfSpinLockRelease(FdoData->SendLock); + + WdfSpinLockRelease(FdoData->Lock); + + + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<--- MPReset, status=%x\n", status); + return(status); +} + + +NTSTATUS +NICLinkDetection( + PFDO_DATA FdoData + ) +/*++ + +Routine Description: + + Timer function for postponed link negotiation. Called from + the NICWatchDogEvtTimerFunc. After the link detection is over + we will complete any pending ioctl or send IRPs. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + NT status + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + MEDIA_STATE CurrMediaState; + PNDISPROT_QUERY_OID pQuery = NULL; + PNDISPROT_SET_OID pSet = NULL; + PVOID DataBuffer; + ULONG BytesWritten; + NDIS_OID Oid; + PVOID InformationBuffer; + size_t bufSize; + WDFREQUEST request; + + // + // Handle the link negotiation. + // + if (FdoData->bLinkDetectionWait) + { + status = ScanAndSetupPhy(FdoData); + } + else + { + status = PhyDetect(FdoData); + } + + if (status == STATUS_PENDING) + { + return status; + } + + // + // Reset some variables for link detection + // + FdoData->bLinkDetectionWait = FALSE; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "NICLinkDetection - negotiation done\n"); + + + WdfSpinLockAcquire(FdoData->Lock); + MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION); + + WdfSpinLockRelease(FdoData->Lock); + + // + // Any OID query request pending? + // + + status = NICGetIoctlRequest(FdoData->PendingIoctlQueue, + IOCTL_NDISPROT_QUERY_OID_VALUE, + &request); + + if(NT_SUCCESS(status)) { + status = WdfRequestRetrieveOutputBuffer(request, sizeof(NDISPROT_QUERY_OID), &DataBuffer, &bufSize); + if(NT_SUCCESS(status)) { + + pQuery = (PNDISPROT_QUERY_OID)DataBuffer; + Oid = pQuery->Oid; + InformationBuffer = &pQuery->Data[0]; + switch(Oid) + { + case OID_GEN_LINK_SPEED: + *((PULONG)InformationBuffer) = FdoData->usLinkSpeed * 10000; + BytesWritten = sizeof(ULONG); + + break; + + case OID_GEN_MEDIA_CONNECT_STATUS: + default: + ASSERT(Oid == OID_GEN_MEDIA_CONNECT_STATUS); + + CurrMediaState = NICIndicateMediaState(FdoData); + + RtlMoveMemory(InformationBuffer, + &CurrMediaState, + sizeof(NDIS_MEDIA_STATE)); + + BytesWritten = sizeof(NDIS_MEDIA_STATE); + } + + WdfRequestCompleteWithInformation(request, status, BytesWritten); + } + } + + // + // Any OID set request pending? + // + status = NICGetIoctlRequest(FdoData->PendingIoctlQueue, + IOCTL_NDISPROT_SET_OID_VALUE, + &request); + + if(NT_SUCCESS(status)) { + ULONG PacketFilter; + + status = WdfRequestRetrieveOutputBuffer(request, sizeof(NDISPROT_SET_OID), &DataBuffer, &bufSize); + if(NT_SUCCESS(status)) { + + pSet = (PNDISPROT_SET_OID)DataBuffer; + Oid = pSet->Oid; + InformationBuffer = &pSet->Data[0]; + if (Oid == OID_GEN_CURRENT_PACKET_FILTER) + { + + RtlMoveMemory(&PacketFilter, InformationBuffer, sizeof(ULONG)); + + + WdfSpinLockAcquire(FdoData->Lock); + + status = NICSetPacketFilter( + FdoData, + PacketFilter); + + + WdfSpinLockRelease(FdoData->Lock); + + if (status == STATUS_SUCCESS) + { + FdoData->PacketFilter = PacketFilter; + } + + WdfRequestCompleteWithInformation(request, status, 0); + } + } + } + + // + // Any read pending? + // + + WdfSpinLockAcquire(FdoData->RcvLock); + + // + // Start the NIC receive unit + // + status = NICStartRecv(FdoData); + if (status != STATUS_SUCCESS) + { + MP_SET_HARDWARE_ERROR(FdoData); + } + + + WdfSpinLockRelease(FdoData->RcvLock); + + // + // Send packets which have been queued while link detection was going on. + // + NICCheckForQueuedSends(FdoData); + + return status; +} + + diff --git a/general/pcidrv/kmdf/HW/localwpp.ini b/general/pcidrv/kmdf/HW/localwpp.ini new file mode 100644 index 00000000..c290070a --- /dev/null +++ b/general/pcidrv/kmdf/HW/localwpp.ini @@ -0,0 +1,17 @@ +// +// This defines how to log a len/buffer pair. +// This function should be in trace.h +// + +DEFINE_CPLX_TYPE(HEXDUMP, WPP_LOGHEXDUMP, xstr_t, ItemHEXDump,"s", _HEX_, 0,2); + +// DEFINE_CPLX_TYPE( +// name, // i.e. HEXDUMP // %!HEXDUMP! +// macro, // i.e. WPP_LOGHEXDUMP // Marshalling macro, defined in trace.h +// structure, // i.e. xstr_t // Argument type (structure to be created by above macro) +// item type, // i.e. ItemHEXDump // MOF type that TracePrt can understand +// format specifier, // i.e. "s" // a format specifier that TracePrt can understand +// ???? // i.e. _HEX_ // Type signature (becomes a part of function name) +// ???? // i.e. 0 // Weight (0 is variable data length) +// ???? // i.e. 2 // Slots used by this entry (optional, 1 default) +// ) diff --git a/general/pcidrv/kmdf/HW/macros.h b/general/pcidrv/kmdf/HW/macros.h new file mode 100644 index 00000000..61ed01d6 --- /dev/null +++ b/general/pcidrv/kmdf/HW/macros.h @@ -0,0 +1,312 @@ +/**************************************************************************** +** COPYRIGHT (C) 1994-1997 INTEL CORPORATION ** +** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON ** +** HTTP://WWW.INTEL.COM/ ** +** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND ** +** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER ** +****************************************************************************/ + +/**************************************************************************** +Module Name: + macros.h (inlinef.h) + +This driver runs on the following hardware: + - 82558 based PCI 10/100Mb ethernet adapters + (aka Intel EtherExpress(TM) PRO Adapters) + +Environment: + Kernel Mode - Or whatever is the equivalent on WinNT + +*****************************************************************************/ + +__inline BOOLEAN +WaitScb( + IN PFDO_DATA FdoData + ); + + +//----------------------------------------------------------------------------- +// Procedure: D100IssueScbCommand +// +// Description: This general routine will issue a command to the D100. +// +// Arguments: +// FdoData - ptr to FdoData object instance. +// ScbCommand - The command that is to be issued +// WaitForSCB - A boolean value indicating whether or not a wait for SCB +// must be done before the command is issued to the chip +// +// Returns: +// TRUE if the command was issued to the chip successfully +// FALSE if the command was not issued to the chip +//----------------------------------------------------------------------------- +__inline NTSTATUS +D100IssueScbCommand( + IN PFDO_DATA FdoData, + IN UCHAR ScbCommandLow, + IN BOOLEAN WaitForScb + ) +{ + if(WaitForScb == TRUE) + { + if(!WaitScb(FdoData)) + { + return(STATUS_DEVICE_DATA_ERROR); + } + } + + FdoData->CSRAddress->ScbCommandLow = ScbCommandLow; + + return(STATUS_SUCCESS); +} + + +__inline NTSTATUS +MP_GET_STATUS_FROM_FLAGS( + IN PFDO_DATA FdoData + ) +{ + NTSTATUS Status = STATUS_UNSUCCESSFUL; + + if(MP_TEST_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS)) + { + Status = STATUS_DEVICE_NOT_READY; + } + else if(MP_TEST_FLAG(FdoData, fMP_ADAPTER_HARDWARE_ERROR)) + { + Status = STATUS_DEVICE_OFF_LINE; + } + else if(MP_TEST_FLAG(FdoData, fMP_ADAPTER_NO_CABLE)) + { + Status = STATUS_DEVICE_NOT_CONNECTED; + } + + return Status; +} + +__inline VOID +NICDisableInterrupt( + IN PFDO_DATA FdoData + ) +{ + FdoData->CSRAddress->ScbCommandHigh = SCB_INT_MASK; +} + +EVT_WDF_INTERRUPT_SYNCHRONIZE NICEnableInterrupt; + +__inline BOOLEAN NICEnableInterrupt( + IN WDFINTERRUPT WdfInterrupt, + IN WDFCONTEXT Context + ) +{ + PFDO_DATA FdoData = (PFDO_DATA)Context; + + UNREFERENCED_PARAMETER(WdfInterrupt); + + FdoData->CSRAddress->ScbCommandHigh = 0; + + return TRUE; +} + +__inline +BOOLEAN +IsPoMgmtSupported( + IN PFDO_DATA FdoData + ) +{ + + if (FdoData->RevsionID >= E100_82559_A_STEP + /*&& FdoData->RevsionID <= E100_82559_C_STEP*/) + { + return TRUE; + } + else + { + return FALSE; + } + +} + +__inline +USHORT +NICReadPortUShort ( + IN USHORT * x + ) +{ + return READ_PORT_USHORT (x); +} +__inline +VOID +NICWritePortUShort ( + IN USHORT * x, + IN USHORT y + ) +{ + WRITE_PORT_USHORT (x,y); +} + +__inline +USHORT +NICReadRegisterUShort ( + IN USHORT * x + ) +{ + return READ_REGISTER_USHORT (x); +} + +__inline +VOID +NICWriteRegisterUShort ( + IN USHORT * x, + IN USHORT y + ) +{ + WRITE_REGISTER_USHORT (x,y); +} + + + +// routines.c + +BOOLEAN +MdiRead( + IN PFDO_DATA Adapter, + IN ULONG RegAddress, + IN ULONG PhyAddress, + IN BOOLEAN Recoverable, + IN OUT PUSHORT DataValue + ); + +VOID +MdiWrite( + IN PFDO_DATA FdoData, + IN ULONG RegAddress, + IN ULONG PhyAddress, + IN USHORT DataValue + ); + +NTSTATUS +D100IssueScbCommand( + IN PFDO_DATA FdoData, + IN UCHAR ScbCommandLow, + IN BOOLEAN WaitForScb + ); + +MEDIA_STATE +GetMediaState( + IN PFDO_DATA Adapter + ); + +NTSTATUS +D100SubmitCommandBlockAndWait( + IN PFDO_DATA Adapter + ); + +VOID +NICIssueFullReset( + PFDO_DATA Adapter + ); + +VOID +NICIssueSelectiveReset( + PFDO_DATA Adapter + ); + +VOID +DumpStatsCounters( + IN PFDO_DATA Adapter + ); + + + +// physet.c + +VOID +ResetPhy( + IN PFDO_DATA FdoData + ); + +NTSTATUS +PhyDetect( + IN PFDO_DATA FdoData + ); + +NTSTATUS +ScanAndSetupPhy( + IN PFDO_DATA FdoData + ); + +VOID +SelectPhy( + IN PFDO_DATA FdoData, + IN UINT SelectPhyAddress, + IN BOOLEAN WaitAutoNeg + ); + +NTSTATUS +SetupPhy( + IN PFDO_DATA FdoData + ); + +VOID +FindPhySpeedAndDpx( + IN PFDO_DATA FdoData, + IN UINT PhyId + ); + + + +// eeprom.c +USHORT +GetEEpromAddressSize( + IN USHORT Size + ); + +USHORT +GetEEpromSize( + IN PFDO_DATA FdoData, + IN PUCHAR CSRBaseIoAddress + ); + +USHORT +ReadEEprom( + IN PFDO_DATA FdoData, + IN PUCHAR CSRBaseIoAddress, + IN USHORT Reg, + IN USHORT AddressSize + ); + +VOID +ShiftOutBits( + IN PFDO_DATA FdoData, + IN USHORT data, + IN USHORT count, + IN PUCHAR CSRBaseIoAddress + ); + +USHORT +ShiftInBits( + IN PFDO_DATA FdoData, + IN PUCHAR CSRBaseIoAddress + ); + +VOID +RaiseClock( + IN PFDO_DATA FdoData, + IN OUT USHORT *x, + IN PUCHAR CSRBaseIoAddress + ); + +VOID +LowerClock( + IN PFDO_DATA FdoData, + IN OUT USHORT *x, + IN PUCHAR CSRBaseIoAddress + ); + +VOID +EEpromCleanup( + IN PFDO_DATA FdoData, + IN PUCHAR CSRBaseIoAddress + ); + diff --git a/general/pcidrv/kmdf/HW/nic_def.h b/general/pcidrv/kmdf/HW/nic_def.h new file mode 100644 index 00000000..6e129be0 --- /dev/null +++ b/general/pcidrv/kmdf/HW/nic_def.h @@ -0,0 +1,800 @@ +/**************************************************************************** +** COPYRIGHT (C) 1994-1997 INTEL CORPORATION ** +** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON ** +** HTTP://WWW.INTEL.COM/ ** +** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND ** +** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER ** +****************************************************************************/ + + +#ifndef _NIC_DEF_H +#define _NIC_DEF_H + +#if !defined(WIN2K) + +#include "xfilter.h" // for ETH_* macros + +#else + +#define ETH_LENGTH_OF_ADDRESS 6 + +// +// ZZZ This is a little-endian specific check. +// +#define ETH_IS_MULTICAST(Address) \ + (BOOLEAN)(((PUCHAR)(Address))[0] & ((UCHAR)0x01)) + +// +// Check whether an address is broadcast. +// +#define ETH_IS_BROADCAST(Address) \ + ((((PUCHAR)(Address))[0] == ((UCHAR)0xff)) && (((PUCHAR)(Address))[1] == ((UCHAR)0xff))) + +// +// This macro is used to copy from one network address to +// another. +// +#define ETH_COPY_NETWORK_ADDRESS(_D, _S) \ +{ \ + *((ULONG UNALIGNED *)(_D)) = *((ULONG UNALIGNED *)(_S)); \ + *((USHORT UNALIGNED *)((UCHAR *)(_D)+4)) = *((USHORT UNALIGNED *)((UCHAR *)(_S)+4)); \ +} + +#endif + + + +// packet and header sizes +#define NIC_MAX_PACKET_SIZE 1514 +#define NIC_MIN_PACKET_SIZE 60 +#define NIC_HEADER_SIZE 14 + +// multicast list size +#define NIC_MAX_MCAST_LIST 32 + + +// media type, we use ethernet, change if necessary +#define NIC_MEDIA_TYPE NdisMedium802_3 + +#define NIC_INTERRUPT_MODE NdisInterruptLevelSensitive + +// NIC PCI Device and vendor IDs +#define NIC_PCI_DEVICE_ID 0x1229 +#define NIC_PCI_VENDOR_ID 0x8086 + + // IO space length +#define NIC_MAP_IOSPACE_LENGTH sizeof(CSR_STRUC) + +// PCS config space including the Device Specific part of it/ +#define NIC_PCI_E100_HDR_LENGTH 0xe2 + +// define some types for convenience +// TXCB_STRUC, RFD_STRUC and CSR_STRUC are hardware specific structures +// hardware TCB (Transmit Control Block) structure +typedef TXCB_STRUC HW_TCB; +typedef PTXCB_STRUC PHW_TCB; + +// hardware RFD (Receive Frame Descriptor) structure +typedef RFD_STRUC HW_RFD; +typedef PRFD_STRUC PHW_RFD; + +// hardware CSR (Control Status Register) structure +typedef CSR_STRUC HW_CSR; +typedef PCSR_STRUC PHW_CSR; +// change to your company name instead of using Microsoft +#define NIC_VENDOR_DESC "Microsoft" + +// number of TCBs per processor - min, default and max +#define NIC_MIN_TCBS 1 +#define NIC_DEF_TCBS 32 +#define NIC_MAX_TCBS 64 + +// max number of physical fragments supported per TCB +#define NIC_MAX_PHYS_BUF_COUNT 8 + +// number of RFDs - min, default and max +#define NIC_MIN_RFDS 4 +#define NIC_DEF_RFDS 20 +#define NIC_MAX_RFDS 1024 + +// only grow the RFDs up to this number +#define NIC_MAX_GROW_RFDS 128 + +// How many intervals before the RFD list is shrinked? +#define NIC_RFD_SHRINK_THRESHOLD 10 + +// local data buffer size (to copy send packet data into a local buffer) +#define NIC_BUFFER_SIZE 1520 + +// max lookahead size +#define NIC_MAX_LOOKAHEAD (NIC_MAX_PACKET_SIZE - NIC_HEADER_SIZE) + +// max number of send packets the MiniportSendPackets function can accept +#define NIC_MAX_SEND_PACKETS 10 + +// supported filters +#define NIC_SUPPORTED_FILTERS ( \ + NDIS_PACKET_TYPE_DIRECTED | \ + NDIS_PACKET_TYPE_MULTICAST | \ + NDIS_PACKET_TYPE_BROADCAST | \ + NDIS_PACKET_TYPE_PROMISCUOUS | \ + NDIS_PACKET_TYPE_ALL_MULTICAST) + +// Threshold for a remove +#define NIC_HARDWARE_ERROR_THRESHOLD 5 + +// The CheckForHang intervals before we decide the send is stuck +#define NIC_SEND_HANG_THRESHOLD 5 + +// NIC specific macros +#define NIC_RFD_GET_STATUS(_HwRfd) ((_HwRfd)->RfdCbHeader.CbStatus) +#define NIC_RFD_STATUS_COMPLETED(_Status) ((_Status) & RFD_STATUS_COMPLETE) +#define NIC_RFD_STATUS_SUCCESS(_Status) ((_Status) & RFD_STATUS_OK) +#define NIC_RFD_GET_PACKET_SIZE(_HwRfd) (((_HwRfd)->RfdActualCount) & RFD_ACT_COUNT_MASK) +#define NIC_RFD_VALID_ACTUALCOUNT(_HwRfd) ((((_HwRfd)->RfdActualCount) & (RFD_EOF_BIT | RFD_F_BIT)) == (RFD_EOF_BIT | RFD_F_BIT)) + +#define ListNext(_pL) (_pL)->Flink + +#define ListPrev(_pL) (_pL)->Blink + +// Constants for various purposes of KeStallExecutionProcessor + +#define NIC_DELAY_POST_RESET 20 +// Wait 5 milliseconds for the self-test to complete +#define NIC_DELAY_POST_SELF_TEST_MS 5 + + +// delay used for link detection to minimize the init time +// change this value to match your hardware +#define NIC_LINK_DETECTION_DELAY ((LONGLONG) -MILLISECONDS_TO_100NS * 100) // 100ms +#define NIC_CHECK_FOR_HANG_DELAY ((LONGLONG) -MILLISECONDS_TO_100NS * 400) // 400ms + +// MP_TCB flags +#define fMP_TCB_IN_USE 0x00000001 +#define fMP_TCB_USE_LOCAL_BUF 0x00000002 +#define fMP_TCB_MULTICAST 0x00000004 // a hardware workaround using multicast + +// MP_RFD flags +#define fMP_RFD_RECV_PEND 0x00000001 +#define fMP_RFD_ALLOC_PEND 0x00000002 +#define fMP_RFD_RECV_READY 0x00000004 +#define fMP_RFD_RESOURCES 0x00000008 + +// MP_ADAPTER flags +#define fMP_ADAPTER_SCATTER_GATHER 0x00000001 // obsolete +#define fMP_ADAPTER_RECV_LOOKASIDE 0x00000004 +#define fMP_ADAPTER_INTERRUPT_IN_USE 0x00000008 + +#define fMP_ADAPTER_NON_RECOVER_ERROR 0x00800000 + +#define fMP_ADAPTER_RESET_IN_PROGRESS 0x01000000 +#define fMP_ADAPTER_NO_CABLE 0x02000000 +#define fMP_ADAPTER_HARDWARE_ERROR 0x04000000 +#define fMP_ADAPTER_REMOVE_IN_PROGRESS 0x08000000 +#define fMP_ADAPTER_HALT_IN_PROGRESS 0x10000000 + +#define fMP_ADAPTER_LINK_DETECTION 0x20000000 + +#define NIC_INTERRUPT_DISABLED(_adapter) \ + (_adapter->CSRAddress->ScbCommandHigh & SCB_INT_MASK) + +#define NIC_INTERRUPT_ACTIVE(_adapter) \ + (((_adapter->CSRAddress->ScbStatus & SCB_ALL_INTERRUPT_BITS) != SCB_ALL_INTERRUPT_BITS) \ + && (_adapter->CSRAddress->ScbStatus & SCB_ACK_MASK)) + + +#define NIC_ACK_INTERRUPT(_adapter, _value) { \ + _value = _adapter->CSRAddress->ScbStatus & SCB_ACK_MASK; \ + _adapter->CSRAddress->ScbStatus = _value; } + +#define NIC_IS_RECV_READY(_adapter) \ + ((_adapter->CSRAddress->ScbStatus & SCB_RUS_MASK) == SCB_RUS_READY) + +//------------------------------------------------------------------------- +// NON_TRANSMIT_CB -- Generic Non-Transmit Command Block +//------------------------------------------------------------------------- +typedef struct _NON_TRANSMIT_CB +{ + union + { + MULTICAST_CB_STRUC Multicast; + CONFIG_CB_STRUC Config; + IA_CB_STRUC Setup; + DUMP_CB_STRUC Dump; + FILTER_CB_STRUC Filter; + } NonTxCb; + +} NON_TRANSMIT_CB, *PNON_TRANSMIT_CB; + +typedef enum _MEDIA_STATE { + + Connected = 0, + Disconnected + +} MEDIA_STATE; + +#define ALIGN_16 16 + +// +// The driver should put the data(after Ethernet header) at 8-bytes boundary +// +#define ETH_DATA_ALIGN 8 // the data(after Ethernet header) should be 8-byte aligned +// +// Shift HW_RFD 0xA bytes to make Tcp data 8-byte aligned +// Since the ethernet header is 14 bytes long. If a packet is at 0xA bytes +// offset, its data(ethernet user data) will be at 8 byte boundary +// +#define HWRFD_SHIFT_OFFSET 0xA // Shift HW_RFD 0xA bytes to make Tcp data 8-byte aligned + +// +// The driver has to allocate more data then HW_RFD needs to allow shifting data +// +#define MORE_DATA_FOR_ALIGN (ETH_DATA_ALIGN + HWRFD_SHIFT_OFFSET) +// +// Get a 8-bytes aligned memory address from a given the memory address. +// If the given address is not 8-bytes aligned, return the closest bigger memory address +// which is 8-bytes aligned. +// +#define DATA_ALIGN(_Va) ((PVOID)(((ULONG_PTR)(_Va) + (ETH_DATA_ALIGN - 1)) & ~(ETH_DATA_ALIGN - 1))) +// +// Get the number of bytes the final address shift from the original address +// +#define BYTES_SHIFT(_NewVa, _OrigVa) ((PUCHAR)(_NewVa) - (PUCHAR)(_OrigVa)) + +#define ETH_IS_LOCALLY_ADMINISTERED(Address) \ + (BOOLEAN)(((PUCHAR)(Address))[0] & ((UCHAR)0x02)) + + +//-------------------------------------- +// Some utility macros +//-------------------------------------- +#ifndef min +#define min(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif + +#ifndef max +#define max(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif + +#define MP_ALIGNMEM(_p, _align) (((_align) == 0) ? (_p) : (PUCHAR)(((ULONG_PTR)(_p) + ((_align)-1)) & (~((ULONG_PTR)(_align)-1)))) +#define MP_ALIGNMEM_PHYS(_p, _align) (((_align) == 0) ? (_p) : (((ULONG)(_p) + ((_align)-1)) & (~((ULONG)(_align)-1)))) +#define MP_ALIGNMEM_PA(_p, _align) (((_align) == 0) ? (_p).QuadPart : (((_p).QuadPart + ((_align)-1)) & (~((ULONGLONG)(_align)-1)))) + +#define GetListHeadEntry(ListHead) ((ListHead)->Flink) +#define GetListTailEntry(ListHead) ((ListHead)->Blink) +#define GetListFLink(ListEntry) ((ListEntry)->Flink) + +#define IsSListEmpty(ListHead) (((PSINGLE_LIST_ENTRY)ListHead)->Next == NULL) + +//-------------------------------------- +// Macros for flag and ref count operations +//-------------------------------------- +#define MP_SET_FLAG(_M, _F) ((_M)->Flags |= (_F)) +#define MP_CLEAR_FLAG(_M, _F) ((_M)->Flags &= ~(_F)) +#define MP_CLEAR_FLAGS(_M) ((_M)->Flags = 0) +#define MP_TEST_FLAG(_M, _F) (((_M)->Flags & (_F)) != 0) +#define MP_TEST_FLAGS(_M, _F) (((_M)->Flags & (_F)) == (_F)) + +#if 0 // Not implemented + +//-------------------------------------- +// Coalesce Tx buffer for local data copying +//-------------------------------------- +typedef struct _MP_TXBUF +{ + SINGLE_LIST_ENTRY SList; + PMDL Mdl; + + ULONG AllocSize; + PVOID AllocVa; + PHYSICAL_ADDRESS AllocLa; // Logical Address + + PUCHAR pBuffer; + PHYSICAL_ADDRESS BufferLa; // Logical Address + ULONG BufferSize; + +} MP_TXBUF, *PMP_TXBUF; + +#endif + +//-------------------------------------- +// TCB (Transmit Control Block) +//-------------------------------------- +typedef struct _MP_TCB +{ + struct _MP_TCB *Next; + ULONG Flags; + ULONG Count; + WDFDMATRANSACTION DmaTransaction; + + PHW_TCB HwTcb; // ptr to HW TCB VA + ULONG HwTcbPhys; // ptr to HW TCB PA + PHW_TCB PrevHwTcb; // ptr to previous HW TCB VA + + PTBD_STRUC HwTbd; // ptr to first TBD + ULONG HwTbdPhys; // ptr to first TBD PA + +} MP_TCB, *PMP_TCB; + +//-------------------------------------- +// RFD (Receive Frame Descriptor) +//-------------------------------------- +typedef struct _MP_RFD +{ + LIST_ENTRY List; + PVOID Buffer; // Pointer to Buffer + PMDL Mdl; + PHW_RFD HwRfd; // ptr to hardware RFD + WDFCOMMONBUFFER WdfCommonBuffer; + PHW_RFD OriginalHwRfd; // ptr to shared memory + PHYSICAL_ADDRESS HwRfdLa; // logical address of RFD + PHYSICAL_ADDRESS OriginalHwRfdLa; // Original physical address allocated by NDIS + ULONG HwRfdPhys; // lower part of HwRfdPa + BOOLEAN DeleteCommonBuffer; // Indicates if WdfObjectDelete + // is to be called when freeing MD_RFD. + ULONG Flags; + ULONG PacketSize; // total size of receive frame + WDFMEMORY LookasideMemoryHdl; +} MP_RFD, *PMP_RFD; + +//-------------------------------------- +// Structure for Power Management Info +//-------------------------------------- +typedef struct _MP_POWER_MGMT +{ + + // List of Wake Up Patterns + LIST_ENTRY PatternList; + + // Current Power state of the adapter + UINT PowerState; + + // Is PME_En on this adapter + BOOLEAN PME_En; + + // Wake-up capabailities of the adapter + BOOLEAN bWakeFromD0; + BOOLEAN bWakeFromD1; + BOOLEAN bWakeFromD2; + BOOLEAN bWakeFromD3Hot; + BOOLEAN bWakeFromD3Aux; + // Pad + BOOLEAN Pad[2]; + +} MP_POWER_MGMT, *PMP_POWER_MGMT; + + + +typedef struct _MP_WAKE_PATTERN +{ + // Link to the next Pattern + LIST_ENTRY linkListEntry; + + // E100 specific signature of the pattern + ULONG Signature; + + // Size of this allocation + ULONG AllocationSize; + + // Pattern - This contains the NDIS_PM_PACKET_PATTERN + UCHAR Pattern[1]; + +} MP_WAKE_PATTERN , *PMP_WAKE_PATTERN ; + + +//-------------------------------------- +// Macros specific to miniport adapter structure +//-------------------------------------- +#define MP_TCB_RESOURCES_AVAIABLE(_M) ((_M)->nBusySend < (_M)->NumTcb) + +#define MP_SHOULD_FAIL_SEND(_M) ((_M)->Flags & fMP_ADAPTER_FAIL_SEND_MASK) +#define MP_IS_NOT_READY(_M) ((_M)->Flags & fMP_ADAPTER_NOT_READY_MASK) +#define MP_IS_READY(_M) !((_M)->Flags & fMP_ADAPTER_NOT_READY_MASK) + +#define MP_SET_HARDWARE_ERROR(adapter) MP_SET_FLAG(adapter, fMP_ADAPTER_HARDWARE_ERROR) +#define MP_SET_NON_RECOVER_ERROR(adapter) MP_SET_FLAG(adapter, fMP_ADAPTER_NON_RECOVER_ERROR) + +#define MP_OFFSET(field) ((UINT)FIELD_OFFSET(MP_ADAPTER,field)) +#define MP_SIZE(field) sizeof(((PMP_ADAPTER)0)->field) + + +//-------------------------------------- +// Stall execution and wait with timeout +//-------------------------------------- +/*++ + _condition - condition to wait for + _timeout_ms - timeout value in milliseconds + _result - TRUE if condition becomes true before it times out +--*/ +#define MP_STALL_AND_WAIT(_condition, _timeout_ms, _result) \ +{ \ + int counter; \ + _result = FALSE; \ + for(counter = _timeout_ms * 50; counter != 0; counter--) \ + { \ + if(_condition) \ + { \ + _result = TRUE; \ + break; \ + } \ + KeStallExecutionProcessor(20); \ + } \ +} + +__inline VOID MP_STALL_EXECUTION( + IN ULONG MsecDelay) +{ + // Delay in 100 usec increments + MsecDelay *= 10; + while (MsecDelay) + { + KeStallExecutionProcessor(100); + MsecDelay--; + } +} + +typedef struct _FDO_DATA FDO_DATA, *PFDO_DATA; + + +NTSTATUS +NICGetDeviceInformation( + IN OUT PFDO_DATA FdoData + ); + +NTSTATUS +NICAllocateSoftwareResources( + IN OUT PFDO_DATA FdoData + ); + +NTSTATUS +NICMapHWResources( + IN OUT PFDO_DATA FdoData, + IN WDFCMRESLIST ResourcesRaw, + IN WDFCMRESLIST ResourcesTranslated + ); + +NTSTATUS +NICUnmapHWResources( + IN OUT PFDO_DATA FdoData + ); + + +NTSTATUS +NICFreeSoftwareResources( + IN OUT PFDO_DATA FdoData + ); + +NTSTATUS +NICInitializeAdapter( + IN PFDO_DATA FdoData + ); + +NTSTATUS +NICReadAdapterInfo( + IN PFDO_DATA FdoData + ); + +NTSTATUS +NICSelfTest( + IN PFDO_DATA FdoData + ); + +VOID +HwSoftwareReset( + IN PFDO_DATA FdoData + ); + +EVT_WDF_INTERRUPT_ISR NICEvtInterruptIsr; +EVT_WDF_INTERRUPT_DPC NICEvtInterruptDpc; +EVT_WDF_INTERRUPT_ENABLE NICEvtInterruptEnable; +EVT_WDF_INTERRUPT_DISABLE NICEvtInterruptDisable; + +EVT_WDF_DEVICE_D0_ENTRY_POST_INTERRUPTS_ENABLED NICEvtDeviceD0EntryPostInterruptsEnabled; +EVT_WDF_DEVICE_D0_EXIT_PRE_INTERRUPTS_DISABLED NICEvtDeviceD0ExitPreInterruptsDisabled; + +EVT_WDF_IO_QUEUE_IO_WRITE PciDrvEvtIoWrite; + +EVT_WDF_PROGRAM_DMA NICEvtProgramDmaFunction; + +EVT_WDF_TIMER NICWatchDogEvtTimerFunc; + +EVT_WDF_WORKITEM NICAllocRfdWorkItem; +EVT_WDF_WORKITEM NICFreeRfdWorkItem; + +NTSTATUS +NICAllocAdapterMemory( + IN PFDO_DATA FdoData + ); + +VOID +NICFreeAdapterMemory( + IN PFDO_DATA FdoData + ); + +NTSTATUS +NICAllocRfd( + IN PFDO_DATA FdoData, + IN PMP_RFD pMpRfd + ); + +VOID +NICFreeRfd( + IN PFDO_DATA FdoData, + IN PMP_RFD pMpRfd + ); + +VOID +NICReturnRFD( + IN PFDO_DATA FdoData, + IN PMP_RFD pMpRfd + ); + +NTSTATUS +HwConfigure( + IN PFDO_DATA FdoData + ); + +NTSTATUS +HwSetupIAAddress( + IN PFDO_DATA FdoData + ); + +NTSTATUS +HwClearAllCounters( + IN PFDO_DATA FdoData + ); + +NTSTATUS +NICInitRecvBuffers( + IN PFDO_DATA FdoData + ); + +VOID +NICInitSendBuffers( + IN PFDO_DATA FdoData + ); + +NTSTATUS +NICLinkDetection( + IN PFDO_DATA FdoData + ); + +VOID +NICHandleQueryOidRequest( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + WDF_REQUEST_PARAMETERS *Params + ); + +VOID +NICHandleSetOidRequest( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + WDF_REQUEST_PARAMETERS *Params + ); + +VOID +NICServiceIndicateStatusIrp( + IN PFDO_DATA FdoData + ); + +NTSTATUS +NICGetStatsCounters( + IN PFDO_DATA FdoData, + IN NDIS_OID Oid, + OUT PULONG64 pCounter); + +NTSTATUS +NICSetPacketFilter( + IN PFDO_DATA FdoData, + IN ULONG PacketFilter); + +NTSTATUS +NICSetMulticastList( + IN PFDO_DATA FdoData); + +ULONG +NICGetMediaConnectStatus( + IN PFDO_DATA FdoData); + +NTSTATUS +NICWritePacket( + IN PFDO_DATA FdoData, + IN WDFDMATRANSACTION DmaTransaction, + IN PSCATTER_GATHER_LIST SGList + ); + +NTSTATUS +NICSendPacket( + IN PFDO_DATA FdoData, + IN PMP_TCB pMpTcb, + IN PSCATTER_GATHER_LIST ScatterGather); + +NTSTATUS +NICStartSend( + IN PFDO_DATA FdoData, + IN PMP_TCB pMpTcb); + +_Requires_lock_held_(FdoData->SendLock) +NTSTATUS +NICHandleSendInterrupt( + IN PFDO_DATA FdoData + ); + +VOID +NICCheckForQueuedSends( + IN PFDO_DATA FdoData + ); + +_IRQL_requires_same_ +_IRQL_requires_(DISPATCH_LEVEL) +_Requires_lock_held_(FdoData->SendLock) +VOID +NICFreeQueuedSendPackets( + IN PFDO_DATA FdoData + ); + +_Requires_lock_held_(FdoData->SendLock) +VOID +NICFreeBusySendPackets( + IN PFDO_DATA FdoData + ); + +VOID +NICCompleteSendRequest( + PFDO_DATA FdoData, + WDFREQUEST Request, + NTSTATUS Status, + ULONG Information + ); + +VOID +NICShutdown( + IN PFDO_DATA FdoData + ); + +_IRQL_requires_same_ +_IRQL_requires_(DISPATCH_LEVEL) +_Requires_lock_held_(FdoData->RcvLock) +VOID +NICHandleRecvInterrupt( + IN PFDO_DATA FdoData + ); + +_Requires_lock_held_(FdoData->RcvLock) +NTSTATUS +NICStartRecv( + IN PFDO_DATA FdoData + ); + +VOID +NICResetRecv( + IN PFDO_DATA FdoData + ); + +VOID +NICServiceReadIrps( + PFDO_DATA FdoData, + PMP_RFD *PacketArray, + ULONG PacketArrayCount + ); + +BOOLEAN +NICCheckForHang( + IN PFDO_DATA FdoData + ); + +NTSTATUS +NICReset( + IN PFDO_DATA FdoData + ); + +MEDIA_STATE +NICIndicateMediaState( + IN PFDO_DATA FdoData + ); + +MEDIA_STATE +NICGetMediaState( + IN PFDO_DATA FdoData + ); + +VOID +NICExtractPMInfoFromPciSpace( + PFDO_DATA FdoData, + PUCHAR pPciConfig + ); + +NTSTATUS +NICSetPower( + PFDO_DATA FdoData , + WDF_POWER_DEVICE_STATE PowerState + ); + +NTSTATUS +MPSetPowerD0( + PFDO_DATA FdoData + ); + +NTSTATUS +MPSetPowerLow( + PFDO_DATA FdoData, + WDF_POWER_DEVICE_STATE PowerState + ); + +VOID +NICFillPoMgmtCaps ( + IN PFDO_DATA FdoData, + IN OUT PNDIS_PNP_CAPABILITIES pPower_Management_Capabilities, + IN OUT PNDIS_STATUS pStatus, + IN OUT PULONG pulInfoLen + ); + +NTSTATUS +NICAddWakeUpPattern( + IN PFDO_DATA FdoData, + IN PVOID InformationBuffer, + IN UINT InformationBufferLength, + OUT PULONG BytesRead, + OUT PULONG BytesNeeded + ); + +NTSTATUS +NICRemoveWakeUpPattern( + IN PFDO_DATA FdoData, + IN PVOID InformationBuffer, + IN UINT InformationBufferLength, + OUT PULONG BytesRead, + OUT PULONG BytesNeeded + ); + +VOID +NICRemoveAllWakeUpPatterns( + PFDO_DATA FdoData + ); + +NTSTATUS +NICConfigureForWakeUp( + IN PFDO_DATA FdoData, + IN BOOLEAN AddPattern + ); + +NTSTATUS +NICGetIoctlRequest( + IN WDFQUEUE Queue, + IN ULONG FunctionCode, + OUT WDFREQUEST* Request + ); + +VOID +NICGetDeviceInfSettings( + IN OUT PFDO_DATA FdoData + ); + +NTSTATUS +NICInitiateDmaTransfer( + IN PFDO_DATA FdoData, + IN WDFREQUEST Request + ); + +VOID +NICStartWatchDogTimer( + IN PFDO_DATA FdoData + ); + +typedef +USHORT +(*PREAD_PORT)( + IN USHORT *Register + ); + +typedef +VOID +(*PWRITE_PORT)( + IN USHORT *Register, + IN USHORT Value + ); + +#endif + + diff --git a/general/pcidrv/kmdf/HW/nic_init.c b/general/pcidrv/kmdf/HW/nic_init.c new file mode 100644 index 00000000..947b1d65 --- /dev/null +++ b/general/pcidrv/kmdf/HW/nic_init.c @@ -0,0 +1,2378 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + NIC_INIT.c + +Abstract: + + Contains rotuines to do resource allocation and hardware + initialization & shutdown. + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" + +#if defined(EVENT_TRACING) +#include "nic_init.tmh" +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, NICAllocateSoftwareResources) +#pragma alloc_text (PAGE, NICFreeSoftwareResources) +#pragma alloc_text (PAGE, NICMapHWResources) +#pragma alloc_text (PAGE, NICUnmapHWResources) +#pragma alloc_text (PAGE, NICGetDeviceInformation) +#pragma alloc_text (PAGE, NICReadAdapterInfo) +#pragma alloc_text (PAGE, NICAllocAdapterMemory) +#pragma alloc_text (PAGE, NICFreeAdapterMemory) +#pragma alloc_text (PAGE, NICInitRecvBuffers) +#pragma alloc_text (PAGE, NICSelfTest) +#pragma alloc_text (PAGE, HwClearAllCounters) +#pragma alloc_text (PAGE, NICAllocRfd) +#pragma alloc_text (PAGE, NICFreeRfd) +#pragma alloc_text (PAGE, NICFreeRfdWorkItem) +#endif + +PVOID LocalMmMapIoSpace( + _In_ PHYSICAL_ADDRESS PhysicalAddress, + _In_ SIZE_T NumberOfBytes + ) +{ + typedef + PVOID + (*PFN_MM_MAP_IO_SPACE_EX) ( + _In_ PHYSICAL_ADDRESS PhysicalAddress, + _In_ SIZE_T NumberOfBytes, + _In_ ULONG Protect + ); + + UNICODE_STRING name; + PFN_MM_MAP_IO_SPACE_EX pMmMapIoSpaceEx; + + RtlInitUnicodeString(&name, L"MmMapIoSpaceEx"); + pMmMapIoSpaceEx = (PFN_MM_MAP_IO_SPACE_EX) (ULONG_PTR)MmGetSystemRoutineAddress(&name); + + if (pMmMapIoSpaceEx != NULL){ + // + // Call WIN10 API if available + // + return pMmMapIoSpaceEx(PhysicalAddress, + NumberOfBytes, + PAGE_READWRITE | PAGE_NOCACHE); + } + + return MmMapIoSpace(PhysicalAddress, NumberOfBytes, MmNonCached); +} + +NTSTATUS +NICAllocateSoftwareResources( + IN OUT PFDO_DATA FdoData + ) +/*++ +Routine Description: + + This routine creates two parallel queues and 3 manual queues to hold + Read, Write and IOCTL requests. It also creates the interrupt object and + DMA object, and performs some additional initialization. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + None + +--*/ +{ + NTSTATUS status; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + WDF_DMA_ENABLER_CONFIG dmaConfig; + ULONG maximumLength, maxLengthSupported; + WDF_OBJECT_ATTRIBUTES attributes; + ULONG maxMapRegistersRequired, miniMapRegisters; + ULONG mapRegistersAllocated; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "-->NICAllocateSoftwareResources\n"); + + PAGED_CODE(); + + // + // Initialize all the static data first to make sure we don't touch + // uninitialized list in the ContextCleanup callback if the + // AddDevice fails for any reason. + // + InitializeListHead(&FdoData->PoMgmt.PatternList); + InitializeListHead(&FdoData->RecvList); + + // + // This a global lock, to synchonize access to device context. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = FdoData->WdfDevice; + status = WdfSpinLockCreate(&attributes,&FdoData->Lock); + if(!NT_SUCCESS(status)){ + return status; + } + + + // + // Get the BUS_INTERFACE_STANDARD for our device so that we can + // read & write to PCI config space. + // + status = WdfFdoQueryForInterface(FdoData->WdfDevice, + &GUID_BUS_INTERFACE_STANDARD, + (PINTERFACE) &FdoData->BusInterface, + sizeof(BUS_INTERFACE_STANDARD), + 1, // Version + NULL); //InterfaceSpecificData + if (!NT_SUCCESS (status)){ + return status; + } + + // + // First make sure this is our device before doing whole lot + // of other things. + // + status = NICGetDeviceInformation(FdoData); + if (!NT_SUCCESS (status)){ + return status; + } + + NICGetDeviceInfSettings(FdoData); + + // + // We will create and configure a queue for receiving + // write requests. If these requests have to be pended for any + // reason, they will be forwarded to a manual queue created after this one. + // Framework automatically takes the responsibility of handling + // cancellation when the requests are waiting in the queue. This is + // a managed queue. So the framework will take care of queueing + // incoming requests when the pnp/power state transition takes place. + // Since we have configured the queue to dispatch all the specific requests + // we care about, we don't need a default queue. A default queue is + // used to receive requests that are not preconfigured to go to + // a specific queue. + // + WDF_IO_QUEUE_CONFIG_INIT( + &ioQueueConfig, + WdfIoQueueDispatchParallel + ); + + ioQueueConfig.EvtIoWrite = PciDrvEvtIoWrite; + + status = WdfIoQueueCreate( + FdoData->WdfDevice, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoData->WriteQueue // queue handle + ); + + if (!NT_SUCCESS (status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfIoQueueCreate failed 0x%x\n", status); + return status; + } + + status = WdfDeviceConfigureRequestDispatching( + FdoData->WdfDevice, + FdoData->WriteQueue, + WdfRequestTypeWrite); + + if(!NT_SUCCESS (status)){ + ASSERT(NT_SUCCESS(status)); + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error in config'ing write Queue 0x%x\n", status); + return status; + } + + // + // Manual internal queue for write reqeusts. This will be used to queue + // the write requests presented to us from the parallel default queue + // when we are low in TCB resources or when the device + // is busy doing link detection. + // Requests can be canceled while waiting in the queue without any + // notification to the driver. + // + WDF_IO_QUEUE_CONFIG_INIT( + &ioQueueConfig, + WdfIoQueueDispatchManual + ); + + status = WdfIoQueueCreate ( + FdoData->WdfDevice, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoData->PendingWriteQueue + ); + + if(!NT_SUCCESS (status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error Creating manual write Queue 0x%x\n", status); + return status; + } + + + // + // Manual queue for read requests (WdfRequestTypeRead). We will configure the queue + // so that incoming read requests are directly dispatched to this queue. We will + // manually remove the requests from the queue and service them in our recv + // interrupt handler. WDF_IO_QUEUE_CONFIG_INIT initializes queues to be + // auto managed by default. + // + WDF_IO_QUEUE_CONFIG_INIT( + &ioQueueConfig, + WdfIoQueueDispatchManual + ); + + status = WdfIoQueueCreate ( + FdoData->WdfDevice, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoData->PendingReadQueue + ); + + if(!NT_SUCCESS (status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error Creating read Queue 0x%x\n", status); + return status; + } + + status = WdfDeviceConfigureRequestDispatching( + FdoData->WdfDevice, + FdoData->PendingReadQueue, + WdfRequestTypeRead); + + if(!NT_SUCCESS (status)){ + ASSERT(NT_SUCCESS(status)); + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error in config'ing read Queue 0x%x\n", status); + return status; + } + + + // + // Parallel queue for device I/O control (WdfRequestTypeDeviceControl) requests. + // We will configure the queue so that all the incoming ioctl requests + // go directly to this queue. We will try to service the requests immediately. + // If we can't, we will forward the request to a manual queue created below + // and try to service it from a DPC when the appropriate event happens. + // + WDF_IO_QUEUE_CONFIG_INIT( + &ioQueueConfig, + WdfIoQueueDispatchParallel + ); + + ioQueueConfig.EvtIoDeviceControl = PciDrvEvtIoDeviceControl; + + status = WdfIoQueueCreate ( + FdoData->WdfDevice, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoData->IoctlQueue + ); + + if(!NT_SUCCESS (status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error Creating ioctl Queue 0x%x\n", status); + return status; + } + + status = WdfDeviceConfigureRequestDispatching( + FdoData->WdfDevice, + FdoData->IoctlQueue, + WdfRequestTypeDeviceControl); + + if(!NT_SUCCESS (status)){ + ASSERT(NT_SUCCESS(status)); + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Error in config'ing ioctl Queue 0x%x\n", status); + return status; + } + + // + // Manual internal queue for device I/O control requests. This will be used to + // queue the ioctl requests presented to us from the parallel ioctl queue + // when we cannot handle them immediately. This is a managed queue. + // Requests be get canceled while waiting in the queue without any + // notification to the driver. + // + + WDF_IO_QUEUE_CONFIG_INIT( + &ioQueueConfig, + WdfIoQueueDispatchManual + ); + + status = WdfIoQueueCreate ( + FdoData->WdfDevice, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoData->PendingIoctlQueue + ); + + if(!NT_SUCCESS (status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "Error Creating manual Ioctl Queue 0x%x\n", status); + return status; + } + +#ifndef PCIDRV_CREATE_INTERRUPT_IN_PREPARE_HARDWARE + { + WDF_INTERRUPT_CONFIG interruptConfig; + + // + // Create WDFINTERRUPT object. + // + WDF_INTERRUPT_CONFIG_INIT(&interruptConfig, + NICEvtInterruptIsr, + NICEvtInterruptDpc); + + // + // These first two callbacks will be called at DIRQL. Their job is to + // enable and disable interrupts. + // + interruptConfig.EvtInterruptEnable = NICEvtInterruptEnable; + interruptConfig.EvtInterruptDisable = NICEvtInterruptDisable; + + status = WdfInterruptCreate(FdoData->WdfDevice, + &interruptConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoData->WdfInterrupt); + + if (!NT_SUCCESS (status)) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "WdfInterruptCreate failed: %!STATUS!\n", status); + return status; + } + } +#endif + + // + // Alignment requirement must be 16-byte for this device. This alignment + // value will be inherits by the DMA enabler and used when you allocate + // common buffers. + // + WdfDeviceSetAlignmentRequirement( FdoData->WdfDevice, FILE_OCTA_ALIGNMENT); + + // + // Bare minimum number of map registers required to do + // a single NIC_MAX_PACKET_SIZE transfer. + // + miniMapRegisters = BYTES_TO_PAGES(NIC_MAX_PACKET_SIZE) + 1; + + // + // Maximum map registers required to do simultaneous transfer + // of all TCBs assuming each packet spanning NIC_MAX_PHYS_BUF_COUNT + // Buffer can span multiple MDLs. + // + maxMapRegistersRequired = FdoData->NumTcb * NIC_MAX_PHYS_BUF_COUNT; + + // + // The maximum length of buffer for maxMapRegistersRequired number of + // map registers would be. + // + maximumLength = (maxMapRegistersRequired-1) << PAGE_SHIFT; + + // + // Create a new DMA Object for Scatter/Gather DMA mode. + // + + WDF_DMA_ENABLER_CONFIG_INIT( &dmaConfig, + WdfDmaProfileScatterGather, + maximumLength ); + + status = WdfDmaEnablerCreate( FdoData->WdfDevice, + &dmaConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoData->WdfDmaEnabler ); + + if (!NT_SUCCESS (status)) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "WdfDmaEnblerCreate failed: %08X\n", status); + return status; + } + + maxLengthSupported = (ULONG) WdfDmaEnablerGetFragmentLength(FdoData->WdfDmaEnabler, + WdfDmaDirectionReadFromDevice); + + mapRegistersAllocated = BYTES_TO_PAGES(maxLengthSupported) + 1; + + if(mapRegistersAllocated < miniMapRegisters) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "Not enough map registers: Allocated %d, Required %d\n", + mapRegistersAllocated, miniMapRegisters); + status = STATUS_INSUFFICIENT_RESOURCES; + return status; + } + + // + // Adjust our TCB count based on the MapRegisters we got. We will + // take the best case scenario where the packet is going to span + // no more than 2 pages. + // + FdoData->NumTcb = mapRegistersAllocated/miniMapRegisters; + + // + // Make sure it doesn't exceed NIC_MAX_TCBS. + // + FdoData->NumTcb = min(FdoData->NumTcb, NIC_MAX_TCBS); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "MapRegisters Allocated %d\n", mapRegistersAllocated); + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "Adjusted TCB count is %d\n", FdoData->NumTcb); + + // + // Set the maximum allowable DMA Scatter/Gather list fragmentation size. + // + WdfDmaEnablerSetMaximumScatterGatherElements( FdoData->WdfDmaEnabler, + NIC_MAX_PHYS_BUF_COUNT ); + + // + // Create a lock to protect all the write-related buffer lists. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = FdoData->WdfDevice; + status = WdfSpinLockCreate(&attributes,&FdoData->SendLock); + if(!NT_SUCCESS(status)){ + return status; + } + + // + // Create a lock to protect all the read-related buffer lists + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = FdoData->WdfDevice; + status = WdfSpinLockCreate(&attributes,&FdoData->RcvLock); + if(!NT_SUCCESS(status)){ + return status; + } + + status = NICAllocAdapterMemory(FdoData); + + if (NT_SUCCESS(status)) { + + // + // This sets up send buffers. It doesn't actually touch hardware. + // + + NICInitSendBuffers(FdoData); + + // + // This sets up receive buffers. It doesn't actually touch hardware. + // + + status = NICInitRecvBuffers(FdoData); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<-- NICAllocateSoftwareResources\n"); + + return status; +} + + +NTSTATUS +NICFreeSoftwareResources( + IN OUT PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Free all the software resources. We shouldn't touch the hardware. + This functions is called in the context of EvtDeviceContextCleanup. + Most of the resources created in NICAllocateResources such as queues, + DMA enabler, SpinLocks, CommonBuffer, are already freed by + framework because they are associated with the WDFDEVICE directly + or indirectly as child objects. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + None + +--*/ +{ + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "-->NICFreeSoftwareResources\n"); + + PAGED_CODE(); + + NICFreeAdapterMemory(FdoData); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<--NICFreeSoftwareResources\n"); + + return STATUS_SUCCESS; + +} + +NTSTATUS +NICMapHWResources( + IN OUT PFDO_DATA FdoData, + IN WDFCMRESLIST ResourcesRaw, + IN WDFCMRESLIST ResourcesTranslated + ) +/*++ +Routine Description: + + Gets the HW resources assigned by the bus driver and: + 1) Maps them to system address space. + 2) If PCIDRV_CREATE_INTERRUPT_IN_PREPARE_HARDWARE is defined, + it creates a WDFINTERRUPT object. + + Called during EvtDevicePrepareHardware callback. + + Three base address registers are supported by the 8255x: + 1) CSR Memory Mapped Base Address Register (BAR 0 at offset 10) + 2) CSR I/O Mapped Base Address Register (BAR 1 at offset 14) + 3) Flash Memory Mapped Base Address Register (BAR 2 at offset 18) + + The 8255x requires one BAR for I/O mapping and one BAR for memory + mapping of these registers anywhere within the 32-bit memory address space. + The driver determines which BAR (I/O or Memory) is used to access the + Control/Status Registers. + + Just for illustration, this driver maps both memory and I/O registers and + shows how to use READ_PORT_xxx or READ_REGISTER_xxx functions to perform + I/O in a platform independent basis. On some platforms, the I/O registers + can get mapped into memory space and your driver should be able to handle + this transparently. + + One BAR is also required to map the accesses to an optional Flash memory. + The 82557 implements this register regardless of the presence or absence + of a Flash chip on the adapter. The 82558 and 82559 implement this + register only if a bit is set in the EEPROM. The size of the space requested + by this register is 1Mbyte, and it is always mapped anywhere in the 32-bit + memory address space. + + Note: Although the 82558 only supports up to 64 Kbytes of Flash memory + and the 82559 only supports 128 Kbytes of Flash memory, the driver + requests 1 Mbyte of address space. Software should not access Flash + addresses above 64 Kbytes for the 82558 or 128 Kbytes for the 82559 + because Flash accesses above the limits are aliased to lower addresses. + +Arguments: + + FdoData Pointer to our FdoData + ResourcesRaw - Pointer to list of raw resources passed to + EvtDevicePrepareHardware callback + ResourcesTranslated - Pointer to list of translated resources passed to + EvtDevicePrepareHardware callback + +Return Value: + + NTSTATUS + +--*/ +{ + PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor; + ULONG i; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN bResPort = FALSE; + BOOLEAN bResInterrupt = FALSE; + BOOLEAN bResMemory = FALSE; + ULONG numberOfBARs = 0; + + UNREFERENCED_PARAMETER(ResourcesRaw); + + PAGED_CODE(); + + for (i=0; iType) { + + case CmResourceTypePort: + // + // We will increment the BAR count only for valid resources. We will + // not count the private device types added by the PCI bus driver. + // + numberOfBARs++; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "I/O mapped CSR: (%x) Length: (%d)\n", + descriptor->u.Port.Start.LowPart, + descriptor->u.Port.Length); + + // + // The resources are listed in the same order the as + // BARs in the config space, so this should be the second one. + // + if(numberOfBARs != 2) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "I/O mapped CSR is not in the right order\n"); + status = STATUS_DEVICE_CONFIGURATION_ERROR; + return status; + } + + // + // The port is in I/O space on this machine. + // We should use READ_PORT_Xxx, and WRITE_PORT_Xxx routines + // to read or write to the port. + // + + FdoData->IoBaseAddress = ULongToPtr(descriptor->u.Port.Start.LowPart); + FdoData->IoRange = descriptor->u.Port.Length; + // + // Since all our accesses are USHORT wide, we will create an accessor + // table just for these two functions. + // + FdoData->ReadPort = NICReadPortUShort; + FdoData->WritePort = NICWritePortUShort; + + bResPort = TRUE; + FdoData->MappedPorts = FALSE; + break; + + case CmResourceTypeMemory: + + numberOfBARs++; + + if(numberOfBARs == 1) { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Memory mapped CSR:(%x:%x) Length:(%d)\n", + descriptor->u.Memory.Start.LowPart, + descriptor->u.Memory.Start.HighPart, + descriptor->u.Memory.Length); + // + // Our CSR memory space should be 0x1000 in size. + // + ASSERT(descriptor->u.Memory.Length == 0x1000); + FdoData->MemPhysAddress = descriptor->u.Memory.Start; + FdoData->CSRAddress = LocalMmMapIoSpace( + descriptor->u.Memory.Start, + NIC_MAP_IOSPACE_LENGTH); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "CSRAddress=%p\n", FdoData->CSRAddress); + + bResMemory = TRUE; + + } else if(numberOfBARs == 2){ + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "I/O mapped CSR in Memory Space: (%x) Length: (%d)\n", + descriptor->u.Memory.Start.LowPart, + descriptor->u.Memory.Length); + // + // The port is in memory space on this machine. + // We should call LocalMmMapIoSpace to map the physical to virtual + // address, and also use the READ/WRITE_REGISTER_xxx function + // to read or write to the port. + // + + FdoData->IoBaseAddress = LocalMmMapIoSpace( + descriptor->u.Memory.Start, + descriptor->u.Memory.Length); + + FdoData->ReadPort = NICReadRegisterUShort; + FdoData->WritePort = NICWriteRegisterUShort; + FdoData->MappedPorts = TRUE; + bResPort = TRUE; + + } else if(numberOfBARs == 3){ + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Flash memory:(%x:%x) Length:(%d)\n", + descriptor->u.Memory.Start.LowPart, + descriptor->u.Memory.Start.HighPart, + descriptor->u.Memory.Length); + // + // Our flash memory should be 1MB in size. Since we don't + // access the memory, let us not bother mapping it. + // + //ASSERT(descriptor->u.Memory.Length == 0x100000); + } else { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "Memory Resources are not in the right order\n"); + status = STATUS_DEVICE_CONFIGURATION_ERROR; + return status; + } + + break; + + case CmResourceTypeInterrupt: + + ASSERT(!bResInterrupt); + +#ifdef PCIDRV_CREATE_INTERRUPT_IN_PREPARE_HARDWARE + { + WDF_INTERRUPT_CONFIG interruptConfig; + + // + // Create WDFINTERRUPT object. + // + WDF_INTERRUPT_CONFIG_INIT(&interruptConfig, + NICEvtInterruptIsr, + NICEvtInterruptDpc); + + // + // These first two callbacks will be called at DIRQL. Their job is to + // enable and disable interrupts. + // + interruptConfig.EvtInterruptEnable = NICEvtInterruptEnable; + interruptConfig.EvtInterruptDisable = NICEvtInterruptDisable; + interruptConfig.InterruptTranslated = descriptor; + interruptConfig.InterruptRaw = + WdfCmResourceListGetDescriptor(ResourcesRaw, i); + + status = WdfInterruptCreate(FdoData->WdfDevice, + &interruptConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoData->WdfInterrupt); + + if (!NT_SUCCESS (status)) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "WdfInterruptCreate failed: %!STATUS!\n", status); + return status; + } + } +#endif + + bResInterrupt = TRUE; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "Interrupt level: 0x%0x, Vector: 0x%0x\n", + descriptor->u.Interrupt.Level, + descriptor->u.Interrupt.Vector); + + break; + + default: + // + // This could be device-private type added by the PCI bus driver. We + // shouldn't filter this or change the information contained in it. + // + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Unhandled resource type (0x%x)\n", + descriptor->Type); + break; + } + + } + + // + // Make sure we got all the 3 resources to work with. + // + if (!(bResPort && bResInterrupt && bResMemory)) { + status = STATUS_DEVICE_CONFIGURATION_ERROR; + return status; + } + + // + // Read additional info from NIC such as MAC address + // + status = NICReadAdapterInfo(FdoData); + if (status != STATUS_SUCCESS) + { + return status; + } + + // + // Test our adapter hardware + // + status = NICSelfTest(FdoData); + if (status != STATUS_SUCCESS) + { + return status; + } + + return status; + +} + +NTSTATUS +NICUnmapHWResources( + IN OUT PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Disconnect the interrupt and unmap all the memory and I/O resources. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + // + // Free hardware resources + // + if (FdoData->CSRAddress) + { + MmUnmapIoSpace(FdoData->CSRAddress, NIC_MAP_IOSPACE_LENGTH); + FdoData->CSRAddress = NULL; + } + + if(FdoData->MappedPorts){ + MmUnmapIoSpace(FdoData->IoBaseAddress, FdoData->IoRange); + FdoData->IoBaseAddress = NULL; + } + + return STATUS_SUCCESS; + +} + + +NTSTATUS +NICGetDeviceInformation( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + This function reads the PCI config space and make sure that it's our + device and stores the device IDs and power information in the device + extension. Should be done in the StartDevice. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + None + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) UCHAR buffer[NIC_PCI_E100_HDR_LENGTH ]; + PPCI_COMMON_CONFIG pPciConfig = (PPCI_COMMON_CONFIG) buffer; + USHORT usPciCommand; + ULONG bytesRead =0; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "---> NICGetDeviceInformation\n"); + + PAGED_CODE(); + + RtlZeroMemory(buffer, sizeof(buffer)); + bytesRead = FdoData->BusInterface.GetBusData( + FdoData->BusInterface.Context, + PCI_WHICHSPACE_CONFIG, //READ + buffer, + FIELD_OFFSET(PCI_COMMON_CONFIG, VendorID), + NIC_PCI_E100_HDR_LENGTH); + + if (bytesRead != NIC_PCI_E100_HDR_LENGTH) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "GetBusData (NIC_PCI_E100_HDR_LENGTH) failed =%d\n", + bytesRead); + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Is this our device? + // + + if (pPciConfig->VendorID != NIC_PCI_VENDOR_ID || + pPciConfig->DeviceID != NIC_PCI_DEVICE_ID) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "VendorID/DeviceID don't match - %x/%x\n", + pPciConfig->VendorID, pPciConfig->DeviceID); + //return STATUS_DEVICE_DOES_NOT_EXIST; + + } + + // + // save TRACE_LEVEL_INFORMATION from config space + // + FdoData->RevsionID = pPciConfig->RevisionID; + FdoData->SubVendorID = pPciConfig->u.type0.SubVendorID; + FdoData->SubSystemID = pPciConfig->u.type0.SubSystemID; + + NICExtractPMInfoFromPciSpace (FdoData, (PUCHAR)pPciConfig); + + usPciCommand = pPciConfig->Command; + + if ((usPciCommand & PCI_ENABLE_WRITE_AND_INVALIDATE) && (FdoData->MWIEnable)){ + FdoData->MWIEnable = TRUE; + } else { + FdoData->MWIEnable = FALSE; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICGetDeviceInformation\n"); + + return status; +} + +NTSTATUS +NICReadAdapterInfo( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Read the mac addresss from the adapter + +Arguments: + + FdoData Pointer to our device context + +Return Value: + + NTSTATUS code + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + USHORT usValue; + int i; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICReadAdapterInfo\n"); + + PAGED_CODE(); + + FdoData->EepromAddressSize = GetEEpromAddressSize( + GetEEpromSize(FdoData, (PUCHAR)FdoData->IoBaseAddress)); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "EepromAddressSize = %d\n", + FdoData->EepromAddressSize); + + + // + // Read node address from the EEPROM + // + for (i=0; i< ETH_LENGTH_OF_ADDRESS; i += 2) + { + usValue = ReadEEprom(FdoData, (PUCHAR)FdoData->IoBaseAddress, + (USHORT)(EEPROM_NODE_ADDRESS_BYTE_0 + (i/2)), + FdoData->EepromAddressSize); + + *((PUSHORT)(&FdoData->PermanentAddress[i])) = usValue; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "Permanent Address = %02x-%02x-%02x-%02x-%02x-%02x\n", + FdoData->PermanentAddress[0], FdoData->PermanentAddress[1], + FdoData->PermanentAddress[2], FdoData->PermanentAddress[3], + FdoData->PermanentAddress[4], FdoData->PermanentAddress[5]); + + if (ETH_IS_MULTICAST(FdoData->PermanentAddress) || + ETH_IS_BROADCAST(FdoData->PermanentAddress)) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Permanent address is invalid\n"); + + status = STATUS_INVALID_ADDRESS; + } + else + { + if (!FdoData->bOverrideAddress) + { + ETH_COPY_NETWORK_ADDRESS(FdoData->CurrentAddress, FdoData->PermanentAddress); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "Current Address = %02x-%02x-%02x-%02x-%02x-%02x\n", + FdoData->CurrentAddress[0], FdoData->CurrentAddress[1], + FdoData->CurrentAddress[2], FdoData->CurrentAddress[3], + FdoData->CurrentAddress[4], FdoData->CurrentAddress[5]); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICReadAdapterInfo, status=%x\n", + status); + + return status; +} + + + +NTSTATUS +NICAllocAdapterMemory( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Allocate all the memory blocks for send, receive and others + +Arguments: + + FdoData Pointer to our adapter + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PUCHAR pMem; + ULONG MemPhys; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICAllocAdapterMemory\n"); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "NumTcb=%d\n", FdoData->NumTcb); + + do + { + // + // Send + Misc + // + // + // Allocate MP_TCB's + // + status = RtlULongMult(FdoData->NumTcb, + sizeof(MP_TCB), + &FdoData->MpTcbMemSize); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "RtlUlongMult failed 0x%x\n", status); + break; + } + + pMem = ExAllocatePoolWithTag(NonPagedPool, + FdoData->MpTcbMemSize, PCIDRV_POOL_TAG); + if (NULL == pMem ) + { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "Failed to allocate MP_TCB's\n"); + break; + } + + RtlZeroMemory(pMem, FdoData->MpTcbMemSize); + FdoData->MpTcbMem = pMem; + + // HW_START + + // + // Allocate shared memory for send + // + FdoData->HwSendMemAllocSize = FdoData->NumTcb * (sizeof(TXCB_STRUC) + + NIC_MAX_PHYS_BUF_COUNT * sizeof(TBD_STRUC)); + + _Analysis_assume_(FdoData->HwSendMemAllocSize > 0); + status = WdfCommonBufferCreate( FdoData->WdfDmaEnabler, + FdoData->HwSendMemAllocSize, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoData->WdfSendCommonBuffer ); + + if (status != STATUS_SUCCESS) + { + FdoData->HwSendMemAllocSize = 0; + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfCommonBufferCreate(Send) " + "failed %08X\n", status ); + break; + } + + FdoData->HwSendMemAllocVa = WdfCommonBufferGetAlignedVirtualAddress( + FdoData->WdfSendCommonBuffer); + + FdoData->HwSendMemAllocLa = WdfCommonBufferGetAlignedLogicalAddress( + FdoData->WdfSendCommonBuffer); + + RtlZeroMemory(FdoData->HwSendMemAllocVa, + FdoData->HwSendMemAllocSize); + + // + // Allocate shared memory for other uses + // + // FIXME-NOTE: The WdfCommonBufferGetAlignedVirtualAddress functions + // return device-specified aligned pointers...use them. + // + FdoData->HwMiscMemAllocSize = + sizeof(SELF_TEST_STRUC) + ALIGN_16 + + sizeof(DUMP_AREA_STRUC) + ALIGN_16 + + sizeof(NON_TRANSMIT_CB) + ALIGN_16 + + sizeof(ERR_COUNT_STRUC) + ALIGN_16; + + // + // Allocate the shared memory for the command block data structures. + // + status = WdfCommonBufferCreate( FdoData->WdfDmaEnabler, + FdoData->HwMiscMemAllocSize, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoData->WdfMiscCommonBuffer ); + + if (status != STATUS_SUCCESS) + { + FdoData->HwMiscMemAllocSize = 0; + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfCommonBufferCreate(Misc) " + "failed %08X\n", status ); + break; + } + + FdoData->HwMiscMemAllocVa = WdfCommonBufferGetAlignedVirtualAddress( + FdoData->WdfMiscCommonBuffer); + + FdoData->HwMiscMemAllocLa = WdfCommonBufferGetAlignedLogicalAddress( + FdoData->WdfMiscCommonBuffer); + + RtlZeroMemory(FdoData->HwMiscMemAllocVa, + FdoData->HwMiscMemAllocSize); + + pMem = FdoData->HwMiscMemAllocVa; + MemPhys = FdoData->HwMiscMemAllocLa.LowPart ; + + FdoData->SelfTest = (PSELF_TEST_STRUC)MP_ALIGNMEM(pMem, ALIGN_16); + FdoData->SelfTestPhys = MP_ALIGNMEM_PHYS(MemPhys, ALIGN_16); + pMem = (PUCHAR)FdoData->SelfTest + sizeof(SELF_TEST_STRUC); + MemPhys = FdoData->SelfTestPhys + sizeof(SELF_TEST_STRUC); + + FdoData->NonTxCmdBlock = (PNON_TRANSMIT_CB)MP_ALIGNMEM(pMem, ALIGN_16); + FdoData->NonTxCmdBlockPhys = MP_ALIGNMEM_PHYS(MemPhys, ALIGN_16); + pMem = (PUCHAR)FdoData->NonTxCmdBlock + sizeof(NON_TRANSMIT_CB); + MemPhys = FdoData->NonTxCmdBlockPhys + sizeof(NON_TRANSMIT_CB); + + FdoData->DumpSpace = (PDUMP_AREA_STRUC)MP_ALIGNMEM(pMem, ALIGN_16); + FdoData->DumpSpacePhys = MP_ALIGNMEM_PHYS(MemPhys, ALIGN_16); + pMem = (PUCHAR)FdoData->DumpSpace + sizeof(DUMP_AREA_STRUC); + MemPhys = FdoData->DumpSpacePhys + sizeof(DUMP_AREA_STRUC); + + FdoData->StatsCounters = (PERR_COUNT_STRUC)MP_ALIGNMEM(pMem, ALIGN_16); + FdoData->StatsCounterPhys = MP_ALIGNMEM_PHYS(MemPhys, ALIGN_16); + + // HW_END + + // + // Recv + // + + // set the max number of RFDs + // disable the RFD grow/shrink scheme if user specifies a NumRfd value + // larger than NIC_MAX_GROW_RFDS + FdoData->MaxNumRfd = max(FdoData->NumRfd, NIC_MAX_GROW_RFDS); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "NumRfd = %d\n", FdoData->NumRfd); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "MaxNumRfd = %d\n", FdoData->MaxNumRfd); + + // + // The driver should allocate more data than sizeof(RFD_STRUC) to allow the + // driver to align the data(after ethernet header) at 8 byte boundary + // + FdoData->HwRfdSize = sizeof(RFD_STRUC) + MORE_DATA_FOR_ALIGN; + + status = STATUS_SUCCESS; + + } WHILE( FALSE ); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "<-- NICAllocAdapterMemory, status=%x\n", status); + + return status; + +} + +VOID +NICFreeAdapterMemory( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Free all the resources and MP_ADAPTER data block + +Arguments: + + FdoData Pointer to our adapter + +Return Value: + + None + +--*/ +{ + PMP_RFD pMpRfd; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICFreeAdapterMemory\n"); + + PAGED_CODE(); + + // No active and waiting sends + ASSERT(FdoData->nBusySend == 0); + ASSERT(FdoData->nWaitSend == 0); + + ASSERT(FdoData->nReadyRecv == FdoData->CurrNumRfd); + + while (!IsListEmpty(&FdoData->RecvList)) + { + pMpRfd = (PMP_RFD)RemoveHeadList(&FdoData->RecvList); + + pMpRfd->DeleteCommonBuffer = FALSE; + + NICFreeRfd(FdoData, pMpRfd); + } + + FdoData->WdfSendCommonBuffer = NULL; + FdoData->HwSendMemAllocVa = NULL; + + FdoData->WdfMiscCommonBuffer = NULL; + FdoData->HwMiscMemAllocVa = NULL; + FdoData->SelfTest = NULL; + FdoData->StatsCounters = NULL; + FdoData->NonTxCmdBlock = NULL; + FdoData->DumpSpace = NULL; + + // Free the memory for MP_TCB structures + if (FdoData->MpTcbMem) + { + ExFreePoolWithTag(FdoData->MpTcbMem, PCIDRV_POOL_TAG); + FdoData->MpTcbMem = NULL; + } + + //Free all the wake up patterns on this adapter + NICRemoveAllWakeUpPatterns(FdoData); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICFreeAdapterMemory\n"); +} + + + +VOID +NICInitSendBuffers( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Initialize send data structures. Can be called at DISPATCH_LEVEL. + +Arguments: + + FdoData - Pointer to our adapter context + +Return Value: + + None + +--*/ +{ + PMP_TCB pMpTcb; + PHW_TCB pHwTcb; + ULONG HwTcbPhys; + ULONG TcbCount; + + PTBD_STRUC pHwTbd; + ULONG HwTbdPhys; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICInitSendBuffers\n"); + + FdoData->TransmitIdle = TRUE; + FdoData->ResumeWait = TRUE; + + // Setup the initial pointers to the SW and HW TCB data space + pMpTcb = (PMP_TCB) FdoData->MpTcbMem; + pHwTcb = (PHW_TCB) FdoData->HwSendMemAllocVa; + HwTcbPhys = FdoData->HwSendMemAllocLa.LowPart; + + // Setup the initial pointers to the TBD data space. + // TBDs are located immediately following the TCBs + pHwTbd = (PTBD_STRUC) (FdoData->HwSendMemAllocVa + + (sizeof(TXCB_STRUC) * FdoData->NumTcb)); + HwTbdPhys = HwTcbPhys + (sizeof(TXCB_STRUC) * FdoData->NumTcb); + + _Analysis_assume_(FdoData->HwSendMemAllocSize >= (FdoData->NumTcb * sizeof(HW_TCB))); + _Analysis_assume_(FdoData->MpTcbMemSize >= (FdoData->NumTcb * sizeof(MP_TCB))); + + // Go through and set up each TCB + for (TcbCount = 0; TcbCount < FdoData->NumTcb; TcbCount++) + { + pMpTcb->HwTcb = pHwTcb; // save ptr to HW TCB + pMpTcb->HwTcbPhys = HwTcbPhys; // save HW TCB physical address + pMpTcb->HwTbd = pHwTbd; // save ptr to TBD array + pMpTcb->HwTbdPhys = HwTbdPhys; // save TBD array physical address + + if (TcbCount){ + pMpTcb->PrevHwTcb = pHwTcb - 1; + } + else { + pMpTcb->PrevHwTcb = (PHW_TCB)((PUCHAR)FdoData->HwSendMemAllocVa + + ((FdoData->NumTcb - 1) * sizeof(HW_TCB))); + } + pHwTcb->TxCbHeader.CbStatus = 0; // clear the status + pHwTcb->TxCbHeader.CbCommand = CB_EL_BIT | CB_TX_SF_BIT | CB_TRANSMIT; + + + // Set the link pointer in HW TCB to the next TCB in the chain. + // If this is the last TCB in the chain, then set it to the first TCB. + if (TcbCount < FdoData->NumTcb - 1) + { + pMpTcb->Next = pMpTcb + 1; + pHwTcb->TxCbHeader.CbLinkPointer = HwTcbPhys + sizeof(HW_TCB); + } + else + { + pMpTcb->Next = (PMP_TCB) FdoData->MpTcbMem; + pHwTcb->TxCbHeader.CbLinkPointer = + FdoData->HwSendMemAllocLa.LowPart; + } + + pHwTcb->TxCbThreshold = (UCHAR) FdoData->AiThreshold; + pHwTcb->TxCbTbdPointer = HwTbdPhys; + + pMpTcb++; + pHwTcb++; + HwTcbPhys += sizeof(TXCB_STRUC); + pHwTbd = (PTBD_STRUC)((PUCHAR)pHwTbd + sizeof(TBD_STRUC) * NIC_MAX_PHYS_BUF_COUNT); + HwTbdPhys += sizeof(TBD_STRUC) * NIC_MAX_PHYS_BUF_COUNT; + } + + // set the TCB head/tail indexes + // head is the olded one to free, tail is the next one to use + FdoData->CurrSendHead = (PMP_TCB) FdoData->MpTcbMem; + FdoData->CurrSendTail = (PMP_TCB) FdoData->MpTcbMem; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICInitSendBuffers\n"); +} + +NTSTATUS +NICInitRecvBuffers( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Initialize receive data structures + +Arguments: + + FdoData - Pointer to our adapter context + +Return Value: + +--*/ +{ + NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; + PMP_RFD pMpRfd; + ULONG RfdCount; + WDFMEMORY memoryHdl; + PDRIVER_CONTEXT driverContext = GetDriverContext(WdfGetDriver()); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICInitRecvBuffers\n"); + + PAGED_CODE(); + + // Setup each RFD + for (RfdCount = 0; RfdCount < FdoData->NumRfd; RfdCount++) + { + status = WdfMemoryCreateFromLookaside( + driverContext->RecvLookaside, + &memoryHdl + ); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Failed to get lookaside buffer\n"); + continue; + } + pMpRfd = WdfMemoryGetBuffer(memoryHdl, NULL); + if (!pMpRfd) + { + //ErrorValue = ERRLOG_OUT_OF_LOOKASIDE_MEMORY; + continue; + } + pMpRfd->LookasideMemoryHdl = memoryHdl; + // + // Allocate the shared memory for this RFD. + // + _Analysis_assume_(FdoData->HwRfdSize > 0); + status = WdfCommonBufferCreate( FdoData->WdfDmaEnabler, + FdoData->HwRfdSize, + WDF_NO_OBJECT_ATTRIBUTES, + &pMpRfd->WdfCommonBuffer ); + + if (status != STATUS_SUCCESS) + { + pMpRfd->WdfCommonBuffer = NULL; + WdfObjectDelete(pMpRfd->LookasideMemoryHdl); + continue; + } + + pMpRfd->OriginalHwRfd = + WdfCommonBufferGetAlignedVirtualAddress(pMpRfd->WdfCommonBuffer); + + pMpRfd->OriginalHwRfdLa = + WdfCommonBufferGetAlignedLogicalAddress(pMpRfd->WdfCommonBuffer); + + // + // Get a 8-byts aligned memory from the original HwRfd + // + pMpRfd->HwRfd = (PHW_RFD)DATA_ALIGN(pMpRfd->OriginalHwRfd); + + // + // Now HwRfd is already 8-bytes aligned, and the size of HwPfd + // header(not data part) is a multiple of 8, + // If we shift HwRfd 0xA bytes up, the Ethernet header size + // is 14 bytes long, then the data will be at + // 8 byte boundary. + // + pMpRfd->HwRfd = (PHW_RFD)((PUCHAR)(pMpRfd->HwRfd) + HWRFD_SHIFT_OFFSET); + + // + // Update physical address accordingly + // + pMpRfd->HwRfdLa.QuadPart = pMpRfd->OriginalHwRfdLa.QuadPart + + BYTES_SHIFT(pMpRfd->HwRfd, pMpRfd->OriginalHwRfd); + + status = NICAllocRfd(FdoData, pMpRfd); + if (!NT_SUCCESS(status)) + { + WdfObjectDelete(pMpRfd->LookasideMemoryHdl); + continue; + } + // + // Add this RFD to the RecvList + // + FdoData->CurrNumRfd++; + NICReturnRFD(FdoData, pMpRfd); + } + + if (FdoData->CurrNumRfd > NIC_MIN_RFDS) + { + status = STATUS_SUCCESS; + } + + // + // FdoData->CurrNumRfd < NIC_MIN_RFDs + // + if (status != STATUS_SUCCESS) + { + // TODO: Log an entry into the eventlog + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICInitRecvBuffers, status=%x\n", status); + + return status; +} + +NTSTATUS +NICAllocRfd( + IN PFDO_DATA FdoData, + IN PMP_RFD pMpRfd + ) +/*++ +Routine Description: + + Allocate NDIS_PACKET and NDIS_BUFFER associated with a RFD. + Can be called at DISPATCH_LEVEL. + +Arguments: + + FdoData Pointer to our adapter + pMpRfd pointer to a RFD + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PHW_RFD pHwRfd; + + UNREFERENCED_PARAMETER(FdoData); + + PAGED_CODE(); + + do{ + pHwRfd = pMpRfd->HwRfd; + pMpRfd->HwRfdPhys = pMpRfd->HwRfdLa.LowPart; + + pMpRfd->Flags = 0; + + pMpRfd->Mdl = IoAllocateMdl((PVOID)&pHwRfd->RfdBuffer.RxMacHeader, + NIC_MAX_PACKET_SIZE, + FALSE, + FALSE, + NULL); + if (!pMpRfd->Mdl) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + MmBuildMdlForNonPagedPool(pMpRfd->Mdl); + + pMpRfd->Buffer = &pHwRfd->RfdBuffer.RxMacHeader; + + // Init each RFD header + pHwRfd->RfdRbdPointer = DRIVER_NULL; + pHwRfd->RfdSize = NIC_MAX_PACKET_SIZE; + + } WHILE (FALSE); + + + if (!NT_SUCCESS(status)) + { + if (pMpRfd->WdfCommonBuffer) + { + // + // Free HwRfd, we need to free the original memory + // pointed by OriginalHwRfd. + // + WdfObjectDelete( pMpRfd->WdfCommonBuffer ); + + pMpRfd->WdfCommonBuffer = NULL; + pMpRfd->HwRfd = NULL; + pMpRfd->OriginalHwRfd = NULL; + + pMpRfd->DeleteCommonBuffer = TRUE; + } + } + + return status; + +} + +VOID +NICFreeRfd( + IN PFDO_DATA FdoData, + IN PMP_RFD pMpRfd + ) +/*++ +Routine Description: + + Free a RFD. + Can be called at DISPATCH_LEVEL. + +Arguments: + + FdoData Pointer to our adapter + pMpRfd Pointer to a RFD + +Return Value: + + None + +--*/ +{ + UNREFERENCED_PARAMETER(FdoData); + PAGED_CODE(); + + ASSERT(pMpRfd->HwRfd); + ASSERT(pMpRfd->Mdl); + + IoFreeMdl(pMpRfd->Mdl); + + // + // Free HwRfd, we need to free the original memory pointed + // by OriginalHwRfd. + // + if (pMpRfd->DeleteCommonBuffer == TRUE) { + + WdfObjectDelete( pMpRfd->WdfCommonBuffer ); + } + + pMpRfd->WdfCommonBuffer = NULL; + pMpRfd->HwRfd = NULL; + pMpRfd->OriginalHwRfd = NULL; + + WdfObjectDelete(pMpRfd->LookasideMemoryHdl); +} + +VOID +NICAllocRfdWorkItem( + IN WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + + Worker routine to allocate memory for RFD at PASSIVE_LEVEL. + +Arguments: + + WorkItem - Handle to framework item object. + +Return Value: + + VOID + +--*/ +{ + PFDO_DATA FdoData; + //KIRQL oldIrql; + PMP_RFD TempMpRfd; + NTSTATUS status; + PWORKER_ITEM_CONTEXT item; + WDFMEMORY tempMpRfdMemHdl; + PDRIVER_CONTEXT driverContext = GetDriverContext(WdfGetDriver()); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "---> NICAllocRfdWorkItem\n"); + + item = GetWorkItemContext(WorkItem); + FdoData = item->FdoData; + + status = WdfMemoryCreateFromLookaside(driverContext->RecvLookaside, &tempMpRfdMemHdl); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Failed to get lookaside buffer\n"); + return ; + } + TempMpRfd = WdfMemoryGetBuffer(tempMpRfdMemHdl, NULL); + if (TempMpRfd) + { + TempMpRfd->LookasideMemoryHdl = tempMpRfdMemHdl; + // + // Allocate the shared memory for this RFD. + // + _Analysis_assume_(FdoData->HwRfdSize > 0); + status = WdfCommonBufferCreate(FdoData->WdfDmaEnabler, + FdoData->HwRfdSize, + WDF_NO_OBJECT_ATTRIBUTES, + &TempMpRfd->WdfCommonBuffer); + + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, + "WdfCommonBufferCreate failed %X\n", status); + WdfObjectDelete(TempMpRfd->LookasideMemoryHdl); + goto Exit; + } + + TempMpRfd->OriginalHwRfd = + WdfCommonBufferGetAlignedVirtualAddress( + TempMpRfd->WdfCommonBuffer); + + TempMpRfd->OriginalHwRfdLa = + WdfCommonBufferGetAlignedLogicalAddress( + TempMpRfd->WdfCommonBuffer); + + // + // First get a HwRfd at 8 byte boundary from OriginalHwRfd + // + TempMpRfd->HwRfd = (PHW_RFD)DATA_ALIGN(TempMpRfd->OriginalHwRfd); + // + // Then shift HwRfd so that the data(after ethernet header) is at 8 bytes boundary + // + TempMpRfd->HwRfd = (PHW_RFD)((PUCHAR)TempMpRfd->HwRfd + HWRFD_SHIFT_OFFSET); + // + // Update physical address as well + // + TempMpRfd->HwRfdLa.QuadPart = TempMpRfd->OriginalHwRfdLa.QuadPart + + BYTES_SHIFT(TempMpRfd->HwRfd, TempMpRfd->OriginalHwRfd); + + status = NICAllocRfd(FdoData, TempMpRfd); + if (!NT_SUCCESS(status)) + { + // + // NICAllocRfd frees common buffer when it returns an TRACE_LEVEL_ERROR. + // So, let us not worry about freeing that here. + // + WdfObjectDelete(TempMpRfd->LookasideMemoryHdl); + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "Recv: NICAllocRfd failed %x\n", status); + goto Exit; + } + + + WdfSpinLockAcquire(FdoData->RcvLock); + + // + // Add this RFD to the RecvList + // + FdoData->CurrNumRfd++; + NICReturnRFD(FdoData, TempMpRfd); + + + WdfSpinLockRelease(FdoData->RcvLock); + + ASSERT(FdoData->CurrNumRfd <= FdoData->MaxNumRfd); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, + "CurrNumRfd=%d\n", FdoData->CurrNumRfd); + + } + +Exit: + FdoData->AllocNewRfd = FALSE; + + WdfObjectDelete(WorkItem); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<--- NICAllocRfdWorkItem\n"); + + return; +} + +VOID +NICFreeRfdWorkItem( + IN WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + + Worker routine to RFD memory at PASSIVE_LEVEL. + +Arguments: + + WorkItem - Handle to framework item object. + +Return Value: + + VOID + +--*/ +{ + PFDO_DATA fdoData; + PMP_RFD pMpRfd; + PWORKER_ITEM_CONTEXT item; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "---> NICFreeRfdWorkItem\n"); + + PAGED_CODE(); + + item = GetWorkItemContext(WorkItem); + fdoData = item->FdoData; + pMpRfd = (PMP_RFD)item->Argument1; + + NICFreeRfd(fdoData, pMpRfd); + + WdfObjectDelete(WorkItem); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<--- NICFreeRfdWorkItem\n"); + + return; +} + + + +NTSTATUS +NICSelfTest( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Perform a NIC self-test + +Arguments: + + FdoData Pointer to our adapter + +Return Value: + + NT status code + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG SelfTestCommandCode; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICSelfTest\n"); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "SelfTest=%p, SelfTestPhys=%x\n", + FdoData->SelfTest, FdoData->SelfTestPhys); + + // + // Issue a software reset to the adapter + // + HwSoftwareReset(FdoData); + + // + // Execute The PORT Self Test Command On The 82558. + // + ASSERT(FdoData->SelfTestPhys != 0); + SelfTestCommandCode = FdoData->SelfTestPhys; + + // + // Setup SELF TEST Command Code in D3 - D0 + // + SelfTestCommandCode |= PORT_SELFTEST; + + // + // Initialize the self-test signature and results DWORDS + // + FdoData->SelfTest->StSignature = 0; + FdoData->SelfTest->StResults = 0xffffffff; + + // + // Do the port command + // + FdoData->CSRAddress->Port = SelfTestCommandCode; + + MP_STALL_EXECUTION(NIC_DELAY_POST_SELF_TEST_MS); + + // + // if The First Self Test DWORD Still Zero, We've timed out. If the second + // DWORD is not zero then we have an TRACE_LEVEL_ERROR. + // + if ((FdoData->SelfTest->StSignature == 0) || (FdoData->SelfTest->StResults != 0)) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "StSignature=%x, StResults=%x\n", + FdoData->SelfTest->StSignature, FdoData->SelfTest->StResults); + + status = STATUS_DEVICE_CONFIGURATION_ERROR; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICSelfTest, status=%x\n", status); + + return status; +} + +NTSTATUS +NICInitializeAdapter( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Initialize the adapter and set up everything + +Arguments: + + FdoData Pointer to our adapter + +Return Value: + + NT Status Code + +--*/ +{ + NTSTATUS status; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "--> NICInitializeAdapter\n"); + + do + { + + // set up our link indication variable + // it doesn't matter what this is right now because it will be + // set correctly if link fails + FdoData->MediaState = Connected; + + // Issue a software reset to the D100 + HwSoftwareReset(FdoData); + + // Load the CU BASE (set to 0, because we use linear mode) + FdoData->CSRAddress->ScbGeneralPointer = 0; + status = D100IssueScbCommand(FdoData, SCB_CUC_LOAD_BASE, FALSE); + if (status != STATUS_SUCCESS) + { + break; + } + + // Wait for the SCB command word to clear before we set the general pointer + if (!WaitScb(FdoData)) + { + status = STATUS_DEVICE_DATA_ERROR; + break; + } + + // Load the RU BASE (set to 0, because we use linear mode) + FdoData->CSRAddress->ScbGeneralPointer = 0; + status = D100IssueScbCommand(FdoData, SCB_RUC_LOAD_BASE, FALSE); + if (status != STATUS_SUCCESS) + { + break; + } + + // Configure the adapter + status = HwConfigure(FdoData); + if (status != STATUS_SUCCESS) + { + break; + } + + status = HwSetupIAAddress(FdoData); + if (status != STATUS_SUCCESS) + { + break; + } + + // Clear the internal counters + HwClearAllCounters(FdoData); + + + } WHILE (FALSE); + + if (status != STATUS_SUCCESS) + { + // TODO: Log an entry into the eventlog + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<-- NICInitializeAdapter, Status=%x\n", status); + + return status; +} + +VOID +NICShutdown( + IN PFDO_DATA FdoData) +/*++ + +Routine Description: + + Shutdown the device + +Arguments: + + FdoData - Pointer to our adapter + +Return Value: + + None + +--*/ +{ + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "---> NICShutdown\n"); + + if(FdoData->CSRAddress) { + // + // Disable interrupt and issue a full reset + // + NICDisableInterrupt(FdoData); + NICIssueFullReset(FdoData); + // + // Reset the PHY chip. We do this so that after a warm boot, the PHY will + // be in a known state, with auto-negotiation enabled. + // + ResetPhy(FdoData); + } + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "<--- NICShutdown\n"); +} + +VOID +HwSoftwareReset( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Issue a software reset to the hardware + +Arguments: + + FdoData Pointer to our adapter + +Return Value: + + None + +--*/ +{ + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> HwSoftwareReset\n"); + + // Issue a PORT command with a data word of 0 + FdoData->CSRAddress->Port = PORT_SOFTWARE_RESET; + + // wait after the port reset command + KeStallExecutionProcessor(NIC_DELAY_POST_RESET); + + // Mask off our interrupt line -- its unmasked after reset + NICDisableInterrupt(FdoData); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- HwSoftwareReset\n"); +} + + + +NTSTATUS +HwConfigure( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Configure the hardware + +Arguments: + + FdoData Pointer to our adapter + +Return Value: + + NT Status + + +--*/ +{ + NTSTATUS status; + PCB_HEADER_STRUC NonTxCmdBlockHdr = + (PCB_HEADER_STRUC)FdoData->NonTxCmdBlock; + UINT i; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> HwConfigure\n"); + + // + // Init the packet filter to nothing. + // + FdoData->OldPacketFilter = FdoData->PacketFilter; + FdoData->PacketFilter = 0; + + // + // Store the current setting for BROADCAST/PROMISCUOS modes + FdoData->OldParameterField = CB_557_CFIG_DEFAULT_PARM15; + + // Setup the non-transmit command block header for the configure command. + NonTxCmdBlockHdr->CbStatus = 0; + NonTxCmdBlockHdr->CbCommand = CB_CONFIGURE; + NonTxCmdBlockHdr->CbLinkPointer = DRIVER_NULL; + + // Fill in the configure command data. + + // First fill in the static (end user can't change) config bytes + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[0] = CB_557_CFIG_DEFAULT_PARM0; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[2] = CB_557_CFIG_DEFAULT_PARM2; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[3] = CB_557_CFIG_DEFAULT_PARM3; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[6] = CB_557_CFIG_DEFAULT_PARM6; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[9] = CB_557_CFIG_DEFAULT_PARM9; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[10] = CB_557_CFIG_DEFAULT_PARM10; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[11] = CB_557_CFIG_DEFAULT_PARM11; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[12] = CB_557_CFIG_DEFAULT_PARM12; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[13] = CB_557_CFIG_DEFAULT_PARM13; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[14] = CB_557_CFIG_DEFAULT_PARM14; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[16] = CB_557_CFIG_DEFAULT_PARM16; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[17] = CB_557_CFIG_DEFAULT_PARM17; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[18] = CB_557_CFIG_DEFAULT_PARM18; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[20] = CB_557_CFIG_DEFAULT_PARM20; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[21] = CB_557_CFIG_DEFAULT_PARM21; + + // Now fill in the rest of the configuration bytes (the bytes that contain + // user configurable parameters). + + // Set the Tx and Rx Fifo limits + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[1] = + (UCHAR) ((FdoData->AiTxFifo << 4) | FdoData->AiRxFifo); + + if (FdoData->MWIEnable) + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[3] |= CB_CFIG_B3_MWI_ENABLE; + } + + // Set the Tx and Rx DMA maximum byte count fields. + if ((FdoData->AiRxDmaCount) || (FdoData->AiTxDmaCount)) + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[4] = + FdoData->AiRxDmaCount; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[5] = + (UCHAR) (FdoData->AiTxDmaCount | CB_CFIG_DMBC_EN); + } + else + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[4] = + CB_557_CFIG_DEFAULT_PARM4; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[5] = + CB_557_CFIG_DEFAULT_PARM5; + } + + + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[7] = + (UCHAR) ((CB_557_CFIG_DEFAULT_PARM7 & (~CB_CFIG_URUN_RETRY)) | + (FdoData->AiUnderrunRetry << 1) + ); + + // Setup for MII or 503 operation. The CRS+CDT bit should only be set + // when operating in 503 mode. + if (FdoData->PhyAddress == 32) + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[8] = + (CB_557_CFIG_DEFAULT_PARM8 & (~CB_CFIG_503_MII)); + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[15] = + (CB_557_CFIG_DEFAULT_PARM15 | CB_CFIG_CRS_OR_CDT); + } + else + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[8] = + (CB_557_CFIG_DEFAULT_PARM8 | CB_CFIG_503_MII); + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[15] = + ((CB_557_CFIG_DEFAULT_PARM15 & (~CB_CFIG_CRS_OR_CDT)) | CB_CFIG_BROADCAST_DIS); + } + + + // Setup Full duplex stuff + + // If forced to half duplex + if (FdoData->AiForceDpx == 1) + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = + (CB_557_CFIG_DEFAULT_PARM19 & + (~(CB_CFIG_FORCE_FDX| CB_CFIG_FDX_ENABLE))); + + // If forced to full duplex + else if (FdoData->AiForceDpx == 2) + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = + (CB_557_CFIG_DEFAULT_PARM19 | CB_CFIG_FORCE_FDX); + + // If auto-duplex + else + { + // We must force full duplex on if we are using PHY 0, and we are + // supposed to run in FDX mode. We do this because the D100 has only + // one FDX# input pin, and that pin will be connected to PHY 1. + if ((FdoData->PhyAddress == 0) && (FdoData->usDuplexMode == 2)) + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = + (CB_557_CFIG_DEFAULT_PARM19 | CB_CFIG_FORCE_FDX); + else + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = + CB_557_CFIG_DEFAULT_PARM19; + } + + + // display the config TRACE_LEVEL_INFORMATION to the debugger + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Issuing Configure command\n"); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Config Block at virt addr %p phys address %x\n", + &NonTxCmdBlockHdr->CbStatus, FdoData->NonTxCmdBlockPhys); + + for (i=0; i < CB_CFIG_BYTE_COUNT; i++) + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Config byte %x = %.2x\n", + i, FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[i]); + + // Wait for the SCB command word to clear before we set the general pointer + if (!WaitScb(FdoData)) + { + status = STATUS_DEVICE_DATA_ERROR; + } + else + { + ASSERT(FdoData->CSRAddress->ScbCommandLow == 0); + FdoData->CSRAddress->ScbGeneralPointer = FdoData->NonTxCmdBlockPhys; + + // Submit the configure command to the chip, and wait for it to complete. + status = D100SubmitCommandBlockAndWait(FdoData); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, + "<-- HwConfigure, Status=%x\n", status); + + return status; +} + + +NTSTATUS +HwSetupIAAddress( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Set up the individual MAC address + +Arguments: + + FdoData Pointer to our adapter + +Return Value: + + NT Status code + +--*/ +{ + NTSTATUS status; + UINT i; + PCB_HEADER_STRUC NonTxCmdBlockHdr = + (PCB_HEADER_STRUC)FdoData->NonTxCmdBlock; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> HwSetupIAAddress\n"); + + // Individual Address Setup + NonTxCmdBlockHdr->CbStatus = 0; + NonTxCmdBlockHdr->CbCommand = CB_IA_ADDRESS; + NonTxCmdBlockHdr->CbLinkPointer = DRIVER_NULL; + + // Copy in the station's individual address + for (i = 0; i < ETH_LENGTH_OF_ADDRESS; i++) + FdoData->NonTxCmdBlock->NonTxCb.Setup.IaAddress[i] = FdoData->CurrentAddress[i]; + + // Update the command list pointer. We don't need to do a WaitSCB here + // because this command is either issued immediately after a reset, or + // after another command that runs in polled mode. This guarantees that + // the low byte of the SCB command word will be clear. The only commands + // that don't run in polled mode are transmit and RU-start commands. + ASSERT(FdoData->CSRAddress->ScbCommandLow == 0); + FdoData->CSRAddress->ScbGeneralPointer = FdoData->NonTxCmdBlockPhys; + + // Submit the IA configure command to the chip, and wait for it to complete. + status = D100SubmitCommandBlockAndWait(FdoData); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "<-- HwSetupIAAddress, Status=%x\n", status); + + return status; +} + +NTSTATUS +HwClearAllCounters( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + This routine will clear the hardware TRACE_LEVEL_ERROR statistic counters + +Arguments: + + FdoData Pointer to our adapter + +Return Value: + + NT Status code + + +--*/ +{ + NTSTATUS status; + BOOLEAN bResult; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "--> HwClearAllCounters\n"); + + PAGED_CODE(); + + do + { + // Load the dump counters pointer. Since this command is generated only + // after the IA setup has complete, we don't need to wait for the SCB + // command word to clear + ASSERT(FdoData->CSRAddress->ScbCommandLow == 0); + FdoData->CSRAddress->ScbGeneralPointer = FdoData->StatsCounterPhys; + + // Issue the load dump counters address command + status = D100IssueScbCommand(FdoData, SCB_CUC_DUMP_ADDR, FALSE); + if (status != STATUS_SUCCESS) + break; + + // Now dump and reset all of the statistics + status = D100IssueScbCommand(FdoData, SCB_CUC_DUMP_RST_STAT, TRUE); + if (status != STATUS_SUCCESS) + break; + + // Now wait for the dump/reset to complete, timeout value 2 secs + MP_STALL_AND_WAIT(FdoData->StatsCounters->CommandComplete == 0xA007, 2000, bResult); + if (!bResult) + { + MP_SET_HARDWARE_ERROR(FdoData); + status = STATUS_DEVICE_DATA_ERROR; + break; + } + + // init packet counts + FdoData->GoodTransmits = 0; + FdoData->GoodReceives = 0; + + // init transmit error counts + FdoData->TxAbortExcessCollisions = 0; + FdoData->TxLateCollisions = 0; + FdoData->TxDmaUnderrun = 0; + FdoData->TxLostCRS = 0; + FdoData->TxOKButDeferred = 0; + FdoData->OneRetry = 0; + FdoData->MoreThanOneRetry = 0; + FdoData->TotalRetries = 0; + + // init receive error counts + FdoData->RcvCrcErrors = 0; + FdoData->RcvAlignmentErrors = 0; + FdoData->RcvResourceErrors = 0; + FdoData->RcvDmaOverrunErrors = 0; + FdoData->RcvCdtFrames = 0; + FdoData->RcvRuntErrors = 0; + + } WHILE (FALSE); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, + "<-- HwClearAllCounters, Status=%x\n", status); + + return status; +} + +VOID +NICGetDeviceInfSettings( + IN OUT PFDO_DATA FdoData + ) +{ + + // + // Number of ReceiveFrameDescriptors + // + if(!PciDrvReadRegistryValue(FdoData, + L"NumRfd", + &FdoData->NumRfd)){ + FdoData->NumRfd = 32; + } + + FdoData->NumRfd = min(FdoData->NumRfd, NIC_MAX_RFDS); + FdoData->NumRfd = max(FdoData->NumRfd, 1); + + // + // Number of Transmit Control Blocks + // + if(!PciDrvReadRegistryValue(FdoData, + L"NumTcb", + &FdoData->NumTcb)){ + FdoData->NumTcb = NIC_DEF_TCBS; + + } + + FdoData->NumTcb = min(FdoData->NumTcb, NIC_MAX_TCBS); + FdoData->NumTcb = max(FdoData->NumTcb, 1); + + // + // Max number of buffers required for coalescing fragmented packet. + // Not implemented in this sample + // + if(!PciDrvReadRegistryValue(FdoData, + L"NumCoalesce", + &FdoData->NumBuffers)){ + FdoData->NumBuffers = 8; + } + + FdoData->NumBuffers = min(FdoData->NumBuffers, 32); + FdoData->NumBuffers = max(FdoData->NumBuffers, 1); + + // + // Get the Link Speed & Duplex. + // + if(!PciDrvReadRegistryValue(FdoData, + L"SpeedDuplex", + &FdoData->SpeedDuplex)){ + FdoData->SpeedDuplex = 0; + } + FdoData->SpeedDuplex = min(FdoData->SpeedDuplex, 4); + FdoData->SpeedDuplex = max(FdoData->SpeedDuplex, 0); + // + // Decode SpeedDuplex + // Value 0 means Auto detect + // Value 1 means 10Mb-Half-Duplex + // Value 2 means 10Mb-Full-Duplex + // Value 3 means 100Mb-Half-Duplex + // Value 4 means 100Mb-Full-Duplex + // + switch(FdoData->SpeedDuplex) + { + case 1: + FdoData->AiTempSpeed = 10; FdoData->AiForceDpx = 1; + break; + + case 2: + FdoData->AiTempSpeed = 10; FdoData->AiForceDpx = 2; + break; + + case 3: + FdoData->AiTempSpeed = 100; FdoData->AiForceDpx = 1; + break; + + case 4: + FdoData->AiTempSpeed = 100; FdoData->AiForceDpx = 2; + break; + } + + // + // Rest of these values are currently not configured thru INF. + // + FdoData->PhyAddress = 0xFF; + FdoData->Connector = 0; + FdoData->AiTxFifo = DEFAULT_TX_FIFO_LIMIT; + FdoData->AiRxFifo = DEFAULT_RX_FIFO_LIMIT; + FdoData->AiTxDmaCount = 0; + FdoData->AiRxDmaCount = 0; + FdoData->AiUnderrunRetry = DEFAULT_UNDERRUN_RETRY; + FdoData->AiThreshold = 200; + FdoData->MWIEnable = 1; + FdoData->Congest = 0; + + return; + } + + diff --git a/general/pcidrv/kmdf/HW/nic_pm.c b/general/pcidrv/kmdf/HW/nic_pm.c new file mode 100644 index 00000000..7bdd3b99 --- /dev/null +++ b/general/pcidrv/kmdf/HW/nic_pm.c @@ -0,0 +1,1710 @@ +/**************************************************************************** +** COPYRIGHT (C) 1994-1997 INTEL CORPORATION ** +** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON ** +** HTTP://WWW.INTEL.COM/ ** +** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND ** +** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER ** +****************************************************************************/ + + +#include "precomp.h" + +#if defined(EVENT_TRACING) +#include "nic_pm.tmh" +#endif + + +// Things to note: +// PME_ena bit should be active before the 82558 is set into low power mode +// Default for WOL should generate wake up event after a HW Reset + +// Fixed Packet Filtering +// Need to verify that the micro code is loaded and Micro Machine is active +// Clock signal is active on PCI clock + + +// Address Matching +// Need to enable IAMatch_Wake_En bit and the MCMatch_Wake_En bit is set + +// ARP Wakeup +// Need to set BRCST DISABL bet to 0 (broadcast enable) +// To handle VLAN set the VLAN_ARP bit +// IP address needs to be configured with 16 least significant bits +// Set the IP Address in the IP_Address configuration word. + +// Fixed WakeUp Filters: +// There are 3ight different fixed WakeUp Filters +// ( Unicast, Multicast, Arp. etc). + + +// Link Status Event +// Set Link_Status_Wakeup Enable bit. + +// Flexible filtering: +// Supports: ARP packets, Directed, Magic Packet and Link Event + +// Flexible Filtering Overview: +// driver should program micro-code before setting card into low power +// Incoming packets are compared against the loadable microcode. If PME is +// is enabled then, the system is woken up. + + +// Segments are defined in book - but not implemented here. + +// WakeUp Packet -that causes the machine to wake up will be stored +// in the Micro Machine temporary storage area so that the driver can read it. + + +// Software Work: +// Power Down: +// OS requests the driver to go to a low power state +// SW sets CU and RU to idle by issuing a Selective Reset to the device +// 3rd portion .- Wake Up Segments defintion +// The above three segments are loaded as on chain. The last CB must have +// its EL bit set. +// Device can now be powered down. +// Software driver completes OS request +// OS then physically switches the Device to low power state +// + +// Power Up: +// OS powers up the Device +// driver should NOT initialize the Device. It should NOT issue a Self Test +// Driver Initiates a PORT DUMP command +// Device dumps its internal registers including the wakeup frame storage area +// SW reads the PME register +// SW reads the WakeUp Frame Data, analyzes it and acts accordingly +// SW restores its cvonfiguration and and resumes normal operation. +// + +// +// Power Management definitions from the Intel Handbook +// + +// +// Definitions from Table 4.2, Pg 4.9 +// of the 10/100 Mbit Ethernet Family Software Technical +// Reference Manual +// + +#define PMC_Offset 0xDE +#define E100_PMC_WAKE_FROM_D0 0x1 +#define E100_PMC_WAKE_FROM_D1 0x2 +#define E100_PMC_WAKE_FROM_D2 0x4 +#define E100_PMC_WAKE_FROM_D3HOT 0x8 +#define E100_PMC_WAKE_FROM_D3_AUX 0x10 + +// +// Load Programmable filter definintions. +// Taken from C-19 from the Software Reference Manual. +// It has examples too. The opcode used for load is 0x80000 +// + +#define BIT_15_13 0xA000 + +#define CB_LOAD_PROG_FILTER BIT_3 +#define CU_LOAD_PROG_FILTER_EL BIT_7 +#define CU_SUCCEED_LOAD_PROG_FILTER BIT_15_13 +#define CB_FILTER_EL BIT_7 +#define CB_FILTER_PREDEFINED_FIX BIT_6 +#define CB_FILTER_ARP_WAKEUP BIT_3 +#define CB_FILTER_IA_WAKEUP BIT_1 + +#define CU_SCB_NULL ((UINT)-1) + + +#pragma pack( push, enter_include1, 1 ) + +// +// Define the PM Capabilities register in the device +// portion of the PCI config space +// +typedef struct _MP_PM_CAP_REG { + + #pragma warning(disable:4214) // bit field types other than int warning + + USHORT UnInteresting:11; + USHORT PME_Support:5; + + #pragma warning(default:4214) + +} MP_PM_CAP_REG; + + +// +// Define the PM Control/Status Register +// +typedef struct _MP_PMCSR { + + #pragma warning(disable:4214) // bit field types other than int warning + + USHORT PowerState:2; // Power State; + USHORT Res:2; // reserved + USHORT DynData:1; // Ignored + USHORT Res1:3; // Reserved + USHORT PME_En:1; // Enable device to set the PME Event; + USHORT DataSel:4; // Unused + USHORT DataScale:2; // Data Scale - Unused + USHORT PME_Status:1; // PME Status - Sticky bit; + + #pragma warning(default:4214) + +} MP_PMCSR ; + +typedef struct _MP_PM_PCI_SPACE { + + UCHAR Stuff[PMC_Offset]; + + // PM capabilites + + MP_PM_CAP_REG PMCaps; + + // PM Control Status Register + + MP_PMCSR PMCSR; + + +} MP_PM_PCI_SPACE , *PMP_PM_PCI_SPACE ; + + +// +// This is the Programmable Filter Command Structure +// +typedef struct _MP_PROG_FILTER_COMM_STRUCT +{ + // CB Status Word + USHORT CBStatus; + + // CB Command Word + USHORT CBCommand; + + //Next CB PTR == ffff ffff + ULONG NextCBPTR; + + //Programmable Filters + ULONG FilterData[16]; + + +} MP_PROG_FILTER_COMM_STRUCT,*PMP_PROG_FILTER_COMM_STRUCT; + +typedef struct _MP_PMDR +{ + #pragma warning(disable:4214) // bit field types other than int warning + + // Status of the PME bit + UCHAR PMEStatus:1; + + // Is the TCO busy + UCHAR TCORequest:1; + + // Force TCO indication + UCHAR TCOForce:1; + + // Is the TCO Ready + UCHAR TCOReady:1; + + // Reserved + UCHAR Reserved:1; + + // Has an InterestingPacket been received + UCHAR InterestingPacket:1; + + // Has a Magic Packet been received + UCHAR MagicPacket:1; + + // Has the Link Status been changed + UCHAR LinkStatus:1; + + #pragma warning(default:4214) + +} MP_PMDR , *PMP_PMDR; + +//------------------------------------------------------------------------- +// Structure used to set up a programmable filter. +// This is overlayed over the Control/Status Register (CSR) +//------------------------------------------------------------------------- +typedef struct _CSR_FILTER_STRUC { + + // Status- used to verify if the load prog filter command + // has been accepted .set to 0xa000 + USHORT ScbStatus; // SCB Status register + + // Set to an opcode of 0x8 + // + UCHAR ScbCommandLow; // SCB Command register (low byte) + + // 80. Low + High gives the required opcode 0x80080000 + UCHAR ScbCommandHigh; // SCB Command register (high byte) + + // Set to NULL ff ff ff ff + ULONG NextPointer; // SCB General pointer + + // Set to a hardcoded filter, Arp + IA Match, + IP address + + union + { + ULONG u32; + + struct { + UCHAR IPAddress[2]; + UCHAR Reserved; + UCHAR Set; + + }PreDefined; + + }Programmable; // Wake UP Filter union + +} CSR_FILTER_STRUC, *PCSR_FILTER_STRUC; + +#pragma pack( pop, enter_include1 ) + +#define MP_CLEAR_PMDR(pPMDR) (*pPMDR) = ((*pPMDR) | 0xe0); // clear the 3 uppermost bits in the PMDR + + +//------------------------------------------------------------------------- +// L O C A L P R O T O T Y P E S +//------------------------------------------------------------------------- + +__inline +NTSTATUS +MPIssueScbPoMgmtCommand( + IN PFDO_DATA Adapter, + IN PCSR_FILTER_STRUC pFilter, + IN BOOLEAN WaitForScb + ); + + +VOID +MPCreateProgrammableFilter ( + IN PMP_WAKE_PATTERN pMpWakePattern , + IN PUCHAR pFilter, + IN OUT PULONG pNext + ); + + + +//------------------------------------------------------------------------- +// P O W E R M G M T F U N C T I O N S +//------------------------------------------------------------------------- + +PUCHAR +HwReadPowerPMDR( + IN PFDO_DATA Adapter + ) +/*++ +Routine Description: + + This routine will read Hardware's PM registers + +Arguments: + + Adapter Pointer to our adapter + +Return Value: + + STATUS_SUCCESS + NTSTATUS_HARD_ERRORS + +--*/ +{ + UCHAR PMDR =0; + PUCHAR pPMDR = NULL; + +#define CSR_SIZE sizeof (*Adapter->CSRAddress) + + + + ASSERT (CSR_SIZE == 0x18); + + pPMDR = 0x18 + (PUCHAR)Adapter->CSRAddress ; + + PMDR = *pPMDR; + + return pPMDR; + +} + + +NTSTATUS +MpClearPME_En ( + IN PFDO_DATA FdoData, + IN MP_PMCSR PMCSR + ) +{ + NTSTATUS status; + UINT ulResult; + + PMCSR.PME_En = 0; + + ulResult = FdoData->BusInterface.SetBusData( + FdoData->BusInterface.Context, + PCI_WHICHSPACE_CONFIG, + (PVOID)&PMCSR, + FIELD_OFFSET(MP_PM_PCI_SPACE, PMCSR), + sizeof(PMCSR)); + + ASSERT (ulResult == sizeof(PMCSR)); + if (ulResult == sizeof(PMCSR)) { + status = STATUS_SUCCESS; + + } else { + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + + + +VOID +MPSetPowerLowPrivate( + WDFINTERRUPT WdfInterrupt, + PFDO_DATA FdoData + ) +/*++ +Routine Description: + + The section follows the steps mentioned in + Section C.2.6.2 of the Reference Manual. + + +Arguments: + + Adapter Pointer to our adapter + +Return Value: + +--*/ +{ + CSR_FILTER_STRUC Filter; + USHORT IntStatus; + MP_PMCSR PMCSR = {0}; + ULONG ulResult; + + UNREFERENCED_PARAMETER( WdfInterrupt ); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "-->MPSetPowerLowPrivate\n"); + RtlZeroMemory (&Filter, sizeof (Filter)); + + // + // Before issue the command to low power state, we should ack all the + // pending interrupts, then set the adapter's power to low state. + // + NIC_ACK_INTERRUPT(FdoData, IntStatus); + + // + // If the driver should wake up the machine + // + if (FdoData->AllowWakeArming) + { + // + // Send the WakeUp Pattern to the nic + MPIssueScbPoMgmtCommand(FdoData, &Filter, TRUE); + + + // + // Section C.2.6.2 - The driver needs to wait for the CU to idle + // The above function already waits for the CU to idle + // + ASSERT((FdoData->CSRAddress->ScbStatus & SCB_CUS_MASK) == SCB_CUS_IDLE); + } + else + { + + ulResult = FdoData->BusInterface.GetBusData( + FdoData->BusInterface.Context, + PCI_WHICHSPACE_CONFIG, + (PVOID)&PMCSR, + FIELD_OFFSET(MP_PM_PCI_SPACE, PMCSR), + sizeof(PMCSR)); + + if(ulResult != sizeof(PMCSR)){ + ASSERT(ulResult == sizeof(PMCSR)); + TraceEvents(TRACE_LEVEL_ERROR, DBG_POWER, "GetBusData for PMCSR failed\n"); + return; + } + if (PMCSR.PME_En == 1) + { + // + // PME is enabled. Clear the PME_En bit. + // So that it is not asserted + // + MpClearPME_En (FdoData,PMCSR); + + } + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<--MPSetPowerLowPrivate\n"); + +} + +NTSTATUS +MPSetPowerD0Private ( + IN PFDO_DATA FdoData + ) +{ + PUCHAR pPMDR; + NTSTATUS status; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "-->MPSetPowerD0Private\n"); + + // Dump the packet if necessary + //Cause of Wake Up + + pPMDR = HwReadPowerPMDR(FdoData); + + status = NICInitializeAdapter(FdoData); + + // Clear the PMDR + MP_CLEAR_PMDR(pPMDR); + + NICIssueSelectiveReset(FdoData); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<--MPSetPowerD0Private\n"); + + return status; +} + + +VOID +HwSetWakeUpConfigure( + IN PFDO_DATA FdoData, + PUCHAR pPoMgmtConfigType, + UINT WakeUpParameter + ) +{ + UNREFERENCED_PARAMETER( WakeUpParameter ); + + if (IsPoMgmtSupported( FdoData) == TRUE) + { + (*pPoMgmtConfigType)= ((*pPoMgmtConfigType) | + CB_WAKE_ON_LINK_BYTE9 | + CB_WAKE_ON_ARP_PKT_BYTE9 ); + } +} + +NTSTATUS +MPSetUpFilterCB( + IN PFDO_DATA FdoData + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PCB_HEADER_STRUC NonTxCmdBlockHdr = (PCB_HEADER_STRUC)FdoData->NonTxCmdBlock; + PFILTER_CB_STRUC pFilterCb = (PFILTER_CB_STRUC)NonTxCmdBlockHdr; + ULONG Curr = 0; + ULONG Next = 0; + PLIST_ENTRY pPatternEntry = ListNext(&FdoData->PoMgmt.PatternList) ; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> MPSetUpFilterCB\n"); + + RtlZeroMemory (pFilterCb, sizeof(*pFilterCb)); + + // Individual Address Setup + NonTxCmdBlockHdr->CbStatus = 0; + NonTxCmdBlockHdr->CbCommand = CB_EL_BIT | CB_LOAD_PROG_FILTER; + NonTxCmdBlockHdr->CbLinkPointer = DRIVER_NULL; + + // go through each filter in the list. + + while (pPatternEntry != (&FdoData->PoMgmt.PatternList)) + { + PMP_WAKE_PATTERN pWakeUpPattern = NULL; + //PNDIS_PM_PACKET_PATTERN pCurrPattern = NULL;; + + // initialize local variables + pWakeUpPattern = CONTAINING_RECORD(pPatternEntry, MP_WAKE_PATTERN, linkListEntry); + + // increment the iterator + pPatternEntry = ListNext (pPatternEntry); + + // Update the Curr Array Pointer + Curr = Next; + + // Create the Programmable filter for this device. + MPCreateProgrammableFilter (pWakeUpPattern , (PUCHAR)&pFilterCb->Pattern[Curr], &Next); + + if (Next >=16) + { + break; + } + + } + + { + // Set the EL bit on the last pattern + PUCHAR pLastPattern = (PUCHAR) &pFilterCb->Pattern[Curr]; + + // Get to bit 31 + pLastPattern[3] |= CB_FILTER_EL ; + + + } + + ASSERT(FdoData->CSRAddress->ScbCommandLow == 0); + + // Wait for the CU to Idle before giving it this command + if(!WaitScb(FdoData)) + { + status = STATUS_DEVICE_DATA_ERROR; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- MPSetUpFilterCB\n"); + + return status; + + +} + +NTSTATUS +MPIssueScbPoMgmtCommand( + IN PFDO_DATA FdoData, + IN PCSR_FILTER_STRUC pNewFilter, + IN BOOLEAN WaitForScb + ) +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + + UNREFERENCED_PARAMETER( pNewFilter ); + UNREFERENCED_PARAMETER( WaitForScb ); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, + "--> MPIssueScbPoMgmtCommand\n"); + + do + { + // Set up SCB to issue this command + + status = MPSetUpFilterCB(FdoData); + + if (status != STATUS_SUCCESS) + { + break; + } + + // Submit the configure command to the chip, and wait for + // it to complete. + + FdoData->CSRAddress->ScbGeneralPointer = FdoData->NonTxCmdBlockPhys; + + status = D100SubmitCommandBlockAndWait(FdoData); + + if(status != STATUS_SUCCESS) + { + status = STATUS_DEVICE_DATA_ERROR; + break; + } + + } WHILE (FALSE); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, + "<-- MPIssueScbPoMgmtCommand %x\n", status); + + return status; +} + + + +NTSTATUS +MPCalculateE100PatternForFilter ( + IN PUCHAR pFrame, + IN ULONG FrameLength, + IN PUCHAR pMask, + IN ULONG MaskLength, + OUT PULONG pSignature + ) +/*++ +Routine Description: + + This function outputs the E100 specific Pattern Signature + used to wake up the machine. + + Section C.2.4 - CRC word calculation of a Flexible Filer + + +Arguments: + + pFrame - Pattern Set by the protocols + FrameLength - Length of the Pattern + pMask - Mask set by the Protocols + MaskLength - Length of the Mask + pSignature - caller allocated return structure + +Return Value: + Returns Success + Failure - if the Pattern is greater than 129 bytes + +--*/ +{ + + const ULONG Coefficients = 0x04c11db7; + ULONG Signature = 0; + ULONG n = 0; + ULONG i= 0; + PUCHAR pCurrentMaskByte = pMask - 1; // init to -1 + ULONG MaskOffset = 0; + ULONG BitOffsetInMask = 0; + ULONG MaskBit = 0; + ULONG ShiftBy = 0; + UCHAR FrameByte = 0; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> MPCalculateE100PatternForFilter\n"); + + *pSignature = 0; + + do + { + if (FrameLength > 128) + { + status = STATUS_UNSUCCESSFUL; + break; + } + + // The E100 driver can only accept 3 DWORDS of Mask in a single pattern + if (MaskLength > (3*sizeof(ULONG))) + { + status = STATUS_UNSUCCESSFUL; + break; + } + + for (n=i=0;(n<128) && (n < FrameLength); ++n) + { + + // The first half deals with the question - + // Is the nth Frame byte to be included in the Filter + // + + BitOffsetInMask = (n % 8); + + if (BitOffsetInMask == 0) + { + // + // We need to move to a new byte. + // [0] for 0th byte, [1] for 8th byte, [2] for 16th byte, etc. + // + MaskOffset = n/8; // This is the new byte we need to go + + // + // + if (MaskOffset == MaskLength) + { + break; + } + + pCurrentMaskByte ++; + ASSERT (*pCurrentMaskByte == pMask[n/8]); + } + + + // Now look at the actual bit in the mask + MaskBit = 1 << BitOffsetInMask ; + + // If the current Mask Bit is set in the Mask then + // we need to use it in the CRC calculation, otherwise we ignore it + + if (! (MaskBit & pCurrentMaskByte[0])) + { + continue; + } + + // We are suppossed to take in the current byte as part of the CRC calculation + // Initialize the variables + FrameByte = pFrame[n]; + ShiftBy = (i % 3 ) * 8; + + ASSERT (ShiftBy!= 24); // Bit 24 is never used + + if (Signature & 0x80000000) + { + Signature = ((Signature << 1) ^ ( FrameByte << ShiftBy) ^ Coefficients); + } + else + { + Signature = ((Signature << 1 ) ^ (FrameByte << ShiftBy)); + } + ++i; + + } + + // Clear bits 22-31 + Signature &= 0x00ffffff; + + // Update the result + *pSignature = Signature; + + // We have succeeded + status = STATUS_SUCCESS; + + } WHILE (FALSE); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- MPCalculateE100PatternForFilter\n"); + + return status; +} + + +VOID +MPCreateProgrammableFilter ( + IN PMP_WAKE_PATTERN pMpWakePattern , + IN PUCHAR pFilter, + IN OUT PULONG pNext + ) +/*++ +Routine Description: + + This function outputs the E100 specific Pattern Signature + used to wake up the machine. + + Section C.2.4 - Load Programmable Filter page C.20 + + +Arguments: + + pMpWakePattern - Filter will be created for this pattern, + pFilter - Filter will be stored here, + pNext - Used for validation . This Ulong will also be incremented by the size + of the filter (in ulongs) + +Return Value: + +--*/ +{ + PUCHAR pCurrentByte = pFilter; + ULONG NumBytesWritten = 0; + PULONG pCurrentUlong = (PULONG)pFilter; + PNDIS_PM_PACKET_PATTERN pNdisPattern = (PNDIS_PM_PACKET_PATTERN)(&pMpWakePattern->Pattern[0]); + ULONG LengthOfFilter = 0; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> MPCreateProgrammableFilter\n"); + + // Is there enough room for this pattern + // + { + // Length in DWORDS + LengthOfFilter = pNdisPattern->MaskSize /4; + + if (pNdisPattern->MaskSize % 4 != 0) + { + LengthOfFilter++; + } + + // Increment LengthOfFilter to account for the 1st DWORD + LengthOfFilter++; + + // We are only allowed 16 DWORDS in a filter + if (*pNext + LengthOfFilter >= 16) + { + // Failure - early exit + return; + } + + } + // Clear the Predefined bit; already cleared in the previous function. + // first , initialize - + *pCurrentUlong = 0; + + // Mask Length goes into Bits 27-29 of the 1st DWORD. MaskSize is measured in DWORDs + { + ULONG dwMaskSize = pNdisPattern->MaskSize /4; + ULONG dwMLen = 0; + + + // If there is a remainder a remainder then increment + if (pNdisPattern->MaskSize % 4 != 0) + { + dwMaskSize++; + } + + + // + // If we fail this assertion, it means our + // MaskSize is greater than 16 bytes. + // This filter should have been failed upfront at the time of the request + // + + ASSERT (0 < dwMaskSize && dwMaskSize < 5); + // + // In the Spec, 0 - Single DWORD maske, 001 - 2 DWORD mask, + // 011 - 3 DWORD mask, 111 - 4 Dword Mask. + // + + if (dwMaskSize == 1) dwMLen = 0; + if (dwMaskSize == 2) dwMLen = 1; + if (dwMaskSize == 3) dwMLen = 3; + if (dwMaskSize == 4) dwMLen = 7; + + // Adjust the Mlen, so it is in the correct position + + dwMLen = (dwMLen << 3); + + + + if (dwMLen != 0) + { + ASSERT (dwMLen <= 0x38 && dwMLen >= 0x08); + } + + // These go into bits 27,28,29 (bits 3,4 and 5 of the 4th byte) + pCurrentByte[3] |= dwMLen ; + + + } + + // Add the signature to bits 0-23 of the 1st DWORD + { + PUCHAR pSignature = (PUCHAR)&pMpWakePattern->Signature; + + + // Bits 0-23 are also the 1st three bytes of the DWORD + pCurrentByte[0] = pSignature[0]; + pCurrentByte[1] = pSignature[1]; + pCurrentByte[2] = pSignature[2]; + + } + + + // Lets move to the next DWORD. Init variables + pCurrentByte += 4 ; + NumBytesWritten = 4; + pCurrentUlong = (PULONG)pCurrentByte; + + // We Copy in the Mask over here + { + // The Mask is at the end of the pattern + + PUCHAR pMask = (PUCHAR)pNdisPattern + sizeof(*pNdisPattern); + + //Dump (pMask,pNdisPattern->MaskSize, 0,1); + + RtlMoveMemory (pCurrentByte, pMask, pNdisPattern->MaskSize); + + NumBytesWritten += pNdisPattern->MaskSize; + + } + + + // Update the output value + { + ULONG NumUlongs = (NumBytesWritten /4); + + if ((NumBytesWritten %4) != 0) + { + NumUlongs ++; + } + + ASSERT (NumUlongs == LengthOfFilter); + + *pNext = *pNext + NumUlongs; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- MPCreateProgrammableFilter\n"); + + return; +} + +NTSTATUS +MPSetPowerD0( + PFDO_DATA FdoData + ) +/*++ +Routine Description: + + This routine is called when the adapter receives a SetPower + to D0. + +Arguments: + + Adapter Pointer to the adapter structure + PowerState NewPowerState + +Return Value: + + +--*/ +{ + NTSTATUS status; + //KIRQL oldIrql; + + // + // MPSetPowerD0Private Initializes the adapte, issues a selective reset. + // + MPSetPowerD0Private (FdoData); + ASSERT(FdoData->DevicePowerState == PowerDeviceD0); + // + // Set up the packet filter + // + + WdfSpinLockAcquire(FdoData->Lock); + status = NICSetPacketFilter( + FdoData, + FdoData->OldPacketFilter); + // + // If Set Packet Filter succeeds, restore the old packet filter + // + if (status == STATUS_SUCCESS) + { + FdoData->PacketFilter = FdoData->OldPacketFilter; + } + + + WdfSpinLockRelease(FdoData->Lock); + + // + // Set up the multicast list address + // + + WdfSpinLockAcquire(FdoData->RcvLock); + + status = NICSetMulticastList(FdoData); + + NICStartRecv(FdoData); + + + WdfSpinLockRelease(FdoData->RcvLock); + + return status; +} + +NTSTATUS +MPSetPowerLow( + PFDO_DATA FdoData, + WDF_POWER_DEVICE_STATE PowerState + ) +/*++ +Routine Description: + + This routine is called when the FdoData receives a SetPower + to a PowerState > D0 + +Arguments: + + FdoData Pointer to the FdoData structure + PowerState NewPowerState + +Return Value: + NDIS_STATUS_SUCCESS + NDIS_STATUS_PENDING + STATUS_DEVICE_DATA_ERROR + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER( PowerState ); + + // + // Stop sending packets. Create a new flag and make it part + // of the Send Fail Mask. TODO: Does something need to happen here? + // + + // + // Stop hardware from receiving packets - Set the RU to idle. + // TODO: Does something need to happen here? + // + + // + // Check the current status of the receive unit + // + if ((FdoData->CSRAddress->ScbStatus & SCB_RUS_MASK) != SCB_RUS_IDLE) + { + // + // Issue an RU abort. Since an interrupt will be issued, the + // RU will be started by the DPC. + // + status = D100IssueScbCommand(FdoData, SCB_RUC_ABORT, TRUE); + } + + if (!NT_SUCCESS(status)) { + return status; + } + + // + // MPSetPowerLowPrivate first disables the interrupt, acknowledges all the pending + // interrupts and sets FdoData->DevicePowerState to the given low power state + // then starts Hardware specific part of the transition to low power state + // Setting up wake-up patterns, filters, wake-up events etc + // + // Interrupt is disabled and disconnected before entering D0Exit, so no need to + // sychronize. + // + + MPSetPowerLowPrivate(NULL, FdoData); + + return STATUS_SUCCESS; +} + +BOOLEAN +MPAreTwoPatternsEqual( + IN PNDIS_PM_PACKET_PATTERN pNdisPattern1, + IN PNDIS_PM_PACKET_PATTERN pNdisPattern2 + ) +/*++ +Routine Description: + + This routine will compare two wake up patterns to see if they are equal + +Arguments: + + pNdisPattern1 - Pattern1 + pNdisPattern2 - Pattern 2 + + +Return Value: + + True - if patterns are equal + False - Otherwise +--*/ +{ + BOOLEAN bEqual = FALSE; + + // Local variables used later in the compare section of this function + PUCHAR pMask1, pMask2; + PUCHAR pPattern1, pPattern2; + UINT MaskSize, PatternSize; + + do + { + + bEqual = (BOOLEAN)(pNdisPattern1->Priority == pNdisPattern2->Priority); + + if (bEqual == FALSE) + { + break; + } + + bEqual = (BOOLEAN)(pNdisPattern1->MaskSize == pNdisPattern2->MaskSize); + if (bEqual == FALSE) + { + break; + } + + // + // Verify the Mask + // + MaskSize = pNdisPattern1->MaskSize ; + pMask1 = (PUCHAR) pNdisPattern1 + sizeof (NDIS_PM_PACKET_PATTERN); + pMask2 = (PUCHAR) pNdisPattern2 + sizeof (NDIS_PM_PACKET_PATTERN); + + bEqual = (BOOLEAN)RtlEqualMemory (pMask1, pMask2, MaskSize); + + if (bEqual == FALSE) + { + break; + } + + // + // Verify the Pattern + // + bEqual = (BOOLEAN)(pNdisPattern1->PatternSize == pNdisPattern2->PatternSize); + + if (bEqual == FALSE) + { + break; + } + + PatternSize = pNdisPattern2->PatternSize; + pPattern1 = (PUCHAR) pNdisPattern1 + pNdisPattern1->PatternOffset; + pPattern2 = (PUCHAR) pNdisPattern2 + pNdisPattern2->PatternOffset; + + bEqual = (BOOLEAN)RtlEqualMemory (pPattern1, pPattern2, PatternSize ); + + if (bEqual == FALSE) + { + break; + } + + } WHILE (FALSE); + + return bEqual; +} + +VOID +NICExtractPMInfoFromPciSpace( + PFDO_DATA FdoData, + PUCHAR pPciConfig + ) +/*++ +Routine Description: + + Looks at the PM information in the + device specific section of the PCI Config space. + + Interprets the register values and stores it + in the adapter structure + + Definitions from Table 4.2 & 4.3, Pg 4-9 & 4-10 + of the 10/100 Mbit Ethernet Family Software Technical + Reference Manual + + +Arguments: + + Adapter Pointer to our adapter + pPciConfig Pointer to Common Pci Space + +Return Value: + +--*/ +{ + PMP_PM_PCI_SPACE pPmPciConfig = (PMP_PM_PCI_SPACE )pPciConfig; + MP_PMCSR PMCSR; + + // + // First interpret the PM Capabities register + // + { + MP_PM_CAP_REG PmCaps; + + PmCaps = pPmPciConfig->PMCaps; + + if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D0) + { + FdoData->PoMgmt.bWakeFromD0 = TRUE; + } + + if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D1) + { + FdoData->PoMgmt.bWakeFromD1 = TRUE; + } + + if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D2) + { + FdoData->PoMgmt.bWakeFromD2 = TRUE; + } + + if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D3HOT) + { + FdoData->PoMgmt.bWakeFromD3Hot = TRUE; + } + + if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D3_AUX) + { + FdoData->PoMgmt.bWakeFromD3Aux = TRUE; + } + + } + + // + // Interpret the PM Control/Status Register + // + { + PMCSR = pPmPciConfig->PMCSR; + + if (PMCSR.PME_En == 1) + { + // + // PME is enabled. Clear the PME_En bit. + // So that it is not asserted + // + MpClearPME_En (FdoData,PMCSR); + + } + + } + +} + + +NTSTATUS +NICSetPower( + PFDO_DATA FdoData , + WDF_POWER_DEVICE_STATE PowerState + ) +/*++ +Routine Description: + + This routine is called when the FdoData receives a SetPower + request. It redirects the call to an appropriate routine to + Set the New PowerState + +Arguments: + + FdoData Pointer to the FdoData structure + PowerState NewPowerState + +Return Value: + + NTSTATUS Code + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + if(IsPoMgmtSupported(FdoData)){ + + if (PowerState == PowerDeviceD0) + { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "Entering fully on state\n"); + MPSetPowerD0 (FdoData); + } + else + { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "Entering a deeper sleep state\n"); + status = MPSetPowerLow (FdoData, PowerState); + } + } + + return status; +} + + + +NTSTATUS +NICAddWakeUpPattern( + IN PFDO_DATA FdoData, + IN PVOID InformationBuffer, + IN UINT InformationBufferLength, + OUT PULONG BytesRead, + OUT PULONG BytesNeeded + ) +/*++ +Routine Description: + + This routine will allocate a local memory structure, copy the pattern, + insert the pattern into a linked list and return success + + We are gauranteed that we wll get only one request at a time, so this is implemented + without locks. + +Arguments: + + FdoData FdoData structure + InformationBuffer Wake up Pattern + InformationBufferLength Wake Up Pattern Length + +Return Value: + + STATUS_Success - if successful. + STATUS_UNSUCCESSFUL - if memory allocation fails. + +--*/ +{ + + NTSTATUS status = STATUS_UNSUCCESSFUL; + PMP_WAKE_PATTERN pWakeUpPattern = NULL; + ULONG AllocationLength = 0; + PNDIS_PM_PACKET_PATTERN pPmPattern = NULL; + ULONG Signature = 0; + ULONG CopyLength = 0; + ULONG safeAddResult; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> NICAddWakeUpPattern\n"); + + do + { + + if(!FdoData->AllowWakeArming) { + status = STATUS_NOT_SUPPORTED; + break; + } + + pPmPattern = (PNDIS_PM_PACKET_PATTERN) InformationBuffer; + + if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) + { + status = STATUS_BUFFER_TOO_SMALL; + + *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); + break; + } + + // + // safeAddResult = pPmPattern->PatternOffset + pPmPattern->PatternSize + // + status = RtlULongAdd( + pPmPattern->PatternOffset, + pPmPattern->PatternSize, + &safeAddResult) ; + if (!NT_SUCCESS(status)) + { + break; + } + if (InformationBufferLength < safeAddResult) + { + status = STATUS_BUFFER_TOO_SMALL; + + *BytesNeeded = safeAddResult; + break; + } + + *BytesRead = safeAddResult; + + + // + // Calculate the e100 signature + // + status = MPCalculateE100PatternForFilter ( + (PUCHAR)pPmPattern+ pPmPattern->PatternOffset, + pPmPattern->PatternSize, + (PUCHAR)pPmPattern +sizeof(NDIS_PM_PACKET_PATTERN), + pPmPattern->MaskSize, + &Signature ); + + if ( status != STATUS_SUCCESS) + { + break; + } + + CopyLength = safeAddResult; + + // + // Allocate the memory to hold the WakeUp Pattern + // + // AllocationLength = sizeof (MP_WAKE_PATTERN) + CopyLength; + // + status = RtlULongAdd( + sizeof(MP_WAKE_PATTERN), + CopyLength, + &AllocationLength); + if (!NT_SUCCESS(status)) + { + break; + } + + pWakeUpPattern = ExAllocatePoolWithTag(NonPagedPool, AllocationLength, PCIDRV_POOL_TAG); + + if (!pWakeUpPattern) + { + break; + } + + // + // Initialize pWakeUpPattern + // + RtlZeroMemory (pWakeUpPattern, AllocationLength); + + pWakeUpPattern->AllocationSize = AllocationLength; + + pWakeUpPattern->Signature = Signature; + + // + // Copy the pattern into local memory + // + RtlMoveMemory (&pWakeUpPattern->Pattern[0], InformationBuffer, CopyLength); + + ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); + + // + // Insert the pattern into the list + // + /* ExInterlockedInsertHeadList (&FdoData->PoMgmt.PatternList, + &pWakeUpPattern->linkListEntry, + &FdoData->Lock); + */ + + WdfSpinLockAcquire(FdoData->Lock); + InsertHeadList(&FdoData->PoMgmt.PatternList,&pWakeUpPattern->linkListEntry ); + WdfSpinLockRelease(FdoData->Lock); + + + status = STATUS_SUCCESS; + + } WHILE (FALSE); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- NICAddWakeUpPattern\n"); + + return status; +} + +NTSTATUS +NICRemoveWakeUpPattern( + IN PFDO_DATA FdoData, + IN PVOID InformationBuffer, + IN UINT InformationBufferLength, + OUT PULONG BytesRead, + OUT PULONG BytesNeeded + ) +/*++ +Routine Description: + + This routine will walk the list of wake up pattern and attempt to match the wake up pattern. + If it finds a copy , it will remove that WakeUpPattern + +Arguments: + + FdoData FdoData structure + InformationBuffer Wake up Pattern + InformationBufferLength Wake Up Pattern Length + +Return Value: + + Success - if successful. + STATUS_UNSUCCESSFUL - if memory allocation fails. + +--*/ +{ + + NTSTATUS status = STATUS_UNSUCCESSFUL; + PNDIS_PM_PACKET_PATTERN pReqPattern = (PNDIS_PM_PACKET_PATTERN)InformationBuffer; + PLIST_ENTRY pPatternEntry = ListNext(&FdoData->PoMgmt.PatternList) ; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> NICRemoveWakeUpPattern\n"); + + do + { + if(!FdoData->AllowWakeArming) { + status = STATUS_NOT_SUPPORTED; + break; + } + + if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) + { + status = STATUS_BUFFER_TOO_SMALL; + + *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); + break; + } + if (InformationBufferLength < pReqPattern->PatternOffset + pReqPattern->PatternSize) + { + status = STATUS_BUFFER_TOO_SMALL; + + *BytesNeeded = pReqPattern->PatternOffset + pReqPattern->PatternSize; + break; + } + + *BytesRead = pReqPattern->PatternOffset + pReqPattern->PatternSize; + + while (pPatternEntry != (&FdoData->PoMgmt.PatternList)) + { + BOOLEAN bIsThisThePattern = FALSE; + PMP_WAKE_PATTERN pWakeUpPattern = NULL; + PNDIS_PM_PACKET_PATTERN pCurrPattern = NULL;; + + // + // initialize local variables + // + pWakeUpPattern = CONTAINING_RECORD(pPatternEntry, MP_WAKE_PATTERN, linkListEntry); + + pCurrPattern = (PNDIS_PM_PACKET_PATTERN)&pWakeUpPattern->Pattern[0]; + + // + // increment the iterator + // + pPatternEntry = ListNext (pPatternEntry); + + // + // Begin Check : Is (pCurrPattern == pReqPattern) + // + bIsThisThePattern = MPAreTwoPatternsEqual(pReqPattern, pCurrPattern); + + + if (bIsThisThePattern == TRUE) + { + // + // we have a match - remove the entry + // + RemoveEntryList (&pWakeUpPattern->linkListEntry); + + // + // Free the entry + // + ExFreePoolWithTag(pWakeUpPattern, PCIDRV_POOL_TAG); + + status = STATUS_SUCCESS; + break; + } + + } + + } WHILE (FALSE); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- NICRemoveWakeUpPattern\n"); + + return status; +} + + + +VOID +NICRemoveAllWakeUpPatterns( + PFDO_DATA FdoData + ) +/*++ +Routine Description: + + This routine will walk the list of wake up pattern and free it + +Arguments: + + FdoData FdoData structure + +Return Value: + + Success - if successful. + +--*/ +{ + + PLIST_ENTRY pPatternEntry = ListNext(&FdoData->PoMgmt.PatternList) ; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> NICRemoveAllWakeUpPatterns\n"); + + while (pPatternEntry != (&FdoData->PoMgmt.PatternList)) + { + PMP_WAKE_PATTERN pWakeUpPattern = NULL; + + // + // initialize local variables + // + pWakeUpPattern = CONTAINING_RECORD(pPatternEntry, MP_WAKE_PATTERN,linkListEntry); + + // + // increment the iterator + // + pPatternEntry = ListNext (pPatternEntry); + + // + // Remove the entry from the list + // + RemoveEntryList (&pWakeUpPattern->linkListEntry); + + // + // Free the memory + // + ExFreePoolWithTag(pWakeUpPattern, PCIDRV_POOL_TAG); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- NICRemoveAllWakeUpPatterns\n"); + +} + + +NTSTATUS +NICConfigureForWakeUp( + IN PFDO_DATA FdoData, + IN BOOLEAN AddPattern + ) +/*++ +Routine Description: + + +Arguments: + + FdoData FdoData structure + +Return Value: + + Success - if successful. + +--*/ +{ +#define MAX_WAKEUP_PATTERN_LENGTH 128 + + UCHAR Buffer[sizeof(NDIS_PM_PACKET_PATTERN) + + MAX_WAKEUP_PATTERN_LENGTH]; + PCHAR patternBuffer, nextMask, nextPattern; + ULONG maskLen; + PNDIS_PM_PACKET_PATTERN ndisPattern; + ULONG bufLen; + NTSTATUS status; + ULONG unUsed; + CHAR wakePattern[]={0xff,0xff,0xff,0xff,0xff,0xff}; //broadcast address + + patternBuffer = (PCHAR)&Buffer[0]; + + ndisPattern = (PNDIS_PM_PACKET_PATTERN)patternBuffer; + RtlZeroMemory(ndisPattern, sizeof(NDIS_PM_PACKET_PATTERN)); + + + ndisPattern->PatternSize = sizeof(wakePattern); + + maskLen = (ndisPattern->PatternSize-1)/8 + 1; + + nextMask = (PCHAR)patternBuffer + sizeof(NDIS_PM_PACKET_PATTERN); + + nextPattern = nextMask + maskLen; + + *nextMask = 0x3f; + + ndisPattern->MaskSize = maskLen; + ndisPattern->PatternOffset = (ULONG) ((ULONG_PTR) nextPattern - (ULONG_PTR) patternBuffer); + + bufLen = sizeof(NDIS_PM_PACKET_PATTERN) + maskLen + ndisPattern->PatternSize; + + RtlCopyMemory(nextPattern, FdoData->CurrentAddress, ETHERNET_ADDRESS_LENGTH); + + if(AddPattern){ + status = NICAddWakeUpPattern(FdoData, Buffer, bufLen, &unUsed, &unUsed); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "NICAddWakeupPattern failed %x\n", status); + } + }else{ + status = NICRemoveWakeUpPattern(FdoData, Buffer, bufLen, &unUsed, &unUsed); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "NICRemoveWakeUpPattern failed %x\n", status); + } + } + + return status; +} + +#if 0 +NTSTATUS +NICConfigureForWakeUp( + IN PFDO_DATA FdoData, + IN BOOLEAN AddPattern + ) +{ +#define MAX_WAKEUP_PATTERN_LENGTH 128 +#define ETHER_IP_ICMP_HEADER_SIZE 14+20+8 + + UCHAR Buffer[sizeof(NDIS_PM_PACKET_PATTERN) + + MAX_WAKEUP_PATTERN_LENGTH]; + PCHAR patternBuffer, nextMask, nextPattern; + ULONG maskLen; + PNDIS_PM_PACKET_PATTERN ndisPattern; + ULONG bufLen; + NTSTATUS status; + ULONG unUsed; + CHAR pingPattern[]={'a','b','c','d','e','f','g','h'}; + + patternBuffer = (PCHAR)&Buffer[0]; + + ndisPattern = (PNDIS_PM_PACKET_PATTERN)patternBuffer; + RtlZeroMemory(ndisPattern, sizeof(NDIS_PM_PACKET_PATTERN)); + + + ndisPattern->PatternSize = ETHER_IP_ICMP_HEADER_SIZE + sizeof(pingPattern); + + maskLen = (ndisPattern->PatternSize-1)/8 + 1; + + nextMask = (PCHAR)patternBuffer + sizeof(NDIS_PM_PACKET_PATTERN); + + nextPattern = nextMask + maskLen; + + *nextMask = 0x0;nextMask++; + *nextMask = 0x0;nextMask++; + *nextMask = 0x0;nextMask++; + *nextMask = 0x0;nextMask++; + *nextMask = 0x0;nextMask++; + *nextMask = 0x3f;nextMask++; + *nextMask = 0x0C; + + ndisPattern->MaskSize = maskLen; + ndisPattern->PatternOffset = (ULONG) ((ULONG_PTR) nextPattern - (ULONG_PTR) patternBuffer); + + bufLen = sizeof(NDIS_PM_PACKET_PATTERN) + maskLen + ndisPattern->PatternSize; + + RtlZeroMemory(nextPattern, ETHER_IP_ICMP_HEADER_SIZE); + nextPattern += ETHER_IP_ICMP_HEADER_SIZE; + + RtlCopyMemory(nextPattern, pingPattern, sizeof(pingPattern)); + + if(AddPattern){ + status = MPAddWakeUpPattern(FdoData, Buffer, bufLen, &unUsed, &unUsed); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "MpAddWakeupPattern failed %x\n", status); + } + }else{ + status = MPRemoveWakeUpPattern(FdoData, Buffer, bufLen, &unUsed, &unUsed); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "MPRemoveWakeUpPattern failed %x\n", status); + } + } + + return status; +} + +#endif + + diff --git a/general/pcidrv/kmdf/HW/nic_recv.c b/general/pcidrv/kmdf/HW/nic_recv.c new file mode 100644 index 00000000..41cc0ec7 --- /dev/null +++ b/general/pcidrv/kmdf/HW/nic_recv.c @@ -0,0 +1,630 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + NIC_RECV.C + +Abstract: + This module contains miniport receive routines + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" + +#if defined(EVENT_TRACING) +#include "nic_recv.tmh" +#endif + + +_IRQL_requires_same_ +_IRQL_requires_(DISPATCH_LEVEL) +_Requires_lock_held_(FdoData->RcvLock) +VOID +NICHandleRecvInterrupt( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Interrupt handler for receive processing. Put the received packets + into an array and call NICServiceReadIrps. If we run low on + RFDs, allocate another one. + + Assumption: This function is called with the Rcv SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + None + +--*/ +{ + PMP_RFD pMpRfd = NULL; + PHW_RFD pHwRfd = NULL; + + PMP_RFD PacketArray[NIC_DEF_RFDS]; + PMP_RFD PacketFreeArray[NIC_DEF_RFDS]; + UINT PacketArrayCount; + UINT PacketFreeCount; + UINT Index; + UINT LoopIndex = 0; + UINT LoopCount = NIC_MAX_RFDS / NIC_DEF_RFDS + 1; // avoid staying here too long + + BOOLEAN bContinue = TRUE; + BOOLEAN bAllocNewRfd = FALSE; + USHORT PacketStatus; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "---> NICHandleRecvInterrupt\n"); + + ASSERT(FdoData->nReadyRecv >= NIC_MIN_RFDS); + + while (LoopIndex++ < LoopCount && bContinue) + { + PacketArrayCount = 0; + PacketFreeCount = 0; + + // + // Process up to the array size RFD's + // + while (PacketArrayCount < NIC_DEF_RFDS) + { + if (IsListEmpty(&FdoData->RecvList)) + { + ASSERT(FdoData->nReadyRecv == 0); + bContinue = FALSE; + break; + } + + // + // Get the next MP_RFD to process + // + pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); + + // + // Get the associated HW_RFD + // + pHwRfd = pMpRfd->HwRfd; + + // + // Is this packet completed? + // + PacketStatus = NIC_RFD_GET_STATUS(pHwRfd); + if (!NIC_RFD_STATUS_COMPLETED(PacketStatus)) + { + bContinue = FALSE; + break; + } + + // + // HW specific - check if actual count field has been updated + // + if (!NIC_RFD_VALID_ACTUALCOUNT(pHwRfd)) + { + bContinue = FALSE; + break; + } + + + // + // Remove the RFD from the head of the List + // + RemoveEntryList((PLIST_ENTRY)pMpRfd); + FdoData->nReadyRecv--; + + ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RECV_READY)); + MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RECV_READY); + + // + // A good packet? drop it if not. + // + if (!NIC_RFD_STATUS_SUCCESS(PacketStatus)) + { + TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, + "Receive failure = %x\n", PacketStatus); + NICReturnRFD(FdoData, pMpRfd); + continue; + } + + // + // Do not receive any packets until a filter has been set + // + if (!FdoData->PacketFilter) + { + NICReturnRFD(FdoData, pMpRfd); + continue; + } + + // + // Do not receive any packets until we are at D0 + // + if (FdoData->DevicePowerState != PowerDeviceD0) + { + NICReturnRFD(FdoData, pMpRfd); + continue; + } + + pMpRfd->PacketSize = NIC_RFD_GET_PACKET_SIZE(pHwRfd); + + KeFlushIoBuffers(pMpRfd->Mdl, TRUE, TRUE); + + // + // set the status on the packet, either resources or success + // + if (FdoData->nReadyRecv >= MIN_NUM_RFD) + { + MP_SET_FLAG(pMpRfd, fMP_RFD_RECV_PEND); + + } + else + { + MP_SET_FLAG(pMpRfd, fMP_RFD_RESOURCES); + + _Analysis_assume_(PacketFreeCount <= PacketArrayCount); + PacketFreeArray[PacketFreeCount] = pMpRfd; + PacketFreeCount++; + + // + // Reset the RFD shrink count - don't attempt to shrink RFD + // + FdoData->RfdShrinkCount = 0; + + // + // Remember to allocate a new RFD later + // + bAllocNewRfd = TRUE; + } + + PacketArray[PacketArrayCount] = pMpRfd; + PacketArrayCount++; + } + + // + // if we didn't process any receives, just return from here + // + if (PacketArrayCount == 0) + { + break; + } + + + WdfSpinLockRelease(FdoData->RcvLock); + + WdfSpinLockAcquire(FdoData->Lock); + // + // if we have a Recv interrupt and have reported a media disconnect status + // time to indicate the new status + // + + if (Disconnected == FdoData->MediaState) + { + TraceEvents(TRACE_LEVEL_WARNING, DBG_READ, "Media state changed to Connected\n"); + + MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_NO_CABLE); + + FdoData->MediaState = Connected; + + + WdfSpinLockRelease(FdoData->Lock); + // + // Indicate the media event + // + NICServiceIndicateStatusIrp(FdoData); + } + + else + { + + WdfSpinLockRelease(FdoData->Lock); + } + + + NICServiceReadIrps( + FdoData, + PacketArray, + PacketArrayCount); + + + WdfSpinLockAcquire(FdoData->RcvLock); + + // + // Return all the RFDs to the pool. + // + for (Index = 0; Index < PacketFreeCount; Index++) + { + + // + // Get the MP_RFD saved in this packet, in NICAllocRfd + // + pMpRfd = PacketFreeArray[Index]; + + ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RESOURCES)); + MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RESOURCES); + + NICReturnRFD(FdoData, pMpRfd); + } + + } + + // + // If we ran low on RFD's, we need to allocate a new RFD + // + if (bAllocNewRfd) + { + // + // Allocate one more RFD only if it doesn't exceed the max RFD limit + // + if (FdoData->CurrNumRfd < FdoData->MaxNumRfd + && !FdoData->AllocNewRfd) + { + NTSTATUS status; + + FdoData->AllocNewRfd = TRUE; + + // + // Since we are running at DISPATCH_LEVEL, we will queue a workitem + // to allocate RFD memory at PASSIVE_LEVEL. Note that + // AllocateCommonBuffer and FreeCommonBuffer can be called only at + // PASSIVE_LEVEL. + // + status = PciDrvQueuePassiveLevelCallback(FdoData, + NICAllocRfdWorkItem, + NULL, NULL); + if(!NT_SUCCESS(status)){ + FdoData->AllocNewRfd = FALSE; + } + } + } + + ASSERT(FdoData->nReadyRecv >= NIC_MIN_RFDS); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<--- NICHandleRecvInterrupt\n"); +} + +VOID +NICReturnRFD( + IN PFDO_DATA FdoData, + IN PMP_RFD pMpRfd + ) +/*++ +Routine Description: + + Recycle a RFD and put it back onto the receive list + + Assumption: This function is called with the Rcv SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + pMpRfd Pointer to the RFD + +Return Value: + + None + +--*/ +{ + PMP_RFD pLastMpRfd; + PHW_RFD pHwRfd = pMpRfd->HwRfd; + + ASSERT(pMpRfd->Flags == 0); + MP_SET_FLAG(pMpRfd, fMP_RFD_RECV_READY); + + // + // HW_SPECIFIC_START + // + pHwRfd->RfdCbHeader.CbStatus = 0; + pHwRfd->RfdActualCount = 0; + pHwRfd->RfdCbHeader.CbCommand = (RFD_EL_BIT); + pHwRfd->RfdCbHeader.CbLinkPointer = DRIVER_NULL; + + // + // Append this RFD to the RFD chain + if (!IsListEmpty(&FdoData->RecvList)) + { + pLastMpRfd = (PMP_RFD)GetListTailEntry(&FdoData->RecvList); + + // Link it onto the end of the chain dynamically + pHwRfd = pLastMpRfd->HwRfd; + pHwRfd->RfdCbHeader.CbLinkPointer = pMpRfd->HwRfdPhys; + pHwRfd->RfdCbHeader.CbCommand = 0; + } + + // + // HW_SPECIFIC_END + // + + // + // The processing on this RFD is done, so put it back on the tail of + // our list + // + InsertTailList(&FdoData->RecvList, (PLIST_ENTRY)pMpRfd); + FdoData->nReadyRecv++; + ASSERT(FdoData->nReadyRecv <= FdoData->CurrNumRfd); +} + +_Requires_lock_held_(FdoData->RcvLock) +NTSTATUS +NICStartRecv( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Start the receive unit if it's not in a ready state + + Assumption: This function is called with the Rcv SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + NT Status code + +--*/ +{ + PMP_RFD pMpRfd; + NTSTATUS status; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "---> NICStartRecv\n"); + + // + // If the receiver is ready, then don't try to restart. + // + if (NIC_IS_RECV_READY(FdoData)) + { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "Receive unit already active\n"); + return STATUS_SUCCESS; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "Re-start receive unit...\n"); + ASSERT(!IsListEmpty(&FdoData->RecvList)); + + // + // Get the MP_RFD head + // + pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); + + // + // If more packets are received, clean up RFD chain again + // + if (NIC_RFD_GET_STATUS(pMpRfd->HwRfd)) + { + NICHandleRecvInterrupt(FdoData); + ASSERT(!IsListEmpty(&FdoData->RecvList)); + + // + // Get the new MP_RFD head + // + pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); + } + + // + // Wait for the SCB to clear before we set the general pointer + // + if (!WaitScb(FdoData)) + { + status = STATUS_DEVICE_DATA_ERROR; + goto exit; + } + + if (FdoData->DevicePowerState > PowerDeviceD0) + { + status = STATUS_DEVICE_DATA_ERROR; + goto exit; + } + // + // Set the SCB General Pointer to point the current Rfd + // + FdoData->CSRAddress->ScbGeneralPointer = pMpRfd->HwRfdPhys; + + // + // Issue the SCB RU start command + // + status = D100IssueScbCommand(FdoData, SCB_RUC_START, FALSE); + if (status == STATUS_SUCCESS) + { + // wait for the command to be accepted + if (!WaitScb(FdoData)) + { + status = STATUS_DEVICE_DATA_ERROR; + } + } + + exit: + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<--- NICStartRecv, Status=%x\n", status); + return status; +} + + + +VOID +NICResetRecv( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Reset the receive list + + Assumption: This function is called with the Rcv SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + None + +--*/ +{ + PMP_RFD pMpRfd; + PHW_RFD pHwRfd; + ULONG RfdCount; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> NICResetRecv\n"); + + ASSERT(!IsListEmpty(&FdoData->RecvList)); + + // + // Get the MP_RFD head + // + pMpRfd = (PMP_RFD)GetListHeadEntry(&FdoData->RecvList); + for (RfdCount = 0; RfdCount < FdoData->nReadyRecv; RfdCount++) + { + pHwRfd = pMpRfd->HwRfd; + pHwRfd->RfdCbHeader.CbStatus = 0; + + pMpRfd = (PMP_RFD)GetListFLink(&pMpRfd->List); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- NICResetRecv\n"); +} + + +VOID +NICServiceReadIrps( + PFDO_DATA FdoData, + PMP_RFD *PacketArray, + ULONG PacketArrayCount + ) +/*++ +Routine Description: + + Copy the data from the recv buffers to pending read IRP buffers + and complete the IRP. When used as network driver, copy operation + can be avoided by devising a private interface between us and the + NDIS-WDM filter and have the NDIS-WDM edge to indicate our buffers + directly to NDIS. + + Called at DISPATCH_LEVEL. Take advantage of that fact while + acquiring spinlocks. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + None + +--*/ +{ + PMP_RFD pMpRfd = NULL; + ULONG index; + NTSTATUS status; + PVOID buffer; + WDFREQUEST request; + size_t bufLength=0; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "--> NICServiceReadIrps\n"); + + + for(index=0; index < PacketArrayCount; index++) + { + pMpRfd = PacketArray[index]; + ASSERT(pMpRfd); + + status = WdfIoQueueRetrieveNextRequest( FdoData->PendingReadQueue, + &request ); + + if(NT_SUCCESS(status)){ + + WDF_REQUEST_PARAMETERS params; + ULONG length = 0; + + WDF_REQUEST_PARAMETERS_INIT(¶ms); + + WdfRequestGetParameters( + request, + ¶ms + ); + + ASSERT(status == STATUS_SUCCESS); + + bufLength = params.Parameters.Read.Length; + + status = WdfRequestRetrieveOutputBuffer(request, + bufLength, + &buffer, + &bufLength); + if(NT_SUCCESS(status) ) { + + length = min((ULONG)bufLength, pMpRfd->PacketSize); + + RtlCopyMemory(buffer, pMpRfd->Buffer, length); + + Hexdump((TRACE_LEVEL_VERBOSE, DBG_READ, + "Received Packet Data: %!HEXDUMP!\n", + log_xstr(buffer, (USHORT)length))); + FdoData->BytesReceived += length; + } + + WdfRequestCompleteWithInformation(request, status, length); + }else { + ASSERTMSG("WdfIoQueueRetrieveNextRequest failed", + (status == STATUS_NO_MORE_ENTRIES || + status == STATUS_WDF_PAUSED)); + } + + WdfSpinLockAcquire(FdoData->RcvLock); + + ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RECV_PEND)); + MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RECV_PEND); + + + if (FdoData->RfdShrinkCount < NIC_RFD_SHRINK_THRESHOLD) + { + NICReturnRFD(FdoData, pMpRfd); + } + else + { + ASSERT(FdoData->CurrNumRfd > FdoData->NumRfd); + status = PciDrvQueuePassiveLevelCallback(FdoData, + NICFreeRfdWorkItem, (PVOID)pMpRfd, + NULL); + if(NT_SUCCESS(status)){ + + FdoData->RfdShrinkCount = 0; + FdoData->CurrNumRfd--; + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "Shrink... CurrNumRfd = %d\n", + FdoData->CurrNumRfd); + } else { + // + // We couldn't queue a workitem to free memory, so let us + // put that back in the main pool and try again next time. + // + NICReturnRFD(FdoData, pMpRfd); + } + } + + + WdfSpinLockRelease(FdoData->RcvLock); + + }// end of loop + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "<-- NICServiceReadIrps\n"); + + return; + +} + + + + diff --git a/general/pcidrv/kmdf/HW/nic_req.c b/general/pcidrv/kmdf/HW/nic_req.c new file mode 100644 index 00000000..2621ee9f --- /dev/null +++ b/general/pcidrv/kmdf/HW/nic_req.c @@ -0,0 +1,1453 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + mp_req.c + +Abstract: + This module handle NDIS OID ioctls. This module is not required + if the upper edge is not NDIS. + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" + +#if defined(EVENT_TRACING) +#include "nic_req.tmh" +#endif + +// +// Following status values are copied from NDIS.H +// +#define NDIS_STATUS_MEDIA_CONNECT 0x4001000BL +#define NDIS_STATUS_MEDIA_DISCONNECT 0x4001000CL + + +PCHAR DbgGetOidName(ULONG oid); + + +VOID +NICHandleQueryOidRequest( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + WDF_REQUEST_PARAMETERS *Params + ) +/*++ + +Routine Description: + + Query an arbitrary OID value from the miniport. + +Arguments: + + Queue - Default queue handle + Request - IOCTL request handle + Params - pointer to params structure for the request. This is + equivalent to the IRP stack location pointer. + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PNDISPROT_QUERY_OID pQuery = NULL; + NDIS_OID Oid = 0; + ULONG ulInfo = 0; + ULONG64 ul64Info = 0; + PVOID pInfo = (PVOID) &ulInfo; + ULONG ulInfoLen = sizeof(ulInfo); + PVOID InformationBuffer = NULL; + ULONG InformationBufferLength = 0; + MEDIA_STATE CurrMediaState; + PVOID DataBuffer; + size_t BufferLength; + NDIS_PNP_CAPABILITIES Power_Management_Capabilities; + PFDO_DATA FdoData = NULL; + + + UNREFERENCED_PARAMETER( Params ); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, + "--> HandleQueryOIDRequest \n"); + + FdoData = FdoGetData(WdfIoQueueGetDevice(Queue)); + + // + // Since the IOCTL is buffered, WdfRequestRetrieveOutputBuffer & + // WdfRequestRetrieveInputBuffer return the same buffer pointer. + // So make sure you read all the information you need from + // the buffer before you write to it. + // + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof(NDISPROT_QUERY_OID), + &DataBuffer, + &BufferLength); + if( !NT_SUCCESS(status) ) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTLS, + "WdfRequestRetrieveInputBuffer failed 0x%x\n", status); + WdfRequestComplete(Request, status); + return; + } + + do { + + pQuery = (PNDISPROT_QUERY_OID)DataBuffer; + Oid = pQuery->Oid; + if(OID_GEN_LINK_SPEED != Oid) { // To avoid flood of trace messages + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "\t%s\n", + DbgGetOidName(Oid)); + } + InformationBuffer = &pQuery->Data[0]; + InformationBufferLength = (ULONG)BufferLength - + FIELD_OFFSET(NDISPROT_QUERY_OID, Data); + + switch(Oid) + { + + case OID_GEN_LINK_SPEED: + case OID_GEN_MEDIA_CONNECT_STATUS: + + if (InformationBufferLength < sizeof(ULONG)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + + WdfSpinLockAcquire(FdoData->Lock); + if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION)) + { + status = WdfRequestForwardToIoQueue(Request, + FdoData->PendingIoctlQueue); + + WdfSpinLockRelease(FdoData->Lock); + if(NT_SUCCESS(status)) { + goto End; + } + break; + } + else + { + + WdfSpinLockRelease(FdoData->Lock); + if (Oid == OID_GEN_LINK_SPEED) + { + ulInfo = FdoData->usLinkSpeed * 10000; + } else { + + CurrMediaState = NICIndicateMediaState(FdoData); + ulInfo = CurrMediaState; + } + } + break; + + case OID_802_3_PERMANENT_ADDRESS: + + if (InformationBufferLength < ETH_LENGTH_OF_ADDRESS) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + pInfo = FdoData->PermanentAddress; + ulInfoLen = ETH_LENGTH_OF_ADDRESS; + break; + + case OID_802_3_CURRENT_ADDRESS: + + if (InformationBufferLength < ETH_LENGTH_OF_ADDRESS) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + pInfo = FdoData->CurrentAddress; + ulInfoLen = ETH_LENGTH_OF_ADDRESS; + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + + if (InformationBufferLength < sizeof(ULONG)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + ulInfo = NIC_MAX_MCAST_LIST; + break; + + case OID_GEN_XMIT_OK: + case OID_GEN_RCV_OK: + case OID_GEN_XMIT_ERROR: + case OID_GEN_RCV_ERROR: + case OID_GEN_RCV_NO_BUFFER: + case OID_GEN_RCV_CRC_ERROR: + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + case OID_802_3_RCV_ERROR_ALIGNMENT: + case OID_802_3_XMIT_ONE_COLLISION: + case OID_802_3_XMIT_MORE_COLLISIONS: + case OID_802_3_XMIT_DEFERRED: + case OID_802_3_XMIT_MAX_COLLISIONS: + case OID_802_3_RCV_OVERRUN: + case OID_802_3_XMIT_UNDERRUN: + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + case OID_802_3_XMIT_TIMES_CRS_LOST: + case OID_802_3_XMIT_LATE_COLLISIONS: + + if (InformationBufferLength < sizeof(ULONG)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + ulInfoLen = sizeof(ul64Info); + status = NICGetStatsCounters(FdoData, Oid, &ul64Info); + if (status == STATUS_SUCCESS) + { + ulInfoLen = min(InformationBufferLength, ulInfoLen); + pInfo = &ul64Info; + } + break; + + case OID_PNP_CAPABILITIES: + // + // This query is sent during init to get the PNP capabilities of the device. + // + NICFillPoMgmtCaps (FdoData, + &Power_Management_Capabilities, + (PNDIS_STATUS) &status, + &ulInfoLen); + if (status == STATUS_SUCCESS && + ulInfoLen <= InformationBufferLength) + { + pInfo = (PVOID) &Power_Management_Capabilities; + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + pInfo = NULL; + } + break; + + case OID_PNP_QUERY_POWER: + // + // NDIS sends the query when it receives Query-DIRP. + // As a power policy owner, NDIS generates query-DIRP when it + // receives query-SIRP from the system. + // NDIS will forward the D-IRP to lower stack only if we answer this query + // successfully. + // + status = STATUS_SUCCESS; + break; + + default: + status = STATUS_NOT_SUPPORTED; + break; + } + + } WHILE (FALSE); + + if (status == STATUS_SUCCESS) + { + RtlMoveMemory(InformationBuffer, pInfo, ulInfoLen); + } + // + // Adjust the size to include the structure. + // + ulInfoLen += FIELD_OFFSET(NDISPROT_QUERY_OID, Data); + WdfRequestCompleteWithInformation(Request, status, ulInfoLen); + +End: + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, + "<--HandleQueryOIDRequest: Status %x\n", + status); + + return; + +} + +VOID +NICHandleSetOidRequest( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + WDF_REQUEST_PARAMETERS *Params + ) +/*++ + +Routine Description: + + This routine is called to handle set OID request sent in the ioctl buffer. + If the device is busy, we will forward the request into a queue and complete + it later in the DPC. + +Arguments: + + Queue - Default queue handle + Request - IOCTL request handle + Params - pointer to params structure for the request. This is + equivalent to the IRP stack location pointer. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PNDISPROT_SET_OID pSet; + NDIS_OID Oid; + ULONG PacketFilter; + PVOID InformationBuffer = NULL; + ULONG InformationBufferLength = 0; + PVOID DataBuffer; + size_t BufferLength; + ULONG unUsed; + WDF_POWER_DEVICE_STATE newDeviceState; + WDF_POWER_DEVICE_STATE oldDeviceState; + PFDO_DATA FdoData = NULL; + + UNREFERENCED_PARAMETER( Params ); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, + "--> HandleSetOIDRequest\n"); + + FdoData = FdoGetData(WdfIoQueueGetDevice(Queue)); + + // + // Since the IOCTL is buffered, WdfRequestRetrieveOutputBuffer & + // WdfRequestRetrieveInputBuffer return the same buffer pointer. + // So make sure you read all the information you need from + // the buffer before you write to it. + // + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(NDISPROT_SET_OID), + &DataBuffer, + &BufferLength); + if( !NT_SUCCESS(status) ) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTLS, + "WdfRequestRetrieveInputBuffer failed 0x%x\n", + status); + WdfRequestComplete(Request, status); + return; + } + + Oid = 0; + + do { + + if (BufferLength < sizeof(NDISPROT_SET_OID)) + { + status = STATUS_BUFFER_OVERFLOW; + break; + } + + pSet = (PNDISPROT_SET_OID)DataBuffer; + Oid = pSet->Oid; + InformationBuffer = &pSet->Data[0]; + InformationBufferLength = + (ULONG)BufferLength - FIELD_OFFSET(NDISPROT_SET_OID, Data); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "\t%s\n", + DbgGetOidName(Oid)); + + switch(Oid) + { + + case OID_802_3_MULTICAST_LIST: + // + // Verify the length + // + if (InformationBufferLength % ETH_LENGTH_OF_ADDRESS != 0) + { + status = STATUS_INVALID_BUFFER_SIZE; + break; + + } + + // + // Save the number of MC list size + // + FdoData->MCAddressCount = InformationBufferLength / ETH_LENGTH_OF_ADDRESS; + ASSERT(FdoData->MCAddressCount <= NIC_MAX_MCAST_LIST); + + // + // Save the MC list + // + RtlMoveMemory( + FdoData->MCList, + InformationBuffer, + InformationBufferLength); + + + WdfSpinLockAcquire(FdoData->Lock); + + WdfSpinLockAcquire(FdoData->RcvLock); + + status = NICSetMulticastList(FdoData); + + + WdfSpinLockRelease(FdoData->RcvLock); + + WdfSpinLockRelease(FdoData->Lock); + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + // + // Verify the Length + // + if (InformationBufferLength != sizeof(ULONG)) + { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + RtlMoveMemory(&PacketFilter, InformationBuffer, sizeof(ULONG)); + + // + // any bits not supported? + // + if (PacketFilter & ~NIC_SUPPORTED_FILTERS) + { + status = STATUS_NOT_SUPPORTED; + break; + } + + // + // any filtering changes? + // + if (PacketFilter == FdoData->PacketFilter) + { + break; + } + + + WdfSpinLockAcquire(FdoData->Lock); + + WdfSpinLockAcquire(FdoData->RcvLock); + + if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION)) + { + + status = WdfRequestForwardToIoQueue(Request, + FdoData->PendingIoctlQueue); + WdfSpinLockRelease(FdoData->RcvLock); + WdfSpinLockRelease(FdoData->Lock); + + if(NT_SUCCESS(status)) { + goto End; + } + + break; + } + + status = NICSetPacketFilter( + FdoData, + PacketFilter); + + + WdfSpinLockRelease(FdoData->RcvLock); + + WdfSpinLockRelease(FdoData->Lock); + + if (status == STATUS_SUCCESS) + { + FdoData->PacketFilter = PacketFilter; + } + + break; + + case OID_PNP_SET_POWER: + + // + // NDIS sends this query when it receives Set D-IRP. As a power policy + // owner, it requests a set D-IRP when it recieves a set S-IRP from the system. + // + if (InformationBufferLength != sizeof(NDIS_DEVICE_POWER_STATE )) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + newDeviceState = *(PDEVICE_POWER_STATE UNALIGNED)InformationBuffer; + oldDeviceState = FdoData->DevicePowerState; + FdoData->DevicePowerState = newDeviceState; + // + // Set the power state - Cannot fail this request. + // + status = NICSetPower(FdoData, newDeviceState ); + + if (status != STATUS_SUCCESS) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTLS, "SET Power: Hardware error !!!\n"); + break; + } + + status = STATUS_SUCCESS; + break; + + case OID_PNP_ADD_WAKE_UP_PATTERN: + // + // call a function that would program the adapter's wake + // up pattern, return success + // + + status = NICAddWakeUpPattern(FdoData, + InformationBuffer, + InformationBufferLength, + &unUsed, + &unUsed); + break; + + + case OID_PNP_REMOVE_WAKE_UP_PATTERN: + + // + // call a function that would remove the adapter's wake + // up pattern, return success + // + + status = NICRemoveWakeUpPattern(FdoData, + InformationBuffer, + InformationBufferLength, + &unUsed, + &unUsed); + + break; + + case OID_PNP_ENABLE_WAKE_UP: + // + // call a function that would enable wake up on the adapter + // return success + // + if (IsPoMgmtSupported(FdoData)) + { + ULONG WakeUpEnable; + RtlMoveMemory(&WakeUpEnable, InformationBuffer,sizeof(ULONG)); + // + // The WakeUpEable can only be 0, or NDIS_PNP_WAKE_UP_PATTERN_MATCH since the driver only + // supports wake up pattern match + // + if ((WakeUpEnable != 0) + && ((WakeUpEnable & NDIS_PNP_WAKE_UP_PATTERN_MATCH) != NDIS_PNP_WAKE_UP_PATTERN_MATCH )) + { + status = STATUS_NOT_SUPPORTED; + FdoData->AllowWakeArming = FALSE; + break; + } + // + // When the driver goes to low power state, it would check WakeUpEnable to decide + // which wake up methed it should use to wake up the machine. If WakeUpEnable is 0, + // no wake up method is enabled. + // + FdoData->AllowWakeArming = TRUE; + + status = STATUS_SUCCESS; + } + else + { + status = STATUS_NOT_SUPPORTED; + } + + break; + default: + status = STATUS_NOT_SUPPORTED; + break; + } + } WHILE (FALSE); + + WdfRequestCompleteWithInformation(Request, status, 0); + +End: + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, + "<-- HandleSetOIDRequest %x\n", status); + + return; +} + + +VOID +NICServiceIndicateStatusIrp( + IN PFDO_DATA FdoData + ) +/*++ + +Routine Description: + + We process the IRP based on the input arguments and complete + the IRP. If the IRP was cancelled for some reason we will let + the cancel routine do the IRP completion. + +Arguments: + + Cancel - Should the IRP be cancelled right away. + +Return Value: + + None + +--*/ +{ + PNDISPROT_INDICATE_STATUS pIndicateStatus = NULL; + NTSTATUS status; + ULONG bytes = 0; + size_t bufLength; + WDFREQUEST request; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "-->ndisServiceIndicateStatusIrp\n"); + + status = NICGetIoctlRequest(FdoData->PendingIoctlQueue, + IOCTL_NDISPROT_INDICATE_STATUS, + &request); + + if(!NT_SUCCESS(status)) { + return; + } + + // + // Since the IOCTL is buffered, WdfRequestRetrieveOutputBuffer & + // WdfRequestRetrieveInputBuffer return the same buffer pointer. + // So make sure you read all the information you need from + // the buffer before you write to it. + // + status = WdfRequestRetrieveOutputBuffer(request, sizeof(NDISPROT_INDICATE_STATUS), &pIndicateStatus, &bufLength); + if( !NT_SUCCESS(status) ) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTLS, "WdfRequestRetrieveInputBuffer failed 0x%x\n", status); + WdfRequestComplete(request, status); + return; + } + + // + // Check to see whether the buffer is large enough. + // + + + if(MP_TEST_FLAG(FdoData, fMP_ADAPTER_NO_CABLE)){ + pIndicateStatus->IndicatedStatus = NDIS_STATUS_MEDIA_DISCONNECT; + } else { + pIndicateStatus->IndicatedStatus = NDIS_STATUS_MEDIA_CONNECT; + } + + pIndicateStatus->StatusBufferLength = 0; + pIndicateStatus->StatusBufferOffset = 0; + status = STATUS_SUCCESS; + bytes = sizeof(NDISPROT_INDICATE_STATUS); + WdfRequestCompleteWithInformation(request, status, bytes); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "<--ndisServiceIndicateStatusIrp\n"); + return; +} + +NTSTATUS +NICGetIoctlRequest( + IN WDFQUEUE Queue, + IN ULONG FunctionCode, + OUT WDFREQUEST* Request + ) +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + WDF_REQUEST_PARAMETERS params; + WDFREQUEST tagRequest; + WDFREQUEST prevTagRequest; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "--> NICGetIoctlRequest\n"); + + WDF_REQUEST_PARAMETERS_INIT(¶ms); + + *Request = NULL; + prevTagRequest = tagRequest = NULL; + + do { + + WDF_REQUEST_PARAMETERS_INIT(¶ms); + status = WdfIoQueueFindRequest(Queue, + prevTagRequest, + NULL, + ¶ms, + &tagRequest); + + // + // WdfIoQueueFindRequest takes an extra reference on the returned tagRequest to + // prevent the memory from being freed. However, the tagRequest still + // in the queue and can be cancelled or removed by another thread and + // completed. + // + if(prevTagRequest) { + WdfObjectDereference(prevTagRequest); + } + + if(status == STATUS_NO_MORE_ENTRIES) { + status = STATUS_UNSUCCESSFUL; + break; + } + + if(status == STATUS_NOT_FOUND) { + // + // It seems like prevTagRequest disappeared from the + // queue for some reason - either it got cancelled, got + // dispatched to the driver. There might be other requests + // that match our criteria so let us restart the search. + // + prevTagRequest = tagRequest = NULL; + continue; + } + + if( !NT_SUCCESS(status)) { + // + // Something bad happened. + // + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTLS, + "WdfIoQueueFindRequest failed %!STATUS!\n", status); + status = STATUS_UNSUCCESSFUL; + break; + } + + if(FunctionCode == params.Parameters.DeviceIoControl.IoControlCode){ + + status = WdfIoQueueRetrieveFoundRequest( + Queue, + tagRequest, // TagRequest + Request + ); + + WdfObjectDereference(tagRequest); + + if(status == STATUS_NOT_FOUND) { + // + // It seems like the tagrequest disappeared from the + // queue for some reason - either it got cancelled, got + // dispatched to the driver. There might be other requests + // that match our criteria so let us restart the search. + // + prevTagRequest = tagRequest = NULL; + continue; + } + + if( !NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTLS, + "WdfIoQueueRetrieveNextRequest failed %!STATUS!\n", status); + status = STATUS_UNSUCCESSFUL; + break; + } + + // + // We got a request. Drop the extra reference taken by peek request before + // returning the call. + // + ASSERT(*Request == tagRequest); + status = STATUS_SUCCESS; + break; + + }else { + // + // This is not the request we need. We will drop the reference + // on the tagrequest after we looking for the next request. + // + prevTagRequest = tagRequest; + continue; + } + + } WHILE (TRUE); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "<-- NICGetIoctlRequest\n"); + + return status; + +} + +MEDIA_STATE +NICIndicateMediaState( + IN PFDO_DATA FdoData + ) +{ + MEDIA_STATE CurrMediaState; + + + WdfSpinLockAcquire(FdoData->Lock); + + CurrMediaState = GetMediaState(FdoData); + + if (CurrMediaState != FdoData->MediaState) + { + TraceEvents(TRACE_LEVEL_WARNING, DBG_IOCTLS, "Media state changed to %s\n", + ((CurrMediaState == Connected)? + "Connected": "Disconnected")); + + FdoData->MediaState = CurrMediaState; + + if (CurrMediaState == Connected) + { + MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_NO_CABLE); + } + else + { + MP_SET_FLAG(FdoData, fMP_ADAPTER_NO_CABLE); + } + + + WdfSpinLockRelease(FdoData->Lock); + + // Indicate the media event + NICServiceIndicateStatusIrp(FdoData); + } + else + { + + WdfSpinLockRelease(FdoData->Lock); + } + + return CurrMediaState; +} + + +NTSTATUS +NICGetStatsCounters( + IN PFDO_DATA FdoData, + IN NDIS_OID Oid, + OUT PULONG64 pCounter + ) +/*++ +Routine Description: + + Get the value for a statistics OID + +Arguments: + + FdoData Pointer to our FdoData + Oid Self-explanatory + pCounter Pointer to receive the value + +Return Value: + + NT Status code + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "--> NICGetStatsCounters\n"); + + *pCounter = 0; + + DumpStatsCounters(FdoData); + + switch(Oid) + { + case OID_GEN_XMIT_OK: + *pCounter = FdoData->GoodTransmits; + break; + + case OID_GEN_RCV_OK: + *pCounter = FdoData->GoodReceives; + break; + + case OID_GEN_XMIT_ERROR: + *pCounter = FdoData->TxAbortExcessCollisions + + FdoData->TxDmaUnderrun + + FdoData->TxLostCRS + + FdoData->TxLateCollisions; + break; + + case OID_GEN_RCV_ERROR: + *pCounter = FdoData->RcvCrcErrors + + FdoData->RcvAlignmentErrors + + FdoData->RcvResourceErrors + + FdoData->RcvDmaOverrunErrors + + FdoData->RcvRuntErrors; + break; + + case OID_GEN_RCV_NO_BUFFER: + *pCounter = FdoData->RcvResourceErrors; + break; + + case OID_GEN_RCV_CRC_ERROR: + *pCounter = FdoData->RcvCrcErrors; + break; + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + *pCounter = FdoData->nWaitSend; + break; + + case OID_802_3_RCV_ERROR_ALIGNMENT: + *pCounter = FdoData->RcvAlignmentErrors; + break; + + case OID_802_3_XMIT_ONE_COLLISION: + *pCounter = FdoData->OneRetry; + break; + + case OID_802_3_XMIT_MORE_COLLISIONS: + *pCounter = FdoData->MoreThanOneRetry; + break; + + case OID_802_3_XMIT_DEFERRED: + *pCounter = FdoData->TxOKButDeferred; + break; + + case OID_802_3_XMIT_MAX_COLLISIONS: + *pCounter = FdoData->TxAbortExcessCollisions; + break; + + case OID_802_3_RCV_OVERRUN: + *pCounter = FdoData->RcvDmaOverrunErrors; + break; + + case OID_802_3_XMIT_UNDERRUN: + *pCounter = FdoData->TxDmaUnderrun; + break; + + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + *pCounter = FdoData->TxLostCRS; + break; + + case OID_802_3_XMIT_TIMES_CRS_LOST: + *pCounter = FdoData->TxLostCRS; + break; + + case OID_802_3_XMIT_LATE_COLLISIONS: + *pCounter = FdoData->TxLateCollisions; + break; + + default: + status = STATUS_NOT_SUPPORTED; + break; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "<-- NICGetStatsCounters\n"); + + return(status); +} + +NTSTATUS NICSetPacketFilter( + IN PFDO_DATA FdoData, + IN ULONG PacketFilter + ) +/*++ +Routine Description: + + This routine will set up the FdoData so that it accepts packets + that match the specified packet filter. The only filter bits + that can truly be toggled are for broadcast and promiscuous + +Arguments: + + FdoData Pointer to our FdoData + PacketFilter The new packet filter + +Return Value: + + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + UCHAR NewParameterField; + UINT i; + BOOLEAN bResult; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "--> NICSetPacketFilter, PacketFilter=%08x\n", PacketFilter); + + // + // Need to enable or disable broadcast and promiscuous support depending + // on the new filter + // + NewParameterField = CB_557_CFIG_DEFAULT_PARM15; + + if (PacketFilter & NDIS_PACKET_TYPE_BROADCAST) + { + NewParameterField &= ~CB_CFIG_BROADCAST_DIS; + } + else + { + NewParameterField |= CB_CFIG_BROADCAST_DIS; + } + + if (PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) + { + NewParameterField |= CB_CFIG_PROMISCUOUS; + } + else + { + NewParameterField &= ~CB_CFIG_PROMISCUOUS; + } + + do + { + if ((FdoData->OldParameterField == NewParameterField ) && + !(PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST)) + { + break; + } + + // + // Only need to do something to the HW if the filter bits have changed. + // + FdoData->OldParameterField = NewParameterField; + ((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbCommand = CB_CONFIGURE; + ((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbStatus = 0; + ((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbLinkPointer = DRIVER_NULL; + + // + // First fill in the static (end user can't change) config bytes + // + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[0] = CB_557_CFIG_DEFAULT_PARM0; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[2] = CB_557_CFIG_DEFAULT_PARM2; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[3] = CB_557_CFIG_DEFAULT_PARM3; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[6] = CB_557_CFIG_DEFAULT_PARM6; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[9] = CB_557_CFIG_DEFAULT_PARM9; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[10] = CB_557_CFIG_DEFAULT_PARM10; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[11] = CB_557_CFIG_DEFAULT_PARM11; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[12] = CB_557_CFIG_DEFAULT_PARM12; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[13] = CB_557_CFIG_DEFAULT_PARM13; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[14] = CB_557_CFIG_DEFAULT_PARM14; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[16] = CB_557_CFIG_DEFAULT_PARM16; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[17] = CB_557_CFIG_DEFAULT_PARM17; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[18] = CB_557_CFIG_DEFAULT_PARM18; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[20] = CB_557_CFIG_DEFAULT_PARM20; + + // + // Set the Tx underrun retries + // + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[7] = + (UCHAR) (CB_557_CFIG_DEFAULT_PARM7 | (FdoData->AiUnderrunRetry << 1)); + + // + // Set the Tx and Rx Fifo limits + // + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[1] = + (UCHAR) ((FdoData->AiTxFifo << 4) | FdoData->AiRxFifo); + + // + // set the MWI enable bit if needed + // + if (FdoData->MWIEnable) + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[3] |= CB_CFIG_B3_MWI_ENABLE; + + // + // Set the Tx and Rx DMA maximum byte count fields. + // + if ((FdoData->AiRxDmaCount) || (FdoData->AiTxDmaCount)) + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[4] = + FdoData->AiRxDmaCount; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[5] = + (UCHAR) (FdoData->AiTxDmaCount | CB_CFIG_DMBC_EN); + } + else + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[4] = + CB_557_CFIG_DEFAULT_PARM4; + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[5] = + CB_557_CFIG_DEFAULT_PARM5; + } + + // + // Setup for MII or 503 operation. The CRS+CDT bit should only be + // set when operating in 503 mode. + // + if (FdoData->PhyAddress == 32) + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[8] = + (CB_557_CFIG_DEFAULT_PARM8 & (~CB_CFIG_503_MII)); + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[15] = + (UCHAR) (NewParameterField | CB_CFIG_CRS_OR_CDT); + } + else + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[8] = + (CB_557_CFIG_DEFAULT_PARM8 | CB_CFIG_503_MII); + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[15] = + (UCHAR) (NewParameterField & (~CB_CFIG_CRS_OR_CDT)); + } + + // + // Setup Full duplex stuff + // + + // + // If forced to half duplex + // + if (FdoData->AiForceDpx == 1) + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = + (CB_557_CFIG_DEFAULT_PARM19 & + (~(CB_CFIG_FORCE_FDX| CB_CFIG_FDX_ENABLE))); + } + // + // If forced to full duplex + // + else if (FdoData->AiForceDpx == 2) + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = + (CB_557_CFIG_DEFAULT_PARM19 | CB_CFIG_FORCE_FDX); + } + // + // If auto-duplex + // + else + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[19] = + CB_557_CFIG_DEFAULT_PARM19; + } + + // + // if multicast all is being turned on, set the bit + // + if (PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[21] = + (CB_557_CFIG_DEFAULT_PARM21 | CB_CFIG_MULTICAST_ALL); + } + else + { + FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[21] = + CB_557_CFIG_DEFAULT_PARM21; + } + + + // + // Wait for the SCB to clear before we check the CU status. + // + if (!WaitScb(FdoData)) + { + status = STATUS_DEVICE_DATA_ERROR; + break; + } + + // + // If we have issued any transmits, then the CU will either be active, + // or in the suspended state. If the CU is active, then we wait for + // it to be suspended. + // + if (FdoData->TransmitIdle == FALSE) + { + // + // Wait for suspended state + // + MP_STALL_AND_WAIT((FdoData->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_ACTIVE, 5000, bResult); + if (!bResult) + { + MP_SET_HARDWARE_ERROR(FdoData); + status = STATUS_DEVICE_DATA_ERROR; + break; + } + + // + // Check the current status of the receive unit + // + if ((FdoData->CSRAddress->ScbStatus & SCB_RUS_MASK) != SCB_RUS_IDLE) + { + // Issue an RU abort. Since an interrupt will be issued, the + // RU will be started by the DPC. + status = D100IssueScbCommand(FdoData, SCB_RUC_ABORT, TRUE); + if (status != STATUS_SUCCESS) + { + break; + } + } + + if (!WaitScb(FdoData)) + { + status = STATUS_DEVICE_DATA_ERROR; + break; + } + + // + // Restore the transmit software flags. After the multicast + // command is issued, the command unit will be idle, because the + // EL bit will be set in the multicast commmand block. + // + FdoData->TransmitIdle = TRUE; + FdoData->ResumeWait = TRUE; + } + + // + // Display config information + // + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Re-Issuing Configure command for filter change\n"); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Config Block at virt addr %p, phys address %x\n", + &((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbStatus, FdoData->NonTxCmdBlockPhys); + + for (i = 0; i < CB_CFIG_BYTE_COUNT; i++) + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, " Config byte %x = %.2x\n", + i, FdoData->NonTxCmdBlock->NonTxCb.Config.ConfigBytes[i]); + + // + // Submit the configure command to the chip, and wait for it to complete. + // + FdoData->CSRAddress->ScbGeneralPointer = FdoData->NonTxCmdBlockPhys; + status = D100SubmitCommandBlockAndWait(FdoData); + if (status != STATUS_SUCCESS) + { + status = STATUS_DEVICE_NOT_READY; + } + + } WHILE (FALSE); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "<-- NICSetPacketFilter, Status=%x\n", status); + + return(status); +} + +NTSTATUS +NICSetMulticastList( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + This routine will set up the FdoData for a specified multicast address list + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + +--*/ +{ + NTSTATUS status; + PUCHAR McAddress; + UINT i, j; + BOOLEAN bResult; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "--> NICSetMulticastList\n"); + + // + // Setup the command block for the multicast command. + // + for (i = 0; i < FdoData->MCAddressCount; i++) + { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "MC(%d) = %02x-%02x-%02x-%02x-%02x-%02x\n", + i, + FdoData->MCList[i][0], + FdoData->MCList[i][1], + FdoData->MCList[i][2], + FdoData->MCList[i][3], + FdoData->MCList[i][4], + FdoData->MCList[i][5]); + + McAddress = &FdoData->NonTxCmdBlock->NonTxCb.Multicast.McAddress[i*ETHERNET_ADDRESS_LENGTH]; + + for (j = 0; j < ETH_LENGTH_OF_ADDRESS; j++) + *(McAddress++) = FdoData->MCList[i][j]; + } + + FdoData->NonTxCmdBlock->NonTxCb.Multicast.McCount = + (USHORT)(FdoData->MCAddressCount * ETH_LENGTH_OF_ADDRESS); + ((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbStatus = 0; + ((PCB_HEADER_STRUC)FdoData->NonTxCmdBlock)->CbCommand = CB_MULTICAST; + + // + // Wait for the SCB to clear before we check the CU status. + // + if (!WaitScb(FdoData)) + { + status = STATUS_DEVICE_DATA_ERROR; + goto exit; + } + + // + // If we have issued any transmits, then the CU will either be active, or + // in the suspended state. If the CU is active, then we wait for it to be + // suspended. + // + if (FdoData->TransmitIdle == FALSE) + { + // + // Wait for suspended state + // + MP_STALL_AND_WAIT((FdoData->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_ACTIVE, 5000, bResult); + if (!bResult) + { + MP_SET_HARDWARE_ERROR(FdoData); + status = STATUS_DEVICE_DATA_ERROR; + } + + // + // Restore the transmit software flags. After the multicast command is + // issued, the command unit will be idle, because the EL bit will be + // set in the multicast commmand block. + // + FdoData->TransmitIdle = TRUE; + FdoData->ResumeWait = TRUE; + } + + // + // Update the command list pointer. + // + FdoData->CSRAddress->ScbGeneralPointer = FdoData->NonTxCmdBlockPhys; + + // + // Submit the multicast command to the FdoData and wait for it to complete. + // + status = D100SubmitCommandBlockAndWait(FdoData); + if (status != STATUS_SUCCESS) + { + status = STATUS_DEVICE_NOT_READY; + } + + exit: + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "<-- NICSetMulticastList, Status=%x\n", status); + + return(status); + +} + + +VOID +NICFillPoMgmtCaps ( + IN PFDO_DATA FdoData, + IN OUT PNDIS_PNP_CAPABILITIES pPower_Management_Capabilities, + IN OUT PNDIS_STATUS pStatus, + IN OUT PULONG pulInfoLen + ) +/*++ +Routine Description: + + Fills in the Power Managment structure depending the capabilities of + the software driver and the card. + + Currently this is only supported on 82559 Version of the driver + +Arguments: + + FdoData Pointer to the FdoData structure + pPower_Management_Capabilities - Power management struct as defined in the DDK, + pStatus Status to be returned by the request, + pulInfoLen Length of the pPowerManagmentCapabilites + +Return Value: + + Success or failure depending on the type of card +--*/ + +{ + + BOOLEAN bIsPoMgmtSupported; + + bIsPoMgmtSupported = IsPoMgmtSupported(FdoData); + + if (bIsPoMgmtSupported == TRUE) + { + pPower_Management_Capabilities->Flags = NDIS_DEVICE_WAKE_UP_ENABLE; + pPower_Management_Capabilities->WakeUpCapabilities.MinMagicPacketWakeUp = NdisDeviceStateUnspecified; + pPower_Management_Capabilities->WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateD3; + pPower_Management_Capabilities->WakeUpCapabilities.MinLinkChangeWakeUp = NdisDeviceStateUnspecified; + *pulInfoLen = sizeof (*pPower_Management_Capabilities); + *pStatus = STATUS_SUCCESS; + } + else + { + RtlZeroMemory (pPower_Management_Capabilities, sizeof(*pPower_Management_Capabilities)); + *pStatus = STATUS_NOT_SUPPORTED; + *pulInfoLen = 0; + + } +} + +PCHAR +DbgGetOidName(ULONG oid) +{ + PCHAR oidName; + + switch (oid){ + + #undef MAKECASE + #define MAKECASE(oidx) case oidx: oidName = #oidx; break; + + MAKECASE(OID_GEN_SUPPORTED_LIST) + MAKECASE(OID_GEN_HARDWARE_STATUS) + MAKECASE(OID_GEN_MEDIA_SUPPORTED) + MAKECASE(OID_GEN_MEDIA_IN_USE) + MAKECASE(OID_GEN_MAXIMUM_LOOKAHEAD) + MAKECASE(OID_GEN_MAXIMUM_FRAME_SIZE) + MAKECASE(OID_GEN_LINK_SPEED) + MAKECASE(OID_GEN_TRANSMIT_BUFFER_SPACE) + MAKECASE(OID_GEN_RECEIVE_BUFFER_SPACE) + MAKECASE(OID_GEN_TRANSMIT_BLOCK_SIZE) + MAKECASE(OID_GEN_RECEIVE_BLOCK_SIZE) + MAKECASE(OID_GEN_VENDOR_ID) + MAKECASE(OID_GEN_VENDOR_DESCRIPTION) + MAKECASE(OID_GEN_CURRENT_PACKET_FILTER) + MAKECASE(OID_GEN_CURRENT_LOOKAHEAD) + MAKECASE(OID_GEN_DRIVER_VERSION) + MAKECASE(OID_GEN_MAXIMUM_TOTAL_SIZE) + MAKECASE(OID_GEN_PROTOCOL_OPTIONS) + MAKECASE(OID_GEN_MAC_OPTIONS) + MAKECASE(OID_GEN_MEDIA_CONNECT_STATUS) + MAKECASE(OID_GEN_MAXIMUM_SEND_PACKETS) + MAKECASE(OID_GEN_VENDOR_DRIVER_VERSION) + MAKECASE(OID_GEN_SUPPORTED_GUIDS) + MAKECASE(OID_GEN_NETWORK_LAYER_ADDRESSES) + MAKECASE(OID_GEN_TRANSPORT_HEADER_OFFSET) + MAKECASE(OID_GEN_MEDIA_CAPABILITIES) + MAKECASE(OID_GEN_PHYSICAL_MEDIUM) + MAKECASE(OID_GEN_XMIT_OK) + MAKECASE(OID_GEN_RCV_OK) + MAKECASE(OID_GEN_XMIT_ERROR) + MAKECASE(OID_GEN_RCV_ERROR) + MAKECASE(OID_GEN_RCV_NO_BUFFER) + MAKECASE(OID_GEN_DIRECTED_BYTES_XMIT) + MAKECASE(OID_GEN_DIRECTED_FRAMES_XMIT) + MAKECASE(OID_GEN_MULTICAST_BYTES_XMIT) + MAKECASE(OID_GEN_MULTICAST_FRAMES_XMIT) + MAKECASE(OID_GEN_BROADCAST_BYTES_XMIT) + MAKECASE(OID_GEN_BROADCAST_FRAMES_XMIT) + MAKECASE(OID_GEN_DIRECTED_BYTES_RCV) + MAKECASE(OID_GEN_DIRECTED_FRAMES_RCV) + MAKECASE(OID_GEN_MULTICAST_BYTES_RCV) + MAKECASE(OID_GEN_MULTICAST_FRAMES_RCV) + MAKECASE(OID_GEN_BROADCAST_BYTES_RCV) + MAKECASE(OID_GEN_BROADCAST_FRAMES_RCV) + MAKECASE(OID_GEN_RCV_CRC_ERROR) + MAKECASE(OID_GEN_TRANSMIT_QUEUE_LENGTH) + MAKECASE(OID_GEN_GET_TIME_CAPS) + MAKECASE(OID_GEN_GET_NETCARD_TIME) + MAKECASE(OID_GEN_NETCARD_LOAD) + MAKECASE(OID_GEN_DEVICE_PROFILE) + MAKECASE(OID_GEN_INIT_TIME_MS) + MAKECASE(OID_GEN_RESET_COUNTS) + MAKECASE(OID_GEN_MEDIA_SENSE_COUNTS) + MAKECASE(OID_PNP_CAPABILITIES) + MAKECASE(OID_PNP_SET_POWER) + MAKECASE(OID_PNP_QUERY_POWER) + MAKECASE(OID_PNP_ADD_WAKE_UP_PATTERN) + MAKECASE(OID_PNP_REMOVE_WAKE_UP_PATTERN) + MAKECASE(OID_PNP_ENABLE_WAKE_UP) + MAKECASE(OID_802_3_PERMANENT_ADDRESS) + MAKECASE(OID_802_3_CURRENT_ADDRESS) + MAKECASE(OID_802_3_MULTICAST_LIST) + MAKECASE(OID_802_3_MAXIMUM_LIST_SIZE) + MAKECASE(OID_802_3_MAC_OPTIONS) + MAKECASE(OID_802_3_RCV_ERROR_ALIGNMENT) + MAKECASE(OID_802_3_XMIT_ONE_COLLISION) + MAKECASE(OID_802_3_XMIT_MORE_COLLISIONS) + MAKECASE(OID_802_3_XMIT_DEFERRED) + MAKECASE(OID_802_3_XMIT_MAX_COLLISIONS) + MAKECASE(OID_802_3_RCV_OVERRUN) + MAKECASE(OID_802_3_XMIT_UNDERRUN) + MAKECASE(OID_802_3_XMIT_HEARTBEAT_FAILURE) + MAKECASE(OID_802_3_XMIT_TIMES_CRS_LOST) + MAKECASE(OID_802_3_XMIT_LATE_COLLISIONS) + + default: + oidName = "<** UNKNOWN OID **>"; + break; + } + + return oidName; +} + + diff --git a/general/pcidrv/kmdf/HW/nic_send.c b/general/pcidrv/kmdf/HW/nic_send.c new file mode 100644 index 00000000..dafabed1 --- /dev/null +++ b/general/pcidrv/kmdf/HW/nic_send.c @@ -0,0 +1,838 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + nic_send.c + +Abstract: + This module contains routines to write packets. + +Environment: + Kernel mode + +--*/ + +#include "precomp.h" + +#if defined(EVENT_TRACING) +#include "nic_send.tmh" +#endif + +_IRQL_requires_same_ +_IRQL_requires_(DISPATCH_LEVEL) +_Requires_lock_held_(FdoData->SendLock) +__inline +VOID +MP_FREE_SEND_PACKET( + IN PFDO_DATA FdoData, + IN PMP_TCB pMpTcb, + IN NTSTATUS Status + ) +/*++ +Routine Description: + + Recycle a MP_TCB and complete the packet if necessary + + Assumption: This function is called with the Send SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + pMpTcb Pointer to MP_TCB + +Return Value: + + None + +--*/ +{ + + WDFREQUEST request; + WDFDMATRANSACTION dmaTransaction; + size_t length; + + ASSERT(MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE)); + + dmaTransaction = pMpTcb->DmaTransaction; + pMpTcb->DmaTransaction = NULL; + + MP_CLEAR_FLAGS(pMpTcb); + + FdoData->CurrSendHead = FdoData->CurrSendHead->Next; + FdoData->nBusySend--; + + request = WdfDmaTransactionGetRequest(dmaTransaction); + length = WdfDmaTransactionGetBytesTransferred(dmaTransaction); + + WdfObjectDelete( dmaTransaction ); + + if (request) + { + WdfSpinLockRelease(FdoData->SendLock); + WdfRequestCompleteWithInformation(request, Status, length); + FdoData->BytesTransmitted += length; + + WdfSpinLockAcquire(FdoData->SendLock); + } +} + +VOID +PciDrvEvtIoWrite( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + Called by the framework as soon as it receive a write IRP. + If the device is not ready, fail the request. Otherwise + get scatter-gather list for this request and send the + packet to the hardware for DMA. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + Request - Handle to a framework request object. + + Length - Length of the IO operation + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + +--*/ +{ + NTSTATUS status; + PFDO_DATA FdoData; + WDFDEVICE hDevice; + PMDL mdl = NULL; + + UNREFERENCED_PARAMETER(Length); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "--> PciDrvEvtIoWrite Request %p\n", Request); + + hDevice = WdfIoQueueGetDevice(Queue); + FdoData = FdoGetData(hDevice); + + status = WdfRequestRetrieveInputWdmMdl(Request, &mdl); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfRequestRetrieveInputWdmMdl failed %x\n", status); + WdfRequestCompleteWithInformation(Request, status, 0); + + } else { + + status = NICInitiateDmaTransfer(FdoData, Request); + if(!NT_SUCCESS(status)) { + + WdfRequestCompleteWithInformation(Request, status, 0); + } + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "<-- PciDrvEvtIoWrite %X\n", status); + + return; +} + +NTSTATUS +NICInitiateDmaTransfer( + IN PFDO_DATA FdoData, + IN WDFREQUEST Request + ) +{ + WDFDMATRANSACTION dmaTransaction; + NTSTATUS status; + BOOLEAN bCreated = FALSE; + + do { + // + // Create a new DmaTransaction. + // + status = WdfDmaTransactionCreate( FdoData->WdfDmaEnabler, + WDF_NO_OBJECT_ATTRIBUTES, + &dmaTransaction ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfDmaTransactionCreate failed %X\n", status); + break; + } + + bCreated = TRUE; + // + // Initialize the new DmaTransaction. + // + + status = WdfDmaTransactionInitializeUsingRequest( + dmaTransaction, + Request, + NICEvtProgramDmaFunction, + WdfDmaDirectionWriteToDevice ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfDmaTransactionInitalizeUsingRequest failed %X\n", + status); + break; + } + + // + // Execute this DmaTransaction. + // + status = WdfDmaTransactionExecute( dmaTransaction, + dmaTransaction ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfDmaTransactionExecute failed %X\n", status); + break; + } + + } WHILE (FALSE); + + if(!NT_SUCCESS(status)){ + + if(bCreated) { + WdfObjectDelete( dmaTransaction ); + + } + } + + return status; +} + + +BOOLEAN +NICEvtProgramDmaFunction( + IN WDFDMATRANSACTION Transaction, + IN WDFDEVICE Device, + IN PVOID Context, + IN WDF_DMA_DIRECTION Direction, + IN PSCATTER_GATHER_LIST ScatterGather + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + PFDO_DATA fdoData; + WDFREQUEST request; + NTSTATUS status; + + UNREFERENCED_PARAMETER( Context ); + UNREFERENCED_PARAMETER( Direction ); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "--> NICEvtProgramDmaFunction\n"); + + fdoData = FdoGetData(Device); + request = WdfDmaTransactionGetRequest(Transaction); + + + WdfSpinLockAcquire(fdoData->SendLock); + + // + // If tcb or link is not available, queue the request + // + if (!MP_TCB_RESOURCES_AVAIABLE(fdoData) || + MP_TEST_FLAG(fdoData, fMP_ADAPTER_LINK_DETECTION)) + { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "Resource is not available: queue Request %p\n", request); + + // + // Must abort the transaction before deleting. + // + (VOID) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status); + ASSERT(NT_SUCCESS(status)); + WdfObjectDelete( Transaction ); + + // + // Queue the request for later processing. + // + status = WdfRequestForwardToIoQueue(request, + fdoData->PendingWriteQueue); + + if(!NT_SUCCESS(status)) { + ASSERTMSG(" WdfRequestForwardToIoQueue failed ", FALSE); + WdfSpinLockRelease(fdoData->SendLock); + WdfRequestCompleteWithInformation(request, STATUS_UNSUCCESSFUL, 0); + return FALSE; + } + fdoData->nWaitSend++; + + } else { + + status = NICWritePacket(fdoData, Transaction, ScatterGather); + + if(!NT_SUCCESS(status)){ + + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "<-- NICEvtProgramDmaFunction returning %!STATUS!\n", + status); + // + // Must abort the transaction before deleting. + // + (VOID )WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status); + ASSERT(NT_SUCCESS(status)); + WdfObjectDelete( Transaction ); + + WdfSpinLockRelease(fdoData->SendLock); + WdfRequestCompleteWithInformation(request, STATUS_UNSUCCESSFUL, 0); + return FALSE; + } + } + + + WdfSpinLockRelease(fdoData->SendLock); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "<-- NICEvtProgramDmaFunction\n"); + + return TRUE; +} + + +NTSTATUS +NICWritePacket( + IN PFDO_DATA FdoData, + IN WDFDMATRANSACTION DmaTransaction, + IN PSCATTER_GATHER_LIST SGList + ) +/*++ +Routine Description: + + Do the work to send a packet + + Assumption: This function is called with the Send SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + +--*/ +{ + PMP_TCB pMpTcb = NULL; + NTSTATUS status; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "--> NICWritePacket: SGList %p\n", SGList); + + // + // Initialize the Transfer Control Block. + // + pMpTcb = FdoData->CurrSendTail; + ASSERT(!MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE)); + + pMpTcb->DmaTransaction = DmaTransaction; + + MP_SET_FLAG(pMpTcb, fMP_TCB_IN_USE); + + // + // Call the send handler, it only needs to deal with the ScatterGather list + // + status = NICSendPacket(FdoData, pMpTcb, SGList); + if(!NT_SUCCESS(status)){ + MP_CLEAR_FLAG(pMpTcb, fMP_TCB_IN_USE); + return status; + } + + FdoData->nBusySend++; + ASSERT(FdoData->nBusySend <= FdoData->NumTcb); + + FdoData->CurrSendTail = FdoData->CurrSendTail->Next; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "<-- NICWritePacket\n"); + + return status; +} + +NTSTATUS +NICSendPacket( + IN PFDO_DATA FdoData, + IN PMP_TCB pMpTcb, + IN PSCATTER_GATHER_LIST ScatterGather + ) +/*++ +Routine Description: + + NIC specific send handler + + Assumption: This function is called with the Send SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + pMpTcb Pointer to MP_TCB + ScatterGather The pointer to the frag list to be filled + +Return Value: + + NTSTATUS code + +--*/ +{ + NTSTATUS status; + ULONG index; + UCHAR TbdCount = 0; + + PHW_TCB pHwTcb = pMpTcb->HwTcb; + PTBD_STRUC pHwTbd = pMpTcb->HwTbd; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "--> NICSendPacket\n"); + + for (index = 0; index < ScatterGather->NumberOfElements; index++) + { + if (ScatterGather->Elements[index].Length) + { + pHwTbd->TbdBufferAddress = + ScatterGather->Elements[index].Address.LowPart; + + pHwTbd->TbdCount = ScatterGather->Elements[index].Length; + + pHwTbd++; + TbdCount++; + } + } + + pHwTcb->TxCbHeader.CbStatus = 0; + pHwTcb->TxCbHeader.CbCommand = CB_S_BIT | CB_TRANSMIT | CB_TX_SF_BIT; + + pHwTcb->TxCbTbdPointer = pMpTcb->HwTbdPhys; + pHwTcb->TxCbTbdNumber = TbdCount; + pHwTcb->TxCbCount = 0; + pHwTcb->TxCbThreshold = (UCHAR) FdoData->AiThreshold; + + + status = NICStartSend(FdoData, pMpTcb); + + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "NICStartSend returned error %x\n", status); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "<-- NICSendPacket\n"); + + return status; +} + +NTSTATUS +NICStartSend( + IN PFDO_DATA FdoData, + IN PMP_TCB pMpTcb + ) +/*++ +Routine Description: + + Issue a send command to the NIC + + Assumption: This function is called with the Send SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + pMpTcb Pointer to MP_TCB + +Return Value: + + NTSTATUS code + +--*/ +{ + NTSTATUS status; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "--> NICStartSend\n"); + + // + // If the transmit unit is idle (very first transmit) then we must + // setup the general pointer and issue a full CU-start + // + if (FdoData->TransmitIdle) + { + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "CU is idle -- First TCB added to Active List\n"); + + // + // Wait for the SCB to clear before we set the general pointer + // + if (!WaitScb(FdoData)) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "NICStartSend -- WaitScb returned error\n"); + status = STATUS_DEVICE_DATA_ERROR; + goto exit; + } + + // + // Don't try to start the transmitter if the command unit is not + // idle ((not idle) == (Cu-Suspended or Cu-Active)). + // + if ((FdoData->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_IDLE) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "FdoData = %p, CU Not IDLE\n", FdoData); + MP_SET_HARDWARE_ERROR(FdoData); + KeStallExecutionProcessor(25); + } + + FdoData->CSRAddress->ScbGeneralPointer = pMpTcb->HwTcbPhys; + + status = D100IssueScbCommand(FdoData, SCB_CUC_START, FALSE); + + FdoData->TransmitIdle = FALSE; + FdoData->ResumeWait = TRUE; + } + else + { + // + // If the command unit has already been started, then append this + // TCB onto the end of the transmit chain, and issue a CU-Resume. + // + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "adding TCB to Active chain\n"); + + // + // Clear the suspend bit on the previous packet. + // + pMpTcb->PrevHwTcb->TxCbHeader.CbCommand &= ~CB_S_BIT; + + // + // Issue a CU-Resume command to the device. We only need to do a + // WaitScb if the last command was NOT a RESUME. + // + status = D100IssueScbCommand(FdoData, + SCB_CUC_RESUME, + FdoData->ResumeWait); + } + + exit: + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "<-- NICStartSend\n"); + + return status; +} + +_Requires_lock_held_(FdoData->SendLock) +NTSTATUS +NICHandleSendInterrupt( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Interrupt handler for sending processing. Re-claim the send resources, + complete sends and get more to send from the send wait queue. + + Assumption: This function is called with the Send SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + NTSTATUS code + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PMP_TCB pMpTcb; + +#if DBG + ULONG i; +#endif + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "--> NICHandleSendInterrupt\n"); + + // + // Any packets being sent? Any packet waiting in the send queue? + // + if (FdoData->nBusySend == 0) + { + ASSERT(FdoData->CurrSendHead == FdoData->CurrSendTail); + return status; + } + + // + // Check the first TCB on the send list + // + while (FdoData->nBusySend > 0) + { + +#if DBG + pMpTcb = FdoData->CurrSendHead; + for (i = 0; i < FdoData->nBusySend; i++) + { + pMpTcb = pMpTcb->Next; + } + + if (pMpTcb != FdoData->CurrSendTail) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "nBusySend= %d\n", FdoData->nBusySend); + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "CurrSendhead= %p\n", FdoData->CurrSendHead); + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "CurrSendTail= %p\n", FdoData->CurrSendTail); + ASSERT(FALSE); + } +#endif + + pMpTcb = FdoData->CurrSendHead; + + // + // Is this TCB completed? + // + if (pMpTcb->HwTcb->TxCbHeader.CbStatus & CB_STATUS_COMPLETE) + { + // + // Check if this is a multicast hw workaround packet + // + if ((pMpTcb->HwTcb->TxCbHeader.CbCommand & CB_CMD_MASK) != CB_MULTICAST) + { + BOOLEAN transactionComplete; + + ASSERT(pMpTcb->DmaTransaction); + + // + // Indicate this DMA operation has completed: + // This may drive the transfer on the next packet if + // there is still data to be transfered in the DmaTransaction. + // + transactionComplete = + WdfDmaTransactionDmaCompleted( pMpTcb->DmaTransaction, + &status ); + + if(transactionComplete == TRUE) { + + ASSERT(status == STATUS_SUCCESS); + MP_FREE_SEND_PACKET(FdoData, pMpTcb, status); + + } else { + // + // NOTE: For this ethernet driver this should never + // be returned as the packets are <= 1514 bytes. + // It is included to show the complete DmaTransaction + // coding pattern. + // + ASSERT(!"STATUS_MORE_PROCESSING_REQUIRED"); + } + + } + else + { + + // Multicast workaround would be here (???) + + } + } + else + { + break; + } + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "<-- NICHandleSendInterrupt\n"); + return status; +} + +VOID +NICCheckForQueuedSends( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + +--*/ +{ + WDFREQUEST request; + WDFDMATRANSACTION dmaTransaction; + NTSTATUS status; + + UNREFERENCED_PARAMETER( dmaTransaction ); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "--> NICCheckForQueuedSends\n"); + + // + // If we queued any transmits because we didn't have any TCBs earlier, + // dequeue and send those packets now, as long as we have free TCBs. + // + while (MP_TCB_RESOURCES_AVAIABLE(FdoData)) + { + status = WdfIoQueueRetrieveNextRequest( + FdoData->PendingWriteQueue, + &request + ); + + if(!NT_SUCCESS(status) ) { + if(STATUS_NO_MORE_ENTRIES != status) { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "WdfIoQueueRetrieveNextRequest failed %X\n", status); + } + break; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "\t processing Request %p \n", request); + + status = NICInitiateDmaTransfer(FdoData, request); + if(!NT_SUCCESS(status)) { + WdfRequestCompleteWithInformation(request, status, 0); + } + + FdoData->nWaitSend--; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "<-- NICCheckForQueuedSends\n"); +} + +_Requires_lock_held_(FdoData->SendLock) +VOID +NICFreeBusySendPackets( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Free and complete the stopped active sends + + Assumption: This function is called with the Send SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + None + +--*/ +{ + PMP_TCB pMpTcb; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "--> NICFreeBusySendPackets\n"); + + // + // Any packets being sent? Check the first TCB on the send list + // + while (FdoData->nBusySend > 0) + { + pMpTcb = FdoData->CurrSendHead; + + // + // Is this TCB completed? + // + if ((pMpTcb->HwTcb->TxCbHeader.CbCommand & CB_CMD_MASK) != CB_MULTICAST) + { + MP_FREE_SEND_PACKET(FdoData, pMpTcb, STATUS_SUCCESS); + } + else + { + break; + } + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "<-- NICFreeBusySendPackets\n"); +} + + +_IRQL_requires_same_ +_IRQL_requires_(DISPATCH_LEVEL) +_Requires_lock_held_(FdoData->SendLock) +VOID +NICFreeQueuedSendPackets( + IN PFDO_DATA FdoData + ) +/*++ +Routine Description: + + Free and complete the pended sends on SendQueueHead + + Assumption: This function is called with the Send SPINLOCK held. + +Arguments: + + FdoData Pointer to our FdoData + +Return Value: + + None + +--*/ +{ + WDFREQUEST request; + NTSTATUS status = MP_GET_STATUS_FROM_FLAGS(FdoData); + + if (STATUS_UNSUCCESSFUL == status) { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "MP_GET_STATUS_FROM_FLAGS failed %x\n", status); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "--> NICFreeQueuedSendPackets\n"); + + do { + status = WdfIoQueueRetrieveNextRequest( + FdoData->PendingWriteQueue, + &request + ); + + if(!NT_SUCCESS(status) ) { + if(STATUS_NO_MORE_ENTRIES != status){ + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "WdfIoQueueRetrieveNextRequest failed %x\n", status); + } + break; + } + + FdoData->nWaitSend--; + + WdfSpinLockRelease(FdoData->SendLock); + + WdfRequestCompleteWithInformation(request, status, 0); + + WdfSpinLockAcquire(FdoData->SendLock); + + } WHILE (TRUE); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, + "<-- NICFreeQueuedSendPackets\n"); + +} + + diff --git a/general/pcidrv/kmdf/HW/nuiouser.h b/general/pcidrv/kmdf/HW/nuiouser.h new file mode 100644 index 00000000..c33861ae --- /dev/null +++ b/general/pcidrv/kmdf/HW/nuiouser.h @@ -0,0 +1,105 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + nuiouser.h + +Abstract: + + Constants and types to access the NDISPROT driver. + Users must also include ntddndis.h + +Environment: + + User/Kernel mode. + +--*/ + +#ifndef __NUIOUSER__H +#define __NUIOUSER__H + + +#define FSCTL_NDISPROT_BASE FILE_DEVICE_NETWORK + +#define _NDISPROT_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(FSCTL_NDISPROT_BASE, _Function, _Method, _Access) + +#define IOCTL_NDISPROT_OPEN_DEVICE \ + _NDISPROT_CTL_CODE(0x200, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISPROT_QUERY_OID_VALUE \ + _NDISPROT_CTL_CODE(0x201, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISPROT_SET_OID_VALUE \ + _NDISPROT_CTL_CODE(0x205, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISPROT_QUERY_BINDING \ + _NDISPROT_CTL_CODE(0x203, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISPROT_BIND_WAIT \ + _NDISPROT_CTL_CODE(0x204, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISPROT_INDICATE_STATUS \ + _NDISPROT_CTL_CODE(0x206, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + + + +// +// Structure to go with IOCTL_NDISPROT_QUERY_OID_VALUE. +// The Data part is of variable length, determined by +// the input buffer length passed to DeviceIoControl. +// +typedef struct _NDISPROT_QUERY_OID +{ + NDIS_OID Oid; + UCHAR Data[sizeof(ULONG)]; + +} NDISPROT_QUERY_OID, *PNDISPROT_QUERY_OID; + +// +// Structure to go with IOCTL_NDISPROT_SET_OID_VALUE. +// The Data part is of variable length, determined +// by the input buffer length passed to DeviceIoControl. +// +typedef struct _NDISPROT_SET_OID +{ + NDIS_OID Oid; + UCHAR Data[sizeof(ULONG)]; + +} NDISPROT_SET_OID, *PNDISPROT_SET_OID; + + +// +// Structure to go with IOCTL_NDISPROT_QUERY_BINDING. +// The input parameter is BindingIndex, which is the +// index into the list of bindings active at the driver. +// On successful completion, we get back a device name +// and a device descriptor (friendly name). +// +typedef struct _NDISPROT_QUERY_BINDING +{ + ULONG BindingIndex; // 0-based binding number + ULONG DeviceNameOffset; // from start of this struct + ULONG DeviceNameLength; // in bytes + ULONG DeviceDescrOffset; // from start of this struct + ULONG DeviceDescrLength; // in bytes + +} NDISPROT_QUERY_BINDING, *PNDISPROT_QUERY_BINDING; + +// +// Structure to go with IOCTL_NDISPROT_INDICATE_STATUS. +// NDISPROT copies the status indicated by the NIC and +// also the data indicated in the StatusBuffer. +// +typedef struct _NDISPROT_INDICATE_STATUS +{ + ULONG IndicatedStatus; // NDIS_STATUS + ULONG StatusBufferOffset; // from start of this struct + ULONG StatusBufferLength; // in bytes +} NDISPROT_INDICATE_STATUS, *PNDISPROT_INDICATE_STATUS; + +#endif // __NUIOUSER__H + + diff --git a/general/pcidrv/kmdf/HW/physet.c b/general/pcidrv/kmdf/HW/physet.c new file mode 100644 index 00000000..94132fec --- /dev/null +++ b/general/pcidrv/kmdf/HW/physet.c @@ -0,0 +1,1001 @@ +/**************************************************************************** +** COPYRIGHT (C) 1994-1997 INTEL CORPORATION ** +** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON ** +** HTTP://WWW.INTEL.COM/ ** +** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND ** +** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER ** +****************************************************************************/ + +/**************************************************************************** +Module Name: + physet.c + +This driver runs on the following hardware: + - 82558 based PCI 10/100Mb ethernet adapters + (aka Intel EtherExpress(TM) PRO Adapters) + +Environment: + Kernel Mode - Or whatever is the equivalent on WinNT + +*****************************************************************************/ + +//#pragma TRACE_LEVEL_WARNING (disable: 4514) + +//----------------------------------------------------------------------------- +// Procedure: PhyDetect +// +// Description: This routine will detect what phy we are using, set the line +// speed, FDX or HDX, and configure the phy if necessary. +// +// The following combinations are supported: +// - TX or T4 PHY alone at PHY address 1 +// - T4 or TX PHY at address 1 and MII PHY at address 0 +// - 82503 alone (10Base-T mode, no full duplex support) +// - 82503 and MII PHY (TX or T4) at address 0 +// +// The sequence / priority of detection is as follows: +// If there is a PHY Address override use that address. +// else scan based on the 'Connector' setting. +// Switch Connector +// 0 = AutoScan +// 1 = Onboard TPE only +// 2 = MII connector only +// +// Each of the above cases is explained below. +// +// AutoScan means: +// Look for link on addresses 1, 0, 2..31 (in that order). Use the first +// address found that has link. +// If link is not found then use the first valid PHY found in the same scan +// order 1,0,2..31. NOTE: this means that NO LINK or Multi-link cases will +// default to the onboard PHY (address 1). +// +// Onboard TPE only: +// Phy address is set to 1 (No Scanning). +// +// MII connector only means: +// Look for link on addresses 0, 2..31 (again in that order, Note address 1 is +// NOT scanned). Use the first address found that has link. +// If link is not found then use the first valid Phy found in the same scan +// order 0, 2..31. +// In the AutoScan case above we should always find a valid PHY at address 1, +// there is no such guarantee here, so, If NO Phy is found then the driver +// should default to address 0 and continue to load. Note: External +// transceivers should be at address 0 but our early Nitro3 testing found +// transceivers at several non-zero addresses (6,10,14). +// +// +// NWAY +// Additionally auto-negotiation capable (NWAY) and parallel +// detection PHYs are supported. The flow-chart is described in +// the 82557 software writer's manual. +// +// NOTE: 1. All PHY MDI registers are read in polled mode. +// 2. The routines assume that the 82557 has been RESET and we have +// obtained the virtual memory address of the CSR. +// 3. PhyDetect will not RESET the PHY. +// 4. If FORCEFDX is set, SPEED should also be set. The driver will +// check the values for inconsistency with the detected PHY +// technology. +// 5. PHY 1 (the PHY on the adapter) MUST be at address 1. +// 6. Driver ignores FORCEFDX and SPEED overrides if a 503 interface +// is detected. +// +// +// Arguments: +// FdoData - ptr to FdoData object instance +// +// Result: +// Returns: +// STATUS_SUCCESS +// NDIS_STATUS_FAILURE +//----------------------------------------------------------------------------- + +#include "precomp.h" + +#if defined(EVENT_TRACING) +#include "physet.tmh" +#endif + +NTSTATUS PhyDetect( + IN PFDO_DATA FdoData + ) +{ +#if DBG + USHORT MdiControlReg; + USHORT MdiStatusReg; +#endif + + // + // Check for a phy address over-ride of 32 which indicates a 503 + // + if (FdoData->PhyAddress == 32) + { + // + // 503 interface over-ride + // + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " 503 serial component over-ride\n"); + + FdoData->PhyAddress = 32; + + // + // Record the current speed and duplex. We will be in half duplex + // mode unless the user used the force full duplex over-ride. + // + FdoData->usLinkSpeed = 10; + FdoData->usDuplexMode = (USHORT) FdoData->AiForceDpx; + if (!FdoData->usDuplexMode) + { + FdoData->usDuplexMode = 1; + } + + return(STATUS_SUCCESS); + } + + // + // Check for other phy address over-rides. + // If the Phy Address is between 0-31 then there is an over-ride. + // Or the connector was set to 1 + // + if ((FdoData->PhyAddress < 32) || (FdoData->Connector == CONNECTOR_TPE)) + { + + // + // User Override nothing to do but setup Phy and leave + // + if ((FdoData->PhyAddress > 32) && (FdoData->Connector == CONNECTOR_TPE)) + { + FdoData->PhyAddress = 1; // Connector was forced + + // Isolate all other PHYs and unisolate this one + SelectPhy(FdoData, FdoData->PhyAddress, FALSE); + + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, + " Phy address Override to address %d\n", FdoData->PhyAddress); + +#if DBG + // + // Read the MDI control register at override address. + // + MdiRead(FdoData, MDI_CONTROL_REG, FdoData->PhyAddress, FALSE, &MdiControlReg); + + // + // Read the status register at override address. + // + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + // + // Read the status register again because of sticky bits + // + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + + // + // check if we found a valid phy + // + if (!((MdiControlReg == 0xffff) || ((MdiStatusReg == 0) && (MdiControlReg == 0)))) + { + // + // we have a valid phy1 + // + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Over-ride address %d has a valid Phy.\n", FdoData->PhyAddress); + + // + // Read the status register again + // + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + + // + // If there is a valid link then use this Phy. + // + if (MdiStatusReg & MDI_SR_LINK_STATUS) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Phy at address %d has link\n", FdoData->PhyAddress); + } + + } + else + { + // + // no PHY at over-ride address + // + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Over-ride address %d has no Phy!!!!\n", FdoData->PhyAddress); + } +#endif + return(SetupPhy(FdoData)); + } + else // Need to scan - No address over-ride and Connector is AUTO or MII + { + FdoData->CurrentScanPhyIndex = 0; + FdoData->LinkDetectionWaitCount = 0; + FdoData->FoundPhyAt = 0xff; + FdoData->bLookForLink = TRUE; + + return(ScanAndSetupPhy(FdoData)); + + } // End else scan + + +} + +NTSTATUS ScanAndSetupPhy( + IN PFDO_DATA FdoData + ) +{ + USHORT MdiControlReg = 0; + USHORT MdiStatusReg = 0; + + if (FdoData->bLinkDetectionWait) + { + goto NEGOTIATION_WAIT; + } + + SCAN_PHY_START: + + // + // For each PhyAddress 0 - 31 + // + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Index=%d, bLookForLink=%d\n", + FdoData->CurrentScanPhyIndex, FdoData->bLookForLink); + + if (FdoData->bLookForLink) + { + // + // Phy Addresses must be tested in the order 1,0,2..31. + // + switch(FdoData->CurrentScanPhyIndex) + { + case 0: + FdoData->PhyAddress = 1; + break; + + case 1: + FdoData->PhyAddress = 0; + break; + + default: + FdoData->PhyAddress = FdoData->CurrentScanPhyIndex; + break; + } + + // + // Skip OnBoard for MII only case + // + if ((FdoData->PhyAddress == 1)&&(FdoData->Connector == CONNECTOR_MII)) + { + goto SCAN_PHY_NEXT; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Scanning Phy address %d for link\n", FdoData->PhyAddress); + + // + // Read the MDI control register + // + MdiRead(FdoData, MDI_CONTROL_REG, FdoData->PhyAddress, FALSE, &MdiControlReg); + + // + // Read the status register + // + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + // Sticky Bits + } + else + { + // + // Not looking for link + // + if (FdoData->FoundPhyAt < 32) + { + FdoData->PhyAddress = FdoData->FoundPhyAt; + } + else if (FdoData->Connector == CONNECTOR_MII) + { + // + // No valid PHYs were found last time so just default + // + FdoData->PhyAddress = 0; // Default for MII + } + else + { + // + // assume a 503 interface + // + FdoData->PhyAddress = 32; + + // + // Record the current speed and duplex. We will be in half duplex + // mode unless the user used the force full duplex over-ride. + // + FdoData->usLinkSpeed = 10; + FdoData->usDuplexMode = (USHORT) FdoData->AiForceDpx; + if (!FdoData->usDuplexMode) + { + FdoData->usDuplexMode = 1; + } + + return(STATUS_SUCCESS); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " No Links Found!!\n"); + } + + // + // check if we found a valid phy or on !LookForLink pass + // + if (!( (MdiControlReg == 0xffff) || ((MdiStatusReg == 0) && (MdiControlReg == 0))) + || (!FdoData->bLookForLink)) + { + + // + // Valid phy or Not looking for Link + // + +#if DBG + if (!( (MdiControlReg == 0xffff) || ((MdiStatusReg == 0) && (MdiControlReg == 0)))) + { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Found a Phy at address %d\n", FdoData->PhyAddress); + } +#endif + // + // Store highest priority phy found for NO link case + // + if (FdoData->CurrentScanPhyIndex < FdoData->FoundPhyAt && FdoData->FoundPhyAt != 1) + { + // this phy is higher priority + FdoData->FoundPhyAt = (UCHAR) FdoData->PhyAddress; + } + + // + // Select Phy before checking link status + // NOTE: may take up to 3.5 Sec if LookForLink == TRUE + //SelectPhy(FdoData, FdoData->PhyAddress, (BOOLEAN)LookForLink); + // + SelectPhy(FdoData, FdoData->PhyAddress, FALSE); + + NEGOTIATION_WAIT: + + // + // wait for auto-negotiation to complete (up to 3.5 seconds) + // + if (FdoData->LinkDetectionWaitCount++ < RENEGOTIATE_TIME) + { + // Read the status register twice because of sticky bits + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + + if (!(MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE)) + { + return STATUS_PENDING; + } + } + else + { + FdoData->LinkDetectionWaitCount = 0; + } + + // + // Read the MDI control register + // + MdiRead(FdoData, MDI_CONTROL_REG, FdoData->PhyAddress, FALSE, &MdiControlReg); + + // + // Read the status register + // + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + + // + // If there is a valid link or we alreadry tried once then use this Phy. + // + if ((MdiStatusReg & MDI_SR_LINK_STATUS) || (!FdoData->bLookForLink)) + { +#if DBG + if (MdiStatusReg & MDI_SR_LINK_STATUS) + { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Using Phy at address %d with link\n", FdoData->PhyAddress); + } + else + { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Using Phy at address %d WITHOUT link!!!\n", FdoData->PhyAddress); + } +#endif + + return(SetupPhy(FdoData)); // Exit with Link Path + } + } // End if valid PHY + + SCAN_PHY_NEXT: + + FdoData->CurrentScanPhyIndex++; + if (FdoData->CurrentScanPhyIndex >= 32) + { + FdoData->bLookForLink = FALSE; + } + + goto SCAN_PHY_START; +} + + +//*************************************************************************** +// +// Name: SelectPhy +// +// Description: This routine will Isolate all Phy addresses on the MII +// Bus except for the one address to be 'selected'. This +// Phy address will be un-isolated and auto-negotiation will +// be enabled, started, and completed. The Phy will NOT be +// reset and the speed will NOT be set to any value (that is +// done in SetupPhy). +// +// Arguments: SelectPhyAddress - PhyAddress to select +// WaitAutoNeg - Flag TRUE = Wait for Auto Negociation to complete. +// FALSE = don't wait. Good for 'No Link' case. +// +// Returns: Nothing +// +// Modification log: +// Date Who Description +// -------- --- -------------------------------------------------------- +//*************************************************************************** +VOID SelectPhy( + IN PFDO_DATA FdoData, + IN UINT SelectPhyAddress, + IN BOOLEAN WaitAutoNeg + ) +{ + UCHAR i; + USHORT MdiControlReg = 0; + USHORT MdiStatusReg = 0; + + // + // Isolate all other phys and unisolate the one to query + // + for (i = 0; i < 32; i++) + { + if (i != SelectPhyAddress) + { + // isolate this phy + MdiWrite(FdoData, MDI_CONTROL_REG, i, MDI_CR_ISOLATE); + // wait 100 microseconds for the phy to isolate. + KeStallExecutionProcessor(100); + } + } + + // unisolate the phy to query + + // + // Read the MDI control register + // + MdiRead(FdoData, MDI_CONTROL_REG, SelectPhyAddress, FALSE, &MdiControlReg); + + // + // Set/Clear bit unisolate this phy + // + MdiControlReg &= ~MDI_CR_ISOLATE; // Clear the Isolate Bit + + // + // issue the command to unisolate this Phy + // + MdiWrite(FdoData, MDI_CONTROL_REG, SelectPhyAddress, MdiControlReg); + + // + // sticky bits on link + // + MdiRead(FdoData, MDI_STATUS_REG, SelectPhyAddress, FALSE, &MdiStatusReg); + MdiRead(FdoData, MDI_STATUS_REG, SelectPhyAddress, FALSE, &MdiStatusReg); + + // + // if we have link, don't mess with the phy + // + if (MdiStatusReg & MDI_SR_LINK_STATUS) + return; + + // + // Read the MDI control register + // + MdiRead(FdoData, MDI_CONTROL_REG, SelectPhyAddress, FALSE, &MdiControlReg); + + // + // set Restart auto-negotiation + // + MdiControlReg |= MDI_CR_AUTO_SELECT; // Set Auto Neg Enable + MdiControlReg |= MDI_CR_RESTART_AUTO_NEG; // Restart Auto Neg + + // + // restart the auto-negotion process + // + MdiWrite(FdoData, MDI_CONTROL_REG, SelectPhyAddress, MdiControlReg); + + // + // wait 200 microseconds for the phy to unisolate. + // + KeStallExecutionProcessor(200); + + if (WaitAutoNeg) + { + // + // wait for auto-negotiation to complete (up to 3.5 seconds) + // + for (i = RENEGOTIATE_TIME; i != 0; i--) + { + // Read the status register twice because of sticky bits + MdiRead(FdoData, MDI_STATUS_REG, SelectPhyAddress, FALSE, &MdiStatusReg); + MdiRead(FdoData, MDI_STATUS_REG, SelectPhyAddress, FALSE, &MdiStatusReg); + + if (MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE) + break; + + MP_STALL_EXECUTION(100); + } + } +} + +//----------------------------------------------------------------------------- +// Procedure: SetupPhy +// +// Description: This routine will setup phy 1 or phy 0 so that it is configured +// to match a speed and duplex over-ride option. If speed or +// duplex mode is not explicitly specified in the registry, the +// driver will skip the speed and duplex over-ride code, and +// assume the FdoData is automatically setting the line speed, and +// the duplex mode. At the end of this routine, any truly Phy +// specific code will be executed (each Phy has its own quirks, +// and some require that certain special bits are set). +// +// NOTE: The driver assumes that SPEED and FORCEFDX are specified at the +// same time. If FORCEDPX is set without speed being set, the driver +// will encouter a fatal error and log a message into the event viewer. +// +// Arguments: +// FdoData - ptr to FdoData object instance +// +// Result: +// Returns: +// STATUS_SUCCESS +// NDIS_STATUS_FAILURE +//----------------------------------------------------------------------------- + +NTSTATUS SetupPhy( + IN PFDO_DATA FdoData) +{ + USHORT MdiControlReg = 0; + USHORT MdiStatusReg = 0; + USHORT MdiIdLowReg = 0; + USHORT MdiIdHighReg = 0; + USHORT MdiMiscReg = 0; + UINT PhyId; + BOOLEAN ForcePhySetting = FALSE; + + // + // If we are NOT forcing a setting for line speed or full duplex, then + // we won't force a link setting, and we'll jump down to the phy + // specific code. + // + if (((FdoData->AiTempSpeed) || (FdoData->AiForceDpx))) + { + + // + // Find out what kind of technology this Phy is capable of. + // + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + + // + // Read the MDI control register at our phy + // + MdiRead(FdoData, MDI_CONTROL_REG, FdoData->PhyAddress, FALSE, &MdiControlReg); + + // + // Now check the validity of our forced option. If the force option is + // valid, then force the setting. If the force option is not valid, + // we'll set a flag indicating that we should error out. + // + + // + // If speed is forced to 10mb + // + if (FdoData->AiTempSpeed == 10) + { + // If half duplex is forced + if (FdoData->AiForceDpx == 1) + { + if (MdiStatusReg & MDI_SR_10T_HALF_DPX) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Forcing 10mb 1/2 duplex\n"); + MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF); + ForcePhySetting = TRUE; + } + } + + // If full duplex is forced + else if (FdoData->AiForceDpx == 2) + { + if (MdiStatusReg & MDI_SR_10T_FULL_DPX) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Forcing 10mb full duplex\n"); + MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT); + MdiControlReg |= MDI_CR_FULL_HALF; + ForcePhySetting = TRUE; + } + } + + // If auto duplex (we actually set phy to 1/2) + else + { + if (MdiStatusReg & (MDI_SR_10T_FULL_DPX | MDI_SR_10T_HALF_DPX)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Forcing 10mb auto duplex\n"); + MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF); + ForcePhySetting = TRUE; + FdoData->AiForceDpx = 1; + } + } + } + + // + // If speed is forced to 100mb + // + else if (FdoData->AiTempSpeed == 100) + { + // If half duplex is forced + if (FdoData->AiForceDpx == 1) + { + if (MdiStatusReg & (MDI_SR_TX_HALF_DPX | MDI_SR_T4_CAPABLE)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Forcing 100mb half duplex\n"); + MdiControlReg &= ~(MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF); + MdiControlReg |= MDI_CR_10_100; + ForcePhySetting = TRUE; + } + } + + // If full duplex is forced + else if (FdoData->AiForceDpx == 2) + { + if (MdiStatusReg & MDI_SR_TX_FULL_DPX) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Forcing 100mb full duplex\n"); + MdiControlReg &= ~MDI_CR_AUTO_SELECT; + MdiControlReg |= (MDI_CR_10_100 | MDI_CR_FULL_HALF); + ForcePhySetting = TRUE; + } + } + + // If auto duplex (we set phy to 1/2) + else + { + if (MdiStatusReg & (MDI_SR_TX_HALF_DPX | MDI_SR_T4_CAPABLE)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Forcing 100mb auto duplex\n"); + MdiControlReg &= ~(MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF); + MdiControlReg |= MDI_CR_10_100; + ForcePhySetting = TRUE; + FdoData->AiForceDpx = 1; + } + } + } + + if (ForcePhySetting == FALSE) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Can't force speed=%d, duplex=%d\n",FdoData->AiTempSpeed, FdoData->AiForceDpx); + + return(STATUS_UNSUCCESSFUL); + } + + // + // Write the MDI control register with our new Phy configuration + // + MdiWrite(FdoData, MDI_CONTROL_REG, FdoData->PhyAddress, MdiControlReg); + + // + // wait 100 milliseconds for auto-negotiation to complete + // + MP_STALL_EXECUTION(100); + + } + + // + // Find out specifically what Phy this is. We do this because for certain + // phys there are specific bits that must be set so that the phy and the + // 82557 work together properly. + // + MdiRead(FdoData, PHY_ID_REG_1, FdoData->PhyAddress, FALSE, &MdiIdLowReg); + MdiRead(FdoData, PHY_ID_REG_2, FdoData->PhyAddress, FALSE, &MdiIdHighReg); + + PhyId = ((UINT) MdiIdLowReg | ((UINT) MdiIdHighReg << 16)); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, " Phy ID is %x\n", PhyId); + + // + // And out the revsion field of the Phy ID so that we'll be able to detect + // future revs of the same Phy. + // + PhyId &= PHY_MODEL_REV_ID_MASK; + + // + // Handle the National TX + // + if (PhyId == PHY_NSC_TX) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Found a NSC TX Phy\n"); + + MdiRead(FdoData, NSC_CONG_CONTROL_REG, FdoData->PhyAddress, FALSE, &MdiMiscReg); + + MdiMiscReg |= (NSC_TX_CONG_TXREADY | NSC_TX_CONG_F_CONNECT); + + // + // If we are configured to do congestion control, then enable the + // congestion control bit in the National Phy + // + if (FdoData->Congest) + MdiMiscReg |= NSC_TX_CONG_ENABLE; + else + MdiMiscReg &= ~NSC_TX_CONG_ENABLE; + + MdiWrite(FdoData, NSC_CONG_CONTROL_REG, FdoData->PhyAddress, MdiMiscReg); + } + + FindPhySpeedAndDpx(FdoData, PhyId); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Current Speed=%d, Current Duplex=%d\n",FdoData->usLinkSpeed, FdoData->usDuplexMode); + + return(STATUS_SUCCESS); +} + + +//----------------------------------------------------------------------------- +// Procedure: FindPhySpeedAndDpx +// +// Description: This routine will figure out what line speed and duplex mode +// the PHY is currently using. +// +// Arguments: +// FdoData - ptr to FdoData object instance +// PhyId - The ID of the PHY in question. +// +// Returns: +// NOTHING +//----------------------------------------------------------------------------- + +VOID FindPhySpeedAndDpx( + IN PFDO_DATA FdoData, + IN UINT PhyId + ) +{ + USHORT MdiStatusReg = 0; + USHORT MdiMiscReg = 0; + USHORT MdiOwnAdReg = 0; + USHORT MdiLinkPartnerAdReg = 0; + + // + // If there was a speed and/or duplex override, then set our current + // value accordingly + // + FdoData->usLinkSpeed = FdoData->AiTempSpeed; + FdoData->usDuplexMode = (USHORT) FdoData->AiForceDpx; + + // + // If speed and duplex were forced, then we know our current settings, so + // we'll just return. Otherwise, we'll need to figure out what NWAY set + // us to. + // + if (FdoData->usLinkSpeed && FdoData->usDuplexMode) + { + return; + } + + // + // If we didn't have a valid link, then we'll assume that our current + // speed is 10mb half-duplex. + // + + // + // Read the status register twice because of sticky bits + // + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + + // + // If there wasn't a valid link then use default speed & duplex + // + if (!(MdiStatusReg & MDI_SR_LINK_STATUS)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Link Not found for speed detection!!! Using defaults.\n"); + + FdoData->usLinkSpeed = 10; + FdoData->usDuplexMode = 1; + + return; + } + + // + // If this is an Intel PHY (a T4 PHY_100 or a TX PHY_TX), then read bits + // 1 and 0 of extended register 0, to get the current speed and duplex + // settings. + // + if ((PhyId == PHY_100_A) || (PhyId == PHY_100_C) || (PhyId == PHY_TX_ID)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Detecting Speed/Dpx for an Intel PHY\n"); + + // + // Read extended register 0 + // + MdiRead(FdoData, EXTENDED_REG_0, FdoData->PhyAddress, FALSE, &MdiMiscReg); + + // + // Get current speed setting + // + if (MdiMiscReg & PHY_100_ER0_SPEED_INDIC) + { + FdoData->usLinkSpeed = 100; + } + else + { + FdoData->usLinkSpeed = 10; + } + + // + // + // Get current duplex setting -- if bit is set then FDX is enabled + // + if (MdiMiscReg & PHY_100_ER0_FDX_INDIC) + { + FdoData->usDuplexMode = 2; + } + else + { + FdoData->usDuplexMode = 1; + } + + return; + } + + // + // Read our link partner's advertisement register + // + MdiRead(FdoData, + AUTO_NEG_LINK_PARTNER_REG, + FdoData->PhyAddress, + FALSE, + &MdiLinkPartnerAdReg); + // + // See if Auto-Negotiation was complete (bit 5, reg 1) + // + MdiRead(FdoData, MDI_STATUS_REG, FdoData->PhyAddress, FALSE, &MdiStatusReg); + + // + // If a True NWAY connection was made, then we can detect speed/duplex by + // ANDing our FdoData's advertised abilities with our link partner's + // advertised ablilities, and then assuming that the highest common + // denominator was chosed by NWAY. + // + if ((MdiLinkPartnerAdReg & NWAY_LP_ABILITY) && + (MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Detecting Speed/Dpx from NWAY connection\n"); + + // + // Read our advertisement register + // + MdiRead(FdoData, AUTO_NEG_ADVERTISE_REG, FdoData->PhyAddress, FALSE, &MdiOwnAdReg); + + // + // AND the two advertisement registers together, and get rid of any + // extraneous bits. + // + MdiOwnAdReg &= (MdiLinkPartnerAdReg & NWAY_LP_ABILITY); + + // + // Get speed setting + // + if (MdiOwnAdReg & (NWAY_AD_TX_HALF_DPX | NWAY_AD_TX_FULL_DPX | NWAY_AD_T4_CAPABLE)) + { + FdoData->usLinkSpeed = 100; + } + else + { + FdoData->usLinkSpeed = 10; + } + + // + // Get duplex setting -- use priority resolution algorithm + // + if (MdiOwnAdReg & (NWAY_AD_T4_CAPABLE)) + { + FdoData->usDuplexMode = 1; + return; + } + else if (MdiOwnAdReg & (NWAY_AD_TX_FULL_DPX)) + { + FdoData->usDuplexMode = 2; + return; + } + else if (MdiOwnAdReg & (NWAY_AD_TX_HALF_DPX)) + { + FdoData->usDuplexMode = 1; + return; + } + else if (MdiOwnAdReg & (NWAY_AD_10T_FULL_DPX)) + { + FdoData->usDuplexMode = 2; + return; + } + else + { + FdoData->usDuplexMode = 1; + return; + } + } + + // + // If we are connected to a non-NWAY repeater or hub, and the line + // speed was determined automatically by parallel detection, then we have + // no way of knowing exactly what speed the PHY is set to unless that PHY + // has a propietary register which indicates speed in this situation. The + // NSC TX PHY does have such a register. Also, since NWAY didn't establish + // the connection, the duplex setting should HALF duplex. + // + FdoData->usDuplexMode = 1; + + if (PhyId == PHY_NSC_TX) + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_HW_ACCESS, " Detecting Speed/Dpx from non-NWAY NSC connection\n"); + + // + // Read register 25 to get the SPEED_10 bit + // + MdiRead(FdoData, NSC_SPEED_IND_REG, FdoData->PhyAddress, FALSE, &MdiMiscReg); + + // + // If bit 6 was set then we're at 10mb + // + if (MdiMiscReg & NSC_TX_SPD_INDC_SPEED) + { + FdoData->usLinkSpeed = 10; + } + else + { + FdoData->usLinkSpeed = 100; + } + } + // + // If we don't know what line speed we are set at, then we'll default to + // 10mbs + // + else + { + FdoData->usLinkSpeed = 10; + } +} + + +//----------------------------------------------------------------------------- +// Procedure: ResetPhy +// +// Description: This routine will reset the PHY that the FdoData is currently +// configured to use. +// +// Arguments: +// FdoData - ptr to FdoData object instance +// +// Returns: +// NOTHING +//----------------------------------------------------------------------------- + +VOID ResetPhy( + IN PFDO_DATA FdoData + ) +{ + USHORT MdiControlReg; + + // + // Reset the Phy, enable auto-negotiation, and restart auto-negotiation. + // + MdiControlReg = (MDI_CR_AUTO_SELECT | MDI_CR_RESTART_AUTO_NEG | MDI_CR_RESET); + + // + // Write the MDI control register with our new Phy configuration + // + MdiWrite(FdoData, MDI_CONTROL_REG, FdoData->PhyAddress, MdiControlReg); +} + diff --git a/general/pcidrv/kmdf/HW/precomp.h b/general/pcidrv/kmdf/HW/precomp.h new file mode 100644 index 00000000..851ce1f1 --- /dev/null +++ b/general/pcidrv/kmdf/HW/precomp.h @@ -0,0 +1,45 @@ +// +// precomp.h for pcidrv driver +// +#define WIN9X_COMPAT_SPINLOCK +#include +#include + +typedef unsigned int UINT; +typedef unsigned int *PUINT; + +#include // required for GUID definitions +#include // required for WMILIB_CONTEXT +#include +#include +#include + + +// +// Disable warnings that prevent our driver from compiling with /W4 MSC_WARNING_LEVEL +// +// Disable warning C4214: nonstandard extension used : bit field types other than int +// Disable warning C4201: nonstandard extension used : nameless struct/union +// Disable warning C4115: named type definition in parentheses +// +#pragma warning(disable:4214) +#pragma warning(disable:4201) +#pragma warning(disable:4115) + +#include "ntddndis.h" // for OIDs + +#pragma warning(default:4214) +#pragma warning(default:4201) +#pragma warning(default:4115) + +#include "nuiouser.h" // for ioctls recevied from ndisedge +#include "public.h" + +#include "e100_equ.h" +#include "e100_557.h" +#include "trace.h" +#include "nic_def.h" +#include "pcidrv.h" +#include "macros.h" + + diff --git a/general/pcidrv/kmdf/HW/precompsrc.c b/general/pcidrv/kmdf/HW/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/general/pcidrv/kmdf/HW/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/general/pcidrv/kmdf/HW/routines.c b/general/pcidrv/kmdf/HW/routines.c new file mode 100644 index 00000000..d6908ea5 --- /dev/null +++ b/general/pcidrv/kmdf/HW/routines.c @@ -0,0 +1,479 @@ +/**************************************************************************** +** COPYRIGHT (C) 1994-1997 INTEL CORPORATION ** +** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON ** +** HTTP://WWW.INTEL.COM/ ** +** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND ** +** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER ** +****************************************************************************/ + +/**************************************************************************** +Module Name: + routines.c + +This driver runs on the following hardware: + - 82558 based PCI 10/100Mb ethernet adapters + (aka Intel EtherExpress(TM) PRO Adapters) + +Environment: + Kernel Mode - Or whatever is the equivalent on WinNT + +*****************************************************************************/ + +//#pragma TRACE_LEVEL_WARNING (disable: 4514 4706) + +//----------------------------------------------------------------------------- +// Procedure: MdiWrite +// +// Description: This routine will write a value to the specified MII register +// of an external MDI compliant device (e.g. PHY 100). The +// command will execute in polled mode. +// +// Arguments: +// Adapter - ptr to Adapter object instance +// RegAddress - The MII register that we are writing to +// PhyAddress - The MDI address of the Phy component. +// DataValue - The value that we are writing to the MII register. +// +// Returns: +// NOTHING +//----------------------------------------------------------------------------- + +#include "precomp.h" + +#if defined(EVENT_TRACING) +#include "routines.tmh" +#endif + +//----------------------------------------------------------------------------- +// Procedure: WaitScb +// +// Description: This routine checks to see if the D100 has accepted a command. +// It does so by checking the command field in the SCB, which will +// be zeroed by the D100 upon accepting a command. The loop waits +// for up to 600 milliseconds for command acceptance. +// +// Arguments: +// Adapter - ptr to Adapter object instance +// +// Returns: +// TRUE if the SCB cleared within 600 milliseconds. +// FALSE if it didn't clear within 600 milliseconds +//----------------------------------------------------------------------------- +__inline BOOLEAN +WaitScb( + IN PFDO_DATA FdoData + ) +{ + BOOLEAN bResult; + + HW_CSR volatile *pCSRAddress = FdoData->CSRAddress; + + MP_STALL_AND_WAIT(pCSRAddress->ScbCommandLow == 0, 600, bResult); + if(!bResult) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "WaitScb failed, ScbCommandLow=%x\n", pCSRAddress->ScbCommandLow); + if(pCSRAddress->ScbCommandLow != 0x80) + { + //ASSERT(FALSE); + } + MP_SET_HARDWARE_ERROR(FdoData); + } + + return bResult; +} + + +VOID +MdiWrite( + IN PFDO_DATA Adapter, + IN ULONG RegAddress, + IN ULONG PhyAddress, + IN USHORT DataValue + ) +{ + BOOLEAN bResult; + + // Issue the write command to the MDI control register. + Adapter->CSRAddress->MDIControl = (((ULONG) DataValue) | + (RegAddress << 16) | + (PhyAddress << 21) | + (MDI_WRITE << 26)); + + // wait 20usec before checking status + KeStallExecutionProcessor (20); + + // wait 2 seconds for the mdi write to complete + MP_STALL_AND_WAIT(Adapter->CSRAddress->MDIControl & MDI_PHY_READY, 2000, bResult); + + if (!bResult) + { + MP_SET_HARDWARE_ERROR(Adapter); + } +} + + +//----------------------------------------------------------------------------- +// Procedure: MdiRead +// +// Description: This routine will read a value from the specified MII register +// of an external MDI compliant device (e.g. PHY 100), and return +// it to the calling routine. The command will execute in polled +// mode. +// +// Arguments: +// Adapter - ptr to Adapter object instance +// RegAddress - The MII register that we are reading from +// PhyAddress - The MDI address of the Phy component. +// Recoverable - Whether the hardware TRACE_LEVEL_ERROR(if any)if recoverable or not +// +// Results: +// DataValue - The value that we read from the MII register. +// +// Returns: +// None +//----------------------------------------------------------------------------- +BOOLEAN +MdiRead( + IN PFDO_DATA Adapter, + IN ULONG RegAddress, + IN ULONG PhyAddress, + IN BOOLEAN Recoverable, + IN OUT PUSHORT DataValue + ) +{ + BOOLEAN bResult; + + // Issue the read command to the MDI control register. + Adapter->CSRAddress->MDIControl = ((RegAddress << 16) | + (PhyAddress << 21) | + (MDI_READ << 26)); + + // wait 20usec before checking status + KeStallExecutionProcessor (20); + + // Wait up to 2 seconds for the mdi read to complete + MP_STALL_AND_WAIT(Adapter->CSRAddress->MDIControl & MDI_PHY_READY, 2000, bResult); + if (!bResult) + { + if (!Recoverable) + { + MP_SET_NON_RECOVER_ERROR(Adapter); + } + MP_SET_HARDWARE_ERROR(Adapter); + return bResult; + } + + *DataValue = (USHORT) Adapter->CSRAddress->MDIControl; + return bResult; + +} + + +//----------------------------------------------------------------------------- +// Procedure: DumpStatsCounters +// +// Description: This routine will dump and reset the 82557's internal +// Statistics counters. The current stats dump values will be +// added to the "Adapter's" overall statistics. +// Arguments: +// Adapter - ptr to Adapter object instance +// +// Returns: +// NOTHING +//----------------------------------------------------------------------------- +VOID +DumpStatsCounters( + IN PFDO_DATA Adapter + ) +{ + BOOLEAN bResult; + //KIRQL oldIrql; + + // The query is for a driver statistic, so we need to first + // update our statistics in software. + + // clear the dump counters complete DWORD + Adapter->StatsCounters->CommandComplete = 0; + + + WdfSpinLockAcquire(Adapter->Lock); + + // Dump and reset the hardware's statistic counters + D100IssueScbCommand(Adapter, SCB_CUC_DUMP_RST_STAT, TRUE); + + // Restore the resume transmit software flag. After the dump counters + // command is issued, we should do a WaitSCB before issuing the next send. + Adapter->ResumeWait = TRUE; + + + WdfSpinLockRelease(Adapter->Lock); + + // wait up to 2 seconds for the dump/reset to complete + MP_STALL_AND_WAIT(Adapter->StatsCounters->CommandComplete == 0xA007, 2000, bResult); + if (!bResult) + { + MP_SET_HARDWARE_ERROR(Adapter); + return; + } + + // Output the debug counters to the debug terminal. + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Good Transmits %d\n", Adapter->StatsCounters->XmtGoodFrames); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Good Receives %d\n", Adapter->StatsCounters->RcvGoodFrames); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Max Collisions %d\n", Adapter->StatsCounters->XmtMaxCollisions); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Late Collisions %d\n", Adapter->StatsCounters->XmtLateCollisions); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Transmit Underruns %d\n", Adapter->StatsCounters->XmtUnderruns); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Transmit Lost CRS %d\n", Adapter->StatsCounters->XmtLostCRS); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Transmits Deferred %d\n", Adapter->StatsCounters->XmtDeferred); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "One Collision xmits %d\n", Adapter->StatsCounters->XmtSingleCollision); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Mult Collision xmits %d\n", Adapter->StatsCounters->XmtMultCollisions); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Total Collisions %d\n", Adapter->StatsCounters->XmtTotalCollisions); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive CRC errors %d\n", Adapter->StatsCounters->RcvCrcErrors); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive Alignment errors %d\n", Adapter->StatsCounters->RcvAlignmentErrors); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive no resources %d\n", Adapter->StatsCounters->RcvResourceErrors); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive overrun errors %d\n", Adapter->StatsCounters->RcvOverrunErrors); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive CDT errors %d\n", Adapter->StatsCounters->RcvCdtErrors); + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive short frames %d\n", Adapter->StatsCounters->RcvShortFrames); + + // update packet counts + Adapter->GoodTransmits += Adapter->StatsCounters->XmtGoodFrames; + Adapter->GoodReceives += Adapter->StatsCounters->RcvGoodFrames; + + // update transmit TRACE_LEVEL_ERROR counts + Adapter->TxAbortExcessCollisions += Adapter->StatsCounters->XmtMaxCollisions; + Adapter->TxLateCollisions += Adapter->StatsCounters->XmtLateCollisions; + Adapter->TxDmaUnderrun += Adapter->StatsCounters->XmtUnderruns; + Adapter->TxLostCRS += Adapter->StatsCounters->XmtLostCRS; + Adapter->TxOKButDeferred += Adapter->StatsCounters->XmtDeferred; + Adapter->OneRetry += Adapter->StatsCounters->XmtSingleCollision; + Adapter->MoreThanOneRetry += Adapter->StatsCounters->XmtMultCollisions; + Adapter->TotalRetries += Adapter->StatsCounters->XmtTotalCollisions; + + // update receive TRACE_LEVEL_ERROR counts + Adapter->RcvCrcErrors += Adapter->StatsCounters->RcvCrcErrors; + Adapter->RcvAlignmentErrors += Adapter->StatsCounters->RcvAlignmentErrors; + Adapter->RcvResourceErrors += Adapter->StatsCounters->RcvResourceErrors; + Adapter->RcvDmaOverrunErrors += Adapter->StatsCounters->RcvOverrunErrors; + Adapter->RcvCdtFrames += Adapter->StatsCounters->RcvCdtErrors; + Adapter->RcvRuntErrors += Adapter->StatsCounters->RcvShortFrames; +} + + +//----------------------------------------------------------------------------- +// Procedure: NICIssueSelectiveReset +// +// Description: This routine will issue a selective reset, forcing the adapter +// the CU and RU back into their idle states. The receive unit +// will then be re-enabled if it was previously enabled, because +// an RNR interrupt will be generated when we abort the RU. +// +// Arguments: +// Adapter - ptr to Adapter object instance +// +// Returns: +// NOTHING +//----------------------------------------------------------------------------- + +VOID +NICIssueSelectiveReset( + PFDO_DATA Adapter + ) +{ + NTSTATUS status; + BOOLEAN bResult; + + // Wait for the SCB to clear before we check the CU status. + if (!MP_TEST_FLAG(Adapter, fMP_ADAPTER_HARDWARE_ERROR)) + { + WaitScb(Adapter); + } + + // If we have issued any transmits, then the CU will either be active, or + // in the suspended state. If the CU is active, then we wait for it to be + // suspended. If the the CU is suspended, then we need to put the CU back + // into the idle state by issuing a selective reset. + if (Adapter->TransmitIdle == FALSE) + { + // Wait up to 2 seconds for suspended state + MP_STALL_AND_WAIT((Adapter->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_ACTIVE, 2000, bResult) + if (!bResult) + { + MP_SET_HARDWARE_ERROR(Adapter); + } + + // Check the current status of the receive unit + if ((Adapter->CSRAddress->ScbStatus & SCB_RUS_MASK) != SCB_RUS_IDLE) + { + // Issue an RU abort. Since an interrupt will be issued, the + // RU will be started by the DPC. + status = D100IssueScbCommand(Adapter, SCB_RUC_ABORT, TRUE); + } + + // Issue a selective reset. + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "CU suspended. ScbStatus=%04x Issue selective reset\n", Adapter->CSRAddress->ScbStatus); + Adapter->CSRAddress->Port = PORT_SELECTIVE_RESET; + + // Wait after a port sel-reset command + KeStallExecutionProcessor (NIC_DELAY_POST_RESET); + + // wait up to 2 ms for port command to complete + MP_STALL_AND_WAIT(Adapter->CSRAddress->Port == 0, 2, bResult) + if (!bResult) + { + MP_SET_HARDWARE_ERROR(Adapter); + } + + // disable interrupts after issuing reset, because the int + // line gets raised when reset completes. + NICDisableInterrupt(Adapter); + + // Restore the transmit software flags. + Adapter->TransmitIdle = TRUE; + Adapter->ResumeWait = TRUE; + } +} + +VOID +NICIssueFullReset( + PFDO_DATA Adapter + ) +{ + BOOLEAN bResult; + + NICIssueSelectiveReset(Adapter); + + Adapter->CSRAddress->Port = PORT_SOFTWARE_RESET; + + // wait up to 2 ms for port command to complete + MP_STALL_AND_WAIT(Adapter->CSRAddress->Port == 0, 2, bResult); + if (!bResult) + { + MP_SET_HARDWARE_ERROR(Adapter); + return; + } + + NICDisableInterrupt(Adapter); +} + + +//----------------------------------------------------------------------------- +// Procedure: D100SubmitCommandBlockAndWait +// +// Description: This routine will submit a command block to be executed, and +// then it will wait for that command block to be executed. Since +// board ints will be disabled, we will ack the interrupt in +// this routine. +// +// Arguments: +// Adapter - ptr to Adapter object instance +// +// Returns: +// NDIS_STATUS_SUCCESS +// STATUS_DEVICE_DATA_ERROR +//----------------------------------------------------------------------------- + +NTSTATUS +D100SubmitCommandBlockAndWait( + IN PFDO_DATA Adapter + ) +{ + NTSTATUS status; + BOOLEAN bResult; + + // Points to the Non Tx Command Block. + NON_TRANSMIT_CB volatile *CommandBlock = Adapter->NonTxCmdBlock; + + // Set the Command Block to be the last command block + CommandBlock->NonTxCb.Config.ConfigCBHeader.CbCommand |= CB_EL_BIT; + + // Clear the status of the command block + CommandBlock->NonTxCb.Config.ConfigCBHeader.CbStatus = 0; + +#if DBG + // Don't try to start the CU if the command unit is active. + if ((Adapter->CSRAddress->ScbStatus & SCB_CUS_MASK) == SCB_CUS_ACTIVE) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "Scb %p ScbStatus %04x\n", Adapter->CSRAddress, Adapter->CSRAddress->ScbStatus); + ASSERT(FALSE); + MP_SET_HARDWARE_ERROR(Adapter); + return(STATUS_DEVICE_DATA_ERROR); + } +#endif + + // Start the command unit. + D100IssueScbCommand(Adapter, SCB_CUC_START, FALSE); + + // Wait for the SCB to clear, indicating the completion of the command. + if (!WaitScb(Adapter)) + { + return(STATUS_DEVICE_DATA_ERROR); + } + + // Wait for some status, timeout value 3 secs + MP_STALL_AND_WAIT(CommandBlock->NonTxCb.Config.ConfigCBHeader.CbStatus & CB_STATUS_COMPLETE, 3000, bResult); + if (!bResult) + { + MP_SET_HARDWARE_ERROR(Adapter); + return(STATUS_DEVICE_DATA_ERROR); + } + + // Ack any interrupts + if (Adapter->CSRAddress->ScbStatus & SCB_ACK_MASK) + { + // Ack all pending interrupts now + Adapter->CSRAddress->ScbStatus &= SCB_ACK_MASK; + } + + // Check the status of the command, and if the command failed return FALSE, + // otherwise return TRUE. + if (!(CommandBlock->NonTxCb.Config.ConfigCBHeader.CbStatus & CB_STATUS_OK)) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "Command failed\n"); + MP_SET_HARDWARE_ERROR(Adapter); + status = STATUS_DEVICE_DATA_ERROR; + } + else + status = STATUS_SUCCESS; + + return(status); +} + +//----------------------------------------------------------------------------- +// Procedure: GetConnectionStatus +// +// Description: This function returns the connection status that is +// a required indication for PC 97 specification from MS +// the value we are looking for is if there is link to the +// wire or not. +// +// Arguments: IN Adapter structure pointer +// +// Returns: NdisMediaStateConnected +// NdisMediaStateDisconnected +//----------------------------------------------------------------------------- +MEDIA_STATE +GetMediaState( + IN PFDO_DATA Adapter + ) +{ + USHORT MdiStatusReg = 0; + BOOLEAN bResult1; + BOOLEAN bResult2; + + + // Read the status register at phy 1 + bResult1 = MdiRead(Adapter, MDI_STATUS_REG, Adapter->PhyAddress, TRUE, &MdiStatusReg); + bResult2 = MdiRead(Adapter, MDI_STATUS_REG, Adapter->PhyAddress, TRUE, &MdiStatusReg); + + // if there is hardware failure, or let the state remains the same + if (!bResult1 || !bResult2) + { + return Adapter->MediaState; + } + if (MdiStatusReg & MDI_SR_LINK_STATUS) + return(Connected); + else + return(Disconnected); + +} + diff --git a/general/pcidrv/kmdf/PCIDRV.C b/general/pcidrv/kmdf/PCIDRV.C new file mode 100644 index 00000000..41a71b3c --- /dev/null +++ b/general/pcidrv/kmdf/PCIDRV.C @@ -0,0 +1,1792 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + PciDrv.c + +Abstract: + + This is a generic WDM sample driver for Intel 82557/82558 + based PCI Ethernet Adapter (10/100) and Intel compatibles. + The WDM interface in this sample is based on the Toaster function + driver, and all the code to access the hardware is taken from + the E100BEX NDIS miniport sample from the DDK and converted to + use WDM interfaces instead of NDIS functions. + + This driver can be installed as a standalone driver (genpci.inf) + for the Intel PCI device. Please read the PCIDRV.HTM file for + more information. + +Environment: + + Kernel mode + +--*/ + +#include "precomp.h" + +#if defined(EVENT_TRACING) +// +// The trace message header (.tmh) file must be included in a source file +// before any WPP macro calls and after defining a WPP_CONTROL_GUIDS +// macro (defined in toaster.h). During the compilation, WPP scans the source +// files for DoTraceMessage() calls and builds a .tmh file which stores a unique +// data GUID for each message, the text resource string for each message, +// and the data types of the variables passed in for each message. This file +// is automatically generated and used during post-processing. +// +#include "pcidrv.tmh" +#endif + +// +// Global debug error level +// +#if !defined(EVENT_TRACING) +ULONG DebugLevel = TRACE_LEVEL_INFORMATION; +ULONG DebugFlag = 0x2f;//0x46;//0x4FF; //0x00000006; +#else +ULONG DebugLevel; // wouldn't be used to control the TRACE_LEVEL_VERBOSE +ULONG DebugFlag; +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, PciDrvEvtDeviceAdd) +#pragma alloc_text (PAGE, PciDrvEvtDeviceContextCleanup) +#pragma alloc_text (PAGE, PciDrvEvtDevicePrepareHardware) +#pragma alloc_text (PAGE, PciDrvEvtDeviceReleaseHardware) +#pragma alloc_text (PAGE, PciDrvReadRegistryValue) +#pragma alloc_text (PAGE, PciDrvWriteRegistryValue) +#pragma alloc_text (PAGE, PciDrvEvtDriverContextCleanup) +#pragma alloc_text (PAGE, PciDrvEvtDeviceSelfManagedIoCleanup) +#pragma alloc_text (PAGE, PciDrvEvtDeviceSelfManagedIoSuspend) +#pragma alloc_text (PAGE, PciDrvEvtDeviceWakeArmS0) +#pragma alloc_text (PAGE, PciDrvEvtDeviceWakeTriggeredS0) +#pragma alloc_text (PAGE, PciDrvEvtDeviceWakeArmSx) +#pragma alloc_text (PAGE, PciDrvSetPowerPolicy) +#pragma alloc_text (PAGE, PciDrvReadFdoRegistryKeyValue) +#endif + + +#define PARAMATER_NAME_LEN 80 + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path, + to driver-specific key in the registry. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG config; + WDF_OBJECT_ATTRIBUTES attrib; + WDFDRIVER driver; + PDRIVER_CONTEXT driverContext; + + // + // Initialize WPP Tracing + // + WPP_INIT_TRACING( DriverObject, RegistryPath ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "PCIDRV Sample - Driver Framework Edition \n"); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attrib, DRIVER_CONTEXT); + + // + // Register a cleanup callback so that we can call WPP_CLEANUP when + // the framework driver object is deleted during driver unload. + // + attrib.EvtCleanupCallback = PciDrvEvtDriverContextCleanup; + + // + // Initialize the Driver Config structure.. + // + WDF_DRIVER_CONFIG_INIT(&config, PciDrvEvtDeviceAdd); + + // + // Create a WDFDRIVER object. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + &attrib, + &config, + &driver); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "WdfDriverCreate failed with status %!STATUS!\n", status); + // + // Cleanup tracing here because DriverContextCleanup will not be called + // as we have failed to create WDFDRIVER object itself. + // Please note that if your return failure from DriverEntry after the + // WDFDRIVER object is created successfully, you don't have to + // call WPP cleanup because in those cases DriverContextCleanup + // will be executed when the framework deletes the DriverObject. + // + WPP_CLEANUP(DriverObject); + return status; + } + + driverContext = GetDriverContext(driver); + + // + // Create a driver wide lookside list used for allocating memory for the + // MP_RFD structure for all device instances (if there are multiple present). + // + status = WdfLookasideListCreate(WDF_NO_OBJECT_ATTRIBUTES, // LookAsideAttributes + sizeof(MP_RFD), + NonPagedPool, + WDF_NO_OBJECT_ATTRIBUTES, // MemoryAttributes + PCIDRV_POOL_TAG, + &driverContext->RecvLookaside + ); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "Couldn't allocate lookaside list status %!STATUS!\n", status); + return status; + } + + return status; + +} + +NTSTATUS +PciDrvEvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + EvtDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDF_POWER_POLICY_EVENT_CALLBACKS powerPolicyCallbacks; + WDF_OBJECT_ATTRIBUTES fdoAttributes; + WDFDEVICE device; + PFDO_DATA fdoData = NULL; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "-->PciDrvEvtDeviceAdd routine. Driver: 0x%p\n", Driver); + + // + // I/O type is Buffered by default. If required to use something else, + // call WdfDeviceInitSetIoType with the appropriate type. + // + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect); + + // + // Zero out the PnpPowerCallbacks structure. + // + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); + + // + // Set Callbacks for any of the functions we are interested in. + // If no callback is set, Framework will take the default action + // by itself. This sample provides many of the possible callbacks, + // mostly because it's a fairly complex sample that drives full-featured + // hardware. Drivers derived from this sample will often be able to + // provide only some of these. + // + + // + // These callback is invoked to tear down all the driver-managed state + // that is set up in this function. Many times, this callback won't do + // much of anything, since many of the things that are set up here will + // have their lifetimes automatically managed by the Framework. + // + + + // + // These two callbacks set up and tear down hardware state, + // specifically that which only has to be done once. + // + + pnpPowerCallbacks.EvtDevicePrepareHardware = PciDrvEvtDevicePrepareHardware; + pnpPowerCallbacks.EvtDeviceReleaseHardware = PciDrvEvtDeviceReleaseHardware; + + // + // These two callbacks set up and tear down hardware state that must be + // done every time the device moves in and out of the D0-working state. + // + + pnpPowerCallbacks.EvtDeviceD0Entry = PciDrvEvtDeviceD0Entry; + pnpPowerCallbacks.EvtDeviceD0Exit = PciDrvEvtDeviceD0Exit; + + // + // These next two callbacks are for doing work at PASSIVE_LEVEL (low IRQL) + // after all the interrupts are connected and before they are disconnected. + // + // Some drivers need to do device initialization and tear-down while the + // interrupt is connected. (This is a problem for these devices, since + // it opens them up to taking interrupts before they are actually ready + // to handle them, or to taking them after they have torn down too much + // to be able to handle them.) While this hardware design pattern is to + // be discouraged, it is possible to handle it by doing device init and + // tear down in these routines rather than in EvtDeviceD0Entry and + // EvtDeviceD0Exit. + // + // In this sample these callbacks don't do anything. + // + + pnpPowerCallbacks.EvtDeviceD0EntryPostInterruptsEnabled = NICEvtDeviceD0EntryPostInterruptsEnabled; + pnpPowerCallbacks.EvtDeviceD0ExitPreInterruptsDisabled = NICEvtDeviceD0ExitPreInterruptsDisabled; + + // + // This next group of five callbacks allow a driver to become involved in + // starting and stopping operations within a driver as the driver moves + // through various PnP/Power states. These functions are not necessary + // if the Framework is managing all the device's queues and there is no + // activity going on that isn't queue-based. This sample provides these + // callbacks because it uses watchdog timer to monitor whether the device + // is working or not and it needs to start and stop the timer when the device + // is started or removed. It cannot start and stop the timers in the D0Entry + // and D0Exit callbacks because if the device is surprise-removed, D0Exit + // will not be called. + // + pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = PciDrvEvtDeviceSelfManagedIoInit; + pnpPowerCallbacks.EvtDeviceSelfManagedIoCleanup = PciDrvEvtDeviceSelfManagedIoCleanup; + pnpPowerCallbacks.EvtDeviceSelfManagedIoSuspend = PciDrvEvtDeviceSelfManagedIoSuspend; + pnpPowerCallbacks.EvtDeviceSelfManagedIoRestart = PciDrvEvtDeviceSelfManagedIoRestart; + + // + // Register the PnP and power callbacks. Power policy related callbacks will be registered + // later. + // + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + // + // Init the power policy callbacks + // + WDF_POWER_POLICY_EVENT_CALLBACKS_INIT(&powerPolicyCallbacks); + + // + // This group of three callbacks allows this sample driver to manage + // arming the device for wake from the S0 state. Networking devices can + // optionally be put into a low-power state when there is no networking + // cable plugged into them. This sample implements this feature. + // + powerPolicyCallbacks.EvtDeviceArmWakeFromS0 = PciDrvEvtDeviceWakeArmS0; + powerPolicyCallbacks.EvtDeviceDisarmWakeFromS0 = PciDrvEvtDeviceWakeDisarmS0; + powerPolicyCallbacks.EvtDeviceWakeFromS0Triggered = PciDrvEvtDeviceWakeTriggeredS0; + + // + // This group of three callbacks allows the device to be armed for wake + // from Sx (S1, S2, S3 or S4.) Networking devices can optionally be put + // into a state where a packet sent to them will cause the device's wake + // signal to be triggered, which causes the machine to wake, moving back + // into the S0 state. + // + + powerPolicyCallbacks.EvtDeviceArmWakeFromSx = PciDrvEvtDeviceWakeArmSx; + powerPolicyCallbacks.EvtDeviceDisarmWakeFromSx = PciDrvEvtDeviceWakeDisarmSx; + powerPolicyCallbacks.EvtDeviceWakeFromSxTriggered = PciDrvEvtDeviceWakeTriggeredSx; + + // + // Register the power policy callbacks. + // + WdfDeviceInitSetPowerPolicyEventCallbacks(DeviceInit, &powerPolicyCallbacks); + + // Since we are the function driver, we are now the power policy owner + // for the device according to the default framework rule. We will register + // our power policy callbacks after finding the wakeup capability of the device. + + // + // Specify the context type and size for the device we are about to create. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FDO_DATA); + + // + // ContextCleanup will be called by the framework when it deletes the device. + // So you can defer freeing any resources allocated to Cleanup callback in the + // event AddDevice returns any error after the device is created. + // + fdoAttributes.EvtCleanupCallback = PciDrvEvtDeviceContextCleanup; + + status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &device); + + if ( !NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceInitialize failed %!STATUS!\n", status); + return status; + } + + // + // Device creation is complete. + // Get the DeviceExtension and initialize it. + // + fdoData = FdoGetData(device); + fdoData->WdfDevice = device; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "PDO(0x%p) FDO(0x%p), Lower(0x%p) DevExt (0x%p)\n", + WdfDeviceWdmGetPhysicalDevice (device), + WdfDeviceWdmGetDeviceObject (device), + WdfDeviceWdmGetAttachedDevice(device), + fdoData); + + // + // Initialize the device extension and allocate all the software resources + // + status = NICAllocateSoftwareResources(fdoData); + if (!NT_SUCCESS (status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "NICAllocateSoftwareResources failed: %!STATUS!\n", + status); + return status; + } + + // + // If our device supports wait-wake then we will set our power-policy and + // update S0-Idle policy. + // + if (IsPoMgmtSupported(fdoData)) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "Device has wait-wake capability\n"); + status = PciDrvSetPowerPolicy(fdoData); + if (!NT_SUCCESS (status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "PciDrvSetPowerPolicy failed %!STATUS!\n", status); + return status; + } + } + + // + // Tell the Framework that this device will need an interface so that + // application can interact with it. + // + status = WdfDeviceCreateDeviceInterface( + device, + (LPGUID) &GUID_DEVINTERFACE_PCIDRV, + NULL + ); + + if (!NT_SUCCESS (status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceCreateDeviceInterface failed %!STATUS!\n", status); + return status; + } + + status = PciDrvWmiRegistration(device); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- PciDrvEvtDeviceAdd \n"); + + return status; +} + +VOID +PciDrvEvtDeviceContextCleanup ( + WDFOBJECT Device + ) +/*++ + +Routine Description: + + EvtDeviceContextCleanup event callback cleans up anything done in + EvtDeviceAdd, except those things that are automatically cleaned + up by the Framework. + + In the case of this sample, everything is automatically handled. In a + driver derived from this sample, it's quite likely that this function could + be deleted. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + VOID + +--*/ +{ + PFDO_DATA fdoData = NULL; + + PAGED_CODE(); + + fdoData = FdoGetData((WDFDEVICE)Device); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceContextCleanup\n"); + + NICFreeSoftwareResources(fdoData); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceContextCleanup\n"); + +} + +NTSTATUS +PciDrvEvtDevicePrepareHardware ( + WDFDEVICE Device, + WDFCMRESLIST Resources, + WDFCMRESLIST ResourcesTranslated + ) +/*++ + +Routine Description: + + EvtDeviceStart event callback performs operations that are necessary + to make the driver's device operational. The framework calls the driver's + EvtDeviceStart callback when the PnP manager sends an IRP_MN_START_DEVICE + request to the driver stack. + +Arguments: + + Device - Handle to a framework device object. + + Resources - Handle to a collection of framework resource objects. + This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + ResourcesTranslated - Handle to a collection of framework resource objects. + This collection identifies the translated (system-physical) + hardware resources that have been assigned to the device. + The resources appear from the CPU's point of view. + Use this list of resources to map I/O space and + device-accessible memory into virtual address space + +Return Value: + + WDF status code + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PFDO_DATA fdoData = NULL; + + UNREFERENCED_PARAMETER(Resources); + UNREFERENCED_PARAMETER(ResourcesTranslated); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDevicePrepareHardware\n"); + + fdoData = FdoGetData(Device); + + status = NICMapHWResources(fdoData, Resources, ResourcesTranslated); + if (!NT_SUCCESS (status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "NICMapHWResources failed: %!STATUS!\n", status); + return status; + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDevicePrepareHardware\n"); + + return status; + +} + +NTSTATUS +PciDrvEvtDeviceReleaseHardware( + IN WDFDEVICE Device, + IN WDFCMRESLIST ResourcesTranslated + ) +/*++ + +Routine Description: + + EvtDeviceReleaseHardware is called by the framework whenever the PnP manager + is revoking ownership of our resources. This may be in response to either + IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE. The callback is made before + passing down the IRP to the lower driver. + + In this callback, do anything necessary to free those resources. + +Arguments: + + Device - Handle to a framework device object. + + ResourcesTranslated - Handle to a collection of framework resource objects. + This collection identifies the translated (system-physical) + hardware resources that have been assigned to the device. + The resources appear from the CPU's point of view. + Use this list of resources to map I/O space and + device-accessible memory into virtual address space + +Return Value: + + NTSTATUS - Failures will be logged, but not acted on. + +--*/ +{ + PFDO_DATA fdoData = NULL; + + UNREFERENCED_PARAMETER(ResourcesTranslated); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceReleaseHardware\n"); + + fdoData = FdoGetData(Device); + + // + // Unmap any I/O ports. Disconnecting from the interrupt will be done + // automatically by the framework. + // + NICUnmapHWResources(fdoData); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceReleaseHardware\n"); + + return STATUS_SUCCESS; +} + +NTSTATUS +PciDrvEvtDeviceD0Entry( + IN WDFDEVICE Device, + IN WDF_POWER_DEVICE_STATE PreviousState + ) +/*++ + +Routine Description: + + EvtDeviceD0Entry event callback must perform any operations that are + necessary before the specified device is used. It will be called every + time the hardware needs to be (re-)initialized. This includes after + IRP_MN_START_DEVICE, IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE, + IRP_MN_SET_POWER-D0. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + + This function runs at PASSIVE_LEVEL, even though it is not paged. A + driver can optionally make this function pageable if DO_POWER_PAGABLE + is set. Even if DO_POWER_PAGABLE isn't set, this function still runs + at PASSIVE_LEVEL. In this case, though, the function absolutely must + not do anything that will cause a page fault. + +Arguments: + + Device - Handle to a framework device object. + + PreviousState - Device power state which the device was in most recently. + If the device is being newly started, this will be + PowerDeviceUnspecified. + +Return Value: + + NTSTATUS + +--*/ +{ + PFDO_DATA fdoData; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, + "-->PciDrvEvtDeviceD0Entry - coming from %s\n", + DbgDevicePowerString(PreviousState)); + + fdoData = FdoGetData(Device); + + ASSERT(PowerDeviceD0 != PreviousState); + + fdoData->DevicePowerState = PowerDeviceD0; + + if(IsPoMgmtSupported(fdoData)){ + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, + "Entering fully on state\n"); + MPSetPowerD0 (fdoData); + } + + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--PciDrvEvtDeviceD0Entry\n"); + + return STATUS_SUCCESS; +} + + +NTSTATUS +PciDrvEvtDeviceD0Exit( + IN WDFDEVICE Device, + IN WDF_POWER_DEVICE_STATE TargetState + ) +/*++ + +Routine Description: + + This routine undoes anything done in EvtDeviceD0Entry. It is called + whenever the device leaves the D0 state, which happens when the device is + stopped, when it is removed, and when it is powered off. + + The device is still in D0 when this callback is invoked, which means that + the driver can still touch hardware in this routine. + + Note that interrupts have already been disabled by the time that this + callback is invoked. + + EvtDeviceD0Exit event callback must perform any operations that are + necessary before the specified device is moved out of the D0 state. If the + driver needs to save hardware state before the device is powered down, then + that should be done here. + + This function runs at PASSIVE_LEVEL, though it is generally not paged. A + driver can optionally make this function pageable if DO_POWER_PAGABLE is set. + + Even if DO_POWER_PAGABLE isn't set, this function still runs at + PASSIVE_LEVEL. In this case, though, the function absolutely must not do + anything that will cause a page fault. + +Arguments: + + Device - Handle to a framework device object. + + TargetState - Device power state which the device will be put in once this + callback is complete. + +Return Value: + + Success implies that the device can be used. Failure will result in the + device stack being torn down. + +--*/ +{ + PFDO_DATA fdoData; + + UNREFERENCED_PARAMETER(Device); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, + "-->PciDrvEvtDeviceD0Exit - moving to %s\n", + DbgDevicePowerString(TargetState)); + + fdoData = FdoGetData(Device); + + fdoData->DevicePowerState = TargetState; + + switch (TargetState) { + case WdfPowerDeviceD1: + case WdfPowerDeviceD2: + case WdfPowerDeviceD3: + + if(IsPoMgmtSupported(fdoData)){ + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, + "Entering a deeper sleep state\n"); + MPSetPowerLow (fdoData, TargetState); + } + break; + + case WdfPowerDevicePrepareForHibernation: + + // + // Fill in any code to save hardware state here. Do not put in any + // code to shut the device off. If this device cannot support being + // in the paging path (or being a parent or grandparent of a paging + // path device) then this whole case can be deleted. + // + ASSERT(FALSE); // This driver shouldn't get this. + break; + + case WdfPowerDeviceD3Final: + // + // Reset and put the device into a known initial state we're shutting + // down for the last time. + // + NICShutdown(fdoData); + break; + + default: + break; + } + + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--PciDrvEvtDeviceD0Exit\n"); + + return STATUS_SUCCESS; +} + +NTSTATUS +PciDrvEvtDeviceSelfManagedIoInit( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + PciDrvEvtDeviceSelfManagedIoInit is called by the Framework when the device + enters the D0 state. Its job is to start any I/O-related actions that the + Framework isn't managing. This might include releasing queues that are not + power-managed, that is, the Framework is not automatically holding and releasing + them across PnP/Power transitions. (The default behavior for WDFQUEUE is + auto-managed, so most queues don't need to be dealt with here.) This might + also include setting up non-queue-based actions. + + If you allow the Framework to manage most or all of your queues, then when + you build a driver from this sample, you can probably delete this function. + + In this driver, the SelfManagedIo callbacks are used to implement a watchdog timer. + + This function is not marked pagable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - Failures will result in the device stack being torn down. + +--*/ +{ + PFDO_DATA fdoData = NULL; + WDF_TIMER_CONFIG wdfTimerConfig; + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES timerAttributes; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceSelfManagedIoInit\n"); + + fdoData = FdoGetData(Device); + // + // To minimize init-time, create a timer DPC to do link detection. + // This DPC will also be used to check for hardware hang. + // + WDF_TIMER_CONFIG_INIT(&wdfTimerConfig, NICWatchDogEvtTimerFunc); + + WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes); + timerAttributes.ParentObject = fdoData->WdfDevice; + + status = WdfTimerCreate( + &wdfTimerConfig, + &timerAttributes, + &fdoData->WatchDogTimer + ); + + if(!NT_SUCCESS(status) ) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "Error: WdfTimerCreate create failed 0x%x\n", status); + return status; + } + + NICStartWatchDogTimer(fdoData); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceSelfManagedIoInit\n"); + + return status; +} + +NTSTATUS +PciDrvEvtDeviceSelfManagedIoSuspend( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceSelfManagedIoSuspend is called by the Framework before the device + leaves the D0 state. Its job is to stop any I/O-related actions that the + Framework isn't managing, and which cannot be handled when the device + hardware isn't available. In general, this means reversing anything that + was done in EvtDeviceSelfManagedIoStart. + + If you allow the Framework to manage most or all of your queues, then when + you build a driver from this sample, you can probably delete this function. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - Failures will result in the device stack being torn down. + +--*/ +{ + PFDO_DATA fdoData = NULL; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceSelfManagedIoSuspend\n"); + + fdoData = FdoGetData(Device); + + // + // Stop the watchdog timer and wait for DPC to run to completion if + // it's already fired. + // + WdfTimerStop(fdoData->WatchDogTimer, TRUE); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceSelfManagedIoSuspend\n"); + + return STATUS_SUCCESS; +} + +NTSTATUS +PciDrvEvtDeviceSelfManagedIoRestart( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceSelfManagedIoRestart is called by the Framework before the device + is restarted for one of the following reasons: + a) the PnP resources were rebalanced (framework received + query-stop and stop IRPS ) + b) the device resumed from a low power state to D0. + + This function is not marked pagable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - Failure will cause the device stack to be torn down. + +--*/ +{ + PFDO_DATA fdoData; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceSelfManagedIoRestart\n"); + + fdoData = FdoGetData(Device); + + // + // Restart the watchdog timer. + // + NICStartWatchDogTimer(fdoData); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceSelfManagedIoRestart\n"); + + return STATUS_SUCCESS; +} + +VOID +PciDrvEvtDeviceSelfManagedIoCleanup( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceSelfManagedIoCleanup is called by the Framework when the device is + being torn down, either in response to the WDM IRP_MN_REMOVE_DEVICE + It will be called only once. Its job is to stop all outstanding I/O in the driver + that the Framework is not managing. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + None + +--*/ +{ + PFDO_DATA fdoData = NULL; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceSelfManagedIoCleanup\n"); + + fdoData = FdoGetData(Device); + + if(fdoData->WatchDogTimer) { + WdfObjectDelete(fdoData->WatchDogTimer); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceSelfManagedIoCleanup\n"); +} + + +VOID +PciDrvEvtIoDeviceControl( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t OutputBufferLength, + IN size_t InputBufferLength, + IN ULONG IoControlCode + ) +/*++ + +Routine Description: + + This event is called when the framework receives IRP_MJ_DEVICE_CONTROL + requests from the system. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + Request - Handle to a framework request object. + + OutputBufferLength - length of the request's output buffer, + if an output buffer is available. + InputBufferLength - length of the request's input buffer, + if an input buffer is available. + + IoControlCode - the driver-defined or system-defined I/O control code + (IOCTL) that is associated with the request. +Return Value: + + VOID + +--*/ +{ + NTSTATUS status= STATUS_SUCCESS; + PFDO_DATA fdoData = NULL; + WDFDEVICE hDevice; + WDF_REQUEST_PARAMETERS params; + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, + "PciDrvEvtIoDeviceControl called %p\n", Request); + + hDevice = WdfIoQueueGetDevice(Queue); + fdoData = FdoGetData(hDevice); + + WDF_REQUEST_PARAMETERS_INIT(¶ms); + + WdfRequestGetParameters( + Request, + ¶ms + ); + + switch (IoControlCode) + { + case IOCTL_NDISPROT_QUERY_OID_VALUE: + + ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); + + NICHandleQueryOidRequest( + Queue, + Request, + ¶ms + ); + break; + + case IOCTL_NDISPROT_SET_OID_VALUE: + + ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); + + NICHandleSetOidRequest( + Queue, + Request, + ¶ms + ); + + break; + + case IOCTL_NDISPROT_INDICATE_STATUS: + + status = WdfRequestForwardToIoQueue(Request, + fdoData->PendingIoctlQueue); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTLS, + "WdfRequestForwardToIoQueue failed 0x%x\n", status); + WdfRequestComplete(Request, status); + break; + } + + break; + + default: + ASSERTMSG(FALSE, "Invalid IOCTL request\n"); + WdfRequestComplete(Request, STATUS_INVALID_DEVICE_REQUEST); + break; + } + + return; +} + +NTSTATUS +PciDrvEvtDeviceWakeArmS0( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceWakeArmS0 is called when the Framework arms the device for + wake in the S0 state. If there is any device-specific initialization + that needs to be done to arm internal wake signals, or to route internal + interrupt signals to the wake logic, it should be done here. The device + will be moved out of the D0 state soon after this callback is invoked. + + In this sample, wake from S0 involves waking on packet arrival, as does + wake from Sx. A more common NIC implementation might wake on cable + insertion. + + This function is pageable and it will run at PASSIVE_LEVEL. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + NTSTATUS - Failure will result in the device remaining in the D0 state. + +--*/ +{ + NTSTATUS status; + PFDO_DATA fdoData; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceWakeArmS0\n"); + + fdoData = FdoGetData(Device); + + // + // Add pattern before sending wait-wake + // + status = NICConfigureForWakeUp(fdoData, TRUE); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceWakeArmS0 %x\n", status); + + return status; +} + +NTSTATUS +PciDrvEvtDeviceWakeArmSx( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceWakeArmSx is called when the Framework arms the device for + wake from the S1, S2, S3 or S4 states. If there is any device-specific + initialization that needs to be done to arm internal wake signals, or to + route internal interrupt signals to the wake logic, it should be done here. + The device will be moved out of the D0 state soon after this callback is + invoked. + + In this sample, wake from Sx involves arming for wake on packet arrival. + Cable insertion should not be enabled, as nobody would want their machine + to wake up simply because they plugged the cable in. + + This function runs at PASSIVE_LEVEL. Whether it is pageable or not depends + on whether the device has set DO_POWER_PAGABLE. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + NTSTATUS - Failure will result in the device not being armed for wake + while the system is in Sx. + +--*/ +{ + NTSTATUS status; + PFDO_DATA fdoData; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceWakeArmSx\n"); + + fdoData = FdoGetData(Device); + // + // Add pattern before sending wait-wake + // + status = NICConfigureForWakeUp(fdoData, TRUE); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceWakeArmSx %x\n", status); + + return status; +} + +VOID +PciDrvEvtDeviceWakeDisarmS0( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceWakeDisarmS0 reverses anything done in EvtDeviceWakeArmS0. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + VOID. + +--*/ +{ + NTSTATUS status; + PFDO_DATA fdoData; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceWakeDisarmS0\n"); + + fdoData = FdoGetData(Device); + status = NICConfigureForWakeUp(fdoData, FALSE); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceWakeDisarmS0 %x\n", status); + + return ; +} + +VOID +PciDrvEvtDeviceWakeDisarmSx( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceWakeDisarmSx reverses anything done in EvtDeviceWakeArmSx. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + + This function runs at PASSIVE_LEVEL, even though it is not paged. A + driver can optionally make this function pageable if DO_POWER_PAGABLE + is set. Even if DO_POWER_PAGABLE isn't set, this function still runs + at PASSIVE_LEVEL. In this case, though, the function absolutely must + not do anything that will cause a page fault. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS status; + PFDO_DATA fdoData; + + UNREFERENCED_PARAMETER(Device); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceWakeDisarmSx\n"); + + fdoData = FdoGetData(Device); + status = NICConfigureForWakeUp(fdoData, FALSE); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceWakeDisarmSx %x\n", status); + + return; +} + +VOID +PciDrvEvtDeviceWakeTriggeredS0( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceWakeTriggeredS0 will be called whenever the device triggers its + wake signal after being armed for wake from S0. + + This function is pageable and runs at PASSIVE_LEVEL. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + VOID + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceWakeTriggeredS0\n"); + + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceWakeTriggeredS0\n"); +} + +VOID +PciDrvEvtDeviceWakeTriggeredSx( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceWakeTriggeredSx will be called whenever the device triggers its + wake signal after being armed for wake from Sx. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + + This function runs at PASSIVE_LEVEL, even though it is not paged. A + driver can optionally make this function pageable if DO_POWER_PAGABLE + is set. Even if DO_POWER_PAGABLE isn't set, this function still runs + at PASSIVE_LEVEL. In this case, though, the function absolutely must + not do anything that will cause a page fault. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + VOID + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvEvtDeviceWakeTriggeredSx"); + + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvEvtDeviceWakeTriggeredSx"); + + return; +} + + +NTSTATUS +PciDrvQueuePassiveLevelCallback( + IN PFDO_DATA FdoData, + IN PFN_WDF_WORKITEM CallbackFunction, + IN PVOID Context1, + IN PVOID Context2 + ) +/*++ + Routine Description: + + This routine is used to queue workitems so that the callback + functions can be executed at PASSIVE_LEVEL in the conext of + a system thread. + +Arguments: + + FdoData - pointer to a device extenion. + + CallbackFunction - Function to invoke when at PASSIVE_LEVEL. + + Context1 & 2 - Meaning of the context values depends on the + callback function. + +Return Value: + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PWORKER_ITEM_CONTEXT context; + WDF_OBJECT_ATTRIBUTES attributes; + WDF_WORKITEM_CONFIG workitemConfig; + WDFWORKITEM hWorkItem; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, WORKER_ITEM_CONTEXT); + + attributes.ParentObject = FdoData->WdfDevice; + + WDF_WORKITEM_CONFIG_INIT(&workitemConfig, CallbackFunction); + + status = WdfWorkItemCreate( &workitemConfig, + &attributes, + &hWorkItem); + + if (!NT_SUCCESS(status)) { + return status; + } + + context = GetWorkItemContext(hWorkItem); + + context->FdoData = FdoData; + context->Argument1 = Context1; + context->Argument2 = Context2; + + // + // Execute this work item. + // + WdfWorkItemEnqueue(hWorkItem); + + return STATUS_SUCCESS; +} + + +BOOLEAN +PciDrvReadRegistryValue( + _In_ PFDO_DATA FdoData, + _In_ PWSTR Name, + _Out_ PULONG Value + ) +/*++ + +Routine Description: + + Can be used to read any REG_DWORD registry value stored + under Device Parameter. + +Arguments: + + FdoData - pointer to the device extension + Name - Name of the registry value + Value - + + +Return Value: + + TRUE if successful + FALSE if not present/error in reading registry + +--*/ +{ + WDFKEY hKey = NULL; + NTSTATUS status; + BOOLEAN retValue = FALSE; + UNICODE_STRING valueName; + + + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "-->PciDrvReadRegistryValue \n"); + + *Value = 0; + + status = WdfDeviceOpenRegistryKey(FdoData->WdfDevice, + PLUGPLAY_REGKEY_DEVICE, + STANDARD_RIGHTS_ALL, + WDF_NO_OBJECT_ATTRIBUTES, + &hKey); + + if (NT_SUCCESS (status)) { + + RtlInitUnicodeString(&valueName,Name); + + status = WdfRegistryQueryULong( hKey, + &valueName, + Value ); + + if (NT_SUCCESS (status)) { + retValue = TRUE; + } + + WdfRegistryClose(hKey); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "<--PciDrvReadRegistryValue %ws %d \n", Name, *Value); + + return retValue; +} + +BOOLEAN +PciDrvWriteRegistryValue( + _In_ PFDO_DATA FdoData, + _In_ PWSTR Name, + _In_ ULONG Value + ) +/*++ + +Routine Description: + + Can be used to write any REG_DWORD registry value stored + under Device Parameter. + +Arguments: + + +Return Value: + + TRUE - if write is successful + FALSE - otherwise + +--*/ +{ + WDFKEY hKey = NULL; + NTSTATUS status; + BOOLEAN retValue = FALSE; + UNICODE_STRING valueName; + + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "Entered PciDrvWriteRegistryValue\n"); + + // + // write the value out to the registry + // + status = WdfDeviceOpenRegistryKey(FdoData->WdfDevice, + PLUGPLAY_REGKEY_DEVICE, + STANDARD_RIGHTS_ALL, + WDF_NO_OBJECT_ATTRIBUTES, + &hKey); + + if (NT_SUCCESS (status)) { + + RtlInitUnicodeString(&valueName,Name); + + status = WdfRegistryAssignULong (hKey, + &valueName, + Value ); + + if (NT_SUCCESS (status)) { + retValue = TRUE; + } + + WdfRegistryClose(hKey); + } + + return retValue; + +} + +#define PARAMATER_NAME_LEN 80 + +BOOLEAN +PciDrvReadFdoRegistryKeyValue( + _In_ PWDFDEVICE_INIT DeviceInit, + _In_ PWSTR Name, + _Out_ PULONG Value + ) +/*++ + +Routine Description: + + Can be used to read any REG_DWORD registry value stored + under Device Parameter. + +Arguments: + + FdoData - pointer to the device extension + Name - Name of the registry value + Value - + + +Return Value: + + TRUE if successful + FALSE if not present/error in reading registry + +--*/ +{ + WDFKEY hKey = NULL; + NTSTATUS status; + BOOLEAN retValue = FALSE; + UNICODE_STRING valueName; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, + "-->PciDrvReadFdoRegistryKeyValue\n"); + + *Value = 0; + + status = WdfFdoInitOpenRegistryKey(DeviceInit, + PLUGPLAY_REGKEY_DEVICE, + STANDARD_RIGHTS_ALL, + WDF_NO_OBJECT_ATTRIBUTES, + &hKey); + + if (NT_SUCCESS (status)) { + + RtlInitUnicodeString(&valueName,Name); + + status = WdfRegistryQueryULong (hKey, + &valueName, + Value); + + if (NT_SUCCESS (status)) { + retValue = TRUE; + } + + WdfRegistryClose(hKey); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, + "<--PciDrvReadFdoRegistryKeyValue %ws %d \n", + Name, *Value); + + return retValue; +} + +VOID +PciDrvEvtDriverContextCleanup( + IN WDFOBJECT Driver + ) +/*++ +Routine Description: + + Free all the resources allocated in DriverEntry. + +Arguments: + + Driver - handle to a WDF Driver object. + +Return Value: + + VOID. + +--*/ +{ + PDRIVER_CONTEXT driverContext; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "--> PciDrvEvtDriverContextCleanup\n"); + PAGED_CODE (); + + driverContext = GetDriverContext((WDFDRIVER)Driver); + + if (driverContext->RecvLookaside) { + WdfObjectDelete(driverContext->RecvLookaside); + } + // + // Stop WPP Tracing + // + WPP_CLEANUP( WdfDriverWdmGetDriverObject( (WDFDRIVER)Driver ) ); + +} + +NTSTATUS +PciDrvSetPowerPolicy( + IN PFDO_DATA FdoData + ) +{ + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; + WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings; + NTSTATUS status = STATUS_SUCCESS; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "--> PciDrvSetPowerPolicy\n"); + + PAGED_CODE(); + + // + // Init the idle policy structure. + // + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleCanWakeFromS0); + idleSettings.IdleTimeout = 10000; // 10-sec + + status = WdfDeviceAssignS0IdleSettings(FdoData->WdfDevice, &idleSettings); + if ( !NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceSetPowerPolicyS0IdlePolicy failed %x\n", status); + return status; + } + + // + // Init wait-wake policy structure. + // + WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings); + + status = WdfDeviceAssignSxWakeSettings(FdoData->WdfDevice, &wakeSettings); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceAssignSxWakeSettings failed %x\n", status); + return status; + } + + + // + // Functions that program wakeup patterns on the device + // check this variable to see whether the NDIS edge has enabled + // wakeup on this device. If there is no ndis edge, this variable and all + // the checks can be removed because framework as a power policy owner + // it knows when to call the driver to arm/disarm for wakeup. + // + FdoData->AllowWakeArming = TRUE; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "<-- PciDrvSetPowerPolicy\n"); + + return status; +} + +PCHAR +DbgDevicePowerString( + IN WDF_POWER_DEVICE_STATE Type + ) +/*++ + +Updated Routine Description: + DbgDevicePowerString does not change in this stage of the function driver. + +--*/ +{ + switch (Type) + { + case WdfPowerDeviceInvalid: + return "WdfPowerDeviceInvalid"; + case WdfPowerDeviceD0: + return "WdfPowerDeviceD0"; + case WdfPowerDeviceD1: + return "WdfPowerDeviceD1"; + case WdfPowerDeviceD2: + return "WdfPowerDeviceD2"; + case WdfPowerDeviceD3: + return "WdfPowerDeviceD3"; + case WdfPowerDeviceD3Final: + return "WdfPowerDeviceD3Final"; + case WdfPowerDevicePrepareForHibernation: + return "WdfPowerDevicePrepareForHibernation"; + case WdfPowerDeviceMaximum: + return "WdfPowerDeviceMaximum"; + default: + return "UnKnown Device Power State"; + } +} + + +#if !defined(EVENT_TRACING) + +VOID +TraceEvents ( + IN ULONG TraceEventsLevel, + IN ULONG TraceEventsFlag, + IN PCCHAR DebugMessage, + ... + ) + +/*++ + +Routine Description: + + Debug print for the sample driver. + +Arguments: + + TraceEventsLevel - print level between 0 and 3, with 3 the most verbose + +Return Value: + + None. + + --*/ + { +#if DBG +#define TEMP_BUFFER_SIZE 512 + va_list list; + CHAR debugMessageBuffer[TEMP_BUFFER_SIZE]; + NTSTATUS status; + + va_start(list, DebugMessage); + + if (DebugMessage) { + + // + // Using new safe string functions instead of _vsnprintf. + // This function takes care of NULL terminating if the message + // is longer than the buffer. + // + status = RtlStringCbVPrintfA( debugMessageBuffer, + sizeof(debugMessageBuffer), + DebugMessage, + list ); + if(!NT_SUCCESS(status)) { + + DbgPrint (_DRIVER_NAME_": RtlStringCbVPrintfA failed %x\n", + status); + return; + } + if (TraceEventsLevel <= TRACE_LEVEL_INFORMATION || + (TraceEventsLevel <= DebugLevel && + ((TraceEventsFlag & DebugFlag) == TraceEventsFlag))) { + DbgPrint(debugMessageBuffer); + } + } + va_end(list); + + return; +#else + UNREFERENCED_PARAMETER(TraceEventsLevel); + UNREFERENCED_PARAMETER(TraceEventsFlag); + UNREFERENCED_PARAMETER(DebugMessage); +#endif +} + +#endif + diff --git a/general/pcidrv/kmdf/PCIDRV.H b/general/pcidrv/kmdf/PCIDRV.H new file mode 100644 index 00000000..db42bd39 --- /dev/null +++ b/general/pcidrv/kmdf/PCIDRV.H @@ -0,0 +1,401 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + PciDrv.h + +Abstract: + + Header file for the PCIDRV driver modules. + +Environment: + + Kernel mode + +--*/ + + +#if !defined(_PCIDRV_H_) +#define _PCIDRV_H_ + +// +// Let us use newly introduced (.NET DDK) safe string function to avoid +// security issues related buffer overrun. +// The advantages of the RtlStrsafe functions include: +// 1) The size of the destination buffer is always provided to the +// function to ensure that the function does not write past the end of +// the buffer. +// 2) Buffers are guaranteed to be null-terminated, even if the +// operation truncates the intended result. +// + +// +// In this driver we are using a safe version vsnprintf, which is +// RtlStringCbVPrintfA. To use strsafe function on 9x, ME, and Win2K Oses, we +// have to define NTSTRSAFE_LIB before including this header file and explicitly +// link to ntstrsafe.lib. If your driver is just target for XP and above, there is +// no define NTSTRSAFE_LIB and link to the lib. +// +#define NTSTRSAFE_LIB +#include + +//----------------------------------------------------------------------------- +// 4127 -- Conditional Expression is Constant warning +//----------------------------------------------------------------------------- +#define WHILE(constant) \ +__pragma(warning(suppress: 4127)) while(constant) + +#define _DRIVER_NAME_ "PCIDRV" + +#define PCIDRV_POOL_TAG (ULONG) 'DICP' +#define PCIDRV_FDO_INSTANCE_SIGNATURE (ULONG) 'odFT' + +#define MILLISECONDS_TO_100NS (10000) +#define SECOND_TO_MILLISEC (1000) +#define SECOND_TO_100NS (SECOND_TO_MILLISEC * MILLISECONDS_TO_100NS) + +// +// Bit Flag Macros +// + +#define SET_FLAG(Flags, Bit) ((Flags) |= (Bit)) +#define CLEAR_FLAG(Flags, Bit) ((Flags) &= ~(Bit)) +#define TEST_FLAG(Flags, Bit) (((Flags) & (Bit)) != 0) + +// +// The driver context contains global data to the whole driver. +// +typedef struct _DRIVER_CONTEXT { + // + // The assumption here is that there is nothing device specific in the lookaside list + // and hence the same list can be used to do allocations for multiple devices. + // + WDFLOOKASIDE RecvLookaside; + +} DRIVER_CONTEXT, * PDRIVER_CONTEXT; +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DRIVER_CONTEXT, GetDriverContext) + + + +// +// Connector Types +// + +typedef struct _PCIDRV_WMI_STD_DATA { + + // + // Current Mac Address of the NIC + // + + UINT64 MacAddress; + +} PCIDRV_WMI_STD_DATA, * PPCIDRV_WMI_STD_DATA; + + +// +// General purpose workitem context used in dispatching work to +// system worker thread to be executed at PASSIVE_LEVEL. +// +typedef struct _WORKER_ITEM_CONTEXT { + PFDO_DATA FdoData; + PVOID Argument1; + PVOID Argument2; +} WORKER_ITEM_CONTEXT, *PWORKER_ITEM_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(WORKER_ITEM_CONTEXT, GetWorkItemContext) + +// +// The device extension for the device object +// +typedef struct _FDO_DATA +{ + ULONG Signature; // must be PCIDRV_FDO_INSTANCE_SIGNATURE + // beneath this device object. + WDFDEVICE WdfDevice; + + // Power Management + MP_POWER_MGMT PoMgmt; + WDF_POWER_DEVICE_STATE DevicePowerState; // Current power state of the device(D0 - D3) + + // Wait-Wake + BOOLEAN AllowWakeArming; + + // Idle Detection + //BOOLEAN IdleDetectionEnabled; + PCIDRV_WMI_STD_DATA StdDeviceData; + + // Following fields are specific to the hardware + // Configuration + ULONG Flags; + UCHAR PermanentAddress[ETH_LENGTH_OF_ADDRESS]; + UCHAR CurrentAddress[ETH_LENGTH_OF_ADDRESS]; + BOOLEAN bOverrideAddress; + USHORT AiTxFifo; // TX FIFO Threshold + USHORT AiRxFifo; // RX FIFO Threshold + UCHAR AiTxDmaCount; // Tx dma count + UCHAR AiRxDmaCount; // Rx dma count + UCHAR AiUnderrunRetry; // The underrun retry mechanism + UCHAR AiForceDpx; // duplex setting + USHORT AiTempSpeed; // 'Speed', user over-ride of line speed + USHORT AiThreshold; // 'Threshold', Transmit Threshold + BOOLEAN MWIEnable; // Memory Write Invalidate bit in the PCI command word + UCHAR Congest; // Enables congestion control + ULONG SpeedDuplex; // New reg value for speed/duplex + + + // IDs + UCHAR RevsionID; + USHORT SubVendorID; + USHORT SubSystemID; + + // HW Resources + ULONG CacheFillSize; + PULONG IoBaseAddress; + ULONG IoRange; + PHYSICAL_ADDRESS MemPhysAddress; + + WDFINTERRUPT WdfInterrupt; + + BOOLEAN MappedPorts; + PHW_CSR CSRAddress; + BUS_INTERFACE_STANDARD BusInterface; + PREAD_PORT ReadPort; + PWRITE_PORT WritePort; + WDFDMAENABLER WdfDmaEnabler; + + // Media Link State + UCHAR CurrentScanPhyIndex; + UCHAR LinkDetectionWaitCount; + UCHAR FoundPhyAt; + USHORT EepromAddressSize; + MEDIA_STATE MediaState; + + // SEND + PMP_TCB CurrSendHead; + PMP_TCB CurrSendTail; + ULONG nBusySend; + LONG nWaitSend; + LONG nCancelSend; + WDFQUEUE WriteQueue; + WDFQUEUE PendingWriteQueue; + SINGLE_LIST_ENTRY SendBufList; + WDFSPINLOCK SendLock; + + ULONG NumTcb; // Total number of TCBs + LONG RegNumTcb; // 'NumTcb' + ULONG NumBuffers; + + + _Field_size_(MpTcbMemSize) PUCHAR MpTcbMem; + ULONG MpTcbMemSize; + + WDFCOMMONBUFFER WdfSendCommonBuffer; + + _Field_size_(HwSendMemAllocSize) PUCHAR HwSendMemAllocVa; + ULONG HwSendMemAllocSize; + PHYSICAL_ADDRESS HwSendMemAllocLa; // Logical Address + + // command unit status flags + BOOLEAN TransmitIdle; + BOOLEAN ResumeWait; + + // RECV + LIST_ENTRY RecvList; + ULONG nReadyRecv; + LONG RefCount; + + ULONG NumRfd; + ULONG CurrNumRfd; + ULONG MaxNumRfd; + ULONG HwRfdSize; + LONG RfdShrinkCount; + + WDFQUEUE PendingReadQueue; + WDFSPINLOCK RcvLock; + + BOOLEAN AllocNewRfd; + + // spin locks for protecting misc variables + WDFSPINLOCK Lock; + + // Packet Filter and look ahead size. + ULONG PacketFilter; + ULONG OldPacketFilter; + ULONG ulLookAhead; + USHORT usLinkSpeed; + USHORT usDuplexMode; + + // multicast list + UINT MCAddressCount; + UCHAR MCList[NIC_MAX_MCAST_LIST][ETH_LENGTH_OF_ADDRESS]; + + WDFCOMMONBUFFER WdfMiscCommonBuffer; + _Field_size_(HwMiscMemAllocSize) PUCHAR HwMiscMemAllocVa; + ULONG HwMiscMemAllocSize; + PHYSICAL_ADDRESS HwMiscMemAllocLa; // Logical Address + + PSELF_TEST_STRUC SelfTest; // 82558 SelfTest + ULONG SelfTestPhys; + BOOLEAN SelfTested; + + PNON_TRANSMIT_CB NonTxCmdBlock; // 82558 (non transmit) Command Block + ULONG NonTxCmdBlockPhys; + + PDUMP_AREA_STRUC DumpSpace; // 82558 dump buffer area + ULONG DumpSpacePhys; + + PERR_COUNT_STRUC StatsCounters; + ULONG StatsCounterPhys; + + UINT PhyAddress; // Address of the phy component + UCHAR Connector; // 0=Auto, 1=TPE, 2=MII + + UCHAR OldParameterField; + + ULONG HwErrCount; + + // WatchDog timer related fields + WDFTIMER WatchDogTimer; + BOOLEAN bLinkDetectionWait; + BOOLEAN bLookForLink; + BOOLEAN CheckForHang; + + // For handling IOCTLs - required if the upper edge is NDIS + WDFQUEUE IoctlQueue; + WDFQUEUE PendingIoctlQueue; + + // Packet counts + ULONG64 GoodTransmits; + ULONG64 GoodReceives; + ULONG NumTxSinceLastAdjust; + + // Count of transmit errors + ULONG TxAbortExcessCollisions; + ULONG TxLateCollisions; + ULONG TxDmaUnderrun; + ULONG TxLostCRS; + ULONG TxOKButDeferred; + ULONG OneRetry; + ULONG MoreThanOneRetry; + ULONG TotalRetries; + + // Count of receive errors + ULONG RcvCrcErrors; + ULONG RcvAlignmentErrors; + ULONG RcvResourceErrors; + ULONG RcvDmaOverrunErrors; + ULONG RcvCdtFrames; + ULONG RcvRuntErrors; + + // Count of bytes received & transmitted + ULONG64 BytesReceived; + ULONG64 BytesTransmitted; +} FDO_DATA, *PFDO_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DATA, FdoGetData) + +#define CLRMASK(x, mask) ((x) &= ~(mask)); +#define SETMASK(x, mask) ((x) |= (mask)); + + +// +// Function prototypes +// +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD PciDrvEvtDeviceAdd; + +EVT_WDF_OBJECT_CONTEXT_CLEANUP PciDrvEvtDriverContextCleanup; +EVT_WDF_DEVICE_CONTEXT_CLEANUP PciDrvEvtDeviceContextCleanup; + +EVT_WDF_DEVICE_D0_ENTRY PciDrvEvtDeviceD0Entry; +EVT_WDF_DEVICE_D0_EXIT PciDrvEvtDeviceD0Exit; +EVT_WDF_DEVICE_PREPARE_HARDWARE PciDrvEvtDevicePrepareHardware; +EVT_WDF_DEVICE_RELEASE_HARDWARE PciDrvEvtDeviceReleaseHardware; + +EVT_WDF_DEVICE_SELF_MANAGED_IO_CLEANUP PciDrvEvtDeviceSelfManagedIoCleanup; +EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT PciDrvEvtDeviceSelfManagedIoInit; +EVT_WDF_DEVICE_SELF_MANAGED_IO_SUSPEND PciDrvEvtDeviceSelfManagedIoSuspend; +EVT_WDF_DEVICE_SELF_MANAGED_IO_RESTART PciDrvEvtDeviceSelfManagedIoRestart; + +EVT_WDF_DEVICE_ARM_WAKE_FROM_S0 PciDrvEvtDeviceWakeArmS0; +EVT_WDF_DEVICE_ARM_WAKE_FROM_SX PciDrvEvtDeviceWakeArmSx; +EVT_WDF_DEVICE_DISARM_WAKE_FROM_S0 PciDrvEvtDeviceWakeDisarmS0; +EVT_WDF_DEVICE_DISARM_WAKE_FROM_SX PciDrvEvtDeviceWakeDisarmSx; +EVT_WDF_DEVICE_WAKE_FROM_S0_TRIGGERED PciDrvEvtDeviceWakeTriggeredS0; +EVT_WDF_DEVICE_WAKE_FROM_SX_TRIGGERED PciDrvEvtDeviceWakeTriggeredSx; + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL PciDrvEvtIoDeviceControl; + +NTSTATUS +PciDrvReturnResources ( + IN OUT PFDO_DATA FdoData + ); + +NTSTATUS +PciDrvSetPowerPolicy( + IN PFDO_DATA FdoData + ); + +NTSTATUS +PciDrvQueuePassiveLevelCallback( + IN PFDO_DATA FdoData, + IN PFN_WDF_WORKITEM CallbackFunction, + IN PVOID Context1, + IN PVOID Context2 + ); + +BOOLEAN +PciDrvReadRegistryValue( + _In_ PFDO_DATA FdoData, + _In_ PWSTR Name, + _Out_ PULONG Value + ); + +BOOLEAN +PciDrvWriteRegistryValue( + _In_ PFDO_DATA FdoData, + _In_ PWSTR Name, + _In_ ULONG Value + ); + + +NTSTATUS +PciDrvWmiRegistration( + WDFDEVICE hDevice +); + +PCHAR +DbgDevicePowerString( + IN WDF_POWER_DEVICE_STATE Type + ); + + +BOOLEAN +PciDrvReadFdoRegistryKeyValue( + _In_ PWDFDEVICE_INIT DeviceInit, + _In_ PWSTR Name, + _Out_ PULONG Value + ); + +#if defined(WIN2K) + +NTKERNELAPI +VOID +ExFreePoolWithTag( + _In_ PVOID P, + _In_ ULONG Tag + ); + +#endif + +#endif // _PCIDRV_H_ + + diff --git a/general/pcidrv/kmdf/PCIDRV.RC b/general/pcidrv/kmdf/PCIDRV.RC new file mode 100644 index 00000000..dd01a93c --- /dev/null +++ b/general/pcidrv/kmdf/PCIDRV.RC @@ -0,0 +1,33 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "WDM Driver for Intel 8255x Ethernet Adapters" +#define VER_INTERNALNAME_STR "PCIDRV.sys" +#define VER_ORIGINALFILENAME_STR "PCIDRV.sys" + +#define VER_FILEVERSION 1,00,00,0000 +#define VER_FILEVERSION_STR "1.00.00.0000" + +#undef VER_PRODUCTVERSION +#define VER_PRODUCTVERSION VER_FILEVERSION + +#undef VER_PRODUCTVERSION_STR +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) 2003 Microsoft Corporation" +#ifdef VER_COMPANYNAME_STR + +#undef VER_COMPANYNAME_STR +#define VER_COMPANYNAME_STR "Microsoft Corporation" +#endif + +#undef VER_PRODUCTNAME_STR +#define VER_PRODUCTNAME_STR "Microsoft Sample Driver for PCI Device" + +#include "common.ver" + + + diff --git a/general/pcidrv/kmdf/PCIDRV.mof b/general/pcidrv/kmdf/PCIDRV.mof new file mode 100644 index 00000000..283b6fb7 --- /dev/null +++ b/general/pcidrv/kmdf/PCIDRV.mof @@ -0,0 +1,19 @@ +#PRAGMA AUTORECOVER + +[Dynamic, Provider("WMIProv"), + WMI, + Description("PCIDRV Device Information"), + guid("{20E35E40-7179-4f89-A28C-12ED5A3CAAA5}"), + locale("MS\\0x409")] +class PciDeviceInformation +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, + write, + Description("Current Mac Address of the NIC.")] + uint64 MacAddress; +}; \ No newline at end of file diff --git a/general/pcidrv/kmdf/genpci.inx b/general/pcidrv/kmdf/genpci.inx new file mode 100644 index 00000000..b1f30a6c --- /dev/null +++ b/general/pcidrv/kmdf/genpci.inx @@ -0,0 +1,127 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +; KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR +; PURPOSE. +; +;Module Name: +; +; GenPCI.INF +; +;Abstract: +; INF file for a generic PCI device. +; +;--*/ + +[Version] +Signature = "$WINDOWS NT$" +Class = Sample +ClassGuid = {78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider = %MSFT% +DriverVer = 03/20/2003,5.00.3788 +CatalogFile = KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 + +;------------------------------------------------------------------------- +; Class Section +;------------------------------------------------------------------------- +[ClassInstall32] +Addreg = SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 +HKR,,DeviceCharacteristics,0x10001,0x100 ;Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)" ;Allow generic all access to system and built-in Admin. + +;------------------------------------------------------------------------- +; Device Install Section +;------------------------------------------------------------------------- +[ControlFlags] +ExcludeFromSelect = * + +[Manufacturer] +%MSFT%=MSFT,NT$ARCH$ + +[SourceDisksFiles] +pcidrv.sys = 1 + +[SourceDisksNames] +1=%DISK_NAME%, + +; For Win2K +[MSFT] +; DisplayName Section DeviceId +; ----------- ------- -------- +%GenPCI.DRVDESC%=GenPCI_Inst, PCI\VEN_8086&DEV_1229 +%GenPCI.DRVDESC%=GenPCI_Inst, PCI\VEN_8086&DEV_103D +%GenPCI.DRVDESC%=GenPCI_Inst, PCI\VEN_8086&DEV_1031 +%GenPCI.DRVDESC%=GenPCI_Inst, PCI\VEN_8086&DEV_1038 + +; For XP and later +[MSFT.NT$ARCH$] +; DisplayName Section DeviceId +; ----------- ------- -------- +%GenPCI.DRVDESC%=GenPCI_Inst, PCI\VEN_8086&DEV_1229 +%GenPCI.DRVDESC%=GenPCI_Inst, PCI\VEN_8086&DEV_103D +%GenPCI.DRVDESC%=GenPCI_Inst, PCI\VEN_8086&DEV_1031 +%GenPCI.DRVDESC%=GenPCI_Inst, PCI\VEN_8086&DEV_1038 + +[GenPCI_Inst.NT] +CopyFiles = GenPCI.CopyFiles + + +[GenPCI.CopyFiles] +pcidrv.sys + + +[GenPCI_Inst.NT.Services] +AddService = GenPCI,0x00000002,GenPCI_Service + +[GenPCI_Service] +DisplayName = %GenPCI.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\pcidrv.sys + +;------------------------------------------------------------------------- +; WDF Coinstaller installation +;------------------------------------------------------------------------- +[DestinationDirs] +CoInstaller_CopyFiles = 11 + +[GenPCI_Inst.NT.CoInstallers] +AddReg = CoInstaller_AddReg +CopyFiles = CoInstaller_CopyFiles + +[CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll = 1 ; make sure the number matches with SourceDisksNames + +[CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[GenPCI_Inst.NT.Wdf] +KmdfService = GenPCI, GenPCI_wdfsect + +[GenPCI_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +;------------------------------------------------------------------------------ +; String Definitions +;------------------------------------------------------------------------------ + +[Strings] +MSFT = "Microsoft" +ClassName = "Sample Device" +GenPCI.SVCDESC = "Sample WDF PCI Driver Service for Intel 8255x Ethernet Controller" +GenPCI.DRVDESC = "Sample WDF PCI Driver for Intel 8255x Ethernet Controller" +DISK_NAME = "GenPCI Sample Install Disk" diff --git a/general/pcidrv/kmdf/public.h b/general/pcidrv/kmdf/public.h new file mode 100644 index 00000000..70a2087f --- /dev/null +++ b/general/pcidrv/kmdf/public.h @@ -0,0 +1,48 @@ +/*++ + Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + public.h + +Abstract: + + This module contains the common declarations shared by driver + and user applications. + +Environment: + + user and kernel + +--*/ + +// +// Define an Interface Guid for toaster device class. +// This GUID is used to register (IoRegisterDeviceInterface) +// an instance of an interface so that user application +// can control the toaster device. +// + +DEFINE_GUID (GUID_DEVINTERFACE_PCIDRV, + 0xb74cfec2, 0x9366, 0x454a, 0xba, 0x71, 0x7c, 0x27, 0xb5, 0x14, 0x70, 0xa4); +// {B74CFEC2-9366-454a-BA71-7C27B51470A4} + +// +// Define a WMI GUID to get toaster device info. +// + +DEFINE_GUID (PCIDRV_WMI_STD_DATA_GUID, + 0x20e35e40, 0x7179, 0x4f89, 0xa2, 0x8c, 0x12, 0xed, 0x5a, 0x3c, 0xaa, 0xa5); + +// {20E35E40-7179-4f89-A28C-12ED5A3CAAA5} + +// +// GUID definition are required to be outside of header inclusion pragma to avoid +// error during precompiled headers. +// + diff --git a/general/pcidrv/kmdf/trace.h b/general/pcidrv/kmdf/trace.h new file mode 100644 index 00000000..2c041319 --- /dev/null +++ b/general/pcidrv/kmdf/trace.h @@ -0,0 +1,140 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + TRACE.h + +Abstract: + + Header file for the debug tracing related function defintions and macros. + HEXDUMP is not defined by default. So we have to provide a localwpp.ini file + that defines type. + +Environment: + + Kernel mode + +--*/ + +#include // For TRACE_LEVEL definitions + +#if !defined(EVENT_TRACING) + +// +// TODO: These defines are missing in evntrace.h +// in some DDK build environments (XP). +// +#if !defined(TRACE_LEVEL_NONE) + #define TRACE_LEVEL_NONE 0 + #define TRACE_LEVEL_CRITICAL 1 + #define TRACE_LEVEL_FATAL 1 + #define TRACE_LEVEL_ERROR 2 + #define TRACE_LEVEL_WARNING 3 + #define TRACE_LEVEL_INFORMATION 4 + #define TRACE_LEVEL_VERBOSE 5 + #define TRACE_LEVEL_RESERVED6 6 + #define TRACE_LEVEL_RESERVED7 7 + #define TRACE_LEVEL_RESERVED8 8 + #define TRACE_LEVEL_RESERVED9 9 +#endif + + +// +// Define Debug Flags +// +#define DBG_INIT 0x00000001 +#define DBG_PNP 0x00000002 +#define DBG_POWER 0x00000004 +#define DBG_WMI 0x00000008 +#define DBG_CREATE_CLOSE 0x00000010 +#define DBG_IOCTLS 0x00000020 +#define DBG_WRITE 0x00000040 +#define DBG_READ 0x00000080 +#define DBG_DPC 0x00000100 +#define DBG_INTERRUPT 0x00000200 +#define DBG_LOCKS 0x00000400 +#define DBG_QUEUEING 0x00000800 +#define DBG_HW_ACCESS 0x00001000 + +VOID +TraceEvents ( + IN ULONG DebugPrintLevel, + IN ULONG DebugPrintFlag, + IN PCCHAR DebugMessage, + ... + ); + +#define Hexdump(x) // Used for HEXDUMP in case tracing is enabled +#define WPP_INIT_TRACING(DriverObject, RegistryPath) +#define WPP_CLEANUP(DriverObject) + +#else +// +// If software tracing is defined in the sources file.. +// WPP_DEFINE_CONTROL_GUID specifies the GUID used for this driver. +// *** REPLACE THE GUID WITH YOUR OWN UNIQUE ID *** +// WPP_DEFINE_BIT allows setting debug bit masks to selectively print. +// The names defined in the WPP_DEFINE_BIT call define the actual names +// that are used to control the level of tracing for the control guid +// specified. +// +// Name of the logger is PciDrv and the guid is +// {BC6C9364-FC67-42c5-ACF7-ABED3B12ECC6} +// (0xbc6c9364, 0xfc67, 0x42c5, 0xac, 0xf7, 0xab, 0xed, 0x3b, 0x12, 0xec, 0xc6); +// + +#define WPP_CHECK_FOR_NULL_STRING //to prevent exceptions due to NULL strings + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(PciDrvTraceGuid,(bc6c9364,fc67,42c5,acf7,abed3b12ecc6), \ + WPP_DEFINE_BIT(DBG_INIT) /* bit 0 = 0x00000001 */ \ + WPP_DEFINE_BIT(DBG_PNP) /* bit 1 = 0x00000002 */ \ + WPP_DEFINE_BIT(DBG_POWER) /* bit 2 = 0x00000004 */ \ + WPP_DEFINE_BIT(DBG_WMI) /* bit 3 = 0x00000008 */ \ + WPP_DEFINE_BIT(DBG_CREATE_CLOSE) /* bit 4 = 0x00000010 */ \ + WPP_DEFINE_BIT(DBG_IOCTLS) /* bit 5 = 0x00000020 */ \ + WPP_DEFINE_BIT(DBG_WRITE) /* bit 6 = 0x00000040 */ \ + WPP_DEFINE_BIT(DBG_READ) /* bit 7 = 0x00000080 */ \ + WPP_DEFINE_BIT(DBG_DPC) /* bit 8 = 0x00000100 */ \ + WPP_DEFINE_BIT(DBG_INTERRUPT) /* bit 9 = 0x00000200 */ \ + WPP_DEFINE_BIT(DBG_LOCKS) /* bit 10 = 0x00000400 */ \ + WPP_DEFINE_BIT(DBG_QUEUEING) /* bit 11 = 0x00000800 */ \ + WPP_DEFINE_BIT(DBG_HW_ACCESS) /* bit 12 = 0x00001000 */ \ + /* You can have up to 32 defines. If you want more than that,\ + you have to provide another trace control GUID */\ + ) + + +#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) WPP_LEVEL_LOGGER(flags) +#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) + +#pragma warning(disable:4204) // C4204 nonstandard extension used : non-constant aggregate initializer + +// +// Define the 'xstr' structure for logging buffer and length pairs +// and the 'log_xstr' function which returns it to create one in-place. +// this enables logging of complex data types. +// +typedef struct xstr { char * _buf; short _len; } xstr_t; +__inline xstr_t log_xstr(void * p, short l) { xstr_t xs = {(char*)p,l}; return xs; } + +#pragma warning(default:4204) + +// +// Define the macro required for a hexdump use as: +// +// DebugTraceEx((LEVEL, FLAG,"%!HEXDUMP!\n", log_xstr(buffersize,(char *)buffer) )); +// +// +#define WPP_LOGHEXDUMP(x) WPP_LOGPAIR(2, &((x)._len)) WPP_LOGPAIR((x)._len, (x)._buf) + +#endif + + diff --git a/general/pcidrv/kmdf/wmi.c b/general/pcidrv/kmdf/wmi.c new file mode 100644 index 00000000..a0c5eba5 --- /dev/null +++ b/general/pcidrv/kmdf/wmi.c @@ -0,0 +1,144 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + WMI.C + +Abstract: + + This module handle all the WMI Irps. + +Environment: + + Kernel mode + +--*/ + + +#include "precomp.h" + +#if defined(EVENT_TRACING) +#include "wmi.tmh" +#endif + + +#define MOFRESOURCENAME L"PciDrvWMI" + +EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiDeviceInfoQueryInstance; + +EVT_WDF_WMI_INSTANCE_SET_INSTANCE EvtWmiDeviceInfoSetInstance; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, PciDrvWmiRegistration) +#pragma alloc_text(PAGE, EvtWmiDeviceInfoQueryInstance) +#pragma alloc_text(PAGE, EvtWmiDeviceInfoSetInstance) +#endif + +NTSTATUS +PciDrvWmiRegistration( + WDFDEVICE Device + ) +/*++ +Routine Description + + Registers with WMI as a data provider for this + instance of the device + +--*/ +{ + WDF_WMI_PROVIDER_CONFIG providerConfig; + WDF_WMI_INSTANCE_CONFIG instanceConfig; + NTSTATUS status; + DECLARE_CONST_UNICODE_STRING(mofRsrcName, MOFRESOURCENAME); + + PAGED_CODE(); + + status = WdfDeviceAssignMofResourceName(Device, &mofRsrcName); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceAssignMofResourceName failed 0x%x", status); + return status; + } + + WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig, &PCIDRV_WMI_STD_DATA_GUID); + providerConfig.MinInstanceBufferSize = sizeof(PCIDRV_WMI_STD_DATA); + + WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig, &providerConfig); + instanceConfig.Register = TRUE; + instanceConfig.EvtWmiInstanceQueryInstance = EvtWmiDeviceInfoQueryInstance; + instanceConfig.EvtWmiInstanceSetInstance = EvtWmiDeviceInfoSetInstance; + + // + // No need to get the newly creawted handle because we just reference data + // from our device extension directly. + // + status = WdfWmiInstanceCreate(Device, + &instanceConfig, + WDF_NO_OBJECT_ATTRIBUTES, + WDF_NO_HANDLE); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfWmiInstanceCreate failed 0x%x", status); + return status; + } + + return status; +} + +NTSTATUS +EvtWmiDeviceInfoQueryInstance( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG OutBufferSize, + _Out_writes_bytes_to_(OutBufferSize, *BufferUsed) PVOID OutBuffer, + _Out_ PULONG BufferUsed + ) +{ + PFDO_DATA fdoData; + + PAGED_CODE(); + + fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); + + *BufferUsed = sizeof(fdoData->CurrentAddress); + + if (OutBufferSize < sizeof(fdoData->CurrentAddress)) { + return STATUS_BUFFER_TOO_SMALL; + } + + RtlZeroMemory(OutBuffer, OutBufferSize); + RtlCopyMemory(OutBuffer, fdoData->CurrentAddress, sizeof(fdoData->CurrentAddress)); + + return STATUS_SUCCESS; +} + +NTSTATUS +EvtWmiDeviceInfoSetInstance( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG InBufferSize, + _In_reads_bytes_(InBufferSize) PVOID InBuffer + ) +{ + PFDO_DATA fdoData; + + UNREFERENCED_PARAMETER(InBufferSize); + + PAGED_CODE(); + + fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); + + if (InBufferSize < sizeof(fdoData->CurrentAddress)) { + return STATUS_WMI_SET_FAILURE; + } + + RtlCopyMemory(fdoData->CurrentAddress, InBuffer, sizeof(fdoData->CurrentAddress)); + + return STATUS_SUCCESS; +} + diff --git a/general/pcidrv/pcidrv.sln b/general/pcidrv/pcidrv.sln new file mode 100644 index 00000000..e6607b8a --- /dev/null +++ b/general/pcidrv/pcidrv.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{EFA400EA-F405-4FB2-AC59-22D52ED1C479}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HW", "HW", "{8CBB3334-12D6-42CE-BD7A-A6F00E08FE45}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Kmdf", "Kmdf", "{A8847B95-FC0F-4553-9D94-354FE3C0A56E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "myping", "test\myping.vcxproj", "{AAB2FA59-187D-45F7-A84F-09C7089848B3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PCIDRV", "kmdf\HW\PCIDRV.vcxproj", "{2221261F-E1E2-4562-A15D-2EF8BF7D878E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AAB2FA59-187D-45F7-A84F-09C7089848B3}.Debug|Win32.ActiveCfg = Debug|Win32 + {AAB2FA59-187D-45F7-A84F-09C7089848B3}.Debug|Win32.Build.0 = Debug|Win32 + {AAB2FA59-187D-45F7-A84F-09C7089848B3}.Release|Win32.ActiveCfg = Release|Win32 + {AAB2FA59-187D-45F7-A84F-09C7089848B3}.Release|Win32.Build.0 = Release|Win32 + {AAB2FA59-187D-45F7-A84F-09C7089848B3}.Debug|x64.ActiveCfg = Debug|x64 + {AAB2FA59-187D-45F7-A84F-09C7089848B3}.Debug|x64.Build.0 = Debug|x64 + {AAB2FA59-187D-45F7-A84F-09C7089848B3}.Release|x64.ActiveCfg = Release|x64 + {AAB2FA59-187D-45F7-A84F-09C7089848B3}.Release|x64.Build.0 = Release|x64 + {2221261F-E1E2-4562-A15D-2EF8BF7D878E}.Debug|Win32.ActiveCfg = Debug|Win32 + {2221261F-E1E2-4562-A15D-2EF8BF7D878E}.Debug|Win32.Build.0 = Debug|Win32 + {2221261F-E1E2-4562-A15D-2EF8BF7D878E}.Release|Win32.ActiveCfg = Release|Win32 + {2221261F-E1E2-4562-A15D-2EF8BF7D878E}.Release|Win32.Build.0 = Release|Win32 + {2221261F-E1E2-4562-A15D-2EF8BF7D878E}.Debug|x64.ActiveCfg = Debug|x64 + {2221261F-E1E2-4562-A15D-2EF8BF7D878E}.Debug|x64.Build.0 = Debug|x64 + {2221261F-E1E2-4562-A15D-2EF8BF7D878E}.Release|x64.ActiveCfg = Release|x64 + {2221261F-E1E2-4562-A15D-2EF8BF7D878E}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AAB2FA59-187D-45F7-A84F-09C7089848B3} = {EFA400EA-F405-4FB2-AC59-22D52ED1C479} + {2221261F-E1E2-4562-A15D-2EF8BF7D878E} = {8CBB3334-12D6-42CE-BD7A-A6F00E08FE45} + {8CBB3334-12D6-42CE-BD7A-A6F00E08FE45} = {A8847B95-FC0F-4553-9D94-354FE3C0A56E} + EndGlobalSection +EndGlobal diff --git a/general/pcidrv/test/myping.c b/general/pcidrv/test/myping.c new file mode 100644 index 00000000..b0c2d22c --- /dev/null +++ b/general/pcidrv/test/myping.c @@ -0,0 +1,1054 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + MYPING.C + +Abstract: + + + +Environment: + + usermode console application + +--*/ +#define _WINSOCK_DEPRECATED_NO_WARNINGS + +#include "testapp.h" +#include "nuiouser.h" +#include + +#define MAX_HEADER_SIZE (sizeof(ETH_HEADER) + sizeof(IpHeader) + sizeof(IcmpHeader)) +#define MAX_ECHO_PAY_LOAD (ETH_MAX_PACKET_SIZE - MAX_HEADER_SIZE) + + +#define ETH_HEADER_SIZE 14 +#define ETH_MAX_DATA_SIZE 1500 +#define ETH_MAX_PACKET_SIZE ETH_HEADER_SIZE + ETH_MAX_DATA_SIZE +#define ETH_MIN_PACKET_SIZE 60 + +#define ARP_ETYPE_ARP 0x806 +#define IP_PROT_TYPE 0x800 +#define ARP_REQUEST 1 +#define ARP_RESPONSE 2 +#define ARP_HW_ENET 1 + +#define PROTOCOL_ICMP 1 + +// ICMP types and codes +#define ICMPV4_ECHO_REQUEST_TYPE 8 +#define ICMPV4_ECHO_REQUEST_CODE 0 +#define ICMPV4_ECHO_REPLY_TYPE 0 +#define ICMPV4_ECHO_REPLY_CODE 0 +#define ICMPV4_MINIMUM_HEADER 8 + +#define DEFAULT_DATA_SIZE 32 // default data size + +#define DEFAULT_SEND_COUNT 32767 // number of ICMP requests to send + +#define DEFAULT_RECV_TIMEOUT 6000 // six second + +#define DEFAULT_TTL 128 + +#define IP_ADDR_LEN 4 + +#include + +// ======================================================================== +// The IP header +// + +typedef struct iphdr { + unsigned char verlen; + unsigned char tos; // Type of service + unsigned short total_len; // total length of the packet + unsigned short ident; // unique identifier + unsigned short frag_and_flags; // flags + unsigned char ttl; // time to live + unsigned char proto; // protocol (TCP, UDP etc) + unsigned char checksumHigh; + unsigned char checksumLow; + unsigned int sourceIP; // source ip address + unsigned int destIP; // destination ip address + +} IpHeader; + +// ======================================================================== +// ICMP header +// + +typedef struct _ihdr { + BYTE i_type; + BYTE i_code; /* type sub code */ + USHORT i_cksum; + USHORT i_id; + USHORT i_seq; + ULONG timestamp; /* non standard, reserve space for time */ +} IcmpHeader; + +typedef struct _ETH_HEADER +{ + UCHAR DstAddr[MAC_ADDR_LEN]; + UCHAR SrcAddr[MAC_ADDR_LEN]; + USHORT EthType; +} ETH_HEADER, *PETH_HEADER; + +// Structure of an ARP header. +typedef struct _ARP_BODY { + USHORT hw; // Hardware address space. = 00 01 + USHORT pro; // Protocol address space. = 08 00 + UCHAR hlen; // Hardware address length. = 06 + UCHAR plen; // Protocol address length. = 04 + USHORT opcode; // Opcode. + UCHAR SenderHwAddr[MAC_ADDR_LEN]; // Source HW address. + UINT SenderIpAddr; // Source protocol address. + UCHAR DestHwAddr[MAC_ADDR_LEN]; // Destination HW address. + UINT DestIPAddr; // Destination protocol address. +} _ARP_BODY, *PARP_BODY; + +#include + +// +// For Read operation. +// +typedef struct _RCB { + OVERLAPPED Overlapped; + char Buffer[ETH_MAX_PACKET_SIZE]; + PDEVICE_INFO DeviceInfo; +}RCB, *PRCB; + +// +// For Write operation. +// +typedef struct _TCB { + OVERLAPPED Overlapped; + char *Buffer; // packet length is user specified. + ULONG BufferLength; + PDEVICE_INFO DeviceInfo; + +}TCB, *PTCB; + +unsigned short PacketId; + +VOID +PostNextRead( + RCB *pRCB + ); + + +// +// Function: SetIcmpSequence +// +// Description: +// This routine sets the sequence number of the ICMP request packet. +// +VOID +SetIcmpSequence( + _At_((IcmpHeader*)buf, _Out_writes_bytes_all_(bufSize)) char *buf, + _In_ _In_range_(==, sizeof(IcmpHeader)) ULONG bufSize + ) +{ + ULONG sequence=0; + IcmpHeader *icmpv4=NULL; + + #pragma prefast(suppress:__WARNING_USE_OTHER_FUNCTION, "The recommended function GeTickCount64 is available only on Windows Vista/Server 2008 and above") + sequence = GetTickCount(); + + icmpv4 = (IcmpHeader *)buf; + + icmpv4->i_seq = (USHORT)sequence; +} + +// +// Using _Inexpressible_ to suppress prefast warning 2014 : "Potential overflow +// using expression at icmp_hdr->i_code" +// +VOID +InitIcmpHeader( + _Out_writes_bytes_(_Inexpressible_(bufSize + datasize)) char *buf, + ULONG bufSize, + int datasize) +{ + IcmpHeader *icmp_hdr=NULL; + char *datapart=NULL; + + icmp_hdr = (IcmpHeader *)buf; + icmp_hdr->i_type = ICMPV4_ECHO_REQUEST_TYPE; // request an ICMP echo + icmp_hdr->i_code = ICMPV4_ECHO_REQUEST_CODE; + icmp_hdr->i_id = htons((USHORT)PacketId++); + icmp_hdr->i_cksum = 0; + icmp_hdr->i_seq = 0; + #pragma prefast(suppress:__WARNING_USE_OTHER_FUNCTION, "The recommended function GeTickCount64 is available only on Windows Vista/Server 2008 and above") + icmp_hdr->timestamp= htonl(GetTickCount()); + + datapart = buf + sizeof(IcmpHeader); + // + // Place some junk in the buffer. + // + memset(datapart, 'X', datasize); +} + +VOID +ComputeIPChecksum ( + IpHeader *pIPHeader + ) +////////////////////////////////////////////////////////////////////////////// +{ + ULONG Checksum; + PUCHAR NextChar; + + pIPHeader->checksumHigh = pIPHeader->checksumLow = 0; + Checksum = 0; + for ( NextChar = (PUCHAR) pIPHeader + ; (NextChar - (PUCHAR) pIPHeader) <= (sizeof(IpHeader) - 2) + ; NextChar += 2) + { + Checksum += ((ULONG) (NextChar[0]) << 8) + (ULONG) (NextChar[1]); + } + + Checksum = (Checksum >> 16) + (Checksum & 0xffff); + Checksum += (Checksum >> 16); + Checksum = ~Checksum; + + pIPHeader->checksumHigh = (UCHAR) ((Checksum >> 8) & 0xff); + pIPHeader->checksumLow = (UCHAR) (Checksum & 0xff); +} + +// +// Function: checksum +// +// Description: +// This function calculates the 16-bit one's complement sum +// of the supplied buffer (ICMP) header. +// +USHORT +checksum( + USHORT *buffer, + int size + ) +{ + unsigned long cksum=0; + + while (size > 1) + { + cksum += *buffer++; + size -= sizeof(USHORT); + } + if (size) + { + cksum += *(UCHAR*)buffer; + } + cksum = (cksum >> 16) + (cksum & 0xffff); + cksum += (cksum >>16); + return (USHORT)(~cksum); +} + + +VOID +ComputeIcmpChecksum( + _At_((IcmpHeader*) buf, _Inout_updates_bytes_all_(bufSize)) char *buf, + _In_ _In_range_(==, sizeof(IcmpHeader)) ULONG bufSize, + int packetlen + ) +{ + IcmpHeader *icmpv4; + + icmpv4 = (IcmpHeader *)buf; + icmpv4->i_cksum = 0; + icmpv4->i_cksum = checksum((USHORT *)buf, packetlen); +} + +void +InitEtherHeader( + PDEVICE_INFO DeviceInfo, + PETH_HEADER pEthHeader + ) +{ + + pEthHeader->EthType = htons(IP_PROT_TYPE); + memcpy(pEthHeader->DstAddr, DeviceInfo->TargetMacAddr, MAC_ADDR_LEN); + memcpy(pEthHeader->SrcAddr, DeviceInfo->SrcMacAddr, MAC_ADDR_LEN); + + +} + +BOOL +InitIpHeader( + PDEVICE_INFO DeviceInfo, + IpHeader *pIpHeader, + unsigned short IpLength) +{ + pIpHeader->verlen = 0x45; + pIpHeader->tos = 0; //Normal service + pIpHeader->total_len = htons(IpLength); + pIpHeader->ident = htons(0xABBA); + pIpHeader->frag_and_flags = 0; + pIpHeader->ttl = DEFAULT_TTL; + pIpHeader->proto = PROTOCOL_ICMP; + #pragma prefast(suppress:__WARNING_IPV6_NAME_RESOLUTION_IPV4_SPECIFIC, "This test app doesn't support IPv6") + pIpHeader->sourceIP = inet_addr(DeviceInfo->SourceIp); + #pragma prefast(suppress:__WARNING_IPV6_NAME_RESOLUTION_IPV4_SPECIFIC, "This test app doesn't support IPv6") + pIpHeader->destIP = inet_addr(DeviceInfo->DestIp); + + return TRUE; +} +VOID WriteComplete(DWORD dwError, DWORD dwBytesTransferred, LPOVERLAPPED pOvl) +{ + TCB* pTCB = (TCB *)pOvl; + + if (dwError) { + if(dwError == ERROR_DEVICE_NOT_CONNECTED) { + Display(TEXT("WriteComplete: Device not connected")); + } + else { + Display(TEXT("WriteComplete: Error %x"), dwError); + + } + } + + DisplayV(TEXT("Write Complete: %x"), dwBytesTransferred); + HeapFree (GetProcessHeap(), 0, pTCB->Buffer); + HeapFree (GetProcessHeap(), 0, pTCB); +} + +VOID WriteCompleteArp(DWORD dwError, DWORD dwBytesTransferred, LPOVERLAPPED pOvl) +{ + if (dwError) { + if(dwError == ERROR_DEVICE_NOT_CONNECTED) { + Display(TEXT("WriteCompleteArp: Device not connected")); + } + else { + Display(TEXT("WriteCompleteArp: Error %x"), dwError); + + } + } + + DisplayV(TEXT("Write Complete ARP: %x"), dwBytesTransferred); +} + +VOID +ReadMacAddrComplete( + DWORD dwError, + DWORD dwBytesTransferred, + LPOVERLAPPED pOvl + ) +{ + if (ERROR_OPERATION_ABORTED != dwError) + { + RCB* pRCB = (RCB *)pOvl; + + char *Buffer = pRCB->Buffer; + PETH_HEADER ethHeader = (PETH_HEADER) Buffer; + PARP_BODY pBody; + + PDEVICE_INFO deviceInfo = pRCB->DeviceInfo; + WCHAR unicodeIpAddr[MAX_LEN]; + char *ipAddr; + + DisplayV(TEXT("ReadMacAddrComplete: %x"), dwBytesTransferred); + + if(ntohs(ethHeader->EthType) == ARP_ETYPE_ARP){ + + pBody = (PARP_BODY)(Buffer + sizeof(ETH_HEADER)); + + if(ntohs(pBody->opcode) == ARP_RESPONSE){ + #pragma prefast(suppress:__WARNING_IPV6_ADDRESS_STRUCTURE_IPV4_SPECIFIC, "This test app doesn't support IPv6") + struct in_addr IPAddr = {0}; + + memcpy(&IPAddr, &pBody->SenderIpAddr, IP_ADDR_LEN); + + if(memcmp(pBody->DestHwAddr, deviceInfo->SrcMacAddr, MAC_ADDR_LEN) == 0){ + + #pragma prefast(suppress:__WARNING_IPV6_NAME_RESOLUTION_IPV4_SPECIFIC, "This test app doesn't support IPv6") + ipAddr = inet_ntoa(IPAddr); + + if (!MultiByteToWideChar ( + CP_ACP, + 0, + ipAddr, + -1, // string is null terminated + unicodeIpAddr, + sizeof(unicodeIpAddr)/sizeof(WCHAR) + )) { + Display(TEXT("AnsitoUnicode conversion failed")); + + } + + DisplayV(TEXT("Target IP Address: %ws"), unicodeIpAddr); + + DisplayV(TEXT("Target Mac Address: %02X-%02X-%02X-%02X-%02X-%02X-"), + pBody->SenderHwAddr[0], + pBody->SenderHwAddr[1], + pBody->SenderHwAddr[2], + pBody->SenderHwAddr[3], + pBody->SenderHwAddr[4], + pBody->SenderHwAddr[5] + ); + memcpy(deviceInfo->TargetMacAddr, pBody->SenderHwAddr, MAC_ADDR_LEN); + + SetEvent(deviceInfo->PingEvent); + return; + } + } + } + + if(!ReadFileEx(pRCB->DeviceInfo->hDevice, pRCB->Buffer, sizeof(pRCB->Buffer), + &pRCB->Overlapped, (LPOVERLAPPED_COMPLETION_ROUTINE) ReadMacAddrComplete)){ + Display(TEXT("ReadMacAddrComplete: ReadFileEx failed %x"), GetLastError()); + } + + } +} + +BOOL +GetTargetMac( + PDEVICE_INFO DeviceInfo, + PRCB pRCB + ) +{ + char *Buffer = NULL; + PETH_HEADER ethHeader = NULL; + PARP_BODY pBody = NULL; + unsigned int retries =0; + DWORD status; + HANDLE hDevice = pRCB->DeviceInfo->hDevice; + PTCB pTCB = NULL; + + if(!ReadFileEx(hDevice, pRCB->Buffer, sizeof(pRCB->Buffer), + &pRCB->Overlapped, (LPOVERLAPPED_COMPLETION_ROUTINE) ReadMacAddrComplete)) + { + Display(TEXT("GetTargetMac: Error in ReadFile %x"), GetLastError()); + return FALSE; + } + + // + // We will try asking for mac address ten times. If we don't get valid response, + // we will bail out. + // + while(retries < 10){ + + // + // Allocate memory for the TCB and it's buffer. Writes are overlapped. + // We don't wait for the write requests to complete before posting + // another one. + // + pTCB = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TCB)); + if(!pTCB){ + Display(TEXT("Ping: HeapAlloc Failed")); + return FALSE; + } + + pTCB->Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ETH_MAX_PACKET_SIZE); + if(!pTCB->Buffer){ + Display(TEXT("Ping: HeapAlloc Failed")); + HeapFree (GetProcessHeap(), 0, pTCB); + return FALSE; + } + + pTCB->BufferLength = ETH_MAX_PACKET_SIZE; + memset(&pTCB->Overlapped, 0, sizeof(OVERLAPPED)); + pTCB->DeviceInfo = DeviceInfo; + + Buffer = pTCB->Buffer; + memset(Buffer, 0, ETH_MIN_PACKET_SIZE); + + ethHeader = (PETH_HEADER) Buffer; + + + memcpy(ethHeader->SrcAddr, DeviceInfo->SrcMacAddr, MAC_ADDR_LEN); + memset(ethHeader->DstAddr, 0xff, MAC_ADDR_LEN); + ethHeader->EthType = htons(ARP_ETYPE_ARP); //Network byte order + + pBody = (PARP_BODY)(Buffer + sizeof(ETH_HEADER)); + pBody->hw = htons(ARP_REQUEST); //Network byte order + pBody->pro = htons(IP_PROT_TYPE); //Network byte order + pBody->hlen = MAC_ADDR_LEN; + pBody->plen = IP_ADDR_LEN; + pBody->opcode = 0x0100; + + #pragma prefast(suppress:__WARNING_IPV6_NAME_RESOLUTION_IPV4_SPECIFIC, "This test app doesn't support IPv6") + pBody->SenderIpAddr = inet_addr(DeviceInfo->SourceIp); + #pragma prefast(suppress:__WARNING_IPV6_NAME_RESOLUTION_IPV4_SPECIFIC, "This test app doesn't support IPv6") + pBody->DestIPAddr = inet_addr(DeviceInfo->DestIp); + + memcpy(pBody->SenderHwAddr, DeviceInfo->SrcMacAddr, MAC_ADDR_LEN); + + if(!WriteFileEx(hDevice, pTCB->Buffer, ETH_MIN_PACKET_SIZE, &pTCB->Overlapped, (LPOVERLAPPED_COMPLETION_ROUTINE) WriteComplete)) + { + Display(TEXT("GetTargetMac WriteFile failed %x"), GetLastError()); + HeapFree (GetProcessHeap(), 0, pTCB->Buffer); + HeapFree (GetProcessHeap(), 0, pTCB); + return FALSE; + } + + retries++; + // + // Wait for the PingEvent to be signalled. This even is signalled when the + // ReadMacAddrComplete received a valid packet + // +wait: + status = WaitForSingleObjectEx(DeviceInfo->PingEvent, 1000, TRUE ); + + if ( status == WAIT_OBJECT_0 ) { // event fired, not timeout + // + // Got a valid response. Hurray! + // + return TRUE; + } + + if ( status == WAIT_IO_COMPLETION ) { + // Either Read/Write completed. Go back to waiting until the completion rotuine + // processes the response and sigals the event. + goto wait; + } + if (status != WAIT_TIMEOUT){ + // + // It has to be timeout at this point. Or else something fatal, break out. + Display(TEXT("WaitForSingleObjectEx returned error %d"), status); + return FALSE; + } + } + + return FALSE; +} + +BOOL +GetSrcMac( + HANDLE Handle, + PUCHAR pSrcMacAddr + ) +{ + DWORD BytesReturned; + BOOLEAN bSuccess; + UCHAR QueryBuffer[sizeof(NDISPROT_QUERY_OID) + MAC_ADDR_LEN]; + PNDISPROT_QUERY_OID pQueryOid; + + + DisplayV(TEXT("Trying to get src mac address"), NULL); + + pQueryOid = (PNDISPROT_QUERY_OID)&QueryBuffer[0]; + pQueryOid->Oid = OID_802_3_CURRENT_ADDRESS; + + bSuccess = (BOOLEAN)DeviceIoControl( + Handle, + IOCTL_NDISPROT_QUERY_OID_VALUE, + (LPVOID)&QueryBuffer[0], + sizeof(QueryBuffer), + (LPVOID)&QueryBuffer[0], + sizeof(QueryBuffer), + &BytesReturned, + NULL); + + if (bSuccess) + { + DisplayV(TEXT("GetSrcMac: IoControl success")); + memcpy(pSrcMacAddr, &pQueryOid->Data[0], MAC_ADDR_LEN); + + + } + else + { + Display(TEXT("GetSrcMac: IoControl failed %x\n"), GetLastError()); + } + + return (bSuccess); +} + +VOID +PrintIpHeader( + IpHeader *pIpHeader + ) +{ + #pragma prefast(suppress:__WARNING_IPV6_ADDRESS_STRUCTURE_IPV4_SPECIFIC, "This test app doesn't support IPv6") + struct in_addr IPAddr = {0}; + + DisplayV(TEXT("Ip Proto %x"), pIpHeader->proto); + DisplayV(TEXT("Ip VerLen %x"), pIpHeader->verlen); + DisplayV(TEXT("Ip Total Len %d"), pIpHeader->total_len); + DisplayV(TEXT("Ip Ident %d"), pIpHeader->ident); + DisplayV(TEXT("Ip tos %x"), pIpHeader->tos); + + memcpy(&IPAddr, &pIpHeader->sourceIP, IP_ADDR_LEN); + + // Display(TEXT("Source IP %s"), inet_ntoa(IPAddr)); + + memcpy(&IPAddr, &pIpHeader->destIP, IP_ADDR_LEN); + + // Display(TEXT("Dest IP %s"), inet_ntoa(IPAddr)); + +} + + +BOOL +SetPacketFilter( + HANDLE Handle, + ULONG PacketFilter + ) +{ + DWORD BytesReturned; + BOOLEAN bSuccess; + NDISPROT_SET_OID SetOid; + + + DisplayV(TEXT("Trying to SetPacketFilter"), NULL); + + SetOid.Oid = OID_GEN_CURRENT_PACKET_FILTER; + + memcpy(&SetOid.Data[0], &PacketFilter, sizeof(ULONG)); + + bSuccess = (BOOLEAN)DeviceIoControl( + Handle, + IOCTL_NDISPROT_SET_OID_VALUE, + (LPVOID)&SetOid, + sizeof(NDISPROT_SET_OID), + NULL, + 0, + &BytesReturned, + NULL); + + if (!bSuccess) + { + Display(TEXT("SetPacketFilter: IoControl failed: %d"), GetLastError()); + } + + return (bSuccess); +} + + +VOID +ProcessReadBuffer( + PRCB pRCB, + ULONG BufferLength + ) +{ + char *Buffer = pRCB->Buffer; + PETH_HEADER ethHeader = (PETH_HEADER) Buffer; + PARP_BODY pBody = (PARP_BODY)(Buffer + sizeof(ETH_HEADER)); + char ArpResponse[ETH_MIN_PACKET_SIZE] = {0}; + PETH_HEADER pResEthHeader = (PETH_HEADER)ArpResponse; + PARP_BODY pRespBody = (PARP_BODY)(ArpResponse + sizeof(ETH_HEADER)); + OVERLAPPED ov = {0}; + PDEVICE_INFO deviceInfo = pRCB->DeviceInfo; + HANDLE hDevice = deviceInfo->hDevice; + + // Check whether its a broadcast ARP request + if (ethHeader->DstAddr[0] == 0xff && + ethHeader->DstAddr[1] == 0xff ) + { + + if(ntohs(ethHeader->EthType) != ARP_ETYPE_ARP){ + deviceInfo->Sleep = FALSE; + DisplayV(TEXT("Non an arp eth request")); + goto End; + } + + if (IP_PROT_TYPE != ntohs(pBody->pro) || + MAC_ADDR_LEN != pBody->hlen || + IP_ADDR_LEN != pBody->plen ) + { + // + // these are just sanity checks + // + deviceInfo->Sleep = FALSE; + DisplayV(TEXT("Non an arp packet")); + goto End; + } + + DisplayV(TEXT("This is an arp request")); + + memcpy(pResEthHeader->DstAddr, ethHeader->SrcAddr, MAC_ADDR_LEN); + memcpy(pResEthHeader->SrcAddr, deviceInfo->SrcMacAddr, MAC_ADDR_LEN); + pResEthHeader->EthType = ethHeader->EthType; + + pRespBody->hw = pBody->hw; // Hardware address space. = 00 01 + + pRespBody->pro = pBody->pro; // Protocol address space. = 08 00 + + pRespBody->hlen = MAC_ADDR_LEN; // 6 + + pRespBody->plen = IP_ADDR_LEN; // 4 + + pRespBody->opcode = htons(ARP_RESPONSE); // Opcode. + + memcpy(pRespBody->SenderHwAddr, deviceInfo->SrcMacAddr, MAC_ADDR_LEN); // Destination HW address. + + pRespBody->SenderIpAddr = pBody->DestIPAddr ; // Source protocol address. + + memcpy(pRespBody->DestHwAddr, pBody->SenderHwAddr, MAC_ADDR_LEN); // Destination HW address. + + pRespBody->DestIPAddr = pBody->SenderIpAddr; + + PostNextRead(pRCB); + + DisplayV(TEXT("Writing Arp response\n")); + if(!WriteFileEx(hDevice, ArpResponse, sizeof(ArpResponse), &ov, (LPOVERLAPPED_COMPLETION_ROUTINE) WriteCompleteArp)) + { + Display(TEXT("Couldn't write arp response %d"), GetLastError()); + return ; + } + } + // Check whether its an unicast ARP request + else if (ethHeader->DstAddr[0] == deviceInfo->SrcMacAddr[0] && + ethHeader->DstAddr[1] == deviceInfo->SrcMacAddr[1] && + ethHeader->DstAddr[2] == deviceInfo->SrcMacAddr[2] && + ethHeader->DstAddr[3] == deviceInfo->SrcMacAddr[3] && + ethHeader->DstAddr[4] == deviceInfo->SrcMacAddr[4] && + ethHeader->DstAddr[5] == deviceInfo->SrcMacAddr[5] && + ntohs(ethHeader->EthType) == ARP_ETYPE_ARP && + IP_PROT_TYPE == ntohs(pBody->pro) && + MAC_ADDR_LEN == pBody->hlen && + IP_ADDR_LEN == pBody->plen) { + + // If it passed all these checks, its an ARP request + memcpy(pResEthHeader->DstAddr, ethHeader->SrcAddr, MAC_ADDR_LEN); + memcpy(pResEthHeader->SrcAddr, deviceInfo->SrcMacAddr, MAC_ADDR_LEN); + pResEthHeader->EthType = ethHeader->EthType; + + pRespBody->hw = pBody->hw; // Hardware address space. = 00 01 + + pRespBody->pro = pBody->pro; // Protocol address space. = 08 00 + + pRespBody->hlen = MAC_ADDR_LEN; // 6 + + pRespBody->plen = IP_ADDR_LEN; // 4 + + pRespBody->opcode = htons(ARP_RESPONSE); // Opcode. + + memcpy(pRespBody->SenderHwAddr, deviceInfo->SrcMacAddr, MAC_ADDR_LEN); // Destination HW address. + + pRespBody->SenderIpAddr = pBody->DestIPAddr ; // Source protocol address. + + memcpy(pRespBody->DestHwAddr, pBody->SenderHwAddr, MAC_ADDR_LEN); // Destination HW address. + + pRespBody->DestIPAddr = pBody->SenderIpAddr; + + PostNextRead(pRCB); + + DisplayV(TEXT("Writing Arp response\n")); + if(!WriteFileEx(hDevice, ArpResponse, sizeof(ArpResponse), &ov, (LPOVERLAPPED_COMPLETION_ROUTINE) WriteCompleteArp)) + { + Display(TEXT("Couldn't write arp response %d"), GetLastError()); + return ; + } + } + else { + + IpHeader *pIpHeader = (IpHeader *)(Buffer + sizeof(ETH_HEADER)); + IcmpHeader *pIcmpHeader = (IcmpHeader *)(Buffer + sizeof(ETH_HEADER)+sizeof(IpHeader));//TODO: Find the iP len from the packet + ULONG datalen = BufferLength - sizeof(ETH_HEADER) - sizeof(IpHeader) - sizeof(IcmpHeader); + #pragma prefast(suppress:__WARNING_IPV6_ADDRESS_STRUCTURE_IPV4_SPECIFIC, "This test app doesn't support IPv6") + struct in_addr IPAddr = {0}; + char *ipAddr; + WCHAR unicodeIpAddr[MAX_LEN]; + + PrintIpHeader(pIpHeader); + + if(pIpHeader->proto == PROTOCOL_ICMP && pIcmpHeader->i_type == ICMPV4_ECHO_REPLY_TYPE && + pIcmpHeader->i_type == ICMPV4_ECHO_REPLY_CODE){ + + memcpy(&IPAddr, &pIpHeader->sourceIP, IP_ADDR_LEN); + + #pragma prefast(suppress:__WARNING_IPV6_NAME_RESOLUTION_IPV4_SPECIFIC, "This test app doesn't support IPv6") + ipAddr = inet_ntoa(IPAddr); + + if (!MultiByteToWideChar ( + CP_ACP, + 0, + ipAddr, + -1, + unicodeIpAddr, + sizeof(unicodeIpAddr)/sizeof(unicodeIpAddr[0]) + )) { + Display(TEXT("AnsitoUnicode conversion failed")); + + } + + #pragma prefast(suppress:__WARNING_USE_OTHER_FUNCTION, "The recommended function GeTickCount64 is available only on Windows Vista/Server 2008 and above") + Display(TEXT("Reply %d from %ws: bytes=%d time<%dms TTL=%d"), + ntohs(pIcmpHeader->i_id), + unicodeIpAddr, + datalen, + (GetTickCount()-ntohl(pIcmpHeader->timestamp)), + pIpHeader->ttl); + + deviceInfo->NumberOfRequestSent++; + deviceInfo->Sleep = TRUE; + + } + + } + +End: + PostNextRead(pRCB); + SetEvent(deviceInfo->PingEvent); + +} +VOID +ReadComplete( + DWORD dwError, + DWORD dwBytesTransferred, + LPOVERLAPPED pOvl + ) +{ + if (ERROR_OPERATION_ABORTED != dwError) + { + RCB* pRCB = (RCB *)pOvl; + DisplayV(TEXT("ReadComplete: %d"), dwBytesTransferred); + ProcessReadBuffer(pRCB, dwBytesTransferred); + } +} + +VOID +PostNextRead( + RCB *pRCB + ) +{ + + if(!ReadFileEx(pRCB->DeviceInfo->hDevice, pRCB->Buffer, sizeof(pRCB->Buffer), + &pRCB->Overlapped, (LPOVERLAPPED_COMPLETION_ROUTINE) ReadComplete)) + { + Display(TEXT("Error in ReadFile: %x"), GetLastError()); + } + +} + + +BOOLEAN +Ping( + PDEVICE_INFO DeviceInfo + ) +{ + HANDLE hDevice = DeviceInfo->hDevice; + char *icmpbuf; + char *ipHeader; + char *etherHeader; + unsigned int icmpbuflen, packetlen; + PTCB pTCB = NULL; + + packetlen = sizeof(IcmpHeader) + sizeof(IpHeader) + sizeof(ETH_HEADER); + + // Add in the data size + if(FAILED(UIntAdd(packetlen, DeviceInfo->PacketSize, &packetlen))) { + Display(TEXT("Ping: UIntAdd Failed")); + goto Error; + } + + pTCB = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TCB)); + if(!pTCB){ + Display(TEXT("Ping: HeapAlloc Failed")); + goto Error; + } + + pTCB->Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, packetlen); + if(!pTCB->Buffer){ + Display(TEXT("Ping: HeapAlloc Failed")); + goto Error; + } + + pTCB->BufferLength = packetlen; + memset(&pTCB->Overlapped, 0, sizeof(OVERLAPPED)); + + + // Allocate the buffer that will contain the ICMP request + etherHeader = pTCB->Buffer; + + ipHeader = etherHeader + sizeof(ETH_HEADER); + + icmpbuf = ipHeader + sizeof(IpHeader); + + + icmpbuflen = sizeof(IcmpHeader) + DeviceInfo->PacketSize; + + InitEtherHeader(DeviceInfo, (PETH_HEADER)etherHeader); + + + InitIcmpHeader(icmpbuf, icmpbuflen, DeviceInfo->PacketSize); + + // Set the sequence number and compute the checksum + SetIcmpSequence(icmpbuf, sizeof(IcmpHeader)); + + DisplayV(TEXT("Icmp header %d packetlen %d"), sizeof(IcmpHeader), icmpbuflen); + + ComputeIcmpChecksum(icmpbuf, sizeof(IcmpHeader), icmpbuflen); + + if(!InitIpHeader(DeviceInfo, (IpHeader *)ipHeader, (USHORT)icmpbuflen+sizeof(IpHeader))){ + goto Error; + } + + ComputeIPChecksum((IpHeader *)ipHeader); + + PrintIpHeader((IpHeader *)ipHeader); + + if(!WriteFileEx(hDevice, etherHeader, packetlen, &pTCB->Overlapped, (LPOVERLAPPED_COMPLETION_ROUTINE) WriteComplete)) + { + Display(TEXT("Ping: WriteFile failed %x"), GetLastError()); + goto Error; + } + + return TRUE; + +Error: + + if(pTCB) { + if(pTCB->Buffer){ + HeapFree (GetProcessHeap(), 0, pTCB->Buffer); + } + HeapFree (GetProcessHeap(), 0, pTCB); + } + return FALSE; +} + +DWORD +PingThread ( + PDEVICE_INFO DeviceInfo + ) +{ + RCB RCB; + HANDLE hDevice = DeviceInfo->hDevice; + DWORD status; + + + Display(TEXT("Pinging %ws from %ws with %d bytes of data"), + DeviceInfo->UnicodeDestIp, + DeviceInfo->UnicodeSourceIp, + DeviceInfo->PacketSize); + Sleep(1000); + + // + // Every time a ping response is recevied, PingEvent will + // be signalled. + // + DeviceInfo->PingEvent = CreateEvent(NULL, FALSE, FALSE, L"PingEvent"); + if (DeviceInfo->PingEvent == NULL) { + Display(TEXT("CreateEvent failed 0x%x"), GetLastError()); + goto Exit; + } + + DeviceInfo->NumberOfRequestSent = 0; + DeviceInfo->Sleep = FALSE; + DeviceInfo->TimeOut = 0; + PacketId = 1; + + // + // Get the MAC address of the local NIC + // + if (!GetSrcMac(hDevice, DeviceInfo->SrcMacAddr)) + { + Display(TEXT("Failed to obtain local MAC address")); + goto Exit; + } + + // + // Set the hardware filter to receive directed on broadcast packets. + // + if (!SetPacketFilter(hDevice, NDIS_PACKET_TYPE_DIRECTED|NDIS_PACKET_TYPE_BROADCAST)) + { + Display(TEXT("Failed to set packet filter")); + goto Exit; + } + + // + // Initialize read control block. Reads requests are serialized. + // Only one read is outstanding at any time. We allocate memory + // for the RCB in the stack. + // + RCB.DeviceInfo = DeviceInfo; + memset(&RCB.Overlapped, 0, sizeof(OVERLAPPED)); + + + // + // Get MAC address of the target machine by sending an ARP + // request. We have the target machine's IP address from the user. + // + if(!GetTargetMac(DeviceInfo, &RCB)){ + Display(TEXT("Couldn't find the host")); + goto Exit; + } + + // + // Set the hardware filter to receive directed packets only + // + if (!SetPacketFilter(hDevice, NDIS_PACKET_TYPE_DIRECTED)) + { + Display(TEXT("Failed to set packet filter")); + goto Exit; + } + + // + // Post a read buffer and start sending ping packets. + // + PostNextRead(&RCB); + + Ping(DeviceInfo); + + // + // We will exit out of this thread if the number of ping count + // exceeds the DEFAULT_SEND_COUNT or the main thread requested + // us to exit by setting ExitThread value to TRUE. + // + while(DeviceInfo->NumberOfRequestSent < DEFAULT_SEND_COUNT + && DeviceInfo->ExitThread == FALSE){ + + status = WaitForSingleObjectEx(DeviceInfo->PingEvent, 1000, TRUE ); + if ( status == WAIT_OBJECT_0 ) { // event fired, not timeout + // + // Probably we received a valid ping response from the target. + // + if(DeviceInfo->Sleep){ + Sleep(PING_SLEEP_TIME); // sleep for a sec before sending another ECHO + } + Ping(DeviceInfo); + continue; + } + // + // This is just a notification that either Read/Write operation got + // completed. We will know the result of the acutal operation later + // when the APC is called. + // + if( status == WAIT_IO_COMPLETION ) { + continue; + } + if (status != WAIT_TIMEOUT){ + + Display(TEXT("WaitForSingleObjectEx returned error %d"), status); + break; + } + // + // It seems like the wait timed out. So let us send another ping + // and see if we get any response. + // + DeviceInfo->TimeOut++; + if(DeviceInfo->TimeOut > MAX_PING_RETRY) { + Display(TEXT("No response from the target")); + break; + } + + Ping(DeviceInfo); + } + +Exit: + + CloseHandle(DeviceInfo->hDevice); + DeviceInfo->hDevice = INVALID_HANDLE_VALUE; + DeviceInfo->ThreadHandle = NULL; + if (DeviceInfo->PingEvent) { + CloseHandle(DeviceInfo->PingEvent); + DeviceInfo->PingEvent = NULL; + } + + Display(TEXT("PingThread is exiting")); + return 0; +} + + + + + diff --git a/general/pcidrv/test/myping.vcxproj b/general/pcidrv/test/myping.vcxproj new file mode 100644 index 00000000..182ebe8c --- /dev/null +++ b/general/pcidrv/test/myping.vcxproj @@ -0,0 +1,201 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {AAB2FA59-187D-45F7-A84F-09C7089848B3} + $(MSBuildProjectName) + Debug + Win32 + {E28A6724-75F7-4959-8204-0A5787BBF7B9} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + myping + + + myping + + + myping + + + myping + + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + true + Level4 + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + + + %(AdditionalDependencies);setupapi.lib;wsock32.lib;ws2_32.lib;ole32.lib + 0x01000000 + + + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + true + Level4 + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + + + %(AdditionalDependencies);setupapi.lib;wsock32.lib;ws2_32.lib;ole32.lib + 0x01000000 + + + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + true + Level4 + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + + + %(AdditionalDependencies);setupapi.lib;wsock32.lib;ws2_32.lib;ole32.lib + 0x01000000 + + + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + true + Level4 + + + %(AdditionalIncludeDirectories);..\kmdf;..\kmdf\hw + + + %(AdditionalDependencies);setupapi.lib;wsock32.lib;ws2_32.lib;ole32.lib + 0x01000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/pcidrv/test/myping.vcxproj.Filters b/general/pcidrv/test/myping.vcxproj.Filters new file mode 100644 index 00000000..32295c73 --- /dev/null +++ b/general/pcidrv/test/myping.vcxproj.Filters @@ -0,0 +1,30 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {B8B1F2DC-5ED4-4A1F-BFEE-587F0F5312D5} + + + h;hpp;hxx;hm;inl;inc;xsd + {8259798A-8FE0-4AE5-B848-77626974747D} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {48C95CA9-FD65-42C9-8FAF-C20DEB7390E8} + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/pcidrv/test/resource.h b/general/pcidrv/test/resource.h new file mode 100644 index 00000000..96cfe365 --- /dev/null +++ b/general/pcidrv/test/resource.h @@ -0,0 +1,17 @@ +#define ID_EDIT 1 + +#define IDM_PING 100 +#define IDM_CLOSE 101 +#define IDM_EXIT 102 +#define IDM_CLEAR 103 +#define IDM_ENUMERATE 104 +#define IDM_VERBOSE 105 + +#define IDD_DIALOG 115 +#define ID_OK 118 +#define ID_CANCEL 119 +#define IDC_DEVICE_INDEX 1000 +#define IDC_SOURCE_IP 1001 +#define IDC_DESTINATION_IP 1002 +#define IDC_PACKET_SIZE 1003 +#define IDC_STATIC -1 diff --git a/general/pcidrv/test/testapp.c b/general/pcidrv/test/testapp.c new file mode 100644 index 00000000..cd83f633 --- /dev/null +++ b/general/pcidrv/test/testapp.c @@ -0,0 +1,1224 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: Testapp.c + + +Abstract: + + Testapp for PCIDRV + +Environment: + + User mode only. + +--*/ + +#include "testapp.h" + + +// +// Global variables +// +HINSTANCE HWndInstance; +HWND HWndList; // handle to the embedded list box +TCHAR WindowTitle[]=TEXT("MyPing - Test Application for PCIDRV"); +LIST_ENTRY ListHead; +HDEVNOTIFY InterfaceNotificationHandle; +TCHAR OutText[500]; +UINT ListBoxIndex = 0; +GUID InterfaceGuid;// = GUID_DEVINTERFACE_PCIDRV; +ULONG DeviceIndex; +BOOLEAN Verbose = FALSE; + + +VOID +Display( + _In_ LPWSTR pstrFormat, // @parm A printf style format string + ... // @parm | ... | Variable paramters based on

+ ) +{ + HRESULT hr; + va_list va; + + va_start(va, pstrFormat); + // + // Truncation is acceptable. + // + hr = StringCbVPrintf(OutText, sizeof(OutText)-sizeof(WCHAR), pstrFormat, va); + va_end(va); + + if(FAILED(hr)){ + return; + } + + SendMessage(HWndList, LB_INSERTSTRING, ListBoxIndex, (LPARAM)OutText); + SendMessage(HWndList, LB_SETCURSEL, ListBoxIndex, 0); + ListBoxIndex++; + +} + +_Use_decl_annotations_ +int +PASCAL +WinMain ( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nShowCmd + ) +{ + static TCHAR szAppName[]=TEXT("MYPING"); + HWND hWnd; + MSG msg; + WNDCLASS wndclass; + + InterfaceGuid = GUID_DEVINTERFACE_PCIDRV; + HWndInstance=hInstance; + + if (!hPrevInstance) + { + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION); + wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclass.hbrBackground= GetStockObject(WHITE_BRUSH); + wndclass.lpszMenuName = TEXT("GenericMenu"); + wndclass.lpszClassName= szAppName; + + RegisterClass(&wndclass); + } + + hWnd = CreateWindow (szAppName, + WindowTitle, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + hInstance, + NULL); + + ShowWindow (hWnd, nShowCmd); + UpdateWindow(hWnd); + + while (GetMessage (&msg, NULL, 0,0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return (0); +} + + +LRESULT FAR PASCAL +WndProc (HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + DWORD nEventType = (DWORD)wParam; + PDEV_BROADCAST_HDR p = (PDEV_BROADCAST_HDR) lParam; + DEV_BROADCAST_DEVICEINTERFACE filter; + WSADATA wsd; + + switch (message) + { + + case WM_COMMAND: + HandleCommands(hWnd, message, wParam, lParam); + return 0; + + case WM_CREATE: + + // Load Winsock + if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) + { + MessageBox(hWnd, TEXT("WSAStartup failed"), TEXT("Error"), MB_OK); + exit(0); + } + + HWndList = CreateWindow (TEXT("listbox"), + NULL, + WS_CHILD|WS_VISIBLE|LBS_NOTIFY | + WS_VSCROLL | WS_BORDER, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + hWnd, + (HMENU)ID_EDIT, + HWndInstance, + NULL); + + filter.dbcc_size = sizeof(filter); + filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + filter.dbcc_classguid = InterfaceGuid; + InterfaceNotificationHandle = RegisterDeviceNotification(hWnd, &filter, 0); + + InitializeListHead(&ListHead); + EnumExistingDevices(hWnd); + + return 0; + + case WM_SIZE: + + MoveWindow(HWndList, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); + return 0; + + case WM_SETFOCUS: + SetFocus(HWndList); + return 0; + + case WM_DEVICECHANGE: + + // + // The DBT_DEVNODES_CHANGED broadcast message is sent + // everytime a device is added or removed. This message + // is typically handled by Device Manager kind of apps, + // which uses it to refresh window whenever something changes. + // The lParam is always NULL in this case. + // + if(DBT_DEVNODES_CHANGED == wParam) { + DisplayV(TEXT("Received DBT_DEVNODES_CHANGED broadcast message")); + return 0; + } + + // + // All the events we're interested in come with lParam pointing to + // a structure headed by a DEV_BROADCAST_HDR. This is denoted by + // bit 15 of wParam being set, and bit 14 being clear. + // + if((wParam & 0xC000) == 0x8000) { + + if (!p) + return 0; + + if (p->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + + HandleDeviceInterfaceChange(hWnd, nEventType, (PDEV_BROADCAST_DEVICEINTERFACE) p); + } else if (p->dbch_devicetype == DBT_DEVTYP_HANDLE) { + + HandleDeviceChange(hWnd, nEventType, (PDEV_BROADCAST_HANDLE) p); + } + } + return 0; + + case WM_POWERBROADCAST: + HandlePowerBroadcast(hWnd, wParam, lParam); + return 0; + + case WM_CLOSE: + Cleanup(hWnd); + UnregisterDeviceNotification(InterfaceNotificationHandle); + return DefWindowProc(hWnd,message, wParam, lParam); + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProc(hWnd,message, wParam, lParam); + } + + +LRESULT +HandleCommands( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) + +{ + PDIALOG_RESULT result = NULL; + PDEVICE_INFO deviceInfo = NULL; + + switch (wParam) { + + case IDM_CLOSE: + Cleanup(hWnd); + Display(TEXT("Handle to the device closed")); + EnableMenuItem(GetMenu(hWnd), IDM_PING, MF_BYCOMMAND|MF_GRAYED); + EnableMenuItem(GetMenu(hWnd), IDM_CLOSE, MF_BYCOMMAND|MF_GRAYED); + break; + + case IDM_ENUMERATE: + // + // First cleanup everything, and then reenumerate all the devices. + Cleanup(hWnd); + EnumExistingDevices(hWnd); + EnableMenuItem(GetMenu(hWnd), IDM_PING, MF_BYCOMMAND|MF_ENABLED); + break; + + case IDM_PING: + + result = (PDIALOG_RESULT)DialogBox(HWndInstance, MAKEINTRESOURCE(IDD_DIALOG), hWnd, DlgProc); + if(result) { + deviceInfo = FindDeviceInfo(result); + if(!deviceInfo){ + MessageBox(hWnd, TEXT("FindDeviceInfo failed"), TEXT("Error"), MB_OK); + break; + } + + if(!OpenDevice(hWnd, deviceInfo)){ + MessageBox(hWnd, TEXT("OpenDevice failed"), TEXT("Error"), MB_OK); + break; + } + + if (!CreatePingThread(deviceInfo)) { + MessageBox(hWnd, TEXT("CreatePingThread failed"), TEXT("Error"), MB_OK); + break; + } + + EnableMenuItem(GetMenu(hWnd), IDM_PING, MF_BYCOMMAND|MF_GRAYED); + EnableMenuItem(GetMenu(hWnd), IDM_CLOSE, MF_BYCOMMAND|MF_ENABLED); + + } + break; + + case IDM_CLEAR: + SendMessage(HWndList, LB_RESETCONTENT, 0, 0); + ListBoxIndex = 0; + break; + + case IDM_VERBOSE: { + + HMENU hMenu = GetMenu(hWnd); + Verbose = !Verbose; + if(Verbose) { + CheckMenuItem(hMenu, (UINT)wParam, MF_CHECKED); + } else { + CheckMenuItem(hMenu, (UINT)wParam, MF_UNCHECKED); + } + } + break; + + case IDM_EXIT: + Cleanup(hWnd); + PostQuitMessage(0); + break; + + default: + break; + } + + if(result) { + HeapFree (GetProcessHeap(), 0, result); + } + return TRUE; +} + +INT_PTR CALLBACK +DlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam +) +{ + BOOL success; + PDIALOG_RESULT dialogResult = NULL; + ULONG value; + WCHAR SourceIP[80]; + WCHAR DestinationIP[80]; + DWORD SourceIPLen = sizeof(SourceIP); + + switch(message) + { + case WM_INITDIALOG: + // + // Set default values. + // + if(GetRegistryInfo(SourceIP, &SourceIPLen, DestinationIP, &SourceIPLen)) { + SetDlgItemText(hDlg, IDC_SOURCE_IP, SourceIP); + SetDlgItemText(hDlg, IDC_DESTINATION_IP, DestinationIP); + } else { + SetDlgItemText(hDlg, IDC_SOURCE_IP, DEF_SOURCE_IP); + SetDlgItemText(hDlg, IDC_DESTINATION_IP, DEF_DEST_IP); + + } + + SetDlgItemInt(hDlg, IDC_PACKET_SIZE, MAX_PAYLOAD_SIZE, FALSE); + return TRUE; + + case WM_COMMAND: + switch( wParam) + { + case ID_OK: + // + // Allocate memory to store the input values. + // + dialogResult = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(DIALOG_RESULT)); + if(dialogResult) { + + dialogResult->DeviceIndex = GetDlgItemInt(hDlg, + IDC_DEVICE_INDEX, &success, FALSE ); + if(!success){ + break; + } + value = GetDlgItemText(hDlg, IDC_SOURCE_IP, + dialogResult->SourceIp, MAX_LEN-1 ); + if(!value){ + break; + } + + + GetDlgItemText(hDlg, IDC_DESTINATION_IP, + dialogResult->DestIp, MAX_LEN-1 ); + if(!value){ + break; + } + + value = GetDlgItemInt(hDlg,IDC_PACKET_SIZE, &success, FALSE ); + if(success){ + value = min(value, MAX_PAYLOAD_SIZE); + value = max(value, MIN_PAYLOAD_SIZE); + + } else { + value = MIN_PAYLOAD_SIZE; + } + + dialogResult->PacketSize = value; + + SetRegistryInfo(dialogResult->SourceIp, + sizeof(dialogResult->SourceIp), + dialogResult->DestIp, + sizeof(dialogResult->DestIp)); + + } + EndDialog(hDlg, (UINT_PTR)dialogResult); + return TRUE; + case ID_CANCEL: + EndDialog(hDlg, 0); + return TRUE; + + } + break; + + } + return FALSE; +} + + +BOOL +HandleDeviceInterfaceChange( + HWND hWnd, + DWORD evtype, + PDEV_BROADCAST_DEVICEINTERFACE dip + ) +{ + switch (evtype) + { + case DBT_DEVICEARRIVAL: + // + // New device arrived. Create a devicinfo structure and record + // information about the device. + // + Display(TEXT("New device Arrived (Interface Change Notification)")); + + if(!CreateDeviceInfo(dip->dbcc_name)) { + return FALSE; + } + + break; + + case DBT_DEVICEREMOVECOMPLETE: + // + // Device Removed. + // + Display(TEXT("Remove Complete (Interface Change Notification)"), NULL); + break; + + default: + DisplayV(TEXT("Unknown (Interface Change Notification)"), NULL); + break; + } + return TRUE; +} + +BOOL +HandleDeviceChange( + HWND hWnd, + DWORD evtype, + PDEV_BROADCAST_HANDLE dhp + ) +{ + PDEVICE_INFO deviceInfo = NULL; + PLIST_ENTRY thisEntry; + + // + // Walk the list to get the deviceInfo for this device + // by matching the notification handle saved in our deviceInfo + // and the one provided as part of the message. + // + for(thisEntry = ListHead.Flink; thisEntry != &ListHead; + thisEntry = thisEntry->Flink) + { + deviceInfo = CONTAINING_RECORD(thisEntry, DEVICE_INFO, ListEntry); + if(dhp->dbch_hdevnotify == deviceInfo->hHandleNotification) { + break; + } + deviceInfo = NULL; + } + + if(!deviceInfo) { + Display(TEXT("Error: spurious message Event Type %x, Device Type %x"), + evtype, dhp->dbch_devicetype); + return FALSE; + } + + switch (evtype) + { + + case DBT_DEVICEQUERYREMOVE: + + Display(TEXT("Query Remove (Handle Notification): %ws"), + deviceInfo->DeviceName); + // User is trying to disable, uninstall, or eject our device. + // Terminate the ping thread and close the handle + // to the device so that the target device can + // get removed. Do not unregister the notification + // at this point, because we want to know whether + // the device is successfully removed or not. + // + TerminatePingThread(deviceInfo); + break; + + case DBT_DEVICEREMOVECOMPLETE: + + Display(TEXT("Remove Complete (Handle Notification):%ws"), + deviceInfo->DeviceName); + // + // Device is getting surprise removed. So terminate the + // ping thread to close the handle to device and + // unregister the PNP notification. + // + TerminatePingThread(deviceInfo); + + if (deviceInfo->hHandleNotification) { + UnregisterDeviceNotification(deviceInfo->hHandleNotification); + deviceInfo->hHandleNotification = NULL; + } + + // + // Unlink this deviceInfo from the list and free the memory + // + RemoveEntryList(&deviceInfo->ListEntry); + HeapFree (GetProcessHeap(), 0, deviceInfo); + + break; + + case DBT_DEVICEREMOVEPENDING: + + Display(TEXT("Remove Pending (Handle Notification):%ws"), + deviceInfo->DeviceName); + // + // Device is successfully removed so unregister the notification + // and free the memory. + // + FreeDeviceInfo(deviceInfo); + + break; + + case DBT_DEVICEQUERYREMOVEFAILED : + Display(TEXT("Remove failed (Handle Notification):%ws"), + deviceInfo->DeviceName); + // + // Remove failed. So reopen the device and register for + // notification on the new handle. But first we should unregister + // the previous notification. + // + if (deviceInfo->hHandleNotification) { + UnregisterDeviceNotification(deviceInfo->hHandleNotification); + deviceInfo->hHandleNotification = NULL; + } + + if(!OpenDevice(hWnd, deviceInfo)) { + Display(TEXT("Failed to reopen the device: %ws"), + deviceInfo->DeviceName); + FreeDeviceInfo(deviceInfo); + break; + } + + Display(TEXT("Reopened device %ws"), deviceInfo->DeviceName); + // + // Restart the ping operation. + // + if(CreatePingThread(deviceInfo)){ + FreeDeviceInfo(deviceInfo); + break; + } + + break; + + default: + Display(TEXT("Unknown (Handle Notification)")); + break; + + } + return TRUE; +} + + +BOOLEAN +EnumExistingDevices( + HWND hWnd +) +{ + HDEVINFO hardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL; + ULONG predictedLength = 0; + ULONG requiredLength = 0, i; + DWORD error; + PDEVICE_INFO deviceInfo =NULL; + + DisplayV(TEXT("Entered EnumExistingDevices")); + + // + // Make sure the list is empty + // + if(!IsListEmpty(&ListHead) ){ + MessageBox(hWnd, TEXT("ListHead should be empty"), TEXT("Error!"), MB_OK); + return FALSE; + } + + DeviceIndex = 0; + + hardwareDeviceInfo = SetupDiGetClassDevs ( + (LPGUID)&InterfaceGuid, + NULL, // Define no enumerator (global) + NULL, // Define no + (DIGCF_PRESENT | // Only Devices present + DIGCF_DEVICEINTERFACE)); // Function class devices. + if(INVALID_HANDLE_VALUE == hardwareDeviceInfo) + { + goto Error; + } + + // + // Enumerate devices of a specific interface class + // + deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); + + for(i=0; SetupDiEnumDeviceInterfaces (hardwareDeviceInfo, + 0, // No care about specific PDOs + (LPGUID)&InterfaceGuid, + i, // + &deviceInterfaceData); i++ ) { + + // + // Allocate a function class device data structure to + // receive the information about this particular device. + // + + // + // First find out required length of the buffer + // + if (deviceInterfaceDetailData) { + HeapFree (GetProcessHeap(), 0, deviceInterfaceDetailData); + deviceInterfaceDetailData = NULL; + } + + if(!SetupDiGetDeviceInterfaceDetail ( + hardwareDeviceInfo, + &deviceInterfaceData, + NULL, // probing so no output buffer yet + 0, // probing so output buffer length of zero + &requiredLength, + NULL) && (error = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) + { + goto Error; + } + predictedLength = requiredLength; + + deviceInterfaceDetailData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + predictedLength); + if (deviceInterfaceDetailData == NULL) { + goto Error; + } + + deviceInterfaceDetailData->cbSize = + sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA); + + + if (! SetupDiGetDeviceInterfaceDetail ( + hardwareDeviceInfo, + &deviceInterfaceData, + deviceInterfaceDetailData, + predictedLength, + &requiredLength, + NULL)) { + goto Error; + } + + deviceInfo = CreateDeviceInfo(deviceInterfaceDetailData->DevicePath); + + if(!deviceInfo) + goto Error; + + + } + + if(deviceInterfaceDetailData) { + HeapFree (GetProcessHeap(), 0, deviceInterfaceDetailData); + } + + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + return 0; + +Error: + + error = GetLastError(); + MessageBox(hWnd, TEXT("EnumExisting Devices failed"), TEXT("Error!"), MB_OK); + if(deviceInterfaceDetailData) + HeapFree (GetProcessHeap(), 0, deviceInterfaceDetailData); + + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + Cleanup(hWnd); + return 0; +} + +PDEVICE_INFO +FindDeviceInfo( + PDIALOG_RESULT InputInfo + ) +{ + PLIST_ENTRY thisEntry, listHead; + PDEVICE_INFO deviceInfo = NULL, result = NULL; + + listHead = &ListHead; + + for(thisEntry = listHead->Flink; + thisEntry != listHead; + thisEntry = thisEntry->Flink){ + + deviceInfo = CONTAINING_RECORD(thisEntry, DEVICE_INFO, ListEntry); + + if(deviceInfo->DeviceIndex == InputInfo->DeviceIndex){ + + if(deviceInfo->IsANetworkMiniport){ + Display(TEXT("You can't use this app on a device installed as a network device")); + break; + } + + if(deviceInfo->hDevice && + deviceInfo->hDevice != INVALID_HANDLE_VALUE){ + Display(TEXT("%ws device is already in use"), + deviceInfo->DeviceName); + break; + } + + + deviceInfo->DeviceIndex = InputInfo->DeviceIndex; + deviceInfo->PacketSize = InputInfo->PacketSize; + memcpy(deviceInfo->UnicodeSourceIp, InputInfo->SourceIp, MAX_LEN); + memcpy(deviceInfo->UnicodeDestIp, InputInfo->DestIp, MAX_LEN); + // + // Convert the unicode source and destination IP string + // to ANSI and store it. + // + WideCharToMultiByte(CP_ACP, //ANSI code page + 0, deviceInfo->UnicodeSourceIp, -1, + deviceInfo->SourceIp, MAX_LEN, NULL, NULL); + + // + // Convert Unicode string to ANSI. + // + WideCharToMultiByte(CP_ACP, 0, deviceInfo->UnicodeDestIp, -1, + deviceInfo->DestIp, MAX_LEN, NULL, NULL); + + + result = deviceInfo; + break; + } + + } + + return result; + +} + +PDEVICE_INFO +CreateDeviceInfo( + _In_ LPWSTR DevicePath + ) +{ + PDEVICE_INFO deviceInfo = NULL; + HRESULT hr; + + DisplayV(TEXT("Entered CreateDeviceInfo")); + + deviceInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEVICE_INFO)); + if(!deviceInfo) { + goto Error; + } + + + if(!GetDeviceDescription(DevicePath, + deviceInfo->DeviceName, + sizeof(deviceInfo->DeviceName), + &deviceInfo->IsANetworkMiniport + )) { + Display(TEXT("GetDeviceDescription failed %x"), GetLastError()); + goto Error; + } + + + // + // Copy the device path so that we can open the device using CreateFile. + // + hr = StringCchCopy(deviceInfo->DevicePath, MAX_PATH, DevicePath); + if(FAILED(hr)){ + goto Error; + } + + DeviceIndex++; + deviceInfo->DeviceIndex = DeviceIndex; + + // + // Link this to the global list of devices. + // + InitializeListHead(&deviceInfo->ListEntry); + InsertTailList(&ListHead, &deviceInfo->ListEntry); + + Display(TEXT("Device %d is %ws"), DeviceIndex, deviceInfo->DeviceName); + + return deviceInfo; + +Error: + + if(deviceInfo) { + HeapFree (GetProcessHeap(), 0, deviceInfo); + } + return NULL; + +} + +VOID +FreeDeviceInfo( + _In_ PDEVICE_INFO DeviceInfo + ) +{ + DisplayV(TEXT("Entered FreeDeviceInfo")); + + if (DeviceInfo->hHandleNotification) { + UnregisterDeviceNotification(DeviceInfo->hHandleNotification); + DeviceInfo->hHandleNotification = NULL; + } + if (DeviceInfo->hDevice != INVALID_HANDLE_VALUE && + DeviceInfo->hDevice != NULL) { + CloseHandle(DeviceInfo->hDevice); + DeviceInfo->hDevice = INVALID_HANDLE_VALUE; + Display(TEXT("Closed handle to device %ws"), DeviceInfo->DeviceName ); + } + + RemoveEntryList(&DeviceInfo->ListEntry); + + HeapFree (GetProcessHeap(), 0, DeviceInfo); + + return; +} + +BOOL +SetRegistryInfo( + _In_reads_bytes_(SourceIPLen) LPWSTR SourceIP, + _In_ DWORD SourceIPLen, + _In_reads_bytes_(DestinationIPLen) LPWSTR DestinationIP, + _In_ DWORD DestinationIPLen + ) +{ + HKEY hKey; + BOOL ret = FALSE; + size_t srcStrLen, destStrLen; + + if (FAILED(StringCbLengthW(SourceIP, SourceIPLen, &srcStrLen))) { + return ret; + } + + if (FAILED(StringCbLengthW(DestinationIP, DestinationIPLen, &destStrLen))) { + return ret; + } + + // + // RegSetValueEx takes a DWORD, in the rare case that size_t is larger than + // a DWORD return an error. + // + if (srcStrLen > (DWORD_MAX - sizeof(WCHAR))|| + destStrLen > (DWORD_MAX - sizeof(WCHAR))) { + return ret; + } + + if (RegOpenKey(HKEY_LOCAL_MACHINE, REG_PATH, &hKey)) { + + if (ERROR_SUCCESS != RegCreateKey(HKEY_LOCAL_MACHINE, REG_PATH, &hKey)) { + Display(TEXT("RegCreateKey failed: %x"), GetLastError()); + return ret; + } + } + + if (ERROR_SUCCESS == RegSetValueEx(hKey, L"SourceIP", 0, REG_SZ, + (LPBYTE)SourceIP, (DWORD) srcStrLen+sizeof(WCHAR))) { + + if (ERROR_SUCCESS == RegSetValueEx(hKey, L"DestinationIP", 0, REG_SZ, + (LPBYTE)DestinationIP, (DWORD) destStrLen+sizeof(WCHAR))) { + ret = TRUE; + } + } + + RegCloseKey(hKey); + return ret; +} + +_Success_(return) +BOOL +GetRegistryInfo( + _Out_writes_bytes_(* SourceIPLen) PWSTR SourceIP, + _Inout_ LPDWORD SourceIPLen, + _Out_writes_bytes_(* DestinationIPLen) PWSTR DestinationIP, + _Inout_ LPDWORD DestinationIPLen + ) +{ + HKEY hKey; + DWORD dwType = REG_SZ; + BOOL ret = FALSE; + + if(ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, REG_PATH, &hKey)) { + + if(ERROR_SUCCESS == RegQueryValueEx(hKey, L"SourceIP", NULL, &dwType, + (LPBYTE)SourceIP, SourceIPLen)){ + + if(ERROR_SUCCESS == RegQueryValueEx(hKey, L"DestinationIP", NULL, &dwType, + (LPBYTE)DestinationIP, DestinationIPLen)){ + + ret = TRUE; + } + + } + RegCloseKey(hKey); + } + + return ret; +} + + + +BOOLEAN +OpenDevice( + _In_ HWND HWnd, + _In_ PDEVICE_INFO DeviceInfo + ) +{ + DEV_BROADCAST_HANDLE filter; + HANDLE hDevice; + + DisplayV(TEXT("Entered OpenDevice")); + + // + // Open an handle to the device. + // + hDevice = CreateFile ( + DeviceInfo->DevicePath, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, // no SECURITY_ATTRIBUTES structure + OPEN_EXISTING, // No special create flags + FILE_FLAG_OVERLAPPED, + NULL); + + if (INVALID_HANDLE_VALUE == hDevice) { + Display(TEXT("Failed to open the device: %ws"), + DeviceInfo->DeviceName); + return FALSE; + } + + Display(TEXT("Opened handled to the device: %ws"), + DeviceInfo->DeviceName); + // + // Register handle based notification to receive pnp + // device change notification on the handle. + // + + memset (&filter, 0, sizeof(filter)); //zero the structure + filter.dbch_size = sizeof(filter); + filter.dbch_devicetype = DBT_DEVTYP_HANDLE; + filter.dbch_handle = hDevice; + + DeviceInfo->hHandleNotification = RegisterDeviceNotification(HWnd, &filter, 0); + if(!DeviceInfo->hHandleNotification){ + Display(TEXT("Failed to register notification: %ws"), + DeviceInfo->DeviceName); + CloseHandle(hDevice); + return FALSE; + } + + DeviceInfo->hDevice = hDevice; + + return TRUE; + +} + + +BOOL +CreatePingThread( + PDEVICE_INFO DeviceInfo + ) +{ + ULONG id; + + DisplayV(TEXT("CreatePingThread")); + + DeviceInfo->ExitThread = FALSE; + + // + // Start the ping operation in a separate thread. + // + DeviceInfo->ThreadHandle = CreateThread( NULL, // security attributes + 0, // initial stack size + (LPTHREAD_START_ROUTINE) PingThread, // Main() function + DeviceInfo, // arg to Reader thread + 0, // creation flags + (LPDWORD)&id); // returned thread id + + if ( NULL == DeviceInfo->ThreadHandle) { + Display(TEXT("CreateThread failed %x"), GetLastError()); + return FALSE; + } + + return TRUE; +} + +VOID +TerminatePingThread( + PDEVICE_INFO DeviceInfo + ) +{ + DWORD status; + + DisplayV(TEXT("TerminatePingThread")); + + if(DeviceInfo->ThreadHandle){ + + DeviceInfo->ExitThread = TRUE; + // + // Wait for the thread to exit + // + status = WaitForSingleObjectEx(DeviceInfo->ThreadHandle, 1000, TRUE ); + if(status == WAIT_FAILED){ + Display(TEXT("Wait failed %x"), GetLastError()); + } + CloseHandle(DeviceInfo->ThreadHandle); + DeviceInfo->ThreadHandle = NULL; + } + +} + +BOOLEAN +Cleanup( + HWND hWnd + ) +/*++ + + This routine walks the global list of currently enumerated devices + and close all handles and frees the memory. + --*/ +{ + PDEVICE_INFO deviceInfo =NULL; + PLIST_ENTRY thisEntry; + + DisplayV(TEXT("Entered Cleanup")); + + while (!IsListEmpty(&ListHead)) { + thisEntry = ListHead.Flink; + deviceInfo = CONTAINING_RECORD(thisEntry, DEVICE_INFO, ListEntry); + // + // First let us make sure the PingThread is not running. + // + TerminatePingThread(deviceInfo); + FreeDeviceInfo(deviceInfo); + } + return TRUE; +} + + +_Success_(return != FALSE) +BOOL +GetDeviceDescription( + _In_ LPTSTR DevPath, + _Out_writes_bytes_all_(OutBufferLen) LPTSTR OutBuffer, + _In_ ULONG OutBufferLen, + BOOL *NetClassDevice +) +{ + HDEVINFO hardwareDeviceInfo = NULL; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + SP_DEVINFO_DATA deviceInfoData; + DWORD dwRegType, error; + TCHAR classGuidString[MAX_GUID_STRING_LEN]; + HRESULT hr; + GUID classGuid; + BOOL ret = FALSE; + + DisplayV(TEXT("GetDeviceDescription")); + + hardwareDeviceInfo = SetupDiCreateDeviceInfoList(NULL, NULL); + if(INVALID_HANDLE_VALUE == hardwareDeviceInfo) + { + Display(TEXT("Couldn't create DeviceInfoList: %x"), GetLastError()); + goto Error; + } + + // + // Enumerate devices of toaster class + // + deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); + + SetupDiOpenDeviceInterface (hardwareDeviceInfo, DevPath, + 0, // + &deviceInterfaceData); + + deviceInfoData.cbSize = sizeof(deviceInfoData); + if(!SetupDiGetDeviceInterfaceDetail ( + hardwareDeviceInfo, + &deviceInterfaceData, + NULL, // probing so no output buffer yet + 0, // probing so output buffer length of zero + NULL, + &deviceInfoData) && (error = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) + { + Display(TEXT("Couldn't get interface detail: %x"), GetLastError()); + goto Error; + } + // + // Get the friendly name for this instance, if that fails + // try to get the device description. + // + + if(!SetupDiGetDeviceRegistryProperty(hardwareDeviceInfo, &deviceInfoData, + SPDRP_FRIENDLYNAME, + &dwRegType, + (BYTE*) OutBuffer, + OutBufferLen, + NULL)) + { + if(!SetupDiGetDeviceRegistryProperty(hardwareDeviceInfo, &deviceInfoData, + SPDRP_DEVICEDESC, + &dwRegType, + (BYTE*) OutBuffer, + OutBufferLen, + NULL)){ + Display(TEXT("Couldn't get friendlyname: %x"), GetLastError()); + goto Error; + + } + + + } + + // + // Get the class guid of the device and find out whether this is a + // network miniport. + // + if(!SetupDiGetDeviceRegistryProperty(hardwareDeviceInfo, + &deviceInfoData, + SPDRP_CLASSGUID, + &dwRegType, + (BYTE*) classGuidString, + sizeof(classGuidString), + NULL)) { + Display(TEXT("Class guid is not available for device: %ws"), OutBuffer ); + } + + hr = CLSIDFromString(classGuidString, &classGuid); + if(FAILED(hr)) { + goto Error; + } + + if(IsEqualGUID(&classGuid, &GUID_DEVCLASS_NET)){ + *NetClassDevice = TRUE; + } else { + *NetClassDevice = FALSE; + } + + ret = TRUE; + +Error: + + if(hardwareDeviceInfo) { + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + } + return ret; +} + + +BOOL +HandlePowerBroadcast( + HWND hWnd, + WPARAM wParam, + LPARAM lParam) +{ + BOOL fRet = TRUE; + + switch (wParam) + { + case PBT_APMQUERYSTANDBY: + DisplayV(TEXT("PBT_APMQUERYSTANDBY")); + break; + case PBT_APMQUERYSUSPEND: + DisplayV(TEXT("PBT_APMQUERYSUSPEND")); + break; + case PBT_APMSTANDBY : + DisplayV(TEXT("PBT_APMSTANDBY")); + break; + case PBT_APMSUSPEND : + DisplayV(TEXT("PBT_APMSUSPEND")); + break; + case PBT_APMQUERYSTANDBYFAILED: + DisplayV(TEXT("PBT_APMQUERYSTANDBYFAILED")); + break; + case PBT_APMRESUMESTANDBY: + DisplayV(TEXT("PBT_APMRESUMESTANDBY")); + break; + case PBT_APMQUERYSUSPENDFAILED: + DisplayV(TEXT("PBT_APMQUERYSUSPENDFAILED")); + break; + case PBT_APMRESUMESUSPEND: + DisplayV(TEXT("PBT_APMRESUMESUSPEND")); + break; + case PBT_APMBATTERYLOW: + DisplayV(TEXT("PBT_APMBATTERYLOW")); + break; + case PBT_APMOEMEVENT: + DisplayV(TEXT("PBT_APMOEMEVENT")); + break; + case PBT_APMRESUMEAUTOMATIC: + DisplayV(TEXT("PBT_APMRESUMEAUTOMATIC")); + break; + case PBT_APMRESUMECRITICAL: + DisplayV(TEXT("PBT_APMRESUMECRITICAL")); + break; + case PBT_APMPOWERSTATUSCHANGE: + DisplayV(TEXT("PBT_APMPOWERSTATUSCHANGE")); + break; + default: + DisplayV(TEXT("Default")); + break; + } + return fRet; +} + + diff --git a/general/pcidrv/test/testapp.h b/general/pcidrv/test/testapp.h new file mode 100644 index 00000000..3a75e4f5 --- /dev/null +++ b/general/pcidrv/test/testapp.h @@ -0,0 +1,260 @@ +/*++ +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + notify.h + +Abstract: + +--*/ + +#ifndef __TESTAPP_H +#define __TESTAPP_H + +#pragma warning(disable:4214 4201 4115 4100) + +#define UNICODE 1 +#define INITGUID + +#include // for using Windows data types and functions +#include // for using winsock utility inet_addr/ntohs functions. +#include // for using SetupDi functions +#include // for PNP device notification interfaces +#include // for defining ioctls +#include // for NDIS OIDs +#include // for safe string functions +#include // for GUID_DEVCLASS_NET +#include // for MAX_GUID_STRING_LEN +#include // for CLSIDFromString. Link to ole32.lib + +#include "public.h" +#include "resource.h" +#include + +// +// Registry path where the IP addresses are saved +// +#define REG_PATH L"Software\\Microsoft\\PCIDRV\\MyPing" +#define DEF_SOURCE_IP L"192.168.0.2" +#define DEF_DEST_IP L"192.168.0.1" + +#define PING_SLEEP_TIME 100 +#define MAC_ADDR_LEN 6 + +#define MAX_LEN 64 +#define MAX_PAYLOAD_SIZE 1428 +#define MIN_PAYLOAD_SIZE 32 +#define MAX_PING_RETRY 10 + +extern BOOLEAN Verbose; + +typedef struct _DEVICE_INFO +{ + LIST_ENTRY ListEntry; + HANDLE hDevice; // file handle + HDEVNOTIFY hHandleNotification; // notification handle + TCHAR DeviceName[MAX_PATH];// friendly name of device description + TCHAR DevicePath[MAX_PATH];// + ULONG DeviceIndex; // Serial number of the device. + CHAR SourceIp[MAX_LEN]; + CHAR DestIp[MAX_LEN]; + WCHAR UnicodeSourceIp[MAX_LEN]; + WCHAR UnicodeDestIp[MAX_LEN]; + ULONG PacketSize; + UCHAR SrcMacAddr[MAC_ADDR_LEN]; + UCHAR TargetMacAddr[MAC_ADDR_LEN]; + HANDLE PingEvent; + ULONG NumberOfRequestSent; + BOOL Sleep; + ULONG TimeOut; + BOOL IsANetworkMiniport; + BOOLEAN ExitThread; + HANDLE ThreadHandle; + +} DEVICE_INFO, *PDEVICE_INFO; + + +typedef struct _DIALOG_RESULT +{ + ULONG DeviceIndex; + WCHAR SourceIp[MAX_LEN]; + WCHAR DestIp[MAX_LEN]; + ULONG PacketSize; +} DIALOG_RESULT, *PDIALOG_RESULT; + + +// +// Copied Macros from ntddk.h. Used to using the kernel-mode +// style of linked list. +// + +#define CONTAINING_RECORD(address, type, field) ((type *)( \ + (PCHAR)(address) - \ + (ULONG_PTR)(&((type *)0)->field))) + + +#define InitializeListHead(ListHead) (\ + (ListHead)->Flink = (ListHead)->Blink = (ListHead)) + +#define RemoveHeadList(ListHead) \ + (ListHead)->Flink;\ + {RemoveEntryList((ListHead)->Flink)} + +#define IsListEmpty(ListHead) \ + ((ListHead)->Flink == (ListHead)) + + +#define RemoveEntryList(Entry) {\ + PLIST_ENTRY _EX_Blink;\ + PLIST_ENTRY _EX_Flink;\ + _EX_Flink = (Entry)->Flink;\ + _EX_Blink = (Entry)->Blink;\ + _EX_Blink->Flink = _EX_Flink;\ + _EX_Flink->Blink = _EX_Blink;\ + } + +#define InsertTailList(ListHead,Entry) {\ + PLIST_ENTRY _EX_Blink;\ + PLIST_ENTRY _EX_ListHead;\ + _EX_ListHead = (ListHead);\ + _EX_Blink = _EX_ListHead->Blink;\ + (Entry)->Flink = _EX_ListHead;\ + (Entry)->Blink = _EX_Blink;\ + _EX_Blink->Flink = (Entry);\ + _EX_ListHead->Blink = (Entry);\ + } + + +#ifndef min +#define min(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif + +#ifndef max +#define max(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif + +#define DisplayV(pstrFormat, ...) if (Verbose) {Display(pstrFormat, __VA_ARGS__);} + +LRESULT FAR PASCAL +WndProc ( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam + ); + +BOOLEAN EnumExistingDevices( + HWND hWnd + ); + +BOOL HandleDeviceInterfaceChange( + HWND hwnd, + DWORD evtype, + PDEV_BROADCAST_DEVICEINTERFACE dip + ); + +BOOL HandleDeviceChange( + HWND hwnd, + DWORD evtype, + PDEV_BROADCAST_HANDLE dhp + ); + +LRESULT +HandleCommands( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ); + +BOOL +HandlePowerBroadcast( + HWND hWnd, + WPARAM wParam, + LPARAM lParam); + +BOOLEAN Cleanup( + HWND hWnd + ); + +_Success_(return != FALSE) +BOOL +GetDeviceDescription( + _In_ LPTSTR DevPath, + _Out_writes_bytes_all_(OutBufferLen) LPTSTR OutBuffer, + _In_ ULONG OutBufferLen, + BOOL *NetClassDevice +); + +BOOLEAN +OpenDevice( + _In_ HWND HWnd, + _In_ PDEVICE_INFO DeviceInfo + ); + + +INT_PTR CALLBACK +DlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam); + +DWORD +PingThread ( + PDEVICE_INFO DeviceInfo + ); + +VOID Display( + _In_ LPWSTR pstrFormat, + ... + ) ; + + +BOOL +CreatePingThread( + PDEVICE_INFO DeviceInfo + ); + +PDEVICE_INFO +FindDeviceInfo( + PDIALOG_RESULT InputInfo + ); + +VOID +FreeDeviceInfo( + _In_ PDEVICE_INFO DeviceInfo + ); + +PDEVICE_INFO +CreateDeviceInfo( + _In_ LPWSTR DevicePath + ); + +VOID +TerminatePingThread( + PDEVICE_INFO DeviceInfo + ); + +_Success_(return) +BOOL +GetRegistryInfo( + _Out_writes_bytes_(* SourceIPLen) PWSTR SourceIP, + _Inout_ LPDWORD SourceIPLen, + _Out_writes_bytes_(* DestinationIPLen) PWSTR DestinationIP, + _Inout_ LPDWORD DestinationIPLen + ); + +BOOL +SetRegistryInfo( + _In_reads_bytes_(SourceIPLen) LPWSTR SourceIP, + _In_ DWORD SourceIPLen, + _In_reads_bytes_(DestinationIPLen) LPWSTR DestinationIP, + _In_ DWORD DestinationIPLen + ); + + +#endif + + diff --git a/general/pcidrv/test/testapp.rc b/general/pcidrv/test/testapp.rc new file mode 100644 index 00000000..dcef4be0 --- /dev/null +++ b/general/pcidrv/test/testapp.rc @@ -0,0 +1,37 @@ +#include +#include "resource.h" + +GenericMenu MENU + { + POPUP "&Menu" + { + MENUITEM "&Start Ping", IDM_PING + MENUITEM "&Stop", IDM_CLOSE + MENUITEM "&Re-enumerate All Devices" IDM_ENUMERATE + MENUITEM "Clear &Display", IDM_CLEAR + MENUITEM "Verbose", IDM_VERBOSE + MENUITEM "E&xit", IDM_EXIT + } + } + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DIALOG DIALOG DISCARDABLE 0, 0, 291, 118 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ping" +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "OK",ID_OK,71,96,50,14,BS_NOTIFY + PUSHBUTTON "CANCEL",ID_CANCEL,167,96,50,14,BS_NOTIFY + LTEXT "Device Index :",IDC_STATIC,19,13,55,8 + LTEXT "Source IP :",IDC_STATIC,19,35,55,8 + EDITTEXT IDC_DEVICE_INDEX,75,11,24,14,ES_NUMBER + EDITTEXT IDC_SOURCE_IP,76,32,103,14,ES_AUTOHSCROLL + LTEXT "Destination IP :",IDC_STATIC,18,55,55,8 + EDITTEXT IDC_DESTINATION_IP,75,53,101,14,ES_AUTOHSCROLL + LTEXT "Packet Size",IDC_STATIC,18,77,55,8 + EDITTEXT IDC_PACKET_SIZE,75,75,24,14,ES_NUMBER +END diff --git a/general/perfcounters/kcs/ReadMe.md b/general/perfcounters/kcs/ReadMe.md new file mode 100644 index 00000000..4f28894c --- /dev/null +++ b/general/perfcounters/kcs/ReadMe.md @@ -0,0 +1,15 @@ +Kernel Counter Sample (Kcs) +=========================== + +The Kcs sample driver demonstrates the use of the [kernel-mode performance library](http://msdn.microsoft.com/en-us/library/windows/hardware/ff548159). The sample driver does not control any hardware; it simply provides example code that demonstrates how to provide counter data from a kernel-mode driver. The code contains comments to explain what each function does. The sample creates geometric wave and trigonometric wave counter sets. + +This module contains sample code to demonstrate how to provide counter data from a kernel driver. + +This sample driver should not be used in a production environment. + +Kcs is designed for Windows 7 and later versions of Windows. + +The Microsoft Windows operating system allows system components and third parties to expose performance metrics in a standard way by using [Performance Counters](http://msdn.microsoft.com/en-us/library/windows/hardware/aa373083). Kernel-mode PCW providers are installed in the system as Performance Counter Library (PERFLIB) (Version 2 providers), which allows their counters to be browsed, and allows for data collection and instance enumeration. Consumers can query KM PCW providers by using PDH and PERFLIB Version 1 without any modification to the consumer code. + + + diff --git a/general/perfcounters/kcs/kcs.c b/general/perfcounters/kcs/kcs.c new file mode 100644 index 00000000..ae4405b9 --- /dev/null +++ b/general/perfcounters/kcs/kcs.c @@ -0,0 +1,407 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + kcs.c + +Abstract: + + This module contains sample code to demonstrate how to provide + counter data from a kernel driver. + +Environment: + + Kernel mode only. + +--*/ + + +#include +#include "kcs.h" +#include "kcsCounters.h" + +#pragma code_seg("PAGE") + +DRIVER_INITIALIZE DriverEntry; +DRIVER_UNLOAD KcsUnload; + +NTSTATUS +KcsAddGeometricInstance ( + _In_ PPCW_BUFFER Buffer, + _In_ PCWSTR Name, + _In_ ULONG MinimalValue, + _In_ ULONG Amplitude + ) + +/*++ + +Routine Description: + + This utility function adds instance to the callback buffer. + +Arguments: + + Buffer - Data will be returned in this buffer. + + Name - Name of instances to be added. + + MinimalValue - Minimum value of the wave. + + Amplitude - Amplitude of the wave. + +Return Value: + + NTSTATUS indicating if the function succeeded. + +--*/ + +{ + ULONG Index; + LARGE_INTEGER Timestamp; + UNICODE_STRING UnicodeName; + GEOMETRIC_WAVE_VALUES Values; + + PAGED_CODE(); + + KeQuerySystemTime(&Timestamp); + + Index = (Timestamp.QuadPart / 10000000) % 10; + + Values.Triangle = MinimalValue + Amplitude * abs(5 - Index) / 5; + Values.Square = MinimalValue + Amplitude * (Index < 5); + + RtlInitUnicodeString(&UnicodeName, Name); + + return KcsAddGeometricWave(Buffer, &UnicodeName, 0, &Values); +} + +NTSTATUS NTAPI +KcsGeometricWaveCallback ( + _In_ PCW_CALLBACK_TYPE Type, + _In_ PPCW_CALLBACK_INFORMATION Info, + _In_opt_ PVOID Context + ) + +/*++ + +Routine Description: + + This function returns the list of counter instances and counter data. + +Arguments: + + Type - Request type. + + Info - Buffer for returned data. + + Context - Not used. + +Return Value: + + NTSTATUS indicating if the function succeeded. + +--*/ + +{ + NTSTATUS Status; + UNICODE_STRING UnicodeName; + + UNREFERENCED_PARAMETER(Context); + + PAGED_CODE(); + + switch (Type) { + case PcwCallbackEnumerateInstances: + + // + // Instances are being enumerated, so we add them without values. + // + + RtlInitUnicodeString(&UnicodeName, L"Small Wave"); + Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer, + &UnicodeName, + 0, + NULL); + if (!NT_SUCCESS(Status)) { + return Status; + } + + RtlInitUnicodeString(&UnicodeName, L"Medium Wave"); + Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer, + &UnicodeName, + 0, + NULL); + if (!NT_SUCCESS(Status)) { + return Status; + } + + RtlInitUnicodeString(&UnicodeName, L"Large Wave"); + Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer, + &UnicodeName, + 0, + NULL); + if (!NT_SUCCESS(Status)) { + return Status; + } + + break; + + case PcwCallbackCollectData: + + // + // Add values for 3 instances of Geometric Wave Counter Set. + // + + Status = KcsAddGeometricInstance(Info->CollectData.Buffer, + L"Small Wave", + 40, + 20); + if (!NT_SUCCESS(Status)) { + return Status; + } + + Status = KcsAddGeometricInstance(Info->CollectData.Buffer, + L"Medium Wave", + 30, + 40); + if (!NT_SUCCESS(Status)) { + return Status; + } + + Status = KcsAddGeometricInstance(Info->CollectData.Buffer, + L"Large Wave", + 20, + 60); + if (!NT_SUCCESS(Status)) { + return Status; + } + + break; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +KcsAddTrignometricInstance ( + _In_ PPCW_BUFFER Buffer, + _In_ PCWSTR Name, + _In_ ULONG MinimalValue, + _In_ ULONG Amplitude + ) + +/*++ + +Routine Description: + + This utility function adds instance to the callback buffer. + +Arguments: + + Buffer - Data will be returned in this buffer. + + Name - Name of instances to be added. + + MinimalValue - Minimum value of the wave. + + Amplitude - Amplitude of the wave. + +Return Value: + + NTSTATUS indicating if the function succeeded. + +--*/ + +{ + double Angle; + KFLOATING_SAVE FloatSave; + NTSTATUS Status; + LARGE_INTEGER Timestamp; + UNICODE_STRING UnicodeName; + TRIGNOMETRIC_WAVE_VALUES Values; + + PAGED_CODE(); + + Status = KeSaveFloatingPointState(&FloatSave); + if (!NT_SUCCESS(Status)) { + return Status; + } + + KeQuerySystemTime(&Timestamp); + + Angle = (double)(Timestamp.QuadPart / 400000) * (22/7) / 180; + + Values.Constant = MinimalValue; + Values.Cosine = (ULONG)(MinimalValue + Amplitude * cos(Angle)); + Values.Sine = (ULONG)(MinimalValue + Amplitude * sin(Angle)); + + KeRestoreFloatingPointState(&FloatSave); + + // + // Add instance name & values to the caller's buffer. + // + + RtlInitUnicodeString(&UnicodeName, Name); + + return KcsAddTrignometricWave(Buffer, &UnicodeName, 0, &Values); +} + +NTSTATUS NTAPI +KcsTrignometricWaveCallback ( + _In_ PCW_CALLBACK_TYPE Type, + _In_ PPCW_CALLBACK_INFORMATION Info, + _In_opt_ PVOID Context + ) + +/*++ + +Routine Description: + + This function returns the list of counter instances and counter data. + +Arguments: + + Type - Request type. + + Info - Buffer for returned data. + + Context - Not used. + +Return Value: + + NTSTATUS indicating if the function succeeded. + +--*/ + +{ + NTSTATUS Status; + UNICODE_STRING UnicodeName; + + UNREFERENCED_PARAMETER(Context); + + PAGED_CODE(); + + switch (Type) { + case PcwCallbackEnumerateInstances: + RtlInitUnicodeString(&UnicodeName, L"default"); + Status = KcsAddTrignometricWave(Info->EnumerateInstances.Buffer, + &UnicodeName, + 0, + NULL); + if (!NT_SUCCESS(Status)) { + return Status; + } + + break; + + case PcwCallbackCollectData: + + // + // Add values for Single Instance of Trignometirc Wave Counter Set. + // + + return KcsAddTrignometricInstance(Info->CollectData.Buffer, + L"default", + 50, + 30); + } + + return STATUS_SUCCESS; +} + +VOID +KcsUnload ( + _In_ PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This function unregisters countersets + +Arguments: + + DriverObject - Not used. + +Return Value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER(DriverObject); + + PAGED_CODE(); + + // + // Unregister Countersets. + // + + KcsUnregisterGeometricWave(); + KcsUnregisterTrignometricWave(); +} + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This function registers countersets on initial loading of the driver. + +Arguments: + + DriverObject - Supplies the driver object of the driver being loaded. + + RegistryPath - Not used. + +Return Value: + + NTSTATUS indicating if driver was properly loaded. + +--*/ + +{ + NTSTATUS Status; + + UNREFERENCED_PARAMETER(RegistryPath); + + PAGED_CODE(); + + // + // Register Countersets. + // + + Status = KcsRegisterGeometricWave(KcsGeometricWaveCallback, NULL); + if (!NT_SUCCESS(Status)) { + return Status; + } + + Status = KcsRegisterTrignometricWave(KcsTrignometricWaveCallback, NULL); + if (!NT_SUCCESS(Status)) { + KcsUnregisterTrignometricWave(); + return Status; + } + + // + // Success path - set up unload routine and return success. + // + + DriverObject->DriverUnload = KcsUnload; + + return STATUS_SUCCESS; +} + diff --git a/general/perfcounters/kcs/kcs.h b/general/perfcounters/kcs/kcs.h new file mode 100644 index 00000000..4d845cc3 --- /dev/null +++ b/general/perfcounters/kcs/kcs.h @@ -0,0 +1,34 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + kcs.h + +Abstract: + + This module contains sample code to demonstrate how to provide + counter data from a kernel driver. + +Environment: + + Kernel mode only. + +--*/ + +typedef struct _GEOMETRIC_WAVE_VALUES { + ULONG Square; + ULONG Triangle; +} GEOMETRIC_WAVE_VALUES, *PGEOMETRIC_WAVE_VALUES; + +typedef struct _TRIGNOMETRIC_WAVE_VALUES { + ULONG Constant; + ULONG Cosine; + ULONG Sine; +} TRIGNOMETRIC_WAVE_VALUES, *PTRIGNOMETRIC_WAVE_VALUES; \ No newline at end of file diff --git a/general/perfcounters/kcs/kcs.man b/general/perfcounters/kcs/kcs.man new file mode 100644 index 00000000..a5a16ffb --- /dev/null +++ b/general/perfcounters/kcs/kcs.man @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/perfcounters/kcs/kcs.rc b/general/perfcounters/kcs/kcs.rc new file mode 100644 index 00000000..7c19d061 --- /dev/null +++ b/general/perfcounters/kcs/kcs.rc @@ -0,0 +1 @@ +#include "kcsCounters.rc" diff --git a/general/perfcounters/kcs/kcs.sln b/general/perfcounters/kcs/kcs.sln new file mode 100644 index 00000000..6630e582 --- /dev/null +++ b/general/perfcounters/kcs/kcs.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kcs", "kcs.vcxproj", "{FA4BC0C3-DA1C-46DA-BFF4-AA448B5D0A0A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FA4BC0C3-DA1C-46DA-BFF4-AA448B5D0A0A}.Debug|Win32.ActiveCfg = Debug|Win32 + {FA4BC0C3-DA1C-46DA-BFF4-AA448B5D0A0A}.Debug|Win32.Build.0 = Debug|Win32 + {FA4BC0C3-DA1C-46DA-BFF4-AA448B5D0A0A}.Release|Win32.ActiveCfg = Release|Win32 + {FA4BC0C3-DA1C-46DA-BFF4-AA448B5D0A0A}.Release|Win32.Build.0 = Release|Win32 + {FA4BC0C3-DA1C-46DA-BFF4-AA448B5D0A0A}.Debug|x64.ActiveCfg = Debug|x64 + {FA4BC0C3-DA1C-46DA-BFF4-AA448B5D0A0A}.Debug|x64.Build.0 = Debug|x64 + {FA4BC0C3-DA1C-46DA-BFF4-AA448B5D0A0A}.Release|x64.ActiveCfg = Release|x64 + {FA4BC0C3-DA1C-46DA-BFF4-AA448B5D0A0A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/general/perfcounters/kcs/kcs.vcxproj b/general/perfcounters/kcs/kcs.vcxproj new file mode 100644 index 00000000..c58a9026 --- /dev/null +++ b/general/perfcounters/kcs/kcs.vcxproj @@ -0,0 +1,174 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {FA4BC0C3-DA1C-46DA-BFF4-AA448B5D0A0A} + $(MSBuildProjectName) + Debug + Win32 + {C44D3FE1-3E60-4619-A1F9-E34A55C6FCA7} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + kcs + + + kcs + + + kcs + + + kcs + + + + true + Level4 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + + + true + Level4 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + + + true + Level4 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + + + true + Level4 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $([System.IO.Path]::GetDirectoryName($(ProjectDir)\$(IntDir))) + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/perfcounters/kcs/kcs.vcxproj.Filters b/general/perfcounters/kcs/kcs.vcxproj.Filters new file mode 100644 index 00000000..fedf64ea --- /dev/null +++ b/general/perfcounters/kcs/kcs.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {1E487FA6-DCB9-4A49-A9B9-3A1C9D75EB43} + + + h;hpp;hxx;hm;inl;inc;xsd + {E4CC3C2E-1709-47A1-A48A-1EB157A1251C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {3D42F00B-B811-48EC-BB7A-2D8047551422} + + + inf;inv;inx;mof;mc; + {4AE0C581-1388-4311-8C6C-9087111D70F5} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/registry/regfltr/ReadMe.md b/general/registry/regfltr/ReadMe.md new file mode 100644 index 00000000..731e8ca3 --- /dev/null +++ b/general/registry/regfltr/ReadMe.md @@ -0,0 +1,23 @@ +RegFltr Sample Driver +===================== + +The RegFltr sample shows how to write a [registry filter driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff545879).In addition to providing some basic examples, this sample demonstrates the following: + +- How to handle transactional registry operations. +- How and when to capture input parameters. +- Issues and workarounds for version 1.0 of registry filtering. +- Changes in version 1.1 of registry filtering. +- How to use version 1 of the [**REG\_CREATE\_KEY\_INFORMATION**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff560920) and [**REG\_OPEN\_KEY\_INFORMATION**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff560957) data structures. + +The RegFltr sample demonstrates the registry filtering system on Windows Vista, Windows Server 2008 and later versions of the Windows operating system. It does not work for Windows XP or Windows Server 2003. + +The RegFltr sample contains several examples of user-mode and kernel-mode registry-filtering operations. Each example comes with its own corresponding registry callback routine, and performs the following steps: + +1. Does some setup work. +2. Registers the callback routine. +3. Performs one or more registry operations. +4. Unregisters the callback routine. +5. Verifies that the sample completed correctly. + +The sample driver is a minimal driver that is not intended to be used on production systems. To keep the samples simple, the registry callback routines provided do not check for all possible situations and error conditions. This sample is designed to demonstrate typical scenarios and no other registry filtering driver is expected to be active. + diff --git a/general/registry/regfltr/exe/capture.c b/general/registry/regfltr/exe/capture.c new file mode 100644 index 00000000..aaef2555 --- /dev/null +++ b/general/registry/regfltr/exe/capture.c @@ -0,0 +1,151 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Capture.c + +Abstract: + + A sample that shows how to capture input parameters + +Environment: + + User mode only + +--*/ + +#include "regctrl.h" + + +VOID +CaptureSample( + ) +/*++ + +Routine Description: + + This sample shows how to capture input parameters when the registery + operation comes from user mode. + + The main part of this sample and a detailed explanation of why and how to + capture user mode parameters can be found in ..\sys\capture.c. The user + mode part of this sample simply calls RegSetValueEx and DeleteValue + since the REG_XXX_INFORMATION structure for these two operations are + only partially captured. + + See ..\sys\Capture.c for the callback routine used in this sample. + +Return Value: + + None + +--*/ +{ + LONG Res; + HRESULT hr; + DWORD ValueData = 0xDEADBEEF; + BOOL Result; + BOOL Success = FALSE; + DWORD BytesReturned; + REGISTER_CALLBACK_INPUT RegisterCallbackInput = {0}; + REGISTER_CALLBACK_OUTPUT RegisterCallbackOutput = {0}; + UNREGISTER_CALLBACK_INPUT UnRegisterCallbackInput = {0}; + + + InfoPrint(""); + InfoPrint("=== Capture Sample ===="); + + // + // Register callback + // + + RtlZeroMemory(RegisterCallbackInput.Altitude, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR)); + + hr = StringCbPrintf(RegisterCallbackInput.Altitude, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR), + CALLBACK_ALTITUDE); + + if (!SUCCEEDED(hr)) { + ErrorPrint("Copying altitude string failed. Error %d", hr); + goto Exit; + } + + RegisterCallbackInput.CallbackMode = CALLBACK_MODE_CAPTURE; + + Result = DeviceIoControl(g_Driver, + IOCTL_REGISTER_CALLBACK, + &RegisterCallbackInput, + sizeof(REGISTER_CALLBACK_INPUT), + &RegisterCallbackOutput, + sizeof(REGISTER_CALLBACK_OUTPUT), + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("RegisterCallback failed. Error %d", GetLastError()); + goto Exit; + } + + Success = TRUE; + + // + // Create a value and delete it. Both should be successful. + // + + Res = RegSetValueEx(g_RootKey, + VALUE_NAME, + 0, + REG_DWORD, + (BYTE *) &ValueData, + sizeof(ValueData)); + + if(Res != ERROR_SUCCESS) { + ErrorPrint("RegSetValueEx return unexpected status %d", Res); + Success = FALSE; + } + + Res = RegDeleteValue(g_RootKey, VALUE_NAME); + + if (Res != ERROR_SUCCESS) { + ErrorPrint("RegDeleteValue on original value returned unexpected status: %d", + Res); + Success = FALSE; + } + + // + // Unregister the callback + // + + UnRegisterCallbackInput.Cookie = RegisterCallbackOutput.Cookie; + + Result = DeviceIoControl(g_Driver, + IOCTL_UNREGISTER_CALLBACK, + &UnRegisterCallbackInput, + sizeof(UNREGISTER_CALLBACK_INPUT), + NULL, + 0, + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("UnRegisterCallback failed. Error %d", GetLastError()); + Success = FALSE; + } + + Exit: + + if (Success) { + InfoPrint("Capture Sample succeeded."); + } else { + ErrorPrint("Capture Sample failed."); + } + +} + diff --git a/general/registry/regfltr/exe/common.h b/general/registry/regfltr/exe/common.h new file mode 100644 index 00000000..7b2ca958 --- /dev/null +++ b/general/registry/regfltr/exe/common.h @@ -0,0 +1,168 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Common.h + +Abstract: + + Definitions common to both the driver and the executable. + +Environment: + + User and kernel mode + +--*/ + +#pragma once + +// +// Driver and device names. +// + +#define DRIVER_NAME L"RegFltr" +#define DRIVER_NAME_WITH_EXT L"RegFltr.sys" + +#define NT_DEVICE_NAME L"\\Device\\RegFltr" +#define DOS_DEVICES_LINK_NAME L"\\DosDevices\\RegFltr" +#define WIN32_DEVICE_NAME L"\\\\.\\RegFltr" + +// +// SDDL string used when creating the device. This string +// limits access to this driver to system and admins only. +// + +#define DEVICE_SDDL L"D:P(A;;GA;;;SY)(A;;GA;;;BA)" + +// +// IOCTLs exposed by the driver. +// + +#define IOCTL_DO_KERNELMODE_SAMPLES CTL_CODE (FILE_DEVICE_UNKNOWN, (0x800 + 0), METHOD_BUFFERED, FILE_SPECIAL_ACCESS) +#define IOCTL_REGISTER_CALLBACK CTL_CODE (FILE_DEVICE_UNKNOWN, (0x800 + 1), METHOD_BUFFERED, FILE_SPECIAL_ACCESS) +#define IOCTL_UNREGISTER_CALLBACK CTL_CODE (FILE_DEVICE_UNKNOWN, (0x800 + 2), METHOD_BUFFERED, FILE_SPECIAL_ACCESS) +#define IOCTL_GET_CALLBACK_VERSION CTL_CODE (FILE_DEVICE_UNKNOWN, (0x800 + 3), METHOD_BUFFERED, FILE_SPECIAL_ACCESS) + +// +// Common definitions +// + +#define ROOT_KEY_ABS_PATH L"\\REGISTRY\\MACHINE\\Software\\_RegFltrRoot" +#define ROOT_KEY_REL_PATH L"Software\\_RegFltrRoot" +#define KEY_NAME L"_RegFltrKey" +#define MODIFIED_KEY_NAME L"_RegFltrModifiedKey" +#define NOT_MODIFIED_KEY_NAME L"_RegFltrNotModifiedKey" +#define VALUE_NAME L"_RegFltrValue" +#define MODIFIED_VALUE_NAME L"_RegFltrModifiedValue" +#define NOT_MODIFIED_VALUE_NAME L"_RegFltrNotModifiedValue " + +#define CALLBACK_LOW_ALTITUDE L"380000" +#define CALLBACK_ALTITUDE L"380010" +#define CALLBACK_HIGH_ALTITUDE L"380020" + +#define MAX_ALTITUDE_BUFFER_LENGTH 10 + +// +// List of callback modes +// +typedef enum _CALLBACK_MODE { + CALLBACK_MODE_PRE_NOTIFICATION_BLOCK, + CALLBACK_MODE_PRE_NOTIFICATION_BYPASS, + CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_ERROR, + CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_SUCCESS, + CALLBACK_MODE_TRANSACTION_REPLAY, + CALLBACK_MODE_TRANSACTION_ENLIST, + CALLBACK_MODE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE, + CALLBACK_MODE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION, + CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR, + CALLBACK_MODE_SET_CALL_CONTEXT, + CALLBACK_MODE_SET_OBJECT_CONTEXT, + CALLBACK_MODE_CAPTURE, + CALLBACK_MODE_VERSION_BUGCHECK, + CALLBACK_MODE_VERSION_CREATE_OPEN_V1, +} CALLBACK_MODE; + + +// +// List of kernel mode samples +// +typedef enum _KERNELMODE_SAMPLE { + KERNELMODE_SAMPLE_PRE_NOTIFICATION_BLOCK = 0, + KERNELMODE_SAMPLE_PRE_NOTIFICATION_BYPASS, + KERNELMODE_SAMPLE_POST_NOTIFICATION_OVERRIDE_ERROR, + KERNELMODE_SAMPLE_POST_NOTIFICATION_OVERRIDE_SUCCESS, + KERNELMODE_SAMPLE_TRANSACTION_REPLAY, + KERNELMODE_SAMPLE_TRANSACTION_ENLIST, + KERNELMODE_SAMPLE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE, + KERNELMODE_SAMPLE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION, + KERNELMODE_SAMPLE_SET_CALL_CONTEXT, + KERNELMODE_SAMPLE_SET_OBJECT_CONTEXT, + KERNELMODE_SAMPLE_VERSION_CREATE_OPEN_V1, + MAX_KERNELMODE_SAMPLES +} KERNELMODE_SAMPLE; + + +// +// Input and output data structures for the various driver IOCTLs +// + +typedef struct _REGISTER_CALLBACK_INPUT { + + // + // specifies the callback mode for the callback context + // + CALLBACK_MODE CallbackMode; + + // + // specifies the altitude to register the callback at + // + WCHAR Altitude[MAX_ALTITUDE_BUFFER_LENGTH]; + +} REGISTER_CALLBACK_INPUT, *PREGISTER_CALLBACK_INPUT; + +typedef struct _REGISTER_CALLBACK_OUTPUT { + + // + // receives the cookie value from registering the callback + // + LARGE_INTEGER Cookie; + +} REGISTER_CALLBACK_OUTPUT, *PREGISTER_CALLBACK_OUTPUT; + + +typedef struct _UNREGISTER_CALLBACK_INPUT { + // + // specifies the cookie value for the callback + // + LARGE_INTEGER Cookie; + +} UNREGISTER_CALLBACK_INPUT, *PUNREGISTER_CALLBACK_INPUT; + + +typedef struct _GET_CALLBACK_VERSION_OUTPUT { + + // + // Receives the version number of the registry callback + // + ULONG MajorVersion; + ULONG MinorVersion; + +} GET_CALLBACK_VERSION_OUTPUT, *PGET_CALLBACK_VERSION_OUTPUT; + + +typedef struct _DO_KERNELMODE_SAMPLES_OUTPUT { + + // + // An array that receives the results of the kernel mode samples. + // + BOOLEAN SampleResults[MAX_KERNELMODE_SAMPLES]; + +} DO_KERNELMODE_SAMPLES_OUTPUT, *PDO_KERNELMODE_SAMPLES_OUTPUT; + + diff --git a/general/registry/regfltr/exe/post.c b/general/registry/regfltr/exe/post.c new file mode 100644 index 00000000..d10a7a22 --- /dev/null +++ b/general/registry/regfltr/exe/post.c @@ -0,0 +1,366 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Post.c + +Abstract: + + Samples that show what callbacks can do during the post-notification + phase. + +Environment: + + User mode only + +--*/ + + +#include "regctrl.h" + +/*++ + + In registry callback version 1.0, there is a bug with post-notification + processing and multiple registry filter drivers that can break the samples + here. It is fixed with version 1.1. + + The bug occurs when a driver blocks or bypasses a registry operation in the + pre-notification phase. Even though the processing of the operation stops + there, registry filter drivers registered at higher altitudes will still + get a post-notification for the operation. If the higher altitude driver + tries to change the status of the operation from failure to success or + vice versa, this change will be ignored and the status returned + will be the status returned by the driver who bypassed or blocked the + operation during the pre-notification phase. + + For more information on how notification processing works with multiple + registry filter drivers registered see ..\sys\MultiAlt.c + + For more information on issues in version 1.0 and changes in version 1.1 + see ..\sys\Version.c + + Beginning with Windows 8.1, it is no longer possible to pass the object + provided to a RegNtPostCreateKeyEx or RegNtPostOpenKeyEx callout to + ObOpenObjectByPointer. To work around this, filters should perform all + create key or open key processing in a RegNtPreCreateKeyEx or + RegNtPreOpenKeyEx callout. If for any reason the desired processing cannot + be performed in a RegNtPreCreateKeyEx or RegNtPreOpenKeyEx callout, then + use CmSetCallbackObjectContext to tag a newly-created or newly-opened key + as unprocessed and process it in the pre-operation callback for a + subsequent operation. + +--*/ + + +VOID +PostNotificationOverrideSuccessSample( + ) +/*++ + +Routine Description: + + This sample shows how registry callbacks can fail a registry operation + in the post-notification phase. + + Two values are created. The creates normally should succeeded, but one + is intercepted by the callback and failed with ERROR_ACCESS_DENIED. + + See ..\sys\Post.c for the callback routine used in this sample. + +--*/ +{ + LONG Res; + HRESULT hr; + BOOL Result; + BOOL Success = FALSE; + DWORD BytesReturned; + DWORD ValueData = 0xDEADBEEF; + REGISTER_CALLBACK_INPUT RegisterCallbackInput = {0}; + REGISTER_CALLBACK_OUTPUT RegisterCallbackOutput = {0}; + UNREGISTER_CALLBACK_INPUT UnRegisterCallbackInput = {0}; + + + InfoPrint(""); + InfoPrint("=== Post-Notification Override Success Sample ===="); + + // + // Register a callback with the specified callback mode and altitude. + // + + RtlZeroMemory(RegisterCallbackInput.Altitude, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR)); + + hr = StringCbPrintf(RegisterCallbackInput.Altitude, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR), + CALLBACK_ALTITUDE); + + if (!SUCCEEDED(hr)) { + ErrorPrint("Copying altitude string failed. Error %d", hr); + goto Exit; + } + + RegisterCallbackInput.CallbackMode = CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_SUCCESS; + + Result = DeviceIoControl(g_Driver, + IOCTL_REGISTER_CALLBACK, + &RegisterCallbackInput, + sizeof(REGISTER_CALLBACK_INPUT), + &RegisterCallbackOutput, + sizeof(REGISTER_CALLBACK_OUTPUT), + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("RegisterCallback failed. Error %d", GetLastError()); + goto Exit; + } + + Success = TRUE; + + // + // Set two values. + // Setting the "not modified" value will succeed. + // Setting the other value will fail with file not found. + // + + Res = RegSetValueEx(g_RootKey, + NOT_MODIFIED_VALUE_NAME, + 0, + REG_DWORD, + (BYTE *) &ValueData, + sizeof(ValueData)); + + if(Res != ERROR_SUCCESS) { + ErrorPrint("RegSetValueEx return unexpected status %d", Res); + Success = FALSE; + } + + + Res = RegSetValueEx(g_RootKey, + VALUE_NAME, + 0, + REG_DWORD, + (BYTE *) &ValueData, + sizeof(ValueData)); + + if(Res != ERROR_ACCESS_DENIED) { + ErrorPrint("RegSetValueEx return unexpected status %d", Res); + Success = FALSE; + } + + // + // Unregister the callback + // + + UnRegisterCallbackInput.Cookie = RegisterCallbackOutput.Cookie; + + Result = DeviceIoControl(g_Driver, + IOCTL_UNREGISTER_CALLBACK, + &UnRegisterCallbackInput, + sizeof(UNREGISTER_CALLBACK_INPUT), + NULL, + 0, + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("UnRegisterCallback failed. Error %d", GetLastError()); + Success = FALSE; + } + + + // + // Verify that the set value call was failed by + // checking that the value with VALUE_NAME does not + // exist. + // + + Res = RegDeleteValue(g_RootKey, VALUE_NAME); + + if (Res != ERROR_FILE_NOT_FOUND) { + ErrorPrint("RegDeleteValue on value returned unexpected status: %d", + Res); + Success = FALSE; + } + + Exit: + + RegDeleteValue(g_RootKey, VALUE_NAME); + RegDeleteValue(g_RootKey, NOT_MODIFIED_VALUE_NAME); + + if (Success) { + InfoPrint("Post-Notification Override Success Sample succeeded."); + } else { + ErrorPrint("Post-Notification Override Success Sample FAILED."); + } + +} + + +VOID +PostNotificationOverrideErrorSample( + ) +/*++ + +Routine Description: + + This sample shows how a registry callback can change a failed registry + operation into a successful operation in the post-notification phase. + + A key that does not exist is opened. The opens should fail, but it is + intercepted by the callback and the open is redirected to a key that + does exist. + + See ..\sys\Post.c for the callback routine used in this sample. + +Return Value: + + None + +--*/ +{ + + LONG Res; + HRESULT hr; + BOOL Success = FALSE; + BOOL Result; + HKEY Key = NULL; + HKEY ModifiedKey = NULL; + DWORD BytesReturned; + REGISTER_CALLBACK_INPUT RegisterCallbackInput = {0}; + REGISTER_CALLBACK_OUTPUT RegisterCallbackOutput = {0}; + UNREGISTER_CALLBACK_INPUT UnRegisterCallbackInput = {0}; + + InfoPrint(""); + InfoPrint("=== Post-Notification Override Error Sample ===="); + + // + // Create a key with name MODIFIED_KEY_NAME + // + + Res = RegCreateKeyEx(g_RootKey, + MODIFIED_KEY_NAME, + 0, + NULL, + 0, + KEY_ALL_ACCESS, + NULL, + &ModifiedKey, + NULL); + + if (Res != ERROR_SUCCESS) { + ErrorPrint("RegCreateKeyEx returned unexpected error %d", Res); + goto Exit; + } + + // + // Now try to open a key by KEY_NAME which does not exist. Verify that + // this fails. + // + + Res = RegOpenKeyEx(g_RootKey, + KEY_NAME, + 0, + KEY_ALL_ACCESS, + &Key); + + if (Res != ERROR_FILE_NOT_FOUND) { + ErrorPrint("RegOpenKeyEx returned unexpected error %d", Res); + goto Exit; + } + + // + // Register a callback with the specified callback mode and altitude. + // + + RtlZeroMemory(RegisterCallbackInput.Altitude, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR)); + + hr = StringCbPrintf(RegisterCallbackInput.Altitude, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR), + CALLBACK_ALTITUDE); + + if (!SUCCEEDED(hr)) { + ErrorPrint("Copying altitude string failed. Error %d", hr); + goto Exit; + } + + + RegisterCallbackInput.CallbackMode = CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_ERROR; + + Result = DeviceIoControl(g_Driver, + IOCTL_REGISTER_CALLBACK, + &RegisterCallbackInput, + sizeof(REGISTER_CALLBACK_INPUT), + &RegisterCallbackOutput, + sizeof(REGISTER_CALLBACK_OUTPUT), + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("RegisterCallback failed. Error %d", GetLastError()); + goto Exit; + } + + Success = TRUE; + + // + // Open key again. The callback will intercept this and make it succeed. + // + + Res = RegOpenKeyEx(g_RootKey, + KEY_NAME, + 0, + KEY_ALL_ACCESS, + &Key); + + if (Res != ERROR_SUCCESS) { + ErrorPrint("RegOpenKeyEx returned unexpected error %d", Res); + Success = FALSE; + } + + // + // Unregister the callback + // + + UnRegisterCallbackInput.Cookie = RegisterCallbackOutput.Cookie; + + Result = DeviceIoControl(g_Driver, + IOCTL_UNREGISTER_CALLBACK, + &UnRegisterCallbackInput, + sizeof(UNREGISTER_CALLBACK_INPUT), + NULL, + 0, + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("UnRegisterCallback failed. Error %d", GetLastError()); + Success = FALSE; + } + + Exit: + + if (Key != NULL) { + RegCloseKey(Key); + } + if (ModifiedKey != NULL) { + RegCloseKey(ModifiedKey); + } + RegDeleteKey(g_RootKey, KEY_NAME); + RegDeleteKey(g_RootKey, MODIFIED_KEY_NAME); + + if (Success) { + InfoPrint("Post-Notification Override Error Sample succeeded."); + } else { + ErrorPrint("Post-Notification Override Error Sample FAILED."); + } + +} + diff --git a/general/registry/regfltr/exe/pre.c b/general/registry/regfltr/exe/pre.c new file mode 100644 index 00000000..14d7e57e --- /dev/null +++ b/general/registry/regfltr/exe/pre.c @@ -0,0 +1,428 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Pre.c + +Abstract: + + Samples that show what callbacks can do during the pre-notification + phase. + +Environment: + + User mode only + +--*/ + + + +#include "regctrl.h" + + +VOID +PreNotificationBlockSample( + ) +/*++ + +Routine Description: + + This sample shows how to block a registry operation in the + pre-notification phase. + + Two keys are created. The create operations should succeed, but one + is intercepted by the callback and failed with ERROR_ACCESS_DENIED. + The same is done for two values. + + See ..\sys\Pre.c for the callback routine used in this sample. + +--*/ +{ + + LONG Res; + HRESULT hr; + HKEY Key = NULL; + HKEY NotModifiedKey = NULL; + DWORD ValueData = 0xDEADBEEF; + DWORD BytesReturned; + BOOL Result; + BOOL Success = FALSE; + REGISTER_CALLBACK_INPUT RegisterCallbackInput = {0}; + REGISTER_CALLBACK_OUTPUT RegisterCallbackOutput = {0}; + UNREGISTER_CALLBACK_INPUT UnRegisterCallbackInput = {0}; + + InfoPrint(""); + InfoPrint("=== Pre-Notification Block Sample ===="); + + // + // Register callback + // + + RtlZeroMemory(RegisterCallbackInput.Altitude, MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR)); + hr = StringCbPrintf(RegisterCallbackInput.Altitude, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR), + CALLBACK_ALTITUDE); + + if (!SUCCEEDED(hr)) { + ErrorPrint("Copying altitude string failed. Error %d", hr); + goto Exit; + } + + RegisterCallbackInput.CallbackMode = CALLBACK_MODE_PRE_NOTIFICATION_BLOCK; + + Result = DeviceIoControl(g_Driver, + IOCTL_REGISTER_CALLBACK, + &RegisterCallbackInput, + sizeof(REGISTER_CALLBACK_INPUT), + &RegisterCallbackOutput, + sizeof(REGISTER_CALLBACK_OUTPUT), + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("RegisterCallback failed. Error %d", GetLastError()); + goto Exit; + } + + Success = TRUE; + + // + // Create two keys. + // Creating the "not modified" key will succeed. + // Creating the other key will fail with ERROR_ACCESS_DENIED + // + + // + // NOTE: In the kernel debugger output, you will see 3 pre-notifications + // for create key even though we only call create key twice. + // You will also see the message that create key is blocked twice. + // + // Kd output: + // + // RegFltr: Callback: Altitude-380010, NotifyClass-RegNtPreCreateKeyEx. + // RegFltr: Callback: Altitude-380010, NotifyClass-RegNtPostCreateKeyEx. + // RegFltr: Callback: Altitude-380010, NotifyClass-RegNtPreCreateKeyEx. + // RegFltr: Callback: Create key _RegFltrKey blocked. + // RegFltr: Callback: Altitude-380010, NotifyClass-RegNtPreCreateKeyEx. + // RegFltr: Callback: Create key _RegFltrKey blocked. + // + // The reason this happens is that RegCreateKeyEx is more than just a + // wrapper around NtCreateKey. If the call to NtCreateKey fails, + // RegCreateKeyEx will retry the call in slightly different ways + // depending on the error returned. + // + + Res = RegCreateKeyEx(g_RootKey, + NOT_MODIFIED_KEY_NAME, + 0, + L"Regfltr_test_class", + 0, + KEY_ALL_ACCESS, + NULL, + &NotModifiedKey, + NULL); + + if (Res != ERROR_SUCCESS) { + ErrorPrint("RegCreateKeyEx returned unexpected error %d", Res); + Success = FALSE; + } + + Res = RegCreateKeyEx(g_RootKey, + KEY_NAME, + 0, + L"Regfltr_test_class", + 0, + KEY_ALL_ACCESS, + NULL, + &Key, + NULL); + + if (Res != ERROR_ACCESS_DENIED) { + ErrorPrint("RegCreateKeyEx returned unexpected error %d", Res); + Success = FALSE; + } + + // + // Set two values. + // Setting the "not modified" value will succeed. + // Setting the other value will fail with ERROR_ACCESS_DENIED. + // + + Res = RegSetValueEx(g_RootKey, + NOT_MODIFIED_VALUE_NAME, + 0, + REG_DWORD, + (BYTE *) &ValueData, + sizeof(ValueData)); + + if(Res != ERROR_SUCCESS) { + ErrorPrint("RegSetValueEx return unexpected status %d", Res); + Success = FALSE; + } + + Res = RegSetValueEx(g_RootKey, + VALUE_NAME, + 0, + REG_DWORD, + (BYTE *) &ValueData, + sizeof(ValueData)); + + if(Res != ERROR_ACCESS_DENIED) { + ErrorPrint("RegSetValueEx return unexpected status %d", Res); + Success = FALSE; + } + + // + // Unregister the callback + // + + UnRegisterCallbackInput.Cookie = RegisterCallbackOutput.Cookie; + + Result = DeviceIoControl(g_Driver, + IOCTL_UNREGISTER_CALLBACK, + &UnRegisterCallbackInput, + sizeof(UNREGISTER_CALLBACK_INPUT), + NULL, + 0, + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("UnRegisterCallback failed. Error %d", GetLastError()); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + RegCloseKey(Key); + } + RegDeleteKey(g_RootKey, KEY_NAME); + + if (NotModifiedKey != NULL) { + RegCloseKey(NotModifiedKey); + } + RegDeleteKey(g_RootKey, NOT_MODIFIED_KEY_NAME); + + RegDeleteValue(g_RootKey, NOT_MODIFIED_VALUE_NAME); + RegDeleteValue(g_RootKey, VALUE_NAME); + + if (Success) { + InfoPrint("Pre-Notification Block Sample succeeded."); + } else { + ErrorPrint("Pre-Notification Block Sample FAILED."); + } + +} + + +VOID +PreNotificationBypassSample( + ) +/*++ + +Routine Description: + + This sample shows how to bypass a registry operation so that the CM does + not process the operation. Unlike block, an operation that is bypassed + is still considered successful so the callback must provide the caller + with what the CM would have provided. + + A key and a value are created. However both operations are bypassed by the + callback so that the key and value actually created have different names + than would is expected. + + See ..\sys\Pre.c for the callback routine used in this sample. + +Return Value: + + None + +--*/ +{ + LONG Res; + HRESULT hr; + HKEY Key = NULL; + DWORD ValueData = 0xDEADBEEF; + BOOL Result; + BOOL Success = FALSE; + DWORD BytesReturned; + REGISTER_CALLBACK_INPUT RegisterCallbackInput = {0}; + REGISTER_CALLBACK_OUTPUT RegisterCallbackOutput = {0}; + UNREGISTER_CALLBACK_INPUT UnRegisterCallbackInput = {0}; + + + InfoPrint(""); + InfoPrint("=== Pre-Notification Bypass Sample ===="); + + // + // Register callback + // + + RtlZeroMemory(RegisterCallbackInput.Altitude, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR)); + + hr = StringCbPrintf(RegisterCallbackInput.Altitude, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR), + CALLBACK_ALTITUDE); + + if (!SUCCEEDED(hr)) { + ErrorPrint("Copying altitude string failed. Error %d", hr); + goto Exit; + } + + RegisterCallbackInput.CallbackMode = CALLBACK_MODE_PRE_NOTIFICATION_BYPASS; + + Result = DeviceIoControl(g_Driver, + IOCTL_REGISTER_CALLBACK, + &RegisterCallbackInput, + sizeof(REGISTER_CALLBACK_INPUT), + &RegisterCallbackOutput, + sizeof(REGISTER_CALLBACK_OUTPUT), + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("RegisterCallback failed. Error %d", GetLastError()); + goto Exit; + } + + Success = TRUE; + + // + // Create a key and create a value. Both should succeed. + // + + Res = RegCreateKeyEx(g_RootKey, + KEY_NAME, + 0, + L"Regfltr_test_class", + 0, + KEY_ALL_ACCESS, + NULL, + &Key, + NULL); + + if (Res != ERROR_SUCCESS) { + ErrorPrint("RegCreateKeyEx returned unexpected error %d", Res); + Success = FALSE; + } + + Res = RegSetValueEx(g_RootKey, + VALUE_NAME, + 0, + REG_DWORD, + (BYTE *) &ValueData, + sizeof(ValueData)); + + if(Res != ERROR_SUCCESS) { + ErrorPrint("RegSetValueEx return unexpected status %d", Res); + Success = FALSE; + } + + // + // Unregister the callback + // + + UnRegisterCallbackInput.Cookie = RegisterCallbackOutput.Cookie; + + Result = DeviceIoControl(g_Driver, + IOCTL_UNREGISTER_CALLBACK, + &UnRegisterCallbackInput, + sizeof(UNREGISTER_CALLBACK_INPUT), + NULL, + 0, + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("UnRegisterCallback failed. Error %d", GetLastError()); + Success = FALSE; + } + + // + // Check that a key with the expected name KEY_NAME cannot be found + // but a key with the "modified" name can be found. + // + + if (Key != NULL) { + RegCloseKey(Key); + } + + Res = RegOpenKeyEx(g_RootKey, + KEY_NAME, + 0, + KEY_ALL_ACCESS, + &Key); + + if (Res != ERROR_FILE_NOT_FOUND) { + ErrorPrint("RegOpenKeyEx returned unexpected error: %d", Res); + if (Key != NULL) { + RegCloseKey(Key); + Key = NULL; + } + Success = FALSE; + } + + Res = RegOpenKeyEx(g_RootKey, + MODIFIED_KEY_NAME, + 0, + KEY_ALL_ACCESS, + &Key); + + if (Res != ERROR_SUCCESS) { + ErrorPrint("RegOpenKeyEx returned unexpected error: %d", Res); + Success = FALSE; + } + + // + // Do the same check by trying to delete a value with VALUE_NAME and + // with the "modified" name. + // + + Res = RegDeleteValue(g_RootKey, VALUE_NAME); + + if (Res != ERROR_FILE_NOT_FOUND) { + ErrorPrint("RegDeleteValue on original value returned unexpected status: %d", + Res); + Success = FALSE; + } + + Res = RegDeleteValue(g_RootKey, MODIFIED_VALUE_NAME); + + if (Res != ERROR_SUCCESS) { + ErrorPrint("RegDeleteValue on original value returned unexpected status: %d", + Res); + Success = FALSE; + } + + Exit: + + if (Success) { + InfoPrint("Pre-Notification Bypass Sample succeeded."); + } else { + ErrorPrint("Pre-Notification Bypass Sample failed."); + } + + // + // Clean up + // + + if (Key != NULL) { + RegCloseKey(Key); + } + RegDeleteKey(g_RootKey, KEY_NAME); + RegDeleteKey(g_RootKey, MODIFIED_KEY_NAME); + +} + diff --git a/general/registry/regfltr/exe/regctrl.c b/general/registry/regfltr/exe/regctrl.c new file mode 100644 index 00000000..bf486e77 --- /dev/null +++ b/general/registry/regfltr/exe/regctrl.c @@ -0,0 +1,288 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + regctrl.c + +Abstract: + + Invokes the usermode and kernel mode callback samples. + +Environment: + + User mode Win32 console application + +Revision History: + +--*/ + +#include "regctrl.h" + +// +// Global variables +// + +// +// Handle to the driver +// +HANDLE g_Driver; + +// +// Handle to the root test key +// +HKEY g_RootKey; + +// +// Version number for the registry callback +// +ULONG g_MajorVersion; +ULONG g_MinorVersion; + + + +BOOL +GetCallbackVersion(); + +VOID +DoKernelModeSamples(); + +VOID +DoUserModeSamples(); + +LPCWSTR +GetKernelModeSampleName ( + _In_ KERNELMODE_SAMPLE Sample + ); + + +VOID __cdecl +wmain( + _In_ ULONG argc, + _In_reads_(argc) LPCWSTR argv[] + ) +{ + + BOOL Result; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + Result = UtilLoadDriver(DRIVER_NAME, + DRIVER_NAME_WITH_EXT, + WIN32_DEVICE_NAME, + &g_Driver); + + if (Result != TRUE) { + ErrorPrint("UtilLoadDriver failed, exiting..."); + exit(1); + } + + printf("\n"); + printf("Starting Callback samples...\n"); + printf("\n"); + printf("To get more detailed output from the sample, do either one of these steps:\n"); + printf("\n"); + printf("\tA. In kernel debugger: \n"); + printf("\tkd> ed nt!Kd_IHVDRIVER_Mask 0x8\n\n"); + printf("\tB. Run this script and reboot:\n"); + printf("\treg add \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Debug Print Filter\" /v IHVDRIVER /t REG_DWORD /d 0x8\n\n"); + + // + // Get the registry callback version to determine what samples can + // run on the system. + // + + if (GetCallbackVersion()) { + InfoPrint("Callback version is %u.%u", g_MajorVersion, g_MinorVersion); + } + + DoKernelModeSamples(); + DoUserModeSamples(); + + UtilUnloadDriver(g_Driver, NULL, DRIVER_NAME); + +} + + +BOOL +GetCallbackVersion( + ) +/*++ + +Routine Description: + + This routine asks the driver for the registry callback version and + stores it in the global variables g_MajorVersion and g_MinorVersion. + +--*/ +{ + + DWORD BytesReturned = 0; + BOOL Result; + GET_CALLBACK_VERSION_OUTPUT Output = {0}; + + Result = DeviceIoControl(g_Driver, + IOCTL_GET_CALLBACK_VERSION, + NULL, + 0, + &Output, + sizeof(GET_CALLBACK_VERSION_OUTPUT), + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("DeviceIoControl for GET_CALLBACK_VERSION failed, error %d\n", GetLastError()); + return FALSE; + } + + g_MajorVersion = Output.MajorVersion; + g_MinorVersion = Output.MinorVersion; + + return TRUE; + +} + +VOID +DoUserModeSamples( + ) +/*++ + +Routine Description: + + Creates the callback root test key and calls the usermode samples. + +--*/ +{ + + LONG Res; + + Res = RegCreateKeyEx(HKEY_LOCAL_MACHINE, + ROOT_KEY_REL_PATH, + 0, + NULL, + 0, + KEY_ALL_ACCESS, + NULL, + &g_RootKey, + NULL); + + if (Res != ERROR_SUCCESS) { + ErrorPrint("Creating root key failed. Error %d", Res); + goto Exit; + } + + PreNotificationBlockSample(); + PreNotificationBypassSample(); + PostNotificationOverrideSuccessSample(); + PostNotificationOverrideErrorSample(); + CaptureSample(); + + Exit: + + if (g_RootKey != NULL) { + RegCloseKey(g_RootKey); + } + RegDeleteKey(HKEY_LOCAL_MACHINE, ROOT_KEY_REL_PATH); + +} + + +VOID +DoKernelModeSamples( + ) +/*++ + +Routine Description: + + Tells the driver to run the kernel mode samples and prints out the + results. + +--*/ +{ + + UINT Index; + DWORD BytesReturned = 0; + BOOL Result; + DO_KERNELMODE_SAMPLES_OUTPUT Output = {0}; + + Result = DeviceIoControl (g_Driver, + IOCTL_DO_KERNELMODE_SAMPLES, + NULL, + 0, + &Output, + sizeof(DO_KERNELMODE_SAMPLES_OUTPUT), + &BytesReturned, + NULL); + + if (Result != TRUE) { + ErrorPrint("DeviceIoControl for DO_KERNELMODE_SAMPLES failed, error %d\n", GetLastError()); + return; + } + + InfoPrint(""); + InfoPrint("=== Results of KernelMode Samples ==="); + + for (Index = 0; Index < MAX_KERNELMODE_SAMPLES; Index++) { + InfoPrint("\t%S: %s", + GetKernelModeSampleName(Index), + Output.SampleResults[Index]? "Succeeded" : "FAILED"); + } + +} + + + +LPCWSTR +GetKernelModeSampleName ( + _In_ KERNELMODE_SAMPLE Sample + ) +/*++ + +Routine Description: + + Converts from a KERNELMODE_SAMPLE value to a string + +Arguments: + + Sample - value that identifies a kernel mode sample + +Return Value: + + Returns a string of the name of Sample. + +--*/ +{ + switch (Sample) { + case KERNELMODE_SAMPLE_PRE_NOTIFICATION_BLOCK: + return L"Pre-Notification Block Sample"; + case KERNELMODE_SAMPLE_PRE_NOTIFICATION_BYPASS: + return L"Pre-Notification Bypass Sample"; + case KERNELMODE_SAMPLE_POST_NOTIFICATION_OVERRIDE_SUCCESS: + return L"Post-Notification Override Success Sample"; + case KERNELMODE_SAMPLE_POST_NOTIFICATION_OVERRIDE_ERROR: + return L"Post-Notification Override Error Sample"; + case KERNELMODE_SAMPLE_TRANSACTION_REPLAY: + return L"Transaction Replay Sample"; + case KERNELMODE_SAMPLE_TRANSACTION_ENLIST: + return L"Transaction Enlist Sample"; + case KERNELMODE_SAMPLE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE: + return L"Multiple Altitude Block During Pre Sample"; + case KERNELMODE_SAMPLE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION: + return L"Multiple Altitude Internal Invocation Sample"; + case KERNELMODE_SAMPLE_SET_CALL_CONTEXT: + return L"Set Call Context Sample"; + case KERNELMODE_SAMPLE_SET_OBJECT_CONTEXT: + return L"Set Object Context Sample"; + case KERNELMODE_SAMPLE_VERSION_CREATE_OPEN_V1: + return L"Create Open V1 Sample"; + default: + return L"Unsupported Kernel Mode Sample"; + } +} + diff --git a/general/registry/regfltr/exe/regctrl.h b/general/registry/regfltr/exe/regctrl.h new file mode 100644 index 00000000..798b29fc --- /dev/null +++ b/general/registry/regfltr/exe/regctrl.h @@ -0,0 +1,108 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + regctrl.h + +Environment: + + User mode only + +--*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common.h" + +// +// Utility macro +// + +#define ARRAY_LENGTH(array) (sizeof (array) / sizeof (array[0])) + +// +// Logging macros +// + +#define InfoPrint(str, ...) \ + printf(##str"\n", \ + __VA_ARGS__) + +#define ErrorPrint(str, ...) \ + printf("ERROR: %u: "##str"\n", \ + __LINE__, \ + __VA_ARGS__) + +// +// Global variables +// + +// +// Handle to the driver +// +extern HANDLE g_Driver; + +// +// Handle to the root test key +// +extern HKEY g_RootKey; + +// +// Version number for the registry callback +// +extern ULONG g_MajorVersion; +extern ULONG g_MinorVersion; + + +// +// The user mode samples +// + +VOID +PreNotificationBlockSample(); + +VOID +PreNotificationBypassSample(); + +VOID +PostNotificationOverrideSuccessSample(); + +VOID +PostNotificationOverrideErrorSample(); + +VOID +CaptureSample(); + +// +// Utility routines to load and unload the driver +// + +BOOL +UtilLoadDriver( + _In_ LPTSTR szDriverNameNoExt, + _In_ LPTSTR szDriverNameWithExt, + _In_ LPTSTR szWin32DeviceName, + _Out_ HANDLE *pDriver + ); + +BOOL +UtilUnloadDriver( + _In_ HANDLE hDriver, + _In_opt_ SC_HANDLE hSCM, + _In_ LPTSTR szDriverNameNoExt + ); + + diff --git a/general/registry/regfltr/exe/regctrl.rc b/general/registry/regfltr/exe/regctrl.rc new file mode 100644 index 00000000..5e0506fa --- /dev/null +++ b/general/registry/regfltr/exe/regctrl.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Registry Filter Test App" +#define VER_INTERNALNAME_STR "regctrl.exe" +#define VER_ORIGINALFILENAME_STR "regctrl.exe" + +#include "common.ver" \ No newline at end of file diff --git a/general/registry/regfltr/exe/regctrl.vcxproj b/general/registry/regfltr/exe/regctrl.vcxproj new file mode 100644 index 00000000..a0a9e234 --- /dev/null +++ b/general/registry/regfltr/exe/regctrl.vcxproj @@ -0,0 +1,204 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {F3CBF3E0-E60F-409E-9402-A508C8008EB7} + $(MSBuildProjectName) + Debug + Win32 + {7DBA6C3C-01A4-4CB0-A5A2-0D1E014C3F17} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + regctrl + + + regctrl + + + regctrl + + + regctrl + + + + Disabled + true + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);advapi32.lib;ntdll.lib;kernel32.lib + + + + + Disabled + true + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);advapi32.lib;ntdll.lib;kernel32.lib + + + + + Disabled + true + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);advapi32.lib;ntdll.lib;kernel32.lib + + + + + Disabled + true + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);advapi32.lib;ntdll.lib;kernel32.lib + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/registry/regfltr/exe/regctrl.vcxproj.Filters b/general/registry/regfltr/exe/regctrl.vcxproj.Filters new file mode 100644 index 00000000..33b9e1e1 --- /dev/null +++ b/general/registry/regfltr/exe/regctrl.vcxproj.Filters @@ -0,0 +1,39 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {9713D19A-A900-45FB-AA68-19BDFD105192} + + + h;hpp;hxx;hm;inl;inc;xsd + {8A005B43-A062-4866-BEC2-037F5820B234} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {768BFEC7-A3C9-42EA-BCB0-30162CB4DA90} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/registry/regfltr/exe/util.c b/general/registry/regfltr/exe/util.c new file mode 100644 index 00000000..34d24c38 --- /dev/null +++ b/general/registry/regfltr/exe/util.c @@ -0,0 +1,707 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + util.c + +Abstract: + + Utility routines to dynamically load and unload a Windows NT kernel-mode + driver using the Service Control Manager APIs. + +Environment: + + User mode only + +--*/ + + + +#include "regctrl.h" + + +BOOL +UtilCreateService( + _In_ SC_HANDLE hSCM, + _In_ LPTSTR szDriverName, + _In_ LPTSTR szDriverPath +); + +BOOL +UtilStartService( + _In_ SC_HANDLE hSCM, + _In_ LPTSTR szDriverName +); + +BOOL +UtilStopService( + _In_ SC_HANDLE hSCM, + _In_ LPTSTR szDriverName +); + +BOOL +UtilDeleteService( + _In_ SC_HANDLE hSCM, + _In_ LPTSTR szDriverName +); + +BOOL +UtilOpenDevice( + _In_ LPTSTR szWin32DeviceName, + _Out_ HANDLE * phDevice); + +BOOL +UtilGetServiceState( + _In_ SC_HANDLE hService, + _Out_ DWORD * State); + +BOOL +UtilWaitForServiceState( + _In_ SC_HANDLE hService, + _In_ DWORD State); + + +BOOL +UtilLoadDriver( + _In_ LPTSTR szDriverName, + _In_ LPTSTR szDriverFileName, + _In_ LPTSTR szWin32DeviceName, + _Out_ HANDLE *pDriver + ) +/*++ + +Routine Description: + + This routine uses the Service Control Manager APIs to create an entry + for a standalone driver. It then opens a handle to the driver. + The driver is assumed to be in the current directory. + + NOTE: This routine creates an entry for a standalone driver. If this + is modified for use with a driver that requires a Tag, Group, and/or + Dependencies, it may be necessary to query the registry for existing + driver information (in order to determine a unique Tag, etc.). + +Arguments: + + szDriverName - name of the driver (no extension) + + szDriverFileName - name of driver (with extension) + + szWin32DeviceName - Name of driver (no extension) prefixed with \\.\ + + pDriver - pointer to a variable that receives the handle to the driver + +Return Value: + + TRUE if driver is loaded successfully. + +--*/ +{ + BOOL ReturnValue = FALSE; + TCHAR* pPathSeparator; + TCHAR szDriverPath[MAX_PATH] = _T(""); + DWORD dwSize; + SC_HANDLE hSCM = NULL; + HANDLE hDriver = NULL; + + *pDriver = NULL; + + // + // Construct driver path. + // + + dwSize = GetModuleFileName(NULL, szDriverPath, ARRAY_LENGTH(szDriverPath)); + + if (dwSize == 0) { + ErrorPrint("GetModuleFileName failed, last error 0x%u", GetLastError()); + goto Exit; + } + + pPathSeparator = _tcsrchr(szDriverPath, _T('\\')); + + if (pPathSeparator != NULL) { + pPathSeparator[1] = _T('\0'); + _tcscat_s(szDriverPath, MAX_PATH, szDriverFileName); + } else { + ErrorPrint("_tcsrchr failed to file \\ in driver path."); + goto Exit; + } + + // + // Get a handle to SCM + // + + hSCM = OpenSCManager ( NULL, NULL, SC_MANAGER_ALL_ACCESS ); + + if (hSCM == NULL) { + ErrorPrint("OpenSCManager failed, last error 0x%x", GetLastError()); + goto Exit; + } + + // + // First, uninstall and unload the driver. + // + + ReturnValue = UtilUnloadDriver( INVALID_HANDLE_VALUE, hSCM, szDriverName); + + if (ReturnValue != TRUE) { + ErrorPrint("UnloadDriver failed"); + goto Exit; + } + + // + // Install the driver. + // + + ReturnValue = UtilCreateService(hSCM, szDriverName, szDriverPath); + + if (ReturnValue == FALSE) { + ErrorPrint("UtilCreateService failed"); + goto Exit; + } + + // + // Load the driver. + // + + ReturnValue = UtilStartService(hSCM, szDriverName); + + if (ReturnValue == FALSE) { + ErrorPrint("UtilStartService failed"); + goto Exit; + } + + // + // Open a handle to the device. + // + + ReturnValue = UtilOpenDevice(szWin32DeviceName, &hDriver); + + if (ReturnValue == FALSE) { + ErrorPrint("UtilOpenDevice failed"); + goto Exit; + } + + *pDriver = hDriver; + ReturnValue = TRUE; + +Exit: + + if (hSCM != NULL) { + CloseServiceHandle(hSCM); + } + + return ReturnValue; +} + + +BOOL +UtilUnloadDriver( + _In_ HANDLE hDriver, + _In_opt_ SC_HANDLE hPassedSCM, + _In_ LPTSTR szDriverName + ) +/*++ + +Routine Description: + + Unloads the driver using SCManager. + +Arguments: + + hDriver - handle to the driver + + hPassedSCM - handle to the SCManager (optional) + + szDriverName - name of driver (no extension) + +Return Value: + + TRUE if driver is successfully unloaded + +--*/ +{ + + BOOL ReturnValue = FALSE; + SC_HANDLE hSCM = hPassedSCM; + + // + // Get a handle to SCM if not passed in + // + + if (hSCM == NULL) { + + hSCM = OpenSCManager ( NULL, NULL, SC_MANAGER_ALL_ACCESS ); + + if (hSCM == NULL) { + ErrorPrint("OpenSCManager failed, last error 0x%x", GetLastError()); + goto Exit; + } + } + + + // + // Close our handle to the device. + // + + if ((hDriver != NULL) && (hDriver != INVALID_HANDLE_VALUE)) { + CloseHandle (hDriver); + hDriver = INVALID_HANDLE_VALUE; + } + + // + // Unload the driver. + // + + ReturnValue = UtilStopService(hSCM, szDriverName); + + if (ReturnValue == FALSE) { + ErrorPrint("UtilStopService failed"); + goto Exit; + } + + // + // Delete the service. + // + + ReturnValue = UtilDeleteService(hSCM, szDriverName); + + if (ReturnValue == FALSE) { + ErrorPrint("UtilDeleteService failed"); + goto Exit; + } + + ReturnValue = TRUE; + +Exit: + + if ((hPassedSCM == NULL) && (hSCM != NULL)) { + CloseServiceHandle(hSCM); + } + + return ReturnValue; +} + + + +BOOL +UtilGetServiceState ( + _In_ SC_HANDLE hService, + _Out_ DWORD* State + ) +/*++ + +Routine Description: + + Gets the state of the service using QueryServiceStatusEx + +Arguments: + + hService - handle to the service to query + + State - pointer to a variable that receives the state + +Return Value: + + TRUE if service is queried successfully. + +--*/ +{ + SERVICE_STATUS_PROCESS ServiceStatus; + DWORD BytesNeeded; + BOOL Result; + + *State = 0; + + Result = QueryServiceStatusEx ( hService, + SC_STATUS_PROCESS_INFO, + (LPBYTE)&ServiceStatus, + sizeof(ServiceStatus), + &BytesNeeded); + + if (Result == FALSE) { + ErrorPrint("QueryServiceStatusEx failed, last error 0x%x", GetLastError()); + return FALSE; + } + + *State = ServiceStatus.dwCurrentState; + + return TRUE; +} + + +BOOL +UtilWaitForServiceState ( + _In_ SC_HANDLE hService, + _In_ DWORD State + ) +/*++ + +Routine Description: + + This routine waits for the service to reach a certain state + +Arguments: + + hService - handle to the service + + State - the desired state + +Return Value: + + TRUE if service reaches the desired state or FALSE if querying the + service returns an error. + +--*/ +{ + + DWORD ServiceState; + BOOL Result; + + for (;;) { + + Result = UtilGetServiceState (hService, &ServiceState); + + if (Result == FALSE) { + return FALSE; + } + + if (ServiceState == State) { + break; + } + + Sleep (1000); + } + + return TRUE; +} + +// +// UtilCreateService +// + +BOOL +UtilCreateService( + _In_ SC_HANDLE hSCM, + _In_ LPTSTR szDriverName, + _In_ LPTSTR szDriverPath + ) +/*++ + +Routine Description: + + Uses SCManager to create a service + +Arguments: + + hSCM - handle to the SCManager + + szDriverName - name of driver (no extension) which will serve as the + created service's name + + szDriverPath - path to driver + +Return Value: + + TRUE if service is created successfully, FALSE otherwise. + +--*/ +{ + BOOL ReturnValue = FALSE; + + // + // Create the service + // + + SC_HANDLE hService = CreateService ( + hSCM, // handle to SC manager + szDriverName, // name of service + szDriverName, // display name + SERVICE_ALL_ACCESS, // access mask + SERVICE_KERNEL_DRIVER, // service type + SERVICE_DEMAND_START, // start type + SERVICE_ERROR_NORMAL, // error control + szDriverPath, // full path to driver + NULL, // load ordering + NULL, // tag id + NULL, // dependency + NULL, // account name + NULL // password + ); + + if ((hService == NULL) && (GetLastError() != ERROR_SERVICE_EXISTS)) { + ErrorPrint("CreateService failed, last error 0x%x", GetLastError()); + goto Exit; + } + + ReturnValue = TRUE; + +Exit: + + if (hService) { + CloseServiceHandle(hService); + } + + return ReturnValue; +} + + +BOOL +UtilStartService( + _In_ SC_HANDLE hSCM, + _In_ LPTSTR szDriverName + ) +/*++ + +Routine Description: + + Starts a service + +Arguments: + + hSCM - handle to the SCManager + + szDriverName - name of driver (without extension), services as name of + the service to start + +Return Value: + + TRUE if service is successfully started, FALSE otherwise. + +--*/ +{ + BOOL ReturnValue = FALSE; + + // + // Open the service. The function assumes that + // UtilCreateService has been called before this one + // and the service is already installed. + // + + SC_HANDLE hService = OpenService ( hSCM, szDriverName, SERVICE_ALL_ACCESS ); + + if (hService == NULL) { + ErrorPrint("OpenService failed, last error 0x%x", GetLastError()); + goto Exit; + } + + // + // Start the service + // + + if (! StartService (hService, 0, NULL)) { + + if (GetLastError() != ERROR_SERVICE_ALREADY_RUNNING) { + ErrorPrint("StartService failed, last error 0x%x", GetLastError()); + goto Exit; + } + } + + if (FALSE == UtilWaitForServiceState (hService, SERVICE_RUNNING)) { + goto Exit; + } + + ReturnValue = TRUE; + +Exit: + + if (hService) { + CloseServiceHandle(hService); + } + + return ReturnValue; +} + + +BOOL +UtilStopService( + _In_ SC_HANDLE hSCM, + _In_ LPTSTR szDriverName + ) +/*++ + +Routine Description: + + Stops a service + +Arguments: + + hSCM - handle to the SCManager + + szDriverName - name of driver (without extension), services as name of + the service + +Return Value: + + TRUE if service is successfully stopped, FALSE otherwise. + +--*/ +{ + BOOL ReturnValue = FALSE; + SERVICE_STATUS ServiceStatus; + + // + // Open the service so we can stop it + // + + SC_HANDLE hService = OpenService ( hSCM, szDriverName, SERVICE_ALL_ACCESS ); + + if (hService == NULL) { + if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) { + ReturnValue = TRUE; + } else { + ErrorPrint("OpenService failed, last error 0x%x", GetLastError()); + } + goto Exit; + } + + // + // Stop the service + // + + if (FALSE == ControlService (hService, SERVICE_CONTROL_STOP, &ServiceStatus)) { + if (GetLastError() != ERROR_SERVICE_NOT_ACTIVE) { + ErrorPrint("ControlService failed, last error 0x%x", GetLastError()); + goto Exit; + } + } + + if (FALSE == UtilWaitForServiceState (hService, SERVICE_STOPPED)) { + goto Exit; + } + + ReturnValue = TRUE; + +Exit: + + if (hService) { + CloseServiceHandle (hService); + } + + return ReturnValue; +} + + +BOOL +UtilDeleteService( + _In_ SC_HANDLE hSCM, + _In_ LPTSTR szDriverName + ) +/*++ + +Routine Description: + + Deletes a service + +Arguments: + + hSCM - handle to the SCManager + + szDriverName - name of driver (without extension), services as name of + the service + +Return Value: + + TRUE if service is successfully deleted, FALSE otherwise. + +--*/ +{ + BOOL ReturnValue = FALSE; + + // + // Open the service so we can delete it + // + + SC_HANDLE hService = OpenService ( hSCM, szDriverName, SERVICE_ALL_ACCESS ); + + if (hService == NULL) { + if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) { + ReturnValue = TRUE; + } else { + ErrorPrint("OpenService failed, last error 0x%x", GetLastError()); + } + goto Exit; + } + + // + // Delete the service + // + + if (! DeleteService (hService)) { + if (GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE) { + ErrorPrint("DeleteService failed, last error 0x%x", GetLastError()); + goto Exit; + } + } + + ReturnValue = TRUE; + +Exit: + + if (hService) { + CloseServiceHandle (hService); + } + + return ReturnValue; +} + + +BOOL + +UtilOpenDevice( + _In_ LPTSTR szWin32DeviceName, + _Out_ HANDLE *phDevice + ) +/*++ + +Routine Description: + + Opens a device + +Arguments: + + szWin32DeviceName - name of the device + + phDevice - pointer to a variable that receives the handle to the device + +Return Value: + + TRUE if the device is successfully opened, FALSE otherwise. + +--*/ +{ + BOOL ReturnValue = FALSE; + HANDLE hDevice; + + // + // Open the device + // + + hDevice = CreateFile ( szWin32DeviceName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hDevice == INVALID_HANDLE_VALUE) { + ErrorPrint("CreateFile(%ls) failed, last error 0x%x", + szWin32DeviceName, + GetLastError() ); + goto Exit; + } + + ReturnValue = TRUE; + +Exit: + + *phDevice = hDevice; + return ReturnValue; +} diff --git a/general/registry/regfltr/regfltr.sln b/general/registry/regfltr/regfltr.sln new file mode 100644 index 00000000..1b1e31c3 --- /dev/null +++ b/general/registry/regfltr/regfltr.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{C7CD81BA-CF73-4628-909C-77E4B3370C77}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{700EF2F6-A93B-4398-AE93-EFA0C49511C6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "regctrl", "exe\regctrl.vcxproj", "{F3CBF3E0-E60F-409E-9402-A508C8008EB7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "regfltr", "sys\regfltr.vcxproj", "{12666DFF-2CD6-4000-AFE6-0796D9B6D330}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F3CBF3E0-E60F-409E-9402-A508C8008EB7}.Debug|Win32.ActiveCfg = Debug|Win32 + {F3CBF3E0-E60F-409E-9402-A508C8008EB7}.Debug|Win32.Build.0 = Debug|Win32 + {F3CBF3E0-E60F-409E-9402-A508C8008EB7}.Release|Win32.ActiveCfg = Release|Win32 + {F3CBF3E0-E60F-409E-9402-A508C8008EB7}.Release|Win32.Build.0 = Release|Win32 + {F3CBF3E0-E60F-409E-9402-A508C8008EB7}.Debug|x64.ActiveCfg = Debug|x64 + {F3CBF3E0-E60F-409E-9402-A508C8008EB7}.Debug|x64.Build.0 = Debug|x64 + {F3CBF3E0-E60F-409E-9402-A508C8008EB7}.Release|x64.ActiveCfg = Release|x64 + {F3CBF3E0-E60F-409E-9402-A508C8008EB7}.Release|x64.Build.0 = Release|x64 + {12666DFF-2CD6-4000-AFE6-0796D9B6D330}.Debug|Win32.ActiveCfg = Debug|Win32 + {12666DFF-2CD6-4000-AFE6-0796D9B6D330}.Debug|Win32.Build.0 = Debug|Win32 + {12666DFF-2CD6-4000-AFE6-0796D9B6D330}.Release|Win32.ActiveCfg = Release|Win32 + {12666DFF-2CD6-4000-AFE6-0796D9B6D330}.Release|Win32.Build.0 = Release|Win32 + {12666DFF-2CD6-4000-AFE6-0796D9B6D330}.Debug|x64.ActiveCfg = Debug|x64 + {12666DFF-2CD6-4000-AFE6-0796D9B6D330}.Debug|x64.Build.0 = Debug|x64 + {12666DFF-2CD6-4000-AFE6-0796D9B6D330}.Release|x64.ActiveCfg = Release|x64 + {12666DFF-2CD6-4000-AFE6-0796D9B6D330}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F3CBF3E0-E60F-409E-9402-A508C8008EB7} = {C7CD81BA-CF73-4628-909C-77E4B3370C77} + {12666DFF-2CD6-4000-AFE6-0796D9B6D330} = {700EF2F6-A93B-4398-AE93-EFA0C49511C6} + EndGlobalSection +EndGlobal diff --git a/general/registry/regfltr/sys/capture.c b/general/registry/regfltr/sys/capture.c new file mode 100644 index 00000000..7b350e73 --- /dev/null +++ b/general/registry/regfltr/sys/capture.c @@ -0,0 +1,620 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + capture.c + +Abstract: + + This file contains + 1. Documentation for what parameters need to be captured + 2. A simple sample showing how to capture the parameters for + SetValueKey and DeleteValueKey operations. + 3. Helper routines for capturing buffers and UNICODE_STRINGs. + +Environment: + + Kernel mode only + +--*/ + + +#include "regfltr.h" + + +/*++ + + Probing and Capturing Parameters + + This section defines how registry filtering drivers should handle input + parameters. + + + I. Probed Parameters + + All members in related registry structures (e.g. REG_CREATE_KEY_INFORMATION, + REG_SAVE_KEY_INFORMATION) and all structures and buffers pointed to by + their buffers are already probed by registry. Parameters are probed only + when ExGetPreviousMode indicates that the previous mode was user mode. If + the previous mode is kernel mode (i.e. KernelMode. is returned by + ExGetPreviousMode), all parameters are considered valid and no probing is + done in this case. + + + II. Captured parameters + + Most but not all members in related registry structures are captured. + If the previous mode is kernel mode, all parameters are considered valid + and no capturing is done in this case. If the previous mode is user mode, + all parameters are probed but not necessarily captured. Since this impacts + how registry filtering drivers should handle input parameters, we outline + below exactly which parameters are captured. It is important to note that + some members currently probed but not captured by the OS may be captured + in the future. On the other hand registry filtering drivers can rely on + the fact that parameters currently captured will be captured in all future + OS releases. + + * Note: As of Windows 8, all structures except output buffers are fully + captured. + + 1. Fully captured structures: + + Note: A fully captured structure is a captured structure where all + structures and buffers pointed to by its members are also fully + captured. + + For example: the UNICODE_STRING structure pointed to by the + CompleteName member in the fully captured REG_CREATE_KEY_INFORMATION + structure is also captured (including the Buffer member of the + UNICODE_STRING). + + List of fully captured structures (in Windows 7): + + REG_CREATE_KEY_INFORMATION (** incorrect) + REG_CREATE_KEY_INFORMATION_V1 (** incorrect) + REG_DELETE_KEY_INFORMATION + REG_FLUSH_KEY_INFORMATION + REG_KEY_HANDLE_CLOSE_INFORMATION + REG_OPEN_KEY_INFORMATION + REG_OPEN_KEY_INFORMATION_V1 + REG_QUERY_KEY_SECURITY_INFORMATION (** added) + REG_REPLACE_KEY_INFORMATION + REG_RESTORE_KEY_INFORMATION + REG_SAVE_KEY_INFORMATION + REG_SET_KEY_SECURITY_INFORMATION. + REG_UNLOAD_KEY_INFORMATION + + ** There were incorrect entries in this list that are now + corrected. REG_CREATE_KEY_INFORMATION and + REG_CREATE_KEY_INFORMATION_V1 are not fully captured and should + be on the partially captured structures. + REG_QUERY_KEY_SECURITY_INFORMATION has now been added to the list + of fully captured structures. + + 2. Partially captured structures + + Note: This section has been modified for clarity in the Windows 8 + release of the WDK. However this information only applies to Windows 7 + since in Windows 8 all input buffers and structures are captured. + + This section defines which members are captured in the partially + captured structures. + + Notes: + + A. CallContext, ObjectContext members are not consumed by registry + and are not treated as probed or captured by this document. + B. Integer types (e.g. enum, int, …) are considered captured in the + structure and are not explicitly listed below. + C. The Object member is always captured and it's not explicitly + listed below. + D. Output buffers are probed but not captured + + List of partially captured structures and the members that are NOT + captured: + + REG_CREATE_KEY_INFORMATION: Class->Buffer + REG_CREATE_KEY_INFORMATION_V1: Class->Buffer + REG_DELETE_VALUE_KEY_INFORMATION: ValueName->Buffer + REG_LOAD_KEY_INFORMATION: KeyName->Buffer + REG_QUERY_VALUE_KEY_INFORMATION: ValueName->Buffer + REG_QUERY_MULTIPLE_VALUE_KEY_INFORMATION: BufferLength + REG_RENAME_KEY_INFORMATION: NewName->Buffer + REG_SET_VALUE_KEY_INFORMATION: Data + + + All other REG_Xxx_INFORMATION structures do not have fields that + require capturing other than those mentioned in notes B and C. + + Example: + + REG_ENUMERATE_KEY_INFORMATION: + Object: captured, see (C) above + Index: captured, see (B) above + KeyInformationClass : captured, see (B) above + KeyInformation: only probed, see (D) above + Length: captured, see (B) above + ResultLength: only probed, see (D) above + CallContext, ObjectContext: unknown, see (A) above + Reserved: currently undefined + + List of these structures: + + REG_CALLBACK_CONTEXT_CLEANUP_INFORMATION + REG_DELETE_KEY_INFORMATION + REG_ENUMERATE_KEY_INFORMATION + REG_KEY_HANDLE_CLOSE_INFORMATION + REG_QUERY_KEY_INFORMATION + REG_QUERY_KEY_SECURITY_INFORMATION + REG_REPLACE_KEY_INFORMATION + REG_RESTORE_KEY_INFORMATION + REG_SAVE_KEY_INFORMATION + REG_SET_INFORMATION_KEY_INFORMATION + REG_SET_KEY_SECURITY_INFORMATION + REG_UNLOAD_KEY_INFORMATION + + + III. Handling Registry Filtering Parameters + + Registry filtering drivers must handle input parameters correctly. If the + previous mode is user mode and the driver needs to use a parameter, it must + depending on the scenario either wrap every access with a try-except + construct or capture the parameter. If the driver wants to call a Zw + registry API or any other kernel mode Zw API, it must ensure that all the + arguments passed to the call are captured because these APIs will not + probe or capture their inputs if the call originated from kernel mode. + + There is no need for drivers to probe any of the parameters as the registry + has already probed them. If the driver uses the OS probe APIs to probe a + parameter that has already been captured by the registry, it will throw an + exception. + + Note: Special handling should be provided for NULL buffers. In some cases + such buffer might be considered valid even for kernel mode. + +--*/ + + +NTSTATUS +CallbackCapture( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 +) +/*++ + +Routine Description: + + This helper callback routine shows how to capture a buffer and a + unicode string with the name of a value. The bulk of the work is down + in the helper capture routines: CaptureBuffer and CaptureUnicodeString. + + In the pre-notification phase, we bypass the set value and delete value + operations and complete them manually by calling ZwSetValueKey and + ZwDeleteValueKey. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_SET_VALUE_KEY_INFORMATION PreSetValueInfo; + PREG_DELETE_VALUE_KEY_INFORMATION PreDeleteValueInfo; + HANDLE RootKey = NULL; + PVOID LocalData = NULL; + PVOID Data = NULL; + UNICODE_STRING LocalValueName = {0}; + PUNICODE_STRING ValueName = NULL; + KPROCESSOR_MODE Mode = KernelMode; + + UNREFERENCED_PARAMETER(CallbackCtx); + + switch(NotifyClass) { + + case RegNtPreSetValueKey: + + PreSetValueInfo = (PREG_SET_VALUE_KEY_INFORMATION) Argument2; + + // + // REG_SET_VALUE_KEY_INFORMATION is a partially captured structure. + // The value name is captured but the data is not. Since we are + // passing the data to a zw* method, we need to capture it. + // + // *Note: as of win8, the data buffer is captured as well + // by the registry. + // + + Mode = ExGetPreviousMode(); + + if (!g_IsWin8OrGreater && (Mode == UserMode)) { + Status = CaptureBuffer(&LocalData, + PreSetValueInfo->Data, + PreSetValueInfo->DataSize, + REGFLTR_CAPTURE_POOL_TAG); + if (!NT_SUCCESS(Status)) { + break; + } + Data = LocalData; + } else { + Data = PreSetValueInfo->Data; + } + + // + // Get a handle to the root key the value is being created under. + // This is in PreInfo->Object. + // + + Status = ObOpenObjectByPointer(PreSetValueInfo->Object, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + NULL, + KernelMode, + &RootKey); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + break; + } + + // + // Set the value. + // + + Status = ZwSetValueKey(RootKey, + PreSetValueInfo->ValueName, + 0, + PreSetValueInfo->Type, + Data, + PreSetValueInfo->DataSize); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue in CallbackModify failed. Status 0x%x", + Status); + ZwClose(RootKey); + break; + } + + // + // Finally return STATUS_CALLBACK_BYPASS to tell the registry + // not to proceed with the original registry operation and to return + // STATUS_SUCCESS to the caller. + // + + InfoPrint("\tCallback: Set value %wZ bypassed.", PreSetValueInfo->ValueName); + Status = STATUS_CALLBACK_BYPASS; + ZwClose(RootKey); + break; + + case RegNtPreDeleteValueKey: + + PreDeleteValueInfo = (PREG_DELETE_VALUE_KEY_INFORMATION) Argument2; + + // + // REG_DELETE_VALUE_KEY_INFORMATION is a partially captured + // structure. The value name's buffer is not captured. Since we are + // passing the name to a zw* method, we need to capture it. + // + // *Note: as of Win8, the data buffer is captured already + // by the registry. + // + + Mode = ExGetPreviousMode(); + + if (!g_IsWin8OrGreater && (Mode == UserMode)) { + Status = CaptureUnicodeString(&LocalValueName, + PreDeleteValueInfo->ValueName, + REGFLTR_CAPTURE_POOL_TAG); + if (!NT_SUCCESS(Status)) { + break; + } + ValueName = &LocalValueName; + } else { + ValueName = PreDeleteValueInfo->ValueName; + } + + // + // Get a handle to the root key the value is being created under. + // This is in PreInfo->Object. + // + + Status = ObOpenObjectByPointer(PreDeleteValueInfo->Object, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + NULL, + KernelMode, + &RootKey); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + break; + } + + // + // Set the value. + // + + Status = ZwDeleteValueKey(RootKey, + ValueName); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwDeleteValue failed. Status 0x%x", + Status); + ZwClose(RootKey); + break; + } + + // + // Finally return STATUS_CALLBACK_BYPASS to tell the registry + // not to proceed with the original registry operation and to return + // STATUS_SUCCESS to the caller. + // + + InfoPrint("\tCallback: Delete value %S bypassed.", ValueName->Buffer); + Status = STATUS_CALLBACK_BYPASS; + ZwClose(RootKey); + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + // + // Free buffers used for capturing user mode values. + // + + if (LocalData != NULL){ + FreeCapturedBuffer(LocalData, REGFLTR_CAPTURE_POOL_TAG); + } + + if (LocalValueName.Buffer != NULL) { + FreeCapturedUnicodeString(&LocalValueName, REGFLTR_CAPTURE_POOL_TAG); + } + + return Status; +} + + + + +NTSTATUS +CaptureBuffer( + _Outptr_result_maybenull_ PVOID *CapturedBuffer, + _In_reads_bytes_(Length) PVOID Buffer, + _In_ SIZE_T Length, + _In_ ULONG PoolTag + ) +/*++ + +Routine Description: + + Captures a buffer using allocations with the specified pool tag. Captured + buffer should be freed using FreeCapturedBuffer. + +Arguments: + + CapturedBuffer - pointer to a variable that receives the location of the + captured buffer. + + Buffer - the buffer to capture + + Length - Length of Buffer + + PoolTag - pool tag + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PVOID TempBuffer = NULL; + + NT_ASSERT(CapturedBuffer != NULL); + + if (Length == 0) { + *CapturedBuffer = NULL; + return Status; + } + + TempBuffer = (PCALLBACK_CONTEXT) ExAllocatePoolWithTag( + PagedPool, + Length, + PoolTag); + + // + // It's a good practice to keep the contents of a try-except block to + // the bare minimum. By keeping the pool allocation call outside of the + // try-except block we don't mask possible pool corruptions. + // + + if (TempBuffer != NULL) { + try { + RtlCopyMemory(TempBuffer, Buffer, Length); + } except (ExceptionFilter(GetExceptionInformation())) { + ErrorPrint("Capturing buffer failed with exception"); + ExFreePoolWithTag(TempBuffer, PoolTag); + TempBuffer = NULL; + Status = GetExceptionCode(); + } + } else { + ErrorPrint("Capturing buffer failed wtih insufficient resources"); + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + *CapturedBuffer = TempBuffer; + + return Status; + } + + +VOID +FreeCapturedBuffer( + _In_ PVOID CapturedBuffer, + _In_ ULONG PoolTag + ) +/*++ + +Routine Description: + + Frees a captured buffer. + +Arguments: + + CapturedBuffer - captured buffer + + PoolTag - pool tag + +--*/ +{ + if (CapturedBuffer != NULL) { + ExFreePoolWithTag(CapturedBuffer, PoolTag); + } +} + + +NTSTATUS +CaptureUnicodeString( + _Inout_ UNICODE_STRING *DestString, + _In_ PCUNICODE_STRING SourceString, + _In_ ULONG PoolTag + ) +/*++ + +Routine Description: + + Captures a unicode string. The buffer is captured based on SourceString's + Length field with the addition of sizeof(WCHAR) bytes for a NULL to + signal the end of the string. + + Use FreeCapturedUnicodeString to free the captured string. + +Arguments: + + DestString - Pointer to the unicode string that will receive the + captured buffer. + + SourceString - Pointer tot he unicode string to be captured. + + PoolTag - pool tag + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + + if (SourceString->Length == 0) { + DestString->Length = 0; + DestString->Buffer = NULL; + DestString->MaximumLength = 0; + return Status; + } + + // + // Only SourceString->Length should be checked. The registry does not + // validate SourceString->MaximumLength. + // + // An additional sizeof(WCHAR) bytes are added to the buffer size since + // SourceString->Length does not include the NULL at the end of the string. + // + + DestString->Length = SourceString->Length; + DestString->MaximumLength = SourceString->Length + sizeof(WCHAR); + + DestString->Buffer = (PWSTR) ExAllocatePoolWithTag( + PagedPool, + DestString->MaximumLength, + PoolTag); + + if (DestString->Buffer != NULL) { + + RtlZeroMemory(DestString->Buffer, DestString->MaximumLength); + + // + // It's a good practice to keep the contents of a try-except block to + // the bare minimum. By keeping the pool allocation call outside of the + // try-except block we don't mask possible pool corruptions. + // + + try { + RtlCopyMemory(DestString->Buffer, + SourceString->Buffer, + SourceString->Length); + } except (ExceptionFilter(GetExceptionInformation())) { + ErrorPrint("Capturing Unicode String failed with exception"); + ExFreePoolWithTag(DestString->Buffer, PoolTag); + DestString->Buffer = NULL; + Status = GetExceptionCode(); + } + + } else { + ErrorPrint("Capturing Unicode String failed wtih insufficient resources"); + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (DestString->Buffer == NULL) { + DestString->Length = 0; + DestString->MaximumLength = 0; + } + + return Status; + +} + + +VOID +FreeCapturedUnicodeString( + _In_ UNICODE_STRING *String, + _In_ ULONG PoolTag + ) +/*++ + +Routine Description: + + Frees a captured buffer. + +Arguments: + + CapturedBuffer - captured buffer + + PoolTag - pool tag + +--*/ +{ + if (String->Length != 0) { + String->Length = 0; + String->MaximumLength = 0; + FreeCapturedBuffer(String->Buffer, PoolTag); + String->Buffer = NULL; + } +} diff --git a/general/registry/regfltr/sys/context.c b/general/registry/regfltr/sys/context.c new file mode 100644 index 00000000..e599606f --- /dev/null +++ b/general/registry/regfltr/sys/context.c @@ -0,0 +1,582 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Context.c + +Abstract: + + Samples that show how to set call contexts and object contexts. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +BOOLEAN +SetObjectContextSample( + ) +/*++ + +Routine Description: + + This sample shows how a registry callback can associate a context on + a registry object using CmSetCallbackObjectContext. + + This context is available in the ObjectContext field of the + REG_Xxx_KEY_INFORMATION data structures. The registry object is a handle + to a key and not the registry key itself. When the handle is closed + or the callback is unregistered, the callback will receive a + RegNtCallbackObjectContextCleanup notification to give a chance to + clean up the context. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES KeyAttributes; + HANDLE RootKeyWithContext = NULL; + DWORD ValueData = 0; + BOOLEAN Success = FALSE; + + + InfoPrint(""); + InfoPrint("=== Set Object Context Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_SET_OBJECT_CONTEXT, + CALLBACK_ALTITUDE); + + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register the callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Open the root key again. The callback will associate an object + // context with the RootKeyWithContext handle. + // + + RtlInitUnicodeString(&Name, ROOT_KEY_ABS_PATH); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwOpenKey(&RootKeyWithContext, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKey on root key failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Set value using both the g_RootKey handle and the RootKeyWithContext + // handle. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue failed. Status 0x%x", Status); + Success = FALSE; + } + + if (RootKeyWithContext != NULL) { + Status = ZwSetValueKey(RootKeyWithContext, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue failed. Status 0x%x", Status); + Success = FALSE; + } + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + } + + // + // Check that the RegNtCallbackObjectContextCleanup notification was + // received when we unregistered the callback. + // + + if (CallbackCtx->ContextCleanupCount != 1) { + ErrorPrint("Callback was not invoked for a context cleanup notification."); + Success = FALSE; + } + + // + // Check that there were two notifications that had the object context set. + // These are the pre and post set value using the RootKeyWithContext handle. + // + + if (CallbackCtx->NotificationWithContextCount != 2) { + ErrorPrint("Callback OperationWithContext count expected 2, instead it was %d", + CallbackCtx->NotificationWithContextCount); + Success = FALSE; + } + + // + // Check that there were two notifications that did not have the object + // context set. These are the pre and post set value using the + // g_RootKey handle. + // + + if (CallbackCtx->NotificationWithNoContextCount != 2) { + ErrorPrint("Callback OperationWithNoContext count expected 2, instead it was %d", + CallbackCtx->NotificationWithNoContextCount); + Success = FALSE; + } + + Exit: + + if (Success == TRUE) { + InfoPrint("Set Object Context Sample Succeeded."); + } else { + ErrorPrint("Set Object Context Sample FAILED."); + } + + // + // Clean up + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + + if (RootKeyWithContext != NULL) { + ZwClose(RootKeyWithContext); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + return Success; +} + + +NTSTATUS +CallbackSetObjectContext( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 +) +/*++ + +Routine Description: + + This helper callback routine shows how to associate a registry key object + with context information using CmSetCallbackObjectContext. The context + set is then only available to this callback. A callback that sets the + object context should be prepared for a RegNtCallbackObjectContextCleanup + where it must clean up the context. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + Always STATUS_SUCCESS; + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_CALLBACK_CONTEXT_CLEANUP_INFORMATION CleanupInfo; + PREG_POST_OPERATION_INFORMATION PostInfo; + PVOID ObjectContext = NULL; + + switch(NotifyClass) { + + case RegNtPostOpenKeyEx: + + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + + // + // If the open key was successful, set an object context + // to the key object. + // + // Note that one of the parameters of CmSetCallbackObjectContext + // is the cookie gotten from registering a callback. The object + // context will only be available to the callback with that + // particular cookie. + // + + if (NT_SUCCESS(PostInfo->Status)) { + + // + // Never call CmSetCallbackObjectContext outside of the + // callback routine. + // + + Status = CmSetCallbackObjectContext(PostInfo->Object, + &CallbackCtx->Cookie, + CallbackCtx, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmSetCallbackobjectContext failed. Status 0x%x", + Status); + } + } + break; + + case RegNtPreSetValueKey: + case RegNtPostSetValueKey: + + // + // All registry operations using the handle received from the open + // key operation will come with the ObjectContext field set to the + // context information. Other operations on the same key but + // using a different handle will not have the ObjectContext field + // set. + // + + if (NotifyClass == RegNtPreSetValueKey) { + ObjectContext = ((PREG_SET_VALUE_KEY_INFORMATION) Argument2)->ObjectContext; + } else { + ObjectContext = ((PREG_POST_OPERATION_INFORMATION) Argument2)->ObjectContext; + } + + if (ObjectContext == NULL) { + InterlockedIncrement(&CallbackCtx->NotificationWithNoContextCount); + } else if (ObjectContext == CallbackCtx) { + InterlockedIncrement(&CallbackCtx->NotificationWithContextCount); + } else { + ErrorPrint("Unexpected ObjectContext value: 0x%p", ObjectContext); + } + + break; + + case RegNtCallbackObjectContextCleanup: + + // + // This is a special notification only invoked for callbacks + // that have set context information to an object. This notification + // is either sent when the registry object is being closed or if + // the callback is being unregistered. In the first case, this + // notification comes after the RegNtPreKeyHandleClose + // notification and before the RegNtPostKeyHandleClose notification. + // + + CleanupInfo = (PREG_CALLBACK_CONTEXT_CLEANUP_INFORMATION) Argument2; + if (CleanupInfo->ObjectContext != CallbackCtx) { + ErrorPrint("ContextCleanup's ObjectContext has unexpected value: 0x%p.", + CleanupInfo->ObjectContext); + } else { + InterlockedIncrement(&CallbackCtx->ContextCleanupCount); + } + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; +} + + +BOOLEAN +SetCallContextSample( + ) +/*++ + +Routine Description: + + This sample shows how a registry callback can associate a context + with a registry operation during the pre-notification phase so that it + is available in the post-notification phase. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + DWORD ValueData = 0; + BOOLEAN Success = FALSE; + + + InfoPrint(""); + InfoPrint("=== Set Operation Context Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_SET_CALL_CONTEXT, + CALLBACK_ALTITUDE); + + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register callback with the context + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Create a key and set a value. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + + // + // Check that the callback records 2 in OperationContextCount. + // The count should be incremented once in the post-notification for the + // create key and once for the set value. + // + + if (CallbackCtx->NotificationWithContextCount != 2) { + ErrorPrint("Callback OperationWithContextCount expected 2, got %d", + CallbackCtx->NotificationWithContextCount); + Success = FALSE; + } + + Exit: + + if (Success == TRUE) { + InfoPrint("Set Call Context sample succeeded."); + } else { + ErrorPrint("Set Call Context sample FAILED."); + } + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + return Success; + +} + + + +NTSTATUS +CallbackSetCallContext( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine shows how to attach context information to + the registry operation itself in the pre-notification phase and + have access to that context in the post-notification phase. The context + is private to this callback. + + ***Note: Any callback that receives a pre-notification will receive + a post-notifcation EXCEPT if the callback returns a non-success value + (this includes STATUS_CALLBACK_BYPASS) during the pre phase. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + Always STATUS_SUCCESS + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_POST_OPERATION_INFORMATION PostInfo; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + PREG_SET_VALUE_KEY_INFORMATION PreSetValueInfo; + + switch(NotifyClass) { + + // + // Set the call context by setting it to the CallContext field of the + // REG_XXX_KEY_INFORMATION structure during the pre-notification phase. + // + + case RegNtPreSetValueKey: + PreSetValueInfo = (PREG_SET_VALUE_KEY_INFORMATION) Argument2; + PreSetValueInfo->CallContext = CallbackCtx; + break; + + case RegNtPreCreateKeyEx: + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2; + PreCreateInfo->CallContext = CallbackCtx; + break; + + // + // In the post-notification phase, check that the CallContext field + // of REG_POST_OPERATION_INFORMATION contains the context we set in + // the pre phase. + // + + case RegNtPostSetValueKey: + case RegNtPostCreateKeyEx: + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + if (PostInfo->CallContext != CallbackCtx) { + ErrorPrint("Unexpected CallContext value: 0x%p", PostInfo->CallContext); + } else { + InterlockedIncrement(&CallbackCtx->NotificationWithContextCount); + } + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; +} + diff --git a/general/registry/regfltr/sys/driver.c b/general/registry/regfltr/sys/driver.c new file mode 100644 index 00000000..d169a1be --- /dev/null +++ b/general/registry/regfltr/sys/driver.c @@ -0,0 +1,468 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + regfltr.c + +Abstract: + + Sample driver used to run the kernel mode registry callback samples. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +DRIVER_INITIALIZE DriverEntry; +DRIVER_UNLOAD DeviceUnload; + +_Dispatch_type_(IRP_MJ_CREATE) DRIVER_DISPATCH DeviceCreate; +_Dispatch_type_(IRP_MJ_CLOSE) DRIVER_DISPATCH DeviceClose; +_Dispatch_type_(IRP_MJ_CLEANUP) DRIVER_DISPATCH DeviceCleanup; +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH DeviceControl; + +// +// Pointer to the device object used to register registry callbacks +// +PDEVICE_OBJECT g_DeviceObj; + +// +// Registry callback version +// +ULONG g_MajorVersion; +ULONG g_MinorVersion; + +// +// Set to TRUE if TM and RM were successfully created and the transaction +// callback was successfully enabled. +// +BOOLEAN g_RMCreated; + + +// +// OS version globals initialized in driver entry +// + +BOOLEAN g_IsWin8OrGreater = FALSE; + +VOID +DetectOSVersion() +/*++ + +Routine Description: + + This routine determines the OS version and initializes some globals used + in the sample. + +Arguments: + + None + +Return value: + + None. On failure, global variables stay at default value + +--*/ +{ + + RTL_OSVERSIONINFOEXW VersionInfo = {0}; + NTSTATUS Status; + ULONGLONG ConditionMask = 0; + + // + // Set VersionInfo to Win7's version number and then use + // RtlVerifVersionInfo to see if this is win8 or greater. + // + + VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo); + VersionInfo.dwMajorVersion = 6; + VersionInfo.dwMinorVersion = 1; + + VER_SET_CONDITION(ConditionMask, VER_MAJORVERSION, VER_LESS_EQUAL); + VER_SET_CONDITION(ConditionMask, VER_MINORVERSION, VER_LESS_EQUAL); + + + + Status = RtlVerifyVersionInfo(&VersionInfo, + VER_MAJORVERSION | VER_MINORVERSION, + ConditionMask); + if (NT_SUCCESS(Status)) { + g_IsWin8OrGreater = FALSE; + InfoPrint("DetectOSVersion: This machine is running Windows 7 or an older OS."); + } else if (Status == STATUS_REVISION_MISMATCH) { + g_IsWin8OrGreater = TRUE; + InfoPrint("DetectOSVersion: This machine is running Windows 8 or a newer OS."); + } else { + ErrorPrint("RtlVerifyVersionInfo returned unexpected error status 0x%x.", + Status); + + // + // default action is to assume this is not win8 + // + g_IsWin8OrGreater = FALSE; + } + +} + + + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine is called by the operating system to initialize the driver. + It allocates a device object, initializes the supported Io callbacks, and + creates a symlink to make the device accessible to Win32. + + It gets the registry callback version and stores it in the global + variables g_MajorVersion and g_MinorVersion. It also calls + CreateKTMResourceManager to create a resource manager that is used in + the transaction samples. + +Arguments: + + DriverObject - Supplies the system control object for this test driver. + + RegistryPath - The string location of the driver's corresponding services + key in the registry. + +Return value: + + Success or appropriate failure code. + +--*/ +{ + NTSTATUS Status; + UNICODE_STRING NtDeviceName; + UNICODE_STRING DosDevicesLinkName; + UNICODE_STRING DeviceSDDLString; + + UNREFERENCED_PARAMETER(RegistryPath); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, + DPFLTR_ERROR_LEVEL, + "RegFltr: DriverEntry()\n"); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, + DPFLTR_ERROR_LEVEL, + "RegFltr: Use ed nt!Kd_IHVDRIVER_Mask 8 to enable more detailed printouts\n"); + + // + // Create our device object. + // + + RtlInitUnicodeString(&NtDeviceName, NT_DEVICE_NAME); + RtlInitUnicodeString(&DeviceSDDLString, DEVICE_SDDL); + + Status = IoCreateDeviceSecure( + DriverObject, // pointer to driver object + 0, // device extension size + &NtDeviceName, // device name + FILE_DEVICE_UNKNOWN, // device type + 0, // device characteristics + TRUE, // not exclusive + &DeviceSDDLString, // SDDL string specifying access + NULL, // device class guid + &g_DeviceObj); // returned device object pointer + + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Set dispatch routines. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = DeviceCreate; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = DeviceClose; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DeviceCleanup; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControl; + DriverObject->DriverUnload = DeviceUnload; + + // + // Create a link in the Win32 namespace. + // + + RtlInitUnicodeString(&DosDevicesLinkName, DOS_DEVICES_LINK_NAME); + + Status = IoCreateSymbolicLink(&DosDevicesLinkName, &NtDeviceName); + + if (!NT_SUCCESS(Status)) { + IoDeleteDevice(DriverObject->DeviceObject); + return Status; + } + + // + // Get callback version. + // + + CmGetCallbackVersion(&g_MajorVersion, &g_MinorVersion); + InfoPrint("Callback version %u.%u", g_MajorVersion, g_MinorVersion); + + // + // Some variations depend on knowing if the OS is win8 or above + // + + DetectOSVersion(); + + // + // Set up KTM resource manager and pass in RMCallback as our + // callback routine. + // + + Status = CreateKTMResourceManager(RMCallback, NULL); + + if (NT_SUCCESS(Status)) { + g_RMCreated = TRUE; + } + + // + // Initialize the callback context list + // + + InitializeListHead(&g_CallbackCtxListHead); + ExInitializeFastMutex(&g_CallbackCtxListLock); + g_NumCallbackCtxListEntries = 0; + + return STATUS_SUCCESS; + +} + + + +NTSTATUS +DeviceCreate ( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + Dispatches file create requests. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + STATUS_NOT_IMPLEMENTED + +--*/ +{ + UNREFERENCED_PARAMETER(DeviceObject); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + + +NTSTATUS +DeviceClose ( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + Dispatches close requests. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + STATUS_SUCCESS + +--*/ +{ + UNREFERENCED_PARAMETER(DeviceObject); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + + +NTSTATUS +DeviceCleanup ( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + Dispatches cleanup requests. Does nothing right now. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + STATUS_SUCCESS + +--*/ +{ + UNREFERENCED_PARAMETER(DeviceObject); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; +} + + + +NTSTATUS +DeviceControl ( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + Dispatches ioctl requests. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + Status returned from the method called. + +--*/ +{ + PIO_STACK_LOCATION IrpStack; + ULONG Ioctl; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(DeviceObject); + + Status = STATUS_SUCCESS; + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + Ioctl = IrpStack->Parameters.DeviceIoControl.IoControlCode; + + switch (Ioctl) + { + + case IOCTL_DO_KERNELMODE_SAMPLES: + Status = DoCallbackSamples(DeviceObject, Irp); + break; + + case IOCTL_REGISTER_CALLBACK: + Status = RegisterCallback(DeviceObject, Irp); + break; + + case IOCTL_UNREGISTER_CALLBACK: + Status = UnRegisterCallback(DeviceObject, Irp); + break; + + case IOCTL_GET_CALLBACK_VERSION: + Status = GetCallbackVersion(DeviceObject, Irp); + break; + + default: + ErrorPrint("Unrecognized ioctl code 0x%x", Ioctl); + } + + // + // Complete the irp and return. + // + + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; + +} + + +VOID +DeviceUnload ( + _In_ PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + Cleans up any driver-level allocations and prepares for unload. All + this driver needs to do is to delete the device object and the + symbolic link between our device name and the Win32 visible name. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + STATUS_NOT_IMPLEMENTED + +--*/ +{ + UNICODE_STRING DosDevicesLinkName; + + // + // Clean up the KTM data structures + // + + DeleteKTMResourceManager(); + + // + // Delete the link from our device name to a name in the Win32 namespace. + // + + RtlInitUnicodeString(&DosDevicesLinkName, DOS_DEVICES_LINK_NAME); + IoDeleteSymbolicLink(&DosDevicesLinkName); + + // + // Finally delete our device object + // + + IoDeleteDevice(DriverObject->DeviceObject); + + DbgPrintEx(DPFLTR_IHVDRIVER_ID, + DPFLTR_ERROR_LEVEL, + "RegFltr: DeviceUnload\n"); +} + diff --git a/general/registry/regfltr/sys/multialt.c b/general/registry/regfltr/sys/multialt.c new file mode 100644 index 00000000..b471d0ba --- /dev/null +++ b/general/registry/regfltr/sys/multialt.c @@ -0,0 +1,776 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + MultiAlt.c + +Abstract: + + Samples that feature multiple callbacks registered at different + altitudes and show what notifications they do and don't recieve. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +BOOLEAN +MultipleAltitudeBlockDuringPreSample( + ) +/*++ + +Routine Description: + + This sample features a stack of three callbacks at different altitudes and + demonstrates what happens when middle callback blocks an operation + in the pre-notification phase. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + + PCALLBACK_CONTEXT CallbackCtxHigh = NULL; + PCALLBACK_CONTEXT CallbackCtxMid = NULL; + PCALLBACK_CONTEXT CallbackCtxLow = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Multiple Altitude Block During Pre Sample ===="); + + // + // Create callback contexts for the 3 callbacks. + // The high and low callbacks will only monitor how many notifications + // they receive. + // + + CallbackCtxHigh = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR, + CALLBACK_HIGH_ALTITUDE); + CallbackCtxMid = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE, + CALLBACK_ALTITUDE); + CallbackCtxLow = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR, + CALLBACK_LOW_ALTITUDE); + + if ((CallbackCtxHigh == NULL) || + (CallbackCtxMid == NULL) || + (CallbackCtxLow == NULL)) { + goto Exit; + } + + // + // Register the callbacks + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxHigh->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxHigh, + &CallbackCtxHigh->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxMid->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxMid, + &CallbackCtxMid->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxLow->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxLow, + &CallbackCtxLow->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Do a create key operation which will be blocked by the middle + // callback and fail with STATUS_ACCESS_DENIED + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwCreateKey returned unexpected status 0x%x", Status); + Success = FALSE; + } + + + // + // Unregister the callbacks + // + + Status = CmUnRegisterCallback(CallbackCtxHigh->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + } + + Status = CmUnRegisterCallback(CallbackCtxMid->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + } + + Status = CmUnRegisterCallback(CallbackCtxLow->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + } + + + // + // Verify that the highest alitude callback receives a pre and a post + // notification. It receives a post notification because it returned + // STATUS_SUCCESS in the pre-notification so it is guaranteed to get a + // post notification. + // + + if ((CallbackCtxHigh->PreNotificationCount != 1) || + (CallbackCtxHigh->PostNotificationCount != 1)) { + ErrorPrint("High Callback should have seen 1 pre and 1 post notifications."); + ErrorPrint("High Callback actually saw %d pre and %d post notifications.", + CallbackCtxHigh->PreNotificationCount, + CallbackCtxHigh->PostNotificationCount); + Success = FALSE; + } + + // + // Verify the middle callback receives only a pre notification. + // It does not get a post notification because it return a non-success + // value in the pre-notification phase. + // + + if ((CallbackCtxMid->PreNotificationCount != 1) || + (CallbackCtxMid->PostNotificationCount != 0)) { + ErrorPrint("Mid Callback should have seen 1 pre and 0 post notifications."); + ErrorPrint("Mid Callback actually saw %d pre and %d post notifications.", + CallbackCtxMid->PreNotificationCount, + CallbackCtxMid->PostNotificationCount); + Success = FALSE; + } + + // + // Verify the lowest callback receives no notifications. + // Once the middle callback blocks, no callbacks at lower altitudes are + // notified. + // + + if ((CallbackCtxLow->PreNotificationCount != 0) || + (CallbackCtxLow->PostNotificationCount != 0)) { + ErrorPrint("Low Callback should have seen 0 pre and 0 post notifications."); + ErrorPrint("Low Callback actually saw %d pre and %d post notifications.", + CallbackCtxLow->PreNotificationCount, + CallbackCtxLow->PostNotificationCount); + Success = FALSE; + } + + Exit: + + if (Success) { + InfoPrint("Multiple Altitude Block During Pre Sample succeeded."); + } else { + ErrorPrint("Multiple Altitude Block During Pre Sample FAILED."); + } + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (CallbackCtxHigh != NULL) { + ExFreePoolWithTag(CallbackCtxHigh, REGFLTR_CONTEXT_POOL_TAG); + } + + if (CallbackCtxMid != NULL) { + ExFreePoolWithTag(CallbackCtxMid, REGFLTR_CONTEXT_POOL_TAG); + } + + if (CallbackCtxLow != NULL) { + ExFreePoolWithTag(CallbackCtxLow, REGFLTR_CONTEXT_POOL_TAG); + } + + return Success; +} + + +BOOLEAN +MultipleAltitudeInternalInvocationSample( + ) +/*++ + +Routine Description: + + This sample features a stack of 3 callbacks at different altitudes and + demonstrates what happens when the middle callback invokes a registry + operation. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtxHigh = NULL; + PCALLBACK_CONTEXT CallbackCtxMid = NULL; + PCALLBACK_CONTEXT CallbackCtxLow = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + BOOLEAN Success = FALSE; + + + InfoPrint(""); + InfoPrint("=== Multiple Altitude Internal Invocation Sample ===="); + + // + // Create callback contexts for the 3 callbacks. + // The high and low callbacks will only monitor how many notifications + // they receive. + // + + CallbackCtxHigh = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR, + CALLBACK_HIGH_ALTITUDE); + CallbackCtxMid = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION, + CALLBACK_ALTITUDE); + CallbackCtxLow = CreateCallbackContext(CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR, + CALLBACK_LOW_ALTITUDE); + + if ((CallbackCtxHigh == NULL) || + (CallbackCtxMid == NULL) || + (CallbackCtxLow == NULL)) { + goto Exit; + } + + // + // Register the callbacks + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxHigh->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxHigh, + &CallbackCtxHigh->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxMid->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxMid, + &CallbackCtxMid->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtxLow->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtxLow, + &CallbackCtxLow->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Create a key. When the middle callback receives the pre-notification + // and the post-notification for this create it will perform an open key + // and a close key operation. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey returned unexpected status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callbacks + // + + Status = CmUnRegisterCallback(CallbackCtxHigh->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Status = CmUnRegisterCallback(CallbackCtxMid->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Status = CmUnRegisterCallback(CallbackCtxLow->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + + // + // Verify the highest altitude callback receives one pre and one post + // notification. This callback does not get notifications for the + // registry operations called by the middle callback. + // + + if ((CallbackCtxHigh->PreNotificationCount != 1) || + (CallbackCtxHigh->PostNotificationCount != 1)) { + ErrorPrint("High Callback should have seen 1 pre and 1 post notifications."); + ErrorPrint("High Callback actually saw %d pre and %d post notifications.", + CallbackCtxHigh->PreNotificationCount, + CallbackCtxHigh->PostNotificationCount); + Success = FALSE; + } + + // + // Verify the middle callback receives one pre and one post notification. + // This callback does not get notifications for the registry operations + // that it calls. + // + + if ((CallbackCtxMid->PreNotificationCount != 1) || + (CallbackCtxMid->PostNotificationCount != 1)) { + ErrorPrint("Mid Callback should have seen 1 pre and 1 post notifications."); + ErrorPrint("Mid Callback actually saw %d pre and %d post notifications.", + CallbackCtxMid->PreNotificationCount, + CallbackCtxMid->PostNotificationCount); + Success = FALSE; + } + + // + // Verify the lowest callback receives 5 pre-notifications and 5 + // post-notifications. This callback receives 1 pre and 1 post from the + // original create key operation. It also receives 2 pre and 2 post for + // the open key and close key operations called by the middle callback + // during the pre phase of the create key and then 2 pre and 2 post again + // for the calls in the post phase of the create key. + // + + if ((CallbackCtxLow->PreNotificationCount != 5) || + (CallbackCtxLow->PostNotificationCount != 5)) { + ErrorPrint("Low Callback should have seen 5 pre and 5 post notifications."); + ErrorPrint("Low Callback actually saw %d pre and %d post notifications.", + CallbackCtxLow->PreNotificationCount, + CallbackCtxLow->PostNotificationCount); + Success = FALSE; + } + + Exit: + + if (Success) { + InfoPrint("Multiple Altitude Internal Invocation Sample succeeded."); + } else { + ErrorPrint("Multiple Altitude Internal Invocation Sample FAILED."); + } + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (CallbackCtxHigh != NULL) { + ExFreePoolWithTag(CallbackCtxHigh, REGFLTR_CONTEXT_POOL_TAG); + } + + if (CallbackCtxMid != NULL) { + ExFreePoolWithTag(CallbackCtxMid, REGFLTR_CONTEXT_POOL_TAG); + } + + if (CallbackCtxLow != NULL) { + ExFreePoolWithTag(CallbackCtxLow, REGFLTR_CONTEXT_POOL_TAG); + } + + return Success; +} + + +NTSTATUS +CallbackMonitor( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine just monitors how many pre and post registry + operations it receives and records it in the callback context. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + Always STATUS_SUCCESS + +--*/ +{ + UNREFERENCED_PARAMETER(Argument2); + + switch(NotifyClass) { + case RegNtPreDeleteKey: + case RegNtPreSetValueKey: + case RegNtPreDeleteValueKey: + case RegNtPreSetInformationKey: + case RegNtPreRenameKey: + case RegNtPreEnumerateKey: + case RegNtPreEnumerateValueKey: + case RegNtPreQueryKey: + case RegNtPreQueryValueKey: + case RegNtPreQueryMultipleValueKey: + case RegNtPreKeyHandleClose: + case RegNtPreCreateKeyEx: + case RegNtPreOpenKeyEx: + case RegNtPreFlushKey: + case RegNtPreLoadKey: + case RegNtPreUnLoadKey: + case RegNtPreQueryKeySecurity: + case RegNtPreSetKeySecurity: + case RegNtPreRestoreKey: + case RegNtPreSaveKey: + case RegNtPreReplaceKey: + InterlockedIncrement(&CallbackCtx->PreNotificationCount); + break; + case RegNtPostDeleteKey: + case RegNtPostSetValueKey: + case RegNtPostDeleteValueKey: + case RegNtPostSetInformationKey: + case RegNtPostRenameKey: + case RegNtPostEnumerateKey: + case RegNtPostEnumerateValueKey: + case RegNtPostQueryKey: + case RegNtPostQueryValueKey: + case RegNtPostQueryMultipleValueKey: + case RegNtPostKeyHandleClose: + case RegNtPostCreateKeyEx: + case RegNtPostOpenKeyEx: + case RegNtPostFlushKey: + case RegNtPostLoadKey: + case RegNtPostUnLoadKey: + case RegNtPostQueryKeySecurity: + case RegNtPostSetKeySecurity: + case RegNtPostRestoreKey: + case RegNtPostSaveKey: + case RegNtPostReplaceKey: + InterlockedIncrement(&CallbackCtx->PostNotificationCount); + break; + } + + return STATUS_SUCCESS; + +} + + +NTSTATUS +CallbackMultipleAltitude( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine first calls CallbackMonitor to record the + number of pre and post notifications received by the callback. Then it + does one of two things depending on the callback mode specified in the + callback context. + + If callback mode is CALLBACK_MODE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE: + Return STATUS_ACCESS_DENIED when we receive a pre-notification for a + create key operation. + + If callback mode is CALLBACK_MODE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION: + Call ZwOpenKey and ZwCloseKey when we receive a pre or post notification + for a create key operation. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + PVOID Object = NULL; + HANDLE ObjectHandle = NULL; + PCUNICODE_STRING ObjectName = NULL; + UNICODE_STRING CapturedObjectName = {0}; + OBJECT_ATTRIBUTES KeyAttributes = {0}; + PREG_POST_OPERATION_INFORMATION PostInfo; + + CallbackMonitor(CallbackCtx, NotifyClass, Argument2); + + if(CallbackCtx->CallbackMode == CALLBACK_MODE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE) { + switch(NotifyClass) { + case RegNtPreSetValueKey: + case RegNtPreCreateKeyEx: + InfoPrint("\tCallback: CreateKey/SetValueKey blocked."); + Status = STATUS_ACCESS_DENIED; + break; + default: + // + // Do nothing for other notifications + // + break; + } + } + + if (CallbackCtx->CallbackMode == CALLBACK_MODE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION) { + + // + // Get the Object that we will open and close. + // + + switch(NotifyClass) { + case RegNtPreCreateKeyEx: + Object = ((PREG_CREATE_KEY_INFORMATION) Argument2)->RootObject; + + // + // RootObject should never be NULL. + // + + ASSERT(Object != NULL); + + break; + + case RegNtPostCreateKeyEx: + + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + + // + // Make sure the operation is successful so far. + // + + if (!NT_SUCCESS(PostInfo->Status)) { + ErrorPrint("Post notification status is unexpectedly 0x%x.", + PostInfo->Status); + break; + } + + // + // If the operation si successful so far, PostInfo->Object should + // not be NULL. However, a misbehaving registry filter driver + // can make this NULL so we do not ASSERT here as we do in the + // pre-notification case. + // + + Object = PostInfo->Object; + if (Object == NULL) { + ErrorPrint("PostInfo->Object is unexpectedly null in RegNtPostCreateKeyEx."); + ErrorPrint("PostInfo->Status is 0x%x", + PostInfo->Status); + } + + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + + if (Object != NULL) { + + // + // Use CmCallbackGetKeyObjectID to get the absolute path to Object. + // + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // In Windows 8, CmCallbackGetKeyObjectIDEx was added to give + // developers a copy of the object name rather than the actual + // object name string. This is a safer programming approach and + // allows the system to safely clean up the old object name in + // operations like renaming the key. + // + // Call CmCallbackReleaseKeyObjectIDEx to release the object name + // returned by CmCallbackGetKeyObjectIDEx. + // + + Status = CmCallbackGetKeyObjectIDEx(&CallbackCtx->Cookie, + Object, + NULL, + &ObjectName, + 0); // Flag: reserved for future + + if (!NT_SUCCESS (Status)) { + ErrorPrint("CmCallbackGetKeyObjectIDEx failed. Status 0x%x", Status); + goto Exit; + } + + CapturedObjectName.Length = ObjectName->Length; + CapturedObjectName.MaximumLength = ObjectName->MaximumLength; + CapturedObjectName.Buffer = ObjectName->Buffer; + +#else + + Status = CmCallbackGetKeyObjectID(&CallbackCtx->Cookie, + Object, + NULL, + &ObjectName); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("CmCallbackGetKeyObjectID failed. Status 0x%x", Status); + goto Exit; + } + + // + // The UNICODE_STRING referenced by ObjectName from + // CmCallbackGetKeyObjectID must not be changed. If you need to + // modify the string, create a copy. + // + // Although this sample does not change the path, we show the + // code to capture the string for demonstration purposes. + // + + Status = CaptureUnicodeString(&CapturedObjectName, ObjectName, + REGFLTR_CAPTURE_POOL_TAG); + + if (!NT_SUCCESS(Status)) { + goto Exit; + } + +#endif //NTDDI_VERSION >= NTDDI_WIN8 + + + InitializeObjectAttributes(&KeyAttributes, + &CapturedObjectName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + InfoPrint("\tCallback: Internal Invocation of ZwOpenKey"); + Status = ZwOpenKey(&ObjectHandle, + KEY_ALL_ACCESS, + &KeyAttributes); + if (!NT_SUCCESS (Status)) { + ErrorPrint("ZwOpenKey failed. Status 0x%x", Status); + } else { + InfoPrint("\tCallback: Internal Invocation of ZwCloseKey"); + ZwClose(ObjectHandle); + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + CmCallbackReleaseKeyObjectIDEx(ObjectName); + +#else + + FreeCapturedUnicodeString(&CapturedObjectName, REGFLTR_CAPTURE_POOL_TAG); + +#endif //NTDDI_VERSION >= NTDDI_WIN8 + + } + } + + Exit: + + return Status; +} + + diff --git a/general/registry/regfltr/sys/post.c b/general/registry/regfltr/sys/post.c new file mode 100644 index 00000000..bff979fe --- /dev/null +++ b/general/registry/regfltr/sys/post.c @@ -0,0 +1,768 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Post.c + +Abstract: + + Samples that show what callbacks can do during the post-notification + phase. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +/*++ + + In registry callback version 1.0, there is a bug with post-notification + processing and multiple registry filter drivers that can break the samples + here. It is fixed with version 1.1. + + The bug occurs when a driver blocks or bypasses a registry operation in the + pre-notification phase. Even though the processing of the operation stops + there, registry filter drivers registered at higher altitudes will still + get a post-notification for the operation. If the higher altitude driver + tries to change the status of the operation from failure to success or + vice versa, this change will be ignored and the status returned + will be the status returned by the driver who bypassed or blocked the + operation during the pre-notification phase. + + For more information on how notification processing works with multiple + registry filter drivers registered see MultiAlt.c + + For more information on issues in version 1.0 and changes in version 1.1 + see Version.c + + + Beginning with Windows 8.1, it is no longer possible to pass the object + provided to a RegNtPostCreateKeyEx or RegNtPostOpenKeyEx callout to + ObOpenObjectByPointer. To work around this, filters should perform all + create key or open key processing in a RegNtPreCreateKeyEx or + RegNtPreOpenKeyEx callout. If for any reason the desired processing cannot + be performed in a RegNtPreCreateKeyEx or RegNtPreOpenKeyEx callout, then + use CmSetCallbackObjectContext to tag a newly-created or newly-opened key + as unprocessed and process it in the pre-operation callback for a + subsequent operation. + +--*/ + + + +BOOLEAN +PostNotificationOverrideSuccessSample( + ) +/*++ + +Routine Description: + + This sample shows how registry callbacks can fail a registry operation + in the post-notification phase. + + Two values are created. The creates normally should succeeded, but one + is intercepted by the callback and failed with STATUS_ACCESS_DENIED. + + NOTE: This sample does not take into account transactions. See txr.c for + examples on how to handle transactional registry operations. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + UNICODE_STRING Name; + DWORD ValueData = 0; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Post-Notification Override Success Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_SUCCESS, + CALLBACK_ALTITUDE); + + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Set two values. + // Setting the "not modified" value will succeed. + // Setting the other value will fail with file not found. + // + + RtlInitUnicodeString(&Name, NOT_MODIFIED_VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue return unexpected status 0x%x", Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwSetValue return unexpected status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + + // + // Verify that the set value calls were failed by + // checking that the value with VALUE_NAME does not + // exist. + // + // Deleting the other value should return STATUS_OBJECT_NAME_NOT_FOUND + // Deleting value with the modified name should succeed. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwDeleteValueKey(g_RootKey, &Name); + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwDeleteValueKey on value failed. Status: 0x%x", Status); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + RtlInitUnicodeString(&Name, NOT_MODIFIED_VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Post-Notification Override Success Sample succeeded."); + } else { + ErrorPrint("Post-Notification Override Success Sample FAILED."); + } + return Success; + +} + + +NTSTATUS +CallbackPostNotificationOverrideSuccess( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine intercepts create key and set value post + notifications and fails the operation with STATUS_ACCESS_DENIED. + + NOTE: This sample does not take into account transactions. See txr.c for + examples on how to handle transactional registry operations. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + PREG_SET_VALUE_KEY_INFORMATION PreSetValueInfo; + PREG_POST_OPERATION_INFORMATION PostInfo; + UNICODE_STRING Name; + HANDLE Key = NULL; + + UNREFERENCED_PARAMETER(CallbackCtx); + + switch(NotifyClass) { + case RegNtPostCreateKeyEx: + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) PostInfo->PreInformation; + + // + // REG_CREATE_KEY_INFORMATION is a partially captured + // structure however no uncaptured fields are used here. For more + // information on what parameters need to be captured, see + // capture.c. + // + + // + // Only intercept the operation if the key being created has the + // name KEY_NAME. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + if (!RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreCreateInfo->CompleteName, + TRUE)) { + break; + } + + // + // Make sure the operation is successful so far. + // + + if (!NT_SUCCESS(PostInfo->Status)) { + ErrorPrint("Operation status in post notification is unexpectedly 0x%x", + PostInfo->Status); + break; + } + + // + // Since this is the post-notification phase, the key has + // already been created. It is stored in PostInfo->Object. + // Get a handle on the key and delete it. + // + + Status = ObOpenObjectByPointer(PostInfo->Object, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + PreCreateInfo->ObjectType, + KernelMode, + &Key); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x\n", Status); + break; + } + + Status = ZwDeleteKey(Key); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwDeleteKey failed. Status 0x%x\n", Status); + break; + } + + ZwClose(Key); + + // + // Dereference the object because it will not be returned to + // the user. NULL out the references to the object in the + // post and pre information structures. + // + + ObDereferenceObject(PostInfo->Object); + PostInfo->Object = NULL; + *PreCreateInfo->ResultObject = NULL; + + InfoPrint("\tCallback: Create key %wZ overrided from success to error.", + PreCreateInfo->CompleteName); + + // + // Put the status to be returned in PostInfo->ReturnStatus and + // return STATUS_CALLBACK_BYPASS to let CM know that + // we want to change the return status. + // + // DO NOT set PostInfo->Status + // + + PostInfo->ReturnStatus = STATUS_ACCESS_DENIED; + Status = STATUS_CALLBACK_BYPASS; + break; + + case RegNtPostSetValueKey: + + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + PreSetValueInfo = (PREG_SET_VALUE_KEY_INFORMATION) PostInfo->PreInformation; + + // + // NOTE: REG_SET_VALUE_KEY_INFORMATION is a partially captured + // structure. The value name is captured but the data buffer is + // not. Since we are only using the value name we do not need to + // capture any parameters. For more information on what parameters + // need to be captured, see capture.c. + // + + // + // Only intercept the operation if the value being set has the + // name VALUE_NAME. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + if (!RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreSetValueInfo->ValueName, + TRUE)) { + break; + } + + // + // Make sure the operation is successful so far. + // + + if (!NT_SUCCESS(PostInfo->Status)) { + ErrorPrint("Post notification status is unexpectedly 0x%x.", + PostInfo->Status); + break; + } + + // + // To fail the operation, we have to delete the value that has + // been created. To do so, we need a handle to the root key. + // + + Status = ObOpenObjectByPointer(PreSetValueInfo->Object, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + NULL, + KernelMode, + &Key); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x\n", Status); + break; + } + + Status = ZwDeleteValueKey(Key, PreSetValueInfo->ValueName); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwDeleteValueKey failed. Status 0x%x\n", Status); + break; + } + + ZwClose(Key); + + // + // Put the status to be returned in PostInfo->ReturnStatus and + // return STATUS_CALLBACK_BYPASS to let CM know that + // we want to change the return status. + // + // DO NOT set PostInfo->Status + // + + InfoPrint("\tCallback: Value %wZ overrided from success to error.", + PreSetValueInfo->ValueName); + PostInfo->ReturnStatus = STATUS_ACCESS_DENIED; + Status = STATUS_CALLBACK_BYPASS; + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; + +} + + +BOOLEAN +PostNotificationOverrideErrorSample( + ) +/*++ + +Routine Description: + + This sample shows how a registry callback can change a failed registry + operation into a successful operation in the post-notification phase. + + A key that does not exist is opened. The opens should fail, but it is + intercepted by the callback and the open is redirected to a key that + does exist. + + NOTE: This sample does not take into account transactions. See txr.c for + examples on how to handle transactional registry operations. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + BOOLEAN Success = FALSE; + HANDLE Key = NULL; + HANDLE ModifiedKey = NULL; + + InfoPrint(""); + InfoPrint("=== Post-Notification Override Error Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_ERROR, + CALLBACK_ALTITUDE); + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Create a key with name MODIFIED_KEY_NAME + // + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&ModifiedKey, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("Creating modified key failed. Status 0x%x", Status); + goto Exit; + } + + // + // Now try to open a key by KEY_NAME which does not exist. Verify that + // this fails. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwCreateKey returned unexpected status 0x%x", Status); + goto Exit; + } + + // + // Register our callback with the context + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Open key again. The callback will intercept this and make it succeed. + // + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKey failed unexpectedly. Status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (ModifiedKey != NULL) { + ZwDeleteKey(ModifiedKey); + ZwClose(ModifiedKey); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Post-Notification Override Error Sample succeeded."); + } else { + ErrorPrint("Post-Notification Override Error Sample FAILED."); + } + + return Success; +} + + +NTSTATUS +CallbackPostNotificationOverrideError( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine intercepts open key post notifications + and if they are failing with STATUS_ACCESS_DENIED, it makes + the operation successful by redirecting the open to antoher key. + + NOTE: This sample does not take into account transactions. See txr.c for + examples on how to handle transactional registry operations. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_OPEN_KEY_INFORMATION PreOpenInfo; + PREG_POST_OPERATION_INFORMATION PostInfo; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES KeyAttributes; + HANDLE Key = NULL; + HANDLE RootKey = NULL; + PVOID Object; + + UNREFERENCED_PARAMETER(CallbackCtx); + + switch(NotifyClass) { + case RegNtPostOpenKeyEx: + + PostInfo = (PREG_POST_OPERATION_INFORMATION) Argument2; + PreOpenInfo = (PREG_OPEN_KEY_INFORMATION) PostInfo->PreInformation; + + // + // NOTE: REG_OPEN_KEY_INFORMATION is a fully captured structure + // so there is no need for the callback to capture any parameters. + // For more information on what parameters need to be captured, see + // capture.c. + // + + // + // Only intercept the operation if the key being created has the + // name KEY_NAME. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + if (!RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreOpenInfo->CompleteName, + TRUE)) { + break; + } + + // + // Verify that operation is currently failing as expected + // + + if (PostInfo->Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("Operation did not fail with status not found as expected. Post status: 0x%x", + PostInfo->Status); + break; + } + + // + // To make the operation successful, an object MUST be supplied as + // the opened handle. In this sample, the object will be a handle + // to another key that does exist. + // + + // + // First open the key and get its handle. + // + + Status = ObOpenObjectByPointer(PreOpenInfo->RootObject, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + PreOpenInfo->ObjectType, + KernelMode, + &RootKey); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + break; + } + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + PreOpenInfo->SecurityDescriptor); + + Status = ZwOpenKey(&Key, + PreOpenInfo->DesiredAccess, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKey failed. Status 0x%x", Status); + ZwClose(RootKey); + break; + } + + ZwClose(RootKey); + + // + // Then, get the object pointer from the new key's handle. + // + + Status = ObReferenceObjectByHandle(Key, + PreOpenInfo->DesiredAccess, + PreOpenInfo->ObjectType, + KernelMode, + &Object, + NULL); + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObReferenceObjectByHandle failed. Status 0x%x", Status); + ZwClose(Key); + break; + } + + // + // Finally, set the ResultObject field in the PreInfo and + // the Object field in the PostInfo to the object just opened. + // + + *PreOpenInfo->ResultObject = Object; + PreOpenInfo->GrantedAccess = PreOpenInfo->DesiredAccess; + + if (PostInfo->Object != NULL) { + ErrorPrint("PostInfo->Object should be NULL! Instead is 0x%p", + PostInfo->Object); + } + PostInfo->Object = Object; + + ZwClose(Key); + InfoPrint("\tCallback: Opening key %wZ overrided from error to success.", + PreOpenInfo->CompleteName); + + // + // Put the status to be returned in PostInfo->ReturnStatus and + // return STATUS_CALLBACK_BYPASS to let CM know that + // we want to change the return status. + // + // DO NOT set PostInfo->Status + // + + PostInfo->ReturnStatus = STATUS_SUCCESS; + Status = STATUS_CALLBACK_BYPASS; + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; + +} + diff --git a/general/registry/regfltr/sys/pre.c b/general/registry/regfltr/sys/pre.c new file mode 100644 index 00000000..0cc895c9 --- /dev/null +++ b/general/registry/regfltr/sys/pre.c @@ -0,0 +1,799 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Pre.c + +Abstract: + + Samples that show what callbacks can do during the pre-notification + phase. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +BOOLEAN +PreNotificationBlockSample( + ) +/*++ + +Routine Description: + + This sample shows how to block a registry operation in the + pre-notification phase. + + Two keys are created. The create operations should succeed, but one + is intercepted by the callback and failed with STATUS_ACCESS_DENIED. + The same is done for two values. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + HANDLE NotModifiedKey = NULL; + DWORD ValueData = 0; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Pre-Notification Block Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_PRE_NOTIFICATION_BLOCK, + CALLBACK_ALTITUDE); + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Create two keys. + // Creating the "not modified" key will succeed. + // Creating the other key will fail with STATUS_ACCESS_DENIED + // + + RtlInitUnicodeString(&Name, NOT_MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&NotModifiedKey, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (Status != STATUS_SUCCESS) { + ErrorPrint("ZwCreateKey returned unexpected status 0x%x", Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwCreateKey returned unexpected status 0x%x", Status); + Success = FALSE; + } + + + // + // Set two values. + // Setting the "not modified" value will succeed. + // Setting the other value will fail with STATUS_ACCESS_DENIED. + // + + RtlInitUnicodeString(&Name, NOT_MODIFIED_VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(Status != STATUS_SUCCESS) { + ErrorPrint("ZwSetValue return unexpected status 0x%x", Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwSetValue return unexpected status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (NotModifiedKey != NULL) { + ZwDeleteKey(NotModifiedKey); + ZwClose(NotModifiedKey); + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + RtlInitUnicodeString(&Name, NOT_MODIFIED_VALUE_NAME); + ZwDeleteValueKey(g_RootKey, &Name); + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Pre-Notification Block Sample succeeded."); + } else { + ErrorPrint("Pre-Notification Block Sample FAILED."); + } + + return Success; + +} + + +NTSTATUS +CallbackPreNotificationBlock( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine shows hot to fail a registry operation + in the pre-notification phase. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + PREG_SET_VALUE_KEY_INFORMATION PreSetValueInfo; + UNICODE_STRING Name; + + UNREFERENCED_PARAMETER(CallbackCtx); + + switch(NotifyClass) { + case RegNtPreCreateKeyEx: + + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2; + + // + // Only intercept the operation if the key being created has the + // name KEY_NAME. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + if (RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreCreateInfo->CompleteName, + TRUE)) { + // + // By returning an error status, we block the operation. + // + + InfoPrint("\tCallback: Create key %wZ blocked.", + PreCreateInfo->CompleteName); + Status = STATUS_ACCESS_DENIED; + } + break; + + case RegNtPreSetValueKey: + + PreSetValueInfo = (PREG_SET_VALUE_KEY_INFORMATION) Argument2; + + // + // Only intercept the operation if the value being set has the + // name VALUE_NAME. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + if (RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreSetValueInfo->ValueName, + TRUE)) { + // + // By returning an error status, we block the operation. + // + + InfoPrint("\tCallback: Set value %wZ blocked.", + PreSetValueInfo->ValueName); + Status = STATUS_ACCESS_DENIED; + } + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; +} + + + +BOOLEAN +PreNotificationBypassSample( + ) +/*++ + +Routine Description: + + This sample shows how to bypass a registry operation so that the CM does + not process the operation. Unlike block, an operation that is bypassed + is still considered successful so the callback must provide the caller + with what the CM would have provided. + + A key and a value are created. However both operations are bypassed by the + callback so that the key and value actually created have different names + than would is expected. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + DWORD ValueData = 0; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Pre-Notification Bypass Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_PRE_NOTIFICATION_BYPASS, + CALLBACK_ALTITUDE); + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register the callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Create a key and set a value. Both should succeed + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwSetValueKey(g_RootKey, + &Name, + 0, + REG_DWORD, + &ValueData, + sizeof(ValueData)); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + + // + // Check that a key with the expected name KEY_NAME cannot be found + // but a key with the "modified" name can be found. + // + + if (Key != NULL) { + ZwClose(Key); + } + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwOpenKey on key returned unexpected status: 0x%x", Status); + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + Key = NULL; + } + Success = FALSE; + } + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKey on modified key path failed. Status: 0x%x", Status); + Success = FALSE; + } + + + // + // Do the same check by trying to delete a value with VALUE_NAME and + // with the "modified" name. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + Status = ZwDeleteValueKey(g_RootKey, &Name); + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwDeleteValueKey on original value returned unexpected status: 0x%x", + Status); + Success = FALSE; + } + + RtlInitUnicodeString(&Name, MODIFIED_VALUE_NAME); + Status = ZwDeleteValueKey(g_RootKey, &Name); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwDeleteValueKey on modified value failed. Status: 0x%x", + Status); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Pre-Notification Bypass Sample succeeded."); + } else { + ErrorPrint("Pre-Notification Bypass Sample FAILED."); + } + + return Success; +} + + + +NTSTATUS +CallbackPreNotificationBypass( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 +) +/*++ + +Routine Description: + + This helper callback routine is the most complex part of the sample. + Here we actually manipulate the registry inside the callback to modify the + outcome and the behavior of the registry operation. + + In the pre-notification phase, we bypass the call but create a key or set + a value with a different name. + + In the post-notification phase we delete the key or value that was + created by the registry and tell the registry to return the bad error + status to the caller. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + PREG_SET_VALUE_KEY_INFORMATION PreSetValueInfo; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + UNICODE_STRING LocalClass = {0}; + PUNICODE_STRING Class = NULL; + HANDLE Key = NULL; + HANDLE RootKey = NULL; + PVOID Object; + PVOID LocalData = NULL; + PVOID Data = NULL; + KPROCESSOR_MODE Mode = KernelMode; + + + UNREFERENCED_PARAMETER(CallbackCtx); + + switch(NotifyClass) { + + case RegNtPreCreateKeyEx: + + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2; + + // + // Only intercept the operation if the key being created has the + // name KEY_NAME. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + if (!RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreCreateInfo->CompleteName, + TRUE)) { + break; + } + + // + // REG_CREATE_KEY_INFORMATION is a partially structure. The class + // field's buffer is not captured. Since it is passed to + // ZwCreateKey, it needs to be captured. + // + // *Note: in Windows 8 all fields are captured. See capture.c + // for more details. + // + + Mode = ExGetPreviousMode(); + + if (!g_IsWin8OrGreater && Mode == UserMode) { + Status = CaptureUnicodeString(&LocalClass, + PreCreateInfo->Class, + REGFLTR_CAPTURE_POOL_TAG); + if (!NT_SUCCESS(Status)) { + break; + } + Class = &LocalClass; + + } else { + Class = PreCreateInfo->Class; + } + + + // + // Next we create a key with a modified name. + // + + Status = ObOpenObjectByPointer(PreCreateInfo->RootObject, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + PreCreateInfo->ObjectType, + KernelMode, + &RootKey); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + break; + } + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + PreCreateInfo->SecurityDescriptor); + + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + Class, + PreCreateInfo->CreateOptions, + PreCreateInfo->Disposition); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + ZwClose(RootKey); + break; + } + + ZwClose(RootKey); + + // + // The we get an object pointer from the new key's handle. + // + + Status = ObReferenceObjectByHandle(Key, + PreCreateInfo->DesiredAccess, + PreCreateInfo->ObjectType, + KernelMode, + &Object, + NULL); + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObReferenceObjectByHandle failed. Status 0x%x", Status); + ZwClose(Key); + break; + } + + ZwClose(Key); + + // + // Set the ResultObject field to the new key object. + // + + *PreCreateInfo->ResultObject = Object; + + // + // Return STATUS_CALLBACK_BYPASS to let CM know we want to bypass + // CM and return STATUS_SUCCESS back to the caller. + // + + InfoPrint("\tCallback: Create key %wZ bypassed.", PreCreateInfo->CompleteName); + Status = STATUS_CALLBACK_BYPASS; + break; + + case RegNtPreSetValueKey: + + PreSetValueInfo = (PREG_SET_VALUE_KEY_INFORMATION) Argument2; + + // + // REG_SET_VALUE_KEY_INFORMATION is a partially captured structure. + // The value name is captured but the data is not. Since we are + // passing the data to a zw* method, we need to capture it. + // + // *Note: in Windows 8 all fields are captured. See capture.c + // for more details. + // + + Mode = ExGetPreviousMode(); + + if (!g_IsWin8OrGreater && Mode == UserMode) { + Status = CaptureBuffer(&LocalData, + PreSetValueInfo->Data, + PreSetValueInfo->DataSize, + REGFLTR_CAPTURE_POOL_TAG); + if (!NT_SUCCESS(Status)) { + break; + } + Data = LocalData; + } else { + Data = PreSetValueInfo->Data; + } + + // + // Only intercept the operation if the value being set has the + // name VALUE_NAME. + // + + RtlInitUnicodeString(&Name, VALUE_NAME); + if (!RtlEqualUnicodeString((PCUNICODE_STRING) &Name, + (PCUNICODE_STRING) PreSetValueInfo->ValueName, + TRUE)) { + break; + } + + // + // Get a handle to the root key the value is being created under. + // This is in PreInfo->Object. + // + + Status = ObOpenObjectByPointer(PreSetValueInfo->Object, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + NULL, + KernelMode, + &RootKey); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + break; + } + + // + // Set a value with the "modified" name. + // + + RtlInitUnicodeString(&Name, MODIFIED_VALUE_NAME); + Status = ZwSetValueKey(RootKey, + &Name, + 0, + PreSetValueInfo->Type, + Data, + PreSetValueInfo->DataSize); + + if(!NT_SUCCESS(Status)) { + ErrorPrint("ZwSetValue failed. Status 0x%x", + Status); + ZwClose(RootKey); + break; + } + + // + // Finally return STATUS_CALLBACK_BYPASS to tell the registry + // not to proceed with the original registry operation and to return + // STATUS_SUCCESS to the caller. + // + + InfoPrint("\tCallback: Set value %wZ bypassed.", PreSetValueInfo->ValueName); + Status = STATUS_CALLBACK_BYPASS; + ZwClose(RootKey); + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + // + // Free buffers used for capturing user mode values. + // + + if (LocalClass.Buffer != NULL) { + FreeCapturedUnicodeString(&LocalClass, REGFLTR_CAPTURE_POOL_TAG); + } + + if (LocalData != NULL) { + FreeCapturedBuffer(LocalData, REGFLTR_CAPTURE_POOL_TAG); + } + + return Status; +} + + diff --git a/general/registry/regfltr/sys/regfltr.c b/general/registry/regfltr/sys/regfltr.c new file mode 100644 index 00000000..5cd85c6c --- /dev/null +++ b/general/registry/regfltr/sys/regfltr.c @@ -0,0 +1,871 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + regfltr.c + +Abstract: + + Sample driver used to run the kernel mode registry callback samples. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +// +// The root key used in the samples +// +HANDLE g_RootKey; + + + +LPCWSTR +GetTransactionNotifyClassString ( + _In_ ULONG TransactionNotifcation + ); + +LPCWSTR +GetNotifyClassString ( + _In_ REG_NOTIFY_CLASS NotifyClass + ); + +VOID +DeleteTestKeys( + ); + + + +NTSTATUS +Callback ( + _In_ PVOID CallbackContext, + _In_opt_ PVOID Argument1, + _In_opt_ PVOID Argument2 +) +/*++ + +Routine Description: + + This is the registry callback we'll register to intercept all registry + operations. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + Argument1 - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. Refer to MSDN for the + mapping from REG_NOTIFY_CLASS to REG_XXX_KEY_INFORMATION. + +Return Value: + + Status returned from the helper callback routine or STATUS_SUCCESS if + the registry operation did not originate from this process. + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + REG_NOTIFY_CLASS NotifyClass; + PCALLBACK_CONTEXT CallbackCtx; + + CallbackCtx = (PCALLBACK_CONTEXT)CallbackContext; + NotifyClass = (REG_NOTIFY_CLASS)(ULONG_PTR)Argument1; + + // + // Ignore registry activity from other processes. If this callback + // wasn't registered by the current process, simply return success. + // + + if (CallbackCtx->ProcessId != PsGetCurrentProcessId()) { + return STATUS_SUCCESS; + } + + InfoPrint("\tCallback: Altitude-%S, NotifyClass-%S.", + CallbackCtx->AltitudeBuffer, + GetNotifyClassString(NotifyClass)); + + // + // Invoke a helper method depending on the value of CallbackMode in + // CallbackCtx. + // + + if (Argument2 == NULL) { + + // + // This should never happen but the sal annotation on the callback + // function marks Argument 2 as opt and is looser than what + // it actually is. + // + + ErrorPrint("\tCallback: Argument 2 unexpectedly 0. Filter will " + "abort and return success."); + return STATUS_SUCCESS; + } + + switch (CallbackCtx->CallbackMode) { + case CALLBACK_MODE_PRE_NOTIFICATION_BLOCK: + Status = CallbackPreNotificationBlock(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_PRE_NOTIFICATION_BYPASS: + Status = CallbackPreNotificationBypass(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_SUCCESS: + Status = CallbackPostNotificationOverrideSuccess(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_POST_NOTIFICATION_OVERRIDE_ERROR: + Status = CallbackPostNotificationOverrideError(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_TRANSACTION_ENLIST: + Status = CallbackTransactionEnlist(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_TRANSACTION_REPLAY: + Status = CallbackTransactionReplay(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_SET_OBJECT_CONTEXT: + Status = CallbackSetObjectContext(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_SET_CALL_CONTEXT: + Status = CallbackSetCallContext(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_MULTIPLE_ALTITUDE_MONITOR: + Status = CallbackMonitor(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE: + case CALLBACK_MODE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION: + Status = CallbackMultipleAltitude(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_CAPTURE: + Status = CallbackCapture(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_VERSION_BUGCHECK: + Status = CallbackBugcheck(CallbackCtx, NotifyClass, Argument2); + break; + case CALLBACK_MODE_VERSION_CREATE_OPEN_V1: + Status = CallbackCreateOpenV1(CallbackCtx, NotifyClass, Argument2); + break; + default: + ErrorPrint("Unknown Callback Mode: %d", CallbackCtx->CallbackMode); + Status = STATUS_INVALID_PARAMETER; + } + + + return Status; + +} + + +NTSTATUS +RMCallback( + _In_ PKENLISTMENT EnlistmentObject, + _In_ PVOID RMContext, + _In_ PVOID TransactionContext, + _In_ ULONG TransactionNotification, + _Inout_ PLARGE_INTEGER TMVirtualClock, + _In_ ULONG ArgumentLength, + _In_ PVOID Argument + ) +/*++ + +Routine Description: + + This callback recieves transaction notifications. + +Arguments: + + EnlistmentObject - Enlistment that this notification is about + + RMContext - The value specified for the RMKey parameter of the + TmEnableCallbacks routine + + TransactionContext - Value specified for the EnlistmentKey parameter + of the ZwCreateEnlistment routine + + TransactionNotification - Type of notification + + TmVirtualClock - Pointer to virtual clock value of time when KTM prepared + the notification. + + ArgumentLength - Length in bytes of the Argument buffer. + + Argument - Buffer containing notification-spcefic arguments. + +Return Value: + + Always STATUS_SUCCESS + +--*/ +{ + PRMCALLBACK_CONTEXT Context = (PRMCALLBACK_CONTEXT) TransactionContext; + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(EnlistmentObject); + UNREFERENCED_PARAMETER(RMContext); + UNREFERENCED_PARAMETER(ArgumentLength); + UNREFERENCED_PARAMETER(Argument); + + InfoPrint("\tRMCallback: NotifyClass-%S.", + GetTransactionNotifyClassString(TransactionNotification)); + + // + // Transaction notifications are bit masks. Record which one(s) + // this callback received. + // + + Context->Notification |= TransactionNotification; + + // + // Call the Tm*Complete methods to inform KTM that we have completed + // processing. (Note: It is possible to use the Zw version of + // these APIs as well). + // + // Make sure that all the notifications you request are handled. The + // type of notification this routine gets is specified when you enlist + // in a transaction. + // + + switch(TransactionNotification) { + case TRANSACTION_NOTIFY_COMMIT: + Status = TmCommitComplete(EnlistmentObject, + TMVirtualClock); + break; + case TRANSACTION_NOTIFY_ROLLBACK: + Status = TmRollbackComplete(EnlistmentObject, + TMVirtualClock); + break; + default: + ErrorPrint("Unsupported Transaction Notification: %x", + TransactionNotification); + NT_ASSERT(FALSE); + } + + // + // It is safe to close the enlistment handle here. + // Closing it before the transaction aborts or commits will abort + // the transaction. + // + + if (Context->Enlistment != NULL) { + ZwClose(Context->Enlistment); + Context->Enlistment = NULL; + } + + return Status; + +} + + + +NTSTATUS +DoCallbackSamples( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine creates the root test key and then invokes the sample. + It records the results of each sample in an array that it returns to + the usermode program. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpStack; + ULONG OutputBufferLength; + PDO_KERNELMODE_SAMPLES_OUTPUT Output; + UNICODE_STRING KeyPath; + OBJECT_ATTRIBUTES KeyAttributes; + + UNREFERENCED_PARAMETER(DeviceObject); + + // + // Get the output buffer from the irp and check it is as large as expected. + // + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + + OutputBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; + + if (OutputBufferLength < sizeof (DO_KERNELMODE_SAMPLES_OUTPUT)) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Output = (PDO_KERNELMODE_SAMPLES_OUTPUT) Irp->AssociatedIrp.SystemBuffer; + + // + // Clean up test keys in case the sample terminated uncleanly. + // + + DeleteTestKeys(); + + // + // Create the root key and the modified root key + // + + RtlInitUnicodeString(&KeyPath, ROOT_KEY_ABS_PATH); + InitializeObjectAttributes(&KeyAttributes, + &KeyPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateKey(&g_RootKey, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + return Status; + } + + // + // Call each demo and record the results in the Output->SampleResults + // array + // + + Output->SampleResults[KERNELMODE_SAMPLE_PRE_NOTIFICATION_BLOCK] = + PreNotificationBlockSample(); + + Output->SampleResults[KERNELMODE_SAMPLE_PRE_NOTIFICATION_BYPASS] = + PreNotificationBypassSample(); + + Output->SampleResults[KERNELMODE_SAMPLE_POST_NOTIFICATION_OVERRIDE_SUCCESS] = + PostNotificationOverrideSuccessSample(); + + Output->SampleResults[KERNELMODE_SAMPLE_POST_NOTIFICATION_OVERRIDE_ERROR] = + PostNotificationOverrideErrorSample(); + + Output->SampleResults[KERNELMODE_SAMPLE_TRANSACTION_ENLIST] = + TransactionEnlistSample(); + + Output->SampleResults[KERNELMODE_SAMPLE_TRANSACTION_REPLAY] = + TransactionReplaySample(); + + Output->SampleResults[KERNELMODE_SAMPLE_SET_CALL_CONTEXT] = + SetObjectContextSample(); + + Output->SampleResults[KERNELMODE_SAMPLE_SET_OBJECT_CONTEXT] = + SetCallContextSample(); + + Output->SampleResults[KERNELMODE_SAMPLE_MULTIPLE_ALTITUDE_BLOCK_DURING_PRE] = + MultipleAltitudeBlockDuringPreSample(); + + Output->SampleResults[KERNELMODE_SAMPLE_MULTIPLE_ALTITUDE_INTERNAL_INVOCATION] = + MultipleAltitudeInternalInvocationSample(); + + Output->SampleResults[KERNELMODE_SAMPLE_VERSION_CREATE_OPEN_V1] = + CreateOpenV1Sample(); + + Irp->IoStatus.Information = sizeof(DO_KERNELMODE_SAMPLES_OUTPUT); + + Exit: + + if (g_RootKey) { + ZwDeleteKey(g_RootKey); + ZwClose(g_RootKey); + } + + InfoPrint(""); + InfoPrint("Kernel Mode Samples End"); + InfoPrint(""); + + return Status; +} + + +NTSTATUS +RegisterCallback( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + Registers a callback with the specified callback mode and altitude + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + Status from CmRegisterCallbackEx + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpStack; + ULONG InputBufferLength; + ULONG OutputBufferLength; + PREGISTER_CALLBACK_INPUT RegisterCallbackInput; + PREGISTER_CALLBACK_OUTPUT RegisterCallbackOutput; + PCALLBACK_CONTEXT CallbackCtx = NULL; + + // + // Get the input and output buffer from the irp and + // check they are the expected size + // + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + + InputBufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; + OutputBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; + + if ((InputBufferLength < sizeof(REGISTER_CALLBACK_INPUT)) || + (OutputBufferLength < sizeof (REGISTER_CALLBACK_OUTPUT))) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + RegisterCallbackInput = (PREGISTER_CALLBACK_INPUT) Irp->AssociatedIrp.SystemBuffer; + + // + // Create the callback context from the specified callback mode and altitude + // + + CallbackCtx = CreateCallbackContext(RegisterCallbackInput->CallbackMode, + RegisterCallbackInput->Altitude); + + if (CallbackCtx == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + // + // Register the callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + DeviceObject->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + if (!InsertCallbackContext(CallbackCtx)) { + Status = STATUS_UNSUCCESSFUL; + goto Exit; + } + + // + // Fill the output buffer with the Cookie received from registering the + // callback and the pointer to the callback context. + // + + RegisterCallbackOutput = (PREGISTER_CALLBACK_OUTPUT)Irp->AssociatedIrp.SystemBuffer; + RegisterCallbackOutput->Cookie = CallbackCtx->Cookie; + Irp->IoStatus.Information = sizeof(REGISTER_CALLBACK_OUTPUT); + + Exit: + if (!NT_SUCCESS(Status)) { + ErrorPrint("RegisterCallback failed. Status 0x%x", Status); + if (CallbackCtx != NULL) { + DeleteCallbackContext(CallbackCtx); + } + } else { + InfoPrint("RegisterCallback succeeded"); + } + + return Status; +} + + + +NTSTATUS +UnRegisterCallback( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + Unregisters a callback with the specified cookie and clean up the + callback context. + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + Status from CmUnRegisterCallback + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpStack; + ULONG InputBufferLength; + PUNREGISTER_CALLBACK_INPUT UnRegisterCallbackInput; + PCALLBACK_CONTEXT CallbackCtx; + + UNREFERENCED_PARAMETER(DeviceObject); + + // + // Get the input buffer and check its size + // + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + + InputBufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; + + if (InputBufferLength < sizeof(UNREGISTER_CALLBACK_INPUT)) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + UnRegisterCallbackInput = (PUNREGISTER_CALLBACK_INPUT) Irp->AssociatedIrp.SystemBuffer; + + // + // Unregister the callback with the cookie + // + + Status = CmUnRegisterCallback(UnRegisterCallbackInput->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + // + // Free the callback context buffer + // + CallbackCtx = FindAndRemoveCallbackContext(UnRegisterCallbackInput->Cookie); + if (CallbackCtx != NULL) { + DeleteCallbackContext(CallbackCtx); + } + + Exit: + + if (!NT_SUCCESS(Status)) { + ErrorPrint("UnRegisterCallback failed. Status 0x%x", Status); + } else { + InfoPrint("UnRegisterCallback succeeded"); + } + InfoPrint(""); + + return Status; + +} + + +NTSTATUS +GetCallbackVersion( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + Calls CmGetCallbackVersion + +Arguments: + + DeviceObject - The device object receiving the request. + + Irp - The request packet. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpStack; + ULONG OutputBufferLength; + PGET_CALLBACK_VERSION_OUTPUT GetCallbackVersionOutput; + + UNREFERENCED_PARAMETER(DeviceObject); + + // + // Get the output buffer and verify its size + // + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + + OutputBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; + + if (OutputBufferLength < sizeof(GET_CALLBACK_VERSION_OUTPUT)) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + GetCallbackVersionOutput = (PGET_CALLBACK_VERSION_OUTPUT) Irp->AssociatedIrp.SystemBuffer; + + // + // Call CmGetCallbackVersion and store the results in the output buffer + // + + CmGetCallbackVersion(&GetCallbackVersionOutput->MajorVersion, + &GetCallbackVersionOutput->MinorVersion); + + Irp->IoStatus.Information = sizeof(GET_CALLBACK_VERSION_OUTPUT); + + Exit: + + if (!NT_SUCCESS(Status)) { + ErrorPrint("GetCallbackVersion failed. Status 0x%x", Status); + } else { + InfoPrint("GetCallbackVersion succeeded"); + } + + return Status; +} + + +LPCWSTR +GetNotifyClassString ( + _In_ REG_NOTIFY_CLASS NotifyClass + ) +/*++ + +Routine Description: + + Converts from NotifyClass to a string + +Arguments: + + NotifyClass - value that identifies the type of registry operation that + is being performed + +Return Value: + + Returns a string of the name of NotifyClass. + +--*/ +{ + switch (NotifyClass) { + case RegNtPreDeleteKey: return L"RegNtPreDeleteKey"; + case RegNtPreSetValueKey: return L"RegNtPreSetValueKey"; + case RegNtPreDeleteValueKey: return L"RegNtPreDeleteValueKey"; + case RegNtPreSetInformationKey: return L"RegNtPreSetInformationKey"; + case RegNtPreRenameKey: return L"RegNtPreRenameKey"; + case RegNtPreEnumerateKey: return L"RegNtPreEnumerateKey"; + case RegNtPreEnumerateValueKey: return L"RegNtPreEnumerateValueKey"; + case RegNtPreQueryKey: return L"RegNtPreQueryKey"; + case RegNtPreQueryValueKey: return L"RegNtPreQueryValueKey"; + case RegNtPreQueryMultipleValueKey: return L"RegNtPreQueryMultipleValueKey"; + case RegNtPreKeyHandleClose: return L"RegNtPreKeyHandleClose"; + case RegNtPreCreateKeyEx: return L"RegNtPreCreateKeyEx"; + case RegNtPreOpenKeyEx: return L"RegNtPreOpenKeyEx"; + case RegNtPreFlushKey: return L"RegNtPreFlushKey"; + case RegNtPreLoadKey: return L"RegNtPreLoadKey"; + case RegNtPreUnLoadKey: return L"RegNtPreUnLoadKey"; + case RegNtPreQueryKeySecurity: return L"RegNtPreQueryKeySecurity"; + case RegNtPreSetKeySecurity: return L"RegNtPreSetKeySecurity"; + case RegNtPreRestoreKey: return L"RegNtPreRestoreKey"; + case RegNtPreSaveKey: return L"RegNtPreSaveKey"; + case RegNtPreReplaceKey: return L"RegNtPreReplaceKey"; + + case RegNtPostDeleteKey: return L"RegNtPostDeleteKey"; + case RegNtPostSetValueKey: return L"RegNtPostSetValueKey"; + case RegNtPostDeleteValueKey: return L"RegNtPostDeleteValueKey"; + case RegNtPostSetInformationKey: return L"RegNtPostSetInformationKey"; + case RegNtPostRenameKey: return L"RegNtPostRenameKey"; + case RegNtPostEnumerateKey: return L"RegNtPostEnumerateKey"; + case RegNtPostEnumerateValueKey: return L"RegNtPostEnumerateValueKey"; + case RegNtPostQueryKey: return L"RegNtPostQueryKey"; + case RegNtPostQueryValueKey: return L"RegNtPostQueryValueKey"; + case RegNtPostQueryMultipleValueKey: return L"RegNtPostQueryMultipleValueKey"; + case RegNtPostKeyHandleClose: return L"RegNtPostKeyHandleClose"; + case RegNtPostCreateKeyEx: return L"RegNtPostCreateKeyEx"; + case RegNtPostOpenKeyEx: return L"RegNtPostOpenKeyEx"; + case RegNtPostFlushKey: return L"RegNtPostFlushKey"; + case RegNtPostLoadKey: return L"RegNtPostLoadKey"; + case RegNtPostUnLoadKey: return L"RegNtPostUnLoadKey"; + case RegNtPostQueryKeySecurity: return L"RegNtPostQueryKeySecurity"; + case RegNtPostSetKeySecurity: return L"RegNtPostSetKeySecurity"; + case RegNtPostRestoreKey: return L"RegNtPostRestoreKey"; + case RegNtPostSaveKey: return L"RegNtPostSaveKey"; + case RegNtPostReplaceKey: return L"RegNtPostReplaceKey"; + + case RegNtCallbackObjectContextCleanup: return L"RegNtCallbackObjectContextCleanup"; + + default: + return L"Unsupported REG_NOTIFY_CLASS"; + } +} + + +LPCWSTR +GetTransactionNotifyClassString ( + _In_ ULONG TransactionNotifcation + ) +/*++ + +Routine Description: + + Converts from TransactionNotification to a string + +Arguments: + + TransactionNotification - value that identifies the type of + transaction notification + +Return Value: + + Returns a string of the name of TransactionNotification + +--*/ +{ + switch (TransactionNotifcation) { + case TRANSACTION_NOTIFY_COMMIT: return L"TRANSACTION_NOTIFY_COMMIT"; + case TRANSACTION_NOTIFY_ROLLBACK: return L"TRANSACTION_NOTIFY_ROLLBACK"; + + default: + return L"Unsupported Transaction Notification"; + } +} + + + +VOID +DeleteTestKeys( + ) +/*++ + + +--*/ +{ + NTSTATUS Status; + UNICODE_STRING KeyPath; + OBJECT_ATTRIBUTES KeyAttributes; + HANDLE RootKey = NULL; + HANDLE ChildKey = NULL; + + // + // Check if the root key can be opened. If it can be opened, a previous + // run must have not completed cleanly. Delete the key and recreate the + // root key. + // + + RtlInitUnicodeString(&KeyPath, ROOT_KEY_ABS_PATH); + InitializeObjectAttributes(&KeyAttributes, + &KeyPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwOpenKey(&RootKey, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { + return; + } else if (!NT_SUCCESS(Status)) { + ErrorPrint("Opening root key fails with unexpected status %x.", Status); + } + + RtlInitUnicodeString(&KeyPath, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &KeyPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + NULL); + + Status = ZwOpenKey(&ChildKey, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (NT_SUCCESS(Status)) { + ZwDeleteKey(ChildKey); + ZwClose(ChildKey); + ChildKey = NULL; + } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("Opening %S key fails with unexpected status %x.", + KEY_NAME, + Status); + } + + RtlInitUnicodeString(&KeyPath, NOT_MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &KeyPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + NULL); + + Status = ZwOpenKey(&ChildKey, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (NT_SUCCESS(Status)) { + ZwDeleteKey(ChildKey); + ZwClose(ChildKey); + ChildKey = NULL; + } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("Opening %S key fails with unexpected status %x.", + NOT_MODIFIED_KEY_NAME, + Status); + } + + RtlInitUnicodeString(&KeyPath, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &KeyPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + NULL); + + Status = ZwOpenKey(&ChildKey, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (NT_SUCCESS(Status)) { + ZwDeleteKey(ChildKey); + ZwClose(ChildKey); + ChildKey = NULL; + } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("Opening %S key fails with unexpected status %x.", + MODIFIED_KEY_NAME, + Status); + } + + ZwDeleteKey(RootKey); + ZwClose(RootKey); + + return; + +} diff --git a/general/registry/regfltr/sys/regfltr.h b/general/registry/regfltr/sys/regfltr.h new file mode 100644 index 00000000..d8787ff2 --- /dev/null +++ b/general/registry/regfltr/sys/regfltr.h @@ -0,0 +1,488 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + regfltr.h + +Abstract: + + Header file for the sample driver + +Environment: + + Kernel mode only + + +--*/ + +#pragma once + +#include +#include +#include + +#include "common.h" + + +// +// Pool tags +// + +#define REGFLTR_CONTEXT_POOL_TAG '0tfR' +#define REGFLTR_CAPTURE_POOL_TAG '1tfR' + + +// +// Logging macros +// + +#define InfoPrint(str, ...) \ + DbgPrintEx(DPFLTR_IHVDRIVER_ID, \ + DPFLTR_INFO_LEVEL, \ + "%S: "##str"\n", \ + DRIVER_NAME, \ + __VA_ARGS__) + +#define ErrorPrint(str, ...) \ + DbgPrintEx(DPFLTR_IHVDRIVER_ID, \ + DPFLTR_ERROR_LEVEL, \ + "%S: %d: "##str"\n", \ + DRIVER_NAME, \ + __LINE__, \ + __VA_ARGS__) + + +// +// The root key used in the samples +// +extern HANDLE g_RootKey; + + +// +// Pointer to the device object used to register registry callbacks +// +extern PDEVICE_OBJECT g_DeviceObj; + + +// +// Registry callback version +// +extern ULONG g_MajorVersion; +extern ULONG g_MinorVersion; + + +// +// Set to TRUE if TM and RM were successfully created and the transaction +// callback was successfully enabled. +// +extern BOOLEAN g_RMCreated; + + +// +// Flag that indicates if the system is win8 or higher. This is set on +// driver entry by calling RtlVerifyVersionInfo. +// +extern BOOLEAN g_IsWin8OrGreater; + + +// +// The following are variables used to manage callback contexts handed +// out to user mode. +// + +#define MAX_CALLBACK_CTX_ENTRIES 10 + +// +// The fast mutex guarding the callback context list +// +extern FAST_MUTEX g_CallbackCtxListLock; + +// +// The list head +// +extern LIST_ENTRY g_CallbackCtxListHead; + +// +// Count of entries in list +// +extern USHORT g_NumCallbackCtxListEntries; + +// +// Context data structure for the transaction callback RMCallback +// + +typedef struct _RMCALLBACK_CONTEXT { + + // + // A bit mask of all transaction notifications types that the RM Callback is + // notified of. + // + ULONG Notification; + + // + // The handle to an enlistment + // + HANDLE Enlistment; + +} RMCALLBACK_CONTEXT, *PRMCALLBACK_CONTEXT; + + +// +// The context data structure for the registry callback. It will be passed +// to the callback function every time it is called. +// + +typedef struct _CALLBACK_CONTEXT { + + // + // List of callback contexts currently active + // + LIST_ENTRY CallbackCtxList; + + // + // Specifies which callback helper method to use + // + CALLBACK_MODE CallbackMode; + + // + // Records the current ProcessId to filter out registry operation from + // other processes. + // + HANDLE ProcessId; + + // + // Records the altitude that the callback was registered at + // + UNICODE_STRING Altitude; + WCHAR AltitudeBuffer[MAX_ALTITUDE_BUFFER_LENGTH]; + + // + // Records the cookie returned by the registry when the callback was + // registered + // + LARGE_INTEGER Cookie; + + // + // A pointer to the context for the transaction callback. + // Used to enlist on a transaction. Only used in the transaction samples. + // + PRMCALLBACK_CONTEXT RMCallbackCtx; + + // + // These fields record information for verifying the behavior of the + // certain samples. They are not used in all samples + // + + // + // Number of times the RegNtCallbackObjectContextCleanup + // notification was received + // + LONG ContextCleanupCount; + + // + // Number of times the callback saw a notification with the call or + // object context set correctly. + // + LONG NotificationWithContextCount; + + // + // Number of times callback saw a notirication without call or without + // object context set correctly + // + LONG NotificationWithNoContextCount; + + // + // Number of pre-notifications received + // + LONG PreNotificationCount; + + // + // Number of post-notifications received + // + LONG PostNotificationCount; + +} CALLBACK_CONTEXT, *PCALLBACK_CONTEXT; + + +// +// The registry and transaction callback routines +// + +EX_CALLBACK_FUNCTION Callback; + +NTSTATUS +RMCallback( + _In_ PKENLISTMENT EnlistmentObject, + _In_ PVOID RMContext, + _In_ PVOID TransactionContext, + _In_ ULONG TransactionNotification, + _Inout_ PLARGE_INTEGER TMVirtualClock, + _In_ ULONG ArgumentLength, + _In_ PVOID Argument + ); + +// +// The samples and their corresponding callback helper methods +// + +NTSTATUS +CallbackPreNotificationBlock( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +PreNotificationBlockSample(); + +NTSTATUS +CallbackPreNotificationBlock( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +PreNotificationBypassSample(); + +NTSTATUS +CallbackPreNotificationBypass( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +PostNotificationOverrideSuccessSample(); + +NTSTATUS +CallbackPostNotificationOverrideSuccess( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +PostNotificationOverrideErrorSample(); + +NTSTATUS +CallbackPostNotificationOverrideError( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +TransactionEnlistSample(); + +NTSTATUS +CallbackTransactionEnlist( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +TransactionReplaySample(); + +NTSTATUS +CallbackTransactionReplay( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +SetObjectContextSample(); + +NTSTATUS +CallbackSetObjectContext( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +SetCallContextSample(); + +NTSTATUS +CallbackSetCallContext( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +MultipleAltitudeBlockDuringPreSample(); + +BOOLEAN +MultipleAltitudeInternalInvocationSample(); + +NTSTATUS +CallbackMonitor( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +NTSTATUS +CallbackMultipleAltitude( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +NTSTATUS +CallbackCapture( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +VOID +BugCheckSample(); + +NTSTATUS +CallbackBugcheck( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +BOOLEAN +CreateOpenV1Sample(); + +NTSTATUS +CallbackCreateOpenV1( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ); + +// +// Driver dispatch functions +// + +NTSTATUS +DoCallbackSamples( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ); + +NTSTATUS +RegisterCallback( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ); + +NTSTATUS +UnRegisterCallback( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ); + +NTSTATUS +GetCallbackVersion( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp + ); + +// +// Transaction related routines +// + +NTSTATUS +CreateKTMResourceManager( + _In_ PTM_RM_NOTIFICATION CallbackRoutine, + _In_opt_ PVOID RMKey + ); + +NTSTATUS +EnlistInTransaction( + _Out_ PHANDLE EnlistmentHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PVOID Transaction, + _In_ NOTIFICATION_MASK NotificationMask, + _In_opt_ PVOID EnlistmentKey + ); + +VOID +DeleteKTMResourceManager( + ); + + +// +// Capture methods +// + +NTSTATUS +CaptureBuffer( + _Outptr_result_maybenull_ PVOID *CapturedBuffer, + _In_reads_bytes_(Length)PVOID Buffer, + _In_ SIZE_T Length, + _In_ ULONG PoolTag + ); + +VOID +FreeCapturedBuffer( + _In_ PVOID Buffer, + _In_ ULONG PoolTag + ); + +NTSTATUS +CaptureUnicodeString( + _Inout_ UNICODE_STRING * DestString, + _In_ PCUNICODE_STRING SourceString, + _In_ ULONG PoolTag + ); + +VOID +FreeCapturedUnicodeString( + _In_ UNICODE_STRING * String, + _In_ ULONG PoolTag + ); + + +// +// Utility methods +// + +PVOID +CreateCallbackContext( + _In_ CALLBACK_MODE CallbackMode, + _In_ PCWSTR AltitudeString + ); + +BOOLEAN +InsertCallbackContext( + _In_ PCALLBACK_CONTEXT CallbackCtx + ); + +PCALLBACK_CONTEXT +FindCallbackContext( + _In_ LARGE_INTEGER Cookie + ); + +PCALLBACK_CONTEXT +FindAndRemoveCallbackContext( + _In_ LARGE_INTEGER Cookie + ); + +VOID +DeleteCallbackContext( + _In_ PCALLBACK_CONTEXT CallbackCtx + ); + + +ULONG +ExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointers + ); + + diff --git a/general/registry/regfltr/sys/regfltr.rc b/general/registry/regfltr/sys/regfltr.rc new file mode 100644 index 00000000..67bc9a4b --- /dev/null +++ b/general/registry/regfltr/sys/regfltr.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Registry Filter System Driver" +#define VER_INTERNALNAME_STR "regfltr.sys" +#define VER_ORIGINALFILENAME_STR "RegFltr.sys" + +#include "common.ver" \ No newline at end of file diff --git a/general/registry/regfltr/sys/regfltr.vcxproj b/general/registry/regfltr/sys/regfltr.vcxproj new file mode 100644 index 00000000..6bddb5e4 --- /dev/null +++ b/general/registry/regfltr/sys/regfltr.vcxproj @@ -0,0 +1,214 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {12666DFF-2CD6-4000-AFE6-0796D9B6D330} + $(MSBuildProjectName) + Debug + Win32 + {3973A2A5-EFFD-4997-985D-51731D2A58CA} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + regfltr + + + regfltr + + + regfltr + + + regfltr + + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + Disabled + true + true + Level4 + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntoskrnl.lib + + + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + Disabled + true + true + Level4 + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntoskrnl.lib + + + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + Disabled + true + true + Level4 + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntoskrnl.lib + + + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + Disabled + true + true + Level4 + + + %(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);..\exe + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntoskrnl.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/registry/regfltr/sys/regfltr.vcxproj.Filters b/general/registry/regfltr/sys/regfltr.vcxproj.Filters new file mode 100644 index 00000000..22722767 --- /dev/null +++ b/general/registry/regfltr/sys/regfltr.vcxproj.Filters @@ -0,0 +1,61 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {2B50099F-1EBF-480D-9136-448FBDF31F1F} + + + h;hpp;hxx;hm;inl;inc;xsd + {1001336B-4543-45F1-A020-C5E84A980E24} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9EEC1AF7-63F4-44AB-81FA-A0DAC270B821} + + + inf;inv;inx;mof;mc; + {AE93F2E1-863E-4BAE-BC5D-09810656976E} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/registry/regfltr/sys/txr.c b/general/registry/regfltr/sys/txr.c new file mode 100644 index 00000000..6fd3c8a6 --- /dev/null +++ b/general/registry/regfltr/sys/txr.c @@ -0,0 +1,784 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + TxR.c + +Abstract: + + Samples that show how to deal with transactional registry operations. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +BOOLEAN +TransactionEnlistSample( + ) +/*++ + +Routine Description: + + This sample shows how to enlist to a transaction that a registry operation + is part of inorder to get notifications when it commits or aborts. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ + +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + PRMCALLBACK_CONTEXT RMCallbackCtx = NULL; + NTSTATUS Status; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES KeyAttributes; + OBJECT_ATTRIBUTES TxAttributes; + HANDLE Key = NULL; + HANDLE Transaction = NULL; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Transaction Enlist Sample ===="); + + if (!g_RMCreated) { + ErrorPrint("Sample can't run because KTM data structures were not successfully created."); + goto Exit; + } + + // + // Create the registry callback context and the transaction callback context. + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_TRANSACTION_ENLIST, + CALLBACK_ALTITUDE); + if (CallbackCtx == NULL) { + goto Exit; + } + + RMCallbackCtx = (PRMCALLBACK_CONTEXT) ExAllocatePoolWithTag ( + PagedPool, + sizeof(RMCALLBACK_CONTEXT), + REGFLTR_CONTEXT_POOL_TAG); + if (RMCallbackCtx == NULL) { + goto Exit; + } + RtlZeroMemory(RMCallbackCtx, sizeof(RMCALLBACK_CONTEXT)); + CallbackCtx->RMCallbackCtx = RMCallbackCtx; + + // + // Create a transaction + // + + InitializeObjectAttributes(&TxAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateTransaction(&Transaction, + TRANSACTION_ALL_ACCESS, + &TxAttributes, + NULL, + NULL, + 0, + 0, + 0, + NULL, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CreateTransaction failed. Status 0x%x", Status); + goto Exit; + } + + // + // Register the callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Success = TRUE; + + // + // Create a key + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwCreateKeyTransacted(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + Transaction, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKeyTransacted failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Commit the transaction + // + + Status = ZwCommitTransaction(Transaction, TRUE); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCommitTransaction failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + // + // Check that the transaction callback context records a commit notification + // + + if (RMCallbackCtx->Notification != TRANSACTION_NOTIFY_COMMIT) { + ErrorPrint("RMContext notification mask is 0x%x instead of 0x%x.", + RMCallbackCtx->Notification, + TRANSACTION_NOTIFY_COMMIT); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Transaction != NULL) { + ZwClose(Transaction); + } + + // + // Need to reopen the key to delete it because the previous + // handle was part of a transaction that is now gone. + // + + if (Key != NULL) { + ZwClose(Key); + Key = NULL; + } + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + ZwOpenKey(&Key, KEY_ALL_ACCESS, &KeyAttributes); + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (RMCallbackCtx != NULL) { + ExFreePoolWithTag(RMCallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Transaction Enlist Demo succeeded."); + } else { + ErrorPrint("Transaction Enlist Demo FAILED."); + } + + return Success; +} + + +NTSTATUS +CallbackTransactionEnlist( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 +) +/*++ + +Routine Description: + + This helper callback routine shows hot to enlist on a transaction. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + PVOID Transaction = NULL; + + switch(NotifyClass) { + + case RegNtPreCreateKeyEx: + + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2; + + // + // Get the transaction object + // + + Transaction = PreCreateInfo->Transaction; + if (Transaction == NULL) { + + // + // Even if the transaction is not provided in the + // REG_Xxx_INFORMATION, we need to call CmGetBoundTransaction + // on the RootObject to check that it isn't associated with + // a transaction. + // + + Transaction = CmGetBoundTransaction(&CallbackCtx->Cookie, + PreCreateInfo->RootObject); + + if (Transaction == NULL) { + ErrorPrint("CreateKey is unexpectedly not transacted."); + break; + } + } + + // + // Use the volatile RM created in CreateKTMResourceManager() + // to enlist in the transaction. We want notifications for + // when the transaction commits or rolls back. + // + // Note: Make sure the callback routine handles all the + // notifications requested here. Look at RMCallback() to see + // how to handle notifications. + // + + Status = EnlistInTransaction(&CallbackCtx->RMCallbackCtx->Enlistment, + ENLISTMENT_SUBORDINATE_RIGHTS, + Transaction, + TRANSACTION_NOTIFY_COMMIT | + TRANSACTION_NOTIFY_ROLLBACK, + CallbackCtx->RMCallbackCtx); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("EnlistInTransaction failed. Status 0x%x.", Status); + } + + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + return Status; +} + + + +BOOLEAN +TransactionReplaySample( + ) +/*++ + +Routine Description: + + This sample shows how to copy a transactional create key operation. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ + +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES KeyAttributes; + OBJECT_ATTRIBUTES TxAttributes; + HANDLE Key = NULL; + HANDLE Transaction = NULL; + HANDLE TransactedRoot = NULL; + BOOLEAN bSuccess = FALSE; + + InfoPrint(""); + InfoPrint("=== Transaction Replay Sample ===="); + + if (!g_RMCreated) { + ErrorPrint("Sample can't run because KTM data structures were not successfully created."); + goto Exit; + } + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_TRANSACTION_REPLAY, + CALLBACK_ALTITUDE); + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Create a transaction + // + + InitializeObjectAttributes(&TxAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateTransaction(&Transaction, + TRANSACTION_ALL_ACCESS, + &TxAttributes, + NULL, + NULL, + 0, + 0, + 0, + NULL, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CreateTransaction failed. Status 0x%x", Status); + goto Exit; + } + + // + // Open a transacted handle to the root key + // + + RtlInitUnicodeString(&Name, ROOT_KEY_ABS_PATH); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwOpenKeyTransacted(&TransactedRoot, + KEY_ALL_ACCESS, + &KeyAttributes, + Transaction); + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKeyTransacted failed. Status 0x%x",Status); + goto Exit; + } + + // + // Register callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + bSuccess = TRUE; + + // + // Create a key using the transacted root key handle. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + TransactedRoot, + NULL); + Status = ZwCreateKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + NULL, + 0, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + bSuccess = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + bSuccess = FALSE; + } + + + // + // Verify that the key created exists and that a key with the + // "modified" name is also exists. + // + + if (Key != NULL) { + ZwClose(Key); + Key = NULL; + } + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + bSuccess = FALSE; + } else { + ZwClose(Key); + Key = NULL; + } + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + TransactedRoot, + NULL); + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKey failed. Status 0x%x", Status); + bSuccess = FALSE; + } else { + ZwClose(Key); + Key = NULL; + } + + // + // Roll back transaction + // + + Status = ZwRollbackTransaction(Transaction, TRUE); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwRollbackTransaction failed. Status 0x%x", Status); + bSuccess = FALSE; + goto Exit; + } + + // + // Check that both keys no longer exist. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwOpenKey returned unexpected status 0x%x. Expected 0x%x", + Status, + STATUS_OBJECT_NAME_NOT_FOUND); + bSuccess = FALSE; + } + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + Key = NULL; + } + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwOpenKey returned unexpected status 0x%x. Expected 0x%x", + Status, + STATUS_OBJECT_NAME_NOT_FOUND); + bSuccess = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (TransactedRoot!= NULL) { + ZwDeleteKey(TransactedRoot); + ZwClose(TransactedRoot); + } + + if (Transaction != NULL) { + ZwClose(Transaction); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (bSuccess) { + InfoPrint("Transaction Replay Sample succeeded."); + } else { + ErrorPrint("Transaction Replay Sample FAILED."); + } + + return bSuccess; + +} + + + +NTSTATUS +CallbackTransactionReplay( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine shows how to get the transaction associated + with a registry operation and shows how to do another operation in the + same transaction. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PREG_CREATE_KEY_INFORMATION PreCreateInfo; + HANDLE TransactionHandle = NULL; + PVOID Transaction = NULL; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + UNICODE_STRING LocalClass = {0}; + PUNICODE_STRING Class = NULL; + HANDLE Key = NULL; + HANDLE RootKey = NULL; + KPROCESSOR_MODE Mode = KernelMode; + + switch(NotifyClass) { + + case RegNtPreCreateKeyEx: + + PreCreateInfo = (PREG_CREATE_KEY_INFORMATION) Argument2; + + // + // Get the transaction object + // + + Transaction = PreCreateInfo->Transaction; + if (Transaction == NULL) { + + // + // Even if the transaction is not provided in the + // REG_Xxx_INFORMATION, we need to call CmGetBoundTransaction + // on the RootObject to check that it isn't associated with + // a transaction. + // + + Transaction = CmGetBoundTransaction(&CallbackCtx->Cookie, + PreCreateInfo->RootObject); + if (Transaction == NULL) { + ErrorPrint("CreateKey is unexpectedly not transacted."); + break; + } + } + + // + // Get a handle to the transaction object + // + + Status = ObOpenObjectByPointer(Transaction, + OBJ_KERNEL_HANDLE, + NULL, + TRANSACTION_ALL_ACCESS, + *TmTransactionObjectType, + KernelMode, + &TransactionHandle); + + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObReferenceObjectByPointer failed. Status 0x%x", Status); + break; + } + + // + // Next replay the create key using the transacted version of the + // API and the transaction handle. + // + + Status = ObOpenObjectByPointer(PreCreateInfo->RootObject, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, // Getting handle with all access + PreCreateInfo->ObjectType, + KernelMode, + &RootKey); + if (!NT_SUCCESS (Status)) { + ErrorPrint("ObReferenceObjectByPointer failed. Status 0x%x", Status); + break; + } + + + // + // REG_CREATE_KEY_INFORMATION is a partially structure. The class + // field's buffer is not captured. Since it is passed to + // ZwCreateKeyTransacted, it needs to be captured. + // + // *Note: in Windows 8 all fields are captured. See capture.c + // for more details. + // + + Mode = ExGetPreviousMode(); + + if (!g_IsWin8OrGreater && Mode == UserMode) { + Status = CaptureUnicodeString(&LocalClass, + PreCreateInfo->Class, + REGFLTR_CAPTURE_POOL_TAG); + if (!NT_SUCCESS(Status)) { + break; + } + Class = &LocalClass; + + } else { + Class = PreCreateInfo->Class; + } + + + RtlInitUnicodeString(&Name, MODIFIED_KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + RootKey, + PreCreateInfo->SecurityDescriptor); + + Status = ZwCreateKeyTransacted(&Key, + KEY_ALL_ACCESS, + &KeyAttributes, + 0, + Class, + PreCreateInfo->CreateOptions, + TransactionHandle, + PreCreateInfo->Disposition); + + ZwClose(RootKey); + ZwClose(TransactionHandle); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateKeyTransacted failed. Status 0x%x.", Status); + break; + } + + ZwClose(Key); + InfoPrint("\tCallback: Create key %wZ replayed in same transaction context.", + PreCreateInfo->CompleteName); + Status = STATUS_SUCCESS; + break; + + default: + // + // Do nothing for other notifications + // + break; + } + + // + // Free buffers used for capturing user mode values. + // + + if (LocalClass.Buffer != NULL) { + FreeCapturedUnicodeString(&LocalClass, REGFLTR_CAPTURE_POOL_TAG); + } + + return Status; +} + diff --git a/general/registry/regfltr/sys/txrutil.c b/general/registry/regfltr/sys/txrutil.c new file mode 100644 index 00000000..337e5cc4 --- /dev/null +++ b/general/registry/regfltr/sys/txrutil.c @@ -0,0 +1,283 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + txrutil.c + +Abstract: + + Utility functions for working with transaction registry operations + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +static HANDLE ResourceManager = NULL; +static HANDLE TransactionManager = NULL; + + +NTSTATUS +CreateKTMResourceManager( + _In_ PTM_RM_NOTIFICATION CallbackRoutine, + _In_opt_ PVOID RMKey + ) +/*++ + +Routine Description: + + This method will create a volatile Transaction Manager (TM) and a volatile + Resource Manager (RM) and enable callback notification through them. + The RM created here is used to enlist onto a transaction so that the + RMCallback routine will be called when the transaction commits or aborts. + +Arguments: + + CallbackRoutine - Pointer to a ResourceManagerNotification Routine + + RMKey - A caller-defined context value that uniquely identifies the + resource manager. The callback routine receives this value as + input. + Note: When you are enlisting to a transaction, you can pass in a + context that is specific to that particular enlistment. + +Return Value: + + NTSTATUS + +--*/ +{ + OBJECT_ATTRIBUTES ObjAttributes; + PKRESOURCEMANAGER RMObject; + NTSTATUS Status = STATUS_SUCCESS; + HANDLE TMHandle = NULL; + HANDLE RMHandle = NULL; + GUID RMGuid; + + InfoPrint("Creating KTM Resource Manager"); + + // + // Create the volatile TM + // + + InitializeObjectAttributes(&ObjAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateTransactionManager(&TMHandle, + TRANSACTIONMANAGER_ALL_ACCESS, + &ObjAttributes, + NULL, + TRANSACTION_MANAGER_VOLATILE, + 0); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CreateTransactionManager failed. Status 0x%x", Status); + goto Exit; + } + + // + // Create the volatile RM + // + + Status = ExUuidCreate(&RMGuid); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ExUuidCreate failed. Status 0x%x", Status); + goto Exit; + } + + InitializeObjectAttributes(&ObjAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateResourceManager(&RMHandle, + RESOURCEMANAGER_ALL_ACCESS, + TMHandle, + &RMGuid, + &ObjAttributes, + + RESOURCE_MANAGER_VOLATILE, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CreateResourceManager failed. Status 0x%x", Status); + goto Exit; + } + + // + // Grab the RM object from the handle + // + + Status = ObReferenceObjectByHandle(RMHandle, + 0, + NULL, + KernelMode, + (PVOID *) &RMObject, + NULL); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ObReferenceObjectbyHandle failed. Status 0x%x", Status); + goto Exit; + } + + // + // Enable callbacks and pass in our notification routine + // + + Status = TmEnableCallbacks(RMObject, + CallbackRoutine, + RMKey); + + ObDereferenceObject(RMObject); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("TmEnableCallbacks failed. Status 0x%x", Status); + goto Exit; + } + + Exit: + + if (!NT_SUCCESS(Status)) { + if (RMHandle != NULL) { + ZwClose(RMHandle); + } + if (TMHandle!= NULL) { + ZwClose(TMHandle); + } + } else { + ResourceManager = RMHandle; + TransactionManager = TMHandle; + } + + return Status; +} + + + +NTSTATUS +EnlistInTransaction( + _Out_ PHANDLE EnlistmentHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PVOID Transaction, + _In_ NOTIFICATION_MASK NotificationMask, + _In_opt_ PVOID EnlistmentKey + ) +/*++ + +Routine Description: + + This method is a wrapper around ZwCreateEnlistment. It outputs a handle + to the enlistment object which represent's a resource manager's + enlistment to a transaction. Enlisting to a transaction allows the + resource manager to receive notifications about a transaction's events. + +Arguments: + + EnlistmentHandle - Pointer to variable that receives the handle to the + new enlistment object. + + DesiredAccess - Specifies the requested access to the enlistment object. + + Transaction - Transaction object + + NotificationMask - A bitwise OR of TRANSACTION_NOTIFY_Xxx values defined + in Ktmtypes.h. It specifies the types of transaction + notifications that KTM will send to the caller. + + EnlistmentKey - A caller-defined context value that uniquely identifies the + enlistment. The callback routine registered when callbacks + were enabled in the resource manager receives this value. + +Return Value: + + NTSTATUS + +--*/ +{ + + NTSTATUS Status; + HANDLE TransactionHandle = NULL; + OBJECT_ATTRIBUTES ObjAttributes; + + // + // Get a handle to the transaction object + // + + Status = ObOpenObjectByPointer(Transaction, + OBJ_KERNEL_HANDLE, + NULL, + TRANSACTION_ALL_ACCESS, + *TmTransactionObjectType, + KernelMode, + &TransactionHandle); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ObOpenObjectByPointer failed. Status 0x%x.", Status); + return Status; + } + + // + // Use the transaction handle and the volatile RM created in + // CreateKTMResourceManager() to enlist to the transaction. + // + + InitializeObjectAttributes(&ObjAttributes, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL); + + Status = ZwCreateEnlistment(EnlistmentHandle, + DesiredAccess, + ResourceManager, + TransactionHandle, + &ObjAttributes, + 0, + NotificationMask, + EnlistmentKey); + + ZwClose(TransactionHandle); + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwCreateEnlistment failed. Status 0x%x", Status); + } + + return Status; + +} + + +VOID +DeleteKTMResourceManager( + ) +/*++ + +Routine Description: + + Clean up any resources associated wtih the resource manager. + +--*/ +{ + if (ResourceManager != NULL) { + ZwClose(ResourceManager); + ResourceManager = NULL; + } + if (TransactionManager != NULL) { + ZwClose(TransactionManager); + TransactionManager = NULL; + } +} diff --git a/general/registry/regfltr/sys/util.c b/general/registry/regfltr/sys/util.c new file mode 100644 index 00000000..068416c8 --- /dev/null +++ b/general/registry/regfltr/sys/util.c @@ -0,0 +1,304 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + util.c + +Abstract: + + Utility routines for the sample driver. + +Environment: + + Kernel mode only + +--*/ + + +#include "regfltr.h" + + +FAST_MUTEX g_CallbackCtxListLock; +LIST_ENTRY g_CallbackCtxListHead; +USHORT g_NumCallbackCtxListEntries; + + +ULONG +ExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointers + ) +/*++ + +Routine Description: + + ExceptionFilter breaks into the debugger if an exception happens + inside the callback. + +Arguments: + + ExceptionPointers - unused + +Return Value: + + Always returns EXCEPTION_CONTINUE_SEARCH + +--*/ +{ + + ErrorPrint("Exception %lx, ExceptionPointers = %p", + ExceptionPointers->ExceptionRecord->ExceptionCode, + ExceptionPointers); + + DbgBreakPoint(); + + return EXCEPTION_EXECUTE_HANDLER; + +} + + +PVOID +CreateCallbackContext( + _In_ CALLBACK_MODE CallbackMode, + _In_ PCWSTR AltitudeString + ) +/*++ + +Routine Description: + + Utility method to create a callback context. Callback context + should be freed using DeleteCallbackContext. + +Arguments: + + CallbackMode - the callback mode value + + AltitudeString - a string with the altitude the callback will be + registered at + +Return Value: + + Pointer to the allocated and initialized callback context + +--*/ +{ + + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + BOOLEAN Success = FALSE; + + CallbackCtx = (PCALLBACK_CONTEXT) ExAllocatePoolWithTag ( + PagedPool, + sizeof(CALLBACK_CONTEXT), + REGFLTR_CONTEXT_POOL_TAG); + + if (CallbackCtx == NULL) { + ErrorPrint("CreateCallbackContext failed due to insufficient resources."); + goto Exit; + } + + RtlZeroMemory(CallbackCtx, sizeof(CALLBACK_CONTEXT)); + + CallbackCtx->CallbackMode = CallbackMode; + CallbackCtx->ProcessId = PsGetCurrentProcessId(); + + Status = RtlStringCbPrintfW(CallbackCtx->AltitudeBuffer, + MAX_ALTITUDE_BUFFER_LENGTH * sizeof(WCHAR), + L"%s", + AltitudeString); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("RtlStringCbPrintfW in CreateCallbackContext failed. Status 0x%x", Status); + goto Exit; + } + + RtlInitUnicodeString (&CallbackCtx->Altitude, CallbackCtx->AltitudeBuffer); + + Success = TRUE; + + Exit: + + if (Success == FALSE) { + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + CallbackCtx = NULL; + } + } + + return CallbackCtx; + +} + + +BOOLEAN +InsertCallbackContext( + _In_ PCALLBACK_CONTEXT CallbackCtx + ) +/*++ + +Routine Description: + + Utility method to insert the callback context into a list. + +Arguments: + + CallbackCtx - the callback context to insert + +Return Value: + + TRUE if successful, FALSE otherwise + +--*/ +{ + + BOOLEAN Success = FALSE; + + ExAcquireFastMutex(&g_CallbackCtxListLock); + + if (g_NumCallbackCtxListEntries < MAX_CALLBACK_CTX_ENTRIES) { + g_NumCallbackCtxListEntries++; + InsertHeadList(&g_CallbackCtxListHead, &CallbackCtx->CallbackCtxList); + Success = TRUE; + } else { + ErrorPrint("Insert Callback Ctx failed: Max CallbackCtx entries reached."); + } + + ExReleaseFastMutex(&g_CallbackCtxListLock); + + return Success; + +} + + +PCALLBACK_CONTEXT +FindCallbackContext( + _In_ LARGE_INTEGER Cookie + ) +/*++ + +Routine Description: + + Utility method to find a callback context using the cookie value. + +Arguments: + + Cookie - the cookie value associated with the callback context. The + cookie is returned when CmRegisterCallbackEx is called. + +Return Value: + + Pointer to the found callback context + +--*/ +{ + + PCALLBACK_CONTEXT CallbackCtx = NULL; + PLIST_ENTRY Entry; + + ExAcquireFastMutex(&g_CallbackCtxListLock); + + Entry = g_CallbackCtxListHead.Flink; + while (Entry != &g_CallbackCtxListHead) { + + CallbackCtx = CONTAINING_RECORD(Entry, + CALLBACK_CONTEXT, + CallbackCtxList); + if (CallbackCtx->Cookie.QuadPart == Cookie.QuadPart) { + break; + } + + Entry = Entry->Flink; + } + + ExReleaseFastMutex(&g_CallbackCtxListLock); + + if (CallbackCtx == NULL) { + ErrorPrint("FindCallbackContext failed: No context with specified cookied was found."); + } + + return CallbackCtx; + +} + +PCALLBACK_CONTEXT +FindAndRemoveCallbackContext( + _In_ LARGE_INTEGER Cookie + ) +/*++ + +Routine Description: + + Utility method to find a callback context using the cookie value and then + remove it. + +Arguments: + + Cookie - the cookie value associated with the callback context. The + cookie is returned when CmRegisterCallbackEx is called. + +Return Value: + + Pointer to the found callback context + +--*/ +{ + + PCALLBACK_CONTEXT CallbackCtx = NULL; + PLIST_ENTRY Entry; + + ExAcquireFastMutex(&g_CallbackCtxListLock); + + Entry = g_CallbackCtxListHead.Flink; + while (Entry != &g_CallbackCtxListHead) { + + CallbackCtx = CONTAINING_RECORD(Entry, + CALLBACK_CONTEXT, + CallbackCtxList); + if (CallbackCtx->Cookie.QuadPart == Cookie.QuadPart) { + RemoveEntryList(&CallbackCtx->CallbackCtxList); + g_NumCallbackCtxListEntries--; + break; + } + } + + ExReleaseFastMutex(&g_CallbackCtxListLock); + + if (CallbackCtx == NULL) { + ErrorPrint("FindAndRemoveCallbackContext failed: No context with specified cookied was found."); + } + + return CallbackCtx; +} + + +VOID +DeleteCallbackContext( + _In_ PCALLBACK_CONTEXT CallbackCtx + ) +/*++ + +Routine Description: + + Utility method to delete a callback context. + +Arguments: + + CallbackCtx - the callback context to insert + +Return Value: + + None + +--*/ +{ + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + +} diff --git a/general/registry/regfltr/sys/version.c b/general/registry/regfltr/sys/version.c new file mode 100644 index 00000000..11edc9a3 --- /dev/null +++ b/general/registry/regfltr/sys/version.c @@ -0,0 +1,611 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Version.c + +Abstract: + + Information and samples that describe: + 1. Changes in registry callback version 1.1 + 2. How to use the version 1 REG_OPEN_KEY_INFORMATION and + REG_CREATE_KEY_INFORMATION structures + 3. Work arounds for issues in callback version 1.0. + +Environment: + + Kernel mode only + +--*/ + +#include "regfltr.h" + + +/*++ + + Callback Version 1.1 is available in Windows 7 and Windows Server 2008 R2. + It is NOT available on Vista or Windows Server 2008 as of Service Pack 2. + + + Issues in callback version 1.0 that have been fixed in version 1.1: + + 1. In the post-notification phase for a create or open key operation, + the PostInfo->Object field might not be NULL even if the operation was + unsuccessful as indicated by PostInfo->ReturnStatus. + + This problem happens when there are multiple registry filter drivers + registered and one of the drivers blocks the operation in the + pre-notification phase by returning a nonsuccess status. Filter drivers + that are at higher altitudes will receive a post-notification where + PostInfo->ReturnStatus is the nonsuccess status value but + PostInfo->Object will not be NULL. PostInfo->Object in this case will + be equal to PostInfo->PreInfo->RootObject. + + 2. In version 1.0, an uncatched exception in a registry callback + routine will be swallowed by the system. In version 1.1 this has been + changed so an uncatched exception will cause the machine to bugcheck. + We provide a sample in this file (BugCheckSample) but obviously it is not + run. + + NOTE: While bugchecking the system is not a good thing to do, we do not + recommend putting your entire callback routine in one big try-except block + and swallow legitimate exceptions like possible pool corruptions. Please + keep what you wrap with a try-except block to the bare minimum. + +--*/ + +/*++ + + Version 1 of the create and open key REG_Xxx_INFORMATION structure is + available in Windows 7 and Windows Server 2008 R2. It is NOT available on + Vista or Windows Server 2008 as of Service Pack 2. + + NOTE: While Version 1 of the create and open key data structures will + likely be available on systems that have callback version 1.1, this + relationship is not guaranteed. You must check the create and open key + data structure to see its version rather than depending on the + callback version. See CreateOpenV1Sample on how to check the version. + + + Issues addressed by version 1 of the create and open key data structures: + + 1. Without the Attributes field provided in the V1 create and open + REG_Xxx_INFORMATION structure, there is no way to exactly replicate + certain create and open operations. See CreateOpenV1Sample for a + demonstration. Unfortunately there is no work around for this issue. + + 2. The PreInfo->CompleteName and PreInfo->RootObject fields in a + create or open key operation do not behave as expected when the key to be + opened or created is represented as an absolute path. + REG_CREATE_KEY_INFORMATION_V1 and REG_OPEN_KEY_INFORMATION_V1 contain a + field PreInfoV1->RemainingName which addresses this issue. + + Example: + + Open operation on this key: \REGISTRY\MACHINE\Software\_RegFltrRoot + + One way of relatively opening the key is to open "Software\_RegFltrRoot" + relative to \REGISTRY\MACHINE. + + RegOpenKeyEx(HKEY_LOCAL_MACHINE, + "Software\\_RegFltrRoot", + 0, + KEY_ALL_ACCESS, + &Key); + + In this case the value of the fields in REG_OPEN_KEY_INFORMATION_V1 + would be: + + RootObject - Handle to the key \REGISTRY\MACHINE + CompleteName - "Software\\_RegFltrRoot" + RemainingName - "Software\\_RegFltrRoot" + + + If the open uses an absolute path, + + RtlInitUnicodeString(&Name, L"\\REGISTRY\\MACHINE\\Software\\_RegFltrRoot") + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL); + + ZwOpenKey(&Key, KEY_ALL_ACCESS, &KeyAttributes); + + the value of the fields in REG_OPEN_KEY_INFORMATION_V1 would be: + + RootObject - Handle to the key \REGISTRY + CompleteName - "\\REGISTRY\\MACHINE\\Software\\_RegFltrRoot" + RemainingName - "MACHINE\\Software\\_RegFltrRoot" + + Note that RootObject is not NULL even though CompleteName holds the + absolute path to the key. + + + The work around for this on systems without REG_OPEN_KEY_INFORMATION_V1 is + to check if the first character of CompleteName is a '\'. If that is the + case you can be sure that CompleteName is holding an absolute path to the + key. + +--*/ + + + +VOID +BugCheckSample( + ) +/*++ + +Routine Description: + + In version 1.1, if a registry filter driver's callback routine throws + an exception the registry will bugcheck the machine: + + REGISTRY_FILTER_DRIVER_EXCEPTION (0x135) + This bugcheck is caused by an unhandled exception in a registry + filtering driver. + + PARAMETERS + 1 - ExceptionCode + 2 - Address of the context record for the exception that caused + the bugcheck + 3 - The driver's callback routine address + 4 - Internal + + DESCRIPTION + This bugcheck indicates that a registry filtering driver didn't handle + exception inside its notification routine. One can identify the driver + by the 3rd parameter. + + In version 1.0, an exception in the callback routine is simply swallowed and + ignored. + + This sample uses a simple callback routine that will access NULL to throw + an exception. The sample is not normally run and is only here for + demonstration purposes. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + HANDLE Key = NULL; + + InfoPrint(""); + InfoPrint("=== Bugcheck Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_VERSION_BUGCHECK, + CALLBACK_ALTITUDE); + + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + // + // Do an open key just to invoke the callback. + // In version 1.1 this will bugcheck and nothing else will run. + // In version 1.0 the open will simply fail as expected. + // + + RtlInitUnicodeString(&Name, KEY_NAME); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + g_RootKey, + NULL); + + Status = ZwOpenKey(&Key, + KEY_ALL_ACCESS, + &KeyAttributes); + + if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { + ErrorPrint("ZwOpenKey returned unexpected status 0x%x", Status); + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwDeleteKey(Key); + ZwClose(Key); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + return; + +} + + +NTSTATUS +CallbackBugcheck( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine throws an exception by dereferencing a + null pointer. + + NOTE: While bugchecking the system is not a good thing to do, we do not + recommend putting your entire callback routine in one big try-except block + and swallow legitimate exceptions like possible pool corruptions. Please + keep what you wrap with a try-except block to the bare minimum. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + Always STATUS_SUCCESS; + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PULONG NullPointer = NULL; + + UNREFERENCED_PARAMETER(CallbackCtx); + UNREFERENCED_PARAMETER(NotifyClass); + UNREFERENCED_PARAMETER(Argument2); + + + InfoPrint("\tCallback is about to throw an exception."); + if (g_MajorVersion == 1 && g_MinorVersion == 0) { + InfoPrint("\tException will be swallowed by registry"); + } else { + ErrorPrint("Exception will cause machine to bugcheck"); + } + + + #pragma prefast(suppress: 6011, "Sample is purposefully dereferencing a null pointer."); + *NullPointer = 0; + + return Status; +} + + +BOOLEAN +CreateOpenV1Sample( + ) +/*++ + +Routine Description: + + This sample shows how the information in the Attributes field of + the REG_OPEN_KEY_INFORMATION_V1 data structure can change the outcome of + a registry operation. Without the Attributes field, it is impossible for + the callback routine to accuratel replay certain registry operations. + + A special key is used in this sample which has security set on it to + protect it from being deleted: + + \REGISTRY\MACHINE\SYSTEM\CurrentControlSet\Enum + + In the sample we try to open this key with DELETE access. Normally this + will work if we do it in kernel mode since the system bypasses all + access checks on handles created in kernel mode. However here we set the + OBJ_FORCE_ACCESS_CHECK flag which tells the system to perform all access + checks on the handle. + + In the callback routine associated with this sample, we will replay the + open operation with and without the flag to show how the presence of + the attributes information can change the outcome of the operation. + +Return Value: + + TRUE if the sample completed successfully. + +--*/ +{ + PCALLBACK_CONTEXT CallbackCtx = NULL; + NTSTATUS Status; + OBJECT_ATTRIBUTES KeyAttributes; + UNICODE_STRING Name; + WCHAR NameBuffer[] = L"\\registry\\machine\\system\\currentcontrolset\\enum"; + HANDLE Key = NULL; + BOOLEAN Success = FALSE; + + InfoPrint(""); + InfoPrint("=== Create/Open V1 Sample ===="); + + // + // Create the callback context + // + + CallbackCtx = CreateCallbackContext(CALLBACK_MODE_VERSION_CREATE_OPEN_V1, + CALLBACK_ALTITUDE); + + if (CallbackCtx == NULL) { + goto Exit; + } + + // + // Register callback + // + + Status = CmRegisterCallbackEx(Callback, + &CallbackCtx->Altitude, + g_DeviceObj->DriverObject, + (PVOID) CallbackCtx, + &CallbackCtx->Cookie, + NULL); + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmRegisterCallback failed. Status 0x%x", Status); + goto Exit; + } + + Success = TRUE; + + // + // Try to open the special key with delete access but have the + // OBJ_FORCE_ACCESS_CHECK flag in object attributes. This operation + // should fail with access denied. + // + + RtlInitUnicodeString(&Name, NameBuffer); + InitializeObjectAttributes(&KeyAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | + OBJ_FORCE_ACCESS_CHECK, + NULL, + NULL); + + Status = ZwOpenKey(&Key, + DELETE, + &KeyAttributes); + + if (Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwOpenKey returned unexpected status 0x%x", Status); + Success = FALSE; + } + + // + // Unregister the callback + // + + Status = CmUnRegisterCallback(CallbackCtx->Cookie); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("CmUnRegisterCallback failed. Status 0x%x", Status); + Success = FALSE; + } + + Exit: + + // + // Clean up + // + + if (Key != NULL) { + ZwClose(Key); + } + + if (CallbackCtx != NULL) { + ExFreePoolWithTag(CallbackCtx, REGFLTR_CONTEXT_POOL_TAG); + } + + if (Success) { + InfoPrint("Create/Open V1 sample succeeded."); + } else { + ErrorPrint("Create/Open V1 sample failed."); + } + + return Success; + +} + + +NTSTATUS +CallbackCreateOpenV1( + _In_ PCALLBACK_CONTEXT CallbackCtx, + _In_ REG_NOTIFY_CLASS NotifyClass, + _Inout_ PVOID Argument2 + ) +/*++ + +Routine Description: + + This helper callback routine will show how to check whether the system + supports version 1 of the REG_OPEN_KEY_INFORMATION structure. + + If version 1 is supported, the callback routine will replay the open + operation during the pre-notification phase with and without the + Attributes field found in REG_OPEN_KEY_INFORMATION_V1 to show how the + outcome is different. + +Arguments: + + CallbackContext - The value that the driver passed to the Context parameter + of CmRegisterCallbackEx when it registers this callback routine. + + NotifyClass - A REG_NOTIFY_CLASS typed value that identifies the type of + registry operation that is being performed and whether the callback + is being called in the pre or post phase of processing. + + Argument2 - A pointer to a structure that contains information specific + to the type of the registry operation. The structure type depends + on the REG_NOTIFY_CLASS value of Argument1. + +Return Value: + + Always STATUS_SUCCESS; + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + PREG_OPEN_KEY_INFORMATION_V1 PreOpenInfo; + OBJECT_ATTRIBUTES KeyAttributes; + HANDLE Key = NULL; + HANDLE RootKey = NULL; + + + UNREFERENCED_PARAMETER(CallbackCtx); + + // + // Check for the pre-notification phase of a create operation + // + + if (NotifyClass != RegNtPreOpenKeyEx) { + goto Exit; + } + + PreOpenInfo = (PREG_OPEN_KEY_INFORMATION_V1) Argument2; + + // + // Check if version 1 is available on this system. If not, + // simply return success. + // + + InfoPrint("\tREG_OPEN_KEY_INFORMATION structure's version is 0x%p", + (PVOID)PreOpenInfo->Version); + + if ((ULONG_PTR) PreOpenInfo->Version != 1) { + InfoPrint("Create/Open v1 sample is only for version 1"); + goto Exit; + } + + // + // Open a handle to the root object + // + + Status = ObOpenObjectByPointer(PreOpenInfo->RootObject, + OBJ_KERNEL_HANDLE, + NULL, + KEY_ALL_ACCESS, + PreOpenInfo->ObjectType, + KernelMode, + &RootKey); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ObObjectByPointer failed. Status 0x%x", Status); + goto Exit; + } + + // + // Do the open with the same attributes as in the original call. This + // includes the OBJ_FORCE_ACCESS_CHECK flag which will cause the open + // operation to fail. + // + // Note: The openkey operation might have originated from user mode so the + // OBJ_KERNEL_HANDLE flag needs to be explicitly added to the attributes + // to prevent user mode handle spoofing attacks. + // + + + InitializeObjectAttributes(&KeyAttributes, + PreOpenInfo->RemainingName, + PreOpenInfo->Attributes | OBJ_KERNEL_HANDLE, + RootKey, + PreOpenInfo->SecurityDescriptor); + + Status = ZwOpenKey(&Key, + PreOpenInfo->DesiredAccess, + &KeyAttributes); + + if (NT_SUCCESS(Status)) { + ZwClose(Key); + Key = NULL; + } + + if (Status != STATUS_ACCESS_DENIED) { + ErrorPrint("ZwOpenKey with attributes returned unexpected status 0x%x", Status); + Status = STATUS_UNSUCCESSFUL; + goto Exit; + } + + // + // Do the open without the attributes in the original call. The open + // operation will succeed now because the access checks will not be + // performed. + // + + InitializeObjectAttributes(&KeyAttributes, + PreOpenInfo->RemainingName, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + RootKey, + PreOpenInfo->SecurityDescriptor); + + Status = ZwOpenKey(&Key, + PreOpenInfo->DesiredAccess, + &KeyAttributes); + + if (!NT_SUCCESS(Status)) { + ErrorPrint("ZwOpenKey without attributes returned unexpected status 0x%x", Status); + goto Exit; + } + + Status = STATUS_SUCCESS; + + + Exit: + + if (Key != NULL) { + ZwClose(Key); + } + + if (RootKey != NULL) { + ZwClose(RootKey); + } + + return Status; +} + diff --git a/general/toaster/toastpkg/ReadMe.md b/general/toaster/toastpkg/ReadMe.md new file mode 100644 index 00000000..7a5b7855 --- /dev/null +++ b/general/toaster/toastpkg/ReadMe.md @@ -0,0 +1,21 @@ +Toaster Package Sample +====================== + +The Toastpkg sample simulates hardware-first and software-first installation of the toaster sample driver. + +The Toaster Installation Package comprises driver projects (.vcxproj files) that are contained in the toastpkg.sln solution file (in general/toaster/toastpkg). + +This document discusses the different approaches that end users take when adding new hardware to their computer, and describes an approach that addresses these scenarios in a consistent, robust manner. It also outlines the mechanisms provided to facilitate additional vendor requirements such as the installation of value-added software. + +**Introduction** + +The installation of software to support an instance of a given device (known as "device installation" or "driver installation") is done in a device-centric fashion in Windows operating systems. A device INF that matches up with one of the device's hardware or compatible IDs is used to identify the required driver file(s), registry modifications, etc., that are needed to make the device fully operational. This INF, along with the files copied thereby and a catalog that contains the digital signatures of the INF and these other files, constitute what is known as a "driver package". + +Because device installation is done for a specific instance of a device, the "natural" method of adding devices to a computer running a Plug and Play operating system is by plugging in the device first, letting Plug and Play find the device and automatically initiate an installation for that device. The device installation may then proceed using a driver package supplied with the OS, or a "3rd-party" driver package (supplied via CD-ROM, the Internet, or some other distribution mechanism). When the device installation is initiated by the addition of hardware, this is termed a "hardware-first" device installation. + +Users may, however, take an alternate approach to adding hardware to their computer. In this scenario, they first run a setup program (perhaps launched as an autorun application when the vendor-supplied CD-ROM is inserted). This setup program may perform installation activities, and then prompt the user to insert their hardware. Upon the hardware's insertion, the vendor-supplied driver package (which was "pre-installed" by the setup program) is then found by Plug and Play, and the installation proceeds as in the hardware-first scenario. When the device installation is initiated by running a setup program, this is termed a "software-first" device installation. This approach to adding new hardware is just as valid as the hardware-first scenario, and some vendors may even instruct their users (via documentation that ships with the hardware) that this is the preferred method. + +Vendors must support the hardware-first scenario (by providing a driver package that may be supplied to the "Found New Hardware" wizard with no "pre-configuration" performed by a setup program or other mechanism). Vendors may optionally support the software-first scenario as well, but the actual installation of the device instance is done by Plug and Play upon the device's arrival, as described above. + +Vendors may also wish to perform additional activities as part of the device installation. For example, the vendor may want to allow the user to optionally install one or more applications that ship with the device (e.g., a scanner that ships with an image processing application). Such software is termed "value-added software". Value-added software is distinct from the files that comprise the driver package because, unlike the core driver files, the device does not require value-added software to function properly. In the previous example of a scanner, for instance, perhaps the user already has an image processing application that they prefer. The user should be given the option of whether or not they want to install any value-added software. Additional activities (such as allowing the user to select value-added software offerings) may be accomplished by using a vendor-supplied device-specific co-installer. + diff --git a/general/toaster/toastpkg/inf/autorun.inf b/general/toaster/toastpkg/inf/autorun.inf new file mode 100644 index 00000000..4c582d7b --- /dev/null +++ b/general/toaster/toastpkg/inf/autorun.inf @@ -0,0 +1,15 @@ +[AutoRun] +open=i386\toastva.exe +icon=i386\toastva.exe,0 + +[AutoRun.i386] +open=i386\toastva.exe + +[AutoRun.ia64] +open=ia64\toastva.exe + +[AutoRun.amd64] +open=amd64\toastva.exe + +[DeviceInstall] +DriverPath=\ diff --git a/general/toaster/toastpkg/inf/toastpkg.inf b/general/toaster/toastpkg/inf/toastpkg.inf new file mode 100644 index 00000000..f427ae87 --- /dev/null +++ b/general/toaster/toastpkg/inf/toastpkg.inf @@ -0,0 +1,137 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +; KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR +; PURPOSE. +; +;Module Name: +; +; TOASTPKG.INF +; +;Abstract: +; +; INF file for installing toaster device drivers (and, optionally, value- +; added software) via device-specific coinstaller. +; This is a mutlios INF file. Same INF file cab be used on +; x86, ia64 and amd64 platforms. +; +;--*/ +[Version] +Signature="$WINDOWS NT$" +Class=TOASTER +ClassGuid={B85B7C50-6A01-11d2-B841-00C04FAD5171} +Provider=%ToastRUs% +DriverVer=09/21/2006,6.0.5736.1 +CatalogFile.NTx86 = tostx86.cat +CatalogFile.NTIA64 = tostia64.cat +CatalogFile.NTAMD64 = tstamd64.cat + +[DestinationDirs] +DefaultDestDir = 12 +CoInstaller_CopyFiles = 11 +ToasterClassInstallerCopyFiles = 11 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=ToasterClassReg +CopyFiles=ToasterClassInstallerCopyFiles + +[ToasterClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,100 +HKR,,Installer32,,"tostrcls.dll,ToasterClassInstaller" +HKR,,DeviceCharacteristics,0x10001,0x100 ; Use same security checks on relative opens + +[ToasterClassInstallerCopyFiles] +tostrcls.dll + +;***************************************** +; Toaster Device Install Section +;***************************************** + +[Manufacturer] +%ToastRUs%=ToastRUs,NTx86, NTia64, NTamd64 + +; For Win2K +[ToastRUs] +%ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + +; For XP and later +[ToastRUs.NTx86] +%ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + +[ToastRUs.NTia64] +%ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + +[ToastRUs.NTamd64] +%ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + + +[Toaster_Device.NT] +CopyFiles=Toaster_Device.NT.Copy +FriendlyNameFormat=%FriendlyNameFormat% + +[Toaster_Device.NT.Copy] +toaster.sys + +[Toaster_Device.NT.HW] +AddReg=Toaster_Device.NT.HW.AddReg + +[Toaster_Device.NT.HW.AddReg] +HKR,,"BeepCount",0x00010003,4 + +;-------------- Service installation + +[Toaster_Device.NT.Services] +AddService = toaster, %SPSVCINST_ASSOCSERVICE%, toaster_Service_Inst + +[toaster_Service_Inst] +DisplayName = %toaster.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\toaster.sys + +;-------------- Coinstaller installation + +[Toaster_Device.NT.CoInstallers] +AddReg=CoInstaller_AddReg +CopyFiles=CoInstaller_CopyFiles + +[CoInstaller_CopyFiles] +tostrco2.dll + +[CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000,"tostrco2.dll,ToasterCoInstaller" + +[ToastCoInfo] +; Used by the toaster co-installer to figure out where the original media is +; located (so it can launch value-added setup programs). +OriginalInfSourcePath = %1% + +[SourceDisksNames.x86] +1 = %DiskId1%, toastpkg.tag,,\i386 + +[SourceDisksNames.ia64] +1 = %DiskId1%, toastpkg.tag,,\ia64 + +[SourceDisksNames.amd64] +1 = %DiskId1%, toastpkg.tag,,\amd64 + +[SourceDisksFiles] +toaster.sys = 1,, +tostrco2.dll = 1,, +tostrcls.dll = 1,, + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +ToastRUs = "Toast'R'Us" +ClassName = "Toaster" +DiskId1 = "Toaster Device Installation Disk #1" +ToasterDevice.DeviceDesc = "Toaster Package Sample Toaster" +toaster.SVCDESC = "Microsoft Toaster Device Driver" +FriendlyNameFormat = "ToasterDevice%1!u!" diff --git a/general/toaster/toastpkg/toastapp/precomp.h b/general/toaster/toastpkg/toastapp/precomp.h new file mode 100644 index 00000000..c71537ea --- /dev/null +++ b/general/toaster/toastpkg/toastapp/precomp.h @@ -0,0 +1,21 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + precomp.h + +Abstract: + + Single container to facilitate use of precompiled headers. + +--*/ + +#include "toastapp.h" +#include \ No newline at end of file diff --git a/general/toaster/toastpkg/toastapp/precompsrc.c b/general/toaster/toastpkg/toastapp/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/general/toaster/toastpkg/toastapp/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/general/toaster/toastpkg/toastapp/rc_ids.h b/general/toaster/toastpkg/toastapp/rc_ids.h new file mode 100644 index 00000000..fd47a4c0 --- /dev/null +++ b/general/toaster/toastpkg/toastapp/rc_ids.h @@ -0,0 +1,22 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + rc_ids.h + +Abstract: + + Resource IDs used by the TOASTAPP sample application. + +--*/ + +#define IDD_DEVICE_INTERFACES 1 +#define IDC_DEVICE_INTERFACE_LIST 2 + diff --git a/general/toaster/toastpkg/toastapp/toastapp.c b/general/toaster/toastpkg/toastapp/toastapp.c new file mode 100644 index 00000000..d7eaebf3 --- /dev/null +++ b/general/toaster/toastpkg/toastapp/toastapp.c @@ -0,0 +1,813 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + toastapp.c + +Abstract: + + TOASTAPP is an application that provides an automatically updated list of + all currently available "toaster" devices (as enumerated by the toaster + sample in the Windows 2000 and Windows XP DDKs). + + The toasters' friendly names, along with their pathnames (i.e., for use + with CreateFile) are displayed in the dialog box. + +Notes: + + For a complete description of device interfaces and PnP event notification, + please see the Microsoft Windows 2000/Windows XP DDK and SDK Documentation. + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Instantiate toaster device interface class GUID (from DDK toaster sample, +// src\general\toaster\bus\common.h) +// +// {781EF630-72B2-11d2-B852-00C04FAD5171} +// + +#include + +DEFINE_GUID (GUID_TOASTER_INTERFACE, 0x781EF630, 0x72B2, 0x11d2, 0xB8, 0x52, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71); + +// +// Declare a global string buffer to be used when retrieving error text via +// FormatMessage. +// +TCHAR ErrorStringBuffer[1024]; + +// +// Define structure used to store device interface dialogbox data. +// +typedef struct _DIDLG_DATA { + HDEVINFO DeviceInfoSet; + HDEVNOTIFY hDevNotify; + GUID InterfaceClassGuid; +} DIDLG_DATA, *PDIDLG_DATA; + +// +// Function prototypes +// +INT_PTR +CALLBACK +DeviceInterfaceDlgProc( + _In_ HWND hwnd, + _In_ UINT msg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +BOOL +FillInDeviceInterfaceListBox( + _In_ HWND hWnd, + _In_ HDEVINFO DeviceInfoSet, + _In_ CONST GUID *InterfaceClassGuid + ); + +BOOL +GetDeviceInterfaceFriendlyName( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, + _Out_writes_all_(FriendlyNameSize) PTSTR FriendlyName, + _In_ DWORD FriendlyNameSize + ); + +// +// Implementation +// + +int +__cdecl +_tmain( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] + ) +{ + INT_PTR DlgResult; + DIDLG_DATA DIDlgData; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + // + // Initialize our dialogbox data structure. + // + ZeroMemory(&DIDlgData, sizeof(DIDlgData)); + + CopyMemory(&(DIDlgData.InterfaceClassGuid), + &GUID_TOASTER_INTERFACE, + sizeof(GUID) + ); + + // + // Now fire off the dialog that will present the automatically-updated list + // of active device interfaces. + // + DlgResult = DialogBoxParam(GetModuleHandle(NULL), + MAKEINTRESOURCE(IDD_DEVICE_INTERFACES), + NULL, + DeviceInterfaceDlgProc, + (LPARAM)&DIDlgData + ); + if(!DlgResult) { + if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + 0, + ErrorStringBuffer, + sizeof(ErrorStringBuffer) / sizeof(TCHAR), + NULL)) { + + _tprintf(TEXT("%s"), ErrorStringBuffer); + } + return -1; + + } else { + return 0; + } +} + + +INT_PTR +CALLBACK +DeviceInterfaceDlgProc( + _In_ HWND hWnd, + _In_ UINT msg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) + +/*++ + +Routine Description: + + This is the dialog procedure for the device interface dialog box that + presents an automatically-updated list of active device interfaces. + + It expects to get an lParam during WM_INITDIALOG that is a pointer to a + DIDLGDATA structure where the InterfaceClassGuid field is initialized to + the interface class GUID for which the device interface list is to be + displayed. (The other fields in this structure are initialized, used, and + destroyed during the lifetime of the dialogbox.) + +--*/ + +{ + PDIDLG_DATA DIDlgData; + DWORD Err; + PDEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface; + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; + + if(msg == WM_INITDIALOG) { + + DEV_BROADCAST_DEVICEINTERFACE NotificationFilter; + HDEVINFO NewDeviceInfoSet; + + DIDlgData = (PDIDLG_DATA)lParam; + + // + // Create a device information set that will be the container for our + // device interfaces. + // + DIDlgData->DeviceInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL); + + if(DIDlgData->DeviceInfoSet == INVALID_HANDLE_VALUE) { + Err = GetLastError(); + _tprintf(TEXT("SetupDiCreateDeviceInfoList failed with %lx\n"), Err); + goto clean0; + } + + // + // Now register to begin receiving notifications for the comings + // and goings of device interfaces which are members of the + // interface class whose GUID was passed in as the lParam to this + // dialog procedure. + // + ZeroMemory(&NotificationFilter, sizeof(NotificationFilter)); + NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); + NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + CopyMemory(&(NotificationFilter.dbcc_classguid), &(DIDlgData->InterfaceClassGuid), sizeof(GUID)); + + DIDlgData->hDevNotify = RegisterDeviceNotification(hWnd, + &NotificationFilter, + DEVICE_NOTIFY_WINDOW_HANDLE + ); + if(!DIDlgData->hDevNotify) { + Err = GetLastError(); + _tprintf(TEXT("RegisterDeviceNotification failed with %lx\n"), Err); + goto clean1; + } + + // + // OK, now we can retrieve the existing list of active device + // interfaces into the device information set we created above. + // + NewDeviceInfoSet = SetupDiGetClassDevsEx(&(DIDlgData->InterfaceClassGuid), + NULL, + NULL, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE, + DIDlgData->DeviceInfoSet, + NULL, + NULL + ); + + if(NewDeviceInfoSet == INVALID_HANDLE_VALUE) { + Err = GetLastError(); + _tprintf(TEXT("SetupDiGetClassDevsEx failed with %lx\n"), Err); + goto clean2; + } + + // + // If SetupDiGetClassDevsEx succeeds and it was passed in an + // existing device information set to be used, then the HDEVINFO + // it returns is the same as the one it was passed in. Thus, we + // can just use the original DeviceInfoSet handle from here on. + // + + // + // Now fill in our listbox with the current device interface list. + // + if(!FillInDeviceInterfaceListBox(hWnd, + DIDlgData->DeviceInfoSet, + &(DIDlgData->InterfaceClassGuid))) { + Err = GetLastError(); + goto clean2; + } + + // + // Success! Store away a pointer to our device interface dialog data + // structure. + // + SetWindowLongPtr(hWnd, DWLP_USER, (LONG_PTR)DIDlgData); + return TRUE; + + // + // Clean-up code for error path... + // +clean2: + UnregisterDeviceNotification(DIDlgData->hDevNotify); +clean1: + SetupDiDestroyDeviceInfoList(DIDlgData->DeviceInfoSet); +clean0: + if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + HRESULT_FROM_SETUPAPI(Err), + 0, + ErrorStringBuffer, + sizeof(ErrorStringBuffer) / sizeof(TCHAR), + NULL)) { + + _tprintf(TEXT("%s"), ErrorStringBuffer); + } + + EndDialog(hWnd, 0); + SetLastError(Err); + return TRUE; + } else { + // + // For the small set of messages that we get before WM_INITDIALOG, we + // won't have a devwizdata pointer! + // + DIDlgData = (PDIDLG_DATA)GetWindowLongPtr(hWnd, DWLP_USER); + if(DIDlgData == NULL) { + // + // If we haven't gotten a WM_INITDIALOG message yet, or if for some + // reason we weren't able to retrieve the DIDlgData pointer when we + // did, then we simply return FALSE. + // + return FALSE; + } + } + + switch(msg) { + + case WM_COMMAND: + if(LOWORD(wParam) == IDOK) { + // + // Clean up and return. + // + UnregisterDeviceNotification(DIDlgData->hDevNotify); + SetupDiDestroyDeviceInfoList(DIDlgData->DeviceInfoSet); + SetWindowLongPtr(hWnd, DWLP_USER, 0); + EndDialog(hWnd, 1); + return TRUE; + } + + // + // All other WM_COMMAND messages unhandled. + // + break; + + case WM_DEVICECHANGE: + // + // All the events we're interested in come with lParam pointing to + // a structure headed by a DEV_BROADCAST_HDR. This is denoted by + // bit 15 of wParam being set, and bit 14 being clear. + // + if((wParam & 0xC000) == 0x8000) { + // + // Make sure that this is a device interface notification... + // + if(((PDEV_BROADCAST_HDR)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + DevBroadcastDeviceInterface = (PDEV_BROADCAST_DEVICEINTERFACE)lParam; + } else { + // + // This isn't a device interface notification. Instead, + // it's a broadcasted notification sent for backwards- + // compatibility (e.g., DBT_DEVTYP_VOLUME). + // + break; + } + + if(wParam == DBT_DEVICEARRIVAL) { + _tprintf(TEXT("Received DBT_DEVICEARRIVAL for %s\n"), DevBroadcastDeviceInterface->dbcc_name); + } else if(wParam == DBT_DEVICEREMOVEPENDING) { + _tprintf(TEXT("Received DBT_DEVICEREMOVEPENDING for %s\n"), DevBroadcastDeviceInterface->dbcc_name); + } else if(wParam == DBT_DEVICEREMOVECOMPLETE) { + _tprintf(TEXT("Received DBT_DEVICEREMOVECOMPLETE for %s\n"), DevBroadcastDeviceInterface->dbcc_name); + } else { + // + // Presently, there are no other events that are sent for + // device interface notification, thus we should never get + // here. + // + break; + } + + } else { + // + // We received some broadcasted system message we don't care + // about (e.g., DBT_QUERYCHANGECONFIG). + // + break; + } + + if(wParam == DBT_DEVICEARRIVAL) { + // + // Open this new device interface into our device information + // set. + // + if(!SetupDiOpenDeviceInterface(DIDlgData->DeviceInfoSet, + DevBroadcastDeviceInterface->dbcc_name, + 0, + NULL)) { + Err = GetLastError(); + _tprintf(TEXT("SetupDiOpenDeviceInterface failed with %lx\n"), Err); + return TRUE; + } + + } else { + // + // First, locate this device interface in our device information + // set. + // + DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if(SetupDiOpenDeviceInterface(DIDlgData->DeviceInfoSet, + DevBroadcastDeviceInterface->dbcc_name, + DIODI_NO_ADD, + &DeviceInterfaceData)) { + + if(!SetupDiDeleteDeviceInterfaceData(DIDlgData->DeviceInfoSet, + &DeviceInterfaceData)) { + + Err = GetLastError(); + _tprintf(TEXT("SetupDiDeleteDeviceInterfaceData failed with %lx\n"), Err); + return TRUE; + } + } + } + + // + // If we get to here, we've successfully added or deleted a + // device interface in our device information set. Now go update + // our listbox with the new list. (Ignore any errors.) + // + FillInDeviceInterfaceListBox(hWnd, + DIDlgData->DeviceInfoSet, + &(DIDlgData->InterfaceClassGuid) + ); + + return TRUE; + + default: + break; + } + + return FALSE; +} + + +BOOL +FillInDeviceInterfaceListBox( + _In_ HWND hWnd, + _In_ HDEVINFO DeviceInfoSet, + _In_ CONST GUID *InterfaceClassGuid + ) + +/*++ + +Routine Description: + + This routine fills in the listbox of currently-active device interfaces + with their corresponding friendly names and pathnames. + +Arguments: + + hWnd - Supplies the window handle of the dialog box containing the device + interface listbox to be updated. + + DeviceInfoSet - Supplies a handle to the device information set containing + device interfaces to be used in updating the listbox. + + InterfaceClassGuid - Supplies the address of the interface class GUID for + the device interfaces to be placed into the listbox. + +Return Value: + + If the function succeeds, the return value is non-zero. + If the function fails, the return value is FALSE. To find out what the + cause of failure was, call GetLastError(). + +--*/ + +{ + DWORD i, Err; + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; + PTSTR FriendlyName; + PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData; + PBYTE Buffer; + DWORD BufferSize = 0; + DWORD RequiredSize; + LRESULT ListBoxReturn; + size_t FriendlyNameLen; + size_t devicePathLen; + + // + // Reset the listbox in preparation for adding the current list of device + // interfaces. + // + SendDlgItemMessage(hWnd, + IDC_DEVICE_INTERFACE_LIST, + LB_RESETCONTENT, + 0, + 0 + ); + + DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + Err = NO_ERROR; + + // + // Start out with a buffer that should be large enough to hold the friendly + // name plus the device interface detail data for a "reasonably-sized" + // device interface pathname. Note that device interface paths aren't + // confined to MAX_PATH length on Windows 2000, so we deal with the case + // where we may need a larger buffer. + // + // Note that we add a space and an open paren between the friendly name and + // the pathname. We have space for this (even in Unicode), because the + // device interface detail data buffer always begins with a DWORD cbSize + // field, that we can overwrite, without touching the character DevicePath + // buffer. + // + BufferSize = (LINE_LEN * sizeof(TCHAR)) + sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + + (MAX_PATH * sizeof(TCHAR)); + + Buffer = malloc(BufferSize); + + if(Buffer) { + // + // Leave the first LINE_LEN characters to retrieve the friendly name + // into... + // + FriendlyName = (PTSTR)Buffer; + DeviceInterfaceDetailData = + (PSP_DEVICE_INTERFACE_DETAIL_DATA)(Buffer + (LINE_LEN * sizeof(TCHAR))); + + DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + } else { + // + // Failure! + // + _tprintf(TEXT("Couldn't allocate %d bytes for device interface detail buffer\n"), + BufferSize + ); + + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + for(i = 0; + SetupDiEnumDeviceInterfaces(DeviceInfoSet, + NULL, + InterfaceClassGuid, + i, + &DeviceInterfaceData); + i++) { + + // + // To retrieve the device interface name (e.g., that you can call + // CreateFile() on... + // + while(!SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, + &DeviceInterfaceData, + DeviceInterfaceDetailData, + BufferSize - (LINE_LEN * sizeof(TCHAR)), + &RequiredSize, + NULL)) { + // + // We failed to get the device interface detail data--was it because + // our buffer was too small? (Hopefully so!) + // + Err = GetLastError(); + + // + // We can get rid of our current buffer regardless of what the + // error was... + // + free(Buffer); + Buffer = NULL; + + if(Err != ERROR_INSUFFICIENT_BUFFER) { + // + // Failure! + // + _tprintf(TEXT("SetupDiGetDeviceInterfaceDetail failed with %lx\n"), Err); + break; + } + + // + // We failed due to insufficient buffer. Allocate one that's + // sufficiently large and try again. + // + BufferSize = RequiredSize + (LINE_LEN * sizeof(TCHAR)); + + Buffer = malloc(BufferSize); + + if(Buffer) { + // + // Leave the first LINE_LEN characters to retrieve the friendly + // name into... + // + FriendlyName = (PTSTR)Buffer; + DeviceInterfaceDetailData = + (PSP_DEVICE_INTERFACE_DETAIL_DATA)(Buffer + (LINE_LEN * sizeof(TCHAR))); + + DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + Err = NO_ERROR; + + } else { + // + // Failure! + // + Err = ERROR_NOT_ENOUGH_MEMORY; + _tprintf(TEXT("Couldn't allocate %d bytes for device interface detail buffer\n"), RequiredSize); + break; + } + } + + if(!Buffer) { + // + // We encountered a failure above--abort. + // + break; + } + + // + // Now that we've successfully retrieved the device interface pathname, + // we can retrieve the friendly name. We left enough space at the + // start of the buffer for this, so we'll now retrieve this string, + // then move the pathname down next to it for display (the pathname + // will be enclosed in parentheses). + // + if(!GetDeviceInterfaceFriendlyName(DeviceInfoSet, + &DeviceInterfaceData, + FriendlyName, + LINE_LEN)) { + // + // This generally won't happen, but it _is_ possible. We'll just + // use two double-quotes to indicate an empty string. + // + if(FAILED(StringCchCopy(FriendlyName, LINE_LEN, TEXT("\"\"")))) { + break; + } + } + + // + // Add a space and the opening paren (see previous comment on why we're + // safe in doing this without fear of overwriting the DevicePath string. + // + if(FAILED(StringCchCat(FriendlyName, LINE_LEN, TEXT(" (")))) { + break; + } + + if(FAILED(StringCchLength(FriendlyName, LINE_LEN, &FriendlyNameLen))) { + break; + } + + // + // Now move the pathname down to the character immediately following + // the open paren. (Note: since source and destination blocks may + // overlap, we must use MoveMemory.) + // + if(FAILED(StringCbLength(DeviceInterfaceDetailData->DevicePath, + MAX_PATH, + &devicePathLen))) { + break; + } + + devicePathLen += sizeof(TCHAR); + + MoveMemory((PBYTE)(FriendlyName + FriendlyNameLen), + DeviceInterfaceDetailData->DevicePath, + devicePathLen + ); + + // + // Now add close paren + // + if (FAILED(StringCchCat(FriendlyName, LINE_LEN, TEXT(")")))) { + break; + } + + // + // Add this device interface to our listbox. + // + ListBoxReturn = SendDlgItemMessage(hWnd, + IDC_DEVICE_INTERFACE_LIST, + LB_ADDSTRING, + 0, + (LPARAM)FriendlyName + ); + + if((ListBoxReturn == LB_ERR) || (ListBoxReturn == LB_ERRSPACE)) { + // + // Set Err to some generic failure + // + Err = ERROR_INVALID_DATA; + + _tprintf(TEXT("Failed to add %s to listbox (%s)\n"), + DeviceInterfaceDetailData->DevicePath, + (ListBoxReturn == LB_ERR) ? TEXT("LB_ERR") : TEXT("LB_ERRSPACE") + ); + break; + } + + // + // Since we may have overwritten the device interface detail data + // 'cbSize' field, restore it now. + // + DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + } + + if(Buffer) { + free(Buffer); + } + + SetLastError(Err); + + return (Err == NO_ERROR); +} + + +BOOL +GetDeviceInterfaceFriendlyName( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, + _Out_writes_all_(FriendlyNameSize) PTSTR FriendlyName, + _In_ DWORD FriendlyNameSize + ) + +/*++ + +Routine Description: + + This routine retrieves the friendly name associated with the specified + device interface. It first looks for a "FriendlyName" value entry in the + device interface's registry key. If not found, it then tries to use the + "FriendlyName" property for the underlying devnode. If that isn't present, + it uses the devnode's device description (and if there isnt one of those, + it returns FALSE). + +Arguments: + + DeviceInfoSet - Supplies a handle to the device information set containing + the device interface whose friendly name is to be retrieved. + + DeviceInterfaceData - Supplies a context structure indicating which device + interface we're retrieving a friendly name for. + + FriendlyName - Supplies a character buffer that is filled in, upon + successful return, with the friendly name for the device interface. + + FriendlyNameSize - Supplies the size, in characters, of the FriendlyName + buffer. + +Return Value: + + If the function succeeds, the return value is non-zero. + If no FriendlyName is found (or buffer is too small), the return value is + FALSE. + +--*/ + +{ + HKEY hkey; + DWORD Err; + SP_DEVINFO_DATA DeviceInfoData; + DWORD RegDataType, RegDataLength; + + // + // First, open up the device interface registry key to see if the interface + // has its own friendly name. + // + hkey = SetupDiOpenDeviceInterfaceRegKey(DeviceInfoSet, + DeviceInterfaceData, + 0, + KEY_READ + ); + + if(hkey != INVALID_HANDLE_VALUE) { + + RegDataLength = FriendlyNameSize * sizeof(TCHAR); + + Err = RegQueryValueEx(hkey, + TEXT("FriendlyName"), + NULL, + &RegDataType, + (PBYTE)FriendlyName, + &RegDataLength + ); + + RegCloseKey(hkey); + + if((Err == ERROR_SUCCESS) && (RegDataType == REG_SZ)) { + return TRUE; + } + } + + // + // Find out what device instance is exposing this interface. + // + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + if(!SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, + DeviceInterfaceData, + NULL, + 0, + NULL, + &DeviceInfoData)) { + // + // We should always get here (i.e., SetupDiGetDeviceInterfaceDetail + // should always fail) since we didn't pass in a buffer to retrieve + // the device interface detail data. Of course, all we really care + // about is getting at the underlying device info data. + // + + // + // Now check the underlying device for a FriendlyName property. + // + if(SetupDiGetDeviceRegistryProperty(DeviceInfoSet, + &DeviceInfoData, + SPDRP_FRIENDLYNAME, + &RegDataType, + (PBYTE)FriendlyName, + FriendlyNameSize * sizeof(TCHAR), + NULL)) { + if(RegDataType == REG_SZ) { + return TRUE; + } + } + + // + // Fall back to device description + // + if(SetupDiGetDeviceRegistryProperty(DeviceInfoSet, + &DeviceInfoData, + SPDRP_DEVICEDESC, + &RegDataType, + (PBYTE)FriendlyName, + FriendlyNameSize * sizeof(TCHAR), + NULL)) { + if(RegDataType == REG_SZ) { + return TRUE; + } + } + } + + // + // Couldn't find anything usable as a friendly name--return failure. + // + return FALSE; +} + diff --git a/general/toaster/toastpkg/toastapp/toastapp.dlg b/general/toaster/toastpkg/toastapp/toastapp.dlg new file mode 100644 index 00000000..d3cc1e53 --- /dev/null +++ b/general/toaster/toastpkg/toastapp/toastapp.dlg @@ -0,0 +1,12 @@ +1 DLGINCLUDE "rc_ids.h" + +IDD_DEVICE_INTERFACES DIALOG 82, 80, 412, 215 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Currently Available Toasters" +FONT 8, "MS Shell Dlg" +BEGIN + LISTBOX IDC_DEVICE_INTERFACE_LIST, 8, 9, 396, 177, LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK", IDD_DEVICE_INTERFACES, 181, 195, 50, 14 +END diff --git a/general/toaster/toastpkg/toastapp/toastapp.h b/general/toaster/toastpkg/toastapp/toastapp.h new file mode 100644 index 00000000..bf34ba76 --- /dev/null +++ b/general/toaster/toastpkg/toastapp/toastapp.h @@ -0,0 +1,29 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + toastapp.h + +Abstract: + + Header files used by the TOASTAPP sample application. + +--*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include "rc_ids.h" diff --git a/general/toaster/toastpkg/toastapp/toastapp.ico b/general/toaster/toastpkg/toastapp/toastapp.ico new file mode 100644 index 00000000..55ab969e Binary files /dev/null and b/general/toaster/toastpkg/toastapp/toastapp.ico differ diff --git a/general/toaster/toastpkg/toastapp/toastapp.rc b/general/toaster/toastpkg/toastapp/toastapp.rc new file mode 100644 index 00000000..857a81b3 --- /dev/null +++ b/general/toaster/toastpkg/toastapp/toastapp.rc @@ -0,0 +1,69 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + toastapp.rc + +Abstract: + + Resources used by the TOASTAPP sample application. + +--*/ + +#include +#include +#include "rc_ids.h" + +// +// Version resources +// +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Microsoft Corporation\0" + VALUE "FileDescription", "Toaster Interface Notification Test Application\0" + VALUE "FileVersion", "1.00.0000.0\0" + VALUE "InternalName", "TOASTAPP.EXE\0" + VALUE "LegalCopyright", "© Microsoft Corporation. All rights reserved.\0" + VALUE "OriginalFilename", "TOASTAPP.EXE\0" + VALUE "ProductName", "Toaster DDK Sample\0" + VALUE "ProductVersion", "1.0.0000.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +// +// Icon resources +// +1 ICON DISCARDABLE "toastapp.ico" + +// +// Dialog resources +// +#include "toastapp.dlg" + diff --git a/general/toaster/toastpkg/toastapp/toastapp.vcxproj b/general/toaster/toastpkg/toastapp/toastapp.vcxproj new file mode 100644 index 00000000..9e71f3da --- /dev/null +++ b/general/toaster/toastpkg/toastapp/toastapp.vcxproj @@ -0,0 +1,209 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {1C818755-28B2-4C84-8623-FF84F3326B64} + $(MSBuildProjectName) + Debug + Win32 + {42007D8E-51B7-469B-B5D0-45C7674BFFEF} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + toastapp + + + toastapp + + + toastapp + + + toastapp + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastpkg/toastapp/toastapp.vcxproj.Filters b/general/toaster/toastpkg/toastapp/toastapp.vcxproj.Filters new file mode 100644 index 00000000..bcbb10b1 --- /dev/null +++ b/general/toaster/toastpkg/toastapp/toastapp.vcxproj.Filters @@ -0,0 +1,30 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {5B7E141F-3C1C-446C-B423-22D8F13BD05C} + + + h;hpp;hxx;hm;inl;inc;xsd + {21C18B76-71F9-4B0C-B377-B15E4821D9CD} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {67F5F556-F3BD-4452-88F3-806BDB94EB5F} + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastpkg/toastcd/ToastApp/setup.ini b/general/toaster/toastpkg/toastcd/ToastApp/setup.ini new file mode 100644 index 00000000..81d61154 Binary files /dev/null and b/general/toaster/toastpkg/toastcd/ToastApp/setup.ini differ diff --git a/general/toaster/toastpkg/toastcd/ToastApp/toastapp.msi b/general/toaster/toastpkg/toastcd/ToastApp/toastapp.msi new file mode 100644 index 00000000..0bdfaa65 Binary files /dev/null and b/general/toaster/toastpkg/toastcd/ToastApp/toastapp.msi differ diff --git a/general/toaster/toastpkg/toastcd/amd64/toaster.sys b/general/toaster/toastpkg/toastcd/amd64/toaster.sys new file mode 100644 index 00000000..d51da6c4 Binary files /dev/null and b/general/toaster/toastpkg/toastcd/amd64/toaster.sys differ diff --git a/general/toaster/toastpkg/toastcd/autorun.inf b/general/toaster/toastpkg/toastcd/autorun.inf new file mode 100644 index 00000000..4c582d7b --- /dev/null +++ b/general/toaster/toastpkg/toastcd/autorun.inf @@ -0,0 +1,15 @@ +[AutoRun] +open=i386\toastva.exe +icon=i386\toastva.exe,0 + +[AutoRun.i386] +open=i386\toastva.exe + +[AutoRun.ia64] +open=ia64\toastva.exe + +[AutoRun.amd64] +open=amd64\toastva.exe + +[DeviceInstall] +DriverPath=\ diff --git a/general/toaster/toastpkg/toastcd/i386/toaster.sys b/general/toaster/toastpkg/toastcd/i386/toaster.sys new file mode 100644 index 00000000..19184dc9 Binary files /dev/null and b/general/toaster/toastpkg/toastcd/i386/toaster.sys differ diff --git a/general/toaster/toastpkg/toastcd/toastpkg.inf b/general/toaster/toastpkg/toastcd/toastpkg.inf new file mode 100644 index 00000000..f427ae87 --- /dev/null +++ b/general/toaster/toastpkg/toastcd/toastpkg.inf @@ -0,0 +1,137 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +; KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR +; PURPOSE. +; +;Module Name: +; +; TOASTPKG.INF +; +;Abstract: +; +; INF file for installing toaster device drivers (and, optionally, value- +; added software) via device-specific coinstaller. +; This is a mutlios INF file. Same INF file cab be used on +; x86, ia64 and amd64 platforms. +; +;--*/ +[Version] +Signature="$WINDOWS NT$" +Class=TOASTER +ClassGuid={B85B7C50-6A01-11d2-B841-00C04FAD5171} +Provider=%ToastRUs% +DriverVer=09/21/2006,6.0.5736.1 +CatalogFile.NTx86 = tostx86.cat +CatalogFile.NTIA64 = tostia64.cat +CatalogFile.NTAMD64 = tstamd64.cat + +[DestinationDirs] +DefaultDestDir = 12 +CoInstaller_CopyFiles = 11 +ToasterClassInstallerCopyFiles = 11 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=ToasterClassReg +CopyFiles=ToasterClassInstallerCopyFiles + +[ToasterClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,100 +HKR,,Installer32,,"tostrcls.dll,ToasterClassInstaller" +HKR,,DeviceCharacteristics,0x10001,0x100 ; Use same security checks on relative opens + +[ToasterClassInstallerCopyFiles] +tostrcls.dll + +;***************************************** +; Toaster Device Install Section +;***************************************** + +[Manufacturer] +%ToastRUs%=ToastRUs,NTx86, NTia64, NTamd64 + +; For Win2K +[ToastRUs] +%ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + +; For XP and later +[ToastRUs.NTx86] +%ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + +[ToastRUs.NTia64] +%ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + +[ToastRUs.NTamd64] +%ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + + +[Toaster_Device.NT] +CopyFiles=Toaster_Device.NT.Copy +FriendlyNameFormat=%FriendlyNameFormat% + +[Toaster_Device.NT.Copy] +toaster.sys + +[Toaster_Device.NT.HW] +AddReg=Toaster_Device.NT.HW.AddReg + +[Toaster_Device.NT.HW.AddReg] +HKR,,"BeepCount",0x00010003,4 + +;-------------- Service installation + +[Toaster_Device.NT.Services] +AddService = toaster, %SPSVCINST_ASSOCSERVICE%, toaster_Service_Inst + +[toaster_Service_Inst] +DisplayName = %toaster.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\toaster.sys + +;-------------- Coinstaller installation + +[Toaster_Device.NT.CoInstallers] +AddReg=CoInstaller_AddReg +CopyFiles=CoInstaller_CopyFiles + +[CoInstaller_CopyFiles] +tostrco2.dll + +[CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000,"tostrco2.dll,ToasterCoInstaller" + +[ToastCoInfo] +; Used by the toaster co-installer to figure out where the original media is +; located (so it can launch value-added setup programs). +OriginalInfSourcePath = %1% + +[SourceDisksNames.x86] +1 = %DiskId1%, toastpkg.tag,,\i386 + +[SourceDisksNames.ia64] +1 = %DiskId1%, toastpkg.tag,,\ia64 + +[SourceDisksNames.amd64] +1 = %DiskId1%, toastpkg.tag,,\amd64 + +[SourceDisksFiles] +toaster.sys = 1,, +tostrco2.dll = 1,, +tostrcls.dll = 1,, + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +ToastRUs = "Toast'R'Us" +ClassName = "Toaster" +DiskId1 = "Toaster Device Installation Disk #1" +ToasterDevice.DeviceDesc = "Toaster Package Sample Toaster" +toaster.SVCDESC = "Microsoft Toaster Device Driver" +FriendlyNameFormat = "ToasterDevice%1!u!" diff --git a/general/toaster/toastpkg/toastcd/toastpkg.tag b/general/toaster/toastpkg/toastcd/toastpkg.tag new file mode 100644 index 00000000..e69de29b diff --git a/general/toaster/toastpkg/toastcd/tostx86.cat b/general/toaster/toastpkg/toastcd/tostx86.cat new file mode 100644 index 00000000..31450f2f Binary files /dev/null and b/general/toaster/toastpkg/toastcd/tostx86.cat differ diff --git a/general/toaster/toastpkg/toastcd/tstamd64.cat b/general/toaster/toastpkg/toastcd/tstamd64.cat new file mode 100644 index 00000000..f91fe53f Binary files /dev/null and b/general/toaster/toastpkg/toastcd/tstamd64.cat differ diff --git a/general/toaster/toastpkg/toastco/precomp.h b/general/toaster/toastpkg/toastco/precomp.h new file mode 100644 index 00000000..2b688022 --- /dev/null +++ b/general/toaster/toastpkg/toastco/precomp.h @@ -0,0 +1,21 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + precomp.h + +Abstract: + + Single container to facilitate use of precompiled headers. + +--*/ + +#include "toastco.h" +#include diff --git a/general/toaster/toastpkg/toastco/precompsrc.c b/general/toaster/toastpkg/toastco/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/general/toaster/toastpkg/toastco/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/general/toaster/toastpkg/toastco/toastco.c b/general/toaster/toastpkg/toastco/toastco.c new file mode 100644 index 00000000..a66b07ac --- /dev/null +++ b/general/toaster/toastpkg/toastco/toastco.c @@ -0,0 +1,1312 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + toastco.c + +Abstract: + + Device-specific co-installer for the Toaster Package sample. + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Constants +// +#define TOASTAPP_SETUP_SUBDIR L"ToastApp" +#define TOASTAPP_SETUP_EXE L"\\setup.exe" +#define TOASTAPP_SETUP_PATH (TOASTAPP_SETUP_SUBDIR TOASTAPP_SETUP_EXE) + +#define TOASTER_MEDIA_SOURCE_ID 1 + +// +// Globals +// +HMODULE g_hInstance; +WCHAR SetupExeName[] = TOASTAPP_SETUP_EXE; + +// +// Structures +// +typedef struct _VALUEADDWIZDATA { + BOOL AppInstallAttempted; // Have we previously attempted to install app? + WCHAR MediaRootDirectory[MAX_PATH]; // Fully-qualified path to root of install media + WCHAR MediaDiskName[LINE_LEN]; // Name of media to prompt for (or empty string) + WCHAR MediaTagFile[MAX_PATH]; // Tagfile identifying removable media (or empty string) +} VALUEADDWIZDATA, *LPVALUEADDWIZDATA; + +// +// Function prototypes +// +INT_PTR +CALLBACK +ValueAddDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +BOOL +InstallToastApp( + _In_ HWND hwndDlg, + _In_ LPCWSTR MediaRootDirectory + ); + +UINT +ValueAddPropSheetPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ); + +_Success_(return == TRUE) +BOOL +GetMediaRootDirectory( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData, + _Out_ LPWSTR *MediaRootDirectory, + _Outptr_result_maybenull_ LPWSTR *MediaDiskName, + _Outptr_result_maybenull_ LPWSTR *MediaTagFile + ); + +HPROPSHEETPAGE +GetValueAddSoftwareWizPage( + _In_ LPCWSTR MediaRootDirectory, + _In_opt_ LPCWSTR MediaDiskName, + _In_opt_ LPCWSTR MediaTagFile + ); + +VOID +SetDeviceFriendlyName( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData + ); + +// +// Implementation +// + +BOOL WINAPI +DllMain( + _In_ HINSTANCE hInstDll, + _In_ DWORD Reason, + _In_ LPVOID Reserved + ) + +/*++ + +Routine Description: + + Initialization/de-initialization entry point for toastco.dll + +Arguments: + + hInstDll - Supplies handle to the DLL module + + Reason - Supplies the reason for calling the function + + pctx - Reserved + +Return Value: + + This function always returns TRUE. + +--*/ + +{ + UNREFERENCED_PARAMETER(Reserved); + + switch(Reason) { + + case DLL_PROCESS_ATTACH: + g_hInstance = hInstDll; + break; + + case DLL_PROCESS_DETACH: + g_hInstance = NULL; + break; + + default: + break; + } + + return TRUE; +} + + +DWORD CALLBACK +ToasterCoInstaller( + _In_ DI_FUNCTION InstallFunction, + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData OPTIONAL, + _Inout_ PCOINSTALLER_CONTEXT_DATA Context + ) + +/*++ + +Routine Description: + + This function acts as a device-specific co-installer for "toaster" devices. + +Arguments: + + InstallFunction - Specifies the device installer function code indicating + the action being performed. + + DeviceInfoSet - Supplies a handle to the device information set being + acted upon by this install action. + + DeviceInfoData - Optionally, supplies the address of a device information + element being acted upon by this install action. + + Context - Supplies the installation context that is per-install request/ + per-coinstaller. + +Return Value: + + If this function successfully completed the requested action (or did + nothing) and wishes for the installation to continue, the return value + is NO_ERROR. + + If this function successfully completed the requested action (or did + nothing) and would like to be called back once installation has + completed, the return value is ERROR_DI_POSTPROCESSING_REQUIRED. + + If an error occurred while attempting to perform the requested action, a + Win32 error code is returned. The install action will be aborted. + +--*/ + +{ + SP_NEWDEVICEWIZARD_DATA NewDeviceWizardData; + HKEY hKey; + DWORD Err; + DWORD RegDataType; + DWORD RequiredSize; + DWORD UserPrompted; + PWSTR MediaRootDirectory, MediaDiskName, MediaTagFile; + + UNREFERENCED_PARAMETER( Context ); + + switch(InstallFunction) { + + case DIF_INSTALLDEVICE: + // + // In version 1 of our coinstaller, we had a hard-coded format + // for the device's FriendlyName. This wasn't a localizable + // solution. Now, we retrieve a (localized) format string from + // the device INF. + // + // We should always get called with a valid DeviceInfoData, but + // just to be sure... + // + if(DeviceInfoData != NULL) { + SetDeviceFriendlyName(DeviceInfoSet, DeviceInfoData); + } + + break; + + case DIF_NEWDEVICEWIZARD_FINISHINSTALL: + // + // We should always get called with a valid DeviceInfoData, but + // just in case we don't we will bail out right away. + // + if(DeviceInfoData == NULL) { + break; + } + + // + // Only supply a finish-install wizard page the first time... + // + if(ERROR_SUCCESS != RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + TEXT("SOFTWARE\\Microsoft\\Toaster"), + 0, + KEY_READ, + &hKey)) { + // + // If we can't open this key, then we can't ascertain whether + // or not the user was previously prompted to select value-add + // software. Assume they haven't been (i.e., this is the first + // time, so the key doesn't exist yet). + // + UserPrompted = 0; + + } else { + // + // Look for non-zero "User Prompted" value entry to indicate + // that the user has previously responded to question about + // installation of value-add software. + // + RequiredSize = sizeof(UserPrompted); + + Err = RegQueryValueEx(hKey, + TEXT("User Prompted"), + NULL, + &RegDataType, + (PBYTE)&UserPrompted, + &RequiredSize + ); + + if(Err != ERROR_SUCCESS) { + UserPrompted = 0; + } + + RegCloseKey(hKey); + } + + if(UserPrompted) { + // + // We asked the user this question before--don't bother them + // again. + // + break; + } + + // + // It's possible that we could return the handle of our finish + // install wizard page, yet it might never be used (e.g., if we're + // in a server-side installation). Thus, we won't set our + // "User Prompted" registry flag just yet. We'll wait until the + // user actually sees this page before setting the flag. + // + + ZeroMemory(&NewDeviceWizardData, sizeof(NewDeviceWizardData)); + NewDeviceWizardData.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); + + if(SetupDiGetClassInstallParams(DeviceInfoSet, + DeviceInfoData, + (PSP_CLASSINSTALL_HEADER)&NewDeviceWizardData, + sizeof(SP_NEWDEVICEWIZARD_DATA), + NULL)) { + // + // First, make sure there's room for us to add a page... + // + if(NewDeviceWizardData.NumDynamicPages >= MAX_INSTALLWIZARD_DYNAPAGES) { + break; + } + + // + // Retrieve the location of the source media based on the INF + // we're installing from. + // + if(!GetMediaRootDirectory(DeviceInfoSet, + DeviceInfoData, + &MediaRootDirectory, + &MediaDiskName, + &MediaTagFile)) { + // + // We couldn't figure out where the source media is, so we + // can't offer any value-added software to the user. + // + break; + } + + NewDeviceWizardData.DynamicPages[NewDeviceWizardData.NumDynamicPages] = + GetValueAddSoftwareWizPage(MediaRootDirectory, + MediaDiskName, + MediaTagFile + ); + + // + // We don't need the media strings any more. + // + GlobalFree(MediaRootDirectory); + + if(MediaDiskName) { + GlobalFree(MediaDiskName); + } + + if(MediaTagFile) { + GlobalFree(MediaTagFile); + } + + if(NewDeviceWizardData.DynamicPages[NewDeviceWizardData.NumDynamicPages] != NULL) { + NewDeviceWizardData.NumDynamicPages++; + } + + SetupDiSetClassInstallParams(DeviceInfoSet, + DeviceInfoData, + (PSP_CLASSINSTALL_HEADER)&NewDeviceWizardData, + sizeof(SP_NEWDEVICEWIZARD_DATA) + ); + } + + break; + + default: + break; + } + + return NO_ERROR; +} + + +BOOL +InstallToastApp( + _In_ HWND hwndWizard, + _In_ LPCWSTR FullSetupPath + ) + +/*++ + +Routine Description: + + This routine hides the wizard, kicks off the ToastApp setup program, then + unhides the wizard when the ToastApp setup process terminates. + +Arguments: + + hwndWizard - Handle to the wizard window to be hidden. + + FullSetupPath - Supplies the path to the setup program to be launched. + +Return Value: + + If the setup app was successfully launched, the return value is TRUE. + Otherwise, it is FALSE. + +--*/ + +{ + BOOL b; + STARTUPINFO StartupInfo; + PROCESS_INFORMATION ProcessInformation; + + // + // Hide our wizard for the duration of the Toaster app's installation... + // + ShowWindow(hwndWizard, SW_HIDE); + GetStartupInfo(&StartupInfo); + + b = CreateProcess(FullSetupPath, + NULL, + NULL, + NULL, + FALSE, + DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, + NULL, + NULL, + &StartupInfo, + &ProcessInformation + ); + + if(b) { + // + // Don't need a handle to the thread... + // + CloseHandle(ProcessInformation.hThread); + + // + // ...but we _do_ want to wait on the process handle. + // + WaitForMultipleObjects(1, &ProcessInformation.hProcess, FALSE, INFINITE); + + CloseHandle(ProcessInformation.hProcess); + } + + // + // Now show our wizard once again... + // + ShowWindow(hwndWizard, SW_SHOW); + + return b; +} + + +UINT +ValueAddPropSheetPageProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ LPPROPSHEETPAGE ppsp + ) + +/*++ + +Routine Description: + + This function is the property sheet page procedure, used to free the + context data associated with the page when it is released. + +Arguments: + + hwnd - Supplies a handle to the property page window + + uMsg - Supplies the message identifying the action being taken + + ppsp - Supplies the PROPSHEETPAGE structure for our page + +Return Value: + + This routine always return non-zero (1). + +--*/ + +{ + UNREFERENCED_PARAMETER(hwnd); + + switch(uMsg) { + + case PSPCB_RELEASE : + GlobalFree((LPVALUEADDWIZDATA)(ppsp->lParam)); + break; + + default : + break; + } + + return 1; // let the page be created (return ignored on page release) +} + + +INT_PTR +CALLBACK +ValueAddDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) + +/*++ + +Routine Description: + + This function is the dialog procedure for the value-add software selection + wizard page. If the user selects any software on this page, the software + will be automatically installed when the user presses "Next". + +Arguments: + + hwndDlg - Supplies a handle to the dialog box window + + uMsg - Supplies the message + + wParam - Supplies the first message parameter + + lParam - Supplies the second message parameter + +Return Value: + + This dialog procedure always returns zero. + +--*/ + +{ + LPVALUEADDWIZDATA pdata; + LPNMHDR lpnm; + HKEY hKey; + + UNREFERENCED_PARAMETER( wParam ); + + // + // Retrieve the shared user data from GWL_USERDATA + // + pdata = (LPVALUEADDWIZDATA) GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch(uMsg) { + + case WM_INITDIALOG : + // + // Get the PROPSHEETPAGE lParam value and load it into GWL_USERDATA + // + pdata = (LPVALUEADDWIZDATA) ((LPPROPSHEETPAGE)lParam)->lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)pdata); + break; + + case WM_NOTIFY : + + lpnm = (LPNMHDR)lParam; + + switch(lpnm->code) { + + case PSN_SETACTIVE : + // + // Enable the Next and Back buttons + // + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT); + break; + + case PSN_WIZNEXT : + // + // Install any applications the user selected. + // + if(IsDlgButtonChecked(hwndDlg, IDC_CHECK1) && !pdata->AppInstallAttempted) { + + BOOL MediaPresent = FALSE; + WCHAR PathToSetupExe[MAX_PATH]; + PWSTR LastChar; + + // + // If we need to prompt the user for media, do so now. + // + if(*(pdata->MediaDiskName)) { + + WCHAR TempString[64]; + size_t PathLength; + + if(!LoadString(g_hInstance, + IDS_MEDIA_PROMPT_TITLE, + TempString, + sizeof(TempString) / sizeof(WCHAR))) { + + *TempString = TEXT('\0'); + } + + // + // Append subdirectory where toastapp's setup.exe + // is located, so we can prompt user for media. + // + if (FAILED(StringCchLength(pdata->MediaRootDirectory, + MAX_PATH, + &PathLength))) { + break; + } + + LastChar = pdata->MediaRootDirectory + PathLength; + + if(FAILED(StringCchCopy(LastChar, + MAX_PATH - PathLength, + TOASTAPP_SETUP_SUBDIR))) { + break; + } + + // + // (Note, we skip the first character in + // SetupExeName for our "FileSought" argument + // below, because we don't want to include the + // first character, which is a path separator.) + // + if(DPROMPT_SUCCESS == SetupPromptForDisk( + GetParent(hwndDlg), + TempString, + pdata->MediaDiskName, + pdata->MediaRootDirectory, + SetupExeName+1, + pdata->MediaTagFile, + IDF_CHECKFIRST | IDF_NOBEEP, + PathToSetupExe, + MAX_PATH, + (PDWORD)&PathLength)) + { + MediaPresent = TRUE; + } + + // + // Strip the ToastApp subdir off media root path + // + *LastChar = L'\0'; + + if(MediaPresent) { + // + // SetupPromptForDisk gives us the directory + // where our setup program is located--now we + // need to append the setup program onto the + // end of that path. + // + PathLength--; // Don't include terminating null + + if((PathToSetupExe[PathLength-1] != L'\\') && + (PathToSetupExe[PathLength-1] != L'/')) { + // + // We need the path separator char... + // + if(FAILED(StringCchCopy(PathToSetupExe+PathLength, + MAX_PATH - PathLength, + SetupExeName))) { + break; + } + + } else { + // + // We don't need the path separator char... + // + if(FAILED(StringCchCopy(PathToSetupExe+PathLength, + MAX_PATH - PathLength, + SetupExeName+1))) { + break; + } + + } + } + + } else { + + // + // Assume media is already present (i.e., because + // we're in our auto-launch setup program running + // off the media. + // + MediaPresent = TRUE; + + // + // Construct the fully-qualified path to the setup + // executable. + // + if(FAILED(StringCchCopy(PathToSetupExe, + MAX_PATH, + pdata->MediaRootDirectory))) { + break; + } + + if(FAILED(StringCchCat(PathToSetupExe, + MAX_PATH, + TOASTAPP_SETUP_PATH))) { + break; + } + + } + + if(MediaPresent) { + // + // We're attempting app install. Success or + // failure, we don't want to try again. + // + pdata->AppInstallAttempted = TRUE; + + if(!InstallToastApp(GetParent(hwndDlg), PathToSetupExe)) { + // + // We failed to install the toast app. Un-check + // the checkbox before we disable it. + // + CheckDlgButton(hwndDlg, IDC_CHECK1, BST_UNCHECKED); + } + + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK1), FALSE); + } + } + + break; + + case PSN_KILLACTIVE : + // + // If we get to this point, we know that our wizard page + // has been displayed. We can now set the "User Prompted" + // registry flag, certain that the user has been given the + // opportunity to select the value-added software they wish + // to install. + // + if(ERROR_SUCCESS == RegCreateKeyEx( + HKEY_LOCAL_MACHINE, + TEXT("SOFTWARE\\Microsoft\\Toaster"), + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, + NULL, + &hKey, + NULL)) { + + DWORD UserPrompted = 1; + + RegSetValueEx(hKey, + TEXT("User Prompted"), + 0, + REG_DWORD, + (PBYTE)&UserPrompted, + sizeof(UserPrompted) + ); + + RegCloseKey(hKey); + } + + break; + + case PSN_WIZBACK : + //Handle a Back button click, if necessary + break; + + case PSN_RESET : + //Handle a Cancel button click, if necessary + break; + + default : + break; + } + + break; + + default: + break; + } + + return 0; +} + +_Success_(return == TRUE) +BOOL +GetMediaRootDirectory( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData, + _Out_ LPWSTR *MediaRootDirectory, + _Outptr_result_maybenull_ LPWSTR *MediaDiskName, + _Outptr_result_maybenull_ LPWSTR *MediaTagFile + ) + +/*++ + +Routine Description: + + This function retrieves the root of the installation media for the INF + selected in the specified device information element. + + There are two possibilities here: + + 1. INF is on source media. If so, then just use that path. + + 2. INF is already in %windir%\Inf. This is less likely, because we + should've previously prompted the user to install software at the + time the INF was installed. However, perhaps someone called + SetupCopyOEMInf to install the INF without prompting the user for + value-add software selection. Another way you could get into this + state is if the user previously elected to install the application, + then subsequently uninstalled it via "Add/Remove Programs". The MSI + package is configured to delete the UserPrompted value from the + registry during uninstall, so if a new toaster is subsequently + inserted, the user will be prompted once again. + + When the INF is in %windir%\Inf, we need to retrieve the original + source location from which the INF was installed. Plug&Play stores + this information for 3rd-party INF files, but it is not directly + accessible. Fortunately, there is an INF DIRID that corresponds to + this path, so we retrieve the value of that DIRID from the INF in + order to ascertain the media root directory. + +Arguments: + + DeviceInfoSet - Supplies a handle to the device information set containing + the element for which an INF driver node is currently selected. + + DeviceInfoData - Supplies the address of a device information element for + which an INF driver node is currently selected. + + MediaRootDirectory - Supplies the address of a string pointer that, upon + successful return, will be set to point to a newly-allocated string + containing the root directory of the setup media. The caller must free + this buffer via GlobalFree. + + This pointer will be set to NULL upon error. + + MediaDiskName - Supplies the address of a string pointer that, upon + successful return will be set to either: + + 1. A newly-allocated string containing the disk name to be used + when prompting for the setup media (when INF is in %windir%\Inf) + 2. NULL (when INF isn't in %windir%\Inf it is presumed to be on + source media, hence no prompting is necessary) + + This pointer will be set to NULL upon error. + + MediaTagFile - Supplies the address of a string pointer that, upon + successful return will be set to either: + + 1. A newly-allocated string containing the disk tagfile to be used + when prompting for the setup media (when INF is in %windir%\Inf) + 2. NULL (when INF isn't in %windir%\Inf it is presumed to be on + source media, hence no prompting is necessary) + + This pointer will be set to NULL upon error. + +Return Value: + + If this function succeeds, the return value is non-zero (TRUE). + + If this function fails, the return value is FALSE. + +--*/ + +{ + SP_DRVINFO_DATA DriverInfoData; + PSP_DRVINFO_DETAIL_DATA DriverInfoDetailData = NULL; + LPWSTR FileNamePart; + WCHAR InfDirPath[MAX_PATH]; + BOOL b = FALSE; + HINF hInf = INVALID_HANDLE_VALUE; + INFCONTEXT InfContext; + DWORD PathLength; + + *MediaRootDirectory = NULL; + *MediaDiskName = NULL; + *MediaTagFile = NULL; + + // + // First, retrieve the full path of the INF being used to install this + // device. + // + DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA); + + if(!SetupDiGetSelectedDriver(DeviceInfoSet, DeviceInfoData, &DriverInfoData)) { + // + // This shouldn't fail, but if it does, just bail. + // + goto clean0; + } + + // + // Retrieve the driver info details. We don't care about the id list at + // the end, so we can just allocate a buffer for the fixed-size part... + // + DriverInfoDetailData = GlobalAlloc(0, sizeof(SP_DRVINFO_DETAIL_DATA)); + if(!DriverInfoDetailData) { + goto clean0; + } + DriverInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA); + + if(!SetupDiGetDriverInfoDetail(DeviceInfoSet, + DeviceInfoData, + &DriverInfoData, + DriverInfoDetailData, + sizeof(SP_DRVINFO_DETAIL_DATA), + NULL) + && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) + { + // + // Again, this should never fail, but if it does we're outta here. + // + goto clean0; + } + + *MediaRootDirectory = GlobalAlloc(0, MAX_PATH * sizeof(WCHAR)); + if(!*MediaRootDirectory) { + goto clean0; + } + + // + // Strip the INF name off of the path. (Resultant path will always end + // with a path separator char ('\\').) + // + PathLength = GetFullPathName(DriverInfoDetailData->InfFileName, + MAX_PATH, + *MediaRootDirectory, + &FileNamePart + ); + + if(!PathLength || (PathLength >= MAX_PATH)) { + goto clean0; + } + + *FileNamePart = L'\0'; + + // + // Check to see this INF is already in %windir%\Inf. + // + PathLength = GetSystemWindowsDirectory(InfDirPath, MAX_PATH); + + if(!PathLength || (PathLength >= MAX_PATH)) { + goto clean0; + } + + // + // Append INF directory to path (making sure we don't end up with two path + // separator chars). + // + if((InfDirPath[PathLength-1] != L'\\') && (InfDirPath[PathLength-1] != L'/')) { + if(FAILED(StringCchCopy(&(InfDirPath[PathLength]), + MAX_PATH - PathLength, + L"\\Inf\\"))) { + goto clean0; + } + } else { + + if(FAILED(StringCchCopy(&(InfDirPath[PathLength]), + MAX_PATH - PathLength, + L"Inf\\"))) { + goto clean0; + } + } + + if(lstrcmpi(*MediaRootDirectory, InfDirPath)) { + // + // The INF isn't in %windir%\Inf, so assume its location is the root of + // the installation media. (We don't bother to retrieve the disk name + // or tagfile name in this case.) + // + b = TRUE; + goto clean0; + } + + // + // Since the INF is already in %windir%\Inf, we need to find out where it + // originally came from. There is no direct way to ascertain an INF's + // path of origin, but we can indirectly determine it by retrieving a field + // from our INF that uses a string substitution of %1% (DIRID_SRCPATH). + // + hInf = SetupOpenInfFile(DriverInfoDetailData->InfFileName, + NULL, + INF_STYLE_WIN4, + NULL + ); + + if(hInf == INVALID_HANDLE_VALUE) { + goto clean0; + } + + // + // Contained within our INF should be a [ToastCoInfo] section with the + // following entry: + // + // OriginalInfSourcePath = %1% + // + // If we retrieve the value (i.e., field 1) of this line, we'll get the + // full path where the INF originally came from. + // + if(!SetupFindFirstLine(hInf, L"ToastCoInfo", L"OriginalInfSourcePath", &InfContext)) { + goto clean0; + } + + if(!SetupGetStringField(&InfContext, 1, *MediaRootDirectory, MAX_PATH, &PathLength) || + (PathLength <= 1)) { + goto clean0; + } + + // + // PathLength we get back includes the terminating null character. Subtract + // one to get actual length of string. + // + PathLength--; + + // + // Ensure the path we retrieved has a path separator character at the end. + // + if(((*MediaRootDirectory)[PathLength-1] != L'\\') && + ((*MediaRootDirectory)[PathLength-1] != L'/')) + { + if(FAILED(StringCchCopy(*MediaRootDirectory+PathLength, + MAX_PATH - PathLength, + L"\\"))) { + goto clean0; + } + } + + // + // Now retrieve the disk name and tagfile for our setup media. + // + *MediaDiskName = GlobalAlloc(0, LINE_LEN * sizeof(WCHAR)); + *MediaTagFile = GlobalAlloc(0, MAX_PATH * sizeof(WCHAR)); + + if(!(*MediaDiskName && *MediaTagFile)) { + goto clean0; + } + + if(!SetupGetSourceInfo(hInf, + TOASTER_MEDIA_SOURCE_ID, + SRCINFO_DESCRIPTION, + *MediaDiskName, + LINE_LEN, + NULL)) { + goto clean0; + } + + if(!SetupGetSourceInfo(hInf, + TOASTER_MEDIA_SOURCE_ID, + SRCINFO_TAGFILE, + *MediaTagFile, + MAX_PATH, + NULL)) { + goto clean0; + } + + b = TRUE; + +clean0: + + if(hInf != INVALID_HANDLE_VALUE) { + SetupCloseInfFile(hInf); + } + + if(DriverInfoDetailData) { + GlobalFree(DriverInfoDetailData); + } + + if(!b) { + if(*MediaRootDirectory) { + GlobalFree(*MediaRootDirectory); + *MediaRootDirectory = NULL; + } + if(*MediaDiskName) { + GlobalFree(*MediaDiskName); + *MediaDiskName = NULL; + } + if(*MediaTagFile) { + GlobalFree(*MediaTagFile); + *MediaTagFile = NULL; + } + } + + return b; +} + + +HPROPSHEETPAGE +GetValueAddSoftwareWizPage( + _In_ LPCWSTR MediaRootDirectory, + _In_opt_ LPCWSTR MediaDiskName, + _In_opt_ LPCWSTR MediaTagFile + ) + +/*++ + +Routine Description: + + This function returns a newly-created property sheet page handle that may + be used in a wizard to allow user-selection of value-added software. + + This wizard page is used by both the toaster co-installer (as a finish- + install wizard page), as well as by the toastva installation application. + +Arguments: + + MediaRootDirectory - Supplies the fully-qualified path to the root of the + installation media. + + MediaDiskName - Optionally, supplies the name of the disk to be used when + prompting the user for source media. If this parameter is not + supplied, the media is assumed to already be present, and no prompting + occurs + + MediaTagFile - Optionally, supplies the tagfile for the disk to be used + when prompting the user for source media. If MediaDiskName is not + specified, this parameter is ignored. + +Return Value: + + If this function succeeds, the return value is a newly-created property + sheet page handle. + + If this function fails, the return value is NULL. + +--*/ + +{ + HPROPSHEETPAGE hpsp; + LPVALUEADDWIZDATA ValueAddWizData; //data for the value-add sw chooser page + PROPSHEETPAGE page; + + ValueAddWizData = GlobalAlloc(0, sizeof(VALUEADDWIZDATA)); + + if(ValueAddWizData) { + ZeroMemory(ValueAddWizData, sizeof(VALUEADDWIZDATA)); + } else { + return NULL; + } + + if(FAILED(StringCchCopy(ValueAddWizData->MediaRootDirectory, + MAX_PATH, + MediaRootDirectory))) { + return NULL; + } + + if(MediaDiskName && MediaTagFile) { + // + // The caller wants us to prompt user for media (e.g., installation + // occurring from %windir%\Inf\OEM.INF, and we want to ensure that + // our CD is in the drive before launching setup.exe from it). + // + if(FAILED(StringCchCopy(ValueAddWizData->MediaDiskName, + LINE_LEN, + MediaDiskName))) { + return NULL; + } + + if(FAILED(StringCchCopy(ValueAddWizData->MediaTagFile, + MAX_PATH, + MediaTagFile))) { + return NULL; + } + } + + ZeroMemory(&page, sizeof(PROPSHEETPAGE)); + + // + // Create the sample Wizard Page + // + page.dwSize = sizeof(PROPSHEETPAGE); + page.dwFlags = PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE|PSP_USETITLE|PSP_USECALLBACK; + page.hInstance = g_hInstance; + page.pszHeaderTitle = MAKEINTRESOURCE(IDS_TITLE); + page.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_SUBTITLE); + page.pszTemplate = MAKEINTRESOURCE(IDD_SAMPLE_INSTALLAPP); + page.pfnDlgProc = ValueAddDlgProc; + page.lParam = (LPARAM)ValueAddWizData; + page.pfnCallback = (LPFNPSPCALLBACKW) ValueAddPropSheetPageProc; + + hpsp = CreatePropertySheetPage(&page); + + if(!hpsp) { + GlobalFree(ValueAddWizData); + } + + return hpsp; +} + + +VOID +SetDeviceFriendlyName( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData + ) + +/*++ + +Routine Description: + + This function retrieves the (localized) string format (suitable for use + with FormatMessage) to be used in generating the device's friendly name, + then constructs the name using that format in concert with the device's UI + number. + +Arguments: + + DeviceInfoSet - Supplies a handle to the device information set containing + the element for which an INF driver node is currently selected. + + DeviceInfoData - Supplies the address of a device information element for + which an INF driver node is currently selected. + +Return Value: + + none + +--*/ + +{ + SP_DRVINFO_DATA DriverInfoData; + SP_DRVINFO_DETAIL_DATA DriverInfoDetailData; + HINF hInf; + WCHAR InfSectionWithExt[255]; + WCHAR FormatString[LINE_LEN]; + INFCONTEXT InfContext; + DWORD UINumber; + PVOID FriendlyNameBuffer; + DWORD FriendlyNameBufferSize; + + // + // First, retrieve the format string from the driver's [DDInstall] section. + // + DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA); + if(!SetupDiGetSelectedDriver(DeviceInfoSet, + DeviceInfoData, + &DriverInfoData)) { + // + // NULL driver install + // + goto clean0; + } + + DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA); + if(!SetupDiGetDriverInfoDetail(DeviceInfoSet, + DeviceInfoData, + &DriverInfoData, + &DriverInfoDetailData, + sizeof(DriverInfoDetailData), + NULL) && + (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) + { + // + // Unable to retrieve detail info about selected driver node + // + goto clean0; + } + + hInf = SetupOpenInfFile(DriverInfoDetailData.InfFileName, + NULL, + INF_STYLE_WIN4, + NULL + ); + + if(hInf == INVALID_HANDLE_VALUE) { + // + // Couldn't open the INF + // + goto clean0; + } + + // + // Figure out actual (potentially decorated) DDInstall section being used + // for this install. + // + *FormatString = L'\0'; // default to empty string in case error occurs. + + if(SetupDiGetActualSectionToInstall(hInf, + DriverInfoDetailData.SectionName, + InfSectionWithExt, + sizeof(InfSectionWithExt) / sizeof(WCHAR), + NULL, + NULL)) { + + if(SetupFindFirstLine(hInf, + InfSectionWithExt, + L"FriendlyNameFormat", + &InfContext)) { + + if(!SetupGetStringField(&InfContext, + 1, + FormatString, + sizeof(FormatString) / sizeof(WCHAR), + NULL)) { + // + // Failed to retrieve format string into our buffer. Make sure + // our buffer still contains an empty string. + // + *FormatString = L'\0'; + } + } + } + + SetupCloseInfFile(hInf); + + if(!(*FormatString)) { + goto clean0; + } + + // + // Now retrieve the device's UI number, which is actually the serial number + // used by the bus driver. + // + if(SetupDiGetDeviceRegistryProperty(DeviceInfoSet, + DeviceInfoData, + SPDRP_UI_NUMBER, + NULL, + (PBYTE)&UINumber, + sizeof(UINumber), + NULL)) { + + FriendlyNameBufferSize = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_STRING + | FORMAT_MESSAGE_ARGUMENT_ARRAY, + FormatString, + 0, + 0, + (LPWSTR)&FriendlyNameBuffer, + 0, + (va_list *)&UINumber + ); + + if(FriendlyNameBufferSize) { + + SetupDiSetDeviceRegistryProperty(DeviceInfoSet, + DeviceInfoData, + SPDRP_FRIENDLYNAME, + (PBYTE)FriendlyNameBuffer, + (FriendlyNameBufferSize + 1) * sizeof(WCHAR) + ); + + LocalFree(FriendlyNameBuffer); + } + } + +clean0: + ; // nothing to do +} + diff --git a/general/toaster/toastpkg/toastco/toastco.h b/general/toaster/toastpkg/toastco/toastco.h new file mode 100644 index 00000000..ab1d4f9b --- /dev/null +++ b/general/toaster/toastpkg/toastco/toastco.h @@ -0,0 +1,33 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + toastco.h + +Abstract: + + Header files and resource IDs used by the Toaster sample co-installer. + +--*/ + + +#include +#include + + +#define IDD_SAMPLE_INSTALLAPP 900 +#define IDC_CHECK1 1000 +#define IDC_CHECK2 1001 +#define IDC_CHECK3 1002 +#define IDC_STATIC -1 +#define IDS_TITLE 1 +#define IDS_SUBTITLE 2 +#define IDS_MEDIA_PROMPT_TITLE 3 + diff --git a/general/toaster/toastpkg/toastco/toastco.rc b/general/toaster/toastpkg/toastco/toastco.rc new file mode 100644 index 00000000..384181ef --- /dev/null +++ b/general/toaster/toastpkg/toastco/toastco.rc @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + toastco.rc + +Abstract: + + Resources used by the Toaster sample co-installer + +--*/ + +#include "toastco.h" +#include + +// +// Version resources +// +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,0,0,0 + PRODUCTVERSION 2,0,0,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Microsoft Corporation\0" + VALUE "FileDescription", "Toaster CoInstaller Sample\0" + VALUE "FileVersion", "2.00.0000.0\0" + VALUE "InternalName", "TOSTRCO2.DLL\0" + VALUE "LegalCopyright", "© Microsoft Corporation. All rights reserved.\0" + VALUE "OriginalFilename", "TOSTRCO2.DLL\0" + VALUE "ProductName", "Toaster DDK Sample\0" + VALUE "ProductVersion", "2.0.0000.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +// +// Dialog resources +// +IDD_SAMPLE_INSTALLAPP DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Please select the applications that you would like to install:", + IDC_STATIC,11,0,304,8 + CONTROL "Toaster Device Interface Watcher",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,21,20,296,10 + CONTROL "Toaster Diagnostic Utility",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,21,34,296,10 + CONTROL "Toast-Made-Easy Recipe Program",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,21,48,296,10 +END + +// +// String Table resources +// +STRINGTABLE DISCARDABLE +BEGIN + IDS_TITLE "Choose Additional Applications" + IDS_SUBTITLE "These applications make it quick and easy to make great toast!" + IDS_MEDIA_PROMPT_TITLE "Toaster" +END + diff --git a/general/toaster/toastpkg/toastco/tostrco2.def b/general/toaster/toastpkg/toastco/tostrco2.def new file mode 100644 index 00000000..5ee4e35b --- /dev/null +++ b/general/toaster/toastpkg/toastco/tostrco2.def @@ -0,0 +1,24 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +; KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR +; PURPOSE. +; +;Module Name: +; +; tostrco2.def +; +;Abstract: +; +; Specifies exports for the Toaster Package sample co-installer (version 2) +; +;--*/ + +LIBRARY tostrco2 + +EXPORTS + ToasterCoInstaller + GetValueAddSoftwareWizPage diff --git a/general/toaster/toastpkg/toastco/tostrco2.vcxproj b/general/toaster/toastpkg/toastco/tostrco2.vcxproj new file mode 100644 index 00000000..ee1a266f --- /dev/null +++ b/general/toaster/toastpkg/toastco/tostrco2.vcxproj @@ -0,0 +1,243 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22} + $(MSBuildProjectName) + Debug + Win32 + {F4010EA2-CDEF-4E68-AF56-C0240162D712} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + tostrco2 + + + tostrco2 + + + tostrco2 + + + tostrco2 + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib;kernel32.lib;comctl32.lib;advapi32.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib;kernel32.lib;comctl32.lib;advapi32.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib;kernel32.lib;comctl32.lib;advapi32.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib;kernel32.lib;comctl32.lib;advapi32.lib + + + + + tostrco2.def + + + + + + + + + tostrco2.def + + + + + + + + + tostrco2.def + + + + + + + + + tostrco2.def + + + + + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastpkg/toastco/tostrco2.vcxproj.Filters b/general/toaster/toastpkg/toastco/tostrco2.vcxproj.Filters new file mode 100644 index 00000000..5639c710 --- /dev/null +++ b/general/toaster/toastpkg/toastco/tostrco2.vcxproj.Filters @@ -0,0 +1,33 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {FF79EB36-D2F2-497B-9B3B-1F5D4BA911D4} + + + h;hpp;hxx;hm;inl;inc;xsd + {A4BFE1CB-F5FD-4F98-8EA6-6B0F543535C6} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {F4E6AFB3-5EF2-4D35-92BF-D0DAD16E87B5} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastpkg/toastpkg.sln b/general/toaster/toastpkg/toastpkg.sln new file mode 100644 index 00000000..13fee6ef --- /dev/null +++ b/general/toaster/toastpkg/toastpkg.sln @@ -0,0 +1,62 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toastapp", "Toastapp", "{BFAE4A6A-CD41-4052-AC38-EAC4DB8494DF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toastco", "Toastco", "{4C142C9D-56A8-44C0-84F1-D4ABBFA3E9F8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toastva", "Toastva", "{47FE1DB3-FA82-4F24-96B1-C6798AA08C61}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "toastapp", "toastapp\toastapp.vcxproj", "{1C818755-28B2-4C84-8623-FF84F3326B64}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tostrco2", "toastco\tostrco2.vcxproj", "{84F11D30-54EE-479A-8F8A-7FBFA8E6BE22}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "toastva", "toastva\toastva.vcxproj", "{7A49D840-4E82-477B-8105-CF546E4FD034}" + ProjectSection(ProjectDependencies) = postProject + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22} = {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1C818755-28B2-4C84-8623-FF84F3326B64}.Debug|Win32.ActiveCfg = Debug|Win32 + {1C818755-28B2-4C84-8623-FF84F3326B64}.Debug|Win32.Build.0 = Debug|Win32 + {1C818755-28B2-4C84-8623-FF84F3326B64}.Release|Win32.ActiveCfg = Release|Win32 + {1C818755-28B2-4C84-8623-FF84F3326B64}.Release|Win32.Build.0 = Release|Win32 + {1C818755-28B2-4C84-8623-FF84F3326B64}.Debug|x64.ActiveCfg = Debug|x64 + {1C818755-28B2-4C84-8623-FF84F3326B64}.Debug|x64.Build.0 = Debug|x64 + {1C818755-28B2-4C84-8623-FF84F3326B64}.Release|x64.ActiveCfg = Release|x64 + {1C818755-28B2-4C84-8623-FF84F3326B64}.Release|x64.Build.0 = Release|x64 + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22}.Debug|Win32.ActiveCfg = Debug|Win32 + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22}.Debug|Win32.Build.0 = Debug|Win32 + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22}.Release|Win32.ActiveCfg = Release|Win32 + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22}.Release|Win32.Build.0 = Release|Win32 + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22}.Debug|x64.ActiveCfg = Debug|x64 + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22}.Debug|x64.Build.0 = Debug|x64 + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22}.Release|x64.ActiveCfg = Release|x64 + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22}.Release|x64.Build.0 = Release|x64 + {7A49D840-4E82-477B-8105-CF546E4FD034}.Debug|Win32.ActiveCfg = Debug|Win32 + {7A49D840-4E82-477B-8105-CF546E4FD034}.Debug|Win32.Build.0 = Debug|Win32 + {7A49D840-4E82-477B-8105-CF546E4FD034}.Release|Win32.ActiveCfg = Release|Win32 + {7A49D840-4E82-477B-8105-CF546E4FD034}.Release|Win32.Build.0 = Release|Win32 + {7A49D840-4E82-477B-8105-CF546E4FD034}.Debug|x64.ActiveCfg = Debug|x64 + {7A49D840-4E82-477B-8105-CF546E4FD034}.Debug|x64.Build.0 = Debug|x64 + {7A49D840-4E82-477B-8105-CF546E4FD034}.Release|x64.ActiveCfg = Release|x64 + {7A49D840-4E82-477B-8105-CF546E4FD034}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1C818755-28B2-4C84-8623-FF84F3326B64} = {BFAE4A6A-CD41-4052-AC38-EAC4DB8494DF} + {84F11D30-54EE-479A-8F8A-7FBFA8E6BE22} = {4C142C9D-56A8-44C0-84F1-D4ABBFA3E9F8} + {7A49D840-4E82-477B-8105-CF546E4FD034} = {47FE1DB3-FA82-4F24-96B1-C6798AA08C61} + EndGlobalSection +EndGlobal diff --git a/general/toaster/toastpkg/toastva/header.bmp b/general/toaster/toastpkg/toastva/header.bmp new file mode 100644 index 00000000..49a4cb45 Binary files /dev/null and b/general/toaster/toastpkg/toastva/header.bmp differ diff --git a/general/toaster/toastpkg/toastva/precomp.h b/general/toaster/toastpkg/toastva/precomp.h new file mode 100644 index 00000000..de91c9b6 --- /dev/null +++ b/general/toaster/toastpkg/toastva/precomp.h @@ -0,0 +1,21 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + precomp.h + +Abstract: + + Single container to facilitate use of precompiled headers. + +--*/ + +#include "toastva.h" +#include "strsafe.h" diff --git a/general/toaster/toastpkg/toastva/precompsrc.c b/general/toaster/toastpkg/toastva/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/general/toaster/toastpkg/toastva/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/general/toaster/toastpkg/toastva/resource.h b/general/toaster/toastpkg/toastva/resource.h new file mode 100644 index 00000000..f5ab4bb0 --- /dev/null +++ b/general/toaster/toastpkg/toastva/resource.h @@ -0,0 +1,61 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + resource.h + +Abstract: + + Resource IDs used by the TOASTVA sample. + +--*/ + +#define IDS_TITLE1 1 +#define IDS_SUBTITLE1 2 +#define IDS_TITLE2 3 +#define IDS_SUBTITLE2 4 +#define IDS_CAPTION 5 +#define IDS_PROMPT_FOR_HW 6 +#define IDB_BANNER 102 +#define IDB_WATERMARK 103 +#define IDI_ICON1 104 +#define IDD_INTRO 107 +#define IDD_INTERIOR1 108 +#define IDD_INTERIOR2 109 +#define IDD_END 110 +#define IDC_TITLE 1000 +#define IDC_INTRO_TEXT 1001 +#define IDC_RADIO1 1002 +#define IDC_RADIO2 1003 +#define IDC_RADIO3 1004 +#define IDC_EDIT2 1006 +#define IDC_EDIT3 1007 +#define IDC_EDIT4 1008 +#define IDC_CHECK1 1010 +#define IDC_GROUP1 1011 +#define IDC_CHECK3 1013 +#define IDC_CHECK4 1014 +#define IDC_CHECK5 1015 +#define IDC_ANIMATE1 1016 +#define IDC_FINISH_TEXT 1017 +#define IDC_STATIC -1 +#define IDA_SEARCHING 300 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1018 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif + diff --git a/general/toaster/toastpkg/toastva/search.avi b/general/toaster/toastpkg/toastva/search.avi new file mode 100644 index 00000000..24850baa Binary files /dev/null and b/general/toaster/toastpkg/toastva/search.avi differ diff --git a/general/toaster/toastpkg/toastva/toastapp.ico b/general/toaster/toastpkg/toastva/toastapp.ico new file mode 100644 index 00000000..55ab969e Binary files /dev/null and b/general/toaster/toastpkg/toastva/toastapp.ico differ diff --git a/general/toaster/toastpkg/toastva/toastva.c b/general/toaster/toastpkg/toastva/toastva.c new file mode 100644 index 00000000..be842667 --- /dev/null +++ b/general/toaster/toastpkg/toastva/toastva.c @@ -0,0 +1,209 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + toastva.c + +Abstract: + + The TOASTVA application illustrates installation techniques that can be + used to seamlessly integrate PnP device installation with installation of + value-added software, regardless of whether the software installation + preceeded the hardware installation, or vice versa. + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Constants +// +#if defined(_IA64_) + #define TOASTVA_PLATFORM_SUBDIRECTORY L"ia64" +#elif defined(_X86_) + #define TOASTVA_PLATFORM_SUBDIRECTORY L"i386" +#elif defined(_AMD64_) + #define TOASTVA_PLATFORM_SUBDIRECTORY L"amd64" +#elif defined(_ARM_) + #define TOASTVA_PLATFORM_SUBDIRECTORY L"arm" +#elif defined(_ARM64_) + #define TOASTVA_PLATFORM_SUBDIRECTORY L"arm64" +#else +#error Unsupported platform +#endif + +#define TOASTVA_PLATFORM_SUBDIRECTORY_SIZE (sizeof(TOASTVA_PLATFORM_SUBDIRECTORY) / sizeof(WCHAR)) + +// +// Globals +// +HINSTANCE g_hInstance; + + +// +// Implementation +// + +INT +WINAPI +WinMain( + _In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPSTR lpCmdLine, + _In_ int nShowCmd + ) +{ + LPWSTR *ArgList; + INT NumArgs; + WCHAR MediaRootDirectory[MAX_PATH]; + LPWSTR FileNamePart; + DWORD DirPathLength; + + g_hInstance = hInstance; + + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + UNREFERENCED_PARAMETER(nShowCmd); + + // + // Windows 2000 doesn't suppress auto-run applications when media (e.g., + // a CD) is inserted while a "Found New Hardware" popup is onscreen. This + // means that, by default, inserting a CD in order to supply PnP with the + // necessary INF and driver files will result in the autorun app launching, + // and obscuring the wizard, causing user confusion, etc. + // + // To avoid this, we retrieve an entrypoint to a Windows 2000 (and later) + // Configuration Manager (CM) API that allows us to detect when a device + // installation is in-progress, and suppress our own application from + // starting. + // + if(IsDeviceInstallInProgress()) { + // + // We don't want to startup right now. Don't worry--the value-added + // software part of the device installation will be invoked (if + // necessary) by our device co-installer during finish-install + // processing. + // + return 0; + } + + // + // Retrieve the full directory path from which our setup program was + // invoked. + // + ArgList = CommandLineToArgvW(GetCommandLine(), &NumArgs); + + if(ArgList && (NumArgs >= 1)) { + + DirPathLength = GetFullPathName(ArgList[0], + MAX_PATH, + MediaRootDirectory, + &FileNamePart + ); + + if(DirPathLength >= MAX_PATH) { + // + // The directory is too large for our buffer. Set our directory + // path length to zero so we'll simply bail out in this rare case. + // + DirPathLength = 0; + } + + if(DirPathLength) { + // + // Strip the filename off the path. + // + *FileNamePart = L'\0'; + + DirPathLength = (DWORD)(FileNamePart - MediaRootDirectory); + } + + } else { + // + // For some reason, we couldn't get the command line arguments that + // were used when invoking our setup app. Assume current directory + // instead. + // + DirPathLength = GetCurrentDirectory(MAX_PATH, MediaRootDirectory); + + if(DirPathLength >= MAX_PATH) { + // + // The current directory is too large for our buffer. Set our + // directory path length to zero so we'll simply bail out in this + // rare case. + // + DirPathLength = 0; + } + + if(DirPathLength) { + // + // Ensure that path ends in a path separator character. + // + if((MediaRootDirectory[DirPathLength-1] != L'\\') && + (MediaRootDirectory[DirPathLength-1] != L'/')) + { + MediaRootDirectory[DirPathLength++] = L'\\'; + + if(DirPathLength < MAX_PATH) { + MediaRootDirectory[DirPathLength] = L'\0'; + } else { + // + // Not enough room in buffer to add path separator char + // + DirPathLength = 0; + } + } + } + } + + if(ArgList) { + GlobalFree(ArgList); + } + + if(!DirPathLength) { + // + // Couldn't figure out what the root directory of our installation + // media was. Bail out. + // + return 0; + } + + // + // If we're being invoked from a platform-specific subdirectory (i.e., + // \i386 or \ia64), then strip off that subdirectory to get the true media + // root path. + // + if(DirPathLength > TOASTVA_PLATFORM_SUBDIRECTORY_SIZE) { + // + // We know that the last character in our MediaRootDirectory string is + // a path separator character. Check to see if the preceding + // characters match our platform-specific subdirectory. + // + if(!_wcsnicmp(&(MediaRootDirectory[DirPathLength - TOASTVA_PLATFORM_SUBDIRECTORY_SIZE]), + TOASTVA_PLATFORM_SUBDIRECTORY, + TOASTVA_PLATFORM_SUBDIRECTORY_SIZE - 1)) { + // + // Platform-specific part matches, just make sure preceding char + // is a path separator char. + // + if((MediaRootDirectory[DirPathLength - TOASTVA_PLATFORM_SUBDIRECTORY_SIZE - 1] == L'\\') || + (MediaRootDirectory[DirPathLength - TOASTVA_PLATFORM_SUBDIRECTORY_SIZE - 1] == L'/')) { + + MediaRootDirectory[DirPathLength - TOASTVA_PLATFORM_SUBDIRECTORY_SIZE] = L'\0'; + } + } + } + + DoValueAddWizard(MediaRootDirectory); + + return 0; +} diff --git a/general/toaster/toastpkg/toastva/toastva.h b/general/toaster/toastpkg/toastva/toastva.h new file mode 100644 index 00000000..10ccb508 --- /dev/null +++ b/general/toaster/toastpkg/toastva/toastva.h @@ -0,0 +1,79 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + toastva.h + +Abstract: + + Header files and resource IDs used by the TOASTVA sample. + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "resource.h" + +// +// String constants +// +#define ENUMERATOR_NAME L"{b85b7c50-6a01-11d2-b841-00c04fad5171}" +#define HW_ID_TO_UPDATE L"{b85b7c50-6a01-11d2-b841-00c04fad5171}\\MsToaster" +#define DEVICE_INF_NAME L"toastpkg.inf" +#define TOASTAPP_SETUP_PATH L"ToastApp\\setup.exe" + +// +// global variables +// +extern HINSTANCE g_hInstance; + +// +// utility routines +// +BOOL +IsDeviceInstallInProgress(VOID); + +VOID +MarkDevicesAsNeedReinstall( + _In_ HDEVINFO DeviceInfoSet + ); + +DWORD +GetDeviceConfigFlags( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData + ); + +VOID +SetDeviceConfigFlags( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData, + _In_ DWORD ConfigFlags + ); + +HDEVINFO +GetNonPresentDevices( + _In_ LPCWSTR Enumerator OPTIONAL, + _In_ LPCWSTR HardwareID + ); + +VOID +DoValueAddWizard( + _In_ LPCWSTR MediaRootDirectory + ); + diff --git a/general/toaster/toastpkg/toastva/toastva.rc b/general/toaster/toastpkg/toastva/toastva.rc new file mode 100644 index 00000000..4868ed3b --- /dev/null +++ b/general/toaster/toastpkg/toastva/toastva.rc @@ -0,0 +1,221 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + toastva.rc + +Abstract: + + Resources used by the TOASTVA sample. + +--*/ + +#include +#include "resource.h" + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Microsoft Corporation\0" + VALUE "FileDescription", "Toaster Value-Add Installer Sample\0" + VALUE "FileVersion", "1.00.0000.0\0" + VALUE "InternalName", "TOASTVA.EXE\0" + VALUE "LegalCopyright", "© Microsoft Corporation. All rights reserved.\0" + VALUE "OriginalFilename", "TOASTVA.EXE\0" + VALUE "ProductName", "Toaster DDK Sample\0" + VALUE "ProductVersion", "1.0.0000.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_INTRO DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Toaster" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Welcome to Toasting Made Simple!",IDC_TITLE,115,9,189, + 31,NOT WS_GROUP + LTEXT "This wizard will help you install supporting software for your toaster, and allow you to choose additional applications that will help you get the most from your new toaster.", + IDC_INTRO_TEXT,115,40,189,53 +END + +IDD_INTERIOR1 DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Animate1",IDC_ANIMATE1,"SysAnimate32", + ACS_TRANSPARENT,148,50,20,20,WS_EX_TRANSPARENT +END + +IDD_END DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Toaster" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Congratulations! You've successfully installed your new toaster's software.", + IDC_TITLE,115,8,195,37 + LTEXT "Enjoy the toast!",IDC_FINISH_TEXT,115,58,195,128 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_INTRO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 310 + VERTGUIDE, 115 + VERTGUIDE, 304 + TOPMARGIN, 7 + BOTTOMMARGIN, 186 + HORZGUIDE, 8 + HORZGUIDE, 40 + END + + IDD_INTERIOR1, DIALOG + BEGIN + LEFTMARGIN, 28 + RIGHTMARGIN, 310 + VERTGUIDE, 28 + BOTTOMMARGIN, 136 + END + + IDD_END, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 310 + VERTGUIDE, 115 + TOPMARGIN, 7 + BOTTOMMARGIN, 186 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_BANNER BITMAP DISCARDABLE "header.bmp" +IDB_WATERMARK BITMAP DISCARDABLE "watermrk.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// AVIs +// + +IDA_SEARCHING AVI "SEARCH.AVI" + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON DISCARDABLE "toastapp.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_TITLE1 "Installing Toaster Support Software" + IDS_SUBTITLE1 "Please wait while we install the software that makes your toaster work." + IDS_TITLE2 "Choose Additional Applications" + IDS_SUBTITLE2 "These applications make it quick and easy to make great toast!" + IDS_PROMPT_FOR_HW "You may now plug in your toaster. It will automatically be discovered and configured for you. Enjoy the toast!" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/general/toaster/toastpkg/toastva/toastva.vcxproj b/general/toaster/toastpkg/toastva/toastva.vcxproj new file mode 100644 index 00000000..1a25ac8c --- /dev/null +++ b/general/toaster/toastpkg/toastva/toastva.vcxproj @@ -0,0 +1,221 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {7A49D840-4E82-477B-8105-CF546E4FD034} + $(MSBuildProjectName) + Debug + Win32 + {2DF87D9A-363B-4C6B-9E31-BE2BF60898DF} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + toastva + + + toastva + + + toastva + + + toastva + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib;shell32.lib;comctl32.lib;newdev.lib;.\..\toastco\$(IntDir)\tostrco2.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib;shell32.lib;comctl32.lib;newdev.lib;.\..\toastco\$(IntDir)\tostrco2.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib;shell32.lib;comctl32.lib;newdev.lib;.\..\toastco\$(IntDir)\tostrco2.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);setupapi.lib;user32.lib;shell32.lib;comctl32.lib;newdev.lib;.\..\toastco\$(IntDir)\tostrco2.lib + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastpkg/toastva/toastva.vcxproj.Filters b/general/toaster/toastpkg/toastva/toastva.vcxproj.Filters new file mode 100644 index 00000000..8ae9e8e3 --- /dev/null +++ b/general/toaster/toastpkg/toastva/toastva.vcxproj.Filters @@ -0,0 +1,36 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C874EFAD-3886-466E-97CC-4A852797DC29} + + + h;hpp;hxx;hm;inl;inc;xsd + {4A7A3632-2660-49C4-9B04-6BD51BEA6316} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {CDCE6E71-5F53-4E60-99F1-ABD1D5819475} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastpkg/toastva/util.c b/general/toaster/toastpkg/toastva/util.c new file mode 100644 index 00000000..e2f68c5b --- /dev/null +++ b/general/toaster/toastpkg/toastva/util.c @@ -0,0 +1,517 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + util.c + +Abstract: + + Utility routines used by the TOASTVA sample. + +--*/ + +#include "precomp.h" +#pragma hdrstop + +/*++ + + Here's the description for the CMP_WaitNoPendingInstallEvents API used to + suppress execution of this app (e.g., via autorun upon CD insertion) while + device installation is underway... + + DWORD + CMP_WaitNoPendingInstallEvents( + _In_ DWORD dwTimeout + ); + + Routine Description: + + This routine waits until there are no pending device install events. + If a timeout value is specified then it will return either when no + install events are pending or when the timeout period has expired, + whichever comes first. This routine is intended to be called after + user-logon, only. + + NOTE: New install events can occur at anytime, this routine just + indicates that there are no install events at this moment. + + Parameters: + + dwTimeout - Specifies the time-out interval, in milliseconds. The + function returns if the interval elapses, even if there are still + pending install events. If dwTimeout is zero, the function just + tests whether there are pending install events and returns + immediately. If dwTimeout is INFINITE, the function's time-out + interval never elapses. + + Return Value: + + If the function succeeds, the return value indicates the event that + caused the function to return. If the function fails, the return value + is WAIT_FAILED. To get extended error information, call GetLastError. + The return value on success is one of the following values: + + WAIT_ABANDONED The specified object is a mutex object that was not + released by the thread that owned the mutex object + before the owning thread terminated. Ownership of the + mutex object is granted to the calling thread, and the + mutex is set to nonsignaled. + WAIT_OBJECT_0 The state of the specified object is signaled. + WAIT_TIMEOUT The time-out interval elapsed, and the object's state is + nonsignaled. + +--*/ + +typedef DWORD (WINAPI *CMP_WAITNOPENDINGINSTALLEVENTS_PROC)( + _In_ DWORD dwTimeout + ); + + +BOOL +IsDeviceInstallInProgress(VOID) + +/*++ + +Routine Description: + + This routine dynamically retrieves the entrypoint to a Windows 2000 (and + later) Configuration Manager (CM) API that can be used to detect whether + there are presently any device installations in-progress. If this API is + not available (it may be obsoleted on future releases of the OS), then the + API assumes there are no device installations in progress. This is OK, + because future versions of the OS will suppress auto-run when "Found New + Hardware" wizard is up, thus eliminating the need for apps to do their own + checking. + +Arguments: + + none + +Return Value: + + If there is presently a device installation in progress, the return value + is TRUE. + + If there is not presently a device installation in progress, or we were + unsuccessful in retrieving the necessary Configuration Manager API, then + the return value is FALSE. + +--*/ + +{ + HMODULE hModule; + CMP_WAITNOPENDINGINSTALLEVENTS_PROC pCMP_WaitNoPendingInstallEvents; + + hModule = GetModuleHandle(L"setupapi.dll"); + + if(!hModule) { + // + // Should never happen since we're linked to setupapi, but... + // + return FALSE; + } + + pCMP_WaitNoPendingInstallEvents = + (CMP_WAITNOPENDINGINSTALLEVENTS_PROC)GetProcAddress( + hModule, + "CMP_WaitNoPendingInstallEvents" + ); + if(!pCMP_WaitNoPendingInstallEvents) { + // + // We're running on a release of the OS that doesn't supply this API. + // Trust the OS to suppress autorun when appropriate. + // + return FALSE; + } + + return (pCMP_WaitNoPendingInstallEvents(0) == WAIT_TIMEOUT); +} + + +VOID +MarkDevicesAsNeedReinstall( + _In_ HDEVINFO DeviceInfoSet + ) + +/*++ + +Routine Description: + + This routine enumerates every device information element in the specified + list and sets the CONFIGFLAG_REINSTALL registry flag for each one. + +Arguments: + + DeviceInfoSet - Supplies a handle to the device information set whose + members are to be marked as need-reinstall. + +Return Value: + + none + +--*/ + +{ + SP_DEVINFO_DATA DeviceInfoData; + DWORD i, ConfigFlags; + + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + for(i = 0; + SetupDiEnumDeviceInfo(DeviceInfoSet, i, &DeviceInfoData); + i++) + { + ConfigFlags = GetDeviceConfigFlags(DeviceInfoSet, &DeviceInfoData); + ConfigFlags |= CONFIGFLAG_REINSTALL; + SetDeviceConfigFlags(DeviceInfoSet, &DeviceInfoData, ConfigFlags); + } +} + + +DWORD +GetDeviceConfigFlags( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData + ) + +/*++ + +Routine Description: + + This routine retrieves the ConfigFlags registry property for the specified + device info element, or zero if the property cannot be retrieved (e.g., + because ConfigFlags haven't yet been set by Found New Hardware process). + +Arguments: + + DeviceInfoSet - Supplies a handle to the device information set containing + the device of interest. + + DeviceInfoData - Supplies context of a device info element for which + ConfigFlags is to be retrieved. + +Return Value: + + If device's REG_DWORD ConfigFlags property can be retrieved, it is returned. + Otherwise, zero is returned. + +--*/ + +{ + DWORD ConfigFlags, RegDataType; + + if(!SetupDiGetDeviceRegistryProperty(DeviceInfoSet, + DeviceInfoData, + SPDRP_CONFIGFLAGS, + &RegDataType, + (PBYTE)&ConfigFlags, + sizeof(ConfigFlags), + NULL) + || (RegDataType != REG_DWORD)) + { + // + // It's possible that this property isn't there, although we should + // never enounter other problems like wrong datatype or data length + // longer than sizeof(DWORD). In any event, just return zero. + // + ConfigFlags = 0; + } + + return ConfigFlags; +} + + +VOID +SetDeviceConfigFlags( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData, + _In_ DWORD ConfigFlags + ) + +/*++ + +Routine Description: + + This routine sets a device's ConfigFlags property to the specified value. + +Arguments: + + DeviceInfoSet - Supplies a handle to the device information set containing + the device of interest. + + DeviceInfoData - Supplies context of a device info element for which + ConfigFlags is to be set. + + ConfigFlags - Specifies the value to be stored to the device's ConfigFlags + property. + +Return Value: + + none + +--*/ + +{ + SetupDiSetDeviceRegistryProperty(DeviceInfoSet, + DeviceInfoData, + SPDRP_CONFIGFLAGS, + (PBYTE)&ConfigFlags, + sizeof(ConfigFlags) + ); +} + + +HDEVINFO +GetNonPresentDevices( + _In_ LPCWSTR Enumerator OPTIONAL, + _In_ LPCWSTR HardwareID + ) + +/*++ + +Routine Description: + + This routine retrieves any non-present devices matching the specified + criteria, and returns them in a device information set. + +Arguments: + + Enumerator - Optionally, supplies the name of the Enumerator under which + this device may be found. If the device may show up under more than + one enumerator, the routine can be called with Enumerator specified as + NULL, in which case all device instances in the registry are examined. + + HardwareID - Supplies the hardware ID to be searched for. This will be + compared against each of the hardware IDs for all device instances in + the system (potentially filtered based on Enumerator), present or not. + +Return Value: + + If any non-present devices are discovered, this routine returns a device + information set containing those devices. This set must be freed via + SetupDiDestroyDeviceInfoList by the caller. + + If no such devices are encountered (or if an error occurs), the return + value is INVALID_HANDLE_VALUE. GetLastError will indicate the cause of + failure. + +--*/ + +{ + HDEVINFO AllDevs, ExistingNonPresentDevices; + DWORD i, Err; + SP_DEVINFO_DATA DeviceInfoData; + LPWSTR HwIdBuffer, CurId; + DWORD HwIdBufferLen, RegDataType, RequiredSize; + BOOL bRet; + ULONG Status, Problem; + TCHAR DeviceInstanceId[MAX_DEVNODE_ID_LEN]; + + ExistingNonPresentDevices = INVALID_HANDLE_VALUE; + + AllDevs = SetupDiGetClassDevs(NULL, + Enumerator, + NULL, + DIGCF_ALLCLASSES + ); + + if(AllDevs == INVALID_HANDLE_VALUE) { + // + // last error has already been set during the above call. + // + return INVALID_HANDLE_VALUE; + } + + // + // Iterate through each device we found, comparing its hardware ID(s) + // against the one we were passed in. + // + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + HwIdBuffer = NULL; + HwIdBufferLen = 0; + Err = NO_ERROR; + bRet = FALSE; + + i = 0; + + while(SetupDiEnumDeviceInfo(AllDevs, i, &DeviceInfoData)) { + // + // Retrieve the HardwareID property for this device info element + // + if(!SetupDiGetDeviceRegistryProperty(AllDevs, + &DeviceInfoData, + SPDRP_HARDWAREID, + &RegDataType, + (PBYTE)HwIdBuffer, + HwIdBufferLen, + &RequiredSize)) { + // + // If the failure was due to buffer-too-small, we can resize and + // try again. + // + if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + + if(HwIdBuffer) { + GlobalFree(HwIdBuffer); + } + + HwIdBuffer = GlobalAlloc(0, RequiredSize); + if(HwIdBuffer) { + HwIdBufferLen = RequiredSize; + // + // try again + // + continue; + } else { + // + // We failed to allocate the buffer we needed. This is + // considered a critical failure that should cause us to + // bail. + // + Err = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + } else { + // + // We failed to retrieve the property for some other reason. + // Skip this device and move on to the next. + // + i++; + continue; + } + } + + if((RegDataType != REG_MULTI_SZ) || (RequiredSize < sizeof(TCHAR))) { + // + // Data is invalid--this should never happen, but we'll skip the + // device in this case... + // + i++; + continue; + } + + // + // If we get to here, then we successfully retrieved the multi-sz + // hardware id list for this device. Compare each of those IDs with + // the caller-supplied one. + // + for(CurId = HwIdBuffer; CurId && *CurId; CurId += (lstrlen(CurId) + 1)) { + + if(!lstrcmpi(CurId, HardwareID)) { + // + // We found a match! + // + bRet = TRUE; + + // + // If the device isn't currently present (as indicated by + // failure to retrieve its status), then add it to the list of + // such devices to be returned to the caller. + // + if(CR_SUCCESS != CM_Get_DevNode_Status(&Status, + &Problem, + (DEVNODE)DeviceInfoData.DevInst, + 0)) + { + if(ExistingNonPresentDevices == INVALID_HANDLE_VALUE) { + // + // This is the first non-present device we've + // encountered--we need to create the HDEVINFO set. + // + ExistingNonPresentDevices = + SetupDiCreateDeviceInfoList(NULL, NULL); + + if(ExistingNonPresentDevices == INVALID_HANDLE_VALUE) { + // + // Failure to create this set is a critical error! + // + Err = GetLastError(); + bRet = FALSE; + break; + } + } + + // + // We need to get the device instance's name so we can + // open it up into our "non-present devices" list + // + if(!SetupDiGetDeviceInstanceId(AllDevs, + &DeviceInfoData, + DeviceInstanceId, + sizeof(DeviceInstanceId) / sizeof(TCHAR), + NULL)) { + // + // Should never fail, but considered critical if it + // does... + // + Err = GetLastError(); + bRet = FALSE; + break; + } + + // + // Now open up the non-present device into our list. + // + if(!SetupDiOpenDeviceInfo(ExistingNonPresentDevices, + DeviceInstanceId, + NULL, + 0, + NULL)) { + // + // This failure is also considered critical! + // + Err = GetLastError(); + bRet = FALSE; + } + + break; + } + } + } + + if(Err != NO_ERROR) { + // + // Critical error encountered--bail! + // + break; + } + + // + // Move onto the next device instance + // + i++; + } + + if(HwIdBuffer) { + GlobalFree(HwIdBuffer); + } + + // + // We can now destroy our temporary list of all devices under consideration + // + SetupDiDestroyDeviceInfoList(AllDevs); + + if((Err != NO_ERROR) && + (ExistingNonPresentDevices != INVALID_HANDLE_VALUE)) { + // + // We encountered a critical error, so we need to destroy the (partial) + // list of non-present devices we'd built. + // + SetupDiDestroyDeviceInfoList(ExistingNonPresentDevices); + ExistingNonPresentDevices = INVALID_HANDLE_VALUE; + } + + SetLastError(Err); + + return ExistingNonPresentDevices; +} + diff --git a/general/toaster/toastpkg/toastva/watermrk.bmp b/general/toaster/toastpkg/toastva/watermrk.bmp new file mode 100644 index 00000000..5749637d Binary files /dev/null and b/general/toaster/toastpkg/toastva/watermrk.bmp differ diff --git a/general/toaster/toastpkg/toastva/wizard.c b/general/toaster/toastpkg/toastva/wizard.c new file mode 100644 index 00000000..5f876a3f --- /dev/null +++ b/general/toaster/toastpkg/toastva/wizard.c @@ -0,0 +1,883 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + wizard.c + +Abstract: + + This module implements the TOASTVA wizard that installs/updates toaster + drivers and allows the user to select additional value-added software. + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Constants +// +#define WMX_UPDATE_DRIVER_DONE (WM_USER + 500) + +// +// Structures +// +typedef struct _SHAREDWIZDATA { + HFONT hTitleFont; // Title font for the Welcome and Completion pages + BOOL HwInsertedFirst; // Is the hardware already present? + LPCWSTR MediaRootDirectory; // Fully-qualified path to root of install media + BOOL DoDriverUpdatePage; // Should Update Driver page do anything? + BOOL RebootRequired; // Did we do anything that requires a reboot? + HWND hwndDlg; // Handle to dialog notified by drv update thread +} SHAREDWIZDATA, *LPSHAREDWIZDATA; + +// +// Function prototypes +// +INT_PTR +CALLBACK +IntroDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR +CALLBACK +IntPage1DlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +INT_PTR +CALLBACK +EndDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ); + +DWORD +WINAPI +UpdateDriverThreadProc( + _In_ LPVOID ThreadData + ); + +INT +CALLBACK +WizardCallback( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ LPARAM lParam + ); + +// +// Prototype for a routine exported from toastco.dll +// +HPROPSHEETPAGE +GetValueAddSoftwareWizPage( + _In_ LPCWSTR MediaRootDirectory, + _In_opt_ LPCWSTR MediaDiskName, + _In_opt_ LPCWSTR MediaTagFile + ); + +// +// Implementation +// + +VOID +DoValueAddWizard( + _In_ LPCWSTR MediaRootDirectory + ) + +/*++ + +Routine Description: + + This routine displays a wizard that steps the user through the following + actions: + + (a) Performs a "driver update" for any currently-present toasters + (b) Installs the INF and CAT in case no toasters presently exist + (c) Optionally, installs value-add software selected by the user (this + wizard page is retrieved from the toaster co-installer, and is the same + page the user gets if they do a "hardware-first" installation using our + driver). + +Arguments: + + MediaRootDirectory - Supplies the fully-qualified path to the root + directory where the installation media is located. + +Return Value: + + none + +--*/ + +{ + PROPSHEETPAGE psp = {0}; //defines the property sheet pages + HPROPSHEETPAGE ahpsp[4] = {0}; //an array to hold the page's HPROPSHEETPAGE handles + PROPSHEETHEADER psh = {0}; //defines the property sheet + SHAREDWIZDATA wizdata = {0}; //the shared data structure + + NONCLIENTMETRICS ncm = {0}; + LOGFONT TitleLogFont; + HDC hdc; + INT FontSize; + INT index = 0; + HRESULT hr; + + // + //Create the Wizard pages + // + // Intro page... + // + psp.dwSize = sizeof(psp); + psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER; + psp.hInstance = g_hInstance; + psp.lParam = (LPARAM) &wizdata; //The shared data structure + psp.pfnDlgProc = IntroDlgProc; + psp.pszTemplate = MAKEINTRESOURCE(IDD_INTRO); + + ahpsp[index++] = CreatePropertySheetPage(&psp); + + // + // Updating drivers page... + // + psp.dwFlags = PSP_DEFAULT|PSP_USEHEADERTITLE|PSP_USEHEADERSUBTITLE|PSP_USETITLE; + psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_TITLE1); + psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_SUBTITLE1); + psp.pszTemplate = MAKEINTRESOURCE(IDD_INTERIOR1); + psp.pfnDlgProc = IntPage1DlgProc; + + ahpsp[index++] = CreatePropertySheetPage(&psp); + + // + // Retrieve the value-add software chooser page from the toaster + // co-installer (toastco.dll). + // + ahpsp[index] = GetValueAddSoftwareWizPage(MediaRootDirectory, NULL, NULL); + + if(ahpsp[index]) { + index++; + } + + // + // Finish page... + // + psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER; + psp.pszTemplate = MAKEINTRESOURCE(IDD_END); + psp.pfnDlgProc = EndDlgProc; + + ahpsp[index] = CreatePropertySheetPage(&psp); + + // + // Create the property sheet... + // + psh.dwSize = sizeof(psh); + psh.hInstance = g_hInstance; + psh.hwndParent = NULL; + psh.phpage = ahpsp; + psh.dwFlags = PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER|PSH_STRETCHWATERMARK|PSH_WIZARD|PSH_USECALLBACK; + psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK); + psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER); + psh.nStartPage = 0; + psh.nPages = 4; + psh.pfnCallback = WizardCallback; + + // + // Set up the font for the titles on the intro and ending pages + // + ncm.cbSize = sizeof(ncm); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); + + // + // Create the intro/end title font + // + TitleLogFont = ncm.lfMessageFont; + TitleLogFont.lfWeight = FW_BOLD; + hr = StringCchCopy(TitleLogFont.lfFaceName, LF_FACESIZE, L"Verdana Bold"); + if(SUCCEEDED(hr) == FALSE) { + return; // assert + } + + hdc = GetDC(NULL); //gets the screen DC + FontSize = 12; + TitleLogFont.lfHeight = 0 - GetDeviceCaps(hdc, LOGPIXELSY) * FontSize / 72; + wizdata.hTitleFont = CreateFontIndirect(&TitleLogFont); + ReleaseDC(NULL, hdc); + wizdata.MediaRootDirectory = MediaRootDirectory; + + // + // Display the wizard + // + PropertySheet(&psh); + + // + // Destroy the fonts + // + DeleteObject(wizdata.hTitleFont); + + // + // If we did anything that requires a reboot, prompt the user + // now. Note that we need to do this regardle + // + if(wizdata.RebootRequired) { + SetupPromptReboot(NULL, NULL, FALSE); + } +} + + +INT_PTR +CALLBACK +IntroDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) + +/*++ + +Routine Description: + + This function is the dialog procedure for the Welcome page of the wizard. + +Arguments: + + hwndDlg - Supplies a handle to the dialog box window + + uMsg - Supplies the message + + wParam - Supplies the first message parameter + + lParam - Supplies the second message parameter + +Return Value: + + This dialog procedure always returns zero. + +--*/ + +{ + LPSHAREDWIZDATA pdata; + LPNMHDR lpnm; + + UNREFERENCED_PARAMETER( wParam ); + + // + // Retrieve the shared user data from GWL_USERDATA + // + pdata = (LPSHAREDWIZDATA) GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch(uMsg) { + + case WM_INITDIALOG : + { + HWND hwndControl; + + // + // Get the shared data from PROPSHEETPAGE lParam valueand load + // it into GWL_USERDATA + // + pdata = (LPSHAREDWIZDATA) ((LPPROPSHEETPAGE) lParam) -> lParam; + + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) pdata); + + // + // It's an intro/end page, so get the title font from the + // shared data and use it for the title control + // + hwndControl = GetDlgItem(hwndDlg, IDC_TITLE); + SetWindowFont(hwndControl,pdata->hTitleFont, TRUE); + break; + } + + case WM_NOTIFY : + + lpnm = (LPNMHDR)lParam; + + switch(lpnm->code) { + + case PSN_SETACTIVE : + // + // Enable the Next button + // + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT); + + // + // When we're moving forward through the wizard, we want + // the driver update page to do its work. + // + pdata->DoDriverUpdatePage = TRUE; + break; + + case PSN_WIZNEXT : + //Handle a Next button click here + break; + + case PSN_RESET : + //Handle a Cancel button click, if necessary + break; + + default : + break; + } + break; + + default: + break; + } + + return 0; +} + + +INT_PTR +CALLBACK +IntPage1DlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) + +/*++ + +Routine Description: + + This function is the dialog procedure for the first interior wizard page. + This page updates the drivers for any existing (present) devices, or + installs the INF if there aren't any present devices. + +Arguments: + + hwndDlg - Supplies a handle to the dialog box window + + uMsg - Supplies the message + + wParam - Supplies the first message parameter + + lParam - Supplies the second message parameter + +Return Value: + + This dialog procedure always returns zero. + +--*/ + +{ + LPSHAREDWIZDATA pdata; + LPNMHDR lpnm; + HANDLE hThread; + HKEY hKey; + DWORD UserPrompted; + + UNREFERENCED_PARAMETER( wParam ); + + // + // Retrieve the shared user data from GWL_USERDATA + // + pdata = (LPSHAREDWIZDATA) GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch(uMsg) { + + case WM_INITDIALOG : + // + // Get the PROPSHEETPAGE lParam value and load it into GWL_USERDATA + // + pdata = (LPSHAREDWIZDATA) ((LPPROPSHEETPAGE) lParam) -> lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) pdata); + break; + + case WM_NOTIFY : + + lpnm = (LPNMHDR)lParam; + + switch(lpnm->code) { + + case PSN_SETACTIVE : + // + // If we're coming here from the intro page, then disable + // the Back and Next buttons (we're going to be busy for a + // little bit updating drivers). + // + // If we're coming to this page from anywhere else, + // immediately jump to the intro page. + // + if(pdata->DoDriverUpdatePage) { + // + // Reset our flag so that we won't try this again if we + // go to later pages, then come back to this one. (We + // only do anything when the wizard page is accessed in + // the forward direction, from the Intro page.) + // + pdata->DoDriverUpdatePage = FALSE; + + // + // Set our "UserPrompted" registry flag so the + // co-installer won't popup its own value-add software + // chooser page during driver update. + // + if(ERROR_SUCCESS == RegCreateKeyEx( + HKEY_LOCAL_MACHINE, + TEXT("SOFTWARE\\Microsoft\\Toaster"), + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, + NULL, + &hKey, + NULL)) { + + UserPrompted = 1; + RegSetValueEx(hKey, + TEXT("User Prompted"), + 0, + REG_DWORD, + (PBYTE)&UserPrompted, + sizeof(UserPrompted) + ); + + RegCloseKey(hKey); + } + + // + // Disable Next, Back, and Cancel + // + PropSheet_SetWizButtons(GetParent(hwndDlg), 0); + EnableWindow(GetDlgItem(GetParent(hwndDlg), IDCANCEL), FALSE); + + // + // Show "searching" animation... + // + ShowWindow(GetDlgItem(hwndDlg, IDC_ANIMATE1), SW_SHOW); + Animate_Open(GetDlgItem(hwndDlg, IDC_ANIMATE1), MAKEINTRESOURCE(IDA_SEARCHING)); + Animate_Play(GetDlgItem(hwndDlg, IDC_ANIMATE1), 0, -1, -1); + + // + // Create a thread to do the work of updating the + // driver, etc. + // + pdata->hwndDlg = hwndDlg; + + hThread = CreateThread(NULL, + 0, + UpdateDriverThreadProc, + pdata, + 0, + NULL + ); + + if(hThread) { + // + // Thread launched successfully--close the handle, + // then just wait to be notified of thread's + // completion. + // + CloseHandle(hThread); + + } else { + // + // Couldn't launch the thread--just move on to the + // value-add software page. + // + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT); + PropSheet_PressButton(GetParent(hwndDlg), PSBTN_NEXT); + } + + } else { + // + // We're coming "back" to this page. Skip it, and go + // to the intro page. + // + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK); + PropSheet_PressButton(GetParent(hwndDlg), PSBTN_BACK); + } + break; + + case PSN_WIZNEXT : + //Handle a Next button click, if necessary + break; + + case PSN_WIZBACK : + //Handle a Back button click, if necessary + break; + + case PSN_RESET : + //Handle a Cancel button click, if necessary + break; + + default : + break; + } + break; + + case WMX_UPDATE_DRIVER_DONE : + // + // Stop "searching" animation... + // + Animate_Stop(GetDlgItem(hwndDlg, IDC_ANIMATE1)); + ShowWindow(GetDlgItem(hwndDlg, IDC_ANIMATE1), SW_HIDE); + + // + // Regardless of whether we succeeded in upgrading any drivers, we'll + // go ahead and proceed to the value-add software page. + // + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT); + EnableWindow(GetDlgItem(GetParent(hwndDlg), IDCANCEL), TRUE); + + PropSheet_PressButton(GetParent(hwndDlg), PSBTN_NEXT); + + break; + + default: + break; + } + + return 0; +} + + +INT_PTR +CALLBACK +EndDlgProc( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) + +/*++ + +Routine Description: + + This function is the dialog procedure for the Finish page of the wizard. + +Arguments: + + hwndDlg - Supplies a handle to the dialog box window + + uMsg - Supplies the message + + wParam - Supplies the first message parameter + + lParam - Supplies the second message parameter + +Return Value: + + This dialog procedure always returns zero. + +--*/ + +{ + LPSHAREDWIZDATA pdata; + LPNMHDR lpnm; + + UNREFERENCED_PARAMETER( wParam ); + + // + // Retrieve the shared user data from GWL_USERDATA + // + pdata = (LPSHAREDWIZDATA) GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch(uMsg) { + + case WM_INITDIALOG : + { + HWND hwndControl; + + // + // Get the shared data from PROPSHEETPAGE lParam value and load + // it into GWL_USERDATA + // + pdata = (LPSHAREDWIZDATA) ((LPPROPSHEETPAGE) lParam) -> lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) pdata); + + // + // It's an intro/end page, so get the title font from userdata + // and use it on the title control + // + hwndControl = GetDlgItem(hwndDlg, IDC_TITLE); + SetWindowFont(hwndControl,pdata->hTitleFont, TRUE); + break; + } + + case WM_NOTIFY : + + lpnm = (LPNMHDR)lParam; + + switch(lpnm->code) { + + case PSN_SETACTIVE : + // + // Enable the correct buttons for the active page + // + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_FINISH); + + // + // Doesn't make sense to have Cancel button enabled here + // + EnableWindow(GetDlgItem(GetParent(hwndDlg), IDCANCEL), FALSE); + + // + // If we didn't find any currently-present devices, then prompt + // the user to insert their device now. + // + if(!pdata->HwInsertedFirst) { + + WCHAR TempString[LINE_LEN]; + + if(LoadString(g_hInstance, + IDS_PROMPT_FOR_HW, + TempString, + sizeof(TempString) / sizeof(WCHAR))) { + + SetDlgItemText(hwndDlg, IDC_FINISH_TEXT, TempString); + } + } + + break; + + case PSN_WIZBACK : + // + // Jumping back from this page, so turn Cancel button back on. + // + EnableWindow(GetDlgItem(GetParent(hwndDlg), IDCANCEL), TRUE); + break; + + default : + break; + } + break; + + default: + break; + } + + return 0; +} + + +DWORD +WINAPI +UpdateDriverThreadProc( + _In_ LPVOID ThreadData + ) + +/*++ + +Routine Description: + + This function updates the drivers for any existing toasters. If there are + no toasters currently connected to the computer, it installs the INF/CAT so + that the system will be ready to automatically install toasters that are + plugged in later. This routine will also mark any non-present (aka, + "phantom") toasters as needs-reinstall, so that they'll be updated to the + new driver if they're ever plugged in again. + +Arguments: + + ThreadData - Supplies a pointer to a SHAREDWIZDATA structure that's used + both by this thread, and by the wizard in the main thread. + +Return Value: + + If successful, the function returns NO_ERROR. + + Otherwise, the function returns a Win32 error code indicating the cause of + failure. + +--*/ + +{ + DWORD Err; + LPSHAREDWIZDATA pdata; + WCHAR FullInfPath[MAX_PATH]; + HDEVINFO ExistingNonPresentDevices; + + + pdata = (LPSHAREDWIZDATA)ThreadData; + Err = NO_ERROR; + + // + // First, attempt to update any present devices to our driver... + // + if (FAILED(StringCchCopy(FullInfPath, MAX_PATH, pdata->MediaRootDirectory))) { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if (FAILED(StringCchCat(FullInfPath, MAX_PATH, DEVICE_INF_NAME))) { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if(UpdateDriverForPlugAndPlayDevices(GetParent(pdata->hwndDlg), + HW_ID_TO_UPDATE, + FullInfPath, + 0, + &pdata->RebootRequired)) { + // + // We know that at least one device existed, and was upgraded. + // + pdata->HwInsertedFirst = TRUE; + + } else { + + Err = GetLastError(); + + // + // We failed to update the driver. If we failed simply because + // there were no toasters currently attached to the computer, then + // we still want to install the INF. + // + if(Err == ERROR_NO_SUCH_DEVINST) { + + pdata->HwInsertedFirst = FALSE; + + // + // Since we didn't do any device installs, the INF (and CAT) + // didn't get automatically installed. We'll install them now, + // so that they'll be present when the user subsequently plugs + // their hardware in. + // + if(!SetupCopyOEMInf(FullInfPath, + NULL, + SPOST_PATH, + 0, + NULL, + 0, + NULL, + NULL)) { + // + // Failure to install the INF is more important (worse) than + // the absence of any devices! + // + Err = GetLastError(); + } + + } else { + // + // Apparently there _were_ existing devices--we just failed to + // upgrade their drivers. This might be due to an installation + // problem, or perhaps because the devices already have drivers + // newer than the one we offered. + // + pdata->HwInsertedFirst = TRUE; + } + } + + if((Err == NO_ERROR) || (Err == ERROR_NO_SUCH_DEVINST)) { + // + // Either we successfully upgraded one or more toasters, or there were + // no present toasters but we successfully installed our INF and CAT. + // + // There may exist, however, devices that were once connected to the + // computer, but presently are not. If such devices are connected + // again in the future, we want to ensure they go through device + // installation. We will retrieve the list of non-present devices, and + // mark each as "needs re-install" to kick them back through the "New + // Hardware Found" process if they ever show up again. (Note that this + // doesn't destroy any device-specific settings they may have, so this + // is just forcing an upgrade, not an uninstall/re-install.) + // + // (The HardwareID used is the one defined for the toaster sample, + // BUS_HARDWARE_IDS in src\general\toaster\bus\common.h. We also take + // advantage of the fact that we know these devices will always be + // enumerated under the "{b85b7c50-6a01-11d2-b841-00c04fad5171}" + // enum namespace.) + // + ExistingNonPresentDevices = GetNonPresentDevices(ENUMERATOR_NAME, + HW_ID_TO_UPDATE + ); + + if(ExistingNonPresentDevices != INVALID_HANDLE_VALUE) { + + MarkDevicesAsNeedReinstall(ExistingNonPresentDevices); + + SetupDiDestroyDeviceInfoList(ExistingNonPresentDevices); + } + } + + PostMessage(pdata->hwndDlg, WMX_UPDATE_DRIVER_DONE, 0, 0); + + return Err; +} + + +INT +CALLBACK +WizardCallback( + _In_ HWND hwndDlg, + _In_ UINT uMsg, + _In_ LPARAM lParam + ) + +/*++ + +Routine Description: + + Call back used to remove the "X" and "?" from the wizard page. + +Arguments: + + hwndDlg - Handle to the property sheet dialog box. + + uMsg - Identifies the message being received. This parameter is one of the + following values: + + PSCB_INITIALIZED - Indicates that the property sheet is being + initialized. The lParam value is zero for this + message. + + PSCB_PRECREATE - Indicates that the property sheet is about to be + created. The hwndDlg parameter is NULL and the + lParam parameter is a pointer to a dialog template + in memory. This template is in the form of a + DLGTEMPLATE structure followed by one or more + DLGITEMTEMPLATE structures. + + lParam - Specifies additional information about the message. The + meaning of this value depends on the uMsg parameter. + +Return Value: + + The function returns zero. + +--*/ + +{ + DLGTEMPLATE *pDlgTemplate; + + UNREFERENCED_PARAMETER( hwndDlg ); + + switch(uMsg) { + + case PSCB_PRECREATE: + if(lParam){ + // + // This is done to hide the X and ? at the top of the wizard + // + pDlgTemplate = (DLGTEMPLATE *)lParam; + pDlgTemplate->style &= ~(DS_CONTEXTHELP | WS_SYSMENU); + } + break; + + default: + break; + } + + return 0; +} + diff --git a/general/toaster/umdf2/Package/package.VcxProj b/general/toaster/umdf2/Package/package.VcxProj new file mode 100644 index 00000000..d03cefec --- /dev/null +++ b/general/toaster/umdf2/Package/package.VcxProj @@ -0,0 +1,91 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + WindowsKernelModeDriver10.0 + Utility + Package + true + Debug + + + + {52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5} + {7F090110-1487-4DA8-8CEA-3C1A43F8C119} + $(MSBuildProjectName) + + + Windows10 + true + + + Windows10 + false + + + Windows10 + true + + + Windows10 + false + + + + + + + + + + + DbgengRemoteDebugger + False + None + + + + + + %PathToInf% + False + False + True + + 133563 + + + + + + + + + + {A60F76D2-C512-4DA6-8C80-263F1B506267} + + + {4867F54B-D8EF-45B0-984E-D571D987174A} + + + + + + \ No newline at end of file diff --git a/general/toaster/umdf2/Package/package.VcxProj.Filters b/general/toaster/umdf2/Package/package.VcxProj.Filters new file mode 100644 index 00000000..8153ab30 --- /dev/null +++ b/general/toaster/umdf2/Package/package.VcxProj.Filters @@ -0,0 +1,21 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {1B3C45DA-0194-4257-B308-970F7B2AB7C0} + + + h;hpp;hxx;hm;inl;inc;xsd + {84FCFB7D-58FA-4617-9482-C1BD332BCC31} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {824C0178-16B0-4768-B411-CE9BDD79D690} + + + inf;inv;inx;mof;mc; + {9C6B14B3-304F-491F-B5F1-C14CA36EC7E8} + + + \ No newline at end of file diff --git a/general/toaster/umdf2/ReadMe.md b/general/toaster/umdf2/ReadMe.md new file mode 100644 index 00000000..8bd9dada --- /dev/null +++ b/general/toaster/umdf2/ReadMe.md @@ -0,0 +1,58 @@ +Toaster Sample (UMDF Version 2) +=============================== + +The Toaster (UMDF version 2) sample is an iterative series of samples that demonstrate fundamental aspects of Windows driver development. + +The Toaster sample collection is comprised of driver projects (.vcxproj files) that are contained in the umdf2toaster.sln solution file. + +For related information, see the [Toaster Sample](http://code.msdn.microsoft.com/windowshardware/Toaster-7d256224). + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Related technologies +-------------------- + +[User-Mode Driver Framework](http://msdn.microsoft.com/en-us/library/windows/hardware/ff560456) + +Run the sample +-------------- + +The computer where you install the driver is called the *target computer* or the *test computer*. Typically this is a separate computer from where you develop and build the driver package. The computer where you develop and build the driver is called the *host computer*. + +The process of moving the driver package to the target computer and installing the driver is called *deploying the driver*. You can deploy a driver sample automatically or manually. + +### Automatic deployment (root enumerated) + +Before you automatically deploy a driver, you must provision the target computer. For instructions, see [Configuring a Computer for Driver Deployment, Testing, and Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/). + +1. On the host computer, in Visual Studio, in Solution Explorer, right click **package** (lower case), and choose **Properties**. Navigate to **Configuration Properties \> Driver Install \> Deployment**. +2. Check **Enable deployment**, and check **Remove previous driver versions before deployment**. For **Target Computer Name**, select the name of a target computer that you provisioned previously. Select **Hardware ID Driver Update**, and enter **root\\toaster** for the hardware ID. Click **OK**. +3. Because this solution contains many projects, you may find it easier to remove some of them before you build and deploy a driver package. To do so, right click **package** (lower case), and choose **Properties**. Navigate to **Common Properties-\>References** and click **Remove Reference** to remove projects you don't want. (You can add them back later by using **Add New Reference**.) Click **OK**. +4. On the **Build** menu, choose **Build Solution** or **Rebuild Solution** (if you removed references). +5. If you removed references and deployment does not succeed, try deleting the contents of the c:\\DriverTest\\Drivers folder on the target machine, and then retry deployment. + +### Manual deployment (root enumerated) + +Before you manually deploy a driver, you must turn on test signing and install a certificate on the target computer. You also need to copy the [DevCon](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544707) tool to the target computer. For instructions, see [Preparing a Computer for Manual Driver Deployment](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265571). + +1. Copy all of the files in your driver package to a folder on the target computer (for example, c:\\Umdf2toaster). +2. On the target computer, open a Command Prompt window as Administrator. Navigate to your driver package folder, and enter a command such as: + + **devcon install wdfsimpleum.inf root\\toaster** + +### View the root enumerated driver in Device Manager + +On the target computer, in a Command Prompt window, enter **devmgmt** to open Device Manager. In Device Manager, on the **View** menu, choose **Devices by type**. In the device tree, locate **Sample WDF Toaster Service + Filter** (for example, this might be under the **Toaster** node). + +In Device Manager, on the **View** menu, choose **Devices by connection**. Locate **Sample WDF Toaster Service + Filter** as a child of the root node of the device tree. + +Build the sample using MSBuild +------------------------------ + +As an alternative to building the driver sample in Visual Studio, you can build it in a Visual Studio Command Prompt window. In Visual Studio, on the **Tools** menu, choose **Visual Studio Command Prompt**. In the Visual Studio Command Prompt window, navigate to the folder that has the solution file, Umdf2toaster.sln. Use the MSBuild command to build the solution. Here is an example: + +**msbuild /p:configuration=â€Win8 Release†/p:platform=â€Win32†Umdf2toaster.sln** + +For more information about using MSBuild to build a driver package, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + diff --git a/general/toaster/umdf2/exe/enum/Enum.vcxproj b/general/toaster/umdf2/exe/enum/Enum.vcxproj new file mode 100644 index 00000000..822a36c7 --- /dev/null +++ b/general/toaster/umdf2/exe/enum/Enum.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {6E1593D4-08A1-45E4-A77F-AF34AC6F0D69} + $(MSBuildProjectName) + Debug + Win32 + {2F2E0D4F-031E-4B76-983A-ED7F18562A15} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + Enum + + + Enum + + + Enum + + + Enum + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/umdf2/exe/enum/Enum.vcxproj.Filters b/general/toaster/umdf2/exe/enum/Enum.vcxproj.Filters new file mode 100644 index 00000000..c84e1e9f --- /dev/null +++ b/general/toaster/umdf2/exe/enum/Enum.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {58C64BCB-5021-4790-BBB0-C1AB7A443B79} + + + h;hpp;hxx;hm;inl;inc;xsd + {17570C2B-4A49-4EB7-9BC8-219A1CDD870D} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {B5B13117-98BA-4E65-833F-2DB49A70E1DF} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/toaster/umdf2/exe/enum/enum.c b/general/toaster/umdf2/exe/enum/enum.c new file mode 100644 index 00000000..ac285115 --- /dev/null +++ b/general/toaster/umdf2/exe/enum/enum.c @@ -0,0 +1,317 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Enum.c + +Abstract: + This application simulates the plugin, unplug or ejection + of devices. + +Environment: + + usermode console application + +Revision History: + + Eliyas Yakub Oct 14, 1998 + + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "public.h" +#include + +// +// Prototypes +// + +BOOLEAN +OpenBusInterface ( + _In_ HDEVINFO HardwareDeviceInfo, + _In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData + ); + + + +#define USAGE \ +"Usage: Enum [-p SerialNo] Plugs in a device. SerialNo must be greater than zero.\n\ + [-u SerialNo or 0] Unplugs device(s) - specify 0 to unplug all \ + the devices enumerated so far.\n\ + [-e SerialNo or 0] Ejects device(s) - specify 0 to eject all \ + the devices enumerated so far.\n" + +BOOLEAN bPlugIn, bUnplug, bEject; +ULONG SerialNo; + +INT __cdecl +main( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] + ) +{ + HDEVINFO hardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + + bPlugIn = bUnplug = bEject = FALSE; + + if(argc <3) { + goto usage; + } + + if(argv[1][0] == '-') { + if(tolower(argv[1][1]) == 'p') { + if(argv[2]) + SerialNo = (USHORT)atol(argv[2]); + bPlugIn = TRUE; + } + else if(tolower(argv[1][1]) == 'u') { + if(argv[2]) + SerialNo = (ULONG)atol(argv[2]); + bUnplug = TRUE; + } + else if(tolower(argv[1][1]) == 'e') { + if(argv[2]) + SerialNo = (ULONG)atol(argv[2]); + bEject = TRUE; + } + else { + goto usage; + } + } + else + goto usage; + + if(bPlugIn && 0 == SerialNo) + goto usage; + // + // Open a handle to the device interface information set of all + // present toaster bus enumerator interfaces. + // + + hardwareDeviceInfo = SetupDiGetClassDevs ( + (LPGUID)&GUID_DEVINTERFACE_BUSENUM_TOASTER, + NULL, // Define no enumerator (global) + NULL, // Define no + (DIGCF_PRESENT | // Only Devices present + DIGCF_DEVICEINTERFACE)); // Function class devices. + + if(INVALID_HANDLE_VALUE == hardwareDeviceInfo) + { + printf("SetupDiGetClassDevs failed: %x\n", GetLastError()); + return 0; + } + + deviceInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA); + + if (SetupDiEnumDeviceInterfaces (hardwareDeviceInfo, + 0, // No care about specific PDOs + (LPGUID)&GUID_DEVINTERFACE_BUSENUM_TOASTER, + 0, // + &deviceInterfaceData)) { + + OpenBusInterface(hardwareDeviceInfo, &deviceInterfaceData); + } else if (ERROR_NO_MORE_ITEMS == GetLastError()) { + + printf( + "Error:Interface GUID_DEVINTERFACE_BUSENUM_TOASTER is not registered\n"); + } + + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + return 0; +usage: + printf(USAGE); + exit(0); +} + +BOOLEAN +OpenBusInterface ( + _In_ HDEVINFO HardwareDeviceInfo, + _In_ PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData + ) +{ + HANDLE file; + PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL; + ULONG predictedLength = 0; + ULONG requiredLength = 0; + ULONG bytes; + BUSENUM_UNPLUG_HARDWARE unplug; + BUSENUM_EJECT_HARDWARE eject; + PBUSENUM_PLUGIN_HARDWARE hardware; + BOOLEAN bSuccess; + + // + // Allocate a function class device data structure to receive the + // information about this particular device. + // + + SetupDiGetDeviceInterfaceDetail ( + HardwareDeviceInfo, + DeviceInterfaceData, + NULL, // probing so no output buffer yet + 0, // probing so output buffer length of zero + &requiredLength, + NULL); // not interested in the specific dev-node + + if(ERROR_INSUFFICIENT_BUFFER != GetLastError()) { + printf("Error in SetupDiGetDeviceInterfaceDetail%d\n", + GetLastError()); + return FALSE; + } + + predictedLength = requiredLength; + + deviceInterfaceDetailData = malloc (predictedLength); + + if(deviceInterfaceDetailData) { + deviceInterfaceDetailData->cbSize = + sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA); + } else { + printf("Couldn't allocate %d bytes for device interface details.\n", predictedLength); + return FALSE; + } + + + if (! SetupDiGetDeviceInterfaceDetail ( + HardwareDeviceInfo, + DeviceInterfaceData, + deviceInterfaceDetailData, + predictedLength, + &requiredLength, + NULL)) { + printf("Error in SetupDiGetDeviceInterfaceDetail\n"); + free (deviceInterfaceDetailData); + return FALSE; + } + + printf("Opening %s\n", deviceInterfaceDetailData->DevicePath); + + file = CreateFile ( deviceInterfaceDetailData->DevicePath, + GENERIC_READ, // Only read access + 0, // FILE_SHARE_READ | FILE_SHARE_WRITE + NULL, // no SECURITY_ATTRIBUTES structure + OPEN_EXISTING, // No special create flags + 0, // No special attributes + NULL); // No template file + + if (INVALID_HANDLE_VALUE == file) { + printf("CreateFile failed: 0x%x", GetLastError()); + free (deviceInterfaceDetailData); + return FALSE; + } + + printf("Bus interface opened!!!\n"); + + // + // From this point on, we need to jump to the end of the routine for + // common clean-up. Keep track of whether we succeeded or failed, so + // we'll know what to return to the caller. + // + bSuccess = FALSE; + + // + // Enumerate Devices + // + + if(bPlugIn) { + + printf("SerialNo. of the device to be enumerated: %d\n", SerialNo); + + hardware = malloc (bytes = (sizeof (BUSENUM_PLUGIN_HARDWARE) + + BUS_HARDWARE_IDS_LENGTH)); + + if(hardware) { + hardware->Size = sizeof (BUSENUM_PLUGIN_HARDWARE); + hardware->SerialNo = SerialNo; + } else { + printf("Couldn't allocate %d bytes for busenum plugin hardware structure.\n", bytes); + goto End; + } + + // + // Allocate storage for the Device ID + // + + memcpy (hardware->HardwareIDs, + BUS_HARDWARE_IDS, + BUS_HARDWARE_IDS_LENGTH); + + if (!DeviceIoControl (file, + IOCTL_BUSENUM_PLUGIN_HARDWARE , + hardware, bytes, + NULL, 0, + &bytes, NULL)) { + free (hardware); + printf("PlugIn failed:0x%x\n", GetLastError()); + goto End; + } + + free (hardware); + } + + // + // Removes a device if given the specific Id of the device. Otherwise this + // ioctls removes all the devices that are enumerated so far. + // + + if(bUnplug) { + printf("Unplugging device(s)....\n"); + + unplug.Size = bytes = sizeof (unplug); + unplug.SerialNo = SerialNo; + if (!DeviceIoControl (file, + IOCTL_BUSENUM_UNPLUG_HARDWARE, + &unplug, bytes, + NULL, 0, + &bytes, NULL)) { + printf("Unplug failed: 0x%x\n", GetLastError()); + goto End; + } + } + + // + // Ejects a device if given the specific Id of the device. Otherwise this + // ioctls ejects all the devices that are enumerated so far. + // + + if(bEject) + { + printf("Ejecting Device(s)\n"); + + eject.Size = bytes = sizeof (eject); + eject.SerialNo = SerialNo; + if (!DeviceIoControl (file, + IOCTL_BUSENUM_EJECT_HARDWARE, + &eject, bytes, + NULL, 0, + &bytes, NULL)) { + printf("Eject failed: 0x%x\n", GetLastError()); + goto End; + } + } + + printf("Success!!!\n"); + bSuccess = TRUE; + +End: + CloseHandle(file); + free (deviceInterfaceDetailData); + return bSuccess; +} + + diff --git a/general/toaster/umdf2/exe/notify/notify.c b/general/toaster/umdf2/exe/notify/notify.c new file mode 100644 index 00000000..3362ce3d --- /dev/null +++ b/general/toaster/umdf2/exe/notify/notify.c @@ -0,0 +1,1244 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: notify.c + + +Abstract: + + +Author: + + Eliyas Yakub Nov 23, 1999 + +Environment: + + User mode only. + +Revision History: + + Modified to use linked list for deviceInfo + instead of arrays. (5/12/2000) + +--*/ +#define UNICODE +#define _UNICODE +#define INITGUID + +// +// Annotation to indicate to prefast that this is nondriver user-mode code. +// +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include +#include +#include +#include +#include +#include +#include +#include "public.h" +#include "notify.h" +#include + +BOOL +HandlePowerBroadcast( + HWND hWnd, + WPARAM wParam, + LPARAM lParam); + +// +// Global variables +// +HINSTANCE hInst; +HWND hWndList; +TCHAR szTitle[]=TEXT("Toaster Package Test Application"); +LIST_ENTRY ListHead; +HDEVNOTIFY hInterfaceNotification; +TCHAR OutText[500]; +UINT ListBoxIndex = 0; +GUID InterfaceGuid;// = GUID_DEVINTERFACE_TOASTER; +BOOLEAN Verbose= FALSE; + +_inline BOOLEAN +IsValid( + ULONG No + ) +{ + PLIST_ENTRY thisEntry; + PDEVICE_INFO deviceInfo; + + if(0==(No)) return TRUE; //special case + + for(thisEntry = ListHead.Flink; thisEntry != &ListHead; + thisEntry = thisEntry->Flink) + { + deviceInfo = CONTAINING_RECORD(thisEntry, DEVICE_INFO, ListEntry); + if((No) == deviceInfo->SerialNo) { + return TRUE; + } + } + return FALSE; +} + +VOID +Display( + _In_ LPWSTR pstrFormat, // @parm A printf style format string + ... // @parm | ... | Variable paramters based on

+ ) +{ + HRESULT hr; + va_list va; + + va_start(va, pstrFormat); + // + // Truncation is acceptable. + // + hr = StringCbVPrintf(OutText, sizeof(OutText)-sizeof(WCHAR), pstrFormat, va); + va_end(va); + + if(FAILED(hr)){ + return; + } + + SendMessage(hWndList, LB_INSERTSTRING, ListBoxIndex, (LPARAM)OutText); + SendMessage(hWndList, LB_SETCURSEL, ListBoxIndex, 0); + ListBoxIndex++; + +} + +VOID +DisplayV( + _In_ LPWSTR pstrFormat, // @parm A printf style format string + ... // @parm | ... | Variable paramters based on

+ ) +{ + va_list va; + + if (Verbose) + { + va_start(va, pstrFormat); + Display(pstrFormat, va); + va_end(va); + } +} +int PASCAL +WinMain ( + _In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPSTR lpCmdLine, + _In_ int nShowCmd + ) +{ + static TCHAR szAppName[]=TEXT("Toaster Notify"); + HWND hWnd; + MSG msg; + WNDCLASS wndclass; + + UNREFERENCED_PARAMETER( lpCmdLine ); + + InterfaceGuid = GUID_DEVINTERFACE_TOASTER; + hInst=hInstance; + + if (!hPrevInstance) + { + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION); + wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclass.hbrBackground= GetStockObject(WHITE_BRUSH); + wndclass.lpszMenuName = TEXT("GenericMenu"); + wndclass.lpszClassName= szAppName; + + RegisterClass(&wndclass); + } + + hWnd = CreateWindow (szAppName, + szTitle, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + hInstance, + NULL); + + ShowWindow (hWnd, nShowCmd); + UpdateWindow(hWnd); + + while (GetMessage (&msg, NULL, 0,0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return (0); +} + + +LRESULT +FAR PASCAL +WndProc ( + HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam + ) +{ + DWORD nEventType = (DWORD)wParam; + PDEV_BROADCAST_HDR p = (PDEV_BROADCAST_HDR) lParam; + DEV_BROADCAST_DEVICEINTERFACE filter; + + switch (message) + { + + case WM_COMMAND: + HandleCommands(hWnd, message, wParam, lParam); + return 0; + + case WM_CREATE: + + // + // Load and set the icon of the program + // + SetClassLongPtr(hWnd, GCLP_HICON, + (LONG_PTR)LoadIcon((HINSTANCE)lParam,MAKEINTRESOURCE(IDI_CLASS_ICON))); + + hWndList = CreateWindow (TEXT("listbox"), + NULL, + WS_CHILD|WS_VISIBLE|LBS_NOTIFY | + WS_VSCROLL | WS_BORDER, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + hWnd, + (HMENU)ID_EDIT, + hInst, + NULL); + + filter.dbcc_size = sizeof(filter); + filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + filter.dbcc_classguid = InterfaceGuid; + hInterfaceNotification = RegisterDeviceNotification(hWnd, &filter, 0); + + InitializeListHead(&ListHead); + EnumExistingDevices(hWnd); + + return 0; + + case WM_SIZE: + + MoveWindow(hWndList, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); + return 0; + + case WM_SETFOCUS: + SetFocus(hWndList); + return 0; + + case WM_DEVICECHANGE: + + // + // The DBT_DEVNODES_CHANGED broadcast message is sent + // everytime a device is added or removed. This message + // is typically handled by Device Manager kind of apps, + // which uses it to refresh window whenever something changes. + // The lParam is always NULL in this case. + // + if(DBT_DEVNODES_CHANGED == wParam) { + DisplayV(TEXT("Received DBT_DEVNODES_CHANGED broadcast message")); + return 0; + } + + // + // All the events we're interested in come with lParam pointing to + // a structure headed by a DEV_BROADCAST_HDR. This is denoted by + // bit 15 of wParam being set, and bit 14 being clear. + // + if((wParam & 0xC000) == 0x8000) { + + if (!p) + return 0; + + if (p->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + + HandleDeviceInterfaceChange(hWnd, nEventType, (PDEV_BROADCAST_DEVICEINTERFACE) p); + } else if (p->dbch_devicetype == DBT_DEVTYP_HANDLE) { + + HandleDeviceChange(hWnd, nEventType, (PDEV_BROADCAST_HANDLE) p); + } + } + return 0; + + case WM_POWERBROADCAST: + HandlePowerBroadcast(hWnd, wParam, lParam); + return 0; + + case WM_CLOSE: + Cleanup(hWnd); + UnregisterDeviceNotification(hInterfaceNotification); + return DefWindowProc(hWnd,message, wParam, lParam); + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProc(hWnd,message, wParam, lParam); + } + + +LRESULT +HandleCommands( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) + +{ + PDIALOG_RESULT result = NULL; + + UNREFERENCED_PARAMETER( uMsg ); + UNREFERENCED_PARAMETER( lParam ); + + switch (wParam) { + + case IDM_OPEN: + Cleanup(hWnd); // close all open handles + EnumExistingDevices(hWnd); + break; + + case IDM_CLOSE: + Cleanup(hWnd); + break; + + case IDM_HIDE: + result = (PDIALOG_RESULT)DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DlgProc); + if(result && result->SerialNo && IsValid(result->SerialNo)) { + DWORD bytes; + PDEVICE_INFO deviceInfo = NULL; + PLIST_ENTRY thisEntry; + + // + // Find out the deviceInfo that matches this SerialNo. + // We need the deviceInfo to get the handle to the device. + // + for(thisEntry = ListHead.Flink; thisEntry != &ListHead; + thisEntry = thisEntry->Flink) + { + deviceInfo = CONTAINING_RECORD(thisEntry, DEVICE_INFO, ListEntry); + if(result->SerialNo == deviceInfo->SerialNo) { + break; + } + deviceInfo = NULL; + } + + // + // If found send I/O control + // + + if (deviceInfo && !DeviceIoControl (deviceInfo->hDevice, + IOCTL_TOASTER_DONT_DISPLAY_IN_UI_DEVICE, + NULL, 0, + NULL, 0, + &bytes, NULL)) { + MessageBox(hWnd, TEXT("Request Failed or Invalid Serial No"), + TEXT("Error"), MB_OK); + } + } + break; + case IDM_PLUGIN: + + result = (PDIALOG_RESULT)DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG), hWnd, DlgProc); + if(result) { + if(!result->SerialNo || !OpenBusInterface(result->SerialNo, result->DeviceId, PLUGIN)){ + MessageBox(hWnd, TEXT("Invalid Serial Number or OpenBusInterface Failed"), TEXT("Error"), MB_OK); + } + } + break; + case IDM_UNPLUG: + result = (PDIALOG_RESULT)DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DlgProc); + + if(result && IsValid(result->SerialNo)) { + if(!OpenBusInterface(result->SerialNo, NULL, UNPLUG)) { + MessageBox(hWnd, TEXT("Invalid Serial Number or OpenBusInterface Failed"), TEXT("Error"), MB_OK); + } + } + break; + case IDM_EJECT: + result = (PDIALOG_RESULT)DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DlgProc); + if(result && IsValid(result->SerialNo)) { + if(!OpenBusInterface(result->SerialNo, NULL, EJECT)) { + MessageBox(hWnd, TEXT("Invalid Serial Number or OpenBusInterface Failed"), TEXT("Error"), MB_OK); + } + } + break; + + case IDM_CLEAR: + SendMessage(hWndList, LB_RESETCONTENT, 0, 0); + ListBoxIndex = 0; + break; + + case IDM_IOCTL: + SendIoctlToFilterDevice(); + break; + + case IDM_VERBOSE: { + + HMENU hMenu = GetMenu(hWnd); + Verbose = !Verbose; + if(Verbose) { + CheckMenuItem(hMenu, (UINT)wParam, MF_CHECKED); + } else { + CheckMenuItem(hMenu, (UINT)wParam, MF_UNCHECKED); + } + } + break; + + case IDM_EXIT: + PostQuitMessage(0); + break; + + default: + break; + } + + if(result) { + HeapFree (GetProcessHeap(), 0, result); + } + return TRUE; +} + +INT_PTR CALLBACK +DlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam +) +{ + BOOL success; + PDIALOG_RESULT dialogResult = NULL; + + UNREFERENCED_PARAMETER( lParam ); + + switch(message) + { + case WM_INITDIALOG: + SetDlgItemText(hDlg, IDC_DEVICEID, BUS_HARDWARE_IDS); + return TRUE; + + case WM_COMMAND: + switch( wParam) + { + case ID_OK: + dialogResult = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + (sizeof(DIALOG_RESULT) + MAX_PATH * sizeof(WCHAR))); + if(dialogResult) { + dialogResult->DeviceId = (PWCHAR)((PCHAR)dialogResult + sizeof(DIALOG_RESULT)); + dialogResult->SerialNo = GetDlgItemInt(hDlg,IDC_SERIALNO, &success, FALSE ); + GetDlgItemText(hDlg, IDC_DEVICEID, dialogResult->DeviceId, MAX_PATH-1 ); + } + EndDialog(hDlg, (UINT_PTR)dialogResult); + return TRUE; + case ID_CANCEL: + EndDialog(hDlg, 0); + return TRUE; + + } + break; + + } + return FALSE; +} + + +BOOL +HandleDeviceInterfaceChange( + HWND hWnd, + DWORD evtype, + PDEV_BROADCAST_DEVICEINTERFACE dip + ) +{ + DEV_BROADCAST_HANDLE filter; + PDEVICE_INFO deviceInfo = NULL; + HRESULT hr; + + switch (evtype) + { + case DBT_DEVICEARRIVAL: + // + // New device arrived. Open handle to the device + // and register notification of type DBT_DEVTYP_HANDLE + // + + deviceInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEVICE_INFO)); + if(!deviceInfo) + return FALSE; + + InitializeListHead(&deviceInfo->ListEntry); + InsertTailList(&ListHead, &deviceInfo->ListEntry); + + + if(!GetDeviceDescription(dip->dbcc_name, + (PBYTE)deviceInfo->DeviceName, + sizeof(deviceInfo->DeviceName), + &deviceInfo->SerialNo)) { + MessageBox(hWnd, TEXT("GetDeviceDescription failed"), TEXT("Error!"), MB_OK); + } + + Display(TEXT("New device Arrived (Interface Change Notification): %ws"), + deviceInfo->DeviceName); + + hr = StringCchCopy(deviceInfo->DevicePath, MAX_PATH, dip->dbcc_name); + if(FAILED(hr)){ + // DeviceInfo will be freed later by the cleanup routine. + break; + } + + deviceInfo->hDevice = CreateFile(dip->dbcc_name, + GENERIC_READ |GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL); + if(deviceInfo->hDevice == INVALID_HANDLE_VALUE) { + Display(TEXT("Failed to open the device: %ws"), deviceInfo->DeviceName); + break; + } + + Display(TEXT("Opened handled to the device: %ws"), deviceInfo->DeviceName); + memset (&filter, 0, sizeof(filter)); //zero the structure + filter.dbch_size = sizeof(filter); + filter.dbch_devicetype = DBT_DEVTYP_HANDLE; + filter.dbch_handle = deviceInfo->hDevice; + + deviceInfo->hHandleNotification = + RegisterDeviceNotification(hWnd, &filter, 0); + break; + + case DBT_DEVICEREMOVECOMPLETE: + Display(TEXT("Remove Complete (Interface Change Notification)")); + break; + + // + // Device Removed. + // + + default: + Display(TEXT("Unknown (Interface Change Notification)")); + break; + } + return TRUE; +} + +BOOL +HandleDeviceChange( + HWND hWnd, + DWORD evtype, + PDEV_BROADCAST_HANDLE dhp + ) +{ + DEV_BROADCAST_HANDLE filter; + PDEVICE_INFO deviceInfo = NULL; + PLIST_ENTRY thisEntry; + + // + // Walk the list to get the deviceInfo for this device + // by matching the handle given in the notification. + // + for(thisEntry = ListHead.Flink; thisEntry != &ListHead; + thisEntry = thisEntry->Flink) + { + deviceInfo = CONTAINING_RECORD(thisEntry, DEVICE_INFO, ListEntry); + if(dhp->dbch_hdevnotify == deviceInfo->hHandleNotification) { + break; + } + deviceInfo = NULL; + } + + if(!deviceInfo) { + Display(TEXT("Error: spurious message, Event Type %x, Device Type %x"), + evtype, dhp->dbch_devicetype); + return FALSE; + } + + switch (evtype) + { + + case DBT_DEVICEQUERYREMOVE: + + Display(TEXT("Query Remove (Handle Notification)"), deviceInfo->DeviceName); + + // User is trying to disable, uninstall, or eject our device. + // Close the handle to the device so that the target device can + // get removed. Do not unregister the notification + // at this point, because we want to know whether + // the device is successfully removed or not. + // + if (deviceInfo->hDevice != INVALID_HANDLE_VALUE) { + + CloseHandle(deviceInfo->hDevice); + deviceInfo->hDevice = INVALID_HANDLE_VALUE; + Display(TEXT("Closed handle to device %ws"), deviceInfo->DeviceName ); + } + break; + + case DBT_DEVICEREMOVECOMPLETE: + + Display(TEXT("Remove Complete (Handle Notification):%ws"), + deviceInfo->DeviceName); + // + // Device is getting surprise removed. So close + // the handle to device and unregister the PNP notification. + // + + if (deviceInfo->hHandleNotification) { + UnregisterDeviceNotification(deviceInfo->hHandleNotification); + deviceInfo->hHandleNotification = NULL; + } + if (deviceInfo->hDevice != INVALID_HANDLE_VALUE) { + + CloseHandle(deviceInfo->hDevice); + deviceInfo->hDevice = INVALID_HANDLE_VALUE; + Display(TEXT("Closed handle to device %ws"), deviceInfo->DeviceName ); + } + // + // Unlink this deviceInfo from the list and free the memory + // + RemoveEntryList(&deviceInfo->ListEntry); + HeapFree (GetProcessHeap(), 0, deviceInfo); + + break; + + case DBT_DEVICEREMOVEPENDING: + + Display(TEXT("Remove Pending (Handle Notification):%ws"), + deviceInfo->DeviceName); + // + // Device is successfully removed so unregister the notification + // and free the memory. + // + if (deviceInfo->hHandleNotification) { + UnregisterDeviceNotification(deviceInfo->hHandleNotification); + deviceInfo->hHandleNotification = NULL; + deviceInfo->hDevice = INVALID_HANDLE_VALUE; + } + // + // Unlink this deviceInfo from the list and free the memory + // + RemoveEntryList(&deviceInfo->ListEntry); + HeapFree (GetProcessHeap(), 0, deviceInfo); + + break; + + case DBT_DEVICEQUERYREMOVEFAILED : + Display(TEXT("Remove failed (Handle Notification):%ws"), + deviceInfo->DeviceName); + // + // Remove failed. So reopen the device and register for + // notification on the new handle. But first we should unregister + // the previous notification. + // + if (deviceInfo->hHandleNotification) { + UnregisterDeviceNotification(deviceInfo->hHandleNotification); + deviceInfo->hHandleNotification = NULL; + } + deviceInfo->hDevice = CreateFile(deviceInfo->DevicePath, + GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + if(deviceInfo->hDevice == INVALID_HANDLE_VALUE) { + Display(TEXT("Failed to reopen the device: %ws"), + deviceInfo->DeviceName); + HeapFree (GetProcessHeap(), 0, deviceInfo); + break; + } + + // + // Register handle based notification to receive pnp + // device change notification on the handle. + // + memset (&filter, 0, sizeof(filter)); //zero the structure + filter.dbch_size = sizeof(filter); + filter.dbch_devicetype = DBT_DEVTYP_HANDLE; + filter.dbch_handle = deviceInfo->hDevice; + + deviceInfo->hHandleNotification = + RegisterDeviceNotification(hWnd, &filter, 0); + Display(TEXT("Reopened device %ws"), deviceInfo->DeviceName); + break; + + default: + Display(TEXT("Unknown (Handle Notification)"), deviceInfo->DeviceName); + break; + + } + return TRUE; +} + + +BOOLEAN +EnumExistingDevices( + HWND hWnd +) +{ + HDEVINFO hardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL; + ULONG predictedLength = 0; + ULONG requiredLength = 0; + DWORD error; + DEV_BROADCAST_HANDLE filter; + PDEVICE_INFO deviceInfo =NULL; + UINT i=0; + HRESULT hr; + + hardwareDeviceInfo = SetupDiGetClassDevs ( + (LPGUID)&InterfaceGuid, + NULL, // Define no enumerator (global) + NULL, // Define no + (DIGCF_PRESENT | // Only Devices present + DIGCF_DEVICEINTERFACE)); // Function class devices. + if(INVALID_HANDLE_VALUE == hardwareDeviceInfo) + { + goto Error; + } + + // + // Enumerate devices of toaster class + // + deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); + + for(i=0; SetupDiEnumDeviceInterfaces (hardwareDeviceInfo, + 0, // No care about specific PDOs + (LPGUID)&InterfaceGuid, + i, // + &deviceInterfaceData); i++ ) { + + // + // Allocate a function class device data structure to + // receive the information about this particular device. + // + + // + // First find out required length of the buffer + // + if(deviceInterfaceDetailData) + { + HeapFree (GetProcessHeap(), 0, deviceInterfaceDetailData); + deviceInterfaceDetailData = NULL; + } + + if(!SetupDiGetDeviceInterfaceDetail ( + hardwareDeviceInfo, + &deviceInterfaceData, + NULL, // probing so no output buffer yet + 0, // probing so output buffer length of zero + &requiredLength, + NULL) && (error = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) + { + goto Error; + } + predictedLength = requiredLength; + + deviceInterfaceDetailData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + predictedLength); + if (deviceInterfaceDetailData == NULL) { + goto Error; + } + deviceInterfaceDetailData->cbSize = + sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA); + + + if (! SetupDiGetDeviceInterfaceDetail ( + hardwareDeviceInfo, + &deviceInterfaceData, + deviceInterfaceDetailData, + predictedLength, + &requiredLength, + NULL)) { + goto Error; + } + + deviceInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(DEVICE_INFO)); + if (deviceInfo == NULL) { + goto Error; + } + + InitializeListHead(&deviceInfo->ListEntry); + InsertTailList(&ListHead, &deviceInfo->ListEntry); + + // + // Get the device details such as friendly name and SerialNo + // + if(!GetDeviceDescription(deviceInterfaceDetailData->DevicePath, + (PBYTE)deviceInfo->DeviceName, + sizeof(deviceInfo->DeviceName), + &deviceInfo->SerialNo)){ + goto Error; + } + + Display(TEXT("Found device %ws"), deviceInfo->DeviceName ); + + hr = StringCchCopy(deviceInfo->DevicePath, MAX_PATH, deviceInterfaceDetailData->DevicePath); + if(FAILED(hr)){ + goto Error; + } + // + // Open an handle to the device. + // + deviceInfo->hDevice = CreateFile ( + deviceInterfaceDetailData->DevicePath, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, // no SECURITY_ATTRIBUTES structure + OPEN_EXISTING, // No special create flags + 0, // No special attributes + NULL); + + if (INVALID_HANDLE_VALUE == deviceInfo->hDevice) { + Display(TEXT("Failed to open the device: %ws"), deviceInfo->DeviceName); + continue; + } + + Display(TEXT("Opened handled to the device: %ws"), deviceInfo->DeviceName); + // + // Register handle based notification to receive pnp + // device change notification on the handle. + // + + memset (&filter, 0, sizeof(filter)); //zero the structure + filter.dbch_size = sizeof(filter); + filter.dbch_devicetype = DBT_DEVTYP_HANDLE; + filter.dbch_handle = deviceInfo->hDevice; + + deviceInfo->hHandleNotification = RegisterDeviceNotification(hWnd, &filter, 0); + + } + + if(deviceInterfaceDetailData) + HeapFree (GetProcessHeap(), 0, deviceInterfaceDetailData); + + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + return 0; + +Error: + + MessageBox(hWnd, TEXT("EnumExisting Devices failed"), TEXT("Error!"), MB_OK); + if(deviceInterfaceDetailData) + HeapFree (GetProcessHeap(), 0, deviceInterfaceDetailData); + + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + Cleanup(hWnd); + return 0; +} + +BOOLEAN Cleanup(HWND hWnd) +{ + PDEVICE_INFO deviceInfo =NULL; + PLIST_ENTRY thisEntry; + + UNREFERENCED_PARAMETER( hWnd ); + + while (!IsListEmpty(&ListHead)) { + thisEntry = RemoveHeadList(&ListHead); + deviceInfo = CONTAINING_RECORD(thisEntry, DEVICE_INFO, ListEntry); + if (deviceInfo->hHandleNotification) { + UnregisterDeviceNotification(deviceInfo->hHandleNotification); + deviceInfo->hHandleNotification = NULL; + } + if (deviceInfo->hDevice != INVALID_HANDLE_VALUE && + deviceInfo->hDevice != NULL) { + CloseHandle(deviceInfo->hDevice); + deviceInfo->hDevice = INVALID_HANDLE_VALUE; + Display(TEXT("Closed handle to device %ws"), deviceInfo->DeviceName ); + } + HeapFree (GetProcessHeap(), 0, deviceInfo); + } + return TRUE; +} + + +BOOL +GetDeviceDescription( + _In_ LPTSTR DevPath, + _Out_writes_bytes_(OutBufferLen) PBYTE OutBuffer, + _In_ ULONG OutBufferLen, + _In_ PULONG SerialNo +) +{ + HDEVINFO hardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + SP_DEVINFO_DATA deviceInfoData; + DWORD dwRegType, error; + + hardwareDeviceInfo = SetupDiCreateDeviceInfoList(NULL, NULL); + if(INVALID_HANDLE_VALUE == hardwareDeviceInfo) + { + goto Error; + } + + // + // Enumerate devices of toaster class + // + deviceInterfaceData.cbSize = sizeof(deviceInterfaceData); + + SetupDiOpenDeviceInterface (hardwareDeviceInfo, DevPath, + 0, // + &deviceInterfaceData); + + deviceInfoData.cbSize = sizeof(deviceInfoData); + if(!SetupDiGetDeviceInterfaceDetail ( + hardwareDeviceInfo, + &deviceInterfaceData, + NULL, // probing so no output buffer yet + 0, // probing so output buffer length of zero + NULL, + &deviceInfoData) && (error = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) + { + goto Error; + } + // + // Get the friendly name for this instance, if that fails + // try to get the device description. + // + + if(!SetupDiGetDeviceRegistryProperty(hardwareDeviceInfo, &deviceInfoData, + SPDRP_FRIENDLYNAME, + &dwRegType, + OutBuffer, + OutBufferLen, + NULL)) + { + if(!SetupDiGetDeviceRegistryProperty(hardwareDeviceInfo, &deviceInfoData, + SPDRP_DEVICEDESC, + &dwRegType, + OutBuffer, + OutBufferLen, + NULL)){ + goto Error; + + } + + + } + + // + // Get the serial number of the device. The bus driver reports + // the device serial number as UINumber in the devcaps. + // + if(!SetupDiGetDeviceRegistryProperty(hardwareDeviceInfo, + &deviceInfoData, + SPDRP_UI_NUMBER, + &dwRegType, + (BYTE*) SerialNo, + sizeof(ULONG), + NULL)) { + Display(TEXT("SerialNo is not available for device: %ws"), OutBuffer ); + } + + + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + return TRUE; + +Error: + + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + return FALSE; +} + + + +BOOLEAN +OpenBusInterface ( + _In_ ULONG SerialNo, + _When_ (Action == PLUGIN, _In_) LPWSTR DeviceId, + _In_ USER_ACTION_TYPE Action + ) +{ + HANDLE hDevice=INVALID_HANDLE_VALUE; + PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL; + ULONG predictedLength = 0; + ULONG requiredLength = 0; + ULONG bytes; + BUSENUM_UNPLUG_HARDWARE unplug; + BUSENUM_EJECT_HARDWARE eject; + PBUSENUM_PLUGIN_HARDWARE hardware; + HDEVINFO hardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + BOOLEAN status = FALSE; + HRESULT hr; + // + // Open a handle to the device interface information set of all + // present toaster bus enumerator interfaces. + // + + hardwareDeviceInfo = SetupDiGetClassDevs ( + (LPGUID)&GUID_DEVINTERFACE_BUSENUM_TOASTER, + NULL, // Define no enumerator (global) + NULL, // Define no + (DIGCF_PRESENT | // Only Devices present + DIGCF_DEVICEINTERFACE)); // Function class devices. + + if(INVALID_HANDLE_VALUE == hardwareDeviceInfo) + { + return FALSE; + } + + deviceInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA); + + if (!SetupDiEnumDeviceInterfaces (hardwareDeviceInfo, + 0, // No care about specific PDOs + (LPGUID)&GUID_DEVINTERFACE_BUSENUM_TOASTER, + 0, // + &deviceInterfaceData)) { + goto Clean0; + } + + // + // Allocate a function class device data structure to receive the + // information about this particular device. + // + + SetupDiGetDeviceInterfaceDetail ( + hardwareDeviceInfo, + &deviceInterfaceData, + NULL, // probing so no output buffer yet + 0, // probing so output buffer length of zero + &requiredLength, + NULL);//not interested in the specific dev-node + + if(ERROR_INSUFFICIENT_BUFFER != GetLastError()) { + goto Clean0; + } + + + predictedLength = requiredLength; + + deviceInterfaceDetailData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + predictedLength); + + if(deviceInterfaceDetailData) { + deviceInterfaceDetailData->cbSize = + sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA); + } else { + goto Clean0; + } + + + if (! SetupDiGetDeviceInterfaceDetail ( + hardwareDeviceInfo, + &deviceInterfaceData, + deviceInterfaceDetailData, + predictedLength, + &requiredLength, + NULL)) { + goto Clean1; + } + + + hDevice = CreateFile ( deviceInterfaceDetailData->DevicePath, + GENERIC_READ, // Only read access + 0, // FILE_SHARE_READ | FILE_SHARE_WRITE + NULL, // no SECURITY_ATTRIBUTES structure + OPEN_EXISTING, // No special create flags + 0, // No special attributes + NULL); // No template file + + if (INVALID_HANDLE_VALUE == hDevice) { + goto Clean1; + } + + // + // Enumerate Devices + // + + if(Action == PLUGIN) { + int length = (int) (wcslen(DeviceId)+2)*sizeof(WCHAR); //in bytes + + bytes = sizeof (BUSENUM_PLUGIN_HARDWARE) + length; + hardware = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bytes); + + if(hardware) { + memset(hardware, 0, bytes); + hardware->Size = sizeof (BUSENUM_PLUGIN_HARDWARE); + hardware->SerialNo = SerialNo; + } else { + goto Clean2; + } + + // + // copy the Device ID + // + hr = StringCchCopy(hardware->HardwareIDs, length/sizeof(WCHAR), DeviceId); + if (SUCCEEDED(hr) && DeviceIoControl (hDevice, + IOCTL_BUSENUM_PLUGIN_HARDWARE , + hardware, bytes, + NULL, 0, + &bytes, NULL)) { + status = TRUE; + } + + HeapFree (GetProcessHeap(), 0, hardware); + + } + + // + // Removes a device if given the specific Id of the device. Otherwise this + // ioctls removes all the devices that are enumerated so far. + // + + if(Action == UNPLUG) { + + unplug.Size = bytes = sizeof (unplug); + unplug.SerialNo = SerialNo; + if (DeviceIoControl (hDevice, + IOCTL_BUSENUM_UNPLUG_HARDWARE, + &unplug, bytes, + NULL, 0, + &bytes, NULL)) { + status = TRUE; + } + } + + // + // Ejects a device if given the specific Id of the device. Otherwise this + // ioctls ejects all the devices that are enumerated so far. + // + + if(Action == EJECT) + { + + eject.Size = bytes = sizeof (eject); + eject.SerialNo = SerialNo; + if (DeviceIoControl (hDevice, + IOCTL_BUSENUM_EJECT_HARDWARE, + &eject, bytes, + NULL, 0, + &bytes, NULL)) { + status = TRUE; + } + } + +Clean2: + CloseHandle(hDevice); +Clean1: + HeapFree (GetProcessHeap(), 0, deviceInterfaceDetailData); +Clean0: + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + return status; +} + +BOOL +HandlePowerBroadcast( + HWND hWnd, + WPARAM wParam, + LPARAM lParam) +{ + BOOL fRet = TRUE; + + UNREFERENCED_PARAMETER( hWnd ); + UNREFERENCED_PARAMETER( lParam ); + + switch (wParam) + { + case PBT_APMQUERYSTANDBY: + DisplayV(TEXT("PBT_APMQUERYSTANDBY")); + break; + case PBT_APMQUERYSUSPEND: + DisplayV(TEXT("PBT_APMQUERYSUSPEND")); + break; + case PBT_APMSTANDBY : + DisplayV(TEXT("PBT_APMSTANDBY")); + break; + case PBT_APMSUSPEND : + DisplayV(TEXT("PBT_APMSUSPEND")); + break; + case PBT_APMQUERYSTANDBYFAILED: + DisplayV(TEXT("PBT_APMQUERYSTANDBYFAILED")); + break; + case PBT_APMRESUMESTANDBY: + DisplayV(TEXT("PBT_APMRESUMESTANDBY")); + break; + case PBT_APMQUERYSUSPENDFAILED: + DisplayV(TEXT("PBT_APMQUERYSUSPENDFAILED")); + break; + case PBT_APMRESUMESUSPEND: + DisplayV(TEXT("PBT_APMRESUMESUSPEND")); + break; + case PBT_APMBATTERYLOW: + DisplayV(TEXT("PBT_APMBATTERYLOW")); + break; + case PBT_APMOEMEVENT: + DisplayV(TEXT("PBT_APMOEMEVENT")); + break; + case PBT_APMRESUMEAUTOMATIC: + DisplayV(TEXT("PBT_APMRESUMEAUTOMATIC")); + break; + case PBT_APMRESUMECRITICAL: + DisplayV(TEXT("PBT_APMRESUMECRITICAL")); + break; + case PBT_APMPOWERSTATUSCHANGE: + DisplayV(TEXT("PBT_APMPOWERSTATUSCHANGE")); + break; + default: + DisplayV(TEXT("Default")); + break; + } + return fRet; +} + +void +SendIoctlToFilterDevice() +{ +#define IOCTL_CUSTOM_CODE CTL_CODE(FILE_DEVICE_UNKNOWN, 0, METHOD_BUFFERED, FILE_READ_DATA) + + HANDLE hControlDevice; + ULONG bytes; + + // + // Open handle to the control device. Please note that even + // a non-admin user can open handle to the device with + // FILE_READ_ATTRIBUTES | SYNCHRONIZE DesiredAccess and send IOCTLs if the + // IOCTL is defined with FILE_ANY_ACCESS. So for better security avoid + // specifying FILE_ANY_ACCESS in your IOCTL defintions. + // If the IOCTL is defined to have FILE_READ_DATA access rights, you can + // open the device with GENERIC_READ and call DeviceIoControl. + // If the IOCTL is defined to have FILE_WRITE_DATA access rights, you can + // open the device with GENERIC_WRITE and call DeviceIoControl. + // + hControlDevice = CreateFile ( TEXT("\\\\.\\ToasterFilter"), + GENERIC_READ, // Only read access + 0, // FILE_SHARE_READ | FILE_SHARE_WRITE + NULL, // no SECURITY_ATTRIBUTES structure + OPEN_EXISTING, // No special create flags + 0, // No special attributes + NULL); // No template file + + if (INVALID_HANDLE_VALUE == hControlDevice) { + Display(TEXT("Failed to open ToasterFilter device")); + } else { + if (!DeviceIoControl (hControlDevice, + IOCTL_CUSTOM_CODE, + NULL, 0, + NULL, 0, + &bytes, NULL)) { + Display(TEXT("Ioctl to ToasterFilter device failed")); + } else { + Display(TEXT("Ioctl to ToasterFilter device succeeded")); + } + CloseHandle(hControlDevice); + } + return; +} diff --git a/general/toaster/umdf2/exe/notify/notify.h b/general/toaster/umdf2/exe/notify/notify.h new file mode 100644 index 00000000..98db2918 --- /dev/null +++ b/general/toaster/umdf2/exe/notify/notify.h @@ -0,0 +1,182 @@ +/*++ +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + notify.h + +Abstract: + + +Author: + + Eliyas Yakub Nov 23, 1999 + +Environment: + + +Revision History: + + +--*/ + +#ifndef __NOTIFY_H +#define __NOTIFY_H + + +// +// Copied Macros from ntddk.h +// + +#define CONTAINING_RECORD(address, type, field) ((type *)( \ + (PCHAR)(address) - \ + (ULONG_PTR)(&((type *)0)->field))) + + +#define InitializeListHead(ListHead) (\ + (ListHead)->Flink = (ListHead)->Blink = (ListHead)) + +#define RemoveHeadList(ListHead) \ + (ListHead)->Flink;\ + {RemoveEntryList((ListHead)->Flink)} + +#define IsListEmpty(ListHead) \ + ((ListHead)->Flink == (ListHead)) + + +#define RemoveEntryList(Entry) {\ + PLIST_ENTRY _EX_Blink;\ + PLIST_ENTRY _EX_Flink;\ + _EX_Flink = (Entry)->Flink;\ + _EX_Blink = (Entry)->Blink;\ + _EX_Blink->Flink = _EX_Flink;\ + _EX_Flink->Blink = _EX_Blink;\ + } + +#define InsertTailList(ListHead,Entry) {\ + PLIST_ENTRY _EX_Blink;\ + PLIST_ENTRY _EX_ListHead;\ + _EX_ListHead = (ListHead);\ + _EX_Blink = _EX_ListHead->Blink;\ + (Entry)->Flink = _EX_ListHead;\ + (Entry)->Blink = _EX_Blink;\ + _EX_Blink->Flink = (Entry);\ + _EX_ListHead->Blink = (Entry);\ + } + +typedef struct _DEVICE_INFO +{ + HANDLE hDevice; // file handle + HDEVNOTIFY hHandleNotification; // notification handle + TCHAR DeviceName[MAX_PATH];// friendly name of device description + TCHAR DevicePath[MAX_PATH];// + ULONG SerialNo; // Serial number of the device. + LIST_ENTRY ListEntry; +} DEVICE_INFO, *PDEVICE_INFO; + + +typedef enum { + + PLUGIN = 1, + UNPLUG, + EJECT + +} USER_ACTION_TYPE; + +typedef struct _DIALOG_RESULT +{ + ULONG SerialNo; + PWCHAR DeviceId; +} DIALOG_RESULT, *PDIALOG_RESULT; + +#define ID_EDIT 1 + +#define IDM_OPEN 100 +#define IDM_CLOSE 101 +#define IDM_EXIT 102 +#define IDM_HIDE 103 +#define IDM_PLUGIN 104 +#define IDM_UNPLUG 105 +#define IDM_EJECT 106 +#define IDM_ENABLE 107 +#define IDM_DISABLE 108 +#define IDM_CLEAR 109 +#define IDM_IOCTL 110 +#define IDM_VERBOSE 111 + +#define IDD_DIALOG 115 +#define IDD_DIALOG1 116 +#define IDD_DIALOG2 117 +#define ID_OK 118 +#define ID_CANCEL 119 +#define IDC_SERIALNO 1000 +#define IDC_DEVICEID 1001 +#define IDC_STATIC -1 + +#define IDI_CLASS_ICON 200 + +LRESULT FAR PASCAL +WndProc ( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam + ); + +BOOLEAN EnumExistingDevices( + HWND hWnd + ); + +BOOL HandleDeviceInterfaceChange( + HWND hwnd, + DWORD evtype, + PDEV_BROADCAST_DEVICEINTERFACE dip + ); + +BOOL HandleDeviceChange( + HWND hwnd, + DWORD evtype, + PDEV_BROADCAST_HANDLE dhp + ); + +LRESULT +HandleCommands( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ); + +BOOLEAN Cleanup( + HWND hWnd + ); + +BOOL +GetDeviceDescription( + _In_ LPTSTR DevPath, + _Out_writes_bytes_(OutBufferLen) PBYTE OutBuffer, + _In_ ULONG OutBufferLen, + _In_ PULONG SerialNo + ); + +BOOLEAN +OpenBusInterface ( + _In_ ULONG SerialNo, + _When_ (Action == PLUGIN, _In_) LPWSTR DeviceId, + _In_ USER_ACTION_TYPE Action + ); + + +INT_PTR CALLBACK +DlgProc( + HWND hDlg, + UINT message, + WPARAM wParam, + LPARAM lParam); + +void +SendIoctlToFilterDevice(); + + +#endif + diff --git a/general/toaster/umdf2/exe/notify/notify.rc b/general/toaster/umdf2/exe/notify/notify.rc new file mode 100644 index 00000000..eb15657f --- /dev/null +++ b/general/toaster/umdf2/exe/notify/notify.rc @@ -0,0 +1,77 @@ +#include "windows.h" + +#include "notify.h" + + +GenericMenu MENU + { + POPUP "&File" + { + MENUITEM "Clear &Display", IDM_CLEAR + MENUITEM "&Verbose Trace", IDM_VERBOSE + MENUITEM "E&xit", IDM_EXIT + } + POPUP "&Bus" + { + MENUITEM "&PlugIn", IDM_PLUGIN + MENUITEM "&UnPlug (Surprise Removal)", IDM_UNPLUG + MENUITEM "&Eject", IDM_EJECT + } + POPUP "&Function" + { + MENUITEM "&Open", IDM_OPEN + MENUITEM "&Close", IDM_CLOSE + MENUITEM "&Hide", IDM_HIDE + + } + POPUP "Fil&ter" + { + MENUITEM "&Ioctl to Control Device", IDM_IOCTL + } + } + + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog for plug in +// + +IDD_DIALOG DIALOG DISCARDABLE 0, 0, 289, 86 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Plug In Device" +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "OK",ID_OK,72,61,50,14,BS_NOTIFY + PUSHBUTTON "CANCEL",ID_CANCEL,170,60,50,14,BS_NOTIFY + LTEXT "Serial Number :",IDC_STATIC,18,13,55,8 + LTEXT "Device ID :",IDC_STATIC,20,35,55,8 + EDITTEXT IDC_SERIALNO,75,11,24,14,ES_NUMBER + EDITTEXT IDC_DEVICEID,76,32,200,14,ES_AUTOHSCROLL +END + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog for unplug/hide/enable/disable +// + +IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 232, 86 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Enter SerialNo of the device" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Serial Number :",IDC_STATIC,18,13,55,8 + EDITTEXT IDC_SERIALNO,75,11,24,14,ES_NUMBER + DEFPUSHBUTTON "OK",ID_OK,27,61,50,14,BS_NOTIFY + PUSHBUTTON "CANCEL",ID_CANCEL,121,60,50,14,BS_NOTIFY +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +IDI_CLASS_ICON ICON DISCARDABLE "TOASTER.ICO" + + diff --git a/general/toaster/umdf2/exe/notify/notify.vcxproj b/general/toaster/umdf2/exe/notify/notify.vcxproj new file mode 100644 index 00000000..9d65f2f7 --- /dev/null +++ b/general/toaster/umdf2/exe/notify/notify.vcxproj @@ -0,0 +1,172 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76} + $(MSBuildProjectName) + Debug + Win32 + {83E3E107-0EC1-44A8-BA29-BFBB6C43DC53} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + notify + + + notify + + + notify + + + notify + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/umdf2/exe/notify/notify.vcxproj.Filters b/general/toaster/umdf2/exe/notify/notify.vcxproj.Filters new file mode 100644 index 00000000..ccccf66b --- /dev/null +++ b/general/toaster/umdf2/exe/notify/notify.vcxproj.Filters @@ -0,0 +1,27 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {26C90330-C836-4747-995F-76F38B79BDA9} + + + h;hpp;hxx;hm;inl;inc;xsd + {04722C15-5D21-4294-B1A9-004CFAF38D17} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {600866EB-F078-4625-8B46-D93F60CD2380} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/umdf2/exe/notify/toaster.ico b/general/toaster/umdf2/exe/notify/toaster.ico new file mode 100644 index 00000000..77ad2081 Binary files /dev/null and b/general/toaster/umdf2/exe/notify/toaster.ico differ diff --git a/general/toaster/umdf2/exe/toast/toast.c b/general/toaster/umdf2/exe/toast/toast.c new file mode 100644 index 00000000..4f0525c6 --- /dev/null +++ b/general/toaster/umdf2/exe/toast/toast.c @@ -0,0 +1,343 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + TOAST.C + +Abstract: + + Lists all the toaster device and interfaces present in the system + and opens the last interface to send an invalidate device Ioctl request + or read requests. + +Environment: + + usermode console application + +Revision History: + + Eliyas Yakub Nov 2, 1998 + + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "public.h" +#include +#include + +#define USAGE \ +"Usage: Toast <-h> {-h option causes the device to hide from Device Manager UI}\n" + +BOOL PrintToasterDeviceInfo(); + +INT __cdecl +main( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] + ) +{ + HDEVINFO hardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL; + ULONG predictedLength = 0; + ULONG requiredLength = 0, bytes=0; + HANDLE file; + int i, ch; + char buffer[10]; + BOOL bHide = FALSE; + + if(argc == 2) { + if(argv[1][0] == '-') { + if(argv[1][1] == 'h' || argv[1][1] == 'H') { + bHide = TRUE; + } else { + printf(USAGE); + exit(0); + } + } + else { + printf(USAGE); + exit(0); + } + } + + + // + // Print a list of devices of Toaster Class + // + if(!PrintToasterDeviceInfo()) + { + printf("No toaster devices present\n"); + return 0; + } + + // + // Open a handle to the device interface information set of all + // present toaster class interfaces. + // + + hardwareDeviceInfo = SetupDiGetClassDevs ( + (LPGUID)&GUID_DEVINTERFACE_TOASTER, + NULL, // Define no enumerator (global) + NULL, // Define no + (DIGCF_PRESENT | // Only Devices present + DIGCF_DEVICEINTERFACE)); // Function class devices. + if(INVALID_HANDLE_VALUE == hardwareDeviceInfo) + { + printf("SetupDiGetClassDevs failed: %x\n", GetLastError()); + return 0; + } + + deviceInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA); + + printf("\nList of Toaster Device Interfaces\n"); + printf("---------------------------------\n"); + + i = 0; + + // + // Enumerate devices of toaster class + // + + for(;;) { + if (SetupDiEnumDeviceInterfaces (hardwareDeviceInfo, + 0, // No care about specific PDOs + (LPGUID)&GUID_DEVINTERFACE_TOASTER, + i, // + &deviceInterfaceData)) { + + if(deviceInterfaceDetailData) { + free (deviceInterfaceDetailData); + deviceInterfaceDetailData = NULL; + } + + // + // Allocate a function class device data structure to + // receive the information about this particular device. + // + + // + // First find out required length of the buffer + // + + if(!SetupDiGetDeviceInterfaceDetail ( + hardwareDeviceInfo, + &deviceInterfaceData, + NULL, // probing so no output buffer yet + 0, // probing so output buffer length of zero + &requiredLength, + NULL)) { // not interested in the specific dev-node + if(ERROR_INSUFFICIENT_BUFFER != GetLastError()) { + printf("SetupDiGetDeviceInterfaceDetail failed %d\n", GetLastError()); + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + return FALSE; + } + + } + + predictedLength = requiredLength; + + deviceInterfaceDetailData = malloc (predictedLength); + + if(deviceInterfaceDetailData) { + deviceInterfaceDetailData->cbSize = + sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA); + } else { + printf("Couldn't allocate %d bytes for device interface details.\n", predictedLength); + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + return FALSE; + } + + + if (! SetupDiGetDeviceInterfaceDetail ( + hardwareDeviceInfo, + &deviceInterfaceData, + deviceInterfaceDetailData, + predictedLength, + &requiredLength, + NULL)) { + printf("Error in SetupDiGetDeviceInterfaceDetail\n"); + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + free (deviceInterfaceDetailData); + return FALSE; + } + printf("%d) %s\n", ++i, + deviceInterfaceDetailData->DevicePath); + } + else if (ERROR_NO_MORE_ITEMS != GetLastError()) { + free (deviceInterfaceDetailData); + deviceInterfaceDetailData = NULL; + continue; + } + else + break; + + } + + + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + + if(!deviceInterfaceDetailData) + { + printf("No device interfaces present\n"); + return 0; + } + + // + // Open the last toaster device interface + // + + printf("\nOpening the last interface:\n %s\n", + deviceInterfaceDetailData->DevicePath); + + file = CreateFile ( deviceInterfaceDetailData->DevicePath, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, // no SECURITY_ATTRIBUTES structure + OPEN_EXISTING, // No special create flags + 0, // No special attributes + NULL); + + if (INVALID_HANDLE_VALUE == file) { + printf("Error in CreateFile: %x", GetLastError()); + free (deviceInterfaceDetailData); + return 0; + } + + // + // Invalidate the Device State + // + + if(bHide) + { + if (!DeviceIoControl (file, + IOCTL_TOASTER_DONT_DISPLAY_IN_UI_DEVICE, + NULL, 0, + NULL, 0, + &bytes, NULL)) { + printf("Invalidate device request failed:0x%x\n", GetLastError()); + free (deviceInterfaceDetailData); + CloseHandle(file); + return 0; + } + printf("\nRequest to hide the device completed successfully\n"); + + } + + + // + // Read/Write to the toaster device. + // + + printf("\nPress 'q' to exit, any other key to read...\n"); + fflush(stdin); + ch = _getche(); + + while(tolower(ch) != 'q' ) + { + + if(!ReadFile(file, buffer, sizeof(buffer), &bytes, NULL)) + { + printf("Error in ReadFile: %x", GetLastError()); + break; + } + printf("Read Successful\n"); + ch = _getche(); + } + + free (deviceInterfaceDetailData); + CloseHandle(file); + return 0; +} + + + +BOOL +PrintToasterDeviceInfo() +{ + HDEVINFO hdi; + DWORD dwIndex=0; + SP_DEVINFO_DATA deid; + BOOL fSuccess=FALSE; + CHAR szCompInstanceId[MAX_PATH]; + CHAR szCompDescription[MAX_PATH]; + CHAR szFriendlyName[MAX_PATH]; + DWORD dwRegType; + BOOL fFound=FALSE; + + // get a list of all devices of class 'GUID_DEVCLASS_TOASTER' + hdi = SetupDiGetClassDevs(&GUID_DEVCLASS_TOASTER, NULL, NULL, + DIGCF_PRESENT); + + if (INVALID_HANDLE_VALUE != hdi) + { + + // enumerate over each device + while (deid.cbSize = sizeof(SP_DEVINFO_DATA), + SetupDiEnumDeviceInfo(hdi, dwIndex, &deid)) + { + dwIndex++; + + // the right thing to do here would be to call this function + // to get the size required to hold the instance ID and then + // to call it second time with a buffer large enough for that size. + // However, that would tend to obscure the control flow in + // the sample code. Lets keep things simple by keeping the + // buffer large enough. + + // get the device instance ID + fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, + szCompInstanceId, + MAX_PATH, NULL); + if (fSuccess) + { + // get the description for this instance + fSuccess = + SetupDiGetDeviceRegistryProperty(hdi, &deid, + SPDRP_DEVICEDESC, + &dwRegType, + (BYTE*) szCompDescription, + MAX_PATH, + NULL); + if (fSuccess) + { + memset(szFriendlyName, 0, MAX_PATH); + SetupDiGetDeviceRegistryProperty(hdi, &deid, + SPDRP_FRIENDLYNAME, + &dwRegType, + (BYTE*) szFriendlyName, + MAX_PATH, + NULL); + fFound = TRUE; + printf("Instance ID : %s\n", szCompInstanceId); + printf("Description : %s\n", szCompDescription); + printf("FriendlyName: %s\n\n", szFriendlyName); + } + } + } + + // release the device info list + SetupDiDestroyDeviceInfoList(hdi); + } + + if(fFound) + return TRUE; + else + return FALSE; +} + diff --git a/general/toaster/umdf2/exe/toast/toast.vcxproj b/general/toaster/umdf2/exe/toast/toast.vcxproj new file mode 100644 index 00000000..45a4ceb0 --- /dev/null +++ b/general/toaster/umdf2/exe/toast/toast.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {9BF4F988-D8B7-4E73-BC77-6658D413212F} + $(MSBuildProjectName) + Debug + Win32 + {5A95B864-3AA6-41E5-AF06-4FB9836D4219} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + toast + + + toast + + + toast + + + toast + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/umdf2/exe/toast/toast.vcxproj.Filters b/general/toaster/umdf2/exe/toast/toast.vcxproj.Filters new file mode 100644 index 00000000..b418a4e3 --- /dev/null +++ b/general/toaster/umdf2/exe/toast/toast.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {74122CFE-AC0A-405A-887C-C84F0A71602A} + + + h;hpp;hxx;hm;inl;inc;xsd + {BF44B779-EC06-4E53-8DA1-5B27378BE11C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {92FCEB4A-CD44-4D16-8B54-1B192FADB341} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/toaster/umdf2/filter/generic/filter.c b/general/toaster/umdf2/filter/generic/filter.c new file mode 100644 index 00000000..d245c2ba --- /dev/null +++ b/general/toaster/umdf2/filter/generic/filter.c @@ -0,0 +1,387 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + filter.c + +Abstract: + + This module shows how to a write a generic filter driver. The driver demonstrates how + to support device I/O control requests through queues. All the I/O requests passed on to + the lower driver. This filter driver shows how to handle IRP postprocessing by forwarding + the requests with and without a completion routine. To forward with a completion routine + set the define FORWARD_REQUEST_WITH_COMPLETION to 1. + +Environment: + + User mode + +--*/ + +#include "filter.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, FilterEvtDeviceAdd) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path, + to driver-specific key in the registry. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ +{ + WDF_DRIVER_CONFIG config; + NTSTATUS status; + WDFDRIVER hDriver; + + KdPrint(("Toaster Generic Filter Driver Sample - Driver Framework Edition.\n")); + + // + // Initiialize driver config to control the attributes that + // are global to the driver. Note that framework by default + // provides a driver unload routine. If you create any resources + // in the DriverEntry and want to be cleaned in driver unload, + // you can override that by manually setting the EvtDriverUnload in the + // config structure. In general xxx_CONFIG_INIT macros are provided to + // initialize most commonly used members. + // + + WDF_DRIVER_CONFIG_INIT( + &config, + FilterEvtDeviceAdd + ); + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + &hDriver); + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDriverCreate failed with status 0x%x\n", status)); + } + + return status; +} + + +NTSTATUS +FilterEvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + EvtDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. Here you can query the device properties + using WdfFdoInitWdmGetPhysicalDevice/IoGetDeviceProperty and based + on that, decide to create a filter device object and attach to the + function stack. If you are not interested in filtering this particular + instance of the device, you can just return STATUS_SUCCESS without creating + a framework device. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + WDF_OBJECT_ATTRIBUTES deviceAttributes; + PFILTER_EXTENSION filterExt; + NTSTATUS status; + WDFDEVICE device; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + + PAGED_CODE (); + + UNREFERENCED_PARAMETER(Driver); + + // + // Tell the framework that you are filter driver. Framework + // takes care of inherting all the device flags & characterstics + // from the lower device you are attaching to. + // + WdfFdoInitSetFilter(DeviceInit); + + // + // Specify the size of device extension where we track per device + // context. + // + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, FILTER_EXTENSION); + + // + // Create a framework device object.This call will inturn create + // a WDM deviceobject, attach to the lower stack and set the + // appropriate flags and attributes. + // + status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device); + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDeviceCreate failed with status code 0x%x\n", status)); + return status; + } + + filterExt = FilterGetData(device); + + // + // Configure the default queue to be Parallel. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, + WdfIoQueueDispatchParallel); + + // + // Framework by default creates non-power managed queues for + // filter drivers. + // + ioQueueConfig.EvtIoDeviceControl = FilterEvtIoDeviceControl; + + status = WdfIoQueueCreate(device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + WDF_NO_HANDLE // pointer to default queue + ); + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfIoQueueCreate failed 0x%x\n", status)); + return status; + } + + return status; +} + +VOID +FilterEvtIoDeviceControl( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t OutputBufferLength, + IN size_t InputBufferLength, + IN ULONG IoControlCode + ) +/*++ + +Routine Description: + + This routine is the dispatch routine for internal device control requests. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + Request - Handle to a framework request object. + + OutputBufferLength - length of the request's output buffer, + if an output buffer is available. + InputBufferLength - length of the request's input buffer, + if an input buffer is available. + + IoControlCode - the driver-defined or system-defined I/O control code + (IOCTL) that is associated with the request. + +Return Value: + + VOID + +--*/ +{ + PFILTER_EXTENSION filterExt; + NTSTATUS status = STATUS_SUCCESS; + WDFDEVICE device; + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + KdPrint(("Entered FilterEvtIoDeviceControl\n")); + + device = WdfIoQueueGetDevice(Queue); + + filterExt = FilterGetData(device); + + switch (IoControlCode) { + + // + // Put your cases for handling IOCTLs here + // + + default: + status = STATUS_SUCCESS; + } + + if (!NT_SUCCESS(status)) { + WdfRequestComplete(Request, status); + return; + } + + // + // Forward the request down. WdfDeviceGetIoTarget returns + // the default target, which represents the device attached to us below in + // the stack. + // +#if FORWARD_REQUEST_WITH_COMPLETION + // + // Use this routine to forward a request if you are interested in post + // processing the IRP. + // + FilterForwardRequestWithCompletionRoutine(Request, + WdfDeviceGetIoTarget(device)); +#else + FilterForwardRequest(Request, WdfDeviceGetIoTarget(device)); +#endif + + return; +} + +VOID +FilterForwardRequest( + IN WDFREQUEST Request, + IN WDFIOTARGET Target + ) +/*++ +Routine Description: + + Passes a request on to the lower driver. + +--*/ +{ + WDF_REQUEST_SEND_OPTIONS options; + BOOLEAN ret; + NTSTATUS status; + + // + // We are not interested in post processing the IRP so + // fire and forget. + // + WDF_REQUEST_SEND_OPTIONS_INIT(&options, + WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET); + + ret = WdfRequestSend(Request, Target, &options); + + if (ret == FALSE) { + status = WdfRequestGetStatus (Request); + KdPrint( ("WdfRequestSend failed: 0x%x\n", status)); + WdfRequestComplete(Request, status); + } + + return; +} + +#if FORWARD_REQUEST_WITH_COMPLETION + +VOID +FilterForwardRequestWithCompletionRoutine( + IN WDFREQUEST Request, + IN WDFIOTARGET Target + ) +/*++ +Routine Description: + + This routine forwards the request to a lower driver with + a completion so that when the request is completed by the + lower driver, it can regain control of the request and look + at the result. + +--*/ +{ + BOOLEAN ret; + NTSTATUS status; + + // + // The following funciton essentially copies the content of + // current stack location of the underlying IRP to the next one. + // + WdfRequestFormatRequestUsingCurrentType(Request); + + WdfRequestSetCompletionRoutine(Request, + FilterRequestCompletionRoutine, + WDF_NO_CONTEXT); + + ret = WdfRequestSend(Request, + Target, + WDF_NO_SEND_OPTIONS); + + if (ret == FALSE) { + status = WdfRequestGetStatus (Request); + KdPrint( ("WdfRequestSend failed: 0x%x\n", status)); + WdfRequestComplete(Request, status); + } + + return; +} + +VOID +FilterRequestCompletionRoutine( + IN WDFREQUEST Request, + IN WDFIOTARGET Target, + PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + IN WDFCONTEXT Context + ) +/*++ + +Routine Description: + + Completion Routine + +Arguments: + + Target - Target handle + Request - Request handle + Params - request completion params + Context - Driver supplied context + + +Return Value: + + VOID + +--*/ +{ + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + WdfRequestComplete(Request, CompletionParams->IoStatus.Status); + + return; +} + +#endif //FORWARD_REQUEST_WITH_COMPLETION + + + + diff --git a/general/toaster/umdf2/filter/generic/filter.h b/general/toaster/umdf2/filter/generic/filter.h new file mode 100644 index 00000000..e9042c29 --- /dev/null +++ b/general/toaster/umdf2/filter/generic/filter.h @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + filter.h + +Abstract: + + Contains structure definitions and function prototypes for a generic filter driver. + +Environment: + + User mode + +--*/ + +#include +#include +#pragma warning( disable: 4201 ) // nonstandard extension used : nameless struct/union +#include +#include +#include +#include + +#if !defined(_FILTER_H_) +#define _FILTER_H_ + + +#define DRIVERNAME "Generic.sys: " + +// +// Change the following define to 1 if you want to forward +// the request with a completion routine. +// +#define FORWARD_REQUEST_WITH_COMPLETION 0 + + +typedef struct _FILTER_EXTENSION +{ + WDFDEVICE WdfDevice; + // More context data here + +}FILTER_EXTENSION, *PFILTER_EXTENSION; + + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FILTER_EXTENSION, + FilterGetData) + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_DEVICE_ADD FilterEvtDeviceAdd; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL FilterEvtIoDeviceControl; + +VOID +FilterForwardRequest( + IN WDFREQUEST Request, + IN WDFIOTARGET Target + ); + +#if FORWARD_REQUEST_WITH_COMPLETION + +VOID +FilterForwardRequestWithCompletionRoutine( + IN WDFREQUEST Request, + IN WDFIOTARGET Target + ); + +VOID +FilterRequestCompletionRoutine( + IN WDFREQUEST Request, + IN WDFIOTARGET Target, + PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + IN WDFCONTEXT Context + ); + +#endif //FORWARD_REQUEST_WITH_COMPLETION + +#endif + diff --git a/general/toaster/umdf2/filter/generic/filter.rc b/general/toaster/umdf2/filter/generic/filter.rc new file mode 100644 index 00000000..0c108f44 --- /dev/null +++ b/general/toaster/umdf2/filter/generic/filter.rc @@ -0,0 +1,12 @@ +#include +#include +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "UMDF 2.0 Filter Driver for the Toaster Stack" +#define VER_INTERNALNAME_STR DRIVERNAME +#define VER_ORIGINALFILENAME_STR DRIVERNAME + +#include "common.ver" + diff --git a/general/toaster/umdf2/filter/generic/filterum.inx b/general/toaster/umdf2/filter/generic/filterum.inx new file mode 100644 index 00000000..3e7013ae --- /dev/null +++ b/general/toaster/umdf2/filter/generic/filterum.inx @@ -0,0 +1,98 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +;Module Name: +; +; filterum.INF +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=TOASTER +ClassGuid={B85B7C50-6A01-11d2-B841-00C04FAD5171} +Provider=%MSFT% +DriverVer=06/16/1999,5.00.2064 +CatalogFile=wudf.cat + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=ToasterClassReg + +[ToasterClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,100 +HKR,,DeviceCharacteristics,0x10001,0x100 ;Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)" ;Allow generic all access to system and built-in Admin. + ;This one overrides the security set by the driver + +;***************************************** +; Toaster Device Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + + +; For XP and later +[Standard.NT$ARCH$] +%WdfSimpleDevice.DeviceDesc%=Toaster_Device, root\toaster + +[Toaster_Device.NT] +CopyFiles=UMDriverCopy + +; ---------------- file copy +[UMDriverCopy] +wdfsimpleum.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME +filterum.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to drivers/umdf + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +wdfsimpleum.dll = 1,, +filterum.dll = 1,, + +;-------------- Service installation + +[Toaster_Device.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +;-------------- WDF specific section ------------- +[Toaster_Device.NT.Wdf] +UmdfService=wdfsimpleum, wdfsimple_Install +UmdfService=filterum, filter_Install +UmdfServiceOrder=wdfsimpleum, filterum + +[wdfsimple_Install] +UmdfLibraryVersion=$UMDFVERSION$ +ServiceBinary=%12%\UMDF\wdfsimpleum.dll + +[filter_Install] +UmdfLibraryVersion=$UMDFVERSION$ +ServiceBinary=%12%\UMDF\filterum.dll + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +MSFT = "Microsoft" +StdMfg = "(Standard system devices)" +DiskId1 = "WDF Sample Toaster Installation Disk #1" +WdfSimpleDevice.DeviceDesc = "Sample WDF Toaster Service + Filter" +ClassName = "Toaster" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" + diff --git a/general/toaster/umdf2/filter/generic/filterum.vcxproj b/general/toaster/umdf2/filter/generic/filterum.vcxproj new file mode 100644 index 00000000..d1f340e4 --- /dev/null +++ b/general/toaster/umdf2/filter/generic/filterum.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A60F76D2-C512-4DA6-8C80-263F1B506267} + $(MSBuildProjectName) + 2 + false + true + Debug + Win32 + {0B7A9ABA-5822-49BC-84D3-571A2C7F009A} + + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + $(InfArch) + true + .\$(IntDir)\filterum.inf + + + + filterum + + + filterum + + + filterum + + + filterum + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/umdf2/filter/generic/filterum.vcxproj.Filters b/general/toaster/umdf2/filter/generic/filterum.vcxproj.Filters new file mode 100644 index 00000000..e36af926 --- /dev/null +++ b/general/toaster/umdf2/filter/generic/filterum.vcxproj.Filters @@ -0,0 +1,34 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {DD0D84E8-FA6D-4F21-A94F-490114347147} + + + h;hpp;hxx;hm;inl;inc;xsd + {D308818F-2345-4AAC-AE3A-51BD7F250DE2} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {79C52444-6CA6-4417-BC6A-85DD2B63D65C} + + + inf;inv;inx;mof;mc; + {D5C7C2FC-CFB2-4B65-879D-EE3C6847D48D} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + \ No newline at end of file diff --git a/general/toaster/umdf2/func/featured/power.c b/general/toaster/umdf2/func/featured/power.c new file mode 100644 index 00000000..60b5df78 --- /dev/null +++ b/general/toaster/umdf2/func/featured/power.c @@ -0,0 +1,385 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Power.C + +Abstract: + + Implements callbacks to manager power transition, wait-wake and selective + suspend. + +Environment: + + Kernel mode + +--*/ + +#include "toaster.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, ToasterEvtDeviceD0Exit) +#pragma alloc_text(PAGE, ToasterEvtDeviceArmWakeFromS0) +#pragma alloc_text(PAGE, ToasterEvtDeviceArmWakeFromSx) +#pragma alloc_text(PAGE, ToasterEvtDeviceWakeFromS0Triggered) +#pragma alloc_text(PAGE, DbgDevicePowerString) +#endif // ALLOC_PRAGMA + + +NTSTATUS +ToasterEvtDeviceD0Entry( + IN WDFDEVICE Device, + IN WDF_POWER_DEVICE_STATE RecentPowerState + ) +/*++ +Routine Description: + + EvtDeviceD0Entry event is called to program the device to goto + D0, which is the working state. The framework calls the driver's + EvtDeviceD0Entry callback when the Power manager sends an + IRP_MN_SET_POWER-DevicePower request to the driver stack. The Power manager + sends this request when the power policy manager of this device stack + (probaby the FDO) requests a change in D-state by calling PoRequestPowerIrp. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - handle to a framework device object. + + RecentPowerState - WDF_POWER_DEVICE_STATE-typed enumerator that identifies the + device power state that the device was in before this transition + to D0. + +Return Value: + + NTSTATUS - A failure here will indicate a fatal error in the driver. + The Framework will attempt to tear down the stack. + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(RecentPowerState); + + KdPrint(("ToasterEvtDeviceD0Entry - coming from %s\n", + DbgDevicePowerString(RecentPowerState))); + + return STATUS_SUCCESS; +} + +NTSTATUS +ToasterEvtDeviceD0Exit( + IN WDFDEVICE Device, + IN WDF_POWER_DEVICE_STATE PowerState + ) +/*++ +Routine Description: + + EvtDeviceD0Entry event is called to program the device to goto + D1, D2 or D3, which are the low-power states. The framework calls the + driver's EvtDeviceD0Exit callback when the Power manager sends an + IRP_MN_SET_POWER-DevicePower request to the driver stack. The Power manager + sends this request when the power policy manager of this device stack + (probaby the FDO) requests a change in D-state by calling PoRequestPowerIrp. + +Arguments: + + Device - handle to a framework device object. + + DeviceState - WDF_POWER_DEVICE_STATE-typed enumerator that identifies the + device power state that the power policy owner (probably the + FDO) has decided is appropriate. + +Return Value: + + NTSTATUS - A failure here will indicate a fatal error in the driver. + The Framework will attempt to tear down the stack. +--*/ +{ + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(PowerState); + + PAGED_CODE(); + + KdPrint(("ToasterEvtDeviceD0Exit %s\n", + DbgDevicePowerString(PowerState))); + + return STATUS_SUCCESS; +} + +NTSTATUS +ToasterEvtDeviceArmWakeFromS0( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceArmWakeFromS0 is called when the Framework arms the device for + wake from S0. If there is any device-specific initialization + that needs to be done to arm internal wake signals, or to route internal + interrupt signals to the wake logic, it should be done here. The device + will be moved out of the D0 state soon after this callback is invoked. + + This function is pageable and it will run at PASSIVE_LEVEL. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + NTSTATUS - Failure will result in the device remaining in the D0 state. + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + + PAGED_CODE(); + + KdPrint(( "--> ToasterEvtDeviceArmWakeFromS0\n")); + + KdPrint(( "<-- ToasterEvtDeviceArmWakeFromS0\n")); + + return STATUS_SUCCESS; +} + +NTSTATUS +ToasterEvtDeviceArmWakeFromSx( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceArmWakeFromSx is called when the Framework arms the device for + wake from Sx. If there is any device-specific initialization + that needs to be done to arm internal wake signals, or to route internal + interrupt signals to the wake logic, it should be done here. The device + will be moved out of the D0 state soon after this callback is invoked. + + This function is pageable and it will run at PASSIVE_LEVEL. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + NTSTATUS - Failure will result in the device remaining in the D0 state. + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + + PAGED_CODE(); + + KdPrint(( "--> ToasterEvtDeviceArmWakeFromSx\n")); + + KdPrint(( "<-- ToasterEvtDeviceArmWakeFromSx\n")); + + return STATUS_SUCCESS; +} + +VOID +ToasterEvtDeviceDisarmWakeFromS0( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceDisarmWakeFromS0 reverses anything done in EvtDeviceArmWakeFromS0. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + VOID. + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + + KdPrint(( "--> ToasterEvtDeviceDisarmWakeFromS0\n")); + + KdPrint(( "<-- ToasterEvtDeviceDisarmWakeFromS0\n")); + + return ; +} + +VOID +ToasterEvtDeviceDisarmWakeFromSx( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceDisarmWakeFromSx reverses anything done in EvtDeviceArmWakeFromSx. + + This function will run at PASSIVE_LEVEL. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + VOID. + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + + KdPrint(( "--> ToasterEvtDeviceDisarmWakeFromSx\n")); + + KdPrint(( "<-- ToasterEvtDeviceDisarmWakeFromSx\n")); + + return ; +} + +VOID +ToasterEvtDeviceWakeFromS0Triggered( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceWakeFromS0Triggered will be called whenever the device triggers its + wake signal after being armed for wake. + + This function is pageable and runs at PASSIVE_LEVEL. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + VOID + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + + PAGED_CODE(); + + KdPrint(( "--> ToasterEvtDeviceWakeFromS0Triggered\n")); + + + KdPrint(( "<-- ToasterEvtDeviceWakeFromS0Triggered\n")); + +} + +VOID +ToasterEvtDeviceWakeFromSxTriggered( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceWakeFromSxTriggered will be called whenever the device triggers its + wake signal after being armed for wake. + + This function runs at PASSIVE_LEVEL. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - Handle to a Framework device object. + +Return Value: + + VOID + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + + KdPrint(( "--> ToasterEvtDeviceWakeFromSxTriggered\n")); + + KdPrint(( "<-- ToasterEvtDeviceWakeFromSxTriggered\n")); + +} + +PCHAR +DbgDevicePowerString( + IN WDF_POWER_DEVICE_STATE Type + ) +/*++ + +New Routine Description: + DbgDevicePowerString converts the device power state code of a power IRP to a + text string that is helpful when tracing the execution of power IRPs. + +Parameters Description: + Type + Type specifies the device power state code of a power IRP. + +Return Value Description: + DbgDevicePowerString returns a pointer to a string that represents the + text description of the incoming device power state code. + +--*/ +{ + PAGED_CODE(); + + switch (Type) + { + case WdfPowerDeviceInvalid: + return "WdfPowerDeviceInvalid"; + case WdfPowerDeviceD0: + return "WdfPowerDeviceD0"; + case WdfPowerDeviceD1: + return "WdfPowerDeviceD1"; + case WdfPowerDeviceD2: + return "WdfPowerDeviceD2"; + case WdfPowerDeviceD3: + return "WdfPowerDeviceD3"; + case WdfPowerDeviceD3Final: + return "WdfPowerDeviceD3Final"; + case WdfPowerDevicePrepareForHibernation: + return "WdfPowerDevicePrepareForHibernation"; + case WdfPowerDeviceMaximum: + return "WdfPowerDeviceMaximum"; + default: + return "UnKnown Device Power State"; + } +} + + + + diff --git a/general/toaster/umdf2/func/featured/toaster.c b/general/toaster/umdf2/func/featured/toaster.c new file mode 100644 index 00000000..c25eebd4 --- /dev/null +++ b/general/toaster/umdf2/func/featured/toaster.c @@ -0,0 +1,862 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Toaster.c + +Abstract: + + This is a featured version of the toaster function driver. This version + shows how to register for PNP and Power events, handle create & close + file requests. + +Environment: + + Kernel mode + +--*/ + +#include "toaster.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, ToasterEvtDeviceAdd) +#pragma alloc_text (PAGE, ToasterEvtDeviceFileCreate) +#pragma alloc_text (PAGE, ToasterEvtFileClose) +#pragma alloc_text (PAGE, ToasterEvtDevicePrepareHardware) +#pragma alloc_text (PAGE, ToasterEvtDeviceReleaseHardware) +#pragma alloc_text (PAGE, ToasterEvtDeviceContextCleanup) +#pragma alloc_text (PAGE, ToasterEvtIoDeviceControl) +#pragma alloc_text (PAGE, ToasterEvtIoRead) +#pragma alloc_text (PAGE, ToasterEvtIoWrite) +#endif + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. DriverEntry specifies the other entry + points in the function driver, such as ToasterAddDevice and ToasterUnload. + +Parameters Description: + + DriverObject - represents the instance of the function driver that is loaded + into memory. DriverEntry must initialize members of DriverObject before it + returns to the caller. DriverObject is allocated by the system before the + driver is loaded, and it is released by the system after the system unloads + the function driver from memory. + + RegistryPath - represents the driver specific path in the Registry. + The function driver can use the path to store driver related data between + reboots. The path does not store hardware instance specific data. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG config; + + KdPrint(("WDF Toaster Function Driver Sample - Featured version\n")); + + // + // Initiialize driver config to control the attributes that + // are global to the driver. Note that framework by default + // provides a driver unload routine. If you create any resources + // in the DriverEntry and want to be cleaned in driver unload, + // you can override that by manually setting the EvtDriverUnload in the + // config structure. In general xxx_CONFIG_INIT macros are provided to + // initialize most commonly used members. + // + + WDF_DRIVER_CONFIG_INIT( + &config, + ToasterEvtDeviceAdd + ); + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate( + DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, // Driver Attributes + &config, // Driver Config Info + WDF_NO_HANDLE + ); + + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDriverCreate failed with status 0x%x\n", status)); + } + + return status; +} + + +NTSTATUS +ToasterEvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + ToasterEvtDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. We create and initialize a device object to + represent a new instance of toaster device. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDF_OBJECT_ATTRIBUTES fdoAttributes; + WDFDEVICE device; + WDF_FILEOBJECT_CONFIG fileConfig; + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; + WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings; + WDF_POWER_POLICY_EVENT_CALLBACKS powerPolicyCallbacks; + WDF_IO_QUEUE_CONFIG queueConfig; + //PFDO_DATA fdoData; + WDFQUEUE queue; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + KdPrint(("ToasterEvtDeviceAdd called\n")); + + // + // Initialize the pnpPowerCallbacks structure. Callback events for PNP + // and Power are specified here. If you don't supply any callbacks, + // the Framework will take appropriate default actions based on whether + // DeviceInit is initialized to be an FDO, a PDO or a filter device + // object. + // + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); + + // + // Register PNP callbacks. + // + pnpPowerCallbacks.EvtDevicePrepareHardware = ToasterEvtDevicePrepareHardware; + pnpPowerCallbacks.EvtDeviceReleaseHardware = ToasterEvtDeviceReleaseHardware; + pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = ToasterEvtDeviceSelfManagedIoInit; + + // + // Register Power callbacks. + // + pnpPowerCallbacks.EvtDeviceD0Entry = ToasterEvtDeviceD0Entry; + pnpPowerCallbacks.EvtDeviceD0Exit = ToasterEvtDeviceD0Exit; + + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + // + // Register power policy event callbacks so that we would know when to + // arm/disarm the hardware to handle wait-wake and when the wake event + // is triggered by the hardware. + // + WDF_POWER_POLICY_EVENT_CALLBACKS_INIT(&powerPolicyCallbacks); + + // + // This group of three callbacks allows this sample driver to manage + // arming the device for wake from the S0 or Sx state. We don't really + // differentiate between S0 and Sx state.. + // + powerPolicyCallbacks.EvtDeviceArmWakeFromS0 = ToasterEvtDeviceArmWakeFromS0; + powerPolicyCallbacks.EvtDeviceDisarmWakeFromS0 = ToasterEvtDeviceDisarmWakeFromS0; + powerPolicyCallbacks.EvtDeviceWakeFromS0Triggered = ToasterEvtDeviceWakeFromS0Triggered; + powerPolicyCallbacks.EvtDeviceArmWakeFromSx = ToasterEvtDeviceArmWakeFromSx; + powerPolicyCallbacks.EvtDeviceDisarmWakeFromSx = ToasterEvtDeviceDisarmWakeFromSx; + powerPolicyCallbacks.EvtDeviceWakeFromSxTriggered = ToasterEvtDeviceWakeFromSxTriggered; + + // + // Register the power policy callbacks. + // + WdfDeviceInitSetPowerPolicyEventCallbacks(DeviceInit, &powerPolicyCallbacks); + + // + // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the + // framework whether you are interested in handling Create, Close and + // Cleanup requests that gets genereate when an application or another + // kernel component opens an handle to the device. If you don't register, + // the framework default behaviour would be complete these requests + // with STATUS_SUCCESS. A driver might be interested in registering these + // events if it wants to do security validation and also wants to maintain + // per handle (fileobject) context. + // + + WDF_FILEOBJECT_CONFIG_INIT( + &fileConfig, + ToasterEvtDeviceFileCreate, + ToasterEvtFileClose, + WDF_NO_EVENT_CALLBACK // not interested in Cleanup + ); + + WdfDeviceInitSetFileObjectConfig(DeviceInit, + &fileConfig, + WDF_NO_OBJECT_ATTRIBUTES); + + // + // Now specify the size of device extension where we track per device + // context. Along with setting the context type as shown below, you should also + // specify the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME in header to specify the + // accessor function name. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FDO_DATA); + + // + // Set a context cleanup routine to cleanup any resources that are not + // parent to this device. This cleanup will be called in the context of + // pnp remove-device when the framework deletes the device object. + // + fdoAttributes.EvtCleanupCallback = ToasterEvtDeviceContextCleanup; + + // + // DeviceInit is completely initialized. So call the framework to create the + // device and attach it to the lower stack. + // + status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &device); + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDeviceCreate failed with Status code 0x%x\n", status)); + return status; + } + + // + // Get the device context by using accessor function specified in + // the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for FDO_DATA. + // + //fdoData = ToasterFdoGetData(device); + + // + // Tell the Framework that this device will need an interface so that + // application can find our device and talk to it. + // + status = WdfDeviceCreateDeviceInterface( + device, + (LPGUID) &GUID_DEVINTERFACE_TOASTER, + NULL + ); + + if (!NT_SUCCESS (status)) { + KdPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status)); + return status; + } + + // + // Register I/O callbacks to tell the framework that you are interested + // in handling IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL requests. + // In case a specific handler is not specified for one of these, + // the request will be dispatched to the EvtIoDefault handler, if any. + // If there is no EvtIoDefault handler, the request will be failed with + // STATUS_INVALID_DEVICE_REQUEST. + // WdfIoQueueDispatchParallel means that we are capable of handling + // all the I/O request simultaneously and we are responsible for protecting + // data that could be accessed by these callbacks simultaneously. + // A default queue gets all the requests that are not + // configure-fowarded using WdfDeviceConfigureRequestDispatching. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, + WdfIoQueueDispatchParallel); // EvtIoCancel + + queueConfig.EvtIoRead = ToasterEvtIoRead; + queueConfig.EvtIoWrite = ToasterEvtIoWrite; + queueConfig.EvtIoDeviceControl = ToasterEvtIoDeviceControl; + + // + // By default, Static Driver Verifier (SDV) displays a warning if it + // doesn't find the EvtIoStop callback on a power-managed queue. + // The 'assume' below causes SDV to suppress this warning. If the driver + // has not explicitly set PowerManaged to WdfFalse, the framework creates + // power-managed queues when the device is not a filter driver. Normally + // the EvtIoStop is required for power-managed queues, but for this driver + // it is not needed b/c the driver doesn't hold on to the requests or + // forward them to other drivers. This driver completes the requests + // directly in the queue's handlers. If the EvtIoStop callback is not + // implemented, the framework waits for all driver-owned requests to be + // done before moving in the Dx/sleep states or before removing the + // device, which is the correct behavior for this type of driver. + // If the requests were taking an indeterminate amount of time to complete, + // or if the driver forwarded the requests to a lower driver/another stack, + // the queue should have an EvtIoStop/EvtIoResume. + // + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(device, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue + ); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if (!NT_SUCCESS (status)) { + + KdPrint( ("WdfIoQueueCreate failed 0x%x\n", status)); + return status; + } + + // + // Set the idle power policy to put the device to Dx if the device is not used + // for the specified IdleTimeout time. Since this is a virtual device we + // tell the framework that we cannot wake ourself if we sleep in S0. Only + // way the device can be brought to D0 is if the device recieves an I/O from + // the system. + // + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleCannotWakeFromS0); + idleSettings.IdleTimeout = 60000; // 60 secs idle timeout + status = WdfDeviceAssignS0IdleSettings(device, &idleSettings); + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDeviceAssignS0IdleSettings failed 0x%x\n", status)); + return status; + } + + // + // Set the wait-wake policy. + // + + WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings); + status = WdfDeviceAssignSxWakeSettings(device, &wakeSettings); + if (!NT_SUCCESS(status)) { + // + // We are probably enumerated on a bus that doesn't support Sx-wake. + // Let us not fail the device add just because we aren't able to support + // wait-wake. I will let the user of this sample decide how important it's + // to support wait-wake for their hardware and return appropriate status. + // + KdPrint( ("WdfDeviceAssignSxWakeSettings failed 0x%x\n", status)); + status = STATUS_SUCCESS; + } + + return status; +} + +NTSTATUS +ToasterEvtDevicePrepareHardware( + WDFDEVICE Device, + WDFCMRESLIST ResourcesRaw, + WDFCMRESLIST ResourcesTranslated + ) +/*++ + +Routine Description: + + EvtDevicePrepareHardware event callback performs operations that are + necessary to make the driver's device operational. The framework calls the + driver's EvtDevicePrepareHardware callback when the PnP manager sends an + IRP_MN_START_DEVICE request to the driver stack. + + Specifically, most drivers will use this callback to map resources. USB + drivers may use it to get device descriptors, config descriptors and to + select configs. + + Some drivers may choose to download firmware to a device in this callback, + but that is usually only a good choice if the device firmware won't be + destroyed by a D0 to D3 transition. If firmware will be gone after D3, + then firmware downloads should be done in EvtDeviceD0Entry, not here. + +Arguments: + + Device - Handle to a framework device object. + + ResourcesRaw - Handle to a collection of framework resource objects. + This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + ResourcesTranslated - Handle to a collection of framework resource objects. + This collection identifies the translated (system-physical) + hardware resources that have been assigned to the device. + The resources appear from the CPU's point of view. + Use this list of resources to map I/O space and + device-accessible memory into virtual address space + +Return Value: + + WDF status code + +--*/ +{ + //PFDO_DATA fdoData; + NTSTATUS status = STATUS_SUCCESS; + ULONG i; + PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor; + + //fdoData = ToasterFdoGetData(Device); + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(ResourcesRaw); + + KdPrint(("ToasterEvtDevicePrepareHardware called\n")); + + PAGED_CODE(); + // + // Get the number item that are currently in Resources collection and + // iterate thru as many times to get more information about the each items + // + for (i=0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++) { + + descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i); + + switch(descriptor->Type) { + + case CmResourceTypePort: + + KdPrint(("I/O Port: (%x) Length: (%d)\n", + descriptor->u.Port.Start.LowPart, + descriptor->u.Port.Length)); + break; + + case CmResourceTypeMemory: + + KdPrint(("Memory: (%x) Length: (%d)\n", + descriptor->u.Memory.Start.LowPart, + descriptor->u.Memory.Length)); + break; + case CmResourceTypeInterrupt: + + KdPrint(("Interrupt level: 0x%0x, Vector: 0x%0x, Affinity: 0x%0Ix\n", + descriptor->u.Interrupt.Level, + descriptor->u.Interrupt.Vector, + descriptor->u.Interrupt.Affinity)); + break; + + default: + break; + } + + } + + return status; + +} + +NTSTATUS +ToasterEvtDeviceReleaseHardware( + IN WDFDEVICE Device, + IN WDFCMRESLIST ResourcesTranslated + ) +/*++ + +Routine Description: + + EvtDeviceReleaseHardware is called by the framework whenever the PnP manager + is revoking ownership of our resources. This may be in response to either + IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE. The callback is made before + passing down the IRP to the lower driver. + + In this callback, do anything necessary to free those resources. + +Arguments: + + Device - Handle to a framework device object. + + ResourcesTranslated - Handle to a collection of framework resource objects. + This collection identifies the translated (system-physical) + hardware resources that have been assigned to the device. + The resources appear from the CPU's point of view. + Use this list of resources to map I/O space and + device-accessible memory into virtual address space + +Return Value: + + NTSTATUS - Failures will be logged, but not acted on. + +--*/ +{ + //PFDO_DATA fdoData; + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(ResourcesTranslated); + + KdPrint(("ToasterEvtDeviceReleaseHardware called\n")); + + PAGED_CODE(); + + //fdoData = ToasterFdoGetData(Device); + // + // Unmap any I/O ports, registers that you mapped in PrepareHardware. + // Disconnecting from the interrupt will be done automatically by the framework. + // + return STATUS_SUCCESS; +} + +NTSTATUS +ToasterEvtDeviceSelfManagedIoInit( + IN WDFDEVICE Device + ) +/*++ + +Routine Description: + + EvtDeviceSelfManagedIoInit is called it once for each device, + after the framework has called the driver's EvtDeviceD0Entry + callback function for the first time. The framework does not + call the EvtDeviceSelfManagedIoInit callback function again for + that device, unless the device is removed and reconnected, or + the drivers are reloaded. + + The EvtDeviceSelfManagedIoInit callback function must initialize + the self-managed I/O operations that the driver will handle + for the device. + + This function is not marked pageable because this function is in the + device power up path. When a function is marked pagable and the code + section is paged out, it will generate a page fault which could impact + the fast resume behavior because the client driver will have to wait + until the system drivers can service this page fault. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - Failures will result in the device stack being torn down. + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + + KdPrint(("ToasterEvtDeviceSelfManagedIoInit called\n")); + + return STATUS_SUCCESS; +} + + +VOID +ToasterEvtDeviceContextCleanup( + IN WDFOBJECT Device + ) +/*++ + +Routine Description: + + EvtDeviceContextCleanup event callback must perform any operations that are + necessary before the specified device is removed. The framework calls + the driver's EvtDeviceContextCleanup callback when the device is deleted in response + to IRP_MN_REMOVE_DEVICE request. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + None + +--*/ +{ + //PFDO_DATA fdoData; + UNREFERENCED_PARAMETER(Device); + KdPrint( ("ToasterEvtDeviceContextCleanup called\n")); + + PAGED_CODE(); + + //fdoData = ToasterFdoGetData((WDFDEVICE)Device); + + return; +} + +VOID +ToasterEvtDeviceFileCreate ( + IN WDFDEVICE Device, + IN WDFREQUEST Request, + IN WDFFILEOBJECT FileObject + ) +/*++ + +Routine Description: + + The framework calls a driver's EvtDeviceFileCreate callback + when the framework receives an IRP_MJ_CREATE request. + The system sends this request when a user application opens the + device to perform an I/O operation, such as reading or writing to a device. + This callback is called in the context of the thread + that created the IRP_MJ_CREATE request. + +Arguments: + + Device - Handle to a framework device object. + FileObject - Pointer to fileobject that represents the open handle. + CreateParams - Parameters for create + +Return Value: + + None + +--*/ +{ + //PFDO_DATA fdoData; + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(Device); + + KdPrint( ("ToasterEvtDeviceFileCreate %p\n", Device)); + + PAGED_CODE (); + + // + // Get the device context given the device handle. + // + //fdoData = ToasterFdoGetData(Device); + + WdfRequestComplete(Request, STATUS_SUCCESS); + + return; +} + + +VOID +ToasterEvtFileClose ( + IN WDFFILEOBJECT FileObject + ) + +/*++ + +Routine Description: + + EvtFileClose is called when all the handles represented by the FileObject + is closed and all the references to FileObject is removed. This callback + may get called in an arbitrary thread context instead of the thread that + called CloseHandle. If you want to delete any per FileObject context that + must be done in the context of the user thread that made the Create call, + you should do that in the EvtDeviceCleanp callback. + +Arguments: + + FileObject - Pointer to fileobject that represents the open handle. + +Return Value: + + None + +--*/ +{ + //PFDO_DATA fdoData; + UNREFERENCED_PARAMETER(FileObject); + PAGED_CODE (); + + //fdoData = ToasterFdoGetData(WdfFileObjectGetDevice(FileObject)); + + KdPrint( ("ToasterEvtFileClose\n")); + + return; +} + + + +VOID +ToasterEvtIoRead ( + WDFQUEUE Queue, + WDFREQUEST Request, + size_t Length + ) +/*++ + +Routine Description: + + Performs read to the toaster device. This event is called when the + framework receives IRP_MJ_READ requests. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + Request - Handle to a framework request object. + + Lenght - Length of the data buffer associated with the request. + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + None + +--*/ +{ + NTSTATUS status; + ULONG_PTR bytesCopied =0; + WDFMEMORY memory; + + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(Queue); + + PAGED_CODE(); + + KdPrint(("ToasterEvtIoRead: Request: 0x%p, Queue: 0x%p\n", + Request, Queue)); + + // + // Get the request memory and perform read operation here + // + status = WdfRequestRetrieveOutputMemory(Request, &memory); + if(NT_SUCCESS(status) ) { + // + // Copy data into the memory buffer using WdfMemoryCopyFromBuffer + // + } + + WdfRequestCompleteWithInformation(Request, status, bytesCopied); + +} + +VOID +ToasterEvtIoWrite ( + WDFQUEUE Queue, + WDFREQUEST Request, + size_t Length + ) +/*++ + +Routine Description: + + Performs write to the toaster device. This event is called when the + framework receives IRP_MJ_WRITE requests. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + Request - Handle to a framework request object. + + Lenght - Length of the data buffer associated with the request. + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + None + +--*/ + +{ + NTSTATUS status; + WDFMEMORY memory; + + UNREFERENCED_PARAMETER(Queue); + + KdPrint(("ToasterEvtIoWrite. Request: 0x%p, Queue: 0x%p\n", + Request, Queue)); + PAGED_CODE(); + + // + // Get the request buffer and perform write operation here + // + status = WdfRequestRetrieveInputMemory(Request, &memory); + if(NT_SUCCESS(status) ) { + // + // 1) Use WdfMemoryCopyToBuffer to copy data from the request + // to driver buffer. + // 2) Or get the buffer pointer from the request by calling + // WdfRequestRetrieveInputBuffer to transfer data to the hw + // 3) Or you can get the buffer pointer from the memory handle + // by calling WdfMemoryGetBuffer to transfer data to the hw. + // + } + + WdfRequestCompleteWithInformation(Request, status, Length); + +} + + +VOID +ToasterEvtIoDeviceControl( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t OutputBufferLength, + IN size_t InputBufferLength, + IN ULONG IoControlCode + ) +/*++ +Routine Description: + + This event is called when the framework receives IRP_MJ_DEVICE_CONTROL + requests from the system. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + Request - Handle to a framework request object. + + OutputBufferLength - length of the request's output buffer, + if an output buffer is available. + InputBufferLength - length of the request's input buffer, + if an input buffer is available. + + IoControlCode - the driver-defined or system-defined I/O control code + (IOCTL) that is associated with the request. + +Return Value: + + None + +--*/ +{ + NTSTATUS status= STATUS_SUCCESS; + WDF_DEVICE_STATE deviceState; + WDFDEVICE hDevice = WdfIoQueueGetDevice(Queue); + + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + KdPrint(("ToasterEvtIoDeviceControl called\n")); + + PAGED_CODE(); + + switch (IoControlCode) { + + case IOCTL_TOASTER_DONT_DISPLAY_IN_UI_DEVICE: + // + // This is just an example on how to hide your device in the + // device manager. Please remove this code when you adapt + // this sample for your hardware. + // + WDF_DEVICE_STATE_INIT(&deviceState); + deviceState.DontDisplayInUI = WdfTrue; + WdfDeviceSetDeviceState( + hDevice, + &deviceState + ); + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Complete the Request. + // + WdfRequestCompleteWithInformation(Request, status, (ULONG_PTR) 0); + +} + + diff --git a/general/toaster/umdf2/func/featured/toaster.rc b/general/toaster/umdf2/func/featured/toaster.rc new file mode 100644 index 00000000..e9ba2ea8 --- /dev/null +++ b/general/toaster/umdf2/func/featured/toaster.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WDF UMDF2 Toaster Device Driver" +#define VER_INTERNALNAME_STR "wdftoaster.dll" +#define VER_ORIGINALFILENAME_STR "wdftoaster.dll" + +#include "common.ver" + diff --git a/general/toaster/umdf2/func/featured/wdffeaturedum.inx b/general/toaster/umdf2/func/featured/wdffeaturedum.inx new file mode 100644 index 00000000..feae7a33 --- /dev/null +++ b/general/toaster/umdf2/func/featured/wdffeaturedum.inx @@ -0,0 +1,98 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +;Module Name: +; wdffeatured.INF +; +;Abstract: +; INF file for installing the UMDF2 Toaster Driver +; +;Installation Notes: +; Using Devcon: Type "devcon install wdffeaturedum.inf root\toaster" to install +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=TOASTER +ClassGuid={B85B7C50-6A01-11d2-B841-00C04FAD5171} +Provider=%MSFT% +DriverVer=03/20/2003,5.00.3788 +CatalogFile=wudf.cat + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=ToasterClassReg + +[ToasterClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,100 +HKR,,DeviceCharacteristics,0x10001,0x100 ;Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)" ;Allow generic all access to system and built-in Admin. + ;This one overrides the security set by the driver + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +wdffeaturedum.dll = 1,, + +;***************************************** +; Toaster Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +; +; Hw Id is root\toaster +; +[Standard.NT$ARCH$] +%Toaster.DeviceDesc%=Toaster_Device, root\toaster + +;---------------- copy files + +[Toaster_Device.NT] +CopyFiles=UMDriverCopy + +[UMDriverCopy] +wdffeaturedum.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to drivers\umdf + +;-------------- Service installation +[Toaster_Device.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +;-------------- WDF specific section ------------- +[Toaster_Device.NT.Wdf] +UmdfService=wdffeaturedum, Toaster_Install +UmdfServiceOrder=wdffeaturedum + +[Toaster_Install] +UmdfLibraryVersion=$UMDFVERSION$ +ServiceBinary=%12%\UMDF\wdffeaturedum.dll + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +MSFT = "Microsoft" +StdMfg = "(Standard system devices)" +DiskId1 = "WDF Sample Toaster Installation Disk #1" +Toaster.DeviceDesc = "Sample UMDF Toaster Driver - featured" +Toaster.SVCDESC = "Sample WDF Toaster Service" +ClassName = "Toaster" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" + diff --git a/general/toaster/umdf2/func/featured/wdffeaturedum.vcxproj b/general/toaster/umdf2/func/featured/wdffeaturedum.vcxproj new file mode 100644 index 00000000..1af04c81 --- /dev/null +++ b/general/toaster/umdf2/func/featured/wdffeaturedum.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {99A6009E-6137-462F-89C4-6931045895B1} + $(MSBuildProjectName) + 2 + Debug + Win32 + {3E5FB5B1-CA59-4675-AAF8-93039F74CF02} + + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + $(InfArch) + true + .\$(IntDir)\wdffeaturedum.inf + + + + wdffeaturedum + + + wdffeaturedum + + + wdffeaturedum + + + wdffeaturedum + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/umdf2/func/featured/wdffeaturedum.vcxproj.Filters b/general/toaster/umdf2/func/featured/wdffeaturedum.vcxproj.Filters new file mode 100644 index 00000000..ad344822 --- /dev/null +++ b/general/toaster/umdf2/func/featured/wdffeaturedum.vcxproj.Filters @@ -0,0 +1,42 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {180B07F2-6C79-44F0-9765-749A2975BA1F} + + + h;hpp;hxx;hm;inl;inc;xsd + {025585BB-48DE-4F3B-A929-D58821892D58} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A90CE102-D7E8-46E0-B8D8-D1A57A6CDE82} + + + inf;inv;inx;mof;mc; + {20EB3DBD-A818-46EE-A8B4-6A507F202B60} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/umdf2/func/shared/toaster.h b/general/toaster/umdf2/func/shared/toaster.h new file mode 100644 index 00000000..006166e7 --- /dev/null +++ b/general/toaster/umdf2/func/shared/toaster.h @@ -0,0 +1,118 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + Toaster.h + +Abstract: + + Header file for the toaster driver modules. + +Environment: + + User mode + +--*/ + + +#if !defined(_TOASTER_H_) +#define _TOASTER_H_ + +#include +#include +#pragma warning( disable: 4201 ) // nonstandard extension used : nameless struct/union +#include +#include +#include +#include +#include +#include "..\inc\driver.h" +#include "..\inc\public.h" + +#define TOASTER_POOL_TAG (ULONG) 'saoT' + +// +// The device extension for the device object +// +typedef struct _FDO_DATA +{ + + WDFWMIINSTANCE WmiDeviceArrivalEvent; + + BOOLEAN WmiPowerDeviceEnableRegistered; + +} FDO_DATA, *PFDO_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DATA, ToasterFdoGetData) + + +// +// Connector Types +// + +#define TOASTER_WMI_STD_I8042 0 +#define TOASTER_WMI_STD_SERIAL 1 +#define TOASTER_WMI_STD_PARALEL 2 +#define TOASTER_WMI_STD_USB 3 + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD ToasterEvtDeviceAdd; + +EVT_WDF_DEVICE_CONTEXT_CLEANUP ToasterEvtDeviceContextCleanup; +EVT_WDF_DEVICE_D0_ENTRY ToasterEvtDeviceD0Entry; +EVT_WDF_DEVICE_D0_EXIT ToasterEvtDeviceD0Exit; +EVT_WDF_DEVICE_PREPARE_HARDWARE ToasterEvtDevicePrepareHardware; +EVT_WDF_DEVICE_RELEASE_HARDWARE ToasterEvtDeviceReleaseHardware; + +EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT ToasterEvtDeviceSelfManagedIoInit; + +// +// Io events callbacks. +// +EVT_WDF_IO_QUEUE_IO_READ ToasterEvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE ToasterEvtIoWrite; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL ToasterEvtIoDeviceControl; +EVT_WDF_DEVICE_FILE_CREATE ToasterEvtDeviceFileCreate; +EVT_WDF_FILE_CLOSE ToasterEvtFileClose; + +NTSTATUS +ToasterWmiRegistration( + _In_ WDFDEVICE Device + ); + +// +// Power events callbacks +// +EVT_WDF_DEVICE_ARM_WAKE_FROM_S0 ToasterEvtDeviceArmWakeFromS0; +EVT_WDF_DEVICE_ARM_WAKE_FROM_SX ToasterEvtDeviceArmWakeFromSx; +EVT_WDF_DEVICE_DISARM_WAKE_FROM_S0 ToasterEvtDeviceDisarmWakeFromS0; +EVT_WDF_DEVICE_DISARM_WAKE_FROM_SX ToasterEvtDeviceDisarmWakeFromSx; +EVT_WDF_DEVICE_WAKE_FROM_S0_TRIGGERED ToasterEvtDeviceWakeFromS0Triggered; +EVT_WDF_DEVICE_WAKE_FROM_SX_TRIGGERED ToasterEvtDeviceWakeFromSxTriggered; + +PCHAR +DbgDevicePowerString( + IN WDF_POWER_DEVICE_STATE Type + ); + +// +// WMI event callbacks +// +EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiInstanceStdDeviceDataQueryInstance; +EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiInstanceToasterControlQueryInstance; +EVT_WDF_WMI_INSTANCE_SET_INSTANCE EvtWmiInstanceStdDeviceDataSetInstance; +EVT_WDF_WMI_INSTANCE_SET_INSTANCE EvtWmiInstanceToasterControlSetInstance; +EVT_WDF_WMI_INSTANCE_SET_ITEM EvtWmiInstanceToasterControlSetItem; +EVT_WDF_WMI_INSTANCE_SET_ITEM EvtWmiInstanceStdDeviceDataSetItem; +EVT_WDF_WMI_INSTANCE_EXECUTE_METHOD EvtWmiInstanceToasterControlExecuteMethod; + +NTSTATUS +ToasterFireArrivalEvent( + _In_ WDFDEVICE Device + ); + +#endif // _TOASTER_H_ + diff --git a/general/toaster/umdf2/func/simple/toaster.c b/general/toaster/umdf2/func/simple/toaster.c new file mode 100644 index 00000000..6ed215db --- /dev/null +++ b/general/toaster/umdf2/func/simple/toaster.c @@ -0,0 +1,418 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Toaster.c + +Abstract: + + This is a simple form of function driver for toaster device. The driver + doesn't handle any PnP and Power events because the framework provides + default behavior for those events. This driver has enough support to + allow an user application (toast/notify.exe) to open the device + interface registered by the driver and send read, write or ioctl requests. + +Environment: + + Kernel mode + +--*/ + +#include "toaster.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, ToasterEvtDeviceAdd) +#pragma alloc_text (PAGE, ToasterEvtIoRead) +#pragma alloc_text (PAGE, ToasterEvtIoWrite) +#pragma alloc_text (PAGE, ToasterEvtIoDeviceControl) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. DriverEntry configures and creates a WDF driver + object. + . +Parameters Description: + + DriverObject - represents the instance of the function driver that is loaded + into memory. DriverObject is allocated by the system before the + driver is loaded, and it is released by the system after the system unloads + the function driver from memory. + + RegistryPath - represents the driver specific path in the Registry. + The function driver can use the path to store driver related data between + reboots. The path does not store hardware instance specific data. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG config; + + KdPrint(("Toaster Function Driver Sample - Driver Framework Edition.\n")); + + // + // Initiialize driver config to control the attributes that + // are global to the driver. Note that framework by default + // provides a driver unload routine. If DriverEntry creates any resources + // that require clean-up in driver unload, + // you can manually override the default by supplying a pointer to the EvtDriverUnload + // callback in the config structure. In general xxx_CONFIG_INIT macros are provided to + // initialize most commonly used members. + // + + WDF_DRIVER_CONFIG_INIT( + &config, + ToasterEvtDeviceAdd + ); + + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate( + DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, // Driver Attributes + &config, // Driver Config Info + WDF_NO_HANDLE + ); + + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDriverCreate failed with status 0x%x\n", status)); + } + + return status; +} + + +NTSTATUS +ToasterEvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + ToasterEvtDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. We create and initialize a WDF device object to + represent a new instance of toaster device. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PFDO_DATA fdoData; + WDF_IO_QUEUE_CONFIG queueConfig; + WDF_OBJECT_ATTRIBUTES fdoAttributes; + WDFDEVICE hDevice; + WDFQUEUE queue; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + KdPrint(("ToasterEvtDeviceAdd called\n")); + + // + // Initialize attributes and a context area for the device object. + // + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FDO_DATA); + + // + // Create a framework device object.This call will in turn create + // a WDM device object, attach to the lower stack, and set the + // appropriate flags and attributes. + // + status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &hDevice); + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDeviceCreate failed with status code 0x%x\n", status)); + return status; + } + + // + // Get the device context by using the accessor function specified in + // the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for FDO_DATA. + // + fdoData = ToasterFdoGetData(hDevice); + + // + // Tell the Framework that this device will need an interface + // + status = WdfDeviceCreateDeviceInterface( + hDevice, + (LPGUID) &GUID_DEVINTERFACE_TOASTER, + NULL // ReferenceString + ); + + if (!NT_SUCCESS (status)) { + KdPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status)); + return status; + } + + // + // Register I/O callbacks to tell the framework that you are interested + // in handling IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL requests. + // If a specific callback function is not specified for one ofthese, + // the request will be dispatched to the EvtIoDefault handler, if any. + // If there is no EvtIoDefault handler, the request will be failed with + // STATUS_INVALID_DEVICE_REQUEST. + // WdfIoQueueDispatchParallel means that we are capable of handling + // all the I/O requests simultaneously and we are responsible for protecting + // data that could be accessed by these callbacks simultaneously. + // A default queue gets all the requests that are not + // configured for forwarding using WdfDeviceConfigureRequestDispatching. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); + + queueConfig.EvtIoRead = ToasterEvtIoRead; + queueConfig.EvtIoWrite = ToasterEvtIoWrite; + queueConfig.EvtIoDeviceControl = ToasterEvtIoDeviceControl; + + // + // By default, Static Driver Verifier (SDV) displays a warning if it + // doesn't find the EvtIoStop callback on a power-managed queue. + // The 'assume' below causes SDV to suppress this warning. If the driver + // has not explicitly set PowerManaged to WdfFalse, the framework creates + // power-managed queues when the device is not a filter driver. Normally + // the EvtIoStop is required for power-managed queues, but for this driver + // it is not needed b/c the driver doesn't hold on to the requests or + // forward them to other drivers. This driver completes the requests + // directly in the queue's handlers. If the EvtIoStop callback is not + // implemented, the framework waits for all driver-owned requests to be + // done before moving in the Dx/sleep states or before removing the + // device, which is the correct behavior for this type of driver. + // If the requests were taking an indeterminate amount of time to complete, + // or if the driver forwarded the requests to a lower driver/another stack, + // the queue should have an EvtIoStop/EvtIoResume. + // + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate( + hDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue + ); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if (!NT_SUCCESS (status)) { + + KdPrint( ("WdfIoQueueCreate failed 0x%x\n", status)); + return status; + } + + return status; +} + +VOID +ToasterEvtIoRead ( + WDFQUEUE Queue, + WDFREQUEST Request, + size_t Length + ) +/*++ + +Routine Description: + + Performs read from the toaster device. This event is called when the + framework receives IRP_MJ_READ requests. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + Request - Handle to a framework request object. + + Lenght - Length of the data buffer associated with the request. + By default, the queue does not dispatch + zero length read & write requests to the driver and instead to + complete such requests with status success. So we will never get + a zero length request. + +Return Value: + + None. + +--*/ +{ + NTSTATUS status; + ULONG_PTR bytesCopied =0; + WDFMEMORY memory; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(Length); + + PAGED_CODE(); + + KdPrint(( "ToasterEvtIoRead: Request: 0x%p, Queue: 0x%p\n", + Request, Queue)); + + // + // Get the request memory and perform read operation here + // + status = WdfRequestRetrieveOutputMemory(Request, &memory); + if(NT_SUCCESS(status) ) { + // + // Copy data into the memory buffer using WdfMemoryCopyFromBuffer + // + } + + WdfRequestCompleteWithInformation(Request, status, bytesCopied); +} + +VOID +ToasterEvtIoWrite ( + WDFQUEUE Queue, + WDFREQUEST Request, + size_t Length + ) +/*++ + +Routine Description: + + Performs write to the toaster device. This event is called when the + framework receives IRP_MJ_WRITE requests. + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + Request - Handle to a framework request object. + + Lenght - Length of the data buffer associated with the request. + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + None +--*/ + +{ + NTSTATUS status; + ULONG_PTR bytesWritten =0; + WDFMEMORY memory; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(Length); + + KdPrint(("ToasterEvtIoWrite. Request: 0x%p, Queue: 0x%p\n", + Request, Queue)); + + PAGED_CODE(); + + // + // Get the request buffer and perform write operation here + // + status = WdfRequestRetrieveInputMemory(Request, &memory); + if(NT_SUCCESS(status) ) { + // + // 1) Use WdfMemoryCopyToBuffer to copy data from the request + // to driver buffer. + // 2) Or get the buffer pointer from the request by calling + // WdfRequestRetrieveInputBuffer + // 3) Or you can get the buffer pointer from the memory handle + // by calling WdfMemoryGetBuffer. + // + bytesWritten = Length; + } + + WdfRequestCompleteWithInformation(Request, status, bytesWritten); + +} + + +VOID +ToasterEvtIoDeviceControl( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t OutputBufferLength, + IN size_t InputBufferLength, + IN ULONG IoControlCode + ) +/*++ +Routine Description: + + This event is called when the framework receives IRP_MJ_DEVICE_CONTROL + requests from the system. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + Request - Handle to a framework request object. + + OutputBufferLength - length of the request's output buffer, + if an output buffer is available. + InputBufferLength - length of the request's input buffer, + if an input buffer is available. + + IoControlCode - the driver-defined or system-defined I/O control code + (IOCTL) that is associated with the request. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS status= STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + KdPrint(("ToasterEvtIoDeviceControl called\n")); + + PAGED_CODE(); + + // + // Use WdfRequestRetrieveInputBuffer and WdfRequestRetrieveOutputBuffer + // to get the request buffers. + // + + switch (IoControlCode) { + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Complete the Request. + // + WdfRequestCompleteWithInformation(Request, status, (ULONG_PTR) 0); +} + + diff --git a/general/toaster/umdf2/func/simple/wdfsimpleum.inx b/general/toaster/umdf2/func/simple/wdfsimpleum.inx new file mode 100644 index 00000000..6edb9388 --- /dev/null +++ b/general/toaster/umdf2/func/simple/wdfsimpleum.inx @@ -0,0 +1,97 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +;Module Name: +; wdfsimpleum.INF +; +;Abstract: +; INF file for installing the UMDF2 Toaster Driver +; +;Installation Notes: +; Using Devcon: Type "devcon install wdfsimpleum.inf root\toaster" to install +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=TOASTER +ClassGuid={B85B7C50-6A01-11d2-B841-00C04FAD5171} +Provider=%MSFT% +DriverVer=03/20/2003,5.00.3788 +CatalogFile=wudf.cat + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=ToasterClassReg + +[ToasterClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,100 +HKR,,DeviceCharacteristics,0x10001,0x100 ;Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)" ;Allow generic all access to system and built-in Admin. + ;This one overrides the security set by the driver + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +wdfsimpleum.dll = 1,, + +;***************************************** +; Toaster Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +; +; Hw Id is root\toaster +; +[Standard.NT$ARCH$] +%Toaster.DeviceDesc%=Toaster_Device, root\toaster + +;---------------- copy files + +[Toaster_Device.NT] +CopyFiles=UMDriverCopy + +[UMDriverCopy] +wdfsimpleum.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to drivers\umdf + +;-------------- Service installation +[Toaster_Device.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +;-------------- WDF specific section ------------- +[Toaster_Device.NT.Wdf] +UmdfService=wdfsimpleum, Toaster_Install +UmdfServiceOrder=wdfsimpleum + +[Toaster_Install] +UmdfLibraryVersion=$UMDFVERSION$ +ServiceBinary=%12%\UMDF\wdfsimpleum.dll + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +MSFT = "Microsoft" +StdMfg = "(Standard system devices)" +DiskId1 = "WDF Sample Toaster Installation Disk #1" +Toaster.DeviceDesc = "Sample UMDF Toaster Driver - simple" +ClassName = "Toaster" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" + diff --git a/general/toaster/umdf2/func/simple/wdfsimpleum.vcxproj b/general/toaster/umdf2/func/simple/wdfsimpleum.vcxproj new file mode 100644 index 00000000..5420719a --- /dev/null +++ b/general/toaster/umdf2/func/simple/wdfsimpleum.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {4867F54B-D8EF-45B0-984E-D571D987174A} + $(MSBuildProjectName) + 2 + false + true + Debug + Win32 + {04436AEB-A589-41DD-A088-A1503096D2D8} + + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + $(InfArch) + true + .\$(IntDir)\wdfsimpleum.inf + + + + wdfsimpleum + + + wdfsimpleum + + + wdfsimpleum + + + wdfsimpleum + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/umdf2/func/simple/wdfsimpleum.vcxproj.Filters b/general/toaster/umdf2/func/simple/wdfsimpleum.vcxproj.Filters new file mode 100644 index 00000000..6054e358 --- /dev/null +++ b/general/toaster/umdf2/func/simple/wdfsimpleum.vcxproj.Filters @@ -0,0 +1,34 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {00F8EE65-92A7-4127-B44C-E8F26A2136D5} + + + h;hpp;hxx;hm;inl;inc;xsd + {77C5B494-74A7-4FE1-97B8-0B859BE75D43} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9769E294-A7B6-4585-9CA6-AFDEA3DCA9F4} + + + inf;inv;inx;mof;mc; + {18744B3D-F47A-476E-9BF7-FE6592698961} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + \ No newline at end of file diff --git a/general/toaster/umdf2/inc/driver.h b/general/toaster/umdf2/inc/driver.h new file mode 100644 index 00000000..b1073363 --- /dev/null +++ b/general/toaster/umdf2/inc/driver.h @@ -0,0 +1,69 @@ +/*++ +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + driver.h + +Abstract: + + This module contains the common declarations for the + bus, function and filter drivers. + +Environment: + + kernel mode only + +--*/ + +//#include "public.h" + +// +// Define an Interface Guid to access the proprietary toaster interface. +// This guid is used to identify a specific interface in IRP_MN_QUERY_INTERFACE +// handler. +// + +DEFINE_GUID(GUID_TOASTER_INTERFACE_STANDARD, + 0xe0b27630, 0x5434, 0x11d3, 0xb8, 0x90, 0x0, 0xc0, 0x4f, 0xad, 0x51, 0x71); +// {E0B27630-5434-11d3-B890-00C04FAD5171} + + +// +// GUID definition are required to be outside of header inclusion pragma to avoid +// error during precompiled headers. +// + +#ifndef __DRIVER_H +#define __DRIVER_H + +// +// Define Interface reference/dereference routines for +// Interfaces exported by IRP_MN_QUERY_INTERFACE +// + +typedef VOID (*PINTERFACE_REFERENCE)(PVOID Context); +typedef VOID (*PINTERFACE_DEREFERENCE)(PVOID Context); + +typedef +BOOLEAN +(*PTOASTER_GET_CRISPINESS_LEVEL)( + IN PVOID Context, + OUT PUCHAR Level + ); + +typedef +BOOLEAN +(*PTOASTER_SET_CRISPINESS_LEVEL)( + IN PVOID Context, + OUT UCHAR Level + ); + +typedef +BOOLEAN +(*PTOASTER_IS_CHILD_PROTECTED)( + IN PVOID Context + ); + +#endif + diff --git a/general/toaster/umdf2/inc/public.h b/general/toaster/umdf2/inc/public.h new file mode 100644 index 00000000..0628f95f --- /dev/null +++ b/general/toaster/umdf2/inc/public.h @@ -0,0 +1,167 @@ +/*++ +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + public.h + +Abstract: + + This module contains the common declarations shared by driver + and user applications. + +Environment: + + user and kernel + +--*/ + +// +// Define an Interface Guid for bus enumerator class. +// This GUID is used to register (IoRegisterDeviceInterface) +// an instance of an interface so that enumerator application +// can send an ioctl to the bus driver. +// + +DEFINE_GUID (GUID_DEVINTERFACE_BUSENUM_TOASTER, + 0xD35F7840, 0x6A0C, 0x11d2, 0xB8, 0x41, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71); +// {D35F7840-6A0C-11d2-B841-00C04FAD5171} + +// +// Define an Interface Guid for toaster device class. +// This GUID is used to register (IoRegisterDeviceInterface) +// an instance of an interface so that user application +// can control the toaster device. +// + +DEFINE_GUID (GUID_DEVINTERFACE_TOASTER, + 0x781EF630, 0x72B2, 0x11d2, 0xB8, 0x52, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71); +//{781EF630-72B2-11d2-B852-00C04FAD5171} + +// +// Define a Setup Class GUID for Toaster Class. This is same +// as the TOASTSER CLASS guid in the INF files. +// + +DEFINE_GUID (GUID_DEVCLASS_TOASTER, + 0xB85B7C50, 0x6A01, 0x11d2, 0xB8, 0x41, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71); +//{B85B7C50-6A01-11d2-B841-00C04FAD5171} + +// +// Define a WMI GUID to get busenum info. +// + +DEFINE_GUID (TOASTER_BUS_WMI_STD_DATA_GUID, + 0x0006A660, 0x8F12, 0x11d2, 0xB8, 0x54, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71); +//{0006A660-8F12-11d2-B854-00C04FAD5171} + +// +// Define a WMI GUID to get toaster device info. +// + +DEFINE_GUID (TOASTER_WMI_STD_DATA_GUID, + 0xBBA21300L, 0x6DD3, 0x11d2, 0xB8, 0x44, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71); + +// +// Define a WMI GUID to represent device arrival notification WMIEvent class. +// + +DEFINE_GUID (TOASTER_NOTIFY_DEVICE_ARRIVAL_EVENT, + 0x1cdaff1, 0xc901, 0x45b4, 0xb3, 0x59, 0xb5, 0x54, 0x27, 0x25, 0xe2, 0x9c); +// {01CDAFF1-C901-45b4-B359-B5542725E29C} + + +// +// GUID definition are required to be outside of header inclusion pragma to avoid +// error during precompiled headers. +// + +#ifndef __PUBLIC_H +#define __PUBLIC_H + +#define BUS_HARDWARE_IDS L"{B85B7C50-6A01-11d2-B841-00C04FAD5171}\\MsToaster\0" +#define BUS_HARDWARE_IDS_LENGTH sizeof (BUS_HARDWARE_IDS) + +#define BUSENUM_COMPATIBLE_IDS L"{B85B7C50-6A01-11d2-B841-00C04FAD5171}\\MsCompatibleToaster\0" +#define BUSENUM_COMPATIBLE_IDS_LENGTH sizeof(BUSENUM_COMPATIBLE_IDS) + + +#define FILE_DEVICE_BUSENUM FILE_DEVICE_BUS_EXTENDER + +#define BUSENUM_IOCTL(_index_) \ + CTL_CODE (FILE_DEVICE_BUSENUM, _index_, METHOD_BUFFERED, FILE_READ_DATA) + +#define IOCTL_BUSENUM_PLUGIN_HARDWARE BUSENUM_IOCTL (0x0) +#define IOCTL_BUSENUM_UNPLUG_HARDWARE BUSENUM_IOCTL (0x1) +#define IOCTL_BUSENUM_EJECT_HARDWARE BUSENUM_IOCTL (0x2) +#define IOCTL_TOASTER_DONT_DISPLAY_IN_UI_DEVICE BUSENUM_IOCTL (0x3) + +// +// Data structure used in PlugIn and UnPlug ioctls +// + +typedef struct _BUSENUM_PLUGIN_HARDWARE +{ + // + // sizeof (struct _BUSENUM_HARDWARE) + // + IN ULONG Size; + + // + // Unique serial number of the device to be enumerated. + // Enumeration will be failed if another device on the + // bus has the same serail number. + // + + IN ULONG SerialNo; + + // + // An array of (zero terminated wide character strings). The array itself + // also null terminated (ie, MULTI_SZ) + // + #pragma warning(disable:4200) // nonstandard extension used + + IN WCHAR HardwareIDs[]; + + #pragma warning(default:4200) + +} BUSENUM_PLUGIN_HARDWARE, *PBUSENUM_PLUGIN_HARDWARE; + +typedef struct _BUSENUM_UNPLUG_HARDWARE +{ + // + // sizeof (struct _REMOVE_HARDWARE) + // + + IN ULONG Size; + + // + // Serial number of the device to be plugged out + // + + ULONG SerialNo; + + ULONG Reserved[2]; + +} BUSENUM_UNPLUG_HARDWARE, *PBUSENUM_UNPLUG_HARDWARE; + +typedef struct _BUSENUM_EJECT_HARDWARE +{ + // + // sizeof (struct _EJECT_HARDWARE) + // + + IN ULONG Size; + + // + // Serial number of the device to be ejected + // + + ULONG SerialNo; + + ULONG Reserved[2]; + +} BUSENUM_EJECT_HARDWARE, *PBUSENUM_EJECT_HARDWARE; + +#endif + diff --git a/general/toaster/umdf2/umdf2toaster.sln b/general/toaster/umdf2/umdf2toaster.sln new file mode 100644 index 00000000..27a7c11c --- /dev/null +++ b/general/toaster/umdf2/umdf2toaster.sln @@ -0,0 +1,123 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Package", "Package", "{DE39E506-CDA6-472B-AA38-CFD787389792}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notify", "Notify", "{6A99D8D7-8707-44E7-8952-C08DA9607026}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{812F37C3-6174-45B1-BAE8-8A1D22588B5A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toast", "Toast", "{C76A23DC-DCC1-457B-A57C-FC050E0E28E0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Enum", "Enum", "{29F16893-9AB9-45AC-AF5B-4E933C2E6A95}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simple", "Simple", "{5652F0ED-6624-49EC-9DB9-B5A3F7253FA3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Func", "Func", "{2C164544-0A50-43A5-A42C-B7B25D777B4E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Featured", "Featured", "{0213881B-B2CC-453E-BBCA-95EE52FEA9C1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Generic", "Generic", "{F2D765A7-8801-482C-924A-0B647DEAE5D5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{870F719A-95BC-485D-9787-2634A3C2652F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notify", "exe\notify\notify.vcxproj", "{FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "toast", "exe\toast\toast.vcxproj", "{9BF4F988-D8B7-4E73-BC77-6658D413212F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Enum", "exe\enum\Enum.vcxproj", "{6E1593D4-08A1-45E4-A77F-AF34AC6F0D69}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wdfsimpleum", "func\simple\wdfsimpleum.vcxproj", "{4867F54B-D8EF-45B0-984E-D571D987174A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wdffeaturedum", "func\featured\wdffeaturedum.vcxproj", "{99A6009E-6137-462F-89C4-6931045895B1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "filterum", "filter\generic\filterum.vcxproj", "{A60F76D2-C512-4DA6-8C80-263F1B506267}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5}.Debug|Win32.ActiveCfg = Debug|Win32 + {52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5}.Debug|Win32.Build.0 = Debug|Win32 + {52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5}.Release|Win32.ActiveCfg = Release|Win32 + {52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5}.Release|Win32.Build.0 = Release|Win32 + {52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5}.Debug|x64.ActiveCfg = Debug|x64 + {52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5}.Debug|x64.Build.0 = Debug|x64 + {52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5}.Release|x64.ActiveCfg = Release|x64 + {52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5}.Release|x64.Build.0 = Release|x64 + {FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76}.Debug|Win32.ActiveCfg = Debug|Win32 + {FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76}.Debug|Win32.Build.0 = Debug|Win32 + {FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76}.Release|Win32.ActiveCfg = Release|Win32 + {FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76}.Release|Win32.Build.0 = Release|Win32 + {FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76}.Debug|x64.ActiveCfg = Debug|x64 + {FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76}.Debug|x64.Build.0 = Debug|x64 + {FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76}.Release|x64.ActiveCfg = Release|x64 + {FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76}.Release|x64.Build.0 = Release|x64 + {9BF4F988-D8B7-4E73-BC77-6658D413212F}.Debug|Win32.ActiveCfg = Debug|Win32 + {9BF4F988-D8B7-4E73-BC77-6658D413212F}.Debug|Win32.Build.0 = Debug|Win32 + {9BF4F988-D8B7-4E73-BC77-6658D413212F}.Release|Win32.ActiveCfg = Release|Win32 + {9BF4F988-D8B7-4E73-BC77-6658D413212F}.Release|Win32.Build.0 = Release|Win32 + {9BF4F988-D8B7-4E73-BC77-6658D413212F}.Debug|x64.ActiveCfg = Debug|x64 + {9BF4F988-D8B7-4E73-BC77-6658D413212F}.Debug|x64.Build.0 = Debug|x64 + {9BF4F988-D8B7-4E73-BC77-6658D413212F}.Release|x64.ActiveCfg = Release|x64 + {9BF4F988-D8B7-4E73-BC77-6658D413212F}.Release|x64.Build.0 = Release|x64 + {6E1593D4-08A1-45E4-A77F-AF34AC6F0D69}.Debug|Win32.ActiveCfg = Debug|Win32 + {6E1593D4-08A1-45E4-A77F-AF34AC6F0D69}.Debug|Win32.Build.0 = Debug|Win32 + {6E1593D4-08A1-45E4-A77F-AF34AC6F0D69}.Release|Win32.ActiveCfg = Release|Win32 + {6E1593D4-08A1-45E4-A77F-AF34AC6F0D69}.Release|Win32.Build.0 = Release|Win32 + {6E1593D4-08A1-45E4-A77F-AF34AC6F0D69}.Debug|x64.ActiveCfg = Debug|x64 + {6E1593D4-08A1-45E4-A77F-AF34AC6F0D69}.Debug|x64.Build.0 = Debug|x64 + {6E1593D4-08A1-45E4-A77F-AF34AC6F0D69}.Release|x64.ActiveCfg = Release|x64 + {6E1593D4-08A1-45E4-A77F-AF34AC6F0D69}.Release|x64.Build.0 = Release|x64 + {4867F54B-D8EF-45B0-984E-D571D987174A}.Debug|Win32.ActiveCfg = Debug|Win32 + {4867F54B-D8EF-45B0-984E-D571D987174A}.Debug|Win32.Build.0 = Debug|Win32 + {4867F54B-D8EF-45B0-984E-D571D987174A}.Release|Win32.ActiveCfg = Release|Win32 + {4867F54B-D8EF-45B0-984E-D571D987174A}.Release|Win32.Build.0 = Release|Win32 + {4867F54B-D8EF-45B0-984E-D571D987174A}.Debug|x64.ActiveCfg = Debug|x64 + {4867F54B-D8EF-45B0-984E-D571D987174A}.Debug|x64.Build.0 = Debug|x64 + {4867F54B-D8EF-45B0-984E-D571D987174A}.Release|x64.ActiveCfg = Release|x64 + {4867F54B-D8EF-45B0-984E-D571D987174A}.Release|x64.Build.0 = Release|x64 + {99A6009E-6137-462F-89C4-6931045895B1}.Debug|Win32.ActiveCfg = Debug|Win32 + {99A6009E-6137-462F-89C4-6931045895B1}.Debug|Win32.Build.0 = Debug|Win32 + {99A6009E-6137-462F-89C4-6931045895B1}.Release|Win32.ActiveCfg = Release|Win32 + {99A6009E-6137-462F-89C4-6931045895B1}.Release|Win32.Build.0 = Release|Win32 + {99A6009E-6137-462F-89C4-6931045895B1}.Debug|x64.ActiveCfg = Debug|x64 + {99A6009E-6137-462F-89C4-6931045895B1}.Debug|x64.Build.0 = Debug|x64 + {99A6009E-6137-462F-89C4-6931045895B1}.Release|x64.ActiveCfg = Release|x64 + {99A6009E-6137-462F-89C4-6931045895B1}.Release|x64.Build.0 = Release|x64 + {A60F76D2-C512-4DA6-8C80-263F1B506267}.Debug|Win32.ActiveCfg = Debug|Win32 + {A60F76D2-C512-4DA6-8C80-263F1B506267}.Debug|Win32.Build.0 = Debug|Win32 + {A60F76D2-C512-4DA6-8C80-263F1B506267}.Release|Win32.ActiveCfg = Release|Win32 + {A60F76D2-C512-4DA6-8C80-263F1B506267}.Release|Win32.Build.0 = Release|Win32 + {A60F76D2-C512-4DA6-8C80-263F1B506267}.Debug|x64.ActiveCfg = Debug|x64 + {A60F76D2-C512-4DA6-8C80-263F1B506267}.Debug|x64.Build.0 = Debug|x64 + {A60F76D2-C512-4DA6-8C80-263F1B506267}.Release|x64.ActiveCfg = Release|x64 + {A60F76D2-C512-4DA6-8C80-263F1B506267}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {52EEA7C4-68B9-4AE9-B3EC-881A49E9E1E5} = {DE39E506-CDA6-472B-AA38-CFD787389792} + {FEBBDE46-4BD4-462D-87BD-8FDC8C1ECB76} = {6A99D8D7-8707-44E7-8952-C08DA9607026} + {9BF4F988-D8B7-4E73-BC77-6658D413212F} = {C76A23DC-DCC1-457B-A57C-FC050E0E28E0} + {6E1593D4-08A1-45E4-A77F-AF34AC6F0D69} = {29F16893-9AB9-45AC-AF5B-4E933C2E6A95} + {4867F54B-D8EF-45B0-984E-D571D987174A} = {5652F0ED-6624-49EC-9DB9-B5A3F7253FA3} + {99A6009E-6137-462F-89C4-6931045895B1} = {0213881B-B2CC-453E-BBCA-95EE52FEA9C1} + {A60F76D2-C512-4DA6-8C80-263F1B506267} = {F2D765A7-8801-482C-924A-0B647DEAE5D5} + {6A99D8D7-8707-44E7-8952-C08DA9607026} = {812F37C3-6174-45B1-BAE8-8A1D22588B5A} + {C76A23DC-DCC1-457B-A57C-FC050E0E28E0} = {812F37C3-6174-45B1-BAE8-8A1D22588B5A} + {29F16893-9AB9-45AC-AF5B-4E933C2E6A95} = {812F37C3-6174-45B1-BAE8-8A1D22588B5A} + {5652F0ED-6624-49EC-9DB9-B5A3F7253FA3} = {2C164544-0A50-43A5-A42C-B7B25D777B4E} + {0213881B-B2CC-453E-BBCA-95EE52FEA9C1} = {2C164544-0A50-43A5-A42C-B7B25D777B4E} + {F2D765A7-8801-482C-924A-0B647DEAE5D5} = {870F719A-95BC-485D-9787-2634A3C2652F} + EndGlobalSection +EndGlobal diff --git a/general/tracing/SystemTraceControl/ReadMe.md b/general/tracing/SystemTraceControl/ReadMe.md new file mode 100644 index 00000000..f581e52e --- /dev/null +++ b/general/tracing/SystemTraceControl/ReadMe.md @@ -0,0 +1,11 @@ +SystemTraceProvider +=================== + +This sample application demonstrates how to use event tracing control APIs to collect events from the system trace provider. + +The sample code provided shows how to start an [Event Tracing](http://msdn.microsoft.com/en-us/library/windows/hardware/bb968803) for Windows trace session and how to enable system events with stacks. When you build and run the application, it collects the trace data for 30 seconds and then stops. The sample application writes the results to a file, Systemtrace.etl. For more information, see [Tools for Software Tracing](http://msdn.microsoft.com/en-us/library/windows/hardware/ff552961). + +You can process the Systemtrace.etl file using Tracerpt.exe. Tracerpt.exe is a command-line trace tool that formats trace events. It also analyzes the events and generates summary reports. Tracerpt is included in Windows XP and later versions of Windows. For more information about how to use this tool, see [Tracerpt](http://go.microsoft.com/fwlink/p/?linkid=179389) topic on the TechNet website. + +You can also process the file using the [Windows Performance Toolkit](http://go.microsoft.com/fwlink/p/?linkid=250774) (WPT), which is available in the SDK. + diff --git a/general/tracing/SystemTraceControl/ReadMe.txt b/general/tracing/SystemTraceControl/ReadMe.txt new file mode 100644 index 00000000..54609bd7 --- /dev/null +++ b/general/tracing/SystemTraceControl/ReadMe.txt @@ -0,0 +1,36 @@ +EventTracing SystemTraceProvider control sample +==================================================================================== +This sample demonstrates how to use event tracing control API's to collect events +from system trace provider. The code provided will start an ETW system trace and +enable system events with stacks. After collecting the data for 30 seconds trace +will be stopped. Resulting file (systemtrace.etl) can be processed with +inbox tracerpt.exe, programmatically (OpenTrace/ProcessTrace/CloseTrace) or using +WPT (Windows Performance Toolkit) available in the SDK. + +Sample Language Implementations +=============================== +C++ + +Files +================================================= +SystemTraceProvider.sln +SystemTraceProvider.vcxproj +SystemTraceProvider.cpp +sources +ReadMe.txt + +To build the sample using the command prompt: +============================================= + 1. Open the Command Prompt window and navigate to the directory. + 2. Type msbuild SystemTraceControl.sln. + +To build the sample using Visual Studio (preferred method): +================================================ + 1. Open File Explorer and navigate to the SystemTraceControl directory. + 2. Double-click the icon for the .sln (solution) file to open the file in Visual Studio. + 3. In the Build menu, select Build Solution. The application will be built in the default \Debug or \Release directory. + +To run the sample: +================= + 1. Navigate to the directory that contains the new executable, using the command prompt or File Explorer. + 2. Type SystemTraceControl.exe at the command line, or double-click the icon for SystemTraceControl.exe to launch it from File Explorer. \ No newline at end of file diff --git a/general/tracing/SystemTraceControl/SystemTraceControl.cpp b/general/tracing/SystemTraceControl/SystemTraceControl.cpp new file mode 100644 index 00000000..a8c55026 --- /dev/null +++ b/general/tracing/SystemTraceControl/SystemTraceControl.cpp @@ -0,0 +1,221 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + SystemTraceControl.cpp + +Abstract: + + This sample demonstrates how to collect events from SystemTraceProvider + on Windows 8. + +Environment: + + User mode only. + +--*/ + +#define INITGUID +#include +#include +#include +#include +#include + +#define MAXIMUM_SESSION_NAME 1024 + +// +// Guid definitions from "NT Kernel Logger Constants" section on MSDN. +// + +DEFINE_GUID ( /* 3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c */ + ProcessGuid, + 0x3d6fa8d0, + 0xfe05, + 0x11d0, + 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c + ); + +DEFINE_GUID ( /* 2cb15d1d-5fc1-11d2-abe1-00a0c911f518 */ + ImageLoadGuid, + 0x2cb15d1d, + 0x5fc1, + 0x11d2, + 0xab, 0xe1, 0x00, 0xa0, 0xc9, 0x11, 0xf5, 0x18 + ); + +PEVENT_TRACE_PROPERTIES +AllocateTraceProperties ( + _In_opt_ PWSTR LoggerName, + _In_opt_ PWSTR LogFileName + ) +{ + PEVENT_TRACE_PROPERTIES TraceProperties = NULL; + ULONG BufferSize; + + BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + + (MAXIMUM_SESSION_NAME + MAX_PATH) * sizeof(WCHAR); + + TraceProperties = (PEVENT_TRACE_PROPERTIES)malloc(BufferSize); + if (TraceProperties == NULL) { + wprintf(L"Unable to allocate %d bytes for properties structure.\n", BufferSize); + goto Exit; + } + + // + // Set the session properties. + // + + ZeroMemory(TraceProperties, BufferSize); + TraceProperties->Wnode.BufferSize = BufferSize; + TraceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + TraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + TraceProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + + (MAXIMUM_SESSION_NAME * sizeof(WCHAR)); + + if (LoggerName != NULL) { + StringCchCopy((LPWSTR)((PCHAR)TraceProperties + TraceProperties->LoggerNameOffset), + MAXIMUM_SESSION_NAME, + LoggerName); + } + + if (LogFileName != NULL) { + StringCchCopy((LPWSTR)((PCHAR)TraceProperties + TraceProperties->LogFileNameOffset), + MAX_PATH, + LogFileName); + } + +Exit: + return TraceProperties; +} + +VOID +FreeTraceProperties ( + _In_ PEVENT_TRACE_PROPERTIES TraceProperties + ) +{ + free(TraceProperties); + return; +} + +int +__cdecl +wmain() +{ + CLASSIC_EVENT_ID EventId[2]; + ULONG Status = ERROR_SUCCESS; + TRACEHANDLE SessionHandle = 0; + PEVENT_TRACE_PROPERTIES TraceProperties; + ULONG SystemTraceFlags[8]; + PWSTR LoggerName = L"MyTrace"; + + HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); + + // + // Allocate EVENT_TRACE_PROPERTIES structure and perform some + // basic initialization. + // + // N.B. LoggerName will be populated during StartTrace call. + // + + TraceProperties = AllocateTraceProperties(NULL, L"SystemTrace.etl"); + if (TraceProperties == NULL) { + Status = ERROR_OUTOFMEMORY; + goto Exit; + } + + // + // Configure additinal trace settings. + // + + TraceProperties->LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL | EVENT_TRACE_SYSTEM_LOGGER_MODE; + TraceProperties->Wnode.ClientContext = 1; // Use QueryPerformanceCounter for time stamps + TraceProperties->MaximumFileSize = 100; // Limit file size to 100MB max + TraceProperties->BufferSize = 512; // Use 512KB trace buffers + TraceProperties->MinimumBuffers = 64; + TraceProperties->MaximumBuffers = 128; + + // + // Start trace session which can receive events from SystemTraceProvider. + // + + Status = StartTrace(&SessionHandle, LoggerName, TraceProperties); + if (Status != ERROR_SUCCESS) { + wprintf(L"StartTrace() failed with %lu\n", Status); + goto Exit; + } + + // + // Configure stack walking. In this example stack traces will be collected on + // ImageLoad and ProcessCreate events. + // + // N.B. Stack tracing is configured before enabling event collection. + // + + ZeroMemory(EventId, sizeof(EventId)); + EventId[0].EventGuid = ImageLoadGuid; + EventId[0].Type = EVENT_TRACE_TYPE_LOAD; + EventId[1].EventGuid = ProcessGuid; + EventId[1].Type = EVENT_TRACE_TYPE_START; + + Status = TraceSetInformation(SessionHandle, + TraceStackTracingInfo, + EventId, + sizeof(EventId)); + + if (Status != ERROR_SUCCESS) { + wprintf(L"TraceSetInformation(StackTracing) failed with %lu\n", Status); + goto Exit; + } + + // + // Enable system events for Process, Thread and Loader groups. + // + + ZeroMemory(SystemTraceFlags, sizeof(SystemTraceFlags)); + SystemTraceFlags[0] = (EVENT_TRACE_FLAG_PROCESS | + EVENT_TRACE_FLAG_THREAD | + EVENT_TRACE_FLAG_IMAGE_LOAD); + + Status = TraceSetInformation(SessionHandle, + TraceSystemTraceEnableFlagsInfo, + SystemTraceFlags, + sizeof(SystemTraceFlags)); + + if (Status != ERROR_SUCCESS) { + wprintf(L"TraceSetInformation(EnableFlags) failed with %lu\n", Status); + goto Exit; + } + + // + // Collect trace for 30 seconds. + // + + Sleep(30 * 1000); + +Exit: + + // + // Stop tracing. + // + + if (SessionHandle != 0) { + Status = ControlTrace(SessionHandle, NULL, TraceProperties, EVENT_TRACE_CONTROL_STOP); + if (Status != ERROR_SUCCESS) { + wprintf(L"StopTrace() failed with %lu\n", Status); + } + } + + if (TraceProperties != NULL) { + FreeTraceProperties(TraceProperties); + } + + return Status; +} diff --git a/general/tracing/SystemTraceControl/SystemTraceControl.sln b/general/tracing/SystemTraceControl/SystemTraceControl.sln new file mode 100644 index 00000000..4e74108b --- /dev/null +++ b/general/tracing/SystemTraceControl/SystemTraceControl.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SystemTraceControl", "SystemTraceControl.vcxproj", "{BBB08463-9C86-4690-B95B-106B49DD46E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BBB08463-9C86-4690-B95B-106B49DD46E2}.Debug|Win32.ActiveCfg = Debug|Win32 + {BBB08463-9C86-4690-B95B-106B49DD46E2}.Debug|Win32.Build.0 = Debug|Win32 + {BBB08463-9C86-4690-B95B-106B49DD46E2}.Release|Win32.ActiveCfg = Release|Win32 + {BBB08463-9C86-4690-B95B-106B49DD46E2}.Release|Win32.Build.0 = Release|Win32 + {BBB08463-9C86-4690-B95B-106B49DD46E2}.Debug|x64.ActiveCfg = Debug|x64 + {BBB08463-9C86-4690-B95B-106B49DD46E2}.Debug|x64.Build.0 = Debug|x64 + {BBB08463-9C86-4690-B95B-106B49DD46E2}.Release|x64.ActiveCfg = Release|x64 + {BBB08463-9C86-4690-B95B-106B49DD46E2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj b/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj new file mode 100644 index 00000000..556e6d7d --- /dev/null +++ b/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {BBB08463-9C86-4690-B95B-106B49DD46E2} + $(MSBuildProjectName) + Debug + Win32 + {E8A8798D-133D-48CA-B07A-E8D8A7C82C30} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + SystemTraceControl + + + SystemTraceControl + + + SystemTraceControl + + + SystemTraceControl + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + true + Level4 + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + true + Level4 + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + true + Level4 + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + true + Level4 + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(SDK_LIB_PATH) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj.Filters b/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj.Filters new file mode 100644 index 00000000..bc043bf7 --- /dev/null +++ b/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C2A52F8F-D414-40D4-998E-62C57FFF543E} + + + h;hpp;hxx;hm;inl;inc;xsd + {CF32093A-DFB7-4C18-B086-1F1DB68AA8F4} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {C370B48B-D85C-4319-911D-CC6D213BB287} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj b/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj new file mode 100644 index 00000000..38a37c6d --- /dev/null +++ b/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj @@ -0,0 +1,195 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {71F967BF-4410-49F0-A9D9-A0968791E7C3} + $(MSBuildProjectName) + Debug + Win32 + {D1927512-3AD6-432A-B43A-993734933EA0} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + .\$(IntDir) + true + "$(SDK_INC_PATH)\winmeta.xml" + .\$(IntDir) + true + evntdrvEvents + true + + + + Eventdrv + + + Eventdrv + + + Eventdrv + + + Eventdrv + + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + true + Level4 + + + %(AdditionalIncludeDirectories);. + + + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + true + Level4 + + + %(AdditionalIncludeDirectories);. + + + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + true + Level4 + + + %(AdditionalIncludeDirectories);. + + + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + true + Level4 + + + %(AdditionalIncludeDirectories);. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj.Filters b/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj.Filters new file mode 100644 index 00000000..b3a19f20 --- /dev/null +++ b/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj.Filters @@ -0,0 +1,34 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {FF39F92A-78D6-4DC4-8C57-61B8235F689E} + + + h;hpp;hxx;hm;inl;inc;xsd + {7D82ACC1-5CB5-4731-9F63-C6ECF120023F} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {C4F65E57-AD43-415D-A828-20341B0A35CA} + + + inf;inv;inx;mof;mc; + {42E3A915-3C68-4861-986D-5BECD364D878} + + + + + Resource Files + + + Resource Files + + + + + Source Files + + + \ No newline at end of file diff --git a/general/tracing/evntdrv/Eventdrv/drvioctl.h b/general/tracing/evntdrv/Eventdrv/drvioctl.h new file mode 100644 index 00000000..2185e451 --- /dev/null +++ b/general/tracing/evntdrv/Eventdrv/drvioctl.h @@ -0,0 +1,34 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + + +Module Name: + + drvioctl.h + +Abstract: + + Definitions of IOCTL codes and data structures exported by TRACEDRV. + + +--*/ + +#ifndef __EVENTKMP_IOCTL__ +#define __EVENTKMP_IOCTL__ + +// +// IOCTL control codes +// +#define IOCTL_EVNTKMP_TRACE_EVENT_A \ + CTL_CODE( FILE_DEVICE_UNKNOWN, 0x801, \ + METHOD_BUFFERED, FILE_ANY_ACCESS ) + +#endif // __EVENTKMP_IOCTL__ + + diff --git a/general/tracing/evntdrv/Eventdrv/evntdrv.c b/general/tracing/evntdrv/Eventdrv/evntdrv.c new file mode 100644 index 00000000..ad8b091d --- /dev/null +++ b/general/tracing/evntdrv/Eventdrv/evntdrv.c @@ -0,0 +1,345 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + + +Module Name: + + evntdrv.c + +Abstract: + + Sample kernel mode trace provider/driver. + + +--*/ +#include +#include +#include "drvioctl.h" + + +// +// evntdrvEvents.h is generated by MC.exe with the -km option, +// using the manifest evntdrv.htm. +// The file contains a macro per event, and the required code to raise the +// event. +#include "evntdrvEvents.h" + + +DRIVER_UNLOAD EventDrvDriverUnload; + +_Dispatch_type_(IRP_MJ_CREATE) +_Dispatch_type_(IRP_MJ_CLOSE) +DRIVER_DISPATCH EventDrvDispatchOpenClose; + +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) +DRIVER_DISPATCH EventDrvDispatchDeviceControl; + +#define EventDrv_NT_DEVICE_NAME L"\\Device\\EventEtw" +#define EventDrv_WIN32_DEVICE_NAME L"\\DosDevices\\EVENTETW" + +DRIVER_INITIALIZE DriverEntry; + +NTSTATUS +EventDrvDispatchOpenClose( + IN PDEVICE_OBJECT pDO, + IN PIRP Irp + ); + +NTSTATUS +EventDrvDispatchDeviceControl( + IN PDEVICE_OBJECT pDO, + IN PIRP Irp + ); + +VOID +EventDrvDriverUnload( + IN PDRIVER_OBJECT DriverObject + ); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, DriverEntry ) +#pragma alloc_text( PAGE, EventDrvDispatchOpenClose ) +#pragma alloc_text( PAGE, EventDrvDispatchDeviceControl ) +#pragma alloc_text( PAGE, EventDrvDriverUnload ) +#endif // ALLOC_PRAGMA + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path + to driver-specific key in the registry + +Return Value: + + STATUS_SUCCESS if successful + STATUS_UNSUCCESSFUL otherwise + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + UNICODE_STRING DeviceName; + UNICODE_STRING LinkName; + PDEVICE_OBJECT EventDrvDeviceObject; + WCHAR DeviceNameString[128]; + ULONG LengthToCopy = 128 * sizeof(WCHAR); + UNREFERENCED_PARAMETER (RegistryPath); + + KdPrint(("EventDrv: DriverEntry\n")); + + // + // Create Dispatch Entry Points. + // + DriverObject->DriverUnload = EventDrvDriverUnload; + DriverObject->MajorFunction[ IRP_MJ_CREATE ] = EventDrvDispatchOpenClose; + DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = EventDrvDispatchOpenClose; + DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = EventDrvDispatchDeviceControl; + + RtlInitUnicodeString( &DeviceName, EventDrv_NT_DEVICE_NAME ); + + // + // Create the Device object + // + Status = IoCreateDevice( + DriverObject, + 0, + &DeviceName, + FILE_DEVICE_UNKNOWN, + 0, + FALSE, + &EventDrvDeviceObject); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + RtlInitUnicodeString( &LinkName, EventDrv_WIN32_DEVICE_NAME ); + Status = IoCreateSymbolicLink( &LinkName, &DeviceName ); + + if ( !NT_SUCCESS( Status )) { + IoDeleteDevice( EventDrvDeviceObject ); + return Status; + } + + + // + // Choose a buffering mechanism + // + EventDrvDeviceObject->Flags |= DO_BUFFERED_IO; + + + // + // Register with ETW + // + EventRegisterSample_Driver(); + + // + // Log an Event with : DeviceNameLength + // DeviceName + // Status + // + + // Copy the device name into the WCHAR local buffer in order + // to place a NULL character at the end, since this field is + // defined in the manifest as a NULL-terminated string + + if (DeviceName.Length <= 128 * sizeof(WCHAR)) { + + LengthToCopy = DeviceName.Length; + + } + + RtlCopyMemory(DeviceNameString, + DeviceName.Buffer, + LengthToCopy); + + DeviceNameString[LengthToCopy/sizeof(WCHAR)] = L'\0'; + + EventWriteStartEvent(NULL, DeviceName.Length, DeviceNameString, Status); + + + return STATUS_SUCCESS; +} + +NTSTATUS +EventDrvDispatchOpenClose( + IN PDEVICE_OBJECT pDO, + IN PIRP Irp + ) +/*++ + +Routine Description: + + Dispatch routine to handle Create/Close IRPs. + +Arguments: + + DeviceObject - pointer to a device object. + + Irp - pointer to an I/O Request Packet. + +Return Value: + + NT status code + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER (pDO); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + return STATUS_SUCCESS; +} + + +NTSTATUS +EventDrvDispatchDeviceControl( + IN PDEVICE_OBJECT pDO, + IN PIRP Irp + ) +/*++ + +Routine Description: + + Dispatch routine to handle IOCTL IRPs. + +Arguments: + + DeviceObject - pointer to a device object. + + Irp - pointer to an I/O Request Packet. + +Return Value: + + NT Status code + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp ); + ULONG ControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER (pDO); + + Irp->IoStatus.Information = + irpStack->Parameters.DeviceIoControl.OutputBufferLength; + + switch ( ControlCode ) { + case IOCTL_EVNTKMP_TRACE_EVENT_A: + { + + EventWriteSampleEventA(NULL); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + break; + } + + default: + + // + // Not one we recognize. Error. + // + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + + break; + } + + // + // Get rid of this request + // + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + return Status; +} + + +VOID +EventDrvDriverUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + Free all the resources allocated in DriverEntry. + +Arguments: + + DriverObject - pointer to a driver object. + +Return Value: + + VOID. + +--*/ +{ + PDEVICE_OBJECT DevObj; + UNICODE_STRING LinkName; + + PAGED_CODE(); + + KdPrint(("EventDrv: Unloading \n")); + + // + // Get pointer to Device object + // + DevObj = DriverObject->DeviceObject; + + EventWriteUnloadEvent(NULL, DevObj); + + // + // Unregister the driver as an ETW provider + // + EventUnregisterSample_Driver(); + + + // + // Form the Win32 symbolic link name. + // + RtlInitUnicodeString( &LinkName, EventDrv_WIN32_DEVICE_NAME ); + + // + // Remove symbolic link from Object + // namespace... + // + IoDeleteSymbolicLink( &LinkName ); + + // + // Unload the callbacks from the kernel to this driver + // + IoDeleteDevice( DevObj ); + +} + + diff --git a/general/tracing/evntdrv/Eventdrv/evntdrv.xml b/general/tracing/evntdrv/Eventdrv/evntdrv.xml new file mode 100644 index 00000000..23cff805 --- /dev/null +++ b/general/tracing/evntdrv/Eventdrv/evntdrv.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/general/tracing/evntdrv/Eventdrv/evntdrvevents.rc b/general/tracing/evntdrv/Eventdrv/evntdrvevents.rc new file mode 100644 index 00000000..1ee5105a --- /dev/null +++ b/general/tracing/evntdrv/Eventdrv/evntdrvevents.rc @@ -0,0 +1,3 @@ +LANGUAGE 0x9,0x1 +1 11 "evntdrvEvents_MSG00001.bin" +1 WEVT_TEMPLATE "evntdrvEventsTEMP.BIN" diff --git a/general/tracing/evntdrv/ReadMe.md b/general/tracing/evntdrv/ReadMe.md new file mode 100644 index 00000000..f5780ef6 --- /dev/null +++ b/general/tracing/evntdrv/ReadMe.md @@ -0,0 +1,62 @@ +Eventdrv +======== + +Eventdrv is a sample kernel-mode trace provider and driver. The driver does not control any hardware; it simply generates trace events. It is designed to demonstrate the use of the [Event Tracing for Windows (ETW)](http://msdn.microsoft.com/en-us/library/windows/hardware/ff545699) API in a driver. + +Evntdrv registers as a provider by calling the [**EtwRegister**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff545603) API. If the registration is successful, it logs a StartEvent with the device's name, the length of the name, and the status code. Then, when the sample receives a DeviceIOControl call, it logs a SampleEventA event. Finally, when the driver gets unloaded, it logs an UnloadEvent event with a pointer to the device object + +**Note**  The Windows Pre-Processor (WPP) Tracing tools such as TraceView.exe cannot be used to start, stop, or view traces. + + +Run the sample +-------------- + +1. Install the manifest (Evntdrv.xml), which is located in the Evntdrv\\Eventdrv folder. Open a Visual Studio Command window (Run as administrator) and use the following command: + + ``` {.syntax xml:space="preserve"} + wevtutil im evntdrv.xml + ``` + + Installing the manifest creates registry keys that enable tools to find the resource and message files that contain event provider information. For further details about the WevtUtil.exe tool, see the MSDN Library. + + **Note**  Using a Visual Studio Command windows sets up the environment variables you need to run the tracing tools for this sample. + +2. Make a folder in the system directory called ETWDriverSample (for example, C:\\ETWDriverSample). + + Copy Eventdrv.sys and Evntctrl.exe to the ETWDriverSample folder. + + The ETWDriverSample directory must be created because the path to the resource file that is specified in the evntdrv.xml manifest points to the %SystemRoot%\\ETWDriverSample folder. If this folder is not created and the Eventdrv.sys binary is not copied, decoding tools cannot find the event information to decode the trace file. + +3. Use Tracelog to start a trace session that is called "TestEventdrv." The following command starts the trace session and creates a trace log file, Eventdrv.etl, in the local directory. + + ``` {.syntax xml:space="preserve"} + Tracelog -start TestEventdrv -guid #b5a0bda9-50fe-4d0e-a83d-bae3f58c94d6 -f Eventdrv.etl + ``` + +4. To generate trace messages, run Evntctrl.exe. Each time you type a character other than **Q** or **q**, Evntctrl sends an IOCTL to the driver that signals it to generate trace messages. To stop Evntctrl, type **Q** or **q**. + +5. To stop the trace session, run the following command: + + ``` {.syntax xml:space="preserve"} + tracelog -stop TestEventdrv + ``` + +6. To display the traces collected in the Tracedrv.etl file, run the following command: + + ``` {.syntax xml:space="preserve"} + tracerpt Eventdrv.etl + ``` + + This command creates two files: Summary.txt and Dumpfile.xml. Dumpfile.xml will contain the event information in an XML format. + +7. To uninstall the manifest, run the following command: + + ``` {.syntax xml:space="preserve"} + wevtutil um evntdrv.xml + ``` + +Notes +----- + +If you are building the Eventdrv sample to test on a 64-bit version of Windows, you need to sign the driver. Starting with Windows Vista, all 64-bit versions of Windows require driver code to have a digital signature for the driver to load. See [Signing a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554809) and [Signing a Driver During Development and Testing](http://msdn.microsoft.com/en-us/library/windows/hardware/hh967733). You might also need to configure the test computer so that it can load test-signed kernel mode code, see [The TESTSIGNING Boot Configuration Option](http://msdn.microsoft.com/en-us/library/windows/hardware/ff553484) and [**BCDEdit /set**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff542202). + diff --git a/general/tracing/evntdrv/eventdrv.sln b/general/tracing/evntdrv/eventdrv.sln new file mode 100644 index 00000000..f7556ef7 --- /dev/null +++ b/general/tracing/evntdrv/eventdrv.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Eventdrv", "Eventdrv", "{622DAC05-C30E-4BEE-B268-690B1B0DA989}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Evntctrl", "Evntctrl", "{90152226-E1D1-482C-9AB8-701B24BA34A1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Eventdrv", "Eventdrv\Eventdrv.vcxproj", "{71F967BF-4410-49F0-A9D9-A0968791E7C3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "evntctrl", "evntctrl\evntctrl.vcxproj", "{09705E95-23AF-44BE-AD49-703F2A469DB0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {71F967BF-4410-49F0-A9D9-A0968791E7C3}.Debug|Win32.ActiveCfg = Debug|Win32 + {71F967BF-4410-49F0-A9D9-A0968791E7C3}.Debug|Win32.Build.0 = Debug|Win32 + {71F967BF-4410-49F0-A9D9-A0968791E7C3}.Release|Win32.ActiveCfg = Release|Win32 + {71F967BF-4410-49F0-A9D9-A0968791E7C3}.Release|Win32.Build.0 = Release|Win32 + {71F967BF-4410-49F0-A9D9-A0968791E7C3}.Debug|x64.ActiveCfg = Debug|x64 + {71F967BF-4410-49F0-A9D9-A0968791E7C3}.Debug|x64.Build.0 = Debug|x64 + {71F967BF-4410-49F0-A9D9-A0968791E7C3}.Release|x64.ActiveCfg = Release|x64 + {71F967BF-4410-49F0-A9D9-A0968791E7C3}.Release|x64.Build.0 = Release|x64 + {09705E95-23AF-44BE-AD49-703F2A469DB0}.Debug|Win32.ActiveCfg = Debug|Win32 + {09705E95-23AF-44BE-AD49-703F2A469DB0}.Debug|Win32.Build.0 = Debug|Win32 + {09705E95-23AF-44BE-AD49-703F2A469DB0}.Release|Win32.ActiveCfg = Release|Win32 + {09705E95-23AF-44BE-AD49-703F2A469DB0}.Release|Win32.Build.0 = Release|Win32 + {09705E95-23AF-44BE-AD49-703F2A469DB0}.Debug|x64.ActiveCfg = Debug|x64 + {09705E95-23AF-44BE-AD49-703F2A469DB0}.Debug|x64.Build.0 = Debug|x64 + {09705E95-23AF-44BE-AD49-703F2A469DB0}.Release|x64.ActiveCfg = Release|x64 + {09705E95-23AF-44BE-AD49-703F2A469DB0}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {71F967BF-4410-49F0-A9D9-A0968791E7C3} = {622DAC05-C30E-4BEE-B268-690B1B0DA989} + {09705E95-23AF-44BE-AD49-703F2A469DB0} = {90152226-E1D1-482C-9AB8-701B24BA34A1} + EndGlobalSection +EndGlobal diff --git a/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj b/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj new file mode 100644 index 00000000..91610949 --- /dev/null +++ b/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {09705E95-23AF-44BE-AD49-703F2A469DB0} + $(MSBuildProjectName) + Debug + Win32 + {4CB0AEF6-22DB-46DE-B86C-BA8FA83720FF} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + evntctrl + + + evntctrl + + + evntctrl + + + evntctrl + + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + %(AdditionalIncludeDirectories);.;..\Eventdrv + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj.Filters b/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj.Filters new file mode 100644 index 00000000..fc9905ff --- /dev/null +++ b/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {CBA4CFF0-1A7A-4894-AC68-18D38BD5EABB} + + + h;hpp;hxx;hm;inl;inc;xsd + {6EB53DDD-D4F4-4C46-9218-63E8B1F46F74} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {479AEB6E-676D-4829-891B-3C82EADD4F94} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/tracing/evntdrv/evntctrl/install.c b/general/tracing/evntdrv/evntctrl/install.c new file mode 100644 index 00000000..9f44073b --- /dev/null +++ b/general/tracing/evntdrv/evntctrl/install.c @@ -0,0 +1,499 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + install.c + +Abstract: + + Win32 routines to dynamically load and unload a Windows NT kernel-mode + driver using the Service Control Manager APIs. + +Environment: + + User mode only + + +--*/ +#define UNICODE +#define _UNICODE +#include +#include +#include +#include + +#include "install.h" + + +BOOLEAN +InstallDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName, + IN LPCTSTR ServiceExe + ); + + +BOOLEAN +RemoveDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ); + +BOOLEAN +StartDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ); + +BOOLEAN +StopDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ); + +BOOLEAN +InstallDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName, + IN LPCTSTR ServiceExe + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + SC_HANDLE schService; + DWORD err; + + // + // NOTE: This creates an entry for a standalone driver. If this + // is modified for use with a driver that requires a Tag, + // Group, and/or Dependencies, it may be necessary to + // query the registry for existing driver information + // (in order to determine a unique Tag, etc.). + // + + // + // Create a new a service object. + // + + schService = CreateService(SchSCManager, // handle of service control manager database + DriverName, // address of name of service to start + DriverName, // address of display name + SERVICE_ALL_ACCESS, // type of access to service + SERVICE_KERNEL_DRIVER, // type of service + SERVICE_DEMAND_START, // when to start service + SERVICE_ERROR_NORMAL, // severity if service fails to start + ServiceExe, // address of name of binary file + NULL, // service does not belong to a group + NULL, // no tag requested + NULL, // no dependency names + NULL, // use LocalSystem account + NULL // no password for service account + ); + + if (schService == NULL) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_EXISTS) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + _tprintf(_T("CreateService failed! Error = %d \n"), err ); + + // + // Indicate an error. + // + + return FALSE; + } + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + // + // Indicate success. + // + + return TRUE; + +} // InstallDriver + +BOOLEAN +ManageDriver( + IN LPCTSTR DriverName, + IN LPCTSTR ServiceName, + IN USHORT Function + ) +{ + + SC_HANDLE schSCManager; + + BOOLEAN rCode = TRUE; + + // + // Insure (somewhat) that the driver and service names are valid. + // + + if (!DriverName || !ServiceName) { + + _tprintf(_T("Invalid Driver or Service provided to ManageDriver() \n")); + + return FALSE; + } + + // + // Connect to the Service Control Manager and open the Services database. + // + + schSCManager = OpenSCManager(NULL, // local machine + NULL, // local database + SC_MANAGER_ALL_ACCESS // access required + ); + + if (!schSCManager) { + + _tprintf(_T("Open SC Manager failed! Error = %d \n"), GetLastError()); + + return FALSE; + } + + // + // Do the requested function. + // + + switch( Function ) { + + case DRIVER_FUNC_INSTALL: + + // + // Install the driver service. + // + + if (InstallDriver(schSCManager, + DriverName, + ServiceName + )) { + + // + // Start the driver service (i.e. start the driver). + // + + rCode = StartDriver(schSCManager, + DriverName + ); + + } else { + + // + // Indicate an error. + // + + rCode = FALSE; + } + + break; + + case DRIVER_FUNC_REMOVE: + + // + // Stop the driver. + // + + StopDriver(schSCManager, + DriverName + ); + + // + // Remove the driver service. + // + + RemoveDriver(schSCManager, + DriverName + ); + + // + // Ignore all errors. + // + + rCode = TRUE; + + break; + + case DRIVER_FUNC_STOP: + + // + // Stop the driver. + // + + StopDriver(schSCManager, + DriverName + ); + + // + // Ignore all errors. + // + + rCode = TRUE; + + break; + + default: + + _tprintf(_T("Unknown ManageDriver() function. \n")); + + rCode = FALSE; + + break; + } + + // + // Close handle to service control manager. + // + + if (schSCManager) { + + CloseServiceHandle(schSCManager); + } + + return rCode; + +} // ManageDriver + + +BOOLEAN +RemoveDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + BOOLEAN rCode; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + _tprintf(_T("OpenService failed! Error = %d \n"), GetLastError()); + + // + // Indicate error. + // + + return FALSE; + } + + // + // Mark the service for deletion from the service control manager database. + // + + if (DeleteService(schService)) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + _tprintf(_T("DeleteService failed! Error = %d \n"), GetLastError()); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + return rCode; + +} // RemoveDriver + + + +BOOLEAN +StartDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + DWORD err; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + _tprintf(_T("OpenService failed! Error = %d \n"), GetLastError()); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Start the execution of the service (i.e. start the driver). + // + + if (!StartService(schService, // service identifier + 0, // number of arguments + NULL // pointer to arguments + )) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_ALREADY_RUNNING) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + _tprintf(_T("StartService failure! Error = %d \n"), err ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + return FALSE; + } + + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + return TRUE; + +} // StartDriver + + + +BOOLEAN +StopDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ) +{ + BOOLEAN rCode = TRUE; + SC_HANDLE schService; + SERVICE_STATUS serviceStatus; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + _tprintf(_T("OpenService failed! Error = %d \n"), GetLastError()); + + return FALSE; + } + + // + // Request that the service stop. + // + + if (ControlService(schService, + SERVICE_CONTROL_STOP, + &serviceStatus + )) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + _tprintf(_T("ControlService failed! Error = %d \n"), GetLastError() ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle (schService); + } + + return rCode; + +} // StopDriver + + + + diff --git a/general/tracing/evntdrv/evntctrl/install.h b/general/tracing/evntdrv/evntctrl/install.h new file mode 100644 index 00000000..879bc693 --- /dev/null +++ b/general/tracing/evntdrv/evntctrl/install.h @@ -0,0 +1,37 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + install.h + +Abstract: + + Win32 routines to dynamically load and unload a Windows NT kernel-mode + driver using the Service Control Manager APIs. + +Environment: + + User mode only + + +--*/ + +#define DRIVER_FUNC_INSTALL 0x01 +#define DRIVER_FUNC_REMOVE 0x02 +#define DRIVER_FUNC_STOP 0x03 + +#define DRIVER_NAME _T("Eventdrv") + +BOOLEAN +ManageDriver( + IN LPCTSTR DriverName, + IN LPCTSTR ServiceName, + IN USHORT Function + ); + diff --git a/general/tracing/evntdrv/evntctrl/tracectl.c b/general/tracing/evntdrv/evntctrl/tracectl.c new file mode 100644 index 00000000..2b9a0b78 --- /dev/null +++ b/general/tracing/evntdrv/evntctrl/tracectl.c @@ -0,0 +1,243 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + tracectl.c + +Environment: + + User mode Win32 console application + +Revision History: + + +--*/ +#define UNICODE +#define _UNICODE +#include +#include +#include +#include +#include "drvioctl.h" +#include "install.h" +#include +#include + +#define WAIT_TIME 10 + +BOOLEAN +SetupDriverName( + _Out_writes_(MAX_PATH)LPTSTR DriverLocation + ); + +int _cdecl main(int argc, LPCTSTR argv[]) +{ + HANDLE hDevice; // handle to a device, file, or directory + DWORD dwError = ERROR_SUCCESS; + LPVOID lpFileName = _T("\\\\.\\EventEtw") ; + TCHAR driverLocation[MAX_PATH]; + DWORD dwOutBuffer[2048]; + DWORD dwOutBufferCount ; + int ch; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + if ((hDevice = CreateFile( + lpFileName, // pointer to name of the file + 0, // access (read-write) mode + 0, // share mode + NULL, // pointer to security attributes + OPEN_EXISTING, // how to create + FILE_ATTRIBUTE_NORMAL, // file attributes + NULL // handle to file with attributes to + // copy + )) == INVALID_HANDLE_VALUE) { + dwError = GetLastError(); + + if ( dwError != ERROR_FILE_NOT_FOUND ) { + _tprintf(_T("CreateFile failed ! error: %d\n"), dwError); + return 1; + } + + // + // Setup full path to driver name + // + + if (!SetupDriverName(driverLocation)) { + + return 2; + + } + + // + // Install driver + // + + if (!ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_INSTALL + )) { + + _tprintf(_T("Unable to install driver. \n")); + + // + // Error - remove driver. + // + + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + return 3; + } + + if ((hDevice = CreateFile( + lpFileName, // pointer to name of the file + 0, // access (read-write) mode + 0, // share mode + NULL, // pointer to security attributes + OPEN_EXISTING, // how to create + FILE_ATTRIBUTE_NORMAL, // file attributes + NULL // handle to file with attributes to + // copy + )) == INVALID_HANDLE_VALUE) { + + _tprintf(_T("Error: CreateFile failed\n")); + return 4; + } + } + + + + _tprintf(_T("\nPress 'q' to exit, any other key to send ioctl...\n")); + fflush(stdin); + ch = _getche(); + + while(tolower(ch) != 'q' ) + { + + _tprintf(_T("Making IOCTL_EVNTKMP_TRACE_EVENT_A ioctl to log events\n")); + if (DeviceIoControl( + hDevice, // handle to a device, file, or directory + IOCTL_EVNTKMP_TRACE_EVENT_A, // control code of operation to perform + NULL, // pointer to buffer to supply input data + 0, // size, in bytes, of input buffer + dwOutBuffer, // pointer to buffer to receive output data + 2048, // size, in bytes, of output buffer + &dwOutBufferCount, // pointer to variable to receive byte count + NULL // pointer to structure for asynchronous operation + ) == 0) { + + _tprintf(_T("DeviceIOControl Failed %d\n"),GetLastError()); + return 5; + + } + ch = _getche(); + } + + if (CloseHandle(hDevice) == 0) { + + _tprintf(_T("CloseHandle Failed %d\n"),GetLastError()); + return 6; + + } + + // + // stop the driver + // + + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + _tprintf(_T("Driver '%s' is removed\n"), DRIVER_NAME); + + return 0; +} + +BOOLEAN +SetupDriverName( + _Out_writes_(MAX_PATH) LPTSTR DriverLocation + ) +{ + HANDLE fileHandle; + + DWORD driverLocLen = 0; + + // + // Get the current directory. + // + + driverLocLen = GetCurrentDirectory(MAX_PATH, + DriverLocation + ); + + if (!driverLocLen) { + + _tprintf(_T("GetCurrentDirectory failed! Error = %d \n"), GetLastError()); + + return FALSE; + } + + // + // Setup path name to driver file. + // + + if (StringCchPrintf(&DriverLocation[_tcslen(DriverLocation)], + (MAX_PATH - _tcslen(DriverLocation)), + _T("\\%s.sys"), + DRIVER_NAME) != S_OK){ + _tprintf(_T("Failed to generate DriverLocation!, StringCchPrintf Error = %d \n"), GetLastError()); + return FALSE; + } + + // + // Insure driver file is in the specified directory. + // + + if ((fileHandle = CreateFile(DriverLocation, + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + )) == INVALID_HANDLE_VALUE) { + + + _tprintf(_T("Driver: '%s' is not in the current directory. \n"), DRIVER_NAME); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Close open file handle. + // + + if (fileHandle) { + + CloseHandle(fileHandle); + } + + // + // Indicate success. + // + + return TRUE; + + +} // SetupDriverName diff --git a/general/tracing/tracedriver/ReadMe.md b/general/tracing/tracedriver/ReadMe.md new file mode 100644 index 00000000..99c46dd5 --- /dev/null +++ b/general/tracing/tracedriver/ReadMe.md @@ -0,0 +1,73 @@ +Tracedrv +======== + +Tracedrv is a sample driver instrumented for software tracing. The driver does not control any hardware; it simply generates trace messages. It is designed to show how to use WPP software tracing macros in a driver. + +Tracedrv initializes tracing (by using WPP\_INIT\_TRACING) and, when it receives a DeviceIOControl call, it starts a thread that logs 100 trace messages. The WPP software tracing directives, calls, and macros in the code are accompanied by comments that explain their purpose + +While examining Tracedrv, read the [WPP Software Tracing](http://msdn.microsoft.com/en-us/library/windows/hardware/ff556204) in the Windows Driver Kit (WDK). This section includes a reference section that describes the directives, macros, and calls required for WPP software tracing. + +Run the sample +-------------- + +To test the Tracedrv event tracing provider, use the following procedure. + +1. Copy the Tracectl.exe file that was created when you built the Tracedrv solution from the Tracectl directory (for example, \\Documents\\Visual Studio 2013\\Projects\\tracedrv\\tracectl\\*platform*) to the Tracedrv directory (for example, \\Documents\\Visual Studio 2013\\Projects\\tracedrv\\tracedrv\\*platform*). +2. Use Tracepdb to create a trace message format (TMF) file and a trace message control (TMC) file from the Tracedrv.pdb file. Tracepdb is located in the C:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\*platform* directory. The PDB file that is used in this command is created when you the build the solution. Open a Visual Studio Command prompt window and navigate to the target build platform and configuration directory. Type the following command: + + **tracepdb -f tracedrv.pdb** + +3. In the same Tracedrv target build directory, create a control GUID file for Tracedrv by opening a text file, adding the following content, and saving the file as Tracedrv.ctl. + + + + + + + + + + + + + + + +
Text
d58c126f-b309-11d1-969e-0000f875a5bc 
+ +4. Use Tracelog to start a trace session that is called *TestTracedrv*. Tracelog is located in the C:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\*platform* directory. The Tracedrv.ctl file that is used in this command was created in the previous step. The following command starts a trace session and creates a trace log file, tracedrv.etl, in the local directory. + + ``` {.syntax xml:space="preserve"} + tracelog -start TestTracedrv -guid tracedrv.ctl -f tracedrv.etl -flag 1 + ``` + + **Note**  Note: Without the -flag parameter, Tracedrv will not generate any trace messages. + +5. To generate trace messages, run Tracectl.exe. This executable file is built when you build the solution. Each time you type a character, other than **Q** or **q**, Tracectl sends an IOCTL to the driver that signals it to generate trace messages. To stop Tracectl, type **Q** or **q**. +6. To stop the trace session, use the following Tracelog command. + + ``` {.syntax xml:space="preserve"} + tracelog -stop TestTracedrv + ``` + +7. To display the trace messages in the Tracedrv.etl file, use Tracefmt.exe. Tracefmt.exe is located in the C:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\*platform*. The TMF file used in this command was created by Tracepdb.exe in step 2. The **-p** option specifies the directory of the TMF file. In this case, the TMF file is in the current directory. Type the following command: + + ``` {.syntax xml:space="preserve"} + tracefmt tracedrv.etl -p . -o Tracedrv.out + ``` + +The resulting Tracedrv.out file is a human-readable text file of the Tracedrv trace messages. To interpret the trace messages, in the Tracedrv.c file, search for the [**DoTraceMessage**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544918) macros. + +Notes +----- + +This sample driver should not be used in a production environment. + +Tracedrv is designed for Windows XP and later versions of Windows. It does not demonstrate how to add WPP software tracing to a Windows 2000 driver. (For information about adding WPP software tracing to a Windows 2000 driver, see the [Software Tracing FAQ](http://msdn.microsoft.com/en-us/library/windows/hardware/ff551795) topic in the Windows DDK documentation.) + +Also, because it is not a Plug and Play driver, Tracedrv does not demonstrate tracing in a Plug and Play environment. + +Tracedrv demonstrates the basic elements required for software tracing. It does not demonstrate more advanced tracing techniques, such as writing customized tracing calls (variations of [**DoTraceMessage**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544918)), or the use of WMI calls for software tracing. + +If you are building the Tracedrv sample to test on a 64-bit version of Windows, you need to sign the driver. Starting with Windows Vista, all 64-bit versions of Windows require driver code to have a digital signature for the driver to load. See [Signing a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554809) and [Signing a Driver During Development and Testing](http://msdn.microsoft.com/en-us/library/windows/hardware/hh967733). You might also need to configure the test computer so that it can load test-signed kernel mode code, see [The TESTSIGNING Boot Configuration Option](http://msdn.microsoft.com/en-us/library/windows/hardware/ff553484) and [**BCDEdit /set**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff542202). + diff --git a/general/tracing/tracedriver/tracectl/install.c b/general/tracing/tracedriver/tracectl/install.c new file mode 100644 index 00000000..9f44073b --- /dev/null +++ b/general/tracing/tracedriver/tracectl/install.c @@ -0,0 +1,499 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + install.c + +Abstract: + + Win32 routines to dynamically load and unload a Windows NT kernel-mode + driver using the Service Control Manager APIs. + +Environment: + + User mode only + + +--*/ +#define UNICODE +#define _UNICODE +#include +#include +#include +#include + +#include "install.h" + + +BOOLEAN +InstallDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName, + IN LPCTSTR ServiceExe + ); + + +BOOLEAN +RemoveDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ); + +BOOLEAN +StartDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ); + +BOOLEAN +StopDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ); + +BOOLEAN +InstallDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName, + IN LPCTSTR ServiceExe + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + SC_HANDLE schService; + DWORD err; + + // + // NOTE: This creates an entry for a standalone driver. If this + // is modified for use with a driver that requires a Tag, + // Group, and/or Dependencies, it may be necessary to + // query the registry for existing driver information + // (in order to determine a unique Tag, etc.). + // + + // + // Create a new a service object. + // + + schService = CreateService(SchSCManager, // handle of service control manager database + DriverName, // address of name of service to start + DriverName, // address of display name + SERVICE_ALL_ACCESS, // type of access to service + SERVICE_KERNEL_DRIVER, // type of service + SERVICE_DEMAND_START, // when to start service + SERVICE_ERROR_NORMAL, // severity if service fails to start + ServiceExe, // address of name of binary file + NULL, // service does not belong to a group + NULL, // no tag requested + NULL, // no dependency names + NULL, // use LocalSystem account + NULL // no password for service account + ); + + if (schService == NULL) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_EXISTS) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + _tprintf(_T("CreateService failed! Error = %d \n"), err ); + + // + // Indicate an error. + // + + return FALSE; + } + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + // + // Indicate success. + // + + return TRUE; + +} // InstallDriver + +BOOLEAN +ManageDriver( + IN LPCTSTR DriverName, + IN LPCTSTR ServiceName, + IN USHORT Function + ) +{ + + SC_HANDLE schSCManager; + + BOOLEAN rCode = TRUE; + + // + // Insure (somewhat) that the driver and service names are valid. + // + + if (!DriverName || !ServiceName) { + + _tprintf(_T("Invalid Driver or Service provided to ManageDriver() \n")); + + return FALSE; + } + + // + // Connect to the Service Control Manager and open the Services database. + // + + schSCManager = OpenSCManager(NULL, // local machine + NULL, // local database + SC_MANAGER_ALL_ACCESS // access required + ); + + if (!schSCManager) { + + _tprintf(_T("Open SC Manager failed! Error = %d \n"), GetLastError()); + + return FALSE; + } + + // + // Do the requested function. + // + + switch( Function ) { + + case DRIVER_FUNC_INSTALL: + + // + // Install the driver service. + // + + if (InstallDriver(schSCManager, + DriverName, + ServiceName + )) { + + // + // Start the driver service (i.e. start the driver). + // + + rCode = StartDriver(schSCManager, + DriverName + ); + + } else { + + // + // Indicate an error. + // + + rCode = FALSE; + } + + break; + + case DRIVER_FUNC_REMOVE: + + // + // Stop the driver. + // + + StopDriver(schSCManager, + DriverName + ); + + // + // Remove the driver service. + // + + RemoveDriver(schSCManager, + DriverName + ); + + // + // Ignore all errors. + // + + rCode = TRUE; + + break; + + case DRIVER_FUNC_STOP: + + // + // Stop the driver. + // + + StopDriver(schSCManager, + DriverName + ); + + // + // Ignore all errors. + // + + rCode = TRUE; + + break; + + default: + + _tprintf(_T("Unknown ManageDriver() function. \n")); + + rCode = FALSE; + + break; + } + + // + // Close handle to service control manager. + // + + if (schSCManager) { + + CloseServiceHandle(schSCManager); + } + + return rCode; + +} // ManageDriver + + +BOOLEAN +RemoveDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + BOOLEAN rCode; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + _tprintf(_T("OpenService failed! Error = %d \n"), GetLastError()); + + // + // Indicate error. + // + + return FALSE; + } + + // + // Mark the service for deletion from the service control manager database. + // + + if (DeleteService(schService)) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + _tprintf(_T("DeleteService failed! Error = %d \n"), GetLastError()); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + return rCode; + +} // RemoveDriver + + + +BOOLEAN +StartDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + DWORD err; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + _tprintf(_T("OpenService failed! Error = %d \n"), GetLastError()); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Start the execution of the service (i.e. start the driver). + // + + if (!StartService(schService, // service identifier + 0, // number of arguments + NULL // pointer to arguments + )) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_ALREADY_RUNNING) { + + // + // Ignore this error. + // + + return TRUE; + + } else { + + _tprintf(_T("StartService failure! Error = %d \n"), err ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + return FALSE; + } + + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle(schService); + } + + return TRUE; + +} // StartDriver + + + +BOOLEAN +StopDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ) +{ + BOOLEAN rCode = TRUE; + SC_HANDLE schService; + SERVICE_STATUS serviceStatus; + + // + // Open the handle to the existing service. + // + + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + + _tprintf(_T("OpenService failed! Error = %d \n"), GetLastError()); + + return FALSE; + } + + // + // Request that the service stop. + // + + if (ControlService(schService, + SERVICE_CONTROL_STOP, + &serviceStatus + )) { + + // + // Indicate success. + // + + rCode = TRUE; + + } else { + + _tprintf(_T("ControlService failed! Error = %d \n"), GetLastError() ); + + // + // Indicate failure. Fall through to properly close the service handle. + // + + rCode = FALSE; + } + + // + // Close the service object. + // + + if (schService) { + + CloseServiceHandle (schService); + } + + return rCode; + +} // StopDriver + + + + diff --git a/general/tracing/tracedriver/tracectl/install.h b/general/tracing/tracedriver/tracectl/install.h new file mode 100644 index 00000000..843a8387 --- /dev/null +++ b/general/tracing/tracedriver/tracectl/install.h @@ -0,0 +1,14 @@ + +#define DRIVER_FUNC_INSTALL 0x01 +#define DRIVER_FUNC_REMOVE 0x02 +#define DRIVER_FUNC_STOP 0x03 + +#define DRIVER_NAME _T("tracedrv") + +BOOLEAN +ManageDriver( + IN LPCTSTR DriverName, + IN LPCTSTR ServiceName, + IN USHORT Function + ); + diff --git a/general/tracing/tracedriver/tracectl/tracectl.c b/general/tracing/tracedriver/tracectl/tracectl.c new file mode 100644 index 00000000..f4476946 --- /dev/null +++ b/general/tracing/tracedriver/tracectl/tracectl.c @@ -0,0 +1,244 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + tracectl.c + +Environment: + + User mode Win32 console application + +Revision History: + + +--*/ +#define UNICODE +#define _UNICODE +#include +#include +#include +#include +#include "drvioctl.h" +#include "install.h" +#include +#include + +#define WAIT_TIME 10 + +BOOLEAN +SetupDriverName( + _Out_writes_(MAX_PATH)LPTSTR DriverLocation + ); + +int _cdecl main(int argc, LPCTSTR argv[]) +{ + HANDLE hDevice; // handle to a device, file, or directory + DWORD dwError = ERROR_SUCCESS; + LPVOID lpFileName = _T("\\\\.\\TraceKmp") ; + TCHAR driverLocation[MAX_PATH]; + DWORD dwOutBuffer[2048]; + DWORD dwOutBufferCount ; + int ch; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + + if ((hDevice = CreateFile( + lpFileName, // pointer to name of the file + 0, // access (read-write) mode + 0, // share mode + NULL, // pointer to security attributes + OPEN_EXISTING, // how to create + FILE_ATTRIBUTE_NORMAL, // file attributes + NULL // handle to file with attributes to + // copy + )) == INVALID_HANDLE_VALUE) { + dwError = GetLastError(); + + if ( dwError != ERROR_FILE_NOT_FOUND ) { + _tprintf(_T("CreateFile failed ! error: %d\n"), dwError); + return 1; + } + + // + // Setup full path to driver name + // + + if (!SetupDriverName(driverLocation)) { + + return 2; + + } + + // + // Install driver + // + + if (!ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_INSTALL + )) { + + _tprintf(_T("Unable to install driver. \n")); + + // + // Error - remove driver. + // + + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + return 3; + } + + if ((hDevice = CreateFile( + lpFileName, // pointer to name of the file + 0, // access (read-write) mode + 0, // share mode + NULL, // pointer to security attributes + OPEN_EXISTING, // how to create + FILE_ATTRIBUTE_NORMAL, // file attributes + NULL // handle to file with attributes to + // copy + )) == INVALID_HANDLE_VALUE) { + + _tprintf(_T("Error: CreateFile failed\n")); + return 4; + } + } + + + + _tprintf(_T("\nPress 'q' to exit, any other key to send ioctl...\n")); + fflush(stdin); + ch = _getche(); + + while(tolower(ch) != 'q' ) + { + + _tprintf(_T("Making TRACEKMP_TRACE_EVENT ioctl to log events\n")); + if (DeviceIoControl( + hDevice, // handle to a device, file, or directory + IOCTL_TRACEKMP_TRACE_EVENT, // control code of operation to perform + NULL, // pointer to buffer to supply input data + 0, // size, in bytes, of input buffer + dwOutBuffer, // pointer to buffer to receive output data + 2048, // size, in bytes, of output buffer + &dwOutBufferCount, // pointer to variable to receive byte count + NULL // pointer to structure for asynchronous operation + ) == 0) { + + _tprintf(_T("DeviceIOControl Failed %d\n"),GetLastError()); + return 5; + + } + ch = _getche(); + } + + if (CloseHandle(hDevice) == 0) { + + _tprintf(_T("CloseHandle Failed %d\n"),GetLastError()); + return 6; + + } + + // + // stop the driver + // + + ManageDriver(DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE + ); + + _tprintf(_T("Driver '%s' is removed\n"), DRIVER_NAME); + + return 0; +} + +BOOLEAN +SetupDriverName( + _Out_writes_(MAX_PATH) LPTSTR DriverLocation + ) +{ + HANDLE fileHandle; + + DWORD driverLocLen = 0; + + // + // Get the current directory. + // + + driverLocLen = GetCurrentDirectory(MAX_PATH, + DriverLocation + ); + + if (!driverLocLen) { + + _tprintf(_T("GetCurrentDirectory failed! Error = %d \n"), GetLastError()); + + return FALSE; + } + + // + // Setup path name to driver file. + // + + if (StringCchPrintf(&DriverLocation[_tcslen(DriverLocation)], + (MAX_PATH - _tcslen(DriverLocation)), + _T("\\%s.sys"), + DRIVER_NAME) != S_OK){ + _tprintf(_T("Failed to generate DriverLocation!, StringCchPrintf Error = %d \n"), GetLastError()); + return FALSE; + } + + // + // Insure driver file is in the specified directory. + // + + if ((fileHandle = CreateFile(DriverLocation, + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + )) == INVALID_HANDLE_VALUE) { + + + _tprintf(_T("Driver: '%s' is not in the current directory. \n"), DRIVER_NAME); + + // + // Indicate failure. + // + + return FALSE; + } + + // + // Close open file handle. + // + + if (fileHandle) { + + CloseHandle(fileHandle); + } + + // + // Indicate success. + // + + return TRUE; + + +} // SetupDriverName diff --git a/general/tracing/tracedriver/tracectl/tracectl.vcxproj b/general/tracing/tracedriver/tracectl/tracectl.vcxproj new file mode 100644 index 00000000..e1e21430 --- /dev/null +++ b/general/tracing/tracedriver/tracectl/tracectl.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {72739B3A-9B9B-41EE-9B0E-E73482470A03} + $(MSBuildProjectName) + Debug + Win32 + {707AFE5D-65EB-4AED-967A-114E753D1037} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + tracectl + + + tracectl + + + tracectl + + + tracectl + + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + %(AdditionalIncludeDirectories);.;..\tracedrv + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/tracing/tracedriver/tracectl/tracectl.vcxproj.Filters b/general/tracing/tracedriver/tracectl/tracectl.vcxproj.Filters new file mode 100644 index 00000000..49a76abb --- /dev/null +++ b/general/tracing/tracedriver/tracectl/tracectl.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {162117FB-9571-483E-8BDC-60E6A31AD53D} + + + h;hpp;hxx;hm;inl;inc;xsd + {ECB2DAEF-8542-46F3-84A4-AB8FC5EDC817} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {CD34668D-32E2-4A6E-8DC5-7CCC09E3FC35} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/tracing/tracedriver/tracedrv.sln b/general/tracing/tracedriver/tracedrv.sln new file mode 100644 index 00000000..b75b8638 --- /dev/null +++ b/general/tracing/tracedriver/tracedrv.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tracectl", "Tracectl", "{F3C08B55-33D7-458F-BE79-6878512FF1B1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tracedrv", "Tracedrv", "{93C02447-AFF7-40E8-AED1-849588063150}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tracectl", "tracectl\tracectl.vcxproj", "{72739B3A-9B9B-41EE-9B0E-E73482470A03}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tracedrv", "tracedrv\tracedrv.vcxproj", "{959FDB65-EF62-4ED3-8856-326B5AD4BB41}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {72739B3A-9B9B-41EE-9B0E-E73482470A03}.Debug|Win32.ActiveCfg = Debug|Win32 + {72739B3A-9B9B-41EE-9B0E-E73482470A03}.Debug|Win32.Build.0 = Debug|Win32 + {72739B3A-9B9B-41EE-9B0E-E73482470A03}.Release|Win32.ActiveCfg = Release|Win32 + {72739B3A-9B9B-41EE-9B0E-E73482470A03}.Release|Win32.Build.0 = Release|Win32 + {72739B3A-9B9B-41EE-9B0E-E73482470A03}.Debug|x64.ActiveCfg = Debug|x64 + {72739B3A-9B9B-41EE-9B0E-E73482470A03}.Debug|x64.Build.0 = Debug|x64 + {72739B3A-9B9B-41EE-9B0E-E73482470A03}.Release|x64.ActiveCfg = Release|x64 + {72739B3A-9B9B-41EE-9B0E-E73482470A03}.Release|x64.Build.0 = Release|x64 + {959FDB65-EF62-4ED3-8856-326B5AD4BB41}.Debug|Win32.ActiveCfg = Debug|Win32 + {959FDB65-EF62-4ED3-8856-326B5AD4BB41}.Debug|Win32.Build.0 = Debug|Win32 + {959FDB65-EF62-4ED3-8856-326B5AD4BB41}.Release|Win32.ActiveCfg = Release|Win32 + {959FDB65-EF62-4ED3-8856-326B5AD4BB41}.Release|Win32.Build.0 = Release|Win32 + {959FDB65-EF62-4ED3-8856-326B5AD4BB41}.Debug|x64.ActiveCfg = Debug|x64 + {959FDB65-EF62-4ED3-8856-326B5AD4BB41}.Debug|x64.Build.0 = Debug|x64 + {959FDB65-EF62-4ED3-8856-326B5AD4BB41}.Release|x64.ActiveCfg = Release|x64 + {959FDB65-EF62-4ED3-8856-326B5AD4BB41}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {72739B3A-9B9B-41EE-9B0E-E73482470A03} = {F3C08B55-33D7-458F-BE79-6878512FF1B1} + {959FDB65-EF62-4ED3-8856-326B5AD4BB41} = {93C02447-AFF7-40E8-AED1-849588063150} + EndGlobalSection +EndGlobal diff --git a/general/tracing/tracedriver/tracedrv/drvioctl.h b/general/tracing/tracedriver/tracedrv/drvioctl.h new file mode 100644 index 00000000..d4b84cdf --- /dev/null +++ b/general/tracing/tracedriver/tracedrv/drvioctl.h @@ -0,0 +1,34 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + + +Module Name: + + drvioctl.h + +Abstract: + + Definitions of IOCTL codes and data structures exported by TRACEDRV. + + +--*/ + +#ifndef __TRACEKMP_IOCTL__ +#define __TRACEKMP_IOCTL__ + +// +// IOCTL control codes +// +#define IOCTL_TRACEKMP_TRACE_EVENT \ + CTL_CODE( FILE_DEVICE_UNKNOWN, 0x801, \ + METHOD_BUFFERED, FILE_ANY_ACCESS ) + +#endif // __TRACEKMP_IOCTL__ + + diff --git a/general/tracing/tracedriver/tracedrv/tracedrv.c b/general/tracing/tracedriver/tracedrv/tracedrv.c new file mode 100644 index 00000000..a1606f7b --- /dev/null +++ b/general/tracing/tracedriver/tracedrv/tracedrv.c @@ -0,0 +1,379 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + + +Module Name: + + tracedrv.c + +Abstract: + + Sample kernel mode trace provider/driver. + +--*/ +#include +#include +#include "drvioctl.h" +#include "tracedrv.h" +#include "tracedrv.tmh" // this is the file that will be auto generated + + +DRIVER_UNLOAD TracedrvDriverUnload; + +_Dispatch_type_(IRP_MJ_CREATE) +_Dispatch_type_(IRP_MJ_CLOSE) +DRIVER_DISPATCH TracedrvDispatchOpenClose; + +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) +DRIVER_DISPATCH TracedrvDispatchDeviceControl; + +VOID +TraceEventLogger( + IN PTRACEHANDLE pLoggerHandle + ); + + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +TracedrvDispatchOpenClose( + IN PDEVICE_OBJECT pDO, + IN PIRP Irp + ); + +NTSTATUS +TracedrvDispatchDeviceControl( + IN PDEVICE_OBJECT pDO, + IN PIRP Irp + ); + +VOID +TracedrvDriverUnload( + IN PDRIVER_OBJECT DriverObject + ); + + +#ifdef ALLOC_PRAGMA + #pragma alloc_text( INIT, DriverEntry ) + #pragma alloc_text( PAGE, TracedrvDispatchOpenClose ) + #pragma alloc_text( PAGE, TracedrvDispatchDeviceControl ) + #pragma alloc_text( PAGE, TracedrvDriverUnload ) +#endif // ALLOC_PRAGMA + + +#define MAXEVENTS 3 + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + +Arguments: + DriverObject - pointer to the driver object + RegistryPath - pointer to a unicode string representing the path + to driver-specific key in the registry + +Return Value: + + STATUS_SUCCESS if successful + STATUS_UNSUCCESSFUL otherwise + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING deviceName; + UNICODE_STRING linkName; + PDEVICE_OBJECT pTracedrvDeviceObject; + + + KdPrint(("TraceDrv: DriverEntry\n")); + + // + // Create Dispatch Entry Points. + // + DriverObject->DriverUnload = TracedrvDriverUnload; + DriverObject->MajorFunction[ IRP_MJ_CREATE ] = TracedrvDispatchOpenClose; + DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = TracedrvDispatchOpenClose; + DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = TracedrvDispatchDeviceControl; + + // + // include this macro to support Win2K. + // + WPP_SYSTEMCONTROL(DriverObject); + + + + RtlInitUnicodeString( &deviceName, TRACEDRV_NT_DEVICE_NAME ); + + // + // Create the Device object + // + status = IoCreateDevice( + DriverObject, + 0, + &deviceName, + FILE_DEVICE_UNKNOWN, + 0, + FALSE, + &pTracedrvDeviceObject); + + if ( !NT_SUCCESS( status )) { + return status; + } + + RtlInitUnicodeString( &linkName, TRACEDRV_WIN32_DEVICE_NAME ); + status = IoCreateSymbolicLink( &linkName, &deviceName ); + + if ( !NT_SUCCESS( status )) { + IoDeleteDevice( pTracedrvDeviceObject ); + return status; + } + + + // + // Choose a buffering mechanism + // + pTracedrvDeviceObject->Flags |= DO_BUFFERED_IO; + + + // + // This macro is required to initialize software tracing. + // + // Win2K use the deviceobject as the first argument. + // + // XP and beyond does not require device object. First argument + // is ignored. + // + WPP_INIT_TRACING(pTracedrvDeviceObject,RegistryPath); + + + return STATUS_SUCCESS; +} + +NTSTATUS +TracedrvDispatchOpenClose( + IN PDEVICE_OBJECT pDO, + IN PIRP Irp + ) +/*++ + +Routine Description: + + Dispatch routine to handle Create/Close IRPs. + +Arguments: + + DeviceObject - pointer to a device object. + + Irp - pointer to an I/O Request Packet. + +Return Value: + + NT status code + +--*/ +{ + + UNREFERENCED_PARAMETER(pDO); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + PAGED_CODE(); + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + return STATUS_SUCCESS; +} + + +NTSTATUS +TracedrvDispatchDeviceControl( + IN PDEVICE_OBJECT pDO, + IN PIRP Irp + ) +/*++ + +Routine Description: + + Dispatch routine to handle IOCTL IRPs. + +Arguments: + + DeviceObject - pointer to a device object. + + Irp - pointer to an I/O Request Packet. + +Return Value: + + NT status code + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp ); + ULONG ControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; + ULONG i=0; + static ULONG ioctlCount = 0; + MachineState CurrentState = Offline; + + + PAGED_CODE(); + UNREFERENCED_PARAMETER(pDO); + + Irp->IoStatus.Information = + irpStack->Parameters.DeviceIoControl.OutputBufferLength; + + switch ( ControlCode ) { + case IOCTL_TRACEKMP_TRACE_EVENT: + // + // Every time we get this IOCTL, we also log a trace Message if + // Trace flag one is enabled. This is used + // to illustrate that the event can be caused by user-mode. + // + + ioctlCount++; + + // + // Log a simple Message + // + + DoTraceMessage(FLAG_ONE, "IOCTL = %d", ioctlCount); + + while (i++ < MAXEVENTS) { + // + // Trace events in a loop. + // + DoTraceMessage(FLAG_ONE, "Hello, %d %s", i, "Hi" ); + + if ( !(i%MAXEVENTS)){ + // + // Trace if level >=2 and 2 bit set by -level 2 -flags 2 in tracelog + // Uses the format string for the defined enum MachineState in the + // scanned header file + // + DoTraceLevelMessage( + TRACE_LEVEL_ERROR, // ETW Level defined in evntrace.h + FLAG_TWO, // Flag defined in WPP_CONTROL_GUIDS + "Machine State :: %!state!", + CurrentState // enum parameter + ); + } + } + + // + // Set a fake error status to fire the TRACE_RETURN macro below + // + status = STATUS_DEVICE_POWERED_OFF; + + Irp->IoStatus.Information = 0; + break; + + // + // Not one we recognize. Error. + // + default: + status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + break; + } + + // + // Trace the return status using the TRACE_RETURN macro wich includes PRE/POST + // macros. The value could be either the fake error or invalid parameter + // + TRACE_RETURN(status); + + if (status != STATUS_INVALID_PARAMETER) { + // + // Set the status back to success + // + status = STATUS_SUCCESS; + } + + // + // Get rid of this request + // + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + return status; +} + + +VOID +TracedrvDriverUnload( + IN PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + Free all the resources allocated in DriverEntry. + +Arguments: + + DriverObject - pointer to a driver object. + +Return Value: + + VOID. + +--*/ +{ + PDEVICE_OBJECT pDevObj; + UNICODE_STRING linkName; + + + PAGED_CODE(); + + KdPrint(("TraceDrv: Unloading \n")); + + // + // Get pointer to Device object + // + pDevObj = DriverObject->DeviceObject; + + // + // Cleanup using DeviceObject on Win2K. Make sure + // this is same deviceobject that used for initializing. + // On XP the Parameter is ignored + WPP_CLEANUP(pDevObj); + + // + // Form the Win32 symbolic link name. + // + RtlInitUnicodeString( &linkName, TRACEDRV_WIN32_DEVICE_NAME ); + + // + // Remove symbolic link from Object + // namespace... + // + IoDeleteSymbolicLink( &linkName ); + + // + // Unload the callbacks from the kernel to this driver + // + IoDeleteDevice( pDevObj ); + +} + + diff --git a/general/tracing/tracedriver/tracedrv/tracedrv.ctl b/general/tracing/tracedriver/tracedrv/tracedrv.ctl new file mode 100644 index 00000000..c5ff6bef --- /dev/null +++ b/general/tracing/tracedriver/tracedrv/tracedrv.ctl @@ -0,0 +1 @@ +d58c126f-b309-11d1-969e-0000f875a5bc CtlGuid \ No newline at end of file diff --git a/general/tracing/tracedriver/tracedrv/tracedrv.h b/general/tracing/tracedriver/tracedrv/tracedrv.h new file mode 100644 index 00000000..1b86834a --- /dev/null +++ b/general/tracing/tracedriver/tracedrv/tracedrv.h @@ -0,0 +1,113 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + tracedrv.h + +Abstract: + + tracedrv.h defines: + - The provider GUID for the driver. + - Macros for tracing with levels and flags + - Tracing enumerations using custom type + - Trace macro that incorporates PRE/POST macros + +Environment: + + Kernel mode + +--*/ + + +#define TRACEDRV_NT_DEVICE_NAME L"\\Device\\TraceKmp" +#define TRACEDRV_WIN32_DEVICE_NAME L"\\DosDevices\\TRACEKMP" + +// +// Software Tracing Definitions +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(CtlGuid,(d58c126f, b309, 11d1, 969e, 0000f875a5bc), \ + WPP_DEFINE_BIT(FLAG_ONE) \ + WPP_DEFINE_BIT(FLAG_TWO) ) + +// +// DoTraceLevelMessage is a custom macro that adds support for levels to the +// default DoTraceMessage, which supports only flags. In this version, both +// flags and level are conditions for generating the trace message. +// The preprocessor is told to recognize the function by using the -func argument +// in the RUN_WPP line on the source file. In the source file you will find +// -func:DoTraceLevelMessage(LEVEL,FLAGS,MSG,...). The conditions for triggering +// this event in the macro are the Levels defined in evntrace.h and the flags +// defined above and are evaluated by the macro WPP_LEVEL_FLAGS_ENABLED below. +// +#define WPP_LEVEL_FLAGS_LOGGER(level,flags) WPP_LEVEL_LOGGER(flags) +#define WPP_LEVEL_FLAGS_ENABLED(level, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= level) + +typedef enum _MachineState { + Offline = 2, + Online = 1, + Failed = 0xFF000001, + Stalled = 0xFF000002 +} MachineState; +// +// Configuration block to scan the enumeration definition MachineState. Used when +// viewing the trace to display names instead of the integer values that users must decode +// +// begin_wpp config +// CUSTOM_TYPE(state, ItemEnum(_MachineState)); +// end_wpp + + +// MACRO: TRACE_RETURN +// Configuration block that defines trace macro. It uses the PRE/POST macros to include +// code as part of the trace macro expansion. TRACE_MACRO is equivalent to the code below: +// +// {if (Status != STATUS_SUCCESS){ // This is the code in the PRE macro +// DoTraceMessage(FLAG_ONE, "Function Return = %!STATUS!", Status) +// ;}} // This is the code in the POST macro +// +// +// USEPREFIX statement: Defines a format string prefix to be used when logging the event, +// below the STDPREFIX is used. The first value is the trace function name with out parenthesis +// and the second value is the format string to be used. +// +// USESUFFIX statement: Defines a suffix format string that gets logged with the event. +// +// FUNC statement: Defines the name and signature of the trace function. The function defined +// below takes one argument, no format string, and predefines the flag equal to FLAG_ONE. +// +// +//begin_wpp config +//USEPREFIX (TRACE_RETURN, "%!STDPREFIX!"); +//FUNC TRACE_RETURN{FLAG=FLAG_ONE}(EXP); +//USESUFFIX (TRACE_RETURN, "Function Return=%!STATUS!",EXP); +//end_wpp + +// +// PRE macro: The name of the macro includes the condition arguments FLAGS and EXP +// define in FUNC above +// +#define WPP_FLAG_EXP_PRE(FLAGS, HR) {if (HR != STATUS_SUCCESS) { + +// +// POST macro +// The name of the macro includes the condition arguments FLAGS and EXP +// define in FUNC above +#define WPP_FLAG_EXP_POST(FLAGS, HR) ;}} + +// +// The two macros below are for checking if the event should be logged and for +// choosing the logger handle to use when calling the ETW trace API +// +#define WPP_FLAG_EXP_ENABLED(FLAGS, HR) WPP_FLAG_ENABLED(FLAGS) +#define WPP_FLAG_EXP_LOGGER(FLAGS, HR) WPP_FLAG_LOGGER(FLAGS) + + diff --git a/general/tracing/tracedriver/tracedrv/tracedrv.rc b/general/tracing/tracedriver/tracedrv/tracedrv.rc new file mode 100644 index 00000000..88c83ddc --- /dev/null +++ b/general/tracing/tracedriver/tracedrv/tracedrv.rc @@ -0,0 +1,28 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + + +Module Name: + + tracedrv.rc + +--*/ + +#include +#include +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Trace Kernel Mode Driver" +#define VER_INTERNALNAME_STR "tracedrv.sys" +#define VER_ORIGINALFILENAME_STR "tracedrv.sys" + +#include "common.ver" + +LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL + diff --git a/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj b/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj new file mode 100644 index 00000000..febcbdd7 --- /dev/null +++ b/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj @@ -0,0 +1,164 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {959FDB65-EF62-4ED3-8856-326B5AD4BB41} + $(MSBuildProjectName) + Debug + Win32 + {01308CF4-AB44-4626-BACF-D0555BC457DF} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + DoTraceLevelMessage(LEVEL,FLAGS,MSG,...) + tracedrv.h + + + true + true + DoTraceLevelMessage(LEVEL,FLAGS,MSG,...) + tracedrv.h + + + + tracedrv + + + tracedrv + + + tracedrv + + + tracedrv + + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj.Filters b/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj.Filters new file mode 100644 index 00000000..029e408d --- /dev/null +++ b/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {91F6D41E-6246-46E8-99D5-002ACC23AA49} + + + h;hpp;hxx;hm;inl;inc;xsd + {DF6424D7-7A22-42ED-B11F-508DD7CA41CD} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A42F3913-01F8-47A1-B888-182B29B1B137} + + + inf;inv;inx;mof;mc; + {EC7A4F43-C45A-4AAA-97BC-DFDF6C41C75C} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/umdfSkeleton/ReadMe.md b/general/umdfSkeleton/ReadMe.md new file mode 100644 index 00000000..923f44df --- /dev/null +++ b/general/umdfSkeleton/ReadMe.md @@ -0,0 +1,7 @@ +UMDF Driver Skeleton Sample (UMDF Version 1) +============================================ + +This sample demonstrates how to use version 1 of the User-Mode Driver Framework to write a minimal driver. + +The Skeleton driver will successfully load on a device (either root enumerated or a real hardware device) but does not support any I/O operations. + diff --git a/general/umdfSkeleton/Skeleton.rc b/general/umdfSkeleton/Skeleton.rc new file mode 100644 index 00000000..b6ecda7f --- /dev/null +++ b/general/umdfSkeleton/Skeleton.rc @@ -0,0 +1,21 @@ +//--------------------------------------------------------------------------- +// Skeleton.rc +// +// Copyright (c) Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include + +// +// TODO: Change the file description and file names to match your binary. +// + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WDF:UMDF Skeleton User-Mode Driver Sample" +#define VER_INTERNALNAME_STR "UMDFSkeleton" +#define VER_ORIGINALFILENAME_STR "UMDFSkeleton.dll" + +#include "common.ver" diff --git a/general/umdfSkeleton/UMDFSkeleton.vcxproj b/general/umdfSkeleton/UMDFSkeleton.vcxproj new file mode 100644 index 00000000..947c3517 --- /dev/null +++ b/general/umdfSkeleton/UMDFSkeleton.vcxproj @@ -0,0 +1,264 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A0B8804E-85A9-4432-A812-E9F6947C647A} + $(MSBuildProjectName) + 1 + 1 + 9 + 9 + Debug + Win32 + {A084ACA0-8591-462C-A3F8-EB936C200BCA} + + + + Windows10 + False + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + internal.h + + + $(InfArch) + true + .\$(IntDir)\UMDFSkeleton_OSR.inf + + + $(InfArch) + true + .\$(IntDir)\UMDFSkeleton_Root.inf + + + true + true + internal.h + + + + UMDFSkeleton + + + UMDFSkeleton + + + UMDFSkeleton + + + UMDFSkeleton + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + 0x0A00 + 0x0A000000 + + + 0x0A00 + 0x0A000000 + + + 0x0A00 + 0x0A000000 + + + 0x0A00 + 0x0A000000 + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/umdfSkeleton/UMDFSkeleton.vcxproj.Filters b/general/umdfSkeleton/UMDFSkeleton.vcxproj.Filters new file mode 100644 index 00000000..da7d8ca7 --- /dev/null +++ b/general/umdfSkeleton/UMDFSkeleton.vcxproj.Filters @@ -0,0 +1,57 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {16B2A720-AEC4-4627-959C-8A289CB6F699} + + + h;hpp;hxx;hm;inl;inc;xsd + {F54667DA-A0EB-4C54-92C9-9D2E3B44E169} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {949A3C23-B62C-4219-A44F-CF5DBE78DD73} + + + inf;inv;inx;mof;mc; + {DDA0FB16-FA5D-4FB7-8EE6-DB37C36326C6} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + Driver Files + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/umdfSkeleton/UMDFSkeleton_OSR.inx b/general/umdfSkeleton/UMDFSkeleton_OSR.inx new file mode 100644 index 00000000..4da9c3f9 --- /dev/null +++ b/general/umdfSkeleton/UMDFSkeleton_OSR.inx @@ -0,0 +1,96 @@ +; UMDFSkeleton_OSR.inf - Install the Skeleton driver on the OSR USB device +; + +[Version] +Signature="$Windows NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFTUMDF% +DriverVer=03/25/2005,0.0.0.1 +CatalogFile=wudf.cat + +[Manufacturer] +%MSFTUMDF%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%SkeletonDeviceName%=Skeleton_Install, USB\Vid_045e&Pid_94aa&mi_00 +%SkeletonDeviceName%=Skeleton_Install, USB\VID_0547&PID_1002 + +[ClassInstall32] +AddReg=SampleClass_RegistryAdd + +[SampleClass_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"-10" + +[SourceDisksFiles] +UMDFSkeleton.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== UMDF Skeleton Device ================================== + +[Skeleton_Install.NT] +CopyFiles=UMDriverCopy +Include=WINUSB.INF ; Import sections from WINUSB.INF +Needs=WINUSB.NT ; Run the CopyFiles & AddReg directives for WinUsb.INF + +[Skeleton_Install.NT.hw] +AddReg=Skeleton_Device_AddReg + +[Skeleton_Install.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall ; flag 0x2 sets this as the service for the device +AddService=WinUsb,0x000001f8,WinUsb_ServiceInstall ; this service is installed because its a filter. + +[Skeleton_Install.NT.CoInstallers] +AddReg=CoInstallers_AddReg + +[Skeleton_Install.NT.Wdf] +KmdfService = WINUSB, WinUsb_Install +UmdfService = UMDFSkeleton, UMDFSkeleton_Install +UmdfServiceOrder = UMDFSkeleton + +[WinUsb_Install] +KmdfLibraryVersion = $KMDFVERSION$ + +[UMDFSkeleton_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID="{d4112073-d09b-458f-a5aa-35ef21eef5de}" +ServiceBinary="%12%\umdf\UMDFSkeleton.dll" + +[Skeleton_Device_AddReg] +HKR,,"LowerFilters",0x00010008,"WinUsb" ; FLG_ADDREG_TYPE_MULTI_SZ | FLG_ADDREG_APPEND + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +[WinUsb_ServiceInstall] +DisplayName = %WinUsb_SvcDesc% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WinUSB.sys + +[CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WUDFCoinstaller.dll" + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to drivers/umdf + +[UMDriverCopy] +UMDFSkeleton.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME + +; =================== Generic ================================== + +[Strings] +MSFTUMDF="Microsoft Internal (WDF:UMDF)" +MediaDescription="Microsoft Sample Driver Installation Media" +ClassName="Sample Device" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" +SkeletonDeviceName="Microsoft Skeleton User-Mode Driver on OSR USB Device Sample" +WinUsb_SvcDesc="WinUSB Driver" diff --git a/general/umdfSkeleton/UMDFSkeleton_Root.inx b/general/umdfSkeleton/UMDFSkeleton_Root.inx new file mode 100644 index 00000000..22b5459a --- /dev/null +++ b/general/umdfSkeleton/UMDFSkeleton_Root.inx @@ -0,0 +1,77 @@ +; +; UMDFSkeleton_Root.inf +; + +[Version] +Signature="$Windows NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFTUMDF% +CatalogFile=WUDF.cat +DriverVer=03/25/2005,0.0.0.1 + +[Manufacturer] +%MSFTUMDF%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%SkeletonDeviceName%=Skeleton_Install,UMDFSamples\Skeleton + +[ClassInstall32] +AddReg=SampleClass_RegistryAdd + +[SampleClass_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"-10" + +[SourceDisksFiles] +UMDFSkeleton.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== UMDF Skeleton Device ================================== + +[Skeleton_Install.NT] +CopyFiles=UMDriverCopy + +[Skeleton_Install.NT.hw] + +[Skeleton_Install.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[Skeleton_Install.NT.CoInstallers] +AddReg=CoInstallers_AddReg + +[Skeleton_Install.NT.Wdf] +UmdfService=UMDFSkeleton,UMDFSkeleton_Install +UmdfServiceOrder=UMDFSkeleton + +[UMDFSkeleton_Install] +UmdfLibraryVersion=$UMDFVERSION$ +ServiceBinary=%12%\UMDF\UMDFSkeleton.dll +DriverCLSID={d4112073-d09b-458f-a5aa-35ef21eef5de} + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +[CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WUDFCoinstaller.dll" + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to driversMdf + +[UMDriverCopy] +UMDFSkeleton.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME + +; =================== Generic ================================== + +[Strings] +MSFTUMDF="Microsoft Internal (WDF:UMDF)" +MediaDescription="Microsoft Sample Driver Installation Media" +ClassName="Sample Device" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" +SkeletonDeviceName="Microsoft Skeleton User-Mode Device Sample" diff --git a/general/umdfSkeleton/comsup.cpp b/general/umdfSkeleton/comsup.cpp new file mode 100644 index 00000000..fd298470 --- /dev/null +++ b/general/umdfSkeleton/comsup.cpp @@ -0,0 +1,344 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + ComSup.cpp + +Abstract: + + This module contains implementations for the functions and methods + used for providing COM support. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" + +#include "comsup.tmh" + +// +// Implementation of CUnknown methods. +// + +CUnknown::CUnknown( + VOID + ) : m_ReferenceCount(1) +/*++ + + Routine Description: + + Constructor for an instance of the CUnknown class. This simply initializes + the reference count of the object to 1. The caller is expected to + call Release() if it wants to delete the object once it has been allocated. + + Arguments: + + None + + Return Value: + + None + +--*/ +{ + // do nothing. +} + +HRESULT +STDMETHODCALLTYPE +CUnknown::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method provides the basic support for query interface on CUnknown. + If the interface requested is IUnknown it references the object and + returns an interface pointer. Otherwise it returns an error. + + Arguments: + + InterfaceId - the IID being requested + + Object - a location to store the interface pointer to return. + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + if (IsEqualIID(InterfaceId, __uuidof(IUnknown))) + { + *Object = QueryIUnknown(); + return S_OK; + } + else + { + *Object = NULL; + return E_NOINTERFACE; + } +} + +IUnknown * +CUnknown::QueryIUnknown( + VOID + ) +/*++ + + Routine Description: + + This helper method references the object and returns a pointer to the + object's IUnknown interface. + + This allows other methods to convert a CUnknown pointer into an IUnknown + pointer without a typecast and without calling QueryInterface and dealing + with the return value. + + Arguments: + + None + + Return Value: + + A pointer to the object's IUnknown interface. + +--*/ +{ + AddRef(); + return static_cast(this); +} + +ULONG +STDMETHODCALLTYPE +CUnknown::AddRef( + VOID + ) +/*++ + + Routine Description: + + This method adds one to the object's reference count. + + Arguments: + + None + + Return Value: + + The new reference count. The caller should only use this for debugging + as the object's actual reference count can change while the caller + examines the return value. + +--*/ +{ + return InterlockedIncrement(&m_ReferenceCount); +} + +ULONG +STDMETHODCALLTYPE +CUnknown::Release( + VOID + ) +/*++ + + Routine Description: + + This method subtracts one to the object's reference count. If the count + goes to zero, this method deletes the object. + + Arguments: + + None + + Return Value: + + The new reference count. If the caller uses this value it should only be + to check for zero (i.e. this call caused or will cause deletion) or + non-zero (i.e. some other call may have caused deletion, but this one + didn't). + +--*/ +{ + ULONG count = InterlockedDecrement(&m_ReferenceCount); + + if (count == 0) + { + delete this; + } + return count; +} + +// +// Implementation of CClassFactory methods. +// + +// +// Define storage for the factory's static lock count variable. +// + +LONG CClassFactory::s_LockCount = 0; + +IClassFactory * +CClassFactory::QueryIClassFactory( + VOID + ) +/*++ + + Routine Description: + + This helper method references the object and returns a pointer to the + object's IClassFactory interface. + + This allows other methods to convert a CClassFactory pointer into an + IClassFactory pointer without a typecast and without dealing with the + return value QueryInterface. + + Arguments: + + None + + Return Value: + + A referenced pointer to the object's IClassFactory interface. + +--*/ +{ + AddRef(); + return static_cast(this); +} + +HRESULT +CClassFactory::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method attempts to retrieve the requested interface from the object. + + If the interface is found then the reference count on that interface (and + thus the object itself) is incremented. + + Arguments: + + InterfaceId - the interface the caller is requesting. + + Object - a location to store the interface pointer. + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + // + // This class only supports IClassFactory so check for that. + // + + if (IsEqualIID(InterfaceId, __uuidof(IClassFactory))) + { + *Object = QueryIClassFactory(); + return S_OK; + } + else + { + // + // See if the base class supports the interface. + // + + return CUnknown::QueryInterface(InterfaceId, Object); + } +} + +HRESULT +STDMETHODCALLTYPE +CClassFactory::CreateInstance( + _In_opt_ IUnknown * /* OuterObject */, + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This COM method is the factory routine - it creates instances of the driver + callback class and returns the specified interface on them. + + Arguments: + + OuterObject - only used for aggregation, which our driver callback class + does not support. + + InterfaceId - the interface ID the caller would like to get from our + new object. + + Object - a location to store the referenced interface pointer to the new + object. + + Return Value: + + Status. + +--*/ +{ + HRESULT hr; + + PCMyDriver driver; + + *Object = NULL; + + hr = CMyDriver::CreateInstance(&driver); + + if (SUCCEEDED(hr)) + { + hr = driver->QueryInterface(InterfaceId, Object); + driver->Release(); + } + + return hr; +} + +HRESULT +STDMETHODCALLTYPE +CClassFactory::LockServer( + _In_ BOOL Lock + ) +/*++ + + Routine Description: + + This COM method can be used to keep the DLL in memory. However since the + driver's DllCanUnloadNow function always returns false, this has little + effect. Still it tracks the number of lock and unlock operations. + + Arguments: + + Lock - Whether the caller wants to lock or unlock the "server" + + Return Value: + + S_OK + +--*/ +{ + if (Lock) + { + InterlockedIncrement(&s_LockCount); + } + else + { + InterlockedDecrement(&s_LockCount); + } + return S_OK; +} + diff --git a/general/umdfSkeleton/comsup.h b/general/umdfSkeleton/comsup.h new file mode 100644 index 00000000..5472338c --- /dev/null +++ b/general/umdfSkeleton/comsup.h @@ -0,0 +1,215 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + ComSup.h + +Abstract: + + This module contains classes and functions use for providing COM support + code. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// Forward type declarations. They are here rather than in internal.h as +// you only need them if you choose to use these support classes. +// + +typedef class CUnknown *PCUnknown; +typedef class CClassFactory *PCClassFactory; + +// +// Base class to implement IUnknown. You can choose to derive your COM +// classes from this class, or simply implement IUnknown in each of your +// classes. +// + +class CUnknown : public IUnknown +{ + +// +// Private data members and methods. These are only accessible by the methods +// of this class. +// +private: + + // + // The reference count for this object. Initialized to 1 in the + // constructor. + // + + LONG m_ReferenceCount; + +// +// Protected data members and methods. These are accessible by the subclasses +// but not by other classes. +// +protected: + + // + // The constructor and destructor are protected to ensure that only the + // subclasses of CUnknown can create and destroy instances. + // + + CUnknown( + VOID + ); + + // + // The destructor MUST be virtual. Since any instance of a CUnknown + // derived class should only be deleted from within CUnknown::Release, + // the destructor MUST be virtual or only CUnknown::~CUnknown will get + // invoked on deletion. + // + // If you see that your CMyDevice specific destructor is never being + // called, make sure you haven't deleted the virtual destructor here. + // + + virtual + ~CUnknown( + VOID + ) + { + // Do nothing + } + +// +// Public Methods. These are accessible by any class. +// +public: + + IUnknown * + QueryIUnknown( + VOID + ); + +// +// COM Methods. +// +public: + + // + // IUnknown methods + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ); + + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ); + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); +}; + +// +// Class factory support class. Create an instance of this from your +// DllGetClassObject method and modify the implementation to create +// an instance of your driver event handler class. +// + +class CClassFactory : public CUnknown, public IClassFactory +{ +// +// Private data members and methods. These are only accessible by the methods +// of this class. +// +private: + + // + // The lock count. This is shared across all instances of IClassFactory + // and can be queried through the public IsLocked method. + // + + static LONG s_LockCount; + +// +// Public Methods. These are accessible by any class. +// +public: + + IClassFactory * + QueryIClassFactory( + VOID + ); + +// +// COM Methods. +// +public: + + // + // IUnknown methods + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + _At_(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + // + // IClassFactory methods. + // + + virtual + HRESULT + STDMETHODCALLTYPE + CreateInstance( + _In_opt_ IUnknown *OuterObject, + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + virtual + HRESULT + STDMETHODCALLTYPE + LockServer( + _In_ BOOL Lock + ); +}; diff --git a/general/umdfSkeleton/device.cpp b/general/umdfSkeleton/device.cpp new file mode 100644 index 00000000..677f39e6 --- /dev/null +++ b/general/umdfSkeleton/device.cpp @@ -0,0 +1,238 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Device.cpp + +Abstract: + + This module contains the implementation of the UMDF Skeleton sample driver's + device callback object. + + The skeleton sample device does very little. It does not implement either + of the PNP interfaces so once the device is setup, it won't ever get any + callbacks until the device is removed. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "device.tmh" + +HRESULT +CMyDevice::CreateInstance( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit, + _Out_ PCMyDevice *Device + ) +/*++ + + Routine Description: + + This method creates and initializs an instance of the skeleton driver's + device callback object. + + Arguments: + + FxDeviceInit - the settings for the device. + + Device - a location to store the referenced pointer to the device object. + + Return Value: + + Status + +--*/ +{ + PCMyDevice device; + HRESULT hr; + + // + // Allocate a new instance of the device class. + // + + device = new CMyDevice(); + + if (NULL == device) + { + return E_OUTOFMEMORY; + } + + // + // Initialize the instance. + // + + hr = device->Initialize(FxDriver, FxDeviceInit); + + if (SUCCEEDED(hr)) + { + *Device = device; + } + else + { + device->Release(); + } + + return hr; +} + +HRESULT +CMyDevice::Initialize( + _In_ IWDFDriver * FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit + ) +/*++ + + Routine Description: + + This method initializes the device callback object and creates the + partner device object. + + The method should perform any device-specific configuration that: + * could fail (these can't be done in the constructor) + * must be done before the partner object is created -or- + * can be done after the partner object is created and which aren't + influenced by any device-level parameters the parent (the driver + in this case) might set. + + Arguments: + + FxDeviceInit - the settings for this device. + + Return Value: + + status. + +--*/ +{ + IWDFDevice *fxDevice; + HRESULT hr; + + // + // Configure things like the locking model before we go to create our + // partner device. + // + + // + // Set no locking unless you need an automatic callbacks synchronization + // + + FxDeviceInit->SetLockingConstraint(None); + + // + // TODO: If you're writing a filter driver then indicate that here. + // + // FxDeviceInit->SetFilter(); + // + + // + // TODO: Any per-device initialization which must be done before + // creating the partner object. + // + + // + // Create a new FX device object and assign the new callback object to + // handle any device level events that occur. + // + + // + // QueryIUnknown references the IUnknown interface that it returns + // (which is the same as referencing the device). We pass that to + // CreateDevice, which takes its own reference if everything works. + // + + { + IUnknown *unknown = this->QueryIUnknown(); + + hr = FxDriver->CreateDevice(FxDeviceInit, unknown, &fxDevice); + + unknown->Release(); + } + + // + // If that succeeded then set our FxDevice member variable. + // + + if (SUCCEEDED(hr)) + { + m_FxDevice = fxDevice; + + // + // Drop the reference we got from CreateDevice. Since this object + // is partnered with the framework object they have the same + // lifespan - there is no need for an additional reference. + // + + fxDevice->Release(); + } + + return hr; +} + +HRESULT +CMyDevice::Configure( + VOID + ) +/*++ + + Routine Description: + + This method is called after the device callback object has been initialized + and returned to the driver. It would setup the device's queues and their + corresponding callback objects. + + Arguments: + + FxDevice - the framework device object for which we're handling events. + + Return Value: + + status + +--*/ +{ + // + // TODO: Setup your device queues and I/O forwarding. + // + + return S_OK; +} + +HRESULT +CMyDevice::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method is called to get a pointer to one of the object's callback + interfaces. + + Since the skeleton driver doesn't support any of the device events, this + method simply calls the base class's BaseQueryInterface. + + If the skeleton is extended to include device event interfaces then this + method must be changed to check the IID and return pointers to them as + appropriate. + + Arguments: + + InterfaceId - the interface being requested + + Object - a location to store the interface pointer if successful + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + return CUnknown::QueryInterface(InterfaceId, Object); +} diff --git a/general/umdfSkeleton/device.h b/general/umdfSkeleton/device.h new file mode 100644 index 00000000..d5e1baa6 --- /dev/null +++ b/general/umdfSkeleton/device.h @@ -0,0 +1,115 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions for the UMDF Skeleton sample + driver's device callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// Class for the iotrace driver. +// + +class CMyDevice : public CUnknown +{ + +// +// Private data members. +// +private: + + IWDFDevice *m_FxDevice; + +// +// Private methods. +// + +private: + + CMyDevice( + VOID + ) + { + m_FxDevice = NULL; + } + + HRESULT + Initialize( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ); + +// +// Public methods +// +public: + + // + // The factory method used to create an instance of this driver. + // + + static + HRESULT + CreateInstance( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit, + _Out_ PCMyDevice *Device + ); + + HRESULT + Configure( + VOID + ); + +// +// COM methods +// +public: + + // + // IUnknown methods. + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + _At_(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + +}; diff --git a/general/umdfSkeleton/dllsup.cpp b/general/umdfSkeleton/dllsup.cpp new file mode 100644 index 00000000..3a59f303 --- /dev/null +++ b/general/umdfSkeleton/dllsup.cpp @@ -0,0 +1,177 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + dllsup.cpp + +Abstract: + + This module contains the implementation of the UMDF Skeleton Sample + Driver's entry point and its exported functions for providing COM support. + + This module can be copied without modification to a new UMDF driver. It + depends on some of the code in comsup.cpp & comsup.h to handle DLL + registration and creating the first class factory. + + This module is dependent on the following defines: + + MYDRIVER_TRACING_ID - A wide string passed to WPP when initializing + tracing. For example the skeleton uses + L"Microsoft\\UMDF\\Skeleton" + + MYDRIVER_CLASS_ID - A GUID encoded in struct format used to + initialize the driver's ClassID. + + These are defined in internal.h for the sample. If you choose + to use a different primary include file, you should ensure they are + defined there as well. + +Environment: + + WDF User-Mode Driver Framework (WDF:UMDF) + +--*/ + +#include "internal.h" +#include "dllsup.tmh" + +const GUID CLSID_MyDriverCoClass = MYDRIVER_CLASS_ID; + +BOOL +WINAPI +DllMain( + HINSTANCE ModuleHandle, + DWORD Reason, + PVOID /* Reserved */ + ) +/*++ + + Routine Description: + + This is the entry point and exit point for the I/O trace driver. This + does very little as the I/O trace driver has minimal global data. + + This method initializes tracing. + + Arguments: + + ModuleHandle - the DLL handle for this module. + + Reason - the reason this entry point was called. + + Reserved - unused + + Return Value: + + TRUE + +--*/ +{ + + UNREFERENCED_PARAMETER( ModuleHandle ); + + if (DLL_PROCESS_ATTACH == Reason) + { + // + // Initialize tracing. + // + + WPP_INIT_TRACING(MYDRIVER_TRACING_ID); + + } + else if (DLL_PROCESS_DETACH == Reason) + { + // + // Cleanup tracing. + // + + WPP_CLEANUP(); + } + + return TRUE; +} + +HRESULT +STDAPICALLTYPE +DllGetClassObject( + _In_ REFCLSID ClassId, + _In_ REFIID InterfaceId, + _Outptr_ LPVOID *Interface + ) +/*++ + + Routine Description: + + This routine is called by COM in order to instantiate the + driver callback object and do an initial query interface on it. + + This method only creates an instance of the driver's class factory, as this + is the minimum required to support UMDF. + + Arguments: + + ClassId - the CLSID of the object being "gotten" + + InterfaceId - the interface the caller wants from that object. + + Interface - a location to store the referenced interface pointer + + Return Value: + + S_OK if the function succeeds or error indicating the cause of the + failure. + +--*/ +{ + PCClassFactory factory; + + HRESULT hr = S_OK; + + *Interface = NULL; + + // + // If the CLSID doesn't match that of our "coclass" (defined in the IDL + // file) then we can't create the object the caller wants. This may + // indicate that the COM registration is incorrect, and another CLSID + // is referencing this drvier. + // + + if (IsEqualCLSID(ClassId, CLSID_MyDriverCoClass) == false) + { + Trace( + TRACE_LEVEL_ERROR, + L"ERROR: Called to create instance of unrecognized class (%!GUID!)", + &ClassId + ); + + return CLASS_E_CLASSNOTAVAILABLE; + } + + // + // Create an instance of the class factory for the caller. + // + + factory = new CClassFactory(); + + if (NULL == factory) + { + hr = E_OUTOFMEMORY; + } + + // + // Query the object we created for the interface the caller wants. After + // that we release the object. This will drive the reference count to + // 1 (if the QI succeeded an referenced the object) or 0 (if the QI failed). + // In the later case the object is automatically deleted. + // + + if (SUCCEEDED(hr)) + { + hr = factory->QueryInterface(InterfaceId, Interface); + factory->Release(); + } + + return hr; +} diff --git a/general/umdfSkeleton/driver.cpp b/general/umdfSkeleton/driver.cpp new file mode 100644 index 00000000..2061ec2d --- /dev/null +++ b/general/umdfSkeleton/driver.cpp @@ -0,0 +1,220 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Driver.cpp + +Abstract: + + This module contains the implementation of the UMDF Skeleton Sample's + core driver callback object. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "driver.tmh" + +HRESULT +CMyDriver::CreateInstance( + _Out_ PCMyDriver *Driver + ) +/*++ + + Routine Description: + + This static method is invoked in order to create and initialize a new + instance of the driver class. The caller should arrange for the object + to be released when it is no longer in use. + + Arguments: + + Driver - a location to store a referenced pointer to the new instance + + Return Value: + + S_OK if successful, or error otherwise. + +--*/ +{ + PCMyDriver driver; + HRESULT hr; + + // + // Allocate the callback object. + // + + driver = new CMyDriver(); + + if (NULL == driver) + { + return E_OUTOFMEMORY; + } + + // + // Initialize the callback object. + // + + hr = driver->Initialize(); + + if (SUCCEEDED(hr)) + { + // + // Store a pointer to the new, initialized object in the output + // parameter. + // + + *Driver = driver; + } + else + { + + // + // Release the reference on the driver object to get it to delete + // itself. + // + + driver->Release(); + } + + return hr; +} + +HRESULT +CMyDriver::Initialize( + VOID + ) +/*++ + + Routine Description: + + This method is called to initialize a newly created driver callback object + before it is returned to the creator. Unlike the constructor, the + Initialize method contains operations which could potentially fail. + + Arguments: + + None + + Return Value: + + None + +--*/ +{ + return S_OK; +} + +HRESULT +CMyDriver::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Interface + ) +/*++ + + Routine Description: + + This method returns a pointer to the requested interface on the callback + object.. + + Arguments: + + InterfaceId - the IID of the interface to query/reference + + Interface - a location to store the interface pointer. + + Return Value: + + S_OK if the interface is supported. + E_NOINTERFACE if it is not supported. + +--*/ +{ + if (IsEqualIID(InterfaceId, __uuidof(IDriverEntry))) + { + *Interface = QueryIDriverEntry(); + return S_OK; + } + else + { + return CUnknown::QueryInterface(InterfaceId, Interface); + } +} + +HRESULT +CMyDriver::OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ) +/*++ + + Routine Description: + + The FX invokes this method when it wants to install our driver on a device + stack. This method creates a device callback object, then calls the Fx + to create an Fx device object and associate the new callback object with + it. + + Arguments: + + FxWdfDriver - the Fx driver object. + + FxDeviceInit - the initialization information for the device. + + Return Value: + + status + +--*/ +{ + HRESULT hr; + + PCMyDevice device = NULL; + + // + // TODO: Do any per-device initialization (reading settings from the + // registry for example) that's necessary before creating your + // device callback object here. Otherwise you can leave such + // initialization to the initialization of the device event + // handler. + // + + // + // Create a new instance of our device callback object + // + + hr = CMyDevice::CreateInstance(FxWdfDriver, FxDeviceInit, &device); + + // + // TODO: Change any per-device settings that the object exposes before + // calling Configure to let it complete its initialization. + // + + // + // If that succeeded then call the device's construct method. This + // allows the device to create any queues or other structures that it + // needs now that the corresponding fx device object has been created. + // + + if (SUCCEEDED(hr)) + { + hr = device->Configure(); + } + + // + // Release the reference on the device callback object now that it's been + // associated with an fx device object. + // + + if (NULL != device) + { + device->Release(); + } + + return hr; +} diff --git a/general/umdfSkeleton/driver.h b/general/umdfSkeleton/driver.h new file mode 100644 index 00000000..c5664ac0 --- /dev/null +++ b/general/umdfSkeleton/driver.h @@ -0,0 +1,149 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Driver.h + +Abstract: + + This module contains the type definitions for the UMDF Skeleton sample's + driver callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// This class handles driver events for the skeleton sample. In particular +// it supports the OnDeviceAdd event, which occurs when the driver is called +// to setup per-device handlers for a new device stack. +// + +class CMyDriver : public CUnknown, public IDriverEntry +{ +// +// Private data members. +// +private: + +// +// Private methods. +// +private: + + // + // Returns a refernced pointer to the IDriverEntry interface. + // + + IDriverEntry * + QueryIDriverEntry( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + HRESULT + Initialize( + VOID + ); + +// +// Public methods +// +public: + + // + // The factory method used to create an instance of this driver. + // + + static + HRESULT + CreateInstance( + _Out_ PCMyDriver *Driver + ); + +// +// COM methods +// +public: + + // + // IDriverEntry methods + // + + virtual + HRESULT + STDMETHODCALLTYPE + OnInitialize( + _In_ IWDFDriver *FxWdfDriver + ) + { + UNREFERENCED_PARAMETER( FxWdfDriver ); + + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ); + + virtual + VOID + STDMETHODCALLTYPE + OnDeinitialize( + _In_ IWDFDriver *FxWdfDriver + ) + { + UNREFERENCED_PARAMETER( FxWdfDriver ); + + return; + } + + // + // IUnknown methods. + // + // We have to implement basic ones here that redirect to the + // base class becuase of the multiple inheritance. + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + _At_(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); +}; diff --git a/general/umdfSkeleton/exports.def b/general/umdfSkeleton/exports.def new file mode 100644 index 00000000..a1ab223c --- /dev/null +++ b/general/umdfSkeleton/exports.def @@ -0,0 +1,10 @@ +; Skeleton.def : Declares the module parameters. + +; +; TODO: Change the library name here to match your binary name. +; + +LIBRARY "UMDFSkeleton.DLL" + +EXPORTS + DllGetClassObject PRIVATE diff --git a/general/umdfSkeleton/internal.h b/general/umdfSkeleton/internal.h new file mode 100644 index 00000000..bd8c3c6d --- /dev/null +++ b/general/umdfSkeleton/internal.h @@ -0,0 +1,90 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Internal.h + +Abstract: + + This module contains the local type definitions for the UMDF Skeleton + driver sample. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +// +// Include the WUDF DDI +// + +#include "wudfddi.h" + +// +// Use specstrings for in/out annotation of function parameters. +// + +#include "specstrings.h" + +// +// Forward definitions of classes in the other header files. +// + +typedef class CMyDriver *PCMyDriver; +typedef class CMyDevice *PCMyDevice; + +// +// Define the tracing flags. +// +// TODO: Choose a different trace control GUID +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + MyDriverTraceControl, (e7541cdd,30e8,4b50,aeb0,51927330ae64), \ + \ + WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ + ) + +#define WPP_FLAG_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAG_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); +// end_wpp +// + +// +// Driver specific #defines +// +// TODO: Change these values to be appropriate for your driver. +// + +#define MYDRIVER_TRACING_ID L"Microsoft\\UMDF\\Skeleton" +#define MYDRIVER_CLASS_ID { 0xd4112073, 0xd09b, 0x458f, { 0xa5, 0xaa, 0x35, 0xef, 0x21, 0xee, 0xf5, 0xde } } + + +// +// Include the type specific headers. +// + +#include "comsup.h" +#include "driver.h" +#include "device.h" diff --git a/general/umdfSkeleton/umdfSkeleton.sln b/general/umdfSkeleton/umdfSkeleton.sln new file mode 100644 index 00000000..2deba81c --- /dev/null +++ b/general/umdfSkeleton/umdfSkeleton.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UMDFSkeleton", "UMDFSkeleton.vcxproj", "{A0B8804E-85A9-4432-A812-E9F6947C647A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A0B8804E-85A9-4432-A812-E9F6947C647A}.Debug|Win32.ActiveCfg = Debug|Win32 + {A0B8804E-85A9-4432-A812-E9F6947C647A}.Debug|Win32.Build.0 = Debug|Win32 + {A0B8804E-85A9-4432-A812-E9F6947C647A}.Release|Win32.ActiveCfg = Release|Win32 + {A0B8804E-85A9-4432-A812-E9F6947C647A}.Release|Win32.Build.0 = Release|Win32 + {A0B8804E-85A9-4432-A812-E9F6947C647A}.Debug|x64.ActiveCfg = Debug|x64 + {A0B8804E-85A9-4432-A812-E9F6947C647A}.Debug|x64.Build.0 = Debug|x64 + {A0B8804E-85A9-4432-A812-E9F6947C647A}.Release|x64.ActiveCfg = Release|x64 + {A0B8804E-85A9-4432-A812-E9F6947C647A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/gpio/samples/ReadMe.md b/gpio/samples/ReadMe.md new file mode 100644 index 00000000..97464c5e --- /dev/null +++ b/gpio/samples/ReadMe.md @@ -0,0 +1,28 @@ +GPIO Sample Drivers +=================== + +The GPIO samples contain annotated code to illustrate how to write a [GPIO controller driver](http://msdn.microsoft.com/en-us/library/windows/hardware/hh439509) that works in conjunction with the [GPIO framework extension](http://msdn.microsoft.com/en-us/library/windows/hardware/hh439512) (GpioClx) to handle GPIO I/O control requests, and a peripheral driver that runs in kernel mode and uses GPIO resources. For a sample that shows how to write a GPIO peripheral driver that runs in user mode, please refer to the SPB accelerometer sample driver (SPB\\peripherals\\accelerometer). + +The GPIO sample set contains the following three samples. + + ++++ + + + + + + + + + + + +
Minifilter Sample +Description

SimGpio

+

The files in this sample contain the source code for a GPIO controller driver that communicates with GpioClx through the GpioClx device driver interface (DDI). The GPIO controller driver is written for a hypothetical memory-mapped GPIO controller (simgpio). The code is meant to be purely instructional. An ASL file illustrates how to specify a GPIO interrupt and I/O descriptor in the ACPI firmware.

SimGpio_I2C

+

The files in this sample contain the source code for a GPIO controller driver that communicates with GpioClx through the GpioClx DDI. In contrast to the SimGpio sample, the GPIO controller in this sample is not memory-mapped. The GPIO controller driver is written for a hypothetical GPIO controller that resides on an I2C bus (simgpio_i2c). The code is meant to be purely instructional. An ASL file illustrates how to specify a GPIO interrupt and I/O descriptor in the ACPI firmware.

+ diff --git a/gpio/samples/sim.sln b/gpio/samples/sim.sln new file mode 100644 index 00000000..edbafcc1 --- /dev/null +++ b/gpio/samples/sim.sln @@ -0,0 +1,72 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simdevice", "Simdevice", "{B836C8C4-99F3-4B16-8B3D-558EFC65F9D3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simdeviceumdf", "Simdeviceumdf", "{5302F4A4-2BE9-43C9-BF4E-4ED0D4A131A6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simgpio", "Simgpio", "{3544A23D-F159-4D5F-B41D-A045D7BB4220}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simgpio_i2c", "Simgpio_i2c", "{E0A658B7-1A02-4468-AB84-54444838B9BD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simdevice", "simdevice\simdevice.vcxproj", "{57BFED37-06F0-435D-A4CA-610B56B8987F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimdeviceUMDF", "simdeviceumdf\SimdeviceUMDF.vcxproj", "{D768CF1C-AE06-4C12-B11D-21C5D70082F7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simgpio", "simgpio\simgpio.vcxproj", "{BCCD1049-70D2-4C07-BE8C-E9B609BD1714}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simgpio_i2c", "simgpio_i2c\simgpio_i2c.vcxproj", "{FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Debug|Win32.ActiveCfg = Debug|Win32 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Debug|Win32.Build.0 = Debug|Win32 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Release|Win32.ActiveCfg = Release|Win32 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Release|Win32.Build.0 = Release|Win32 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Debug|x64.ActiveCfg = Debug|x64 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Debug|x64.Build.0 = Debug|x64 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Release|x64.ActiveCfg = Release|x64 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Release|x64.Build.0 = Release|x64 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Debug|Win32.ActiveCfg = Debug|Win32 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Debug|Win32.Build.0 = Debug|Win32 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Release|Win32.ActiveCfg = Release|Win32 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Release|Win32.Build.0 = Release|Win32 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Debug|x64.ActiveCfg = Debug|x64 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Debug|x64.Build.0 = Debug|x64 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Release|x64.ActiveCfg = Release|x64 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Release|x64.Build.0 = Release|x64 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Debug|Win32.ActiveCfg = Debug|Win32 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Debug|Win32.Build.0 = Debug|Win32 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Release|Win32.ActiveCfg = Release|Win32 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Release|Win32.Build.0 = Release|Win32 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Debug|x64.ActiveCfg = Debug|x64 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Debug|x64.Build.0 = Debug|x64 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Release|x64.ActiveCfg = Release|x64 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Release|x64.Build.0 = Release|x64 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Debug|Win32.ActiveCfg = Debug|Win32 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Debug|Win32.Build.0 = Debug|Win32 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Release|Win32.ActiveCfg = Release|Win32 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Release|Win32.Build.0 = Release|Win32 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Debug|x64.ActiveCfg = Debug|x64 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Debug|x64.Build.0 = Debug|x64 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Release|x64.ActiveCfg = Release|x64 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {57BFED37-06F0-435D-A4CA-610B56B8987F} = {B836C8C4-99F3-4B16-8B3D-558EFC65F9D3} + {D768CF1C-AE06-4C12-B11D-21C5D70082F7} = {5302F4A4-2BE9-43C9-BF4E-4ED0D4A131A6} + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714} = {3544A23D-F159-4D5F-B41D-A045D7BB4220} + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2} = {E0A658B7-1A02-4468-AB84-54444838B9BD} + EndGlobalSection +EndGlobal diff --git a/gpio/samples/simdevice/GpioSample.asl b/gpio/samples/simdevice/GpioSample.asl new file mode 100644 index 00000000..c393d9da --- /dev/null +++ b/gpio/samples/simdevice/GpioSample.asl @@ -0,0 +1,124 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + GpioSample.asl + +Abstract: + + This sample ASL file describes a sample GPIO device and a sample peripheral + device which consumes IO and interrupt resources from the GPIO device. Please + note that: + + 1. The memory and IO descriptor under the GPIO device are simply examples of + what can be described. They are commented out to illustrate this point. Actual + values will vary according to the platform specifications (e.g. GIC and memory) + + 2. The sample ASL DSDT definition block defines only the components relevant + to demonstrate GPIO IO and interrupt resource usage. Rest of the DSDT will vary + according to platform specifications. + + 3. The PNP IDs for the GPIO and peripheral device are for demonstration purposes + only. Actual values need to reflect those chosen for the actual GPIO or + peripheral device. + +--*/ + + +DefinitionBlock ("DSDT.AML", "DSDT", 0x02, "MSFT", "SAMPLE", 0x1) { + + // + // System Bus + // + + Scope (\_SB) { + + // + // Sample GPIO device + // + + Device(GPIO) { + Name (_ADR, 0) + Name (_HID, "TEST0001") + Name (_CID, "TEST0001") + Name(_UID, 4) + + Method (_CRS, 0x0, NotSerialized) { + Name (RBUF, ResourceTemplate () { + + // + // Interrupt resource. In this example, banks 0 & 1 share the same + // interrupt to the parent controller and similarly banks 2 & 3. + // + // N.B. The definition below is chosen for an arbitrary + // test platform. It needs to be changed to reflect the hardware + // configuration of the actual platform. + // + + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {50} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {50} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {51} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {51} + + // + // Memory resource. The definition below is chosen for an arbitrary + // test platform. It needs to be changed to reflect the hardware + // configuration of the actual platform. + // + + Memory32Fixed(ReadWrite, 0x00100000, 0x18) + }) + + Return (RBUF) + } + + Method (_STA, 0x0, NotSerialized) { + Return(0xf) + } + + // + // Sample peripheral device + // + + Device (TDEV) { + Name (_ADR, 0) + Name (_HID, "TEST0003") + Name (_CID, "TEST0003") + Name (_UID, 1) + + Method (_CRS, 0x0, NotSerialized) { + Name (RBUF, ResourceTemplate () { + + // + // GPIO Interrupt Resources + // + + GpioInt(Edge, ActiveHigh, Shared, PullUp, 0, "\\_SB.GPIO", 0, ResourceConsumer,, RawDataBuffer() {1}) {1} + // GpioInt(Edge, ActiveHigh, Shared, PullUp, 0, "\\_SB.GPIO", 0, ResourceConsumer,, RawDataBuffer() {1}) {2} + + // + // GPIO IO Resources + // + + GpioIo(Exclusive, PullUp, 0, 0,, "\\_SB.GPIO",0, ResourceConsumer, , RawDataBuffer() {1}) {10} + GpioIo(Exclusive, PullUp, 0, 0,, "\\_SB.GPIO",0, ResourceConsumer, , RawDataBuffer() {1}) {11} + }) + + Return (RBUF) + } + + Method (_STA, 0x0, NotSerialized) { + Return(0xf) + } + } + } + } + } diff --git a/gpio/samples/simdevice/common.h b/gpio/samples/simdevice/common.h new file mode 100644 index 00000000..315044c8 --- /dev/null +++ b/gpio/samples/simdevice/common.h @@ -0,0 +1,84 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + common.h + +Abstract: + + Header file that provide some utility functionalities to the sample device driver + +Environment: + + Kernel mode + +--*/ + +#pragma once + +#include +#pragma warning(disable:4201) // disable nameless struct/union warnings +#include +#pragma warning(default:4201) + +#define NTSTRSAFE_LIB +#include + +#ifndef MAX_USHORT +#define MAX_USHORT ((USHORT)-1) +#endif + +#ifndef MAX_ULONG +#define MAX_ULONG ((ULONG)-1) +#endif + +#ifndef MAX_ULONG64 +#define MAX_ULONG64 ((ULONG64)-1) +#endif + +// +// Useful macros for setting and checking flags. +// + +#define SET_FLAGS(_x, _f) ((_x) |= (_f)) +#define CLEAR_FLAGS(_x, _f) ((_x) &= ~(_f)) +#define CLEAR_OTHER_FLAGS(_x, _f) ((_x) &= (_f)) +#define CHECK_FLAG(_x, _f) ((_x) & (_f)) + +// +// Macros for rounding up or down. +// + +#define ROUND_DOWN(_x, _alignment) \ + ((_alignment == 1) ? (_x) : (((_x) / (_alignment)) * (_alignment))) + +#define ROUND_UP(_x, _alignment) \ + ROUND_DOWN((_x) + (_alignment) - 1, (_alignment)) + +// +// Macros for find minimum and maximum of two integers. +// + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) < (b)) ? (b) : (a)) + +// +// Define macros to allow easy pointer arithmetic. +// + +#define Add2Ptr(_Ptr, _Value) ((PVOID)((PUCHAR)(_Ptr) + (_Value))) +#define PtrOffset(_Base, _Ptr) ((ULONG_PTR)(_Ptr) - (ULONG_PTR)(_Base)) + +// 4127 -- Conditional Expression is Constant warning +#define WHILE(constant) \ +__pragma(warning(disable: 4127)) while(constant) __pragma(warning(default: 4127)) + +#define FIELD_OFFSET_AND_SIZE(t, f) \ + (FIELD_OFFSET(t, f) + FIELD_SIZE(t, f)) \ No newline at end of file diff --git a/gpio/samples/simdevice/simdevice.c b/gpio/samples/simdevice/simdevice.c new file mode 100644 index 00000000..f54f1a84 --- /dev/null +++ b/gpio/samples/simdevice/simdevice.c @@ -0,0 +1,819 @@ +/*++ + +Copyright (c) 1990-2010 Microsoft Corporation + +Module Name: + + SimpleDevice.c + +Abstract: + + This is a simple device driver that consumes GPIO pins for I/O and interrupt. + + +Environment: + + Kernel mode + +--*/ + +// +// ------------------------------------------------------------------- Includes +// + +#include "common.h" +#include +#define RESHUB_USE_HELPER_ROUTINES +#include + +// +// -------------------------------------------------------------------- Defines +// + +#define MAX_NUMBER_IO_RESOURCES 2 + +// +// -------------------------------------------------------------------- Types +// + +typedef struct _SAMPLE_DRV_DEVICE_EXTENSION { + ULONG IoResourceCount; + ULONG InterruptCount; + LARGE_INTEGER ConnectionIds[MAX_NUMBER_IO_RESOURCES]; +} SAMPLE_DRV_DEVICE_EXTENSION, *PSAMPLE_DRV_DEVICE_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SAMPLE_DRV_DEVICE_EXTENSION, SampleDrvGetDeviceExtension) + +// +// ----------------------------------------------------------------- Prototypes +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD SampleDrvEvtDeviceAdd; +EVT_WDF_DEVICE_D0_ENTRY SampleDrvEvtDeviceD0Entry; +EVT_WDF_DEVICE_PREPARE_HARDWARE SampleDrvEvtDevicePrepareHardware; +EVT_WDF_INTERRUPT_DPC SampleDrvInterruptDpc; +EVT_WDF_INTERRUPT_ISR SampleDrvInterruptIsr; +EVT_WDF_INTERRUPT_ISR SampleDrvInterruptPassiveCallback; + +NTSTATUS +TestReadWrite ( + _In_ WDFDEVICE Device, + _In_ PCUNICODE_STRING RequestString, + _In_ BOOLEAN ReadOperation, + _Inout_ PUCHAR Data, + _In_ _In_range_(>, 0) ULONG Size, + _Out_ WDFIOTARGET *IoTargetOut + ); + +// +// -------------------------------------------------------------------- Pragmas +// + +#pragma alloc_text(PAGE, SampleDrvEvtDeviceAdd) +#pragma alloc_text(PAGE, SampleDrvEvtDevicePrepareHardware) + +// +// ------------------------------------------------------------------ Functions +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is the driver initialization entry point. + +Arguments: + + DriverObject - Pointer to the driver object created by the I/O manager. + + RegistryPath - Pointer to the driver specific registry key. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDFDRIVER Driver; + WDF_DRIVER_CONFIG DriverConfig; + NTSTATUS Status; + + // + // Initialize the driver configuration structure. + // + + WDF_DRIVER_CONFIG_INIT(&DriverConfig, SampleDrvEvtDeviceAdd); + + // + // Create a framework driver object to represent our driver. + // + + Status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &DriverConfig, + &Driver); + + if (!NT_SUCCESS(Status)) { + goto DriverEntryEnd; + } + +DriverEntryEnd: + return Status; +} + +BOOLEAN +SampleDrvInterruptIsr ( + _In_ WDFINTERRUPT Interrupt, + _In_ ULONG MessageID + ) + +/*++ + +Routine Description: + + This routine is the interrupt service routine for the sample device + + N.B. This driver assumes that the interrupt line is not shared with any + other device. Hence it always claims the interrupt. + +Arguments: + + Interupt - Supplies a handle to interrupt object (WDFINTERRUPT) for this + device. + + MessageID - Supplies the MSI message ID for MSI-based interrupts. + +Return Value: + + Always TRUE. + +--*/ + +{ + + + UNREFERENCED_PARAMETER(Interrupt); + UNREFERENCED_PARAMETER(MessageID); + + // + // The sample driver always returns TRUE (e.g. claiming the interrupt) + // from its ISR. In reality, the driver needs to do whatever necessary to + // quiesce the interrupt before claiming the interrupt. In case of spurious + // interrupts, the ISR returns FALSE. If additional work needs to be done + // at a lower IRQL, schedule a DPC. + // + + return TRUE; +} + +#if 0 +BOOLEAN +SampleDrvInterruptPassiveCallback ( + _In_ WDFINTERRUPT Interrupt, + _In_ ULONG MessageID + ) + +/*++ + +Routine Description: + + This routine is the passive interrupt callback routine for the sample device. + As its name suggests, this routine is always invoked at PASSIVE_LEVEL. + This is useful in scenarios where the device is located behind a slow serial + peripheral bus(SPB) and requires communication (possible only at PASSIVE_LEVEL) + over the bus in quiescing the interrupt source. + + N.B. It is possible for passive interrupt callback and DIRQL ISRs to + coexist for the same device and/or IDT entry. Interrupt objects chained to a + given IDT entry are always ordered (at interrupt connect time) by the OS such + that the DIRQL ISR interrupt objects are located before the passive callback + ones. Consequently, during interrupt dispatching, the OS would walk the list + in that order until the first ISR/passive callback returns TRUE to claim the + interrupt. + +Arguments: + + Interupt - Supplies a handle to interrupt object (WDFINTERRUPT) for this + device. + + MessageID - Supplies the MSI message ID for MSI-based interrupts. + +Return Value: + + Always TRUE. + +--*/ + +{ + + + UNREFERENCED_PARAMETER(Interrupt); + UNREFERENCED_PARAMETER(MessageID); + + // + // The sample driver always returns TRUE (e.g. claiming the interrupt) + // from its passive callback. In reality, the driver needs to do whatever necessary to + // quiesce the interrupt before claiming the interrupt. + // + + return TRUE; +} +#endif + +VOID +SampleDrvInterruptDpc ( + _In_ WDFINTERRUPT WdfInterrupt, + _In_ WDFOBJECT WdfDevice + ) + +/*++ + +Routine Description: + + This routine is the DPC callback for the ISR. This routine is unused. + +Arguments: + + Interupt - Supplies a handle to interrupt object (WDFINTERRUPT) for this + device. + + Device - Supplies a handle to the framework device object. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(WdfInterrupt); + UNREFERENCED_PARAMETER(WdfDevice); + return; +} + +_Use_decl_annotations_ +NTSTATUS +SampleDrvEvtDeviceAdd ( + WDFDRIVER Driver, + PWDFDEVICE_INIT DeviceInit +) + +/*++ + +Routine Description: + + This routine is the AddDevice entry point for the sample device driver. + It sets the ISR and DPC routine handlers for the interrupt and the passive + level callback for the passive interrupt + + N.B. The sample device expects two interrupt resources in connecting its + DIRQL ISR and PASSIVE_LEVEL callback. + +Arguments: + + Driver - Supplies a handle to the driver object created in DriverEntry. + + DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT + structure. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDF_PNPPOWER_EVENT_CALLBACKS Callbacks; + WDFDEVICE Device; + WDF_OBJECT_ATTRIBUTES FdoAttributes; + WDF_INTERRUPT_CONFIG InterruptConfiguration; + NTSTATUS Status; + WDFINTERRUPT WdfInterrupt; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + // + // Set PnP callbacks for prepare/release hardware and D0 entry/exit. All + // callbacks not overriden here will be handled by the framework in the + // default manner. + // + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&Callbacks); + Callbacks.EvtDevicePrepareHardware = SampleDrvEvtDevicePrepareHardware; + Callbacks.EvtDeviceD0Entry = SampleDrvEvtDeviceD0Entry; + + // + // Register the PnP callbacks with the framework. + // + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &Callbacks); + + // + // Initialize FDO attributes with the sample device extension. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&FdoAttributes); + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&FdoAttributes, SAMPLE_DRV_DEVICE_EXTENSION); + + // + // Call the framework to create the device and attach it to the lower stack. + // + + Status = WdfDeviceCreate(&DeviceInit, &FdoAttributes, &Device); + if (!NT_SUCCESS(Status)) { + goto EvtDeviceAddEnd; + } + + // + // Create an interrupt object for the DIRQL ISR + // + + WDF_INTERRUPT_CONFIG_INIT(&InterruptConfiguration, + SampleDrvInterruptIsr, + SampleDrvInterruptDpc); + + Status = WdfInterruptCreate(Device, + &InterruptConfiguration, + WDF_NO_OBJECT_ATTRIBUTES, + &WdfInterrupt); + + if (!NT_SUCCESS(Status)) { + + goto EvtDeviceAddEnd; + } + +#if 0 + // + // Create an interrupt object for the passive interrupt callback. Note that + // the interrupt object is chained to the same interrupt line/IDT as the + // DIRQL one. + // + + WDF_INTERRUPT_CONFIG_INIT(&InterruptConfiguration, + SampleDrvInterruptIsr, + NULL); + + // + // Set passive handling to true + // + + InterruptConfiguration.PassiveHandling = TRUE; + + Status = WdfInterruptCreate(Device, + &InterruptConfiguration, + WDF_NO_OBJECT_ATTRIBUTES, + &WdfInterrupt); +#endif + +EvtDeviceAddEnd: + return Status; +} + +NTSTATUS +SampleDrvEvtDevicePrepareHardware ( + _In_ WDFDEVICE Device, + _In_ WDFCMRESLIST ResourcesRaw, + _In_ WDFCMRESLIST ResourcesTranslated + ) + +/*++ + +Routine Description: + + This routine is called by the framework when the PnP manager sends an + IRP_MN_START_DEVICE request to the driver stack. + +Arguments: + + Device - Supplies a handle to a framework device object. + + ResourcesRaw - Supplies a handle to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + ResourcesTranslated - Supplies a handle to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + NT status code. + +--*/ + +{ + + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + PSAMPLE_DRV_DEVICE_EXTENSION SampleDrvExtension; + ULONG Index; + ULONG ResourceCount; + NTSTATUS Status; + ULONG IoResourceIndex; + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(ResourcesRaw); + + PAGED_CODE(); + + SampleDrvExtension = SampleDrvGetDeviceExtension(Device); + Status = STATUS_SUCCESS; + IoResourceIndex = 0; + + SampleDrvExtension->InterruptCount = 0; + + // + // Walk through the resource list and map all the resources. Only one + // memory resource and one interrupt is expected. + // + + ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated); + for (Index = 0; Index < ResourceCount; Index += 1) { + Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index); + switch(Descriptor->Type) { + + // + // This memory resource supplies the base of the device registers. + // + + case CmResourceTypeConnection: + + // + // Check against expected connection type + // + + if ((Descriptor->u.Connection.Class == + CM_RESOURCE_CONNECTION_CLASS_GPIO) && + (Descriptor->u.Connection.Type == + CM_RESOURCE_CONNECTION_TYPE_GPIO_IO)) { + + SampleDrvExtension->ConnectionIds[IoResourceIndex].LowPart = + Descriptor->u.Connection.IdLowPart; + SampleDrvExtension->ConnectionIds[IoResourceIndex].HighPart = + Descriptor->u.Connection.IdHighPart; + IoResourceIndex++; + } else { + + Status = STATUS_UNSUCCESSFUL; + } + + break; + + // + // Interrupt resource + // + + case CmResourceTypeInterrupt: + SampleDrvExtension->InterruptCount++; + + default: + break; + } + + if (!NT_SUCCESS(Status)) { + goto DevicePrepareHardwareEnd; + } + } + + // + // Ensure that at least two interrupt resources are defined. One for DIRQL + // and another for the passive level ISR + // + + NT_ASSERT(SampleDrvExtension->InterruptCount > 0); + + if (SampleDrvExtension->InterruptCount < 1) { + Status = STATUS_UNSUCCESSFUL; + goto DevicePrepareHardwareEnd; + } + + // + // Store the number of GPIO IO connection strings + // + + SampleDrvExtension->IoResourceCount = IoResourceIndex; + +DevicePrepareHardwareEnd: + return Status; +} + +NTSTATUS +SampleDrvEvtDeviceD0Entry ( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousPowerState + ) + +/*++ + +Routine Description: + + This routine is invoked by the framework to program the device to goto + D0, which is the working state. The framework invokes callback every + time the hardware needs to be (re-)initialized. This includes after + IRP_MN_START_DEVICE, IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE, + and IRP_MN_SET_POWER-D0. + + N.B. This function is not marked pageable because this function is in + the device power up path. When a function is marked pagable and the + code section is paged out, it will generate a page fault which could + impact the fast resume behavior because the client driver will have + to wait until the system drivers can service this page fault. + +Arguments: + + Device - Supplies a handle to the framework device object. + + PreviousPowerState - WDF_POWER_DEVICE_STATE-typed enumerator that identifies + the device power state that the device was in before this transition + to D0. + +Return Value: + + NTSTATUS code. A failure here will indicate a fatal error and cause the + framework to tear down the stack. + +--*/ + +{ + + BYTE Data; + NTSTATUS Status; + WDFIOTARGET ReadTarget; + WDFIOTARGET WriteTarget; + PSAMPLE_DRV_DEVICE_EXTENSION SampleDrvExtension; + UNICODE_STRING ReadString; + WCHAR ReadStringBuffer[100]; + UNICODE_STRING WriteString; + WCHAR WriteStringBuffer[100]; + + UNREFERENCED_PARAMETER(PreviousPowerState); + + ReadTarget = NULL; + WriteTarget = NULL; + + SampleDrvExtension = SampleDrvGetDeviceExtension(Device); + + // + // For demonstration purporses, the sample device consumes two IO resources, + // the first of which will be used for input, and the second for output. + // + + RtlInitEmptyUnicodeString(&ReadString, + ReadStringBuffer, + sizeof(ReadStringBuffer)); + + RtlInitEmptyUnicodeString(&WriteString, + WriteStringBuffer, + sizeof(WriteStringBuffer)); + + + // + // Construct full-path string for GPIO read operation + // + + Status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&ReadString, + SampleDrvExtension->ConnectionIds[0].LowPart, + SampleDrvExtension->ConnectionIds[0].HighPart); + + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // Construct full-path string for GPIO write operation + // + + Status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&WriteString, + SampleDrvExtension->ConnectionIds[1].LowPart, + SampleDrvExtension->ConnectionIds[1].HighPart); + + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // Perform the read operation + // + + Data = 0x0; + Status = TestReadWrite(Device, &ReadString, TRUE, &Data, sizeof(Data), &ReadTarget); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // Perform the write operation + // + + Status = TestReadWrite(Device, &WriteString, FALSE, &Data, sizeof(Data), &WriteTarget); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + +Cleanup: + + if (ReadTarget != NULL) { + WdfIoTargetClose(ReadTarget); + WdfObjectDelete(ReadTarget); + } + + if (WriteTarget != NULL) { + WdfIoTargetClose(WriteTarget); + WdfObjectDelete(WriteTarget); + } + + return Status; +} + +NTSTATUS +TestReadWrite ( + _In_ WDFDEVICE Device, + _In_ PCUNICODE_STRING RequestString, + _In_ BOOLEAN ReadOperation, + _Inout_ PUCHAR Data, + _In_ _In_range_(>, 0) ULONG Size, + _Out_ WDFIOTARGET *IoTargetOut + ) + +/*++ + +Routine Description: + + This is a utility routine to test read or write on a set of GPIO pins. + +Arguments: + + Device - Supplies a handle to the framework device object. + + RequestString - Supplies a pointer to the unicode string to be opened. + + ReadOperation - Supplies a boolean that identifies whether read (TRUE) or + write (FALSE) should be performed. + + Data - Supplies a pointer containing the buffer that should be read from + or written to. + + Size - Supplies the size of the data buffer in bytes. + + IoTargetOut - Supplies a pointer that receives the IOTARGET created by + WDF. + +Return Value: + + None. + +--*/ + +{ + + WDF_OBJECT_ATTRIBUTES Attributes; + WDFREQUEST IoctlRequest; + WDFIOTARGET IoTarget; + ULONG DesiredAccess; + WDFMEMORY WdfMemory; + WDF_OBJECT_ATTRIBUTES RequestAttributes; + WDF_REQUEST_SEND_OPTIONS SendOptions; + NTSTATUS Status; + WDF_OBJECT_ATTRIBUTES ObjectAttributes; + WDF_IO_TARGET_OPEN_PARAMS OpenParams; + + WDF_OBJECT_ATTRIBUTES_INIT(&ObjectAttributes); + ObjectAttributes.ParentObject = Device; + + IoctlRequest = NULL; + IoTarget = NULL; + + if ((Data == NULL) || (Size == 0)) { + Status = STATUS_INVALID_PARAMETER; + goto TestReadWriteEnd; + } + + Status = WdfIoTargetCreate(Device, + &ObjectAttributes, + &IoTarget); + + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + // + // Specify desired file access + // + + if (ReadOperation != FALSE) { + DesiredAccess = FILE_GENERIC_READ; + + } else { + DesiredAccess = FILE_GENERIC_WRITE; + } + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, + RequestString, + DesiredAccess); + + // + // Open the IoTarget for I/O operation + // + + Status = WdfIoTargetOpen(IoTarget, &OpenParams); + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&RequestAttributes); + Status = WdfRequestCreate(&RequestAttributes, IoTarget, &IoctlRequest); + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + // + // Set up a WDF memory object for the IOCTL request + // + + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Attributes.ParentObject = IoctlRequest; + Status = WdfMemoryCreatePreallocated(&Attributes, Data, Size, &WdfMemory); + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + // + // Format the request as read or write operation + // + + if (ReadOperation != FALSE) { + Status = WdfIoTargetFormatRequestForIoctl(IoTarget, + IoctlRequest, + IOCTL_GPIO_READ_PINS, + NULL, + 0, + WdfMemory, + 0); + + } else { + Status = WdfIoTargetFormatRequestForIoctl(IoTarget, + IoctlRequest, + IOCTL_GPIO_WRITE_PINS, + WdfMemory, + 0, + WdfMemory, + 0); + } + + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + // + // Send the request synchronously with an arbitrary timeout of 60 seconds + // + + WDF_REQUEST_SEND_OPTIONS_INIT(&SendOptions, + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&SendOptions, + WDF_REL_TIMEOUT_IN_SEC(60)); + + Status = WdfRequestAllocateTimer(IoctlRequest); + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + if (!WdfRequestSend(IoctlRequest, IoTarget, &SendOptions)) { + Status = WdfRequestGetStatus(IoctlRequest); + } + + if (NT_SUCCESS(Status)) { + *IoTargetOut = IoTarget; + } + +TestReadWriteEnd: + if (IoctlRequest != NULL) { + WdfObjectDelete(IoctlRequest); + } + + if (!NT_SUCCESS(Status) && (IoTarget != NULL)) { + WdfIoTargetClose(IoTarget); + WdfObjectDelete(IoTarget); + } + + return Status; +} + + + diff --git a/gpio/samples/simdevice/simdevice.inx b/gpio/samples/simdevice/simdevice.inx new file mode 100644 index 00000000..34e65a68 --- /dev/null +++ b/gpio/samples/simdevice/simdevice.inx @@ -0,0 +1,76 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +;Module Name: +; +; SIMGPIO.INF +; +;Abstract: +; INF file for installing Simulated Peripheral Device Driver. +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=System +ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} +Provider=%MSFT% +DriverVer=05/07/2010 +CatalogFile=gpiosamples.cat + +[SourceDisksNames] +3426=windows cd + +[SourceDisksFiles] +Simdevice.sys = 3426 + +;12 == Windows\System32\Drivers +[DestinationDirs] +DefaultDestDir = 12 + +[ControlFlags] +ExcludeFromSelect=* + +;***************************************** +; SimDevice Install Section +;***************************************** + +[Manufacturer] +%MSFT%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%DeviceDesc%=DriverInstall,ACPI\TEST0003 + +[DriverInstall.NT] +CopyFiles=DriverInstall_Copy + +[DriverInstall.NT.Services] +AddService = simdevice,2,DriverInstall_Service + +[DriverInstall_Copy] +simdevice.sys,,,0x100 + +[DriverInstall_Service] +DisplayName = %SvcDesc% +ServiceType = %SERVICE_KERNEL_DRIVER% +StartType = %SERVICE_DEMAND_START% +ErrorControl = %SERVICE_ERROR_NORMAL% +ServiceBinary = %12%\Simdevice.sys + +[Strings] +;Localizable Strings +MSFT = "Microsoft" +Std = "(Standard system devices) Test device" +SvcDesc = "Test device service" +DeviceDesc = "Test device description" + +;Non-Localizable Strings +SERVICE_KERNEL_DRIVER = 1 +SERVICE_DEMAND_START = 3 +SERVICE_ERROR_NORMAL = 1 + + + + + diff --git a/gpio/samples/simdevice/simdevice.rc b/gpio/samples/simdevice/simdevice.rc new file mode 100644 index 00000000..3171260d --- /dev/null +++ b/gpio/samples/simdevice/simdevice.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Sample device driver" +#define VER_INTERNALNAME_STR "simdevice.sys" +#define VER_ORIGINALFILENAME_STR "simdevice.sys" + +#include "common.ver" + diff --git a/gpio/samples/simdevice/simdevice.vcxproj b/gpio/samples/simdevice/simdevice.vcxproj new file mode 100644 index 00000000..6d685fcf --- /dev/null +++ b/gpio/samples/simdevice/simdevice.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {57BFED37-06F0-435D-A4CA-610B56B8987F} + $(MSBuildProjectName) + 1 + Debug + Win32 + {7E5C1E9A-1990-437C-B2AB-608C84382F8B} + + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + * + true + $(InfArch) + true + .\$(IntDir)\simdevice.inf + + + + simdevice + + + simdevice + + + simdevice + + + simdevice + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gpio/samples/simdevice/simdevice.vcxproj.Filters b/gpio/samples/simdevice/simdevice.vcxproj.Filters new file mode 100644 index 00000000..2cc912e8 --- /dev/null +++ b/gpio/samples/simdevice/simdevice.vcxproj.Filters @@ -0,0 +1,39 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C0D3EEB7-2138-4947-92CF-E7B13BB61AA2} + + + h;hpp;hxx;hm;inl;inc;xsd + {70E36F5B-DD23-4371-98D3-59AB4042B0F1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {DCCD5E1C-EB33-41FC-81B3-52934D7E2A5A} + + + inf;inv;inx;mof;mc; + {6B2886D3-87CA-46FD-B2A0-C624C0F69F92} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/gpio/samples/simdeviceumdf/GpioSampleUMDF.asl b/gpio/samples/simdeviceumdf/GpioSampleUMDF.asl new file mode 100644 index 00000000..7e52795f --- /dev/null +++ b/gpio/samples/simdeviceumdf/GpioSampleUMDF.asl @@ -0,0 +1,122 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + GpioSample.asl + +Abstract: + + This sample ASL file describes a sample GPIO device and a sample peripheral + device which consumes IO and interrupt resources from the GPIO device. Please + note that: + + 1. The memory and IO descriptor under the GPIO device are simply examples of + what can be described. They are commented out to illustrate this point. Actual + values will vary according to the platform specifications (e.g. GIC and memory) + + 2. The sample ASL DSDT definition block defines only the components relevant + to demonstrate GPIO IO and interrupt resource usage. Rest of the DSDT will vary + according to platform specifications. + + 3. The PNP IDs for the GPIO and peripheral device are for demonstration purposes + only. Actual values need to reflect those chosen for the actual GPIO or + peripheral device. + +--*/ + + +DefinitionBlock ("DSDT.AML", "DSDT", 0x02, "MSFT", "SAMPLE", 0x1) { + + // + // System Bus + // + + Scope (\_SB) { + + // + // Sample GPIO device + // + + Device(GPIO) { + Name (_ADR, 0) + Name (_HID, "TEST0001") + Name (_CID, "TEST0001") + Name(_UID, 4) + + Method (_CRS, 0x0, NotSerialized) { + Name (RBUF, ResourceTemplate () { + + // + // Interrupt resource. In this example, banks 0 & 1 share the same + // interrupt to the parent controller and similarly banks 2 & 3. + // + // N.B. The definition below is chosen for an arbitrary + // test platform. It needs to be changed to reflect the hardware + // configuration of the actual platform. + // + + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {50} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {50} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {51} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {51} + + // + // Memory resource. The definition below is chosen for an arbitrary + // test platform. It needs to be changed to reflect the hardware + // configuration of the actual platform. + // + + Memory32Fixed(ReadWrite, 0x00100000, 0x18) + }) + + Return (RBUF) + } + + Method (_STA, 0x0, NotSerialized) { + Return(0xf) + } + + // + // Sample peripheral device + // + + Device (TDEV) { + Name (_ADR, 0) + Name (_HID, "TEST0004") + Name (_CID, "TEST0004") + Name (_UID, 1) + + Method (_CRS, 0x0, NotSerialized) { + Name (RBUF, ResourceTemplate () { + + // + // GPIO Interrupt Resources + // + + GpioInt(Edge, ActiveHigh, Exclusive, PullUp, 0, "\\_SB.GPIO", 0, ResourceConsumer,, RawDataBuffer() {1}) {2} + + // + // GPIO IO Resources + // + + GpioIo(Exclusive, PullUp, 0, 0,, "\\_SB.GPIO",0, ResourceConsumer, , RawDataBuffer() {1}) {10} + }) + + Return (RBUF) + } + + Method (_STA, 0x0, NotSerialized) { + Return(0xf) + } + } + } + } + } diff --git a/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj b/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj new file mode 100644 index 00000000..05a8bfe6 --- /dev/null +++ b/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj @@ -0,0 +1,282 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D768CF1C-AE06-4C12-B11D-21C5D70082F7} + $(MSBuildProjectName) + 1 + 11 + 1 + 11 + Debug + Win32 + {DF2D116B-B9CA-4AE1-BAD4-416349DA768D} + + + + Windows10 + False + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + $(InfArch) + true + .\$(IntDir)\simdeviceUMDFDriver.inf + + + + SimdeviceUMDF + + + SimdeviceUMDF + + + SimdeviceUMDF + + + SimdeviceUMDF + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + 0x0A00 + 0x0A000000 + + + 0x0A00 + 0x0A000000 + + + 0x0A00 + 0x0A000000 + + + 0x0A00 + 0x0A000000 + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH) + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj.Filters b/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj.Filters new file mode 100644 index 00000000..db8bda4d --- /dev/null +++ b/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj.Filters @@ -0,0 +1,54 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {7B1D3759-D374-48F4-8ADD-07752C95369D} + + + h;hpp;hxx;hm;inl;inc;xsd + {A065C827-3D09-4244-997C-14744BF574C4} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9EB984ED-A895-4F5D-97FD-C72ACA844DAD} + + + inf;inv;inx;mof;mc; + {3940722F-AA7D-4F6A-8A19-3CD74FCBD352} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/gpio/samples/simdeviceumdf/comsup.cpp b/gpio/samples/simdeviceumdf/comsup.cpp new file mode 100644 index 00000000..ccafeeaf --- /dev/null +++ b/gpio/samples/simdeviceumdf/comsup.cpp @@ -0,0 +1,344 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + ComSup.cpp + +Abstract: + + This module contains implementations for the functions and methods + used for providing COM support. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" + +// +// Implementation of CUnknown methods. +// + +CUnknown::CUnknown( + VOID + ) : m_ReferenceCount(1) +/*++ + + Routine Description: + + Constructor for an instance of the CUnknown class. This simply initializes + the reference count of the object to 1. The caller is expected to + call Release() if it wants to delete the object once it has been allocated. + + Arguments: + + None + + Return Value: + + None + +--*/ +{ + // do nothing. +} + +HRESULT +STDMETHODCALLTYPE +CUnknown::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method provides the basic support for query interface on CUnknown. + If the interface requested is IUnknown it references the object and + returns an interface pointer. Otherwise it returns an error. + + Arguments: + + InterfaceId - the IID being requested + + Object - a location to store the interface pointer to return. + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + if (IsEqualIID(InterfaceId, __uuidof(IUnknown))) + { + *Object = QueryIUnknown(); + return S_OK; + } + else + { + *Object = NULL; + return E_NOINTERFACE; + } +} + +IUnknown * +CUnknown::QueryIUnknown( + VOID + ) +/*++ + + Routine Description: + + This helper method references the object and returns a pointer to the + object's IUnknown interface. + + This allows other methods to convert a CUnknown pointer into an IUnknown + pointer without a typecast and without calling QueryInterface and dealing + with the return value. + + Arguments: + + None + + Return Value: + + A pointer to the object's IUnknown interface. + +--*/ +{ + AddRef(); + return static_cast(this); +} + +ULONG +STDMETHODCALLTYPE +CUnknown::AddRef( + VOID + ) +/*++ + + Routine Description: + + This method adds one to the object's reference count. + + Arguments: + + None + + Return Value: + + The new reference count. The caller should only use this for debugging + as the object's actual reference count can change while the caller + examines the return value. + +--*/ +{ + return InterlockedIncrement(&m_ReferenceCount); +} + +ULONG +STDMETHODCALLTYPE +CUnknown::Release( + VOID + ) +/*++ + + Routine Description: + + This method subtracts one to the object's reference count. If the count + goes to zero, this method deletes the object. + + Arguments: + + None + + Return Value: + + The new reference count. If the caller uses this value it should only be + to check for zero (i.e. this call caused or will cause deletion) or + non-zero (i.e. some other call may have caused deletion, but this one + didn't). + +--*/ +{ + ULONG count = InterlockedDecrement(&m_ReferenceCount); + + if (count == 0) + { + delete this; + } + return count; +} + +// +// Implementation of CClassFactory methods. +// + +// +// Define storage for the factory's static lock count variable. +// + +LONG CClassFactory::s_LockCount = 0; + +IClassFactory * +CClassFactory::QueryIClassFactory( + VOID + ) +/*++ + + Routine Description: + + This helper method references the object and returns a pointer to the + object's IClassFactory interface. + + This allows other methods to convert a CClassFactory pointer into an + IClassFactory pointer without a typecast and without dealing with the + return value QueryInterface. + + Arguments: + + None + + Return Value: + + A referenced pointer to the object's IClassFactory interface. + +--*/ +{ + AddRef(); + return static_cast(this); +} + +HRESULT +CClassFactory::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method attempts to retrieve the requested interface from the object. + + If the interface is found then the reference count on that interface (and + thus the object itself) is incremented. + + Arguments: + + InterfaceId - the interface the caller is requesting. + + Object - a location to store the interface pointer. + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + // + // This class only supports IClassFactory so check for that. + // + + if (IsEqualIID(InterfaceId, __uuidof(IClassFactory))) + { + *Object = QueryIClassFactory(); + return S_OK; + } + else + { + // + // See if the base class supports the interface. + // + + return CUnknown::QueryInterface(InterfaceId, Object); + } +} + +HRESULT +STDMETHODCALLTYPE +CClassFactory::CreateInstance( + _In_opt_ IUnknown * /* OuterObject */, + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This COM method is the factory routine - it creates instances of the driver + callback class and returns the specified interface on them. + + Arguments: + + OuterObject - only used for aggregation, which our driver callback class + does not support. + + InterfaceId - the interface ID the caller would like to get from our + new object. + + Object - a location to store the referenced interface pointer to the new + object. + + Return Value: + + Status. + +--*/ +{ + HRESULT hr; + + PCSimdeviceDriver driver; + + *Object = NULL; + + hr = CSimdeviceDriver::CreateInstance(&driver); + + if (SUCCEEDED(hr)) + { + hr = driver->QueryInterface(InterfaceId, Object); + driver->Release(); + } + + return hr; +} + +HRESULT +STDMETHODCALLTYPE +CClassFactory::LockServer( + _In_ BOOL Lock + ) +/*++ + + Routine Description: + + This COM method can be used to keep the DLL in memory. However since the + driver's DllCanUnloadNow function always returns false, this has little + effect. Still it tracks the number of lock and unlock operations. + + Arguments: + + Lock - Whether the caller wants to lock or unlock the "server" + + Return Value: + + S_OK + +--*/ +{ + if (Lock) + { + InterlockedIncrement(&s_LockCount); + } + else + { + InterlockedDecrement(&s_LockCount); + } + return S_OK; +} + + diff --git a/gpio/samples/simdeviceumdf/comsup.h b/gpio/samples/simdeviceumdf/comsup.h new file mode 100644 index 00000000..550c04e0 --- /dev/null +++ b/gpio/samples/simdeviceumdf/comsup.h @@ -0,0 +1,217 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + ComSup.h + +Abstract: + + This module contains classes and functions use for providing COM support + code. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// Forward type declarations. They are here rather than in internal.h as +// you only need them if you choose to use these support classes. +// + +typedef class CUnknown *PCUnknown; +typedef class CClassFactory *PCClassFactory; + +// +// Base class to implement IUnknown. You can choose to derive your COM +// classes from this class, or simply implement IUnknown in each of your +// classes. +// + +class CUnknown : public IUnknown +{ + +// +// Private data members and methods. These are only accessible by the methods +// of this class. +// +private: + + // + // The reference count for this object. Initialized to 1 in the + // constructor. + // + + LONG m_ReferenceCount; + +// +// Protected data members and methods. These are accessible by the subclasses +// but not by other classes. +// +protected: + + // + // The constructor and destructor are protected to ensure that only the + // subclasses of CUnknown can create and destroy instances. + // + + CUnknown( + VOID + ); + + // + // The destructor MUST be virtual. Since any instance of a CUnknown + // derived class should only be deleted from within CUnknown::Release, + // the destructor MUST be virtual or only CUnknown::~CUnknown will get + // invoked on deletion. + // + // If you see that your CMyDevice specific destructor is never being + // called, make sure you haven't deleted the virtual destructor here. + // + + virtual + ~CUnknown( + VOID + ) + { + // Do nothing + } + +// +// Public Methods. These are accessible by any class. +// +public: + + IUnknown * + QueryIUnknown( + VOID + ); + +// +// COM Methods. +// +public: + + // + // IUnknown methods + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ); + + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ); + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); +}; + +// +// Class factory support class. Create an instance of this from your +// DllGetClassObject method and modify the implementation to create +// an instance of your driver event handler class. +// + +class CClassFactory : public CUnknown, public IClassFactory +{ +// +// Private data members and methods. These are only accessible by the methods +// of this class. +// +private: + + // + // The lock count. This is shared across all instances of IClassFactory + // and can be queried through the public IsLocked method. + // + + static LONG s_LockCount; + +// +// Public Methods. These are accessible by any class. +// +public: + + IClassFactory * + QueryIClassFactory( + VOID + ); + +// +// COM Methods. +// +public: + + // + // IUnknown methods + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + __drv_arg(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + // + // IClassFactory methods. + // + + virtual + HRESULT + STDMETHODCALLTYPE + CreateInstance( + _In_opt_ IUnknown *OuterObject, + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + virtual + HRESULT + STDMETHODCALLTYPE + LockServer( + _In_ BOOL Lock + ); +}; + diff --git a/gpio/samples/simdeviceumdf/device.cpp b/gpio/samples/simdeviceumdf/device.cpp new file mode 100644 index 00000000..0ced0bd1 --- /dev/null +++ b/gpio/samples/simdeviceumdf/device.cpp @@ -0,0 +1,1106 @@ +/*++ + +Copyright (c) 1990-2010 Microsoft Corporation + +Module Name: + + Device.cpp + +Abstract: + + This is a simple UMDF device driver that consumes GPIO pins for I/O and interrupt. + + +Environment: + + UMDF + +--*/ + +// +// Inlcude the below set of headers to get the CM_RESOURCE_ definitions +// + + +#include "internal.h" + +DEFINE_GUID (GUID_DEVINTERFACE_ECHO, + 0xcdc35b6e, 0xbe4, 0x4936, 0xbf, 0x5f, 0x55, 0x37, 0x38, 0xa, 0x7c, 0x1a); +// {CDC35B6E-0BE4-4936-BF5F-5537380A7C1A} + + +HRESULT +CSimdevice::CreateInstance( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit, + _Out_ PCSimdevice *Device + ) +/*++ + + Routine Description: + + This method creates and initializs an instance of the driver's + device callback object. + + Arguments: + + FxDeviceInit - the settings for the device. + + Device - a location to store the referenced pointer to the device object. + + Return Value: + + Status + +--*/ +{ + PCSimdevice device; + HRESULT hr; + + // + // Allocate a new instance of the device class. + // + + device = new CSimdevice(); + + if (NULL == device) { + return E_OUTOFMEMORY; + } + + // + // Initialize the instance. + // + + hr = device->Initialize(FxDriver, FxDeviceInit); + + if (SUCCEEDED(hr)) { + *Device = device; + } + else { + device->Release(); + } + + return hr; +} + +HRESULT +CSimdevice::Initialize( + _In_ IWDFDriver * FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit + ) + +/*++ + + Routine Description: + + This method initializes the device callback object and creates the + partner device object. + + The method should perform any device-specific configuration that: + * could fail (these can't be done in the constructor) + * must be done before the partner object is created -or- + * can be done after the partner object is created and which aren't + influenced by any device-level parameters the parent (the driver + in this case) might set. + + Arguments: + + FxDeviceInit - the settings for this device. + + Return Value: + + status. + +--*/ +{ + IWDFDevice *fxDevice; + IWDFDeviceInitialize2 *fxDeviceInit2; + HRESULT hr; + + // + // Configure things like the locking model before we go to create our + // partner device. + // + + // + // Set no locking unless you need an automatic callbacks synchronization + // + + FxDeviceInit->SetLockingConstraint(None); + + // + // Create a new FX device object and assign the new callback object to + // handle any device level events that occur. + // + + // + // Set retrieval mode to direct I/O. This needs to be done before the call + // to CreateDevice. + // + + FxDeviceInit->QueryInterface(IID_PPV_ARGS(&fxDeviceInit2)); + + if (fxDeviceInit2 == NULL) { + hr = E_FAIL; + return hr; + } + + fxDeviceInit2->SetIoTypePreference(WdfDeviceIoBufferRetrievalDeferred, + WdfDeviceIoDirect, + WdfDeviceIoDirect); + + SAFE_RELEASE(fxDeviceInit2); + + // + // QueryIUnknown references the IUnknown interface that it returns + // (which is the same as referencing the device). We pass that to + // CreateDevice, which takes its own reference if everything works. + // + + { + IUnknown *unknown = this->QueryIUnknown(); + + hr = FxDriver->CreateDevice(FxDeviceInit, unknown, &fxDevice); + + unknown->Release(); + } + + // + // If that succeeded then set our FxDevice member variable. + // + + if (SUCCEEDED(hr)) { + m_FxDevice = fxDevice; + + // + // Drop the reference we got from CreateDevice. Since this object + // is partnered with the framework object they have the same + // lifespan - there is no need for an additional reference. + // + + fxDevice->Release(); + } + + return hr; +} + +HRESULT +CSimdevice::Configure( + VOID + ) +/*++ + + Routine Description: + + This method is called after the device callback object has been initialized + and returned to the driver. It would setup the device's queues and their + corresponding callback objects. + + Arguments: + + FxDevice - the framework device object for which we're handling events. + + Return Value: + + status + +--*/ +{ + PCSimdeviceQueue defaultQueue; + + HRESULT hr; + + hr = CSimdeviceQueue::CreateInstance(m_FxDevice, &defaultQueue); + + if (FAILED(hr)) { + return hr; + } + + hr = defaultQueue->Configure(); + + if (SUCCEEDED(hr)) { + // + // In case of success store defaultQueue in our member + // The reference is transferred to m_DefaultQueue + // + + m_Queue = defaultQueue; + } + else { + // + // In case of failure release the reference + // + + defaultQueue->Release(); + } + + if (SUCCEEDED(hr)) { + hr = m_FxDevice->CreateDeviceInterface(&GUID_DEVINTERFACE_ECHO, + NULL); + } + + return hr; +} + +HRESULT +CSimdevice::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method is called to get a pointer to one of the object's callback + interfaces. + + Since the sample driver doesn't support any of the device events, this + method simply calls the base class's BaseQueryInterface. + + If the sample is extended to include device event interfaces then this + method must be changed to check the IID and return pointers to them as + appropriate. + + Arguments: + + InterfaceId - the interface being requested + + Object - a location to store the interface pointer if successful + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + HRESULT hr; + + if (IsEqualIID(InterfaceId, __uuidof(IPnpCallbackSelfManagedIo))) { + *Object = QueryIPnpCallbackSelfManagedIo(); + hr = S_OK; + } else if (IsEqualIID(InterfaceId, __uuidof(IPnpCallbackHardware2))) { + *Object = QueryIPnpCallbackHardware2(); + hr = S_OK; + } else if (IsEqualIID(InterfaceId, __uuidof(IPnpCallback))) { + *Object = QueryIPnpCallback(); + hr = S_OK; + } else { + hr = CUnknown::QueryInterface(InterfaceId, Object); + } + + return hr; +} + +HRESULT +CSimdevice::OnSelfManagedIoInit( + _In_ IWDFDevice * pWdfDevice + ) +/*++ + + Routine Description: + + This method is called to allow driver to initialize any resources + that driver might need to process I/O. + + Echo driver needs a thread to process completions. We initialize + this thread here + + Arguments: + + pWdfDevice - framework device object for which to initialze resources + + Return Value: + + S_OK in case of success + HRESULT correponding to error returned by CreateThread, in case of failure + +--*/ +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pWdfDevice); + + + m_ThreadHandle = CreateThread( NULL, // Default Security Attrib. + 0, // Initial Stack Size, + CSimdeviceQueue::CompletionThread, // Thread Func + (LPVOID)m_Queue, // Arg to Thread Func is Queue + 0, // Creation Flags + NULL ); // Don't need the Thread Id. + + if (m_ThreadHandle == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + return hr; +} + +void +CSimdevice::OnSelfManagedIoCleanup( + _In_ IWDFDevice * pWdfDevice + ) +/*++ + + Routine Description: + + This method is called to allow driver to cleanup any resources + that driver allocated to process I/O. + + It is critical that, in this routine driver wait for all of the + threads which it created to exit. Otherwise those threads could + continue to execute when framework unloads the driver which + would lead to a crash. + + Echo driver created a thread to handle completions. We wait for + that thread to exit in this routine + + Arguments: + + pWdfDevice - framework device object for which to cleanup resources + + Return Value: + + None + +--*/ +{ + // + // Kill the thread and + // wait for the thread to die. + // + + UNREFERENCED_PARAMETER(pWdfDevice); + + if (m_ThreadHandle) { + + // + // Ask queue to set terminate flag which will make + // the thread exit + // + + m_Queue->SetExitThread(); + + // + // Wait for the thread to exit + // + + WaitForSingleObject(m_ThreadHandle, INFINITE); + + // + // Close the thread handle + // + + CloseHandle(m_ThreadHandle); + m_ThreadHandle = NULL; + } + + // + // Release the reference we took on the queue callback object + // to keep it alive until the thread exits + // + + SAFE_RELEASE(m_Queue); +} + + +HRESULT +CSimdevice::OnPrepareHardware( + _In_ IWDFDevice3 * pWdfDevice, + _In_ IWDFCmResourceList * pWdfResourcesRaw, + _In_ IWDFCmResourceList * pWdfResourcesTranslated + ) +/*++ + +Routine Description: + + This routine is called by WUDF to initialize hardware resources (e.g. interrupts, + IO resources) + +Arguments: + + pWdfDevice - pointer to an IWDFDevice object for the device + + pWdfResourcesRaw - Supplies a pointer to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + pWdfResourcesTranslated - Supplies a pointer to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + HRESULT + +--*/ +{ + + ULONG i; + HRESULT hr = S_OK; + ULONG ConnectionCount = 0; + BOOLEAN fInterruptFound = FALSE; + PCM_PARTIAL_RESOURCE_DESCRIPTOR DescriptorTranslated; + PCM_PARTIAL_RESOURCE_DESCRIPTOR DescriptorRaw; + + // + // Parse the resource list and save the resource information. + // + + for (i=0; i < pWdfResourcesTranslated->GetCount(); i++) { + + DescriptorTranslated = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)pWdfResourcesTranslated->GetDescriptor(i); + DescriptorRaw = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)pWdfResourcesRaw->GetDescriptor(i); + + if(DescriptorTranslated == NULL) { + return E_FAIL; + } + + switch (DescriptorTranslated->Type) { + + // + // One or more GPIO IO resources are expected. In this implementation of the sample + // driver, however, only the first IO resource will be used (see OnD0Entry). + // + + case CmResourceTypeConnection: + + // + // Check against expected connection type for a GPIO IO descriptor + // + + if ((DescriptorTranslated->u.Connection.Class == + CM_RESOURCE_CONNECTION_CLASS_GPIO) && + (DescriptorTranslated->u.Connection.Type == + CM_RESOURCE_CONNECTION_TYPE_GPIO_IO)) { + + if (ConnectionCount >= MAX_CONNECTIONS) { + break; + } + + // + // Store GPIO IO resource connection ID in the device extension + // + + this->GetDeviceExtension()->ConnectionId[ConnectionCount].LowPart = + DescriptorTranslated->u.Connection.IdLowPart; + + this->GetDeviceExtension()->ConnectionId[ConnectionCount].HighPart = + DescriptorTranslated->u.Connection.IdHighPart; + } + + break; + + case CmResourceTypeInterrupt: + + // + // Connect the first interrupt resource we find + // + + if (fInterruptFound == FALSE) { + hr = this->SimdeviceConnectInterrupt( + pWdfDevice, + DescriptorRaw, + DescriptorTranslated); + + if (SUCCEEDED(hr)) { + fInterruptFound = TRUE; + } + } + default: + + // + // Ignore all other descriptors + // + + break; + } + } + + return hr; + +} + +HRESULT +CSimdevice::OnReleaseHardware( + _In_ IWDFDevice3 * pWdfDevice, + _In_ IWDFCmResourceList * pWdfResourcesTranslated + ) +/*++ + + +Routine Description: + + This method is called by WUDF to uninitialize the hardware. + +Parameters: + + pWdfDevice - pointer to an IWDFDevice object for the device + + pWdfResourcesTranslated - pointer to the translated resource list + +Return Values: + status + +--*/ +{ + UNREFERENCED_PARAMETER(pWdfDevice); + UNREFERENCED_PARAMETER(pWdfResourcesTranslated); + + return S_OK; +} + +HRESULT +CSimdevice::OnD0Entry( + _In_ IWDFDevice* pWdfDevice, + _In_ WDF_POWER_DEVICE_STATE previousState + ) + +/*++ + +Routine Description: + +This method is called after a new device enters the system + +Parameters: + + pWdfDevice - pointer to a device object + + previousState - previous WDF power state + +Return Values: + status + +--*/ +{ + + PDEVICE_EXTENSION DevExt; + HRESULT hr = S_OK; + BYTE Data; + NTSTATUS Status; + NTSTATUS Status1; + WCHAR ReadStringBuffer[100]; + WCHAR WriteStringBuffer[100]; + + UNREFERENCED_PARAMETER(pWdfDevice); + UNREFERENCED_PARAMETER(previousState); + + DevExt = GetDeviceExtension(); + + Status = StringCbPrintfW(&ReadStringBuffer[0], + sizeof(ReadStringBuffer), + L"\\\\.\\RESOURCE_HUB\\%0*I64x", + (size_t)(sizeof(LARGE_INTEGER) * 2), + DevExt->ConnectionId[0].QuadPart); + + Status = StringCbPrintfW(&WriteStringBuffer[0], + sizeof(WriteStringBuffer), + L"\\\\.\\RESOURCE_HUB\\%0*I64x", + (size_t)(sizeof(LARGE_INTEGER) * 2), + DevExt->ConnectionId[0].QuadPart); + + // + // Connect the first GPIO IO descriptor we find (in OnPrepareHardware) for read and write operations + // + + Data = 0xDB; + Status = STATUS_SUCCESS; + Status1 = TestReadWrite(pWdfDevice, &WriteStringBuffer[0], FALSE, &Data, sizeof(Data), NULL); + if (!NT_SUCCESS(Status1)) { + Status = Status1; + } + + Data = 0; + Status1 = TestReadWrite(pWdfDevice, &ReadStringBuffer[0], TRUE, &Data, sizeof(Data), NULL); + if (!NT_SUCCESS(Status1)) { + Status = Status1; + } + + return hr; +} + +HRESULT +CSimdevice::OnD0Exit( + _In_ IWDFDevice* pWdfDevice, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) + +/*++ + +Routine Description: + +This method is called when a device exit D0 + +Parameters: + + pWdfDevice pointer to a device object + + TargetState - target D-state + +Return Values: + status + +--*/ + +{ + + UNREFERENCED_PARAMETER(pWdfDevice); + UNREFERENCED_PARAMETER(TargetState); + + return S_OK; +} + +HRESULT +CSimdevice::TestReadWrite( + _In_ IWDFDevice* pWdfDevice, + _In_ PWSTR RequestString, + _In_ BOOLEAN ReadOperation, + _Inout_updates_bytes_(Size) UCHAR *Data, + _In_ ULONG Size, + _Inout_opt_ IWDFRemoteTarget *IoTargetOut + ) + +/*++ + +Routine Description: + + This is a utility routine to test read or write on a set of GPIO pins. + +Arguments: + + pWdfDevice - Supplies a pointer to the framework device object. + + RequestString - Supplies a pointer to the unicode string to be opened. + + ReadOperation - Supplies a boolean that identifies whether read (TRUE) or + write (FALSE) should be performed. + + Data - Supplies a pointer containing the buffer that should be read from + or written to. + + Size - Supplies the size of the data buffer in bytes. + + IoTargetOut - Supplies a pointer that receives the IOTARGET created by + UMDF. + +Return Value: + + HRESULT + +--*/ + +{ + + IWDFMemory *pOutputMemory = NULL; + IWDFMemory *pInputMemory = NULL; + IRequestCallbackRequestCompletion *pICallback = NULL; + IWDFIoRequest *pIoRequest = NULL; + IWDFRemoteTarget *pRemoteTarget = NULL; + UMDF_IO_TARGET_OPEN_PARAMS OpenParams; + DWORD DesiredAccess; + HRESULT hr = S_OK; + IWDFRequestCompletionParams * FxComplParams = NULL; + IWDFDriver *pWdfDriver = NULL; + + UNREFERENCED_PARAMETER(IoTargetOut); + + if (ReadOperation != FALSE) { + DesiredAccess = FILE_GENERIC_READ; + + } else { + DesiredAccess = FILE_GENERIC_WRITE; + } + + // + // QueryIUnknown references the IUnknown interface that it returns + // (which is the same as referencing the CMyRemoteTarget). We pass that + // to the various Create* calls, which take their own reference if + // everything works. + // + + IUnknown * unknown = this->QueryIUnknown(); + + + IWDFDevice2 *pWdfDevice2 = NULL; + hr = pWdfDevice->QueryInterface(IID_PPV_ARGS(&pWdfDevice2)); + + // Create the IoTarget + + if (SUCCEEDED(hr)) { + hr = pWdfDevice2->CreateRemoteTarget( + unknown, + NULL, + &pRemoteTarget); + } + + // + // Determine whether the request is a read or write + // + + OpenParams.dwCreationDisposition = OPEN_EXISTING; + OpenParams.dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED; + OpenParams.dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + + + if (SUCCEEDED(hr)) { + hr = pRemoteTarget->OpenFileByName(RequestString, + DesiredAccess, + &OpenParams); + } + + // + //Create a new IO request + // + + if (SUCCEEDED(hr)) { + hr = pWdfDevice->CreateRequest(NULL, pWdfDevice, &pIoRequest); + } + + if (SUCCEEDED(hr)) { + hr = this->QueryInterface(__uuidof(IRequestCallbackRequestCompletion), (PVOID*)&pICallback); + + // + //Set completion callback + // + + if (SUCCEEDED(hr)){ + pIoRequest->SetCompletionCallback(pICallback, NULL); + } + + pWdfDevice->GetDriver(&pWdfDriver); + + hr = pWdfDriver->CreatePreallocatedWdfMemory(Data, + Size, + NULL, // no object event callback + pIoRequest, // request object as parent + &pInputMemory); + + hr = pWdfDriver->CreatePreallocatedWdfMemory(Data, + Size, + NULL, // no object event callback + pIoRequest, // request object as parent + &pOutputMemory); + + // + //Format IO request + // + + if (ReadOperation != FALSE){ + if (SUCCEEDED(hr)) { + hr = pRemoteTarget->FormatRequestForIoctl(pIoRequest, + IOCTL_GPIO_READ_PINS, + NULL, + NULL, + NULL, + pOutputMemory, + NULL); + } + } + else { + if (SUCCEEDED(hr)) { + hr = pRemoteTarget->FormatRequestForIoctl(pIoRequest, + IOCTL_GPIO_WRITE_PINS, + NULL, + pInputMemory, + NULL, + pOutputMemory, + NULL); + } + } + + // + //Send down the request + // + if (SUCCEEDED(hr)) { + hr = pIoRequest->Send(pRemoteTarget, + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, + 0); //No timeout + } + + if (SUCCEEDED(hr)) { + + // + //Get IWDFRequestCompletionParams interface and then get completion status. + // + + pIoRequest->GetCompletionParams(&FxComplParams); + hr = FxComplParams->GetCompletionStatus(); + } + + if(FAILED(hr)) { + pIoRequest->DeleteWdfObject(); + pIoRequest = NULL; + } + } + + // + // Clean-up + // + + if (pWdfDevice2 != NULL) { + SAFE_RELEASE(pWdfDevice2); + } + + if (pWdfDriver != NULL) { + SAFE_RELEASE(pWdfDriver); + } + + if (pRemoteTarget != NULL) { + pRemoteTarget->Close(); + } + + return hr; +} + + +void +CSimdevice::OnSurpriseRemoval( + _In_ IWDFDevice* pWdfDevice + ) + +/*++ + +Routine Description: + +This method is called when a device is surprise removed + +Parameters: + + pWdfDevice pointer to a device object + +Return Values: + None + +--*/ + +{ + UNREFERENCED_PARAMETER(pWdfDevice); +} + + +HRESULT +CSimdevice::OnQueryRemove( + _In_ IWDFDevice* pWdfDevice + ) +/*++ + +Routine Description: + +This method is called when a device processes the query remove IRP + +Parameters: + + pWdfDevice pointer to a device object + +Return Values: + HRESULT + +--*/ + + +{ + UNREFERENCED_PARAMETER(pWdfDevice); + return S_OK; +} + +HRESULT +CSimdevice::OnQueryStop( + _In_ IWDFDevice* pWdfDevice + ) +/*++ + +Routine Description: + +This method is called when a device processes the query stop IRP + +Parameters: + + pWdfDevice pointer to a device object + +Return Values: + HRESULT + +--*/ +{ + UNREFERENCED_PARAMETER(pWdfDevice); + return S_OK; +} + +HRESULT +CSimdevice::SimdeviceConnectInterrupt( + _In_ IWDFDevice* pWdfDevice, + _In_opt_ PCM_PARTIAL_RESOURCE_DESCRIPTOR RawResource, + _In_opt_ PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedResource + ) + +/*++ + +Routine Description: + + This is a utility routine to create an interrupt descriptor from the interrupt resource + acquired from OnPrepareHardware and connect the device ISR routine. +Arguments: + + pWdfDevice - Supplies a pointer to the framework device object. + + pWdfResourcesRaw - Supplies a pointer to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + pWdfResourcesTranslated - Supplies a pointer to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + None. + +--*/ + +{ + + IWDFDevice3 * pIWDFDevice3 = NULL; + IWDFInterrupt * spInterrupt = NULL; + HRESULT hr = S_OK; + + if (pWdfDevice == NULL) { + hr = E_INVALIDARG; + } + + if (SUCCEEDED(hr)) { + hr = pWdfDevice->QueryInterface(IID_PPV_ARGS(&pIWDFDevice3)); + } + + if (SUCCEEDED(hr)) { + + // + // Create interrupt + // + + WUDF_INTERRUPT_CONFIG config; + WUDF_INTERRUPT_CONFIG_INIT( + &config, + &CSimdevice::OnInterruptIsr, + &CSimdevice::OnInterruptWorkItem); + + config.InterruptRaw = RawResource; + config.InterruptTranslated = TranslatedResource; + + hr = pIWDFDevice3->CreateInterrupt(&config, &spInterrupt); + } + + // + // Associate the device context with the interrupt + // + + if (SUCCEEDED(hr)) { + hr = spInterrupt->AssignContext(NULL, (void*)this); + } + + return hr; +} + +BOOLEAN +CSimdevice::OnInterruptIsr( + _In_ IWDFInterrupt* pInterrupt, + _In_ ULONG MessageID, + _In_ ULONG Reserved + ) + +/* ++ + +Routine Description: + + This method is called when an interrupt occurs. It determines if the + driver owns the interrupt and queues a work item to defer processing + of the data. + + Arguments: + + pInterrupt - pointer to the interrupt object + MessageID - interrupt message ID + Reserved - + +Return Values: + TRUE if interrupt recognized, else FALSE. + +-- */ + +{ + + UNREFERENCED_PARAMETER(MessageID); + UNREFERENCED_PARAMETER(Reserved); + + IWDFDevice* pWdfDevice = NULL; + CSimdevice* pMyDevice = NULL; + HRESULT hr; + + hr = pInterrupt->RetrieveContext((void**)&pMyDevice); + + if (FAILED(hr)) { + pWdfDevice = pInterrupt->GetDevice(); + pWdfDevice->SetPnpState(WdfPnpStateFailed, WdfTrue); + pWdfDevice->CommitPnpState(); + } + + // + // The sample driver always returns TRUE (e.g. claiming the interrupt) + // from its ISR. In reality, the driver needs to do whatever necessary to + // quiesce the interrupt before claiming the interrupt. + // If additional work needs to be done at , schedule a work item (as we do here). + // + + pInterrupt->QueueWorkItemForIsr(); + + return TRUE; +} + +VOID +CSimdevice::OnInterruptWorkItem( + _In_ IWDFInterrupt* pInterrupt, + _In_ IWDFObject* AssociatedObject + ) + +/* ++ + +Routine Description: + + This method is called on behalf of an interrupt to defer processing. + It retrieves latest data and posts it. + +Arguments: + pInterrupt - pointer to the interrupt object + AssociatedObject - pointer to the associated object + +Return Values: + None. + +--*/ +{ + + UNREFERENCED_PARAMETER(AssociatedObject); + + IWDFDevice* pWdfDevice = NULL; + CSimdevice* pMyDevice; + HRESULT hr; + + hr = pInterrupt->RetrieveContext((void**)&pMyDevice); + + if (FAILED(hr)) { + pWdfDevice = pInterrupt->GetDevice(); + pWdfDevice->SetPnpState(WdfPnpStateFailed, WdfTrue); + pWdfDevice->CommitPnpState(); + } + + + // + // The sample driver does nothing in the work item routine. The real driver + // can add code here to accomplish all that is required to complete the serving + // of the interrupt + // + + return; +} + + diff --git a/gpio/samples/simdeviceumdf/device.h b/gpio/samples/simdeviceumdf/device.h new file mode 100644 index 00000000..e0ceb68c --- /dev/null +++ b/gpio/samples/simdeviceumdf/device.h @@ -0,0 +1,365 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions for the UMDF Echo sample + driver's device callback class. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +#include "queue.h" + +#define MAX_CONNECTIONS (4) +#define MAX_RESOURCES (10) + + +// +// The device extension for the device object +// + +typedef struct _DEVICE_EXTENSION { + + CSimdevice* MyDevice; + + IWDFDevice* Device; + IWDFDevice3* Device3; + + IWDFCmResourceList* CmResourceList; + + LARGE_INTEGER ConnectionId[MAX_CONNECTIONS]; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +// +// Class for the simdevice driver. +// + +class CSimdevice : + public CUnknown, + public IPnpCallbackSelfManagedIo, + public IPnpCallbackHardware2, + public IPnpCallback +{ + +// +// Private data members. +// +private: + + IWDFDevice *m_FxDevice; + + // + // Completion Thread handle used by queue callback object + // + HANDLE m_ThreadHandle; + + // + // Our queue callback object + // Strong reference - since we pass it to the thread we create + // + CSimdeviceQueue *m_Queue; + + // + // device data + // + DEVICE_EXTENSION m_DevExtension; + +// +// Private methods. +// + +private: + + CSimdevice( + VOID + ) + { + m_FxDevice = NULL; + ZeroMemory(&m_DevExtension, sizeof(DEVICE_EXTENSION)); + } + + HRESULT + Initialize( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ); + + IPnpCallbackSelfManagedIo * + QueryIPnpCallbackSelfManagedIo( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + IPnpCallback * + QueryIPnpCallback( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + IPnpCallbackHardware2 * + QueryIPnpCallbackHardware2( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + +// +// Public methods +// +public: + + // + // The factory method used to create an instance of this driver. + // + + static + HRESULT + CreateInstance( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit, + _Out_ PCSimdevice *Device + ); + + HRESULT + Configure( + VOID + ); + +// +// COM methods +// +public: + + // + // IUnknown methods. + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + __drv_arg(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + // + // IPnpCallbackSelfManagedIo methods + // + + // + // We implement this interface to create and tear down + // our completion thread + // + // It is critical that we wait for all the threads we create + // to exit during OnSelfManagedIoCleanup, otherwise thread + // may continue to execute when framework unloads the driver, + // leading to a crash + // + // We don't manage any I/O separate from the queue, so apart + // from OnSelfManagedIoInit and OnSelfManagedIoCleanup, other + // methods have token implementations + // + + virtual + void + STDMETHODCALLTYPE + OnSelfManagedIoCleanup( + _In_ IWDFDevice * pWdfDevice + ); + + virtual + void + STDMETHODCALLTYPE + OnSelfManagedIoFlush( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoInit( + _In_ IWDFDevice * pWdfDevice + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoSuspend( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoRestart( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoStop( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + + return S_OK; + } + + // + // IPnpCallback + // + + virtual + HRESULT + STDMETHODCALLTYPE + OnD0Entry( + _In_ IWDFDevice* pWdfDevice, + _In_ WDF_POWER_DEVICE_STATE previousState + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnD0Exit( + _In_ IWDFDevice* pWdfDevice, + _In_ WDF_POWER_DEVICE_STATE previousState + ); + + virtual + void + STDMETHODCALLTYPE + OnSurpriseRemoval( + _In_ IWDFDevice* pWdfDevice + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnQueryRemove( + _In_ IWDFDevice* pWdfDevice + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnQueryStop( + _In_ IWDFDevice* pWdfDevice + ); + + // + // IPnpCallbackHardware2 + // + + virtual + HRESULT + STDMETHODCALLTYPE + OnPrepareHardware( + _In_ IWDFDevice3 * pWdfDevice, + _In_ IWDFCmResourceList * pWdfResourcesRaw, + _In_ IWDFCmResourceList * pWdfResourcesTranslated + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnReleaseHardware( + _In_ IWDFDevice3 * pWdfDevice, + _In_ IWDFCmResourceList * pWdfResourcesTranslated + ); + + HRESULT + SimdeviceConnectInterrupt( + _In_ IWDFDevice* pWdfDevice, + _In_opt_ PCM_PARTIAL_RESOURCE_DESCRIPTOR RawResource, + _In_opt_ PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedResource + ); + + static WUDF_INTERRUPT_ISR OnInterruptIsr; + static WUDF_INTERRUPT_WORKITEM OnInterruptWorkItem; + + IWDFDevice * + GetFxDevice( + VOID + ) + { + return m_FxDevice; + } + + PDEVICE_EXTENSION + GetDeviceExtension( + VOID + ) + { + return &m_DevExtension; + } + + HRESULT + TestReadWrite( + _In_ IWDFDevice* pWdfDevice, + _In_ PWSTR RequestString, + _In_ BOOLEAN ReadOperation, + _Inout_updates_bytes_(Size) UCHAR *Data, + _In_ ULONG Size, + _Inout_opt_ IWDFRemoteTarget *IoTargetOut + ); + +}; + diff --git a/gpio/samples/simdeviceumdf/dllsup.cpp b/gpio/samples/simdeviceumdf/dllsup.cpp new file mode 100644 index 00000000..7b0ea74d --- /dev/null +++ b/gpio/samples/simdeviceumdf/dllsup.cpp @@ -0,0 +1,151 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + dllsup.cpp + +Abstract: + + This module contains the implementation of the sample UMDF Driver's + entry point and its exported functions for providing COM support. + + This module can be copied without modification to a new UMDF driver. It + depends on some of the code in comsup.cpp & comsup.h to handle DLL + registration and creating the first class factory. + + This module is dependent on the following defines: + + MYDRIVER_CLASS_ID - A GUID encoded in struct format used to + initialize the driver's ClassID. + + These are defined in internal.h for the sample. If you choose + to use a different primary include file, you should ensure they are + defined there as well. + + +Environment: + + WDF User-Mode Driver Framework (WDF:UMDF) + +--*/ + +#include "internal.h" + +const GUID CLSID_MyDriverCoClass = MYDRIVER_CLASS_ID; + +BOOL +WINAPI +DllMain( + HINSTANCE ModuleHandle, + DWORD Reason, + PVOID /* Reserved */ + ) +/*++ + + Routine Description: + + This is the entry point and exit point for the I/O trace driver. This + does very little as the I/O trace driver has minimal global data. + + This method initializes tracing. + + Arguments: + + ModuleHandle - the DLL handle for this module. + + Reason - the reason this entry point was called. + + Reserved - unused + + Return Value: + + TRUE + +--*/ +{ + + UNREFERENCED_PARAMETER(ModuleHandle); + UNREFERENCED_PARAMETER(Reason); + + return TRUE; +} + +_Check_return_ +STDAPI +DllGetClassObject( + _In_ REFCLSID ClassId, + _In_ REFIID InterfaceId, + _Outptr_ LPVOID *Interface + ) +/*++ + + Routine Description: + + This routine is called by COM in order to instantiate the + driver callback object and do an initial query interface on it. + + This method only creates an instance of the driver's class factory, as this + is the minimum required to support UMDF. + + Arguments: + + ClassId - the CLSID of the object being "gotten" + + InterfaceId - the interface the caller wants from that object. + + Interface - a location to store the referenced interface pointer + + Return Value: + + S_OK if the function succeeds or error indicating the cause of the + failure. + +--*/ +{ + PCClassFactory factory; + + HRESULT hr = S_OK; + + *Interface = NULL; + + // + // If the CLSID doesn't match that of our "coclass" (defined in the IDL + // file) then we can't create the object the caller wants. This may + // indicate that the COM registration is incorrect, and another CLSID + // is referencing this drvier. + // + + if (IsEqualCLSID(ClassId, CLSID_MyDriverCoClass) == false) + { + return CLASS_E_CLASSNOTAVAILABLE; + } + + // + // Create an instance of the class factory for the caller. + // + + factory = new CClassFactory(); + + if (NULL == factory) + { + hr = E_OUTOFMEMORY; + } + + // + // Query the object we created for the interface the caller wants. After + // that we release the object. This will drive the reference count to + // 1 (if the QI succeeded an referenced the object) or 0 (if the QI failed). + // In the later case the object is automatically deleted. + // + + if (SUCCEEDED(hr)) + { + hr = factory->QueryInterface(InterfaceId, Interface); + factory->Release(); + } + + return hr; +} + diff --git a/gpio/samples/simdeviceumdf/driver.cpp b/gpio/samples/simdeviceumdf/driver.cpp new file mode 100644 index 00000000..09f47062 --- /dev/null +++ b/gpio/samples/simdeviceumdf/driver.cpp @@ -0,0 +1,222 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Driver.cpp + +Abstract: + + This module contains the implementation of the UMDF Sample's + core driver callback object. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +//#include "driver.tmh" + +HRESULT +CSimdeviceDriver::CreateInstance( + _Out_ PCSimdeviceDriver *Driver + ) +/*++ + + Routine Description: + + This static method is invoked in order to create and initialize a new + instance of the driver class. The caller should arrange for the object + to be released when it is no longer in use. + + Arguments: + + Driver - a location to store a referenced pointer to the new instance + + Return Value: + + S_OK if successful, or error otherwise. + +--*/ +{ + PCSimdeviceDriver driver; + HRESULT hr; + + // + // Allocate the callback object. + // + + driver = new CSimdeviceDriver(); + + if (NULL == driver) + { + return E_OUTOFMEMORY; + } + + // + // Initialize the callback object. + // + + hr = driver->Initialize(); + + if (SUCCEEDED(hr)) + { + // + // Store a pointer to the new, initialized object in the output + // parameter. + // + + *Driver = driver; + } + else + { + + // + // Release the reference on the driver object to get it to delete + // itself. + // + + driver->Release(); + } + + return hr; +} + +HRESULT +CSimdeviceDriver::Initialize( + VOID + ) +/*++ + + Routine Description: + + This method is called to initialize a newly created driver callback object + before it is returned to the creator. Unlike the constructor, the + Initialize method contains operations which could potentially fail. + + Arguments: + + None + + Return Value: + + None + +--*/ +{ + return S_OK; +} + +HRESULT +CSimdeviceDriver::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Interface + ) +/*++ + + Routine Description: + + This method returns a pointer to the requested interface on the callback + object.. + + Arguments: + + InterfaceId - the IID of the interface to query/reference + + Interface - a location to store the interface pointer. + + Return Value: + + S_OK if the interface is supported. + E_NOINTERFACE if it is not supported. + +--*/ +{ + if (IsEqualIID(InterfaceId, __uuidof(IDriverEntry))) + { + *Interface = QueryIDriverEntry(); + return S_OK; + } + else + { + return CUnknown::QueryInterface(InterfaceId, Interface); + } +} + +HRESULT +CSimdeviceDriver::OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ) +/*++ + + Routine Description: + + The FX invokes this method when it wants to install our driver on a device + stack. This method creates a device callback object, then calls the Fx + to create an Fx device object and associate the new callback object with + it. + + Arguments: + + FxWdfDriver - the Fx driver object. + + FxDeviceInit - the initialization information for the device. + + Return Value: + + status + +--*/ +{ + HRESULT hr; + + PCSimdevice device = NULL; + + // + // TODO: Do any per-device initialization (reading settings from the + // registry for example) that's necessary before creating your + // device callback object here. Otherwise you can leave such + // initialization to the initialization of the device event + // handler. + // + + // + // Create a new instance of our device callback object + // + + hr = CSimdevice::CreateInstance(FxWdfDriver, FxDeviceInit, &device); + + // + // TODO: Change any per-device settings that the object exposes before + // calling Configure to let it complete its initialization. + // + + // + // If that succeeded then call the device's construct method. This + // allows the device to create any queues or other structures that it + // needs now that the corresponding fx device object has been created. + // + + if (SUCCEEDED(hr)) + { + hr = device->Configure(); + } + + // + // Release the reference on the device callback object now that it's been + // associated with an fx device object. + // + + if (NULL != device) + { + device->Release(); + } + + return hr; +} + diff --git a/gpio/samples/simdeviceumdf/driver.h b/gpio/samples/simdeviceumdf/driver.h new file mode 100644 index 00000000..a8f464be --- /dev/null +++ b/gpio/samples/simdeviceumdf/driver.h @@ -0,0 +1,151 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Driver.h + +Abstract: + + This module contains the type definitions for the UMDF sample's + driver callback class. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// This class handles driver events for the sample. In particular +// it supports the OnDeviceAdd event, which occurs when the driver is called +// to setup per-device handlers for a new device stack. +// + +class CSimdeviceDriver : public CUnknown, public IDriverEntry +{ +// +// Private data members. +// +private: + +// +// Private methods. +// +private: + + // + // Returns a refernced pointer to the IDriverEntry interface. + // + + IDriverEntry * + QueryIDriverEntry( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + HRESULT + Initialize( + VOID + ); + +// +// Public methods +// +public: + + // + // The factory method used to create an instance of this driver. + // + + static + HRESULT + CreateInstance( + _Out_ PCSimdeviceDriver *Driver + ); + +// +// COM methods +// +public: + + // + // IDriverEntry methods + // + + virtual + HRESULT + STDMETHODCALLTYPE + OnInitialize( + _In_ IWDFDriver *FxWdfDriver + ) + { + UNREFERENCED_PARAMETER( FxWdfDriver ); + + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ); + + virtual + VOID + STDMETHODCALLTYPE + OnDeinitialize( + _In_ IWDFDriver *FxWdfDriver + ) + { + UNREFERENCED_PARAMETER( FxWdfDriver ); + + return; + } + + // + // IUnknown methods. + // + // We have to implement basic ones here that redirect to the + // base class becuase of the multiple inheritance. + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + __drv_arg(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); +}; + diff --git a/gpio/samples/simdeviceumdf/exports.def b/gpio/samples/simdeviceumdf/exports.def new file mode 100644 index 00000000..45a4fb2b --- /dev/null +++ b/gpio/samples/simdeviceumdf/exports.def @@ -0,0 +1,10 @@ +; Echo.def : Declares the module parameters. + +; +; TODO: Change the library name here to match your binary name. +; + +LIBRARY "SimdeviceUMDF.DLL" + +EXPORTS + DllGetClassObject PRIVATE diff --git a/gpio/samples/simdeviceumdf/internal.h b/gpio/samples/simdeviceumdf/internal.h new file mode 100644 index 00000000..4cc79b71 --- /dev/null +++ b/gpio/samples/simdeviceumdf/internal.h @@ -0,0 +1,96 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Internal.h + +Abstract: + + This module contains the local type definitions for the UMDF Echo + driver sample. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ +#ifndef _INTERNAL_H_ +#define _INTERNAL_H_ + +#pragma once + +#define UMDF_USING_NTSTATUS + +#include +#include +#include +#include +#include + +_Analysis_mode_(_Analysis_code_type_user_driver_); // Macro letting the compiler know this is not a kernel driver (this will help surpress needless warnings) + +// Common WPD, UMDF, and WDM headers +#include +#include + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +// +// Include the WUDF DDI +// +#include "wudfddi.h" + +// +// Use specstrings for in/out annotation of function parameters. +// + +#include "specstrings.h" + +// +// Forward definitions of classes in the other header files. +// + +typedef class CSimdeviceDriver *PCSimdeviceDriver; +typedef class CSimdevice *PCSimdevice; +typedef class CSimdeviceQueue *PCSimdeviceQueue; + +// +// Include the type specific headers. +// + +#include "comsup.h" +#include "driver.h" +#include "device.h" +#include "queue.h" + +__forceinline +#ifdef _PREFAST_ +__declspec(noreturn) +#endif +VOID +WdfTestNoReturn( + VOID + ) +{ + // do nothing. +} + +#define WUDF_TEST_DRIVER_ASSERT(p) \ +{ \ + if ( !(p) ) \ + { \ + DebugBreak(); \ + WdfTestNoReturn(); \ + } \ +} + +#define MYDRIVER_CLASS_ID {0x7ab7dcf5, 0xd1d4, 0x4085, {0x95, 0x47, 0x1d, 0xb9, 0x68, 0xcc, 0xa7, 0x20}} + + +#define SAFE_RELEASE(p) {if ((p)) { (p)->Release(); (p) = NULL; }} +#endif diff --git a/gpio/samples/simdeviceumdf/queue.cpp b/gpio/samples/simdeviceumdf/queue.cpp new file mode 100644 index 00000000..4b66a25f --- /dev/null +++ b/gpio/samples/simdeviceumdf/queue.cpp @@ -0,0 +1,529 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + queue.cpp + +Abstract: + + This file implements the I/O queue interface and performs + the read/write/ioctl operations. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + + +#include "internal.h" + +// +// IUnknown implementation +// + +// +// Queue destructor. +// Free up the buffer, wait for thread to terminate and +// delete critical section. +// + + +CSimdeviceQueue::~CSimdeviceQueue( + VOID + ) +/*++ + +Routine Description: + + + IUnknown implementation of Release + +Arguments: + + +Return Value: + + ULONG (reference count after Release) + +--*/ +{ + if (m_Buffer) { + delete [] m_Buffer; + } + + if (m_InitCritSec) { + ::DeleteCriticalSection(&m_Crit); + } +} + + +// +// Initialize +HRESULT +CSimdeviceQueue::CreateInstance( + _In_ IWDFDevice *FxDevice, + _Out_ PCSimdeviceQueue *Queue + ) +/*++ + +Routine Description: + + + CreateInstance creates an instance of the queue object. + +Arguments: + + ppUkwn - OUT parameter is an IUnknown interface to the queue object + +Return Value: + + HRESULT indicating success or failure + +--*/ +{ + CSimdeviceQueue *pMyQueue = new CSimdeviceQueue; + HRESULT hr; + + if (pMyQueue == NULL) { + return E_OUTOFMEMORY; + } + + hr = pMyQueue->Initialize(FxDevice); + + if (SUCCEEDED(hr)) + { + *Queue = pMyQueue; + } + else + { + pMyQueue->Release(); + } + return hr; +} + +HRESULT +CSimdeviceQueue::Initialize( + _In_ IWDFDevice *FxDevice + ) +{ + IWDFIoQueue *fxQueue; + HRESULT hr; + + // + // Initialize the critical section before we continue + // + + if (!InitializeCriticalSectionAndSpinCount(&m_Crit,0x80000400)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Exit; + } + m_InitCritSec = TRUE; + + // + // Create the framework queue + // + + { + IUnknown *unknown = QueryIUnknown(); + hr = FxDevice->CreateIoQueue(unknown, + TRUE, + WdfIoQueueDispatchSequential, + TRUE, + FALSE, + &fxQueue); + unknown->Release(); + } + + if (FAILED(hr)) { + goto Exit; + } + + m_FxQueue = fxQueue; + + fxQueue->Release(); + +Exit: + return hr; +} + +HRESULT +STDMETHODCALLTYPE +CSimdeviceQueue::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + +Routine Description: + + + Query Interface + +Arguments: + + Follows COM specifications + +Return Value: + + HRESULT indicating success or failure + +--*/ +{ + HRESULT hr; + + + if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackWrite))) { + *Object = QueryIQueueCallbackWrite(); + hr = S_OK; + } else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackRead))) { + *Object = QueryIQueueCallbackRead(); + hr = S_OK; + } else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackDeviceIoControl))) { + *Object = QueryIQueueCallbackDeviceIoControl(); + hr = S_OK; + } else { + hr = CUnknown::QueryInterface(InterfaceId, Object); + } + + return hr; +} + +VOID +STDMETHODCALLTYPE +CSimdeviceQueue::OnDeviceIoControl( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T InputBufferSizeInBytes, + _In_ SIZE_T OutputBufferSizeInBytes + ) +/*++ + +Routine Description: + + + DeviceIoControl dispatch routine + +Arguments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + ControlCode - IO Control Code + InputBufferSizeInBytes - Length of input buffer + OutputBufferSizeInBytes - Length of output buffer + + Always succeeds DeviceIoIoctl +Return Value: + + VOID + +--*/ +{ + + UNREFERENCED_PARAMETER(pWdfQueue); + UNREFERENCED_PARAMETER(ControlCode); + UNREFERENCED_PARAMETER(InputBufferSizeInBytes); + UNREFERENCED_PARAMETER(OutputBufferSizeInBytes); + + pWdfRequest->Complete(S_OK); + return; +} + +VOID +STDMETHODCALLTYPE +CSimdeviceQueue::OnWrite( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T BytesToWrite + ) +/*++ + +Routine Description: + + + Write dispatch routine + IQueueCallbackWrite + +Arguments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + BytesToWrite - Length of bytes in the write buffer + + Allocate and copy data to local buffer +Return Value: + + VOID + +--*/ +{ + + HRESULT hr; + IWDFMemory* pRequestMemory = NULL; + IWDFIoRequest2 * pWdfRequest2 = NULL; + + UNREFERENCED_PARAMETER(pWdfQueue); + + // + // Handle Zero length writes. + // + + if (!BytesToWrite) { + pWdfRequest->CompleteWithInformation(S_OK, 0); + return; + } + + if( BytesToWrite > MAX_WRITE_LENGTH ) { + + pWdfRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_MORE_DATA), 0); + return; + } + + // Get memory object + hr = pWdfRequest->QueryInterface(IID_PPV_ARGS(&pWdfRequest2)); + + if (FAILED(hr)) { + pWdfRequest->Complete(hr); + return; + } + + // Release previous buffer if set + + if( m_Buffer != NULL ) { + delete [] m_Buffer; + m_Buffer = NULL; + m_Length = 0L; + } + + // Allocate Buffer + + m_Buffer = new UCHAR[BytesToWrite]; + if (m_Buffer == NULL) { + pWdfRequest->Complete(E_OUTOFMEMORY); + m_Length = 0L; + return; + } + + hr = pWdfRequest2->RetrieveInputMemory(&pRequestMemory); + + if (FAILED(hr)) { + goto Exit; + } + + // Copy from memory object to our buffer + + hr = pRequestMemory->CopyToBuffer(0, m_Buffer, BytesToWrite); + + if (FAILED(hr)) { + goto Exit; + } + + + // + // Save the information so that we can use it + // to complete the request later. + // + + Lock(); + + m_Length = (ULONG) BytesToWrite; + m_XferredBytes = m_Length; + m_CurrentRequest = pWdfRequest2; + pWdfRequest2 = NULL; + + Unlock(); + +Exit: + + if (FAILED(hr)) { + pWdfRequest2->CompleteWithInformation(hr, 0); + delete [] m_Buffer; + m_Buffer = NULL; + } + + SAFE_RELEASE(pRequestMemory); + SAFE_RELEASE(pWdfRequest2); + + return; +} + +VOID +STDMETHODCALLTYPE +CSimdeviceQueue::OnRead( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T SizeInBytes + ) +/*++ + +Routine Description: + + + Read dispatch routine + IQueueCallbackRead + +Arguments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + SizeInBytes - Length of bytes in the read buffer + + Copy available data into the read buffer +Return Value: + + VOID + +--*/ +{ + IWDFMemory* pRequestMemory = NULL; + IWDFIoRequest2 * pWdfRequest2 = NULL; + HRESULT hr; + + UNREFERENCED_PARAMETER(pWdfQueue); + + // + // Handle Zero length reads. + // + + if (!SizeInBytes) { + pWdfRequest->CompleteWithInformation(S_OK, 0); + return; + } + + if (m_Buffer == NULL) { + pWdfRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), SizeInBytes); + return; + } + + if (m_Length < SizeInBytes) { + SizeInBytes = m_Length; + } + + // + // Get memory object + // + + hr = pWdfRequest->QueryInterface(IID_PPV_ARGS(&pWdfRequest2)); + + if (FAILED(hr)) { + pWdfRequest->Complete(hr); + return; + } + + + hr = pWdfRequest2->RetrieveOutputMemory(&pRequestMemory ); + + if (FAILED(hr)) { + goto Exit; + } + + // Copy from buffer to memory object + + hr = pRequestMemory->CopyFromBuffer(0, m_Buffer, SizeInBytes); + + if (FAILED(hr)) { + goto Exit; + } + + + // + // Save the information so that we can use it + // to complete the request later. + // + + Lock(); + + m_CurrentRequest = pWdfRequest2; + m_XferredBytes = SizeInBytes; + pWdfRequest2 = NULL; + + Unlock(); + +Exit: + + if (FAILED(hr)) { + pWdfRequest2->CompleteWithInformation(hr, 0); + } + + SAFE_RELEASE(pRequestMemory); + SAFE_RELEASE(pWdfRequest2); + return; +} + +DWORD +CSimdeviceQueue::CompletionThread( + PVOID ThreadParameter + ) +/*++ + +Routine Description: + + + This routine is called from the thread started to complete + I/O requests. It sleeps for TIMER_PERIOD and then completes + the current request. Note that it has to release the lock + before it calls the request complete method. + +Arguments: + + ThreadParameter - This is a pointer to the Queue object. + +Return Value: + + VOID + +--*/ +{ + CSimdeviceQueue *pQueue = (CSimdeviceQueue *)ThreadParameter; + IWDFIoRequest2 *request; + SIZE_T bytesXferred = 0; + + for (;;) { + + // + // Block for a fixed time and then complete the request. + // + + Sleep(TIMER_PERIOD); + + pQueue->Lock(); + + // + // Process the current request. + // + + request = pQueue->m_CurrentRequest; + + if (request) { + bytesXferred = pQueue->m_XferredBytes; + } + + // + // Reset values. + // + + pQueue->m_CurrentRequest = NULL; + pQueue->m_XferredBytes = 0; + + + pQueue->Unlock(); + + if (request) { + request->CompleteWithInformation(S_OK, bytesXferred); + SAFE_RELEASE(request); + } + + // + // If thread needs to be terminated + // + + if (pQueue->m_ExitThread) { + ExitThread(0); + } + + } + +} diff --git a/gpio/samples/simdeviceumdf/queue.h b/gpio/samples/simdeviceumdf/queue.h new file mode 100644 index 00000000..108299a4 --- /dev/null +++ b/gpio/samples/simdeviceumdf/queue.h @@ -0,0 +1,211 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + queue.h + +Abstract: + + This file defines the queue callback interface. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// Set max write length for testing +#define MAX_WRITE_LENGTH (40*1024) + +// Set timer period in ms +#define TIMER_PERIOD 100 + +// +// Queue Callback Object. +// + +class CSimdeviceQueue : + public IQueueCallbackDeviceIoControl, + public IQueueCallbackRead, + public IQueueCallbackWrite, + public CUnknown +{ + PVOID m_Buffer; // Current buffer + ULONG m_Length; // Length of the buffer + SIZE_T m_XferredBytes; // Amount of bytes transferred for the current request + IWDFIoRequest2 *m_CurrentRequest; // Current request + CRITICAL_SECTION m_Crit; // Lock to protect updates to CSimdeviceQueue fields + BOOLEAN m_ExitThread; // If TRUE Terminate thread. + BOOLEAN m_InitCritSec; // If TRUE lock initialized + + IWDFIoQueue *m_FxQueue; + + CSimdeviceQueue() : + m_Buffer(NULL), + m_Length (0), + m_CurrentRequest(NULL), + m_XferredBytes(0), + m_ExitThread(FALSE), + m_InitCritSec(FALSE), + m_FxQueue(NULL) + { + } + + virtual ~CSimdeviceQueue(); + + __inline + void + Lock( + ) + { + ::EnterCriticalSection(&m_Crit); + } + + __inline + void + Unlock( + ) + { + ::LeaveCriticalSection(&m_Crit); + } + + HRESULT + Initialize( + _In_ IWDFDevice *FxDevice + ); + +public: + + // + // Completion thread routine. + // + + static DWORD CompletionThread( PVOID ThreadParameter); + + // + // Sets the flag to make thread exit + // + + void + SetExitThread() + { + m_ExitThread = TRUE; + } + + static + HRESULT + CreateInstance( + _In_ IWDFDevice *FxDevice, + _Out_ PCSimdeviceQueue *Queue + ); + + HRESULT + Configure( + VOID + ) + { + return S_OK; + } + + + IQueueCallbackDeviceIoControl * + QueryIQueueCallbackDeviceIoControl( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + IQueueCallbackRead * + QueryIQueueCallbackRead( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + IQueueCallbackWrite * + QueryIQueueCallbackWrite( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + // + // IUnknown + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) { + return CUnknown::AddRef(); + } + + __drv_arg(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) { + return CUnknown::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + // + // Wdf Callbacks + // + + // IQueueCallbackDeviceIoControl + // + virtual + VOID + STDMETHODCALLTYPE + OnDeviceIoControl( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T InputBufferSizeInBytes, + _In_ SIZE_T OutputBufferSizeInBytes + ); + + // IQueueCallbackWrite + // + virtual + VOID + STDMETHODCALLTYPE + OnWrite( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T NumOfBytesToWrite + ); + + // IQueueCallbackRead + // + virtual + VOID + STDMETHODCALLTYPE + OnRead( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T NumOfBytesToRead + ); +}; diff --git a/gpio/samples/simdeviceumdf/simdeviceumdf.rc b/gpio/samples/simdeviceumdf/simdeviceumdf.rc new file mode 100644 index 00000000..8465960f --- /dev/null +++ b/gpio/samples/simdeviceumdf/simdeviceumdf.rc @@ -0,0 +1,21 @@ +//--------------------------------------------------------------------------- +// SimdeviceUMDF.rc +// +// Copyright (c) Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include + +// +// TODO: Change the file description and file names to match your binary. +// + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WDF:UMDF Simdevice User-Mode Driver Sample" +#define VER_INTERNALNAME_STR "SimdeviceUMDF" +#define VER_ORIGINALFILENAME_STR "SimdeviceUMDF.dll" + +#include "common.ver" diff --git a/gpio/samples/simdeviceumdf/simdeviceumdfdriver.ctl b/gpio/samples/simdeviceumdf/simdeviceumdfdriver.ctl new file mode 100644 index 00000000..a0ce2089 --- /dev/null +++ b/gpio/samples/simdeviceumdf/simdeviceumdfdriver.ctl @@ -0,0 +1 @@ +d93fb470-afb1-4af8-860e-75f726c66f6b WudfEchoDriverTraceGuid diff --git a/gpio/samples/simdeviceumdf/simdeviceumdfdriver.inx b/gpio/samples/simdeviceumdf/simdeviceumdfdriver.inx new file mode 100644 index 00000000..fa17b2bb --- /dev/null +++ b/gpio/samples/simdeviceumdf/simdeviceumdfdriver.inx @@ -0,0 +1,78 @@ +; +; SimdeviceUMDFDriver.inf +; + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFTWUDF% +CatalogFile=GpioSamples.cat +DriverVer=03/20/2003,5.00.3788 + +[Manufacturer] +%MSFTWUDF%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%SimdeviceUMDFName%=SimdeviceUMDF_Install,ACPI\TEST0004 + +[ClassInstall32] +AddReg=SampleClass_RegistryAdd + +[SampleClass_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"-10" + +[SourceDisksFiles] +SimdeviceUMDF.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== Simdevice Sample UMDF Driver ================================== + +[SimdeviceUMDF_Install.NT] +CopyFiles=UMDriverCopy + +[SimdeviceUMDF_Install.NT.hw] + +[SimdeviceUMDF_Install.NT.Services] +AddService=SimdeviceUMDF,0x000001fa,SimdeviceUMDF_ServiceInstall + +[SimdeviceUMDF_Install.NT.Wdf] +UmdfService=SimdeviceUMDF,SimdeviceUMDF_Install +UmdfServiceOrder=SimdeviceUMDF + +; this enables direct hardware access from UMDF driver +UmdfDirectHardwareAccess=AllowDirectHardwareAccess + +; direct i/o devices cannot run in default pool. +UmdfHostProcessSharing=ProcessSharingDisabled + +[SimdeviceUMDF_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID={7AB7DCF5-D1D4-4085-9547-1DB968CCA720} +ServiceBinary=%12%\UMDF\SimdeviceUMDF.dll + +[SimdeviceUMDF_ServiceInstall] +DisplayName = %SimdeviceUMDFDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to drivers\UMDF +CoInstallers_CopyFiles=11 + +[UMDriverCopy] +SimdeviceUMDF.dll + +; =================== Generic ================================== + +[Strings] +MSFTWUDF="Microsoft Internal (WUDF)" +MediaDescription="Microsoft WUDF Sample Driver Installation Media" +ClassName="Sample Device" +SimdeviceUMDFDisplayName="Sample User-mode Driver Framework Reflector driver" +SimdeviceUMDFName="Sample device UMDF Driver" diff --git a/gpio/samples/simgpio/simgpio.c b/gpio/samples/simgpio/simgpio.c new file mode 100644 index 00000000..4696e20d --- /dev/null +++ b/gpio/samples/simgpio/simgpio.c @@ -0,0 +1,2540 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + simgpio.c + +Abstract: + + This sample implements a GPIO client driver for simulated GPIO (SimGpio) + controller. + + Note: DIRQL in the comments below refers to device IRQL, which is any + IRQL > DISPATCH_LEVEL (and less than some IRQL reserved for OS use). + + +Environment: + + Kernel mode + +--*/ + + +// +// The SimGpio controller has 4 GPIO banks, with banks consisting of +// 32 pins each. Any pin can be configured for interrupt, input or output. +// Each bank has the the following registers. The register set for different +// banks are laid out adjacent to one another in the physical memory map. +// +// Register | Offset | Size +// ------------------------------------------------ +// ModeRegister | 0x0 | 4 bytes +// PolarityRegister | 0x4 | 8 bytes +// EnableRegister | 0xC | 4 bytes +// StatusRegister | 0x10 | 4 bytes +// DirectionRegister | 0x14 | 4 bytes +// LevelRegister | 0x18 | 4 bytes +// +// Mode Register - Interrupt mode (Level = 0x1 or Edge = 0x0) for pins +// enabled for interrupts. +// +// Polarity Register - ActiveLow (0x0) or ActiveHigh (0x1) for Level-triggered +// interrupts; Falling-edge (0x0), Rising-edge (0x1), or Both-Edges (0x3) +// for Edge-triggered interrupts. Note there are two bits per pin. +// +// Enable Register - A mask of which pins are enabled for interrupt. A bit +// is set if the pin is enabled for interrupt. +// +// Status Register - A register containing which pins are actively interrupting. +// If any of the bit is set, then the GPIO controller's interrupt fires. +// +// Direction Register - A register which indicates whether a given pin is +// configured for input (0x1) or output (0x0). +// +// LevelRegister - A bitmask which indicates holds the value for a given pin +// (i.e. whether the pin is high (0x1) or low (0x0)). +// + + +// +// ------------------------------------------------------------------- Includes +// + +#include +#include +#include + +// +// -------------------------------------------------------------------- Defines +// + +// +// Define total number of pins on the simulated GPIO controller. +// + +#define SIM_GPIO_TOTAL_PINS (4 * 32) +#define SIM_GPIO_PINS_PER_BANK (32) +#define SIM_GPIO_TOTAL_BANKS (4) + +// +// Pool tag for SimGpio allocations. +// + +#define SIM_GPIO_POOL_TAG 'GmiS' + +// +// Define that controls whether f-state based power management will be supported +// or not by the client driver. +// + +#define ENABLE_F_STATE_POWER_MGMT + +// +// Define F1 state residency (in sec), latency values (in sec) and nominal +// power. +// +// This sample demonstrates F-state management effected as part of a critical +// system transition (when the system enters connected-standby). To prevent a +// GPIO bank from being transitioned into a low power state as part of runtime +// idle power management (by mini-PEP), specify very high values for residency +// and transition latency. +// +// If GPIO banks need to be runtime power managed, then specify more reasonable +// values below. +// + +#ifdef ENABLE_F_STATE_POWER_MGMT + +#define SIM_GPIO_F1_NOMINAL_POWER (0) +#define SIM_GPIO_F1_RESIDENCY (8*60*60) // 8hrs +#define SIM_GPIO_F1_TRANSITION (1*60*60) // 1hr + +#endif + +// +// Macro for pointer arithmetic. +// + +#define Add2Ptr(Ptr, Value) ((PVOID)((PUCHAR)(Ptr) + (Value))) + +// +// Determine whether the given pin is reserved or not. Currently no pins are +// reserved on the simulated GPIO controller. +// + +__pragma(warning(disable: 4127)) // conditional expression is a constant + +// +// ---------------------------------------------------------------------- Types +// + +// +// Define the registers within the SimGPIO controller. There are 32 pins per +// controller. Note this is a logical device and thus may correspond to a +// physical bank or module if the GPIO controller in hardware has more than +// 32 pins. +// + + + +typedef struct _SIM_GPIO_REGISTERS { + ULONG ModeRegister; + ULONG PolarityRegister[2]; + ULONG EnableRegister; + ULONG StatusRegister; + ULONG DirectionRegister; + ULONG LevelRegister; +} SIM_GPIO_REGISTERS, *PSIM_GPIO_REGISTERS; + +typedef struct _SIM_GPIO_BANK { + LARGE_INTEGER PhysicalBaseAddress; + PSIM_GPIO_REGISTERS Registers; + ULONG Length; + SIM_GPIO_REGISTERS SavedContext; +} SIM_GPIO_BANK, *PSIM_GPIO_BANK; + +// +// The SimGPIO client driver device extension. +// + +typedef struct _SIM_GPIO_CONTEXT { + USHORT TotalPins; + LARGE_INTEGER PhysicalBaseAddress; + PSIM_GPIO_REGISTERS ControllerBase; + ULONG Length; + SIM_GPIO_BANK Banks[SIM_GPIO_TOTAL_BANKS]; +} SIM_GPIO_CONTEXT, *PSIM_GPIO_CONTEXT; + +SIM_GPIO_REGISTERS GlobalGpioRegisters[SIM_GPIO_TOTAL_BANKS] = {0}; + +// +// ----------------------------------------------------------------- Prototypes +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_UNLOAD SimGpioEvtDriverUnload; +EVT_WDF_DRIVER_DEVICE_ADD SimGpioEvtDeviceAdd; + +// +// General interfaces. +// + +GPIO_CLIENT_PREPARE_CONTROLLER SimGpioPrepareController; +GPIO_CLIENT_RELEASE_CONTROLLER SimGpioReleaseController; +GPIO_CLIENT_QUERY_CONTROLLER_BASIC_INFORMATION + SimGpioQueryControllerBasicInformation; + +GPIO_CLIENT_QUERY_SET_CONTROLLER_INFORMATION + SimGpioQuerySetControllerInformation; + +GPIO_CLIENT_START_CONTROLLER SimGpioStartController; +GPIO_CLIENT_STOP_CONTROLLER SimGpioStopController; + +// +// Interrupt enable, disable, mask and unmask handlers. +// + +GPIO_CLIENT_ENABLE_INTERRUPT SimGpioEnableInterrupt; +GPIO_CLIENT_DISABLE_INTERRUPT SimGpioDisableInterrupt; +GPIO_CLIENT_MASK_INTERRUPTS SimGpioMaskInterrupts; +GPIO_CLIENT_UNMASK_INTERRUPT SimGpioUnmaskInterrupt; +GPIO_CLIENT_RECONFIGURE_INTERRUPT SimGpioReconfigureInterrupt; + +// +// Handlers to query active/enabled interrupts and clear active interrupts. +// + +GPIO_CLIENT_QUERY_ACTIVE_INTERRUPTS SimGpioQueryActiveInterrupts; +GPIO_CLIENT_CLEAR_ACTIVE_INTERRUPTS SimGpioClearActiveInterrupts; +GPIO_CLIENT_QUERY_ENABLED_INTERRUPTS SimGpioQueryEnabledInterrupts; + +// +// Handlers for GPIO I/O operations. +// + +GPIO_CLIENT_CONNECT_IO_PINS SimGpioConnectIoPins; +GPIO_CLIENT_DISCONNECT_IO_PINS SimGpioDisconnectIoPins; +GPIO_CLIENT_READ_PINS_MASK SimGpioReadGpioPins; +GPIO_CLIENT_WRITE_PINS_MASK SimGpioWriteGpioPins; + +// +// Handlers for save and restore hardware context callbacks. +// + +GPIO_CLIENT_SAVE_BANK_HARDWARE_CONTEXT SimGpioSaveBankHardwareContext; +GPIO_CLIENT_RESTORE_BANK_HARDWARE_CONTEXT SimGpioRestoreBankHardwareContext; + +// +// -------------------------------------------------------------------- Pragmas +// + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, SimGpioEvtDriverUnload) +#pragma alloc_text(PAGE, SimGpioEvtDeviceAdd) + +// +// ------------------------------------------------------------------ Functions +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is the driver initialization entry point. + +Arguments: + + DriverObject - Pointer to the driver object created by the I/O manager. + + RegistryPath - Pointer to the driver specific registry key. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDFDRIVER Driver; + WDF_DRIVER_CONFIG DriverConfig; + GPIO_CLIENT_REGISTRATION_PACKET RegistrationPacket; + NTSTATUS Status; + + // + // Initialize the driver configuration structure. + // + + WDF_DRIVER_CONFIG_INIT(&DriverConfig, SimGpioEvtDeviceAdd); + DriverConfig.EvtDriverUnload = SimGpioEvtDriverUnload; + + // + // Create a framework driver object to represent our driver. + // + + Status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &DriverConfig, + &Driver); + + if (!NT_SUCCESS(Status)) { + goto DriverEntryEnd; + } + + // + // Initialize the client driver registration packet. + // + + RtlZeroMemory(&RegistrationPacket, sizeof(GPIO_CLIENT_REGISTRATION_PACKET)); + RegistrationPacket.Version = GPIO_CLIENT_VERSION; + RegistrationPacket.Size = sizeof(GPIO_CLIENT_REGISTRATION_PACKET); + + // + // Initialize the device context size. + // + + RegistrationPacket.ControllerContextSize = sizeof(SIM_GPIO_CONTEXT); + + // + // General interfaces. + // + + RegistrationPacket.CLIENT_PrepareController = SimGpioPrepareController; + RegistrationPacket.CLIENT_QueryControllerBasicInformation = + SimGpioQueryControllerBasicInformation; + + // + // The query/set handler is required in this sample only if F-state + // power management is enabled to indicate which banks support f-state + // based power maanagement. + // + +#ifdef ENABLE_F_STATE_POWER_MGMT + + RegistrationPacket.CLIENT_QuerySetControllerInformation = + SimGpioQuerySetControllerInformation; + +#endif + + RegistrationPacket.CLIENT_StartController = SimGpioStartController; + RegistrationPacket.CLIENT_StopController = SimGpioStopController; + RegistrationPacket.CLIENT_ReleaseController = SimGpioReleaseController; + + // + // Interrupt enable and disable handlers. + // + + RegistrationPacket.CLIENT_DisableInterrupt = SimGpioDisableInterrupt; + RegistrationPacket.CLIENT_EnableInterrupt = SimGpioEnableInterrupt; + + // + // Interrupt mask, unmask and reconfigure interrupt handlers. + // + + RegistrationPacket.CLIENT_MaskInterrupts = SimGpioMaskInterrupts; + RegistrationPacket.CLIENT_UnmaskInterrupt = SimGpioUnmaskInterrupt; + RegistrationPacket.CLIENT_ReconfigureInterrupt = SimGpioReconfigureInterrupt; + + // + // Handlers to query active/enabled interrupts and clear active interrupts. + // + + RegistrationPacket.CLIENT_ClearActiveInterrupts = SimGpioClearActiveInterrupts; + RegistrationPacket.CLIENT_QueryActiveInterrupts = SimGpioQueryActiveInterrupts; + RegistrationPacket.CLIENT_QueryEnabledInterrupts = SimGpioQueryEnabledInterrupts; + + // + // Handlers for GPIO I/O operations. + // + + RegistrationPacket.CLIENT_ConnectIoPins = SimGpioConnectIoPins; + RegistrationPacket.CLIENT_DisconnectIoPins = SimGpioDisconnectIoPins; + RegistrationPacket.CLIENT_ReadGpioPinsUsingMask = SimGpioReadGpioPins; + RegistrationPacket.CLIENT_WriteGpioPinsUsingMask = SimGpioWriteGpioPins; + + // + // Handlers for GPIO save and restore context (if F-state power mgmt is + // supported). + // + +#ifdef ENABLE_F_STATE_POWER_MGMT + + RegistrationPacket.CLIENT_SaveBankHardwareContext = SimGpioSaveBankHardwareContext; + RegistrationPacket.CLIENT_RestoreBankHardwareContext = SimGpioRestoreBankHardwareContext; + +#endif + + // + // Register the SimGPIO client driver with the GPIO class extension. + // + + Status = GPIO_CLX_RegisterClient(Driver, &RegistrationPacket, RegistryPath); + +DriverEntryEnd: + return Status; +} + +VOID +SimGpioEvtDriverUnload ( + _In_ WDFDRIVER Driver + ) + +/*++ + +Routine Description: + + This routine is called by WDF to allow final cleanup prior to unloading the + SimGPIO client driver. This routine unregisters the client driver from the + class extension. + +Arguments: + + Driver - Supplies a handle to a framework driver object. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS Status; + + PAGED_CODE(); + + Status = GPIO_CLX_UnregisterClient(Driver); + NT_ASSERT(NT_SUCCESS(Status)); +} + +NTSTATUS +SimGpioEvtDeviceAdd ( + _In_ WDFDRIVER Driver, + _Inout_ PWDFDEVICE_INIT DeviceInit + ) + +/*++ + +Routine Description: + + This routine is the AddDevice entry point for the client driver. This + routine is called by the framework in response to AddDevice call from the + PnP manager. It will create and initialize the device object to represent + a new instance of the simulated GPIO controller. + +Arguments: + + Driver - Supplies a handle to the driver object created in DriverEntry. + + DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT + structure. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDFDEVICE Device; + WDF_OBJECT_ATTRIBUTES FdoAttributes; + NTSTATUS Status; + + PAGED_CODE(); + + // + // Call the GPIO class extension's pre-device create interface. + // + + Status = GPIO_CLX_ProcessAddDevicePreDeviceCreate(Driver, + DeviceInit, + &FdoAttributes); + if (!NT_SUCCESS(Status)) { + goto EvtDeviceAddEnd; + } + + // + // Call the framework to create the device and attach it to the lower stack. + // + + Status = WdfDeviceCreate(&DeviceInit, &FdoAttributes, &Device); + if (!NT_SUCCESS(Status)) { + goto EvtDeviceAddEnd; + } + + // + // Call the GPIO class extension's post-device create interface. + // + + Status = GPIO_CLX_ProcessAddDevicePostDeviceCreate(Driver, Device); + if (!NT_SUCCESS(Status)) { + goto EvtDeviceAddEnd; + } + +EvtDeviceAddEnd: + return Status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +VOID +SimGpiopUnmapControllerBase ( + _In_ PSIM_GPIO_CONTEXT GpioContext + ) + +/*++ + +Routine Description: + + This routine releases the memory mapping for the GPIO controller's + registers, if one has been established. + + N.B. This function is not marked pageable because this function is in + the device power down path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + if ((GpioContext->ControllerBase != NULL) && + (GpioContext->ControllerBase != GlobalGpioRegisters)) { + MmUnmapIoSpace(GpioContext->ControllerBase, GpioContext->Length); + GpioContext->ControllerBase = NULL; + } +} + +// +// ---------------------------------------------------------- General intefaces +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioPrepareController ( + _In_ WDFDEVICE Device, + _In_ PVOID Context, + _In_ WDFCMRESLIST ResourcesRaw, + _In_ WDFCMRESLIST ResourcesTranslated + ) + +/*++ + +Routine Description: + + This routine is called by the GPIO class extension to prepare the + simulated GPIO controller for use. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ResourcesRaw - Supplies a handle to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + ResourcesTranslated - Supplies a handle to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + BANK_ID BankId; + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + ULONG InterruptResourceCount; + ULONG MemoryResourceCount; + ULONG RequiredSize; + ULONG Offset; + ULONG ResourceCount; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(ResourcesRaw); + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + RtlZeroMemory(GpioContext, sizeof(SIM_GPIO_CONTEXT)); + GpioContext->TotalPins = SIM_GPIO_TOTAL_PINS; + + // + // Walk through the resource list and map all the resources. Atleast one + // memory resource and one interrupt resource is expected. The resources + // are described in the ACPI namespace. + // + + InterruptResourceCount = 0; + MemoryResourceCount = 0; + ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated); + Status = STATUS_SUCCESS; + for (Index = 0; Index < ResourceCount; Index += 1) { + Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index); + switch(Descriptor->Type) { + + // + // The memory resource supplies the physical register base for the GPIO + // controller. Map it virtually as non-cached. + // + + case CmResourceTypeMemory: + if (MemoryResourceCount == 0) { + RequiredSize = SIM_GPIO_TOTAL_BANKS * sizeof(SIM_GPIO_REGISTERS); + + NT_ASSERT(Descriptor->u.Memory.Length >= RequiredSize); + + if (Descriptor->u.Memory.Length < RequiredSize) { + Status = STATUS_UNSUCCESSFUL; + break; + } + + GpioContext->PhysicalBaseAddress = Descriptor->u.Memory.Start; + GpioContext->ControllerBase = + (PSIM_GPIO_REGISTERS)MmMapIoSpaceEx( + Descriptor->u.Memory.Start, + RequiredSize, + PAGE_NOCACHE | PAGE_READWRITE); + + // + // Fail initialization if mapping of the memory region failed. + // + + if (GpioContext->ControllerBase == NULL) { + Status = STATUS_UNSUCCESSFUL; + } + + GpioContext->Length = RequiredSize; + } + + MemoryResourceCount += 1; + break; + + // + // IO port resources are unexpected, fail initialization. + // + + case CmResourceTypePort: + Status = STATUS_UNSUCCESSFUL; + break; + + // + // Interrupt resource which supplies the GPIO controller interrupt + // (that connects to the GIC). + // + // N.B. Connecting of the interrupt is handled by the GPIO class + // extension. Only ensure that appropriate number of interrupts + // were described. + // + + case CmResourceTypeInterrupt: + InterruptResourceCount += 1; + break; + + // + // This could be device-private type added by the underlying bus + // driver. Do not filter or alter this resource information. + // + + default: + break; + } + + if (!NT_SUCCESS(Status)) { + goto PrepareControllerEnd; + } + } + + // + // NOTE: As SimGPIO is not a real hardware device, it doesn't really have + // any physical registers. For test purposes, fake the GPIO registers + // using a software defined global structure. This should NOT be done + // for a real GPIO controller + // + // BEGIN: SIMGPIO HACK. + // + + if (MemoryResourceCount == 0) { + GpioContext->ControllerBase = GlobalGpioRegisters; + MemoryResourceCount = 1; + } + + // + // END: SIMGPIO HACK. + // + + // + // Fail initialization if minimum number of interrupt + // and memory resources were not described correctly. + // + + if ((InterruptResourceCount < SIM_GPIO_TOTAL_BANKS) || + (MemoryResourceCount < 1)) { + Status = STATUS_UNSUCCESSFUL; + goto PrepareControllerEnd; + } + + // + // Initialize the base address of registers per bank. + // + + for (BankId = 0; BankId < SIM_GPIO_TOTAL_BANKS; BankId += 1) { + GpioBank = &GpioContext->Banks[BankId]; + Offset = BankId * sizeof(SIM_GPIO_REGISTERS); + GpioBank->Registers = Add2Ptr(GpioContext->ControllerBase, Offset); + GpioBank->Length = sizeof(SIM_GPIO_REGISTERS); + } + +PrepareControllerEnd: + if (!NT_SUCCESS(Status)) { + SimGpiopUnmapControllerBase(GpioContext); + } + + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioReleaseController ( + _In_ WDFDEVICE Device, + _In_ PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called by the GPIO class extension to uninitialize the GPIO + controller. + + N.B. This function is not marked pageable because this function is in + the device power down path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(Context); + + // + // Release the mappings established in the initialize callback. + // + // N.B. Disconnecting of the interrupt is handled by the GPIO class + // extension. + // + + SimGpiopUnmapControllerBase((PSIM_GPIO_CONTEXT)Context); + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioQueryControllerBasicInformation ( + _In_ PVOID Context, + _Out_ PCLIENT_CONTROLLER_BASIC_INFORMATION ControllerInformation + ) + +/*++ + +Routine Description: + + This routine returns the GPIO controller's attributes to the class extension. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ControllerInformation - Supplies a pointer to a buffer that receives + controller's information. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PSIM_GPIO_CONTEXT GpioContext; + + ControllerInformation->Version = GPIO_CONTROLLER_BASIC_INFORMATION_VERSION; + ControllerInformation->Size = sizeof(CLIENT_CONTROLLER_BASIC_INFORMATION); + + // + // Specify the number of pins on the SimGPIO controller. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + ControllerInformation->TotalPins = GpioContext->TotalPins; + ControllerInformation->NumberOfPinsPerBank = SIM_GPIO_PINS_PER_BANK; + + // + // Indicate that the GPIO controller is memory-mapped and thus can be + // manipulated at DIRQL. + // + // N.B. If the GPIO controller is off-SOC behind some serial bus like + // I2C or SPI, then this field must be set to FALSE. + // + + ControllerInformation->Flags.MemoryMappedController = TRUE; + + // + // Indicate that status register must be cleared explicitly. + // + + ControllerInformation->Flags.ActiveInterruptsAutoClearOnRead = 0; + + // + // Indicate that the client driver would like to receive IO requests as a + // set of bitmasks as that maps directly to the register operations. + // + + ControllerInformation->Flags.FormatIoRequestsAsMasks = 1; + + // + // Indicate that the GPIO controller does not support controller-level + // D-state power management. + // + + ControllerInformation->Flags.DeviceIdlePowerMgmtSupported = FALSE; + + // + // Note if bank-level F-state power management is supported, then specify + // as such. Note the QuerySetControllerInformation() handler also needs to + // be implemented in this case. + // + +#ifdef ENABLE_F_STATE_POWER_MGMT + + ControllerInformation->Flags.BankIdlePowerMgmtSupported = TRUE; + +#else + + ControllerInformation->Flags.BankIdlePowerMgmtSupported = FALSE; + +#endif + + // + // Note the IdleTimeout parameter does not need to be initialized if + // D-state power management is not supported. + // + // ControllerInformation->IdleTimeout = IdleTimeoutDefaultValue; + // + + // + // Note if the GPIO controller does not support hardware debouncing and + // software-debouncing should be used instead, set the EmulateDebouncing + // flag. + // + // ControllerInformation->Flags.EmulateDebouncing = TRUE; + // + + // + // Indicate that the client driver prefers GPIO class extension ActiveBoth + // emulation. + // + + ControllerInformation->Flags.EmulateActiveBoth = TRUE; + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioQuerySetControllerInformation ( + _In_ PVOID Context, + _In_ PCLIENT_CONTROLLER_QUERY_SET_INFORMATION_INPUT InputBuffer, + _Out_opt_ PCLIENT_CONTROLLER_QUERY_SET_INFORMATION_OUTPUT OutputBuffer + ) + +/*++ + +Routine Description: + + This routine is the generic GPIO query/set handler. Currently it only + supports returning bank power information. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + InputBuffer - Supplies a pointer to a buffer that receives the parameters + for the query or set operation. + + OutputBuffer - Supplies a pointer to the GPIO class extension allocated + buffer to return the output values. Note on entry, the + OutputBuffer->Size indicates how big the output buffer is. On exit, the + OutputBuffer->Size indicates the filled-in size or required size. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PPO_FX_COMPONENT_IDLE_STATE F1Parameters; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(Context); + + if((InputBuffer == NULL) || (OutputBuffer == NULL)) { + Status = STATUS_NOT_SUPPORTED; + goto QuerySetControllerInformationEnd; + } + + if (InputBuffer->RequestType != QueryBankPowerInformation) { + Status = STATUS_NOT_SUPPORTED; + goto QuerySetControllerInformationEnd; + } + + // + // Set the version and size of the output buffer. + // + + OutputBuffer->Version = GPIO_BANK_POWER_INFORMATION_OUTPUT_VERSION; + OutputBuffer->Size = sizeof(CLIENT_CONTROLLER_QUERY_SET_INFORMATION_OUTPUT); + + // + // Mark the given bank (InputBuffer->BankPowerInformation.BankId) as + // supporting F1 state. Since all banks support it, the BankId is not + // checked. + // + + OutputBuffer->BankPowerInformation.F1StateSupported = TRUE; + + // + // Supply the attributes for the F1 power state. + // + + F1Parameters = &OutputBuffer->BankPowerInformation.F1IdleStateParameters; + F1Parameters->NominalPower = SIM_GPIO_F1_NOMINAL_POWER; + F1Parameters->ResidencyRequirement = + WDF_ABS_TIMEOUT_IN_SEC(SIM_GPIO_F1_RESIDENCY); + + F1Parameters->TransitionLatency = + WDF_ABS_TIMEOUT_IN_SEC(SIM_GPIO_F1_TRANSITION); + + Status = STATUS_SUCCESS; + +QuerySetControllerInformationEnd: + return Status; +} + + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioStartController ( + _In_ PVOID Context, + _In_ BOOLEAN RestoreContext, + _In_ WDF_POWER_DEVICE_STATE PreviousPowerState + ) + +/*++ + +Routine Description: + + This routine starts the simulated GPIO controller. This routine is + responsible for configuring all the pins to their default modes. + + N.B. This function is not marked pageable because this function is in + the device power up path. It is called at PASSIVE_IRQL though. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + RestoreContext - Supplies a flag that indicates whether the client driver + should restore the GPIO controller state to a previously saved state + or not. + + PreviousPowerState - Supplies the device power state that the device was in + before this transition to D0. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + GPIO_SAVE_RESTORE_BANK_HARDWARE_CONTEXT_PARAMETERS RestoreParameters; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + UNREFERENCED_PARAMETER(PreviousPowerState); + + // + // Perform all the steps necessary to start the device. + // + + // + // If restore context is FALSE, then this is initial transition into D0 + // power state for this controller. In such case, disable any interrupts + // that may have been left enabled (e.g. perhaps by FW, previous D0 -> Dx + // transition etc.) Otherwise, such interrupts could trigger an interrupt + // storm if they were to assert without any driver being registered to + // handle such interrupts. + // + // If restore context is TRUE, then this is a transition into D0 power + // state from a lower power Dx state. In such case, restore the context + // that was present before the controller transitioned into the lower + // power state. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + if (RestoreContext == FALSE) { + for (BankId = 0; BankId < SIM_GPIO_TOTAL_BANKS; BankId += 1) { + GpioBank = &GpioContext->Banks[BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Disable all interrupts on this bank by clearing the enable + // register. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + if (PinValue > 0) { + PinValue = 0; + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, + PinValue); + } + } + + } else { + + // + // Restoring the controller state involves restoring the state of + // each SimGPIO bank. + // + + for (BankId = 0; BankId < SIM_GPIO_TOTAL_BANKS; BankId += 1) { + RestoreParameters.BankId = BankId; + RestoreParameters.State = PreviousPowerState; + SimGpioRestoreBankHardwareContext(Context, &RestoreParameters); + } + } + + return STATUS_SUCCESS; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioStopController ( + _In_ PVOID Context, + _In_ BOOLEAN SaveContext, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) + +/*++ + +Routine Description: + + This routine stops the GPIO controller. This routine is responsible for + resetting all the pins to their default modes. + + N.B. This function is not marked pageable because this function is in + the device power down path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + SaveContext - Supplies a flag that indicates whether the client driver + should save the GPIO controller state or not. The state may need + to be restored when the controller is restarted. + + TargetState - Supplies the device power state which the device will be put + in once the callback is complete. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + + BANK_ID BankId; + PSIM_GPIO_CONTEXT GpioContext; + GPIO_SAVE_RESTORE_BANK_HARDWARE_CONTEXT_PARAMETERS SaveParameters; + + UNREFERENCED_PARAMETER(TargetState); + + // + // Perform all the steps necessary to stop the device. + // + + // + // If save context is FALSE, then this is a final transition into D3/off + // power state. Hence saving of context is not necessary. + // + // If save context is TRUE, then this is a transition into a lower power + // Dx state. In such case, save the context as it will need to be + // restored when the device is brought back to D0 (i.e. ON) power state. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + if (SaveContext == TRUE) { + for (BankId = 0; BankId < SIM_GPIO_TOTAL_BANKS; BankId += 1) { + SaveParameters.BankId = BankId; + SaveParameters.State = TargetState; + SimGpioSaveBankHardwareContext(Context, &SaveParameters); + } + } + + return STATUS_SUCCESS; +} + +// +// --------------------------------------------------------- Interrupt Handlers +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioEnableInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_ENABLE_INTERRUPT_PARAMETERS EnableParameters + ) + +/*++ + +Routine Description: + + This routine configures the supplied pin for interrupt. + + N.B. This routine is called from within a regular thread context (i.e., + non-interrupt context) by the class extension. Thus the interrupt lock + needs to be explicitly acquired for memory-mapped GPIO controllers + prior to manipulating any device state that is also affected from a + routine called within the interrupt context. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + EnableParameters - Supplies a pointer to a structure containing enable + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be enabled. The pin + number is relative to the bank. + + Flags - Supplies flags controlling the enable operation. Currently + no flags are defined. + + InterruptMode - Supplies the trigger mode (edge or level) configured for + this interrupt when it was enabled. + + Polarity - Supplies the polarity (active low or active high) configured + for this interrupt when it was enabled. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + VendorData - Supplies an optional pointer to a buffer containing the + vendor data supplied in the GPIO descriptor. This field will be + NULL if no vendor data was supplied. This buffer is read-only. + + VendorDataLength - Supplies the length of the vendor data buffer. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + ULONG PinValue; + PIN_NUMBER ShiftBits; + PSIM_GPIO_REGISTERS SimGpioRegisters; + NTSTATUS Status; + + // + // If the polarity is not supported, then bail out. Note the interrupt + // polarity cannot be InterruptActiveBoth as this sample uses ActiveBoth + // emulation. + // + + if ((EnableParameters->Polarity != InterruptActiveHigh) && + (EnableParameters->Polarity != InterruptActiveLow)) { + + Status = STATUS_NOT_SUPPORTED; + goto EnableInterruptEnd; + } + + BankId = EnableParameters->BankId; + PinNumber = EnableParameters->PinNumber; + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + SimGpioRegisters = GpioBank->Registers; + Status = STATUS_SUCCESS; + + // + // The interrupt enable register/bitmap may be manipulated from within the + // interrupt context. Hence updates to it must be synchronized using the + // interrupt lock. + // + + GPIO_CLX_AcquireInterruptLock(Context, BankId); + + // + // Set the mode register. If the interrupt is Level then set the bit; + // otherwise, clear it (edge-triggered). + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->ModeRegister); + if (EnableParameters->InterruptMode == LevelSensitive) { + PinValue |= (1 << PinNumber); + } else { + PinValue &= ~(1 << PinNumber); + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->ModeRegister, PinValue); + + // + // Set the polarity register. There are two bits for each pin. If the + // interrupt is ActiveHigh (or Rising-edge) then set it to 0x1. + // + + Index = PinNumber / 16; + ShiftBits = (PinNumber % 16) * 2; + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[Index]); + switch (EnableParameters->Polarity) { + + case InterruptActiveHigh: + PinValue &= ~(0x3 << ShiftBits); + PinValue |= (1 << ShiftBits); + break; + + case InterruptActiveLow: + PinValue &= ~(0x3 << ShiftBits); + break; + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[Index], PinValue); + + // + // NOTE: If the GPIO controller supports a separate set of mask registers, + // then any stale value must be cleared here. SimGPIO controller + // doesn't and hence this step is skipped here. + // + + // + // Clear the corresponding status bit first to ignore any stale value. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->StatusRegister); + PinValue &= ~(1 << PinNumber); + WRITE_REGISTER_ULONG(&SimGpioRegisters->StatusRegister, PinValue); + + // + // Enable the interrupt by setting the bit in the interrupt enable register. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + PinValue |= (1 << PinNumber); + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, PinValue); + + // + // Release the interrupt lock. + // + + GPIO_CLX_ReleaseInterruptLock(Context, BankId); + + Status = STATUS_SUCCESS; + +EnableInterruptEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioDisableInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_DISABLE_INTERRUPT_PARAMETERS DisableParameters + ) + +/*++ + +Routine Description: + + This routine disables the supplied pin from interrupting. + + This routine is not marked PAGED as it may be called before/after + the boot device is in D0/D3 if boot device has GPIO dependencies. + + N.B. This routine is called from within a regular thread context (i.e., + non-interrupt context) by the class extension. Thus the interrupt lock + needs to be explicitly acquired prior to manipulating the device state. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + DisableParameters - Supplies a pointer to a structure supplying the + parameters for disabling the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be disabled. The pin + number is relative to the bank. + + Flags - Supplies flags controlling the disable operation. Currently + no flags are defined. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + BankId = DisableParameters->BankId; + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // The interrupt enable register may be manipulated from within the + // interrupt context. Hence updates to it must be synchronized using the + // interrupt lock. + // + + GPIO_CLX_AcquireInterruptLock(Context, BankId); + + // + // Disable the interrupt by clearing the bit in the interrupt enable + // register. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + PinValue &= ~(1 << DisableParameters->PinNumber); + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, PinValue); + + GPIO_CLX_ReleaseInterruptLock(Context, BankId); + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioMaskInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_MASK_INTERRUPT_PARAMETERS MaskParameters + ) + +/*++ + +Routine Description: + + This routine invokes masks the supplied pin from interrupting. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + MaskParameters - Supplies a pointer to a structure containing mask + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinMask - Supplies a bitmask of pins which should be masked. If a pin + should be masked, then the corresponding bit is set in the bitmask. + + FailedMask - Supplies a bitmask of pins that failed to be masked. If + a pin could not be masked, the bit should be set in this field. + + N.B. This should only be done if for non memory-mapped controllers. + Memory-mapped controllers are never expected to fail this + operation. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[MaskParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Mask is essentially same as disable for SimGPIO controller. The + // difference between the routines is that mask callback is called at DIRQL + // and automatically synchronized with other DIRQL interrupts callbacks. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + PinValue &= ~(ULONG)(MaskParameters->PinMask); + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, PinValue); + + // + // Set the bitmask of pins that could not be successfully masked. + // Since this is a memory-mapped controller, the mask operation always + // succeeds. + // + + MaskParameters->FailedMask = 0x0; + return STATUS_SUCCESS; +} + +NTSTATUS +SimGpioUnmaskInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_ENABLE_INTERRUPT_PARAMETERS UnmaskParameters + ) + +/*++ + +Routine Description: + + This routine invokes unmasks the supplied interrupt pin. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + UnmaskParameters - Supplies a pointer to a structure containing parameters + for unmasking the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be unmasked. The pin + number is relative to the bank. + + InterruptMode - Supplies the trigger mode (edge or level) configured for + this interrupt when it was enabled. + + Polarity - Supplies the polarity (active low or active high) configured + for this interrupt when it was enabled. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + VendorData - NULL. + + VendorDataLength - 0. + + N.B. The VendorData and VendorDataLength are not supplied for unmask + operation (i.e., both fields are zero). + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[UnmaskParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Unmask is same as enable on this GPIO controller. The difference between + // this routine and the enable routine is that unmask callback is called at + // DIRQL and automatically synchronized with other DIRQL-level callbacks. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + PinValue |= (1 << UnmaskParameters->PinNumber); + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, PinValue); + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioQueryActiveInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_QUERY_ACTIVE_INTERRUPTS_PARAMETERS QueryActiveParameters + ) + +/*++ + +Routine Description: + + This routine returns the current set of active interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + QueryActiveParameters - Supplies a pointer to a structure containing query + parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + EnabledMask - Supplies a bitmask of pins enabled for interrupts + on the specified GPIO bank. + + ActiveMask - Supplies a bitmask that receives the active interrupt + mask. If a pin is interrupting and set in EnabledMask, then the + corresponding bit is set in the bitmask. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[QueryActiveParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // NOTE: As SimGPIO is not a real hardware device, no interrupt will ever + // fire. Thus the status register value will never change. To pretend + // as if a real interrupt happened, it marks all currently enabled + // interrupts as asserting. Copy the enable interrupt value into + // the status register. + // + // This should NOT be done for a real GPIO controller. + // + + // + // BEGIN: SIMGPIO HACK. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + WRITE_REGISTER_ULONG(&SimGpioRegisters->StatusRegister, PinValue); + + // + // END: SIMGPIO HACK. + // + + // + // Return the current value of the interrupt status register into the + // ActiveMask parameter. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->StatusRegister); + QueryActiveParameters->ActiveMask = (ULONG64)PinValue; + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioQueryEnabledInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_QUERY_ENABLED_INTERRUPTS_PARAMETERS QueryEnabledParameters + ) + +/*++ + +Routine Description: + + This routine returns the current set of enabled interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + QueryEnabledParameters - Supplies a pointer to a structure containing query + parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + EnabledMask - Supplies a bitmask that receives the enabled interrupt + mask. If a pin is enabled, then the corresponding bit is set in the + mask. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called with the + interrupt lock acquired by the class extension, but not always + from within the interrupt context. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[QueryEnabledParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Return the current value of the interrupt enable register into the + // EnabledMask parameter. It is strongly preferred that the true state of + // the hardware is returned, rather than a software-cached variable, since + // CLIENT_QueryEnabledInterrupts is used by the class extension to detect + // interrupt storms. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + QueryEnabledParameters->EnabledMask = (ULONG64)PinValue; + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioClearActiveInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_CLEAR_ACTIVE_INTERRUPTS_PARAMETERS ClearParameters + ) + +/*++ + +Routine Description: + + This routine clears the GPIO controller's active set of interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ClearParameters - Supplies a pointer to a structure containing clear + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + ClearActiveMask - Supplies a mask of pins which should be marked as + inactive. If a pin should be cleared, then the corresponding bit is + set in the mask. + + FailedMask - Supplies a bitmask of pins that failed to be cleared. If + a pin could not be cleared, the bit should be set in this field. + + N.B. This should only be done if for non memory-mapped controllers. + Memory-mapped controllers are never expected to fail this + operation. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ClearParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Clear the bits that are set in the ClearActiveMask parameter. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->StatusRegister); + PinValue &= ~((ULONG)ClearParameters->ClearActiveMask); + WRITE_REGISTER_ULONG(&SimGpioRegisters->StatusRegister, PinValue); + + // + // Set the bitmask of pins that could not be successfully cleared. + // Since this is a memory-mapped controller, the clear operation always + // succeeds. + // + + ClearParameters->FailedClearMask = 0x0; + return STATUS_SUCCESS; +} + +NTSTATUS +SimGpioReconfigureInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_RECONFIGURE_INTERRUPTS_PARAMETERS ReconfigureParameters + ) + +/*++ + +Routine Description: + + This routine reconfigures the interrupt in the specified mode. + + N.B. This routine is called with the interrupt lock acquired by the + class extension. Hence the lock is not re-acquired here. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ReconfigureParameters - Supplies a pointer to a structure containing + parameters for reconfiguring the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be reconfigured. + The pin number is relative to the bank. + + InterruptMode - Supplies the trigger mode (edge or level) for the new + configuration. + + Polarity - Supplies the polarity (active low or active high) for the + new configuration. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + ULONG PinValue; + PIN_NUMBER ShiftBits; + PSIM_GPIO_REGISTERS SimGpioRegisters; + NTSTATUS Status; + + BankId = ReconfigureParameters->BankId; + PinNumber = ReconfigureParameters->PinNumber; + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + SimGpioRegisters = GpioBank->Registers; + Status = STATUS_SUCCESS; + + // + // Clear any stale status bits from the previous configuration. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->StatusRegister); + PinValue &= ~(1 << PinNumber); + WRITE_REGISTER_ULONG(&SimGpioRegisters->StatusRegister, PinValue); + + // + // Set the mode register. If the interrupt is Level then set the bit; + // otherwise, clear it (edge-triggered). + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->ModeRegister); + if (ReconfigureParameters->InterruptMode == LevelSensitive) { + PinValue |= (1 << PinNumber); + } else { + PinValue &= ~(1 << PinNumber); + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->ModeRegister, PinValue); + + // + // Set the polarity register. There are two bits for each pin. If the + // interrupt is ActiveHigh (or Rising-edge) then set it to 0x1. + // + + Index = PinNumber / 16; + ShiftBits = (PinNumber % 16) * 2; + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[Index]); + switch (ReconfigureParameters->Polarity) { + + case InterruptActiveHigh: + PinValue &= ~(0x3 << ShiftBits); + PinValue |= (1 << ShiftBits); + break; + + case InterruptActiveLow: + PinValue &= ~(0x3 << ShiftBits); + break; + + default: + + NT_ASSERT(FALSE); + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[Index], PinValue); + return STATUS_SUCCESS; +} + +// +// --------------------------------------------------------------- I/O Handlers +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioConnectIoPins ( + _In_ PVOID Context, + _In_ PGPIO_CONNECT_IO_PINS_PARAMETERS ConnectParameters + ) + +/*++ + +Routine Description: + + This routine invokes connects the specified pins for IO. The pins can + be read from if connected for input, or written to if connected for + output. + + N.B. This routine is called at PASSIVE_LEVEL but is not marked as + PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ConnectParameters - Supplies a pointer to a structure supplying the + parameters for connecting the IO pins. Fields description: + + BankId - Supplies the ID for the GPIO bank. + + PinNumberTable - Supplies an array of pins to be connected for IO. The + pin numbers are 0-based and relative to the GPIO bank. + + PinCount - Supplies the number of pins in the pin number table. + + ConnectMode - Supplies the mode in which the pins should be configured + (viz. input or output). + + ConnectFlags - Supplies the flags controlling the IO setup. Currently + no flags are defined. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + DriveStrength - Supplies the drive strength to be applied. The value + is in 100th of mA (i.e., 1.21mA will be supplied as 121mA). + + VendorData - Supplies an optional pointer to a buffer containing the + vendor data supplied in the GPIO descriptor. This field will be + NULL if no vendor data was supplied. This buffer is read-only. + + VendorDataLength - Supplies the length of the vendor data buffer. + + ConnectFlags - Supplies the flag to be used for connect operation. + Currently no flags are defined. + +Return Value: + + NT status code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + PPIN_NUMBER PinNumberTable; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ConnectParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + Status = STATUS_SUCCESS; + + // + // Read the current direction register value. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->DirectionRegister); + + // + // Walk through all the supplied pins and connect them in the specified + // mode (input or output). + // + + PinNumberTable = ConnectParameters->PinNumberTable; + for (Index = 0; Index < ConnectParameters->PinCount; Index += 1) { + PinNumber = PinNumberTable[Index]; + + // + // If the pins are being connected for input, then set the bit. + // Otherwise clear the bit. + // + + if (ConnectParameters->ConnectMode == ConnectModeInput) { + PinValue |= (1 << PinNumber); + + } else if (ConnectParameters->ConnectMode == ConnectModeOutput) { + PinValue &= ~(1 << PinNumber); + } + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->DirectionRegister, PinValue); + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioDisconnectIoPins ( + _In_ PVOID Context, + _In_ PGPIO_DISCONNECT_IO_PINS_PARAMETERS DisconnectParameters + ) + +/*++ + +Routine Description: + + This routine invokes disconnects the specified IO pins. The pins are + put back in their original mode. + + N.B. This routine is called at PASSIVE_LEVEL but is not marked as + PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + DisconnectParameters - Supplies a pointer to a structure containing + disconnect operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumberTable - Supplies an array of pins to be disconnected. The pin + numbers are relative to the GPIO bank. + + PinCount - Supplies the number of pins in the pin number table. + + DisconnectMode - Supplies the mode in which the pins are currently + configured (viz. input or output). + + DisconnectFlags - Supplies the flags controlling the IO setup. Currently + no flags are defined. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + PPIN_NUMBER PinNumberTable; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + // + // If the pin configuration should be preserved post disconnect, then + // there is nothing left to do. + // + + if (DisconnectParameters->DisconnectFlags.PreserveConfiguration == 1) { + return STATUS_SUCCESS; + } + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[DisconnectParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Read the current direction register value. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->DirectionRegister); + + // + // Walk through all the supplied pins and disconnect them. On SimGPIO + // controller, all pins are reset to the default mode (output). + // + + PinNumberTable = DisconnectParameters->PinNumberTable; + for (Index = 0; Index < DisconnectParameters->PinCount; Index += 1) { + PinNumber = PinNumberTable[Index]; + PinValue &= ~(1 << PinNumber); + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->DirectionRegister, PinValue); + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +NTSTATUS +SimGpioReadGpioPins ( + _In_ PVOID Context, + _In_ PGPIO_READ_PINS_MASK_PARAMETERS ReadParameters + ) + +/*++ + +Routine Description: + + This routine reads the current values for all the pins. + + As the FormatIoRequestsAsMasks bit was set inside + SimGpioQueryControllerInformation(), all this routine needs to do is read + the level register value and return to the GPIO class extension. It will + return the right set of bits to the caller. + + N.B. This routine is called at DIRQL for memory-mapped GPIOs and thus not + marked as PAGED. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ReadParameters - Supplies a pointer to a structure containing read + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinValues - Supplies a pointer to a variable that receives the current + pin values. + + Flags - Supplies the flag to be used for read operation. Currently + defined flags are: + + WriteConfiguredPins: If set, the read is being done on a set of + pin that were configured for write. In such cases, the + GPIO client driver is expected to read and return the + output register value. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; + PASSIVE_LEVEL if the controller is behind some serial-bus. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable) + and IO callbacks (connect/disconnect). + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ReadParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Read the current level register value. Note the GPIO class may invoke + // the read routine on write-configured pins. In such case the output + // register values should be read. + // + // N.B. In case of SimGPIO, the LevelRegister holds the value for input + // as well as output pins. Thus the same register is read in either + // case. + // + + if (ReadParameters->Flags.WriteConfiguredPins == FALSE) { + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->LevelRegister); + + } else { + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->LevelRegister); + } + + *ReadParameters->PinValues = PinValue; + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +NTSTATUS +SimGpioWriteGpioPins ( + _In_ PVOID Context, + _In_ PGPIO_WRITE_PINS_MASK_PARAMETERS WriteParameters + ) + +/*++ + +Routine Description: + + This routine sets the current values for the specified pins. This call is + synchronized with the write and connect/disconnect IO calls. + + N.B. This routine is called at DIRQL for memory-mapped GPIOs and thus not + marked as PAGED. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + WriteParameters - Supplies a pointer to a structure containing write + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + SetMask - Supplies a mask of pins which should be set (0x1). If a pin + should be set, then the corresponding bit is set in the mask. + All bits that are clear in the mask should be left intact. + + ClearMask - Supplies a mask of pins which should be cleared (0x0). If + a pin should be cleared, then the bit is set in the bitmask. All + bits that are clear in the mask should be left intact. + + Flags - Supplies the flag controlling the write operation. Currently + no flags are defined. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; + PASSIVE_LEVEL if the controller is behind some serial-bus. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable) + and IO callbacks (connect/disconnect). + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[WriteParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Read the current level register value. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->LevelRegister); + + // + // Set the bits specified in the set mask and clear the ones specified + // in the clear mask. + // + + PinValue |= WriteParameters->SetMask; + PinValue &= ~WriteParameters->ClearMask; + + // + // Write the updated value to the register. + // + + WRITE_REGISTER_ULONG(&SimGpioRegisters->LevelRegister, PinValue); + + return STATUS_SUCCESS; +} + +// +// ------------------------------------------------------- Power mgmt handlers +// + +VOID +SimGpioSaveBankHardwareContext ( + _In_ PVOID Context, + _In_ PGPIO_SAVE_RESTORE_BANK_HARDWARE_CONTEXT_PARAMETERS SaveParameters + ) + +/*++ + +Routine Description: + + This routine saves the hardware context for the GPIO controller. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + SaveRestoreParameters - Supplies a pointer to a structure containing + parameters for the save operation: + + BankId - Supplies the ID for the GPIO bank. + + State - Target F-state the bank will be transitioned into. + + Flags - Supplies flags for the save operation: + CriticalTransition - TRUE if this is due to a critical transition. + +Return Value: + + None. + +--*/ + +{ + + PULONG DestinationAddress; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + ULONG RegisterCount; + PSIM_GPIO_REGISTERS SimGpioRegisters; + PULONG SourceAddress; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[SaveParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Copy the contents of the registers into memory. + // + + SourceAddress = &SimGpioRegisters->ModeRegister; + DestinationAddress = &GpioBank->SavedContext.ModeRegister; + RegisterCount = sizeof(SIM_GPIO_REGISTERS) / sizeof(ULONG); + for (Index = 0; Index < RegisterCount; Index += 1) { + *DestinationAddress = READ_REGISTER_ULONG(SourceAddress); + SourceAddress += 1; + DestinationAddress += 1; + } + + return; +} + +VOID +SimGpioRestoreBankHardwareContext ( + _In_ PVOID Context, + _In_ PGPIO_SAVE_RESTORE_BANK_HARDWARE_CONTEXT_PARAMETERS RestoreParameters + ) + +/*++ + +Routine Description: + + This routine saves the hardware context for the GPIO controller. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + SaveRestoreParameters - Supplies a pointer to a structure containing + parameters for the restore operation: + + BankId - Supplies the ID for the GPIO bank. + + State - Target F-state the bank will be transitioned into. + + Flags - Supplies flags for the save operation: + CriticalTransition - TRUE if this is due to a critical transition. + +Return Value: + + None. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[RestoreParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Restore the level register. + // + + PinValue = GpioBank->SavedContext.LevelRegister; + WRITE_REGISTER_ULONG(&SimGpioRegisters->LevelRegister, PinValue); + + // + // Restore the mode, polarity and enable registers. + // + + PinValue = GpioBank->SavedContext.ModeRegister; + WRITE_REGISTER_ULONG(&SimGpioRegisters->ModeRegister, PinValue); + + PinValue = GpioBank->SavedContext.PolarityRegister[0]; + WRITE_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[0], PinValue); + + PinValue = GpioBank->SavedContext.PolarityRegister[1]; + WRITE_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[1], PinValue); + + PinValue = GpioBank->SavedContext.StatusRegister; + WRITE_REGISTER_ULONG(&SimGpioRegisters->StatusRegister, PinValue); + + // + // Restore the direction register. + // + + PinValue = GpioBank->SavedContext.DirectionRegister; + WRITE_REGISTER_ULONG(&SimGpioRegisters->DirectionRegister, PinValue); + + // + // Take care to restore the enable register only after restoring the + // mode and polarity registers. Otherwise, the interrupt line will get + // sampled when the enable register gets written to with the mode and + // polarity at that point in time (and could cause a spurious interrupt). + // + + PinValue = GpioBank->SavedContext.EnableRegister; + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, PinValue); + + return; +} + +__pragma(warning(default: 4127)) // conditional expression is a constant + + diff --git a/gpio/samples/simgpio/simgpio.inx b/gpio/samples/simgpio/simgpio.inx new file mode 100644 index 00000000..0a00ec79 Binary files /dev/null and b/gpio/samples/simgpio/simgpio.inx differ diff --git a/gpio/samples/simgpio/simgpio.rc b/gpio/samples/simgpio/simgpio.rc new file mode 100644 index 00000000..1573b89c --- /dev/null +++ b/gpio/samples/simgpio/simgpio.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Simulated GPIO Client" +#define VER_INTERNALNAME_STR "simgpio.sys" +#define VER_ORIGINALFILENAME_STR "simgpio.sys" + +#include "common.ver" + diff --git a/gpio/samples/simgpio/simgpio.vcxproj b/gpio/samples/simgpio/simgpio.vcxproj new file mode 100644 index 00000000..f9e47196 --- /dev/null +++ b/gpio/samples/simgpio/simgpio.vcxproj @@ -0,0 +1,161 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714} + $(MSBuildProjectName) + 1 + Debug + Win32 + {1C686E11-FC59-4029-AFA6-6083113CBA26} + + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + * + true + $(InfArch) + true + .\$(IntDir)\simgpio.inf + + + + simgpio + + + simgpio + + + simgpio + + + simgpio + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib + + + true + Level4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gpio/samples/simgpio/simgpio.vcxproj.Filters b/gpio/samples/simgpio/simgpio.vcxproj.Filters new file mode 100644 index 00000000..21ab66e9 --- /dev/null +++ b/gpio/samples/simgpio/simgpio.vcxproj.Filters @@ -0,0 +1,39 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {BFF3D918-3BBB-4AF1-B1A2-C2A4FD8E82E8} + + + h;hpp;hxx;hm;inl;inc;xsd + {3BC12FA6-2491-4DA4-86C0-8F10AA4DB124} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {004102F6-6CB6-4E30-A1D8-B663D4B8CE08} + + + inf;inv;inx;mof;mc; + {38B34A48-263B-481A-81DE-81BF490980CD} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.c b/gpio/samples/simgpio_i2c/simgpio_i2c.c new file mode 100644 index 00000000..9cdaee69 --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.c @@ -0,0 +1,2692 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + simgpio_i2c.c + +Abstract: + + This sample implements a GPIO client driver for simulated GPIO (SimGpio) + controller. + + Note: DIRQL in the comments below refers to device IRQL, which is any + IRQL > DISPATCH_LEVEL (and less than some IRQL reserved for OS use). + + +Environment: + + Kernel mode + +--*/ + +// +// The SimGpio_I2C controller has 2 GPIO banks, with banks consisting of +// 8 pins each. Any pin can be configured for interrupt, input or output. +// Each bank has the the following registers. There are two sets of registers: +// the first set for pins 0...7 and second for pins 8...15. +// +// Register | Address | Offset | Size +// ---------------------------------------------------------------- +// +// (Register set for pins 0...7) +// +// ModeRegister1 | 0x0 | 0x0 | 1 byte +// PolarityRegister1 | 0x1 | 0x1 | 1 byte +// EnableRegister1 | 0x2 | 0x2 | 1 byte +// StatusRegister1 | 0x3 | 0x3 | 1 byte +// DirectionRegister1 | 0x4 | 0x4 | 1 byte +// LevelRegister1 | 0x5 | 0x5 | 1 byte +// +// (Register set for pins 8...15) +// +// ModeRegister2 | 0x6 | 0x6 | 1 byte +// PolarityRegister2 | 0x7 | 0x7 | 1 byte +// EnableRegister2 | 0x8 | 0x8 | 1 byte +// StatusRegister2 | 0x9 | 0x9 | 1 byte +// DirectionRegister2 | 0xA | 0xA | 1 byte +// LevelRegister2 | 0xB | 0xB | 1 byte +// +// Mode Register - Interrupt mode (Level = 0x1 or Edge = 0x0) for pins +// enabled for interrupts. +// +// Polarity Register - ActiveLow (0x0) or ActiveHigh (0x1) for Level-triggered +// interrupts; Falling-edge (0x0), Rising-edge (0x1). Note this controller +// does not support ActiveBoth. +// +// Enable Register - A mask of which pins are enabled for interrupt. A bit +// is set if the pin is enabled for interrupt. +// +// Status Register - A register containing which pins are actively interrupting. +// If any of the bit is set, then the GPIO controller's interrupt fires. +// +// Direction Register - A register which indicates whether a given pin is +// configured for input (0x1) or output (0x0). +// +// LevelRegister - A bitmask which indicates holds the value for a given pin +// (i.e. whether the pin is high (0x1) or low (0x0)). +// + +// +// ------------------------------------------------------------------- Includes +// + +#include +#include +#include +#include "simgpio_i2c.h" +#include "trace.h" +#include "simgpio_i2c.tmh" + +// +// -------------------------------------------------------------------- Defines +// + +// +// Determine whether the given pin is reserved or not. Currently no pins are +// reserved on the simulated GPIO controller. +// + +__pragma(warning(disable: 4127)) // conditional expression is a constant + +// +// ----------------------------------------------------------------- Prototypes +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD SimGpioEvtDeviceAdd; +EVT_WDF_DRIVER_UNLOAD SimGpioEvtDriverUnload; + +// +// General interfaces. +// + +GPIO_CLIENT_PREPARE_CONTROLLER SimGpioPrepareController; +GPIO_CLIENT_RELEASE_CONTROLLER SimGpioReleaseController; +GPIO_CLIENT_QUERY_CONTROLLER_BASIC_INFORMATION + SimGpioQueryControllerBasicInformation; + +GPIO_CLIENT_START_CONTROLLER SimGpioStartController; +GPIO_CLIENT_STOP_CONTROLLER SimGpioStopController; + +// +// Interrupt enable, disable, mask and unmask handlers. +// + +GPIO_CLIENT_ENABLE_INTERRUPT SimGpioEnableInterrupt; +GPIO_CLIENT_DISABLE_INTERRUPT SimGpioDisableInterrupt; +GPIO_CLIENT_MASK_INTERRUPTS SimGpioMaskInterrupts; +GPIO_CLIENT_UNMASK_INTERRUPT SimGpioUnmaskInterrupt; +GPIO_CLIENT_RECONFIGURE_INTERRUPT SimGpioReconfigureInterrupt; + +// +// Handlers to query active/enabled interrupts and clear active interrupts. +// + +GPIO_CLIENT_QUERY_ACTIVE_INTERRUPTS SimGpioQueryActiveInterrupts; +GPIO_CLIENT_CLEAR_ACTIVE_INTERRUPTS SimGpioClearActiveInterrupts; +GPIO_CLIENT_QUERY_ENABLED_INTERRUPTS SimGpioQueryEnabledInterrupts; + +// +// Handlers for GPIO I/O operations. +// + +GPIO_CLIENT_CONNECT_IO_PINS SimGpioConnectIoPins; +GPIO_CLIENT_DISCONNECT_IO_PINS SimGpioDisconnectIoPins; +GPIO_CLIENT_READ_PINS_MASK SimGpioReadGpioPins; +GPIO_CLIENT_WRITE_PINS_MASK SimGpioWriteGpioPins; + +// +// -------------------------------------------------------------------- Pragmas +// + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, SimGpioEvtDeviceAdd) +#pragma alloc_text(PAGE, SimGpioEvtDriverUnload) + +// +// ------------------------------------------------------------------ Functions +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is the driver initialization entry point. + +Arguments: + + DriverObject - Pointer to the driver object created by the I/O manager. + + RegistryPath - Pointer to the driver specific registry key. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDFDRIVER Driver; + WDF_DRIVER_CONFIG DriverConfig; + GPIO_CLIENT_REGISTRATION_PACKET RegistrationPacket; + NTSTATUS Status; + + WPP_INIT_TRACING(DriverObject, RegistryPath); + + // + // Initialize the driver configuration structure. + // + + WDF_DRIVER_CONFIG_INIT(&DriverConfig, SimGpioEvtDeviceAdd); + DriverConfig.EvtDriverUnload = SimGpioEvtDriverUnload; + + // + // Create a framework driver object to represent our driver. + // + + Status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &DriverConfig, + &Driver); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: WdfDriverCreate failed status:%#x\n", + __FUNCTION__, + Status); + + WPP_CLEANUP(DriverObject); + goto DriverEntryEnd; + } + + // + // Initialize the client driver registration packet. + // + + RtlZeroMemory(&RegistrationPacket, sizeof(GPIO_CLIENT_REGISTRATION_PACKET)); + RegistrationPacket.Version = GPIO_CLIENT_VERSION; + RegistrationPacket.Size = sizeof(GPIO_CLIENT_REGISTRATION_PACKET); + + // + // Initialize the device context size. + // + + RegistrationPacket.ControllerContextSize = sizeof(SIM_GPIO_CONTEXT); + + // + // General interfaces. + // + + RegistrationPacket.CLIENT_PrepareController = SimGpioPrepareController; + RegistrationPacket.CLIENT_QueryControllerBasicInformation = + SimGpioQueryControllerBasicInformation; + + RegistrationPacket.CLIENT_StartController = SimGpioStartController; + RegistrationPacket.CLIENT_StopController = SimGpioStopController; + RegistrationPacket.CLIENT_ReleaseController = SimGpioReleaseController; + + // + // Interrupt enable and disable handlers. + // + + RegistrationPacket.CLIENT_DisableInterrupt = SimGpioDisableInterrupt; + RegistrationPacket.CLIENT_EnableInterrupt = SimGpioEnableInterrupt; + + // + // Interrupt mask, unmask and reconfigure interrupt handlers. + // + + RegistrationPacket.CLIENT_MaskInterrupts = SimGpioMaskInterrupts; + RegistrationPacket.CLIENT_UnmaskInterrupt = SimGpioUnmaskInterrupt; + RegistrationPacket.CLIENT_ReconfigureInterrupt = SimGpioReconfigureInterrupt; + + // + // Handlers to query active/enabled interrupts and clear active interrupts. + // + + RegistrationPacket.CLIENT_ClearActiveInterrupts = SimGpioClearActiveInterrupts; + RegistrationPacket.CLIENT_QueryActiveInterrupts = SimGpioQueryActiveInterrupts; + RegistrationPacket.CLIENT_QueryEnabledInterrupts = SimGpioQueryEnabledInterrupts; + + // + // Handlers for GPIO I/O operations. + // + + RegistrationPacket.CLIENT_ConnectIoPins = SimGpioConnectIoPins; + RegistrationPacket.CLIENT_DisconnectIoPins = SimGpioDisconnectIoPins; + RegistrationPacket.CLIENT_ReadGpioPinsUsingMask = SimGpioReadGpioPins; + RegistrationPacket.CLIENT_WriteGpioPinsUsingMask = SimGpioWriteGpioPins; + + // + // Register the SimGPIO client driver with the GPIO class extension. + // + + Status = GPIO_CLX_RegisterClient(Driver, &RegistrationPacket, RegistryPath); + +DriverEntryEnd: + return Status; +} + +NTSTATUS +SimGpioEvtDeviceAdd ( + _In_ WDFDRIVER Driver, + _Inout_ PWDFDEVICE_INIT DeviceInit + ) + +/*++ + +Routine Description: + + This routine is the AddDevice entry point for the client driver. This + routine is called by the framework in response to AddDevice call from the + PnP manager. It will create and initialize the device object to represent + a new instance of the simulated GPIO controller. + +Arguments: + + Driver - Supplies a handle to the driver object created in DriverEntry. + + DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT + structure. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDFDEVICE Device; + WDF_OBJECT_ATTRIBUTES FdoAttributes; + NTSTATUS Status; + + PAGED_CODE(); + + // + // Call the GPIO class extension's pre-device create interface. + // + + Status = GPIO_CLX_ProcessAddDevicePreDeviceCreate(Driver, + DeviceInit, + &FdoAttributes); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: GPIO_CLX_ProcessAddDevicePreDeviceCreate() failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto EvtDeviceAddEnd; + } + + // + // Call the framework to create the device and attach it to the lower stack. + // + + Status = WdfDeviceCreate(&DeviceInit, &FdoAttributes, &Device); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: WdfDeviceCreate() failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EvtDeviceAddEnd; + } + + // + // Call the GPIO class extension's post-device create interface. + // + + Status = GPIO_CLX_ProcessAddDevicePostDeviceCreate(Driver, Device); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: GPIO_CLX_ProcessAddDevicePostDeviceCreate() failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto EvtDeviceAddEnd; + } + +EvtDeviceAddEnd: + return Status; +} + +// +// ---------------------------------------------------------- General intefaces +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioPrepareController ( + _In_ WDFDEVICE Device, + _In_ PVOID Context, + _In_ WDFCMRESLIST ResourcesRaw, + _In_ WDFCMRESLIST ResourcesTranslated + ) + +/*++ + +Routine Description: + + This routine is called by the GPIO class extension to prepare the + simulated GPIO controller for use. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ResourcesRaw - Supplies a handle to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + ResourcesTranslated - Supplies a handle to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + UCHAR ConnectionClass; + UCHAR ConnectionType; + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + BOOLEAN I2ResourceFound; + ULONG InterruptResourceCount; + ULONG ResourceCount; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(ResourcesRaw); + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + RtlZeroMemory(GpioContext, sizeof(SIM_GPIO_CONTEXT)); + GpioContext->TotalPins = SIM_GPIO_TOTAL_PINS; + + // + // Walk through the resource list and map all the resources. Atleast one + // memory resource and one interrupt resource is expected. The resources + // are described in the ACPI namespace. + // + + I2ResourceFound = FALSE; + InterruptResourceCount = 0; + ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated); + Status = STATUS_SUCCESS; + for (Index = 0; Index < ResourceCount; Index += 1) { + Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index); + switch(Descriptor->Type) { + + // + // Look for a connection resource that supplies the connection ID + // required to open a handle to the I2C controller. + // + + case CmResourceTypeConnection: + ConnectionClass = Descriptor->u.Connection.Class; + ConnectionType = Descriptor->u.Connection.Type; + if ((ConnectionClass == CM_RESOURCE_CONNECTION_CLASS_SERIAL) && + (ConnectionType == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C) && + (I2ResourceFound == FALSE)) { + + GpioContext->SpbConnectionId.LowPart = + Descriptor->u.Connection.IdLowPart; + + GpioContext->SpbConnectionId.HighPart = + Descriptor->u.Connection.IdHighPart; + + I2ResourceFound = TRUE; + } + + break; + + // + // Interrupt resource which supplies the GPIO controller interrupt + // (that connects to the GIC). + // + // N.B. Connecting of the interrupt is handled by the GPIO class + // extension. Only ensure that appropriate number of interrupts + // were described. + // + + case CmResourceTypeInterrupt: + InterruptResourceCount += 1; + break; + + // + // This could be device-private type added by the underlying bus + // driver. Do not filter or alter this resource information. + // + + default: + break; + } + + if (!NT_SUCCESS(Status)) { + goto PrepareControllerEnd; + } + } + + // + // Fail initialization if minimum number of interrupt and connection + // resources were not described correctly. + // + + if ((InterruptResourceCount != 1) || (I2ResourceFound == FALSE)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: Resource do not match expected values! " + "Interrupt = %d, I2C = %d\n", + __FUNCTION__, + InterruptResourceCount, + I2ResourceFound); + + Status = STATUS_UNSUCCESSFUL; + goto PrepareControllerEnd; + } + + GpioContext->TotalPins = SIM_GPIO_TOTAL_PINS; + GpioContext->TotalBanks = SIM_GPIO_TOTAL_BANKS; + GpioContext->Device = Device; + for (Index = 0; Index < GpioContext->TotalBanks; Index += 1) { + GpioContext->Banks[Index].GpioContext = GpioContext; + GpioContext->Banks[Index].AddressBase = + (USHORT)(Index * MaximumSimGpioAddress); + } + + // + // For SimGpio_i2c, the connection to the I2C controller needs to be + // established. + // + + Status = SimGpioSetupSpbConnection(GpioContext); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSetupSpbConnection() failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto PrepareControllerEnd; + } + +PrepareControllerEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioReleaseController ( + _In_ WDFDEVICE Device, + _In_ PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called by the GPIO class extension to uninitialize the GPIO + controller. + + N.B. This function is not marked pageable because this function is in + the device power down path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Device); + + // + // Release the mappings established in the initialize callback. + // + // N.B. Disconnecting of the interrupt is handled by the GPIO class + // extension. + // + + SimGpioDestroySpbConnection(Context); + + // + // Stop WPP Tracing. + // + // TODO: DO THIS in the cleanup callback! + // + + WPP_CLEANUP(NULL); + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioQueryControllerBasicInformation ( + _In_ PVOID Context, + _Out_ PCLIENT_CONTROLLER_BASIC_INFORMATION ControllerInformation + ) + +/*++ + +Routine Description: + + This routine returns the GPIO controller's attributes to the class extension. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ControllerInformation - Supplies a pointer to a buffer that receives + controller's information. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PSIM_GPIO_CONTEXT GpioContext; + + ControllerInformation->Version = GPIO_CONTROLLER_BASIC_INFORMATION_VERSION; + ControllerInformation->Size = sizeof(CLIENT_CONTROLLER_BASIC_INFORMATION); + + // + // Specify the number of pins on the SimGPIO controller. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + ControllerInformation->TotalPins = GpioContext->TotalPins; + ControllerInformation->NumberOfPinsPerBank = SIM_GPIO_PINS_PER_BANK; + + // + // Indicate that the GPIO controller is off-SoC (i.e. behind some I2C bus) + // and thus can only be serviced at PASSIVE_LEVEL IRQL. + // + + ControllerInformation->Flags.MemoryMappedController = FALSE; + + // + // Indicate that status register must be cleared explicitly. + // + + ControllerInformation->Flags.ActiveInterruptsAutoClearOnRead = FALSE; + + // + // Indicate that the client driver would like to receive IO requests as a + // set of bitmasks as that maps directly to the register operations. + // + + ControllerInformation->Flags.FormatIoRequestsAsMasks = TRUE; + + // + // Indicate that the GPIO controller does not support controller-level + // D-state power management or F-state power management. + // + // N.B. F-state management is only supported for on-SoC GPIO controllers. + // + + ControllerInformation->Flags.DeviceIdlePowerMgmtSupported = FALSE; + ControllerInformation->Flags.BankIdlePowerMgmtSupported = FALSE; + + // + // Note the IdleTimeout parameter does not need to be initialized if + // D-state power management is not supported. + // + // ControllerInformation->IdleTimeout = IdleTimeoutDefaultValue; + // + + // + // Indicate that the client driver prefers GPIO class extension ActiveBoth + // emulation. + // + + ControllerInformation->Flags.EmulateActiveBoth = TRUE; + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioStartController ( + _In_ PVOID Context, + _In_ BOOLEAN RestoreContext, + _In_ WDF_POWER_DEVICE_STATE PreviousPowerState + ) + +/*++ + +Routine Description: + + This routine starts the simulated GPIO controller. This routine is + responsible for configuring all the pins to their default modes. + + N.B. This function is not marked pageable because this function is in + the device power up path. It is called at PASSIVE_IRQL though. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + RestoreContext - Supplies a flag that indicates whether the client driver + should restore the GPIO controller state to a previously saved state + or not. + + PreviousPowerState - Supplies the device power state that the device was in + before this transition to D0. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + BANK_ID BankId; + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(PreviousPowerState); + + // + // Perform all the steps necessary to start the device. + // + + // + // If restore context is FALSE, then this is initial transition into D0 + // power state for this controller. In such case, disable any interrupts + // that may have been left enabled (e.g. perhaps by FW, previous D0 -> Dx + // transition etc.) Otherwise, such interrupts could trigger an interrupt + // storm if they were to assert without any driver being registered to + // handle such interrupts. + // + // If restore context is TRUE, then this is a transition into D0 power + // state from a lower power Dx state. In such case, restore the context + // that was present before the controller transitioned into the lower + // power state. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + Status = STATUS_SUCCESS; + if (RestoreContext == FALSE) { + for (BankId = 0; BankId < GpioContext->TotalBanks; BankId += 1) { + GpioBank = &GpioContext->Banks[BankId]; + + // + // Read the current values of the interrupt enable register. + // + + EnableValue = 0; + Status = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto StartControllerEnd; + } + + // + // If no interrupts are enabled, then skip to the next bank. + // + + if (EnableValue == 0) { + continue; + } + + // + // Disable all interrupts on this bank by clearing the enable + // register. + // + + EnableValue = 0; + Status = SimGpioSpbWriteByte(GpioBank, EnableRegister, EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto StartControllerEnd; + } + } + + } else { + + // + // Restoring the controller state involves restoring the state of + // each SimGPIO bank. + // + + for (BankId = 0; BankId < GpioContext->TotalBanks; BankId += 1) { + Status = SimGpioRestoreBankHardwareContext(Context, BankId); + if (!NT_SUCCESS(Status)) { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioRestoreBankHardwareContext() failed! " + "Context = %p, Bank = %d, Status = %#x\n", + __FUNCTION__, + Context, + BankId, + Status); + + goto StartControllerEnd; + } + } + } + +StartControllerEnd: + return Status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioStopController ( + _In_ PVOID Context, + _In_ BOOLEAN SaveContext, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) + +/*++ + +Routine Description: + + This routine stops the GPIO controller. This routine is responsible for + resetting all the pins to their default modes. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + SaveContext - Supplies a flag that indicates whether the client driver + should save the GPIO controller state or not. The state may need + to be restored when the controller is restarted. + + TargetState - Supplies the device power state which the device will be put + in once the callback is complete. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(TargetState); + + // + // Perform all the steps necessary to stop the device. + // + + // + // If save context is FALSE, then this is a final transition into D3/off + // power state. Hence saving of context is not necessary. + // + // If save context is TRUE, then this is a transition into a lower power + // Dx state. In such case, save the context as it will need to be + // restored when the device is brought back to D0 (i.e. ON) power state. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + Status = STATUS_SUCCESS; + if (SaveContext == TRUE) { + for (BankId = 0; BankId < GpioContext->TotalBanks; BankId += 1) { + Status = SimGpioSaveBankHardwareContext(Context, BankId); + if (!NT_SUCCESS(Status)) { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSaveBankHardwareContext() failed! " + "Context = %p, Bank = %d, Status = %#x\n", + __FUNCTION__, + Context, + BankId, + Status); + + goto StopControllerEnd; + } + } + } + +StopControllerEnd: + return Status; +} + +// +// --------------------------------------------------------- Interrupt Handlers +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioEnableInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_ENABLE_INTERRUPT_PARAMETERS EnableParameters + ) + +/*++ + +Routine Description: + + This routine configures the supplied pin for interrupt. + + N.B. For off-SOC GPIOs, the GPIO class extension synchronizes this callback + with other interrupt-related callbacks. Thus the locking is + automatically taken care of by the class extension. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + EnableParameters - Supplies a pointer to a structure containing enable + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be enabled. The pin + number is relative to the bank. + + Flags - Supplies flags controlling the enable operation. Currently + no flags are defined. + + InterruptMode - Supplies the trigger mode (edge or level) configured for + this interrupt when it was enabled. + + Polarity - Supplies the polarity (active low or active high) configured + for this interrupt when it was enabled. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + VendorData - Supplies an optional pointer to a buffer containing the + vendor data supplied in the GPIO descriptor. This field will be + NULL if no vendor data was supplied. This buffer is read-only. + + VendorDataLength - Supplies the length of the vendor data buffer. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + BANK_ID BankId; + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + UCHAR ModeValue; + PIN_NUMBER PinNumber; + UCHAR PolarityValue; + NTSTATUS Status; + UCHAR StatusRegisterValue; + + // + // If the polarity is not supported, then bail out. Note the interrupt + // polarity cannot be InterruptActiveBoth as this sample uses ActiveBoth + // emulation. + // + + if ((EnableParameters->Polarity != InterruptActiveHigh) && + (EnableParameters->Polarity != InterruptActiveLow)) { + + Status = STATUS_NOT_SUPPORTED; + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: Polarity not supported! Polarity = %d, Status = %#x\n", + __FUNCTION__, + EnableParameters->Polarity, + Status); + + goto EnableInterruptEnd; + } + + BankId = EnableParameters->BankId; + PinNumber = EnableParameters->PinNumber; + Status = STATUS_SUCCESS; + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + + // + // Read the current values of the interrupt mode register, polarity + // register, enable register. + // + + Status = SimGpioSpbReadByte(GpioBank, ModeRegister, &ModeValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(ModeRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + Status = SimGpioSpbReadByte(GpioBank, PolarityRegister, &PolarityValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(PolarityRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + Status = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + // + // Determine the mode register value. If the interrupt is Level then set + // the bit; otherwise, clear it (edge-triggered). + // + + if (EnableParameters->InterruptMode == LevelSensitive) { + ModeValue |= (1 << PinNumber); + } else { + ModeValue &= ~(1 << PinNumber); + } + + // + // Determine the new polarity register value. If the interrupt is ActiveHigh + // (or Rising-edge) then set it to 0x1. Otherwise, clear it. + // + + switch (EnableParameters->Polarity) { + + case InterruptActiveHigh: + PolarityValue |= (1 << PinNumber); + break; + + case InterruptActiveLow: + PolarityValue &= ~(1 << PinNumber); + break; + } + + // + // Clear the corresponding status bit first to ignore any stale value. + // The status register is atomically modified on the controller. It takes + // a bitmask on the bits to be cleared. + // + + StatusRegisterValue = (1 << PinNumber); + + // + // Enable the interrupt by setting the bit in the interrupt enable register. + // + + EnableValue |= (1 << PinNumber); + + // + // Write the new values for the interrupt mode register, polarity + // register, status register and enable register. + // + + Status = SimGpioSpbWriteByte(GpioBank, ModeRegister, ModeValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(ModeRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + Status = SimGpioSpbWriteByte(GpioBank, PolarityRegister, PolarityValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(PolarityRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + Status = SimGpioSpbWriteByte(GpioBank, StatusRegister, StatusRegisterValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(StatusRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + // + // Enable the interrupt by setting the bit in the interrupt enable register. + // + + Status = SimGpioSpbWriteByte(GpioBank, EnableRegister, EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + + goto EnableInterruptEnd; + } + +EnableInterruptEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioDisableInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_DISABLE_INTERRUPT_PARAMETERS DisableParameters + ) + +/*++ + +Routine Description: + + This routine disables the supplied pin from interrupting. + + This routine is not marked PAGED as it may be called before/after + the boot device is in D0/D3 if boot device has GPIO dependencies. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + DisableParameters - Supplies a pointer to a structure supplying the + parameters for disabling the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be disabled. The pin + number is relative to the bank. + + Flags - Supplies flags controlling the disable operation. Currently + no flags are defined. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[DisableParameters->BankId]; + + // + // Read the current value of the interrupt enable register. + // + + Status = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto DisableInterruptEnd; + } + + // + // Disable the interrupt by clearing the bit in the interrupt enable + // register. + // + + EnableValue &= ~(1 << DisableParameters->PinNumber); + Status = SimGpioSpbWriteByte(GpioBank, EnableRegister, EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto DisableInterruptEnd; + } + +DisableInterruptEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioMaskInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_MASK_INTERRUPT_PARAMETERS MaskParameters + ) + +/*++ + +Routine Description: + + This routine invokes masks the supplied pin from interrupting. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + MaskParameters - Supplies a pointer to a structure containing mask + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinMask - Supplies a bitmask of pins which should be masked. If a pin + should be masked, then the corresponding bit is set in the bitmask. + + FailedMask - Supplies a bitmask of pins that failed to be masked. If + a pin could not be masked, the bit should be set in this field. + + N.B. This should only be done if for non memory-mapped controllers. + Memory-mapped controllers are never expected to fail this + operation. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + GPIO_DISABLE_INTERRUPT_PARAMETERS DisableParameters; + PIN_NUMBER PinIndex; + ULONG64 PinMask; + NTSTATUS Status; + + // + // Mask is essentially same as disable for SimGPIO controller. The primary + // difference is that mask callback supplies a bit-mask. + // + + Status = STATUS_SUCCESS; + PinMask = MaskParameters->PinMask; + RtlZeroMemory(&DisableParameters, sizeof(DisableParameters)); + DisableParameters.BankId = MaskParameters->BankId; + while (PinMask > 0) { + PinIndex = RtlFindLeastSignificantBit(PinMask); + if (PinIndex == 0xFF) { + + NT_ASSERT(FALSE); + + goto MaskInterruptsEnd; + } + + DisableParameters.PinNumber = PinIndex; + Status = SimGpioDisableInterrupt(Context, &DisableParameters); + if (!NT_SUCCESS(Status)) { + goto MaskInterruptsEnd; + } + + PinMask &= ~(1 << PinIndex); + } + + // + // Set the bitmask of pins that could not be successfully masked. + // + +MaskInterruptsEnd: + MaskParameters->FailedMask = PinMask; + return Status; +} + +NTSTATUS +SimGpioUnmaskInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_ENABLE_INTERRUPT_PARAMETERS UnmaskParameters + ) + +/*++ + +Routine Description: + + This routine invokes unmasks the supplied interrupt pin. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + UnmaskParameters - Supplies a pointer to a structure containing parameters + for unmasking the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be unmasked. The pin + number is relative to the bank. + + InterruptMode - Supplies the trigger mode (edge or level) configured for + this interrupt when it was enabled. + + Polarity - Supplies the polarity (active low or active high) configured + for this interrupt when it was enabled. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + VendorData - NULL. + + VendorDataLength - 0. + + N.B. The VendorData and VendorDataLength are not supplied for unmask + operation (i.e., both fields are zero). + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[UnmaskParameters->BankId]; + + // + // Read the current value of the interrupt enable register. + // + + Status = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto UnmaskInterruptEnd; + } + + // + // Unmask the interrupt by setting the bit in the interrupt enable + // register. + // + + EnableValue |= (1 << UnmaskParameters->PinNumber); + Status = SimGpioSpbWriteByte(GpioBank, EnableRegister, EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto UnmaskInterruptEnd; + } + +UnmaskInterruptEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioQueryActiveInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_QUERY_ACTIVE_INTERRUPTS_PARAMETERS QueryActiveParameters + ) + +/*++ + +Routine Description: + + This routine returns the current set of active interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + QueryActiveParameters - Supplies a pointer to a structure containing query + parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + EnabledMask - Supplies a bitmask of pins enabled for interrupts + on the specified GPIO bank. + + ActiveMask - Supplies a bitmask that receives the active interrupt + mask. If a pin is interrupting and set in EnabledMask, then the + corresponding bit is set in the bitmask. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS ReturnStatus; + UCHAR StatusValue; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[QueryActiveParameters->BankId]; + + // + // NOTE: As SimGPIO is not a real hardware device, no interrupt will ever + // fire. Thus the status register value will never change. To pretend + // as if a real interrupt happened, it marks all currently enabled + // interrupts as asserting. Copy the enable interrupt value into + // the status register. + // + // This should NOT be done for a real GPIO controller!! + // + + // + // BEGIN: SIMGPIO HACK. + // + + ReturnStatus = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(ReturnStatus)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + ReturnStatus); + + goto QueryActiveInterruptsEnd; + } + + ReturnStatus = SimGpioSpbWriteByte(GpioBank, StatusRegister, EnableValue); + if (!NT_SUCCESS(ReturnStatus)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(StatusRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + ReturnStatus); + + goto QueryActiveInterruptsEnd; + } + + // + // END: SIMGPIO HACK. + // + + // + // Return the current value of the interrupt status register in the + // ActiveMask parameter. + // + + ReturnStatus = SimGpioSpbReadByte(GpioBank, StatusRegister, &StatusValue); + if (!NT_SUCCESS(ReturnStatus)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + ReturnStatus); + + goto QueryActiveInterruptsEnd; + } + + QueryActiveParameters->ActiveMask = (ULONG64)StatusValue; + +QueryActiveInterruptsEnd: + return ReturnStatus; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioQueryEnabledInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_QUERY_ENABLED_INTERRUPTS_PARAMETERS QueryEnabledParameters + ) + +/*++ + +Routine Description: + + This routine returns the current set of enabled interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + QueryEnabledParameters - Supplies a pointer to a structure containing query + parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + EnabledMask - Supplies a bitmask that receives the enabled interrupt + mask. If a pin is enabled, then the corresponding bit is set in the + mask. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called with the + interrupt lock acquired by the class extension, but not always + from within the interrupt context. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS ReturnStatus; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[QueryEnabledParameters->BankId]; + + // + // Return the current value of the interrupt enable register into the + // EnabledMask parameter. It is strongly preferred that the true state of + // the hardware is returned, rather than a software-cached variable, since + // CLIENT_QueryEnabledInterrupts is used by the class extension to detect + // interrupt storms. + // + + ReturnStatus = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(ReturnStatus)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + ReturnStatus); + + goto QueryEnabledInterruptsEnd; + } + + QueryEnabledParameters->EnabledMask = (ULONG64)EnableValue; + +QueryEnabledInterruptsEnd: + return ReturnStatus; +} +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioClearActiveInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_CLEAR_ACTIVE_INTERRUPTS_PARAMETERS ClearParameters + ) + +/*++ + +Routine Description: + + This routine clears the GPIO controller's active set of interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ClearParameters - Supplies a pointer to a structure containing clear + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + ClearActiveMask - Supplies a mask of pins which should be marked as + inactive. If a pin should be cleared, then the corresponding bit is + set in the mask. + + FailedClearMask - Supplies a bitmask of pins that failed to be cleared. If + a pin could not be cleared, the bit should be set in this field. + + N.B. This should only be done if for non memory-mapped controllers. + Memory-mapped controllers are never expected to fail this + operation. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS ReturnStatus; + UCHAR StatusValue; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ClearParameters->BankId]; + + // + // Clear the bits that are set in the ClearActiveMask parameter. + // + + StatusValue = (UCHAR)ClearParameters->ClearActiveMask; + ReturnStatus = SimGpioSpbWriteByte(GpioBank, StatusRegister, StatusValue); + if (!NT_SUCCESS(ReturnStatus)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(StatusRegister) failed! Status = %#x\n", + __FUNCTION__, + ReturnStatus); + + goto ClearActiveInterruptsEnd; + } + + // + // If the operation succeeded, then all the bits were cleared. + // + + StatusValue = 0x0; + + // + // Set the bitmask of pins that could not be successfully cleared. + // Either all bits get written successfully or none does. + // + +ClearActiveInterruptsEnd: + ClearParameters->FailedClearMask = StatusValue; + return ReturnStatus; +} + +NTSTATUS +SimGpioReconfigureInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_RECONFIGURE_INTERRUPTS_PARAMETERS ReconfigureParameters + ) + +/*++ + +Routine Description: + + This routine reconfigures the interrupt in the specified mode. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ReconfigureParameters - Supplies a pointer to a structure containing + parameters for reconfiguring the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be reconfigured. + The pin number is relative to the bank. + + InterruptMode - Supplies the trigger mode (edge or level) for the new + configuration. + + Polarity - Supplies the polarity (active low or active high) for the + new configuration. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + UCHAR ModeValue; + PIN_NUMBER PinNumber; + UCHAR PolarityValue; + NTSTATUS Status; + UCHAR StatusRegisterValue; + + + BankId = ReconfigureParameters->BankId; + PinNumber = ReconfigureParameters->PinNumber; + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + + // + // Read the current values of the interrupt mode register, polarity + // register, enable register. + // + + Status = SimGpioSpbReadByte(GpioBank, ModeRegister, &ModeValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(ModeRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ReconfigureInterruptEnd; + } + + Status = SimGpioSpbReadByte(GpioBank, PolarityRegister, &PolarityValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(PolarityRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ReconfigureInterruptEnd; + } + + // + // Determine the mode register value. If the interrupt is Level then set + // the bit; otherwise, clear it (edge-triggered). + // + + if (ReconfigureParameters->InterruptMode == LevelSensitive) { + ModeValue |= (1 << PinNumber); + } else { + ModeValue &= ~(1 << PinNumber); + } + + // + // Determine the new polarity register value. If the interrupt is ActiveHigh + // (or Rising-edge) then set it to 0x1. Otherwise, clear it. + // + + switch (ReconfigureParameters->Polarity) { + + case InterruptActiveHigh: + PolarityValue |= (1 << PinNumber); + break; + + case InterruptActiveLow: + PolarityValue &= ~(1 << PinNumber); + break; + } + + // + // Clear any stale status bits from the previous configuration. + // + // The status register is atomically modified on the controller. It takes + // a bitmask on the bits to be cleared. + // + + StatusRegisterValue = (1 << PinNumber); + Status = SimGpioSpbWriteByte(GpioBank, StatusRegister, StatusRegisterValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(StatusRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ReconfigureInterruptEnd; + } + + // + // Write the new values for the interrupt mode register, polarity + // register, status register and enable register. + // + + Status = SimGpioSpbWriteByte(GpioBank, ModeRegister, ModeValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(ModeRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ReconfigureInterruptEnd; + } + + Status = SimGpioSpbWriteByte(GpioBank, PolarityRegister, PolarityValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(PolarityRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ReconfigureInterruptEnd; + } + +ReconfigureInterruptEnd: + return Status; +} + +// +// --------------------------------------------------------------- I/O Handlers +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioConnectIoPins ( + _In_ PVOID Context, + _In_ PGPIO_CONNECT_IO_PINS_PARAMETERS ConnectParameters + ) + +/*++ + +Routine Description: + + This routine invokes connects the specified pins for IO. The pins can + be read from if connected for input, or written to if connected for + output. + + N.B. This routine is called at PASSIVE_LEVEL but is not marked as + PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ConnectParameters - Supplies a pointer to a structure supplying the + parameters for connecting the IO pins. Fields description: + + BankId - Supplies the ID for the GPIO bank. + + PinNumberTable - Supplies an array of pins to be connected for IO. The + pin numbers are 0-based and relative to the GPIO bank. + + PinCount - Supplies the number of pins in the pin number table. + + ConnectMode - Supplies the mode in which the pins should be configured + (viz. input or output). + + ConnectFlags - Supplies the flags controlling the IO setup. Currently + no flags are defined. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + DriveStrength - Supplies the drive strength to be applied. The value + is in 100th of mA (i.e., 1.21mA will be supplied as 121mA). + + VendorData - Supplies an optional pointer to a buffer containing the + vendor data supplied in the GPIO descriptor. This field will be + NULL if no vendor data was supplied. This buffer is read-only. + + VendorDataLength - Supplies the length of the vendor data buffer. + + ConnectFlags - Supplies the flag to be used for connect operation. + Currently no flags are defined. + +Return Value: + + NT status code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + PPIN_NUMBER PinNumberTable; + UCHAR PinValue; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ConnectParameters->BankId]; + + // + // Read the current direction register value. + // + + Status = SimGpioSpbReadByte(GpioBank, DirectionRegister, &PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(DirectionRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ConnectIoPinsEnd; + } + + // + // Walk through all the supplied pins and connect them in the specified + // mode (input or output). + // + + PinNumberTable = ConnectParameters->PinNumberTable; + for (Index = 0; Index < ConnectParameters->PinCount; Index += 1) { + PinNumber = PinNumberTable[Index]; + + // + // If the pins are being connected for input, then set the bit. + // Otherwise clear the bit. + // + + if (ConnectParameters->ConnectMode == ConnectModeInput) { + PinValue |= (1 << PinNumber); + + } else if (ConnectParameters->ConnectMode == ConnectModeOutput) { + PinValue &= ~(1 << PinNumber); + } + } + + Status = SimGpioSpbWriteByte(GpioBank, DirectionRegister, PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(DirectionRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ConnectIoPinsEnd; + } + +ConnectIoPinsEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioDisconnectIoPins ( + _In_ PVOID Context, + _In_ PGPIO_DISCONNECT_IO_PINS_PARAMETERS DisconnectParameters + ) + +/*++ + +Routine Description: + + This routine invokes disconnects the specified IO pins. The pins are + put back in their original mode. + + N.B. This routine is called at PASSIVE_LEVEL but is not marked as + PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + DisconnectParameters - Supplies a pointer to a structure containing + disconnect operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumberTable - Supplies an array of pins to be disconnected. The pin + numbers are relative to the GPIO bank. + + PinCount - Supplies the number of pins in the pin number table. + + DisconnectMode - Supplies the mode in which the pins are currently + configured (viz. input or output). + + DisconnectFlags - Supplies the flags controlling the IO setup. Currently + no flags are defined. + +Return Value: + + NT status code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + PPIN_NUMBER PinNumberTable; + UCHAR PinValue; + NTSTATUS Status; + + // + // If the pin configuration should be preserved post disconnect, then + // there is nothing left to do. + // + + if (DisconnectParameters->DisconnectFlags.PreserveConfiguration == 1) { + Status = STATUS_SUCCESS; + goto DisconnectIoPinsEnd; + } + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[DisconnectParameters->BankId]; + + // + // Read the current direction register value. + // + + Status = SimGpioSpbReadByte(GpioBank, DirectionRegister, &PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(DirectionRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto DisconnectIoPinsEnd; + } + + // + // Walk through all the supplied pins and disconnect them. On SimGPIO + // controller, all pins are reset to the default mode (output). + // + + PinNumberTable = DisconnectParameters->PinNumberTable; + for (Index = 0; Index < DisconnectParameters->PinCount; Index += 1) { + PinNumber = PinNumberTable[Index]; + PinValue &= ~(1 << PinNumber); + } + + Status = SimGpioSpbWriteByte(GpioBank, DirectionRegister, PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(DirectionRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto DisconnectIoPinsEnd; + } + +DisconnectIoPinsEnd: + return Status; +} + +_Must_inspect_result_ +NTSTATUS +SimGpioReadGpioPins ( + _In_ PVOID Context, + _In_ PGPIO_READ_PINS_MASK_PARAMETERS ReadParameters + ) + +/*++ + +Routine Description: + + This routine reads the current values for all the pins. + + As the FormatIoRequestsAsMasks bit was set inside + SimGpioQueryControllerInformation(), all this routine needs to do is read + the level register value and return to the GPIO class extension. It will + return the right set of bits to the caller. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ReadParameters - Supplies a pointer to a structure containing read + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinValues - Supplies a pointer to a variable that receives the current + pin values. + + Flags - Supplies the flag to be used for read operation. Currently + defined flags are: + + WriteConfiguredPins: If set, the read is being done on a set of + pin that were configured for write. In such cases, the + GPIO client driver is expected to read and return the + output register value. + +Return Value: + + NT status code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + UCHAR PinValue; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ReadParameters->BankId]; + + // + // Read the current level register value. Note the GPIO class may invoke + // the read routine on write-configured pins. In such case the output + // register values should be read. + // + // N.B. In case of SimGPIO, the LevelRegister holds the value for input + // as well as output pins. Thus the same register is read in either + // case. + // + + if (ReadParameters->Flags.WriteConfiguredPins == FALSE) { + Status = SimGpioSpbReadByte(GpioBank, LevelRegister, &PinValue); + + } else { + Status = SimGpioSpbReadByte(GpioBank, LevelRegister, &PinValue); + } + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(DirectionRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto ReadGpioPinsEnd; + } + + *ReadParameters->PinValues = PinValue; + +ReadGpioPinsEnd: + return Status; +} + +_Must_inspect_result_ +NTSTATUS +SimGpioWriteGpioPins ( + _In_ PVOID Context, + _In_ PGPIO_WRITE_PINS_MASK_PARAMETERS WriteParameters + ) + +/*++ + +Routine Description: + + This routine sets the current values for the specified pins. This call is + synchronized with the write and connect/disconnect IO calls. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + WriteParameters - Supplies a pointer to a structure containing write + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + SetMask - Supplies a mask of pins which should be set (0x1). If a pin + should be set, then the corresponding bit is set in the mask. + All bits that are clear in the mask should be left intact. + + ClearMask - Supplies a mask of pins which should be cleared (0x0). If + a pin should be cleared, then the bit is set in the bitmask. All + bits that are clear in the mask should be left intact. + + Flags - Supplies the flag controlling the write operation. Currently + no flags are defined. + +Return Value: + + NT status code. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + UCHAR PinValue; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[WriteParameters->BankId]; + + // + // Read the current level register value. + // + + Status = SimGpioSpbReadByte(GpioBank, LevelRegister, &PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(DirectionRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto WriteGpioPinsEnd; + } + + // + // Set the bits specified in the set mask and clear the ones specified + // in the clear mask. + // + + PinValue |= WriteParameters->SetMask; + PinValue &= ~WriteParameters->ClearMask; + + // + // Write the updated value to the register. + // + + Status = SimGpioSpbWriteByte(GpioBank, LevelRegister, PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(DirectionRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto WriteGpioPinsEnd; + } + +WriteGpioPinsEnd: + return Status; +} + +VOID +SimGpioEvtDriverUnload ( + _In_ WDFDRIVER Driver + ) + +/*++ + +Routine Description: + + This routine is called by WDF to allow final cleanup prior to unloading + the GPIO class extension. This routine destroys the class library and stops + tracing. + +Arguments: + + Driver - Supplies a handle to a framework driver object. + +Return Value: + + None. + +--*/ + +{ + + PDRIVER_OBJECT DriverObject; + NTSTATUS Status; + + PAGED_CODE(); + + Status = GPIO_CLX_UnregisterClient(Driver); + NT_ASSERT(NT_SUCCESS(Status)); + + DriverObject = WdfDriverWdmGetDriverObject(Driver); + + TraceEvents(TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "%s: Driver unloaded!\n", + __FUNCTION__); + + WPP_CLEANUP(DriverObject); + return; +} + +// +// ------------------------------------------------------- Power mgmt handlers +// + +NTSTATUS +SimGpioSaveBankHardwareContext ( + _In_ PVOID Context, + _In_ BANK_ID BankId + ) + +/*++ + +Routine Description: + + This routine saves the hardware context for the GPIO controller. + + N.B. This function is not marked pageable because this function is in + the device power up path. It is called at PASSIVE_IRQL though. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + BankId - Supplies the ID of the bank. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PUCHAR DestinationAddress; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + Status = STATUS_SUCCESS; + + // + // Loop through all the registers and save them. The status register can + // only be written to be cleared and thus isn't saved. + // + + for (Index = 0; Index < MaximumSimGpioAddress; Index += 1) { + if (Index == StatusRegister) { + continue; + } + + DestinationAddress = &GpioBank->SavedRegisterContext[Index]; + Status = SimGpioSpbReadByte(GpioBank, Index, DestinationAddress); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(Register = %d) failed! " + "Status = %#x\n", + __FUNCTION__, + Index, + Status); + + goto SaveBankHardwareContextEnd; + } + } + +SaveBankHardwareContextEnd: + return Status; +} + +NTSTATUS +SimGpioRestoreBankHardwareContext ( + _In_ PVOID Context, + _In_ BANK_ID BankId + ) + +/*++ + +Routine Description: + + This routine saves the hardware context for the GPIO controller. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + BankId - Supplies the ID of the bank. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + NTSTATUS Status; + UCHAR Value; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + Status = STATUS_SUCCESS; + + // + // Loop through all the registers and write them. The order of restore is + // important. The enable and direction registers need to be programmed + // after the (mode, polarity) and level registers have been written to. + // Hence they are restored at the very end. + // + + for (Index = 0; Index < MaximumSimGpioAddress; Index += 1) { + if ((Index == EnableRegister) || + (Index == DirectionRegister) || + (Index == StatusRegister)) { + + continue; + } + + Value = GpioBank->SavedRegisterContext[Index]; + Status = SimGpioSpbWriteByte(GpioBank, Index, Value); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(Register = %d) failed! " + "Status = %#x\n", + __FUNCTION__, + Index, + Status); + + goto RestoreBankHardwareContextEnd; + } + } + + // + // Restore the direction register. + // + + Value = GpioBank->SavedRegisterContext[DirectionRegister]; + Status = SimGpioSpbWriteByte(GpioBank, DirectionRegister, Value); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(DirectionRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto RestoreBankHardwareContextEnd; + } + + // + // Restore the interrupt enable register at the very end. + // + + Value = GpioBank->SavedRegisterContext[EnableRegister]; + Status = SimGpioSpbWriteByte(GpioBank, EnableRegister, Value); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto RestoreBankHardwareContextEnd; + } + +RestoreBankHardwareContextEnd: + return Status; +} + +__pragma(warning(default: 4127)) // conditional expression is a constant + + diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.h b/gpio/samples/simgpio_i2c/simgpio_i2c.h new file mode 100644 index 00000000..a9c02af3 --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.h @@ -0,0 +1,176 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + simgpio_i2c.h + +Abstract: + + This sample implements a GPIO client driver for simulated GPIO (SimGpio) + controller. + + Note: DIRQL in the comments below refers to device IRQL, which is any + IRQL > DISPATCH_LEVEL (and less than some IRQL reserved for OS use). + + +Environment: + + Kernel mode + +--*/ + +#pragma once + +// +// -------------------------------------------------------------------- Defines +// + +// +// Define total number of pins on the simulated GPIO controller. +// + +#define SIM_GPIO_TOTAL_PINS (16) +#define SIM_GPIO_PINS_PER_BANK (8) +#define SIM_GPIO_TOTAL_BANKS (SIM_GPIO_TOTAL_PINS / SIM_GPIO_PINS_PER_BANK) + +#define SIM_GPIO_REGISTER_ADDRESS_SIZE (sizeof(USHORT)) + +// +// Pool tag for SimGpio allocations. +// + +#define SIM_GPIO_POOL_TAG 'GmiS' + +// +// Macro for pointer arithmetic. +// + +#define Add2Ptr(Ptr, Value) ((PVOID)((PUCHAR)(Ptr) + (Value))) + +// +// ---------------------------------------------------------------------- Types +// + +// +// Define the registers within the SimGPIO controller. There are 16 pins per +// controller. Note this is a logical device and thus may correspond to a +// physical bank or module if the GPIO controller in hardware has more than +// 16 pins. Below is the register set from a logical perspective. +// +// typedef struct _SIM_GPIO_REGISTERS { +// UCHAR ModeRegister1; +// UCHAR PolarityRegister1; +// UCHAR EnableRegister1; +// UCHAR StatusRegister1; +// UCHAR DirectionRegister1; +// UCHAR LevelRegister1; +// UCHAR ModeRegister2; +// UCHAR PolarityRegister2; +// UCHAR EnableRegister2; +// UCHAR StatusRegister2; +// UCHAR DirectionRegister2; +// UCHAR LevelRegister2; +// } SIM_GPIO_REGISTERS, *PSIM_GPIO_REGISTERS; +// +// + +typedef enum _SIM_GPIO_REGISTER_ADDRESS { + ModeRegister = 0x0, + PolarityRegister, + EnableRegister, + StatusRegister, + DirectionRegister, + LevelRegister, + MaximumSimGpioAddress +} SIM_GPIO_REGISTER_ADDRESS, *PSIM_GPIO_REGISTER_ADDRESS; + +struct _SIM_GPIO_CONTEXT; + +typedef struct _SIM_GPIO_BANK { + USHORT AddressBase; + struct _SIM_GPIO_CONTEXT *GpioContext; + UCHAR SavedRegisterContext[MaximumSimGpioAddress]; +} SIM_GPIO_BANK, *PSIM_GPIO_BANK; + +// +// The SimGPIO client driver device extension. +// + +struct _SIM_GPIO_CONTEXT { + USHORT TotalBanks; + USHORT TotalPins; + WDFDEVICE Device; + WDFIOTARGET SpbIoTarget; + LARGE_INTEGER SpbConnectionId; + WDFREQUEST SpbRequest; + // PSIM_GPIO_REGISTERS ControllerBase; + // ULONG Length; + SIM_GPIO_BANK Banks[SIM_GPIO_TOTAL_BANKS]; +}; + +typedef struct _SIM_GPIO_CONTEXT SIM_GPIO_CONTEXT; +typedef SIM_GPIO_CONTEXT *PSIM_GPIO_CONTEXT; + +// +// Request context +// +typedef struct _SIM_GPIO_REQUEST_CONTEXT { + WDFDEVICE Device; + + // + // Whether the request is a sequence or not. + // + + BOOLEAN SequenceRequest; +} SIM_GPIO_REQUEST_CONTEXT, *PSIM_GPIO_REQUEST_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SIM_GPIO_REQUEST_CONTEXT, GetRequestContext); + +// +// ----------------------------------------------------------------- Prototypes +// + +VOID +SimGpioDestroySpbConnection ( + _In_ PSIM_GPIO_CONTEXT SimGpioContext + ); + +NTSTATUS +SimGpioSpbReadByte ( + _In_ PSIM_GPIO_BANK GpioBank, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _Out_writes_(sizeof(UCHAR)) PUCHAR Data + ); + +NTSTATUS +SimGpioSetupSpbConnection ( + _In_ PSIM_GPIO_CONTEXT GpioContext + ); + +NTSTATUS +SimGpioSpbWriteByte ( + _In_ PSIM_GPIO_BANK GpioBank, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _In_ UCHAR Data + ); + +NTSTATUS +SimGpioRestoreBankHardwareContext ( + _In_ PVOID Context, + _In_ BANK_ID BankId + ); + +NTSTATUS +SimGpioSaveBankHardwareContext ( + _In_ PVOID Context, + _In_ BANK_ID BankId + ); + + diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.inx b/gpio/samples/simgpio_i2c/simgpio_i2c.inx new file mode 100644 index 00000000..ab8a130d Binary files /dev/null and b/gpio/samples/simgpio_i2c/simgpio_i2c.inx differ diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.rc b/gpio/samples/simgpio_i2c/simgpio_i2c.rc new file mode 100644 index 00000000..a2b60d9a --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Simulated GPIO I2C Client" +#define VER_INTERNALNAME_STR "simgpio_i2c.sys" +#define VER_ORIGINALFILENAME_STR "simgpio_i2c.sys" + +#include "common.ver" + diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj b/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj new file mode 100644 index 00000000..1e2e182e --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj @@ -0,0 +1,200 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2} + $(MSBuildProjectName) + 1 + Debug + Win32 + {ABDC1F8D-20A4-4A63-9D67-7B8374C9057D} + + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + + + * + true + $(InfArch) + true + .\$(IntDir)\simgpio_i2c.inf + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + + + + simgpio_i2c + + + simgpio_i2c + + + simgpio_i2c + + + simgpio_i2c + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + true + Level4 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + true + Level4 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + true + Level4 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + true + Level4 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters b/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters new file mode 100644 index 00000000..e5d09d4d --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters @@ -0,0 +1,42 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {522EE08F-03EE-45E0-875A-A47E2E9F025C} + + + h;hpp;hxx;hm;inl;inc;xsd + {22B4A1F1-B05A-4383-A9D2-BDBF45C56975} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {E394D95C-DF2A-4F28-9A22-A3C121FC5394} + + + inf;inv;inx;mof;mc; + {5D13D9B5-61BC-45B1-AF0D-4EDE9C7D2F80} + + + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/gpio/samples/simgpio_i2c/spb_i2c.c b/gpio/samples/simgpio_i2c/spb_i2c.c new file mode 100644 index 00000000..a5ac3fc3 --- /dev/null +++ b/gpio/samples/simgpio_i2c/spb_i2c.c @@ -0,0 +1,666 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + spb_i2c.c + +Abstract: + + This sample implements a GPIO client driver for simulated GPIO (SimGpio) + controller. + + Note: DIRQL in the comments below refers to device IRQL, which is any + IRQL > DISPATCH_LEVEL (and less than some IRQL reserved for OS use). + + +Environment: + + Kernel mode + +--*/ + +// +// ------------------------------------------------------------------- Includes +// + +#include +#include +#include +#define RESHUB_USE_HELPER_ROUTINES +#include "reshub.h" // Resource and descriptor definitions +#include "spb.h" // SPB definitions +#include "simgpio_i2c.h" +#include "trace.h" +#include "spb_i2c.tmh" + +// +// -------------------------------------------------------------------- Defines +// + +NTSTATUS +SimGpioSpbRead ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _Out_writes_(DataLength) PUCHAR Data, + _In_ USHORT DataLength + ); + +NTSTATUS +SimGpioSpbSequence ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_reads_(SequenceLength) PVOID Sequence, + _In_ SIZE_T SequenceLength + ); + +NTSTATUS +SimGpioSpbWrite ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_ USHORT RegisterAddress, + _In_reads_(DataLength) PUCHAR Data, + _In_ ULONG DataLength + ); + +// +// ------------------------------------------------------------------ Functions +// + +NTSTATUS +SimGpioSetupSpbConnection ( + _In_ PSIM_GPIO_CONTEXT GpioContext + ) + +/*++ + +Routine Description: + + This routine opens an I/O target to the controller driver using the + connection ID received during PrepareController callback. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + GpioContext - Supplies a pionter to the client driver's device context. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDF_OBJECT_ATTRIBUTES Attributes; + WDF_IO_TARGET_OPEN_PARAMS Parameters; + WDF_OBJECT_ATTRIBUTES RequestAttributes; + PSIM_GPIO_REQUEST_CONTEXT RequestContext; + NTSTATUS Status; + DECLARE_UNICODE_STRING_SIZE(SpbDevicePath, RESOURCE_HUB_PATH_SIZE); + + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Status = WdfIoTargetCreate(GpioContext->Device, + &Attributes, + &GpioContext->SpbIoTarget); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: WdfIoTargetCreate failed to create SPB IoTarget! " + "Device = %p, Status:%#x\n", + __FUNCTION__, + GpioContext->Device, + Status); + + if (GpioContext->SpbIoTarget != NULL) { + WdfObjectDelete(GpioContext->SpbIoTarget); + } + + goto SetupSpbConnectionEnd; + } + + // + // Use the connection ID supplied to create the full device path. This + // device path (indirectly) represents the path for the I2C controller. + // + + Status = RESOURCE_HUB_CREATE_PATH_FROM_ID( + &SpbDevicePath, + GpioContext->SpbConnectionId.LowPart, + GpioContext->SpbConnectionId.HighPart); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "ResourceHub create device path (%wZ) failed Status:%#x", + &SpbDevicePath, + Status); + + goto SetupSpbConnectionEnd; + } + + // + // Initialize the parameters for the SPB IO target. + // + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&Parameters, + &SpbDevicePath, + (GENERIC_READ | GENERIC_WRITE)); + + Parameters.ShareAccess = 0; + Parameters.CreateDisposition = FILE_OPEN; + Parameters.FileAttributes = FILE_ATTRIBUTE_NORMAL; + + // + // Open the SPB IO target. This creates a handle to the I2C controller + // behind which SimGPO resides. + // + + Status = WdfIoTargetOpen(GpioContext->SpbIoTarget, &Parameters); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "WdfIoTargetOpen failed to open SPB target Status:%#x", + Status); + + goto SetupSpbConnectionEnd; + } + + // + // Create a SPB request for writes and reads to the IO target. + // + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&RequestAttributes, \ + SIM_GPIO_REQUEST_CONTEXT); + + Status = WdfRequestCreate(&RequestAttributes, + NULL, + &GpioContext->SpbRequest); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: WdfRequestCreate failed creating SpbRequest Status:%#x", + __FUNCTION__, + Status); + + goto SetupSpbConnectionEnd; + } + + // + // Initialize the request context with default values + // + + RequestContext = GetRequestContext(GpioContext->SpbRequest); + RequestContext->Device = GpioContext->Device; + RequestContext->SequenceRequest = FALSE; + +SetupSpbConnectionEnd: + return Status; +} + +VOID +SimGpioDestroySpbConnection ( + _In_ PSIM_GPIO_CONTEXT GpioContext + ) + +/*++ + +Routine Description: + + This routine closes the SPB I/O target and releases SPB-related resources. + + N.B. This function is not marked pageable because this function is in + the device power down path. + +Arguments: + + GpioContext - Supplies a pionter to the client driver's device context. + +Return Value: + + None. + +--*/ + +{ + + // + // Delete the SPB request. + // + + if (GpioContext->SpbRequest != NULL) { + WdfObjectDelete(GpioContext->SpbRequest); + } + + // + // Delete the IO target. Note this will also close if it is opened. + // + + if (GpioContext->SpbIoTarget != NULL) { + WdfObjectDelete(GpioContext->SpbIoTarget); + } + + return; +} + +NTSTATUS +SimGpioSpbReadByte ( + _In_ PSIM_GPIO_BANK GpioBank, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _Out_writes_(sizeof(UCHAR)) PUCHAR Data + ) + +/*++ + +Routine Description: + + This routine performs a single-byte read from the SPB I/O target. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + GpioBank - Supplies a pionter to the GPIO bank to be read from. + + RegisterAddress - Supplies the bank-relative register address to be read. + + Data - Supplies the byte buffer to read the data into. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + USHORT ActualAddress; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + if (RegisterAddress >= MaximumSimGpioAddress) { + Status = STATUS_NOT_SUPPORTED; + goto SpbReadByteEnd; + } + + ActualAddress = GpioBank->AddressBase + (USHORT)RegisterAddress; + GpioContext = GpioBank->GpioContext; + Status = SimGpioSpbRead(GpioContext, RegisterAddress, Data, sizeof(UCHAR)); + +SpbReadByteEnd: + return Status; +} + +NTSTATUS +SimGpioSpbRead ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _Out_writes_(DataLength) PUCHAR Data, + _In_ USHORT DataLength + ) + +/*++ + +Routine Description: + + This routine performs a read from the SPB I/O target. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + GpioContext - Supplies a pionter to the client driver's device context. + + RegisterAddress - Supplies the absolute register address to read from. + + Data - Supplies the output byte buffer containing the data read. + + DataLength - Supplies the length of the byte buffer data. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + USHORT Address; + UCHAR Index; + SPB_TRANSFER_LIST_AND_ENTRIES(2) Sequence; + NTSTATUS Status; + + // + // Build the SPB sequence (send address, read data). + // + + Address = RegisterAddress; + SPB_TRANSFER_LIST_INIT(&(Sequence.List), 2); + Index = 0; // silence prefast + Sequence.List.Transfers[Index] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE( + SpbTransferDirectionToDevice, + 0, + &Address, + SIM_GPIO_REGISTER_ADDRESS_SIZE); + + Index += 1; // silence prefast + Sequence.List.Transfers[Index] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE( + SpbTransferDirectionFromDevice, + 0x0, + Data, + DataLength); + + // + // Send the read as a sequence request to the SPB target. + // + + Status = SimGpioSpbSequence(GpioContext, &Sequence, sizeof(Sequence)); + if (!NT_SUCCESS(Status)) { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: SpbSequence failed sending a read sequence! " + "Status:%#x\n", + __FUNCTION__, + Status); + } + + return Status; +} + +NTSTATUS +SimGpioSpbWriteByte ( + _In_ PSIM_GPIO_BANK GpioBank, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _In_ UCHAR Data + ) + +/*++ + +Routine Description: + + This routine performs a single-byte write to the SPB I/O target. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + GpioBank - Supplies a pionter to the GPIO bank to be written to. + + RegisterAddress - Supplies the bank-relative register address to write to. + + Data - Supplies the data to be written. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + USHORT ActualAddress; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + if (RegisterAddress >= MaximumSimGpioAddress) { + Status = STATUS_NOT_SUPPORTED; + goto SpbWriteByteEnd; + } + + ActualAddress = GpioBank->AddressBase + (USHORT)RegisterAddress; + GpioContext = GpioBank->GpioContext; + Status = SimGpioSpbWrite(GpioContext, RegisterAddress, &Data, sizeof(UCHAR)); + +SpbWriteByteEnd: + return Status; +} + +NTSTATUS +SimGpioSpbWrite ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_ USHORT RegisterAddress, + _In_reads_(DataLength) PUCHAR Data, + _In_ ULONG DataLength + ) + +/*++ + +Routine Description: + + This routine performs a write to the SPB I/O target. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + GpioContext - Supplies a pionter to the client driver's device context. + + RegisterAddress - Supplies the absolute register address to write to. + + Data - Supplies the byte buffer containing the data to be written. + + DataLength - Supplies the length of the write buffer. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PUCHAR Buffer; + ULONG BufferLength; + ULONG_PTR BytesWritten; + WDF_MEMORY_DESCRIPTOR MemoryDescriptor; + WDFMEMORY MemoryWrite; + NTSTATUS Status; + + MemoryWrite = NULL; + if ((Data == NULL) || (DataLength <= 0)) { + Status = STATUS_INVALID_PARAMETER; + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: Invalid write request! Data:%p Length:%lu Status:%#x\n", + __FUNCTION__, + Data, + DataLength, + Status); + + goto SpbWriteEnd; + } + + // + // A SPB write-write is a single write request with the register + // and data combined in one buffer. So we need to allocate memory + // for the size of a register + data length + + BufferLength = DataLength + SIM_GPIO_REGISTER_ADDRESS_SIZE; + Status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES, + NonPagedPoolNx, + SIM_GPIO_POOL_TAG, + BufferLength, + &MemoryWrite, + (PVOID*)&Buffer); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: WdfMemoryCreate failed allocating memory buffer for write!" + "Status:%#x\n", + __FUNCTION__, + Status); + + goto SpbWriteEnd; + } + + // + // Setup the write buffer. The buffer should contain address followed by + // data. + // + + RtlCopyMemory(Buffer, &RegisterAddress, SIM_GPIO_REGISTER_ADDRESS_SIZE); + RtlCopyMemory((Buffer + SIM_GPIO_REGISTER_ADDRESS_SIZE), Data, DataLength); + + // + // Send the request synchronously. + // + + WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&MemoryDescriptor, MemoryWrite, NULL); + Status = WdfIoTargetSendWriteSynchronously( + GpioContext->SpbIoTarget, + NULL, + &MemoryDescriptor, + NULL, + NULL, + &BytesWritten); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: WdfIoTargetSendWriteSynchronously failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto SpbWriteEnd; + } + +SpbWriteEnd: + if (MemoryWrite != NULL) { + WdfObjectDelete(MemoryWrite); + } + + return Status; +} + +NTSTATUS +SimGpioSpbSequence ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_reads_(SequenceLength) PVOID Sequence, + _In_ SIZE_T SequenceLength + ) + +/*++ + +Routine Description: + + This routine issues a sequence read-write request to the SPB I/O target. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + GpioContext - Supplies a pionter to the client driver's device context. + + Sequence - Supplies a pointer to a list of sequence transfers. + + SequenceLength - Supplies the length of sequence transfers. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDF_OBJECT_ATTRIBUTES Attributes; + ULONG_PTR BytesReturned; + WDF_MEMORY_DESCRIPTOR MemoryDescriptor; + WDFMEMORY MemorySequence; + NTSTATUS Status; + + if ((Sequence == NULL) || (SequenceLength == 0)) { + Status = STATUS_INVALID_PARAMETER; + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: Invalid sequence request! Sequence:%p Length:%lu Status:%#x\n", + __FUNCTION__, + Sequence, + (ULONG)SequenceLength, + Status); + + goto SpbSequenceEnd; + } + + // + // Create preallocated WDFMEMORY. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Status = WdfMemoryCreatePreallocated( + &Attributes, + Sequence, + SequenceLength, + &MemorySequence); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: WdfMemoryCreatePreallocated failed! Status:%#x\n", + __FUNCTION__, + Status); + + goto SpbSequenceEnd; + } + + WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&MemoryDescriptor, MemorySequence, NULL); + + // + // Send the SPB sequence IOCTL. + // + + Status = WdfIoTargetSendIoctlSynchronously( + GpioContext->SpbIoTarget, + NULL, + IOCTL_SPB_EXECUTE_SEQUENCE, + &MemoryDescriptor, + NULL, + NULL, + &BytesReturned); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: Failed sending SPB sequence request! Bytes:%lu Status:%#x", + __FUNCTION__, + (ULONG)BytesReturned, + Status); + + goto SpbSequenceEnd; + } + +SpbSequenceEnd: + return Status; +} + + diff --git a/gpio/samples/simgpio_i2c/trace.h b/gpio/samples/simgpio_i2c/trace.h new file mode 100644 index 00000000..22969f69 --- /dev/null +++ b/gpio/samples/simgpio_i2c/trace.h @@ -0,0 +1,75 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + trace.h + +Abstract: + + This sample implements a GPIO client driver for simulated GPIO (SimGpio) + controller. + + Note: DIRQL in the comments below refers to device IRQL, which is any + IRQL > DISPATCH_LEVEL (and less than some IRQL reserved for OS use). + +Environment: + + Kernel mode + +--*/ + +#ifndef _TRACE_H +#define _TRACE_H + +#include // For TRACE_LEVEL definitions + +// +// Define the tracing flags. +// +// Tracing GUID - e71e3b6e-aaf9-402c-97fe-7610cb3985db +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + SimGpioI2CTraceGuid, \ + (e71e3b6e,aaf9,402c,97fe,7610cb3985db), \ + WPP_DEFINE_BIT(TRACE_FLAG_INIT) \ + WPP_DEFINE_BIT(TRACE_FLAG_SPB) \ + WPP_DEFINE_BIT(TRACE_FLAG_REQUEST) \ + WPP_DEFINE_BIT(TRACE_FLAG_INTERRUPT) \ + WPP_DEFINE_BIT(TRACE_FLAG_IO) \ + ) + +#define WPP_FLAG_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAG_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) + +#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \ + WPP_LEVEL_LOGGER(flags) + +#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \ + (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC FuncEntry{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS); +// FUNC FuncExit{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS); +// USEPREFIX(FuncEntry, "%!STDPREFIX! [%!FUNC!] --> entry"); +// USEPREFIX(FuncExit, "%!STDPREFIX! [%!FUNC!] <--"); +// end_wpp +// + +#endif _TRACE_H \ No newline at end of file diff --git a/network/config/bindview/BINDING.CPP b/network/config/bindview/BINDING.CPP new file mode 100644 index 00000000..b3771004 --- /dev/null +++ b/network/config/bindview/BINDING.CPP @@ -0,0 +1,827 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 2001. +// +// File: B I N D I N G . C P P +// +// Contents: Functions to illustrate +// o How to enumerate binding paths. +// o How to enumerate binding interfaces. +// o How to enable/disable bindings. +// +// Notes: +// +// Author: Alok Sinha 15-May-01 +// +//---------------------------------------------------------------------------- + +#include "bindview.h" + +// +// Function: WriteBindings +// +// Purpose: Write bindings to specified file. +// +// Arguments: +// fp [in] File handle. +// +// Returns: None. +// +// Notes: +// + +VOID WriteBindings (FILE *fp) +{ + INetCfg *pnc; + IEnumNetCfgComponent *pencc; + INetCfgComponent *pncc; + LPWSTR lpszApp; + HRESULT hr; + UINT i; + + + hr = HrGetINetCfg( FALSE, + APP_NAME, + &pnc, + &lpszApp ); + + if ( hr == S_OK ) { + + for (i=CLIENTS_SELECTED; i <= PROTOCOLS_SELECTED; ++i) { + + fwprintf( fp, L"--- Bindings of %s ---\n", lpszNetClass[i] ); + + // + // Get Component Enumerator Interface. + // + + hr = HrGetComponentEnum( pnc, + pguidNetClass[i], + &pencc ); + if ( hr == S_OK ) { + + hr = HrGetFirstComponent( pencc, &pncc ); + + while( hr == S_OK ) { + + // + // Write bindings of the component. + // + + WriteBindingPath( fp, + pncc ); + ReleaseRef( pncc ); + + fwprintf( fp, L"\n" ); + + hr = HrGetNextComponent( pencc, &pncc ); + } + + fwprintf( fp, L"\n" ); + + // + // S_FALSE merely indicates that there are no more components. + // + + if ( hr == S_FALSE ) { + hr = S_OK; + } + + ReleaseRef( pencc ); + } + else { + ErrMsg( hr, + L"Couldn't get the component enumerator interface." ); + } + } + + HrReleaseINetCfg( pnc, FALSE ); + } + else { + if ( (hr == NETCFG_E_NO_WRITE_LOCK) && lpszApp ) { + ErrMsg( hr, + L"%s currently holds the lock, try later.", + lpszApp ); + + CoTaskMemFree( lpszApp ); + } + else { + ErrMsg( hr, + L"Couldn't get the notify object interface." ); + } + } + + return; +} + +// +// Function: WriteBindingPath +// +// Purpose: Write binding paths of a component. +// +// Arguments: +// fp [in] File handle. +// pncc [in] Network component. +// +// Returns: None. +// +// Notes: +// + +VOID WriteBindingPath (FILE *fp, + INetCfgComponent *pncc) +{ + IEnumNetCfgBindingPath *pencbp; + INetCfgBindingPath *pncbp; + LPWSTR lpszName; + HRESULT hr; + + // + // Write the first component's name. + // + + hr = pncc->GetDisplayName( &lpszName ); + + if ( hr == S_OK ) { + fwprintf( fp, L"\n%s", lpszName ); + } + else { + ErrMsg( hr, + L"Unable to get the display name of a component, " + L" some binding paths will not be written." ); + + return; + } + + // + // Get binding path enumerator. + // + + hr = HrGetBindingPathEnum( pncc, + EBP_BELOW, + &pencbp ); + if ( hr == S_OK ) { + + hr = HrGetFirstBindingPath( pencbp, + &pncbp ); + + while( hr == S_OK ) { + + // + // Write interfaces of the binding path. + // + + WriteInterfaces( fp, + pncbp ); + + ReleaseRef( pncbp ); + + hr = HrGetNextBindingPath( pencbp, + &pncbp ); + if ( hr == S_OK ) { + fwprintf( fp, L"\n%s", lpszName ); + } + } + + ReleaseRef( pencbp ); + } + else { + ErrMsg( hr, + L"Couldn't get the binding path enumerator of %s. " + L"Its binding paths will not be written.", + lpszName ); + } + + CoTaskMemFree( lpszName ); + return; +} + +// +// Function: WriteInterfaces +// +// Purpose: Write bindings to specified file. +// +// Arguments: +// fp [in] File handle. +// pncbp [in] Binding path. +// +// Returns: None. +// +// Notes: +// + +VOID WriteInterfaces (FILE *fp, + INetCfgBindingPath *pncbp) +{ + IEnumNetCfgBindingInterface *pencbi; + INetCfgBindingInterface *pncbi; + INetCfgComponent *pnccLower; + LPWSTR lpszName; + HRESULT hr; + + hr = HrGetBindingInterfaceEnum( pncbp, + &pencbi ); + + if ( hr == S_OK ) { + + hr = HrGetFirstBindingInterface( pencbi, + &pncbi ); + + // + // Write lower component of each interface. + // + + while( hr == S_OK ) { + + hr = pncbi->GetLowerComponent ( &pnccLower ); + + if ( hr == S_OK ) { + + hr = pnccLower->GetDisplayName( &lpszName ); + if ( hr == S_OK ) { + fwprintf( fp, L"-->%s", lpszName ); + CoTaskMemFree( lpszName ); + } + } + + ReleaseRef( pnccLower ); + ReleaseRef( pncbi ); + + hr = HrGetNextBindingInterface( pencbi, + &pncbi ); + } + + ReleaseRef( pencbi ); + } + else { + ErrMsg( hr, + L"Couldn't get the binding interface enumerator." + L"The binding interfaces will not be shown." ); + } + + return; +} + +// +// Function: EnumNetBindings +// +// Purpose: Enumerate components and their bindings. +// +// Arguments: +// hwndTree [in] Tree handle. +// uiTypeSelected [in] Type of network component selected. +// +// Returns: TRUE on success. +// +// Notes: +// + +BOOL EnumNetBindings (HWND hwndTree, + UINT uiTypeSelected) +{ + INetCfg *pnc; + IEnumNetCfgComponent *pencc; + INetCfgComponent *pncc; + LPWSTR lpszApp; + HTREEITEM hTreeItem; + HRESULT hr; + + + hr = HrGetINetCfg( FALSE, + APP_NAME, + &pnc, + &lpszApp ); + + if ( hr == S_OK ) { + + // + // Get Component Enumerator Interface. + // + + hr = HrGetComponentEnum( pnc, + pguidNetClass[uiTypeSelected], + &pencc ); + if ( hr == S_OK ) { + + hr = HrGetFirstComponent( pencc, &pncc ); + + while( hr == S_OK ) { + + // + // Add the component's name to the tree. + // + + hTreeItem = AddToTree( hwndTree, + TVI_ROOT, + pncc ); + if ( hTreeItem ) { + + // + // Enumerate bindings. + // + + ListBindings( pncc, + hwndTree, + hTreeItem ); + } + + ReleaseRef( pncc ); + + hr = HrGetNextComponent( pencc, &pncc ); + } + + // + // S_FALSE merely indicates that there are no more components. + // + + if ( hr == S_FALSE ) { + hr = S_OK; + } + + ReleaseRef( pencc ); + } + else { + ErrMsg( hr, + L"Couldn't get the component enumerator interface." ); + } + + HrReleaseINetCfg( pnc, FALSE ); + } + else { + if ( (hr == NETCFG_E_NO_WRITE_LOCK) && lpszApp ) { + ErrMsg( hr, + L"%s currently holds the lock, try later.", + lpszApp ); + + CoTaskMemFree( lpszApp ); + } + else { + ErrMsg( hr, + L"Couldn't get the notify object interface." ); + } + } + + return hr == S_OK; +} + +// +// Function: ListBindings +// +// Purpose: Enumerate bindings of network components. +// +// Arguments: +// pncc [in] Network component. +// hwndTree [in] Tree handle. +// hTreeItemRoot [in] Parent item. +// +// Returns: None. +// +// Notes: +// + +VOID ListBindings (INetCfgComponent *pncc, + HWND hwndTree, + HTREEITEM hTreeItemRoot) +{ + IEnumNetCfgBindingPath *pencbp; + INetCfgBindingPath *pncbp; + HTREEITEM hTreeItem; + ULONG ulIndex; + HRESULT hr; + + hr = HrGetBindingPathEnum( pncc, + EBP_BELOW, + &pencbp ); + if ( hr == S_OK ) { + + hr = HrGetFirstBindingPath( pencbp, + &pncbp ); + + ulIndex = 1; + + while( hr == S_OK ) { + + // + // Add an item for the binding path. + // + + hTreeItem = AddBindNameToTree( pncbp, + hwndTree, + hTreeItemRoot, + ulIndex ); + + if ( hTreeItem ) { + + // + // Enumerate interfaces. + // + + ListInterfaces( pncbp, + hwndTree, + hTreeItem ); + } + + ReleaseRef( pncbp ); + + hr = HrGetNextBindingPath( pencbp, + &pncbp ); + + ulIndex++; + } + + ReleaseRef( pencbp ); + } + else { + LPWSTR lpszName; + + if ( pncc->GetDisplayName(&lpszName) == S_OK ) { + + ErrMsg( hr, + L"Couldn't get the binding path enumerator of %s. " + L"Its binding paths will not be shown.", + lpszName ); + + CoTaskMemFree( lpszName ); + } + else { + ErrMsg( hr, + L"Couldn't get the binding path enumerator of a " + L"network component. The binding paths will not " + L"be shown." ); + } + } + + return; +} + +// +// Function: ListInterfaces +// +// Purpose: Enumerate interfaces of a binding path. +// +// Arguments: +// pncbp [in] Binding path. +// hwndTree [in] Tree handle. +// hTreeItemRoot [in] Parent item. +// +// Returns: None. +// +// Notes: +// + +VOID ListInterfaces (INetCfgBindingPath *pncbp, + HWND hwndTree, + HTREEITEM hTreeItemRoot) +{ + IEnumNetCfgBindingInterface *pencbi; + INetCfgBindingInterface *pncbi; + INetCfgComponent *pnccBound; + HTREEITEM hTreeItem; + HRESULT hr; + + hr = HrGetBindingInterfaceEnum( pncbp, + &pencbi ); + + if ( hr == S_OK ) { + + hr = HrGetFirstBindingInterface( pencbi, + &pncbi ); + hTreeItem = hTreeItemRoot; + + while( (hr == S_OK) && hTreeItem ) { + + // + // Add lower component of every interface to the tree. + // + + pncbi->GetLowerComponent( &pnccBound ); + + hTreeItem = AddToTree( hwndTree, + hTreeItem, + pnccBound ); + + ReleaseRef( pnccBound ); + ReleaseRef( pncbi ); + + hr = HrGetNextBindingInterface( pencbi, + &pncbi ); + } + + // + // If hr is S_OK then, the loop terminated due to error in adding + // the binding path to the tree and pncbi has a reference to an + // interface. + // + + if ( hr == S_OK ) { + + ReleaseRef( pncbi ); + } + + ReleaseRef( pencbi ); + } + else { + ErrMsg( hr, + L"Couldn't get the binding interface enumerator." + L"The binding interfaces will not be shown." ); + } + + return; +} + +// +// Function: HandleBindingPathOperation +// +// Purpose: +// +// Arguments: +// hwndOwner [in] Owner window. +// ulSelection [in] Option selected. +// hItem [in] Item selected. +// lParam [in] lParam of the item. +// +// Returns: None. +// +// Notes: +// + +VOID HandleBindingPathOperation (HWND hwndOwner, + ULONG ulSelection, + HTREEITEM hItem, + LPARAM lParam) +{ + switch( ulSelection ) { + + case IDI_ENABLE: + case IDI_DISABLE: + + // + // Enable/disable binding path. + // + + EnableBindingPath( hwndOwner, + hItem, + (LPWSTR)lParam, + ulSelection == IDI_ENABLE ); + } + + return; +} + +// +// Function: EnableBindingPath +// +// Purpose: Enable/disable binding path. +// +// Arguments: +// hwndOwner [in] Owner window. +// hItem [in] Item handle of the binding path. +// lpszPathToken [in] Path token of the binding path. +// fEnable [in] if TRUE, enable, otherwise disable. +// +// Returns: None. +// +// Notes: +// + +VOID +EnableBindingPath ( + HWND hwndOwner, + HTREEITEM hItem, + _In_ LPWSTR lpszPathToken, + BOOL fEnable) +{ + INetCfg *pnc; + INetCfgBindingPath *pncbp; + LPWSTR lpszInfId; + LPWSTR lpszApp; + HRESULT hr; + + // + // Get PnpID of the owner component. + // + + lpszInfId = GetComponentId( hwndOwner, + hItem ); + + if ( lpszInfId ) { + + hr = HrGetINetCfg( TRUE, + APP_NAME, + &pnc, + &lpszApp ); + + if ( hr == S_OK ) { + + // + // Find the binding path reference. + // + + pncbp = FindBindingPath( pnc, + lpszInfId, + lpszPathToken ); + + if ( pncbp ) { + + // + // Enable/disable. + // + + hr = pncbp->Enable( fEnable ); + + if ( hr == S_OK ) { + hr = pnc->Apply(); + + if ( hr == S_OK ) { + + // + // Refreshe the state of the item representing the + // binding path. + // + + RefreshItemState( hwndOwner, + hItem, + fEnable ); + } + else { + ErrMsg( hr, + L"Failed to apply changes to the binding path." ); + } + } + else { + if ( fEnable ) { + ErrMsg( hr, + L"Failed to enable the binding path." ); + } + else { + ErrMsg( hr, + L"Failed to disable the binding path." ); + } + } + + ReleaseRef( pncbp ); + } + + HrReleaseINetCfg( pnc, + TRUE ); + } + else { + if ( (hr == NETCFG_E_NO_WRITE_LOCK) && lpszApp ) { + ErrMsg( hr, + L"%s currently holds the lock, try later.", + lpszApp ); + + CoTaskMemFree( lpszApp ); + } + else { + ErrMsg( hr, + L"Couldn't get the notify object interface." ); + } + } + } + else { + ErrMsg( HRESULT_FROM_WIN32(GetLastError()), + L"Couldn't determine the owner of the binding path." ); + } + + return; +} + +// +// Function: GetComponentId +// +// Purpose: Find the PnpID of a network component. +// +// Arguments: +// hwndTree [in] Tree handle. +// hItem [in] Item handle of the binding path. +// +// Returns: PnpID of the network component. +// +// Notes: +// + +LPWSTR GetComponentId (HWND hwndTree, + HTREEITEM hItem) +{ + LPWSTR lpszInfId; + HTREEITEM hTreeItemParent; + TVITEMW tvItem; + + lpszInfId = NULL; + + // + // Get the item handle of the owner component. + // + + hTreeItemParent = TreeView_GetParent( hwndTree, + hItem ); + if ( hTreeItemParent ) { + + // + // Get lParam of the owner component. lParam is the PnpID. + // + + ZeroMemory( &tvItem, + sizeof(TVITEMW) ); + + tvItem.hItem = hTreeItemParent; + tvItem.mask = TVIF_PARAM; + + if ( TreeView_GetItem(hwndTree, + &tvItem) ) { + + lpszInfId = (LPWSTR)tvItem.lParam; + } + } + + return lpszInfId; +} + +// +// Function: WriteBindings +// +// Purpose: Find the binding path with a give path token. +// +// Arguments: +// pnc [in] INetCfg reference. +// lpszInfId [in] PnpID of the network component. +// lpszPathTokenSelected [in] Path token of the binding path to search. +// +// Returns: Reference to the binding path on success, otherwise NULL. +// +// Notes: +// + +INetCfgBindingPath * +FindBindingPath ( + INetCfg *pnc, + _In_ LPWSTR lpszInfId, + _In_ LPWSTR lpszPathTokenSelected) +{ + INetCfgComponent *pncc = NULL; + IEnumNetCfgBindingPath *pencbp = NULL; + INetCfgBindingPath *pncbp = NULL; + LPWSTR lpszPathToken; + HRESULT hr; + BOOL fFound; + + + fFound = FALSE; + + // + // Get the component reference. + // + + hr = pnc->FindComponent( lpszInfId, + &pncc ); + + if ( hr == S_OK ) { + + hr = HrGetBindingPathEnum( pncc, + EBP_BELOW, + &pencbp ); + if ( hr == S_OK ) { + + hr = HrGetFirstBindingPath( pencbp, + &pncbp ); + + // Enumerate each binding path and find the one + // whose path token matches the specified one. + // + + while ( !fFound && (hr == S_OK) ) { + + hr = pncbp->GetPathToken( &lpszPathToken ); + + if ( hr == S_OK ) { + fFound = !wcscmp( lpszPathToken, + lpszPathTokenSelected ); + + CoTaskMemFree( lpszPathToken ); + } + + if ( !fFound ) { + ReleaseRef( pncbp ); + + hr = HrGetNextBindingPath( pencbp, + &pncbp ); + } + } + + ReleaseRef( pencbp ); + } + else { + ErrMsg( hr, + L"Couldn't get the binding path enumerator interface." ); + } + } + else { + ErrMsg( hr, + L"Couldn't get an interface pointer to %s.", + lpszInfId ); + } + + return (fFound) ? pncbp : NULL; +} diff --git a/network/config/bindview/BINDVIEW.CPP b/network/config/bindview/BINDVIEW.CPP new file mode 100644 index 00000000..4231f7c4 --- /dev/null +++ b/network/config/bindview/BINDVIEW.CPP @@ -0,0 +1,2336 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 2001. +// +// File: B I N D V I E W . C P P +// +// Contents: +// +// Notes: +// +// Author: Alok Sinha 15-Amy-01 +// +//---------------------------------------------------------------------------- + + +#include "BindView.h" + +//---------------------------------------------------------------------------- +// Globals +// + +// +// Image list for devices of various setup class. +// + +SP_CLASSIMAGELIST_DATA ClassImageListData; + +HINSTANCE hInstance; +HMENU hMainMenu; +HMENU hComponentSubMenu; +HMENU hBindingPathSubMenu; + +// +// Network components whose bindings are enumerated. +// + +LPWSTR lpszNetClass[] = { + L"All Clients", + L"All Services", + L"All Protocols" + }; + +// +// GUIDs of network components. +// + +const GUID *pguidNetClass [] = { + &GUID_DEVCLASS_NETCLIENT, + &GUID_DEVCLASS_NETSERVICE, + &GUID_DEVCLASS_NETTRANS, + &GUID_DEVCLASS_NET + }; + +// +// Program entry point. +// + +int APIENTRY +WinMain ( + _In_ HINSTANCE hInst, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPSTR lpCmdLine, + _In_ int nCmdShow ) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + UNREFERENCED_PARAMETER(nCmdShow); + + // + // Make sure common control DLL is loaded. + // + + hInstance = hInst; + + InitCommonControls(); + + if ( DialogBoxW(hInst, + MAKEINTRESOURCEW(IDD_MAIN), + NULL, + MainDlgProc) == -1 ) { + + ErrMsg( HRESULT_FROM_WIN32(GetLastError()), + L"Failed to create the main dialog box, exiting..." ); + } + + return 0; +} + +// +// WndProc for the main dialog box. +// + +INT_PTR CALLBACK MainDlgProc (HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + HWND hwndBindingTree; + HICON hIcon; + + switch (uMsg) { + + case WM_INITDIALOG: + + hIcon = LoadIcon( hInstance, + MAKEINTRESOURCE(IDI_BINDVIEW) ); + + if ( !hIcon ) { + ErrMsg( HRESULT_FROM_WIN32(GetLastError()), + L"Couldn't load the program icon, exiting..." ); + + return FALSE; + } + + SetClassLongPtr( hwndDlg, + GCLP_HICON, + (LONG_PTR)hIcon ); + + hMainMenu = LoadMenu( hInstance, + MAKEINTRESOURCE(IDM_OPTIONS) ); + + if ( !hMainMenu ) { + + ErrMsg( HRESULT_FROM_WIN32(GetLastError()), + L"Couldn't load the program menu, exiting..." ); + + return FALSE; + } + + hComponentSubMenu = GetSubMenu( hMainMenu, + 0 ); + + hBindingPathSubMenu = GetSubMenu( hMainMenu, + 1 ); + + if ( !hComponentSubMenu || !hBindingPathSubMenu ) { + + ErrMsg( HRESULT_FROM_WIN32(GetLastError()), + L"Couldn't load the program menu, exiting..." ); + + DestroyMenu( hMainMenu ); + return FALSE; + } + + // + // Add the network components types whose bindings are shown. + // + + UpdateComponentTypeList( GetDlgItem(hwndDlg, + IDL_COMPONENT_TYPES) ); + + // + // Load and associate the image list of all device classes with + // tree. + // + + hwndBindingTree = GetDlgItem( hwndDlg, + IDT_BINDINGS ); + + ZeroMemory( &ClassImageListData, sizeof(SP_CLASSIMAGELIST_DATA) ); + ClassImageListData.cbSize = sizeof(SP_CLASSIMAGELIST_DATA); + + if ( SetupDiGetClassImageList(&ClassImageListData) == TRUE ) { + + TreeView_SetImageList( hwndBindingTree, + ClassImageListData.ImageList, + LVSIL_NORMAL ); + } + else { + + // + // In case, we failed to load the image list, abort. + // + + ErrMsg( HRESULT_FROM_WIN32(GetLastError()), + L"Couldn't load the image list of " + L"device classes, exiting..." ); + + DestroyMenu( hMainMenu ); + return FALSE; + } + + // + // Enumerate the bindings of the network component selected by default. + // + + EnumNetBindings( hwndBindingTree, + DEFAULT_COMPONENT_SELECTED ); + + return TRUE; // Tell Windows to continue creating the dialog box. + + case WM_COMMAND: + + switch( LOWORD(wParam) ) { + + case IDL_COMPONENT_TYPES: + + if ( HIWORD(wParam) == CBN_SELCHANGE ) { + + // + // User has selected a new network component type. + // + + RefreshAll( hwndDlg ); + } + + break; + + case IDB_EXPAND_ALL: + case IDB_COLLAPSE_ALL: + + if ( HIWORD(wParam) == BN_CLICKED ) { + + HTREEITEM hItem; + // + // Expand/Collapse the entire tree. + // + + hwndBindingTree = GetDlgItem( hwndDlg, + IDT_BINDINGS ); + + hItem = TreeView_GetSelection( hwndBindingTree ); + + ExpandCollapseAll( hwndBindingTree, + TVI_ROOT, + (LOWORD(wParam) == IDB_EXPAND_ALL) ? + TVE_EXPAND : TVE_COLLAPSE ); + + TreeView_SelectSetFirstVisible( hwndBindingTree, + hItem ); + } + + break; + + case IDB_SAVE: + + if ( HIWORD(wParam) == BN_CLICKED ) { + + // + // Save the binding information to a file. + // + + WCHAR lpszFile[MAX_PATH+1]; + + if ( GetFileName(hwndDlg, + L"Text files (*.txt)\0*.txt\0", + L"Select a file name", + OFN_DONTADDTORECENT | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT, + lpszFile, + L"txt", + TRUE) ) { + + DumpBindings( lpszFile ); + } + } + + break; + + case IDB_INSTALL: + + if ( HIWORD(wParam) == BN_CLICKED ) { + + // + // Install a network component. + // + + if ( (BOOL)DialogBoxW(hInstance, + MAKEINTRESOURCEW(IDD_INSTALL), + hwndDlg, + InstallDlg) == TRUE ) { + + RefreshAll( hwndDlg ); + } + } + + break; + + case IDB_UNINSTALL: + + if ( HIWORD(wParam) == BN_CLICKED ) { + + // + // Uninstall a network component. + // + + if ( (BOOL)DialogBoxW(hInstance, + MAKEINTRESOURCEW(IDD_UNINSTALL), + hwndDlg, + UninstallDlg) == TRUE ) { + + RefreshAll( hwndDlg ); + } + } + } + + break; + + case WM_NOTIFY: + { + LPNMHDR lpnm; + + lpnm = (LPNMHDR)lParam; + + if ( (lpnm->idFrom == IDT_BINDINGS) && + (lpnm->code == NM_RCLICK) ) { + + // + // A network component or a binding path is selected + // with a right-click. + // + + ProcessRightClick( lpnm ); + + // + // Tell Windows that the right-click has been handled + // us. + // + + return TRUE; + } + } + break; + + case WM_SYSCOMMAND: + + if ( (0xFFF0 & wParam) == SC_CLOSE ) { + + // + // Before exiting, make sure to delete the image list + // and the buffers associated with each item in the tree. + // + + SetupDiDestroyClassImageList( &ClassImageListData ); + + ReleaseMemory( GetDlgItem(hwndDlg, IDT_BINDINGS), + TVI_ROOT ); + + DestroyMenu( hMainMenu ); + EndDialog( hwndDlg, 0 ); + } + } + + return FALSE; +} + +// +// WndProc of the dialog box for binding/unbinding components. +// + +INT_PTR CALLBACK BindComponentDlg (HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + LPBIND_UNBIND_INFO lpBindUnbind; + + switch (uMsg) { + + case WM_INITDIALOG: + { + DWORD dwCount; + + // + // Save the lParam which is an index to the selected network + // component. + // + + SetWindowLongPtr( hwndDlg, + DWLP_USER, + (LONG_PTR)lParam ); + + lpBindUnbind = (LPBIND_UNBIND_INFO)lParam; + + // + // fBindTo is TRUE when the user wants to bind the selected + // component to other components. So, we list the components + // that are not bound and can bind. + // + // + // fBindTo is FALSE when the user wants to unbind the selected + // component from other components. So, we list the components + // that are bound to it. + // + // + // ListCompToBindUnbind returns number of components added to + // the list. Keep track of it. If it zero then, we don't want to + // show this dialog box. + // + + dwCount = ListCompToBindUnbind( + lpBindUnbind->lpszInfId, + ADAPTERS_SELECTED, + GetDlgItem(hwndDlg, IDT_COMPONENT_LIST), + lpBindUnbind->fBindTo == FALSE ); + + dwCount += ListCompToBindUnbind( + lpBindUnbind->lpszInfId, + CLIENTS_SELECTED, + GetDlgItem(hwndDlg, IDT_COMPONENT_LIST), + lpBindUnbind->fBindTo == FALSE ); + + dwCount += ListCompToBindUnbind( + lpBindUnbind->lpszInfId, + SERVICES_SELECTED, + GetDlgItem(hwndDlg, IDT_COMPONENT_LIST), + lpBindUnbind->fBindTo == FALSE ); + + dwCount += ListCompToBindUnbind( + lpBindUnbind->lpszInfId, + PROTOCOLS_SELECTED, + GetDlgItem(hwndDlg, IDT_COMPONENT_LIST), + lpBindUnbind->fBindTo == FALSE ); + + if ( dwCount > 0 ) { + + // + // Since the same dialog box is used for unbind operation, + // we need to update the text on the button to reflect that + // it is a bind operation. + // + + if ( lpBindUnbind->fBindTo == FALSE ) { + + SetWindowTextW( hwndDlg, + L"Unbind From Network Components" ); + + SetWindowTextW( GetDlgItem(hwndDlg, IDB_BIND_UNBIND), + L"Unbind" ); + + SetWindowTextW( GetDlgItem(hwndDlg, IDG_COMPONENT_LIST), + L"Select components to unbind from" ); + } + } + else { + if ( lpBindUnbind->fBindTo == TRUE ) { + ErrMsg( 0, + L"There no network components that can " + L"bind to the selected component." ); + } + else { + ErrMsg( 0, + L"There no network components that are " + L"bound to the selected component." ); + } + + PostMessage( hwndDlg, WM_NO_COMPONENTS, 0, 0 ); + } + + return TRUE; + } + + case WM_NO_COMPONENTS: + EndDialog( hwndDlg, 0 ); + break; + + case WM_COMMAND: + + if ( (LOWORD(wParam) == IDB_CLOSE) && + (HIWORD(wParam) == BN_CLICKED) ) { + + // + // Before deleting the list in the tree, free the buffer + // associated with each item. The buffer holds the + // INF Id of network components. + // + + ReleaseMemory( GetDlgItem(hwndDlg, IDT_COMPONENT_LIST), + TVI_ROOT ); + + EndDialog( hwndDlg, 0 ); + } + else { + + // + // User wants to bind/unbind. + // + + if ( (LOWORD(wParam) == IDB_BIND_UNBIND) && + (HIWORD(wParam) == BN_CLICKED) ) { + + + + lpBindUnbind = (LPBIND_UNBIND_INFO)GetWindowLongPtr( hwndDlg, + DWLP_USER ); + + if ( BindUnbind(lpBindUnbind->lpszInfId, + GetDlgItem(hwndDlg, IDT_COMPONENT_LIST), + lpBindUnbind->fBindTo) ) { + + RefreshBindings( hwndDlg, + lpBindUnbind->lpszInfId ); + } + + ReleaseMemory( GetDlgItem(hwndDlg, IDT_COMPONENT_LIST), + TVI_ROOT ); + EndDialog( hwndDlg, 0 ); + } + } + break; + + case WM_SYSCOMMAND: + + if ( (0xFFF0 & wParam) == SC_CLOSE ) { + + // + // Before deleting the list in the tree, free the buffer + // associated with each item. The buffer holds the + // INF Id of network components. + // + + ReleaseMemory( GetDlgItem(hwndDlg, IDT_COMPONENT_LIST), + TVI_ROOT ); + + EndDialog( hwndDlg, 0 ); + } + } + + return FALSE; +} + +// +//WndProc of the dialog box for installing network components. +// + +INT_PTR CALLBACK InstallDlg (HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch (uMsg) { + + case WM_INITDIALOG: + { + HWND hwndTree; + + // + // List types of network components e.g. client, + // protocol and service. + // + + hwndTree = GetDlgItem( hwndDlg, + IDT_COMPONENT_LIST ); + + TreeView_SetImageList( hwndTree, + ClassImageListData.ImageList, + LVSIL_NORMAL ); + + // + // Insert and select client by default. + // + + TreeView_Select( hwndTree, + InsertItem(hwndTree, + CLIENTS_SELECTED), + TVGN_CARET ); + + InsertItem( hwndTree, + SERVICES_SELECTED ); + + InsertItem( hwndTree, + PROTOCOLS_SELECTED ); + + // + // Initialize it to FALSE. It will be set to TRUE when + // at least one component is installed. + // + + SetWindowLongPtr( hwndDlg, + DWLP_USER, + (LONG_PTR)FALSE ); + return TRUE; + } + + case WM_COMMAND: + + switch( LOWORD(wParam) ) { + + case IDB_INSTALL: + + // + // Install from Windows system directory. + // + + if ( HIWORD(wParam) == BN_CLICKED ) { + + InstallSelectedComponentType( hwndDlg, NULL ); + } + break; + + case IDB_BROWSE: + + // + // User wants to specify an INF file for the network + // to install. + // + if ( HIWORD(wParam) == BN_CLICKED ) { + + WCHAR lpszInfFile[MAX_PATH+1]; + + if ( GetFileName(hwndDlg, + L"INF files (*.inf)\0*.inf\0", + L"Select the INF file of the network component to install", + OFN_DONTADDTORECENT | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST, + lpszInfFile, + NULL, + FALSE) ) { + + InstallSelectedComponentType( hwndDlg, + lpszInfFile ); + } + } + break; + + case IDB_CLOSE: + + if ( HIWORD(wParam) == BN_CLICKED ) { + + // + // Return the value of DWLP_USER to indicate whether one or + // more components have been installed. Accordingly, the + // the list will be refreshed. + // + + EndDialog( hwndDlg, + GetWindowLongPtr(hwndDlg, DWLP_USER) ); + } + } + break; + + case WM_NOTIFY: + { + LPNMHDR lpnm; + + lpnm = (LPNMHDR)lParam; + + if ( (lpnm->idFrom == IDT_COMPONENT_LIST) && + (lpnm->code == NM_DBLCLK) ) { + + // + // On double-click, install from Windows system directory. + // + + InstallSelectedComponentType( hwndDlg, NULL ); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + return TRUE; + } + } + break; + + case WM_SYSCOMMAND: + + if ( (0xFFF0 & wParam) == SC_CLOSE ) { + + // + // Return the value of DWLP_USER to indicate whether one or + // more components have been installed. Accordingly, the + // the list will be refreshed. + // + + EndDialog( hwndDlg, + GetWindowLongPtr(hwndDlg, DWLP_USER) ); + } + } + + return FALSE; +} + +// +// WndProc of the dialog box for uninstalling a network component. +// + +INT_PTR CALLBACK UninstallDlg (HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + HWND hwndTree; + + switch (uMsg) { + + case WM_INITDIALOG: + + hwndTree = GetDlgItem( hwndDlg, + IDT_COMPONENT_LIST ); + TreeView_SetImageList( hwndTree, + ClassImageListData.ImageList, + LVSIL_NORMAL ); + + // + // List all the components currently installed. + // + + ListInstalledComponents( hwndTree, + &GUID_DEVCLASS_NETCLIENT); + ListInstalledComponents( hwndTree, + &GUID_DEVCLASS_NETSERVICE ); + ListInstalledComponents( hwndTree, + &GUID_DEVCLASS_NETTRANS ); + + // + // Initialize it to FALSE. It will be set to TRUE when + // at least one component is installed. + // + + SetWindowLongPtr( hwndDlg, + DWLP_USER, + (LONG_PTR)FALSE ); + return TRUE; + + case WM_COMMAND: + + switch( LOWORD(wParam) ) { + + case IDB_REMOVE: + + if ( HIWORD(wParam) == BN_CLICKED ) { + + // + // Uninstall the selected component. + // + + UninstallSelectedComponent( hwndDlg ); + + } + break; + + case IDB_CLOSE: + + if ( HIWORD(wParam) == BN_CLICKED ) { + + hwndTree = GetDlgItem( hwndDlg, + IDT_COMPONENT_LIST ); + ReleaseMemory( hwndTree, + TVI_ROOT ); + + // + // Return the value of DWLP_USER to indicate whether one or + // more components have been installed. Accordingly, the + // the list will be refreshed. + // + + EndDialog( hwndDlg, + GetWindowLongPtr(hwndDlg, DWLP_USER) ); + } + } + + break; + + case WM_NOTIFY: + { + LPNMHDR lpnm; + + lpnm = (LPNMHDR)lParam; + + if ( (lpnm->idFrom == IDT_COMPONENT_LIST) && + (lpnm->code == NM_DBLCLK) ) { + + UninstallSelectedComponent( hwndDlg ); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + return TRUE; + } + } + break; + + case WM_SYSCOMMAND: + + if ( (0xFFF0 & wParam) == SC_CLOSE ) { + + hwndTree = GetDlgItem( hwndDlg, + IDT_COMPONENT_LIST ); + ReleaseMemory( hwndTree, + TVI_ROOT ); + + // + // Return the value of DWLP_USER to indicate whether one or + // more components have been installed. Accordingly, the + // the list will be refreshed. + // + + EndDialog( hwndDlg, + GetWindowLongPtr(hwndDlg, DWLP_USER) ); + } + } + + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// Function: DumpBindings +// +// Purpose: Write the binding information. +// +// Arguments: +// lpszFile [in] Name of the file in which to write. +// +// Returns: None +// +// Notes: +// + +VOID +DumpBindings ( + _In_ LPWSTR lpszFile) +{ + FILE *fp; + errno_t err; + + err = _wfopen_s( &fp, + lpszFile, + L"w" ); + + if ( err != 0 || fp == NULL ) { + + ErrMsg( 0, + L"Unable to open %s.", + lpszFile ); + } + else { + WriteBindings( fp ); + + fclose( fp ); + } + + return; +} + +// +// Function: InstallSelectedComponentType +// +// Purpose: Install a network component. +// +// Arguments: +// hwndDlg [in] Handle to Install dialog box. +// lpszInfFile [in] Inf file of the network component. +// +// Returns: None +// +// Notes: +// If lpszInfFile is NULL, network components are installed from the +// system directory. +// + +VOID +InstallSelectedComponentType ( + HWND hwndDlg, + _In_opt_ LPWSTR lpszInfFile) +{ + HWND hwndTree = NULL; + HTREEITEM hItem = NULL; + LPARAM lParam; + HCURSOR hPrevCursor = NULL; + HCURSOR hWaitCursor = NULL; + HWND hwndFocus = NULL; + DWORD dwType; + BOOL fEnable; + HRESULT hr; + + hwndTree = GetDlgItem( hwndDlg, + IDT_COMPONENT_LIST ); + + // + // Find out the type of component selected. + // + + hItem = TreeView_GetSelection( hwndTree ); + + if ( hItem ) { + if ( GetItemInfo( hwndTree, + hItem, + &lParam, + &dwType, + &fEnable) ) { + + // + // Disable the install dialog controls. + // + + hwndFocus = GetFocus(); + + hWaitCursor = LoadCursor( NULL, + IDC_WAIT ); + if ( hWaitCursor ) { + hPrevCursor = SetCursor( hWaitCursor ); + } + + EnableWindow( hwndTree, FALSE ); + EnableWindow( GetDlgItem(hwndDlg,IDB_INSTALL), + FALSE ); + EnableWindow( GetDlgItem(hwndDlg,IDB_BROWSE), + FALSE ); + EnableWindow( GetDlgItem(hwndDlg,IDB_CLOSE), + FALSE ); + + if ( lpszInfFile ) { + + LPWSTR lpszPnpID; + + // + // Inf file name specified, install the network component + // from this file. + // + + hr = GetPnpID( lpszInfFile, &lpszPnpID ); + + if ( hr == S_OK ) { + + hr = InstallSpecifiedComponent( lpszInfFile, + lpszPnpID, + pguidNetClass[(UINT)lParam] ); + + CoTaskMemFree( lpszPnpID ); + } + else { + ErrMsg( hr, + L"Error reading the INF file %s.", + lpszInfFile ); + } + } + else { + + // + // Install from system directory. + // + + hr = InstallComponent( hwndTree, + pguidNetClass[(UINT)lParam] ); + } + + if ( hWaitCursor ) { + SetCursor( hPrevCursor ); + } + + switch( hr ) { + + case S_OK: + MessageBoxW( + hwndDlg, + L"Component installed successfully.", + L"Network Component Installation", + MB_OK | MB_ICONINFORMATION ); + SetWindowLongPtr( hwndDlg, + DWLP_USER, + (LONG_PTR)TRUE ); + break; + + case NETCFG_S_REBOOT: + MessageBoxW( + hwndDlg, + L"Component installed successfully: " + L"Reboot required.", + L"Network Component Installation", + MB_OK | MB_ICONINFORMATION ); + SetWindowLongPtr( hwndDlg, + DWLP_USER, + (LONG_PTR)TRUE ); + + } + + // + // Enable the install dialog controls. + // + + EnableWindow( hwndTree, TRUE ); + EnableWindow( GetDlgItem(hwndDlg,IDB_INSTALL), + TRUE ); + EnableWindow( GetDlgItem(hwndDlg,IDB_BROWSE), + TRUE ); + EnableWindow( GetDlgItem(hwndDlg,IDB_CLOSE), + TRUE ); + + SetFocus( hwndFocus ); + } + } + + return; +} + +// +// Function: GetPnpID +// +// Purpose: Retrieve PnpID from an inf file. +// +// Arguments: +// lpszInfFile [in] Inf file to search. +// lppszPnpID [out] PnpID found. +// +// Returns: TRUE on success. +// +// Notes: +// + +HRESULT +GetPnpID ( + _In_ LPWSTR lpszInfFile, + _Outptr_ LPWSTR *lppszPnpID) +{ + HINF hInf; + LPWSTR lpszModelSection; + HRESULT hr; + + *lppszPnpID = NULL; + + hInf = SetupOpenInfFileW( lpszInfFile, + NULL, + INF_STYLE_WIN4, + NULL ); + + if ( hInf == INVALID_HANDLE_VALUE ) + { + + return HRESULT_FROM_WIN32(GetLastError()); + } + + // + // Read the Model section name from Manufacturer section. + // + + hr = GetKeyValue( hInf, + L"Manufacturer", + NULL, + 1, + &lpszModelSection ); + + if ( SUCCEEDED(hr) ) + { + + // + // Read PnpID from the Model section. + // + + hr = GetKeyValue( hInf, + lpszModelSection, + NULL, + 2, + lppszPnpID ); + + CoTaskMemFree( lpszModelSection ); + } + + SetupCloseInfFile( hInf ); + + return hr; +} + +// +// Function: GetKeyValue +// +// Purpose: Retrieve the value of a key from the inf file. +// +// Arguments: +// hInf [in] Inf file handle. +// lpszSection [in] Section name. +// lpszKey [in] Key name. +// dwIndex [in] Key index. +// lppszValue [out] Key value. +// +// Returns: S_OK on success, otherwise and error code. +// +// Notes: +// + +HRESULT +GetKeyValue ( + HINF hInf, + _In_ LPCWSTR lpszSection, + _In_opt_ LPCWSTR lpszKey, + DWORD dwIndex, + _Outptr_ LPWSTR *lppszValue) +{ + INFCONTEXT infCtx; + __range(0, 512) DWORD dwSizeNeeded; + HRESULT hr; + + *lppszValue = NULL; + + if ( SetupFindFirstLineW(hInf, + lpszSection, + lpszKey, + &infCtx) == FALSE ) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + if ( SetupGetStringFieldW(&infCtx, + dwIndex, + NULL, + 0, + &dwSizeNeeded) ) + { + *lppszValue = (LPWSTR)CoTaskMemAlloc( sizeof(WCHAR) * dwSizeNeeded ); + + if ( !*lppszValue ) + { + return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + } + + if ( SetupGetStringFieldW(&infCtx, + dwIndex, + *lppszValue, + dwSizeNeeded, + NULL) == FALSE ) + { + + hr = HRESULT_FROM_WIN32(GetLastError()); + + CoTaskMemFree( *lppszValue ); + *lppszValue = NULL; + } + else + { + hr = S_OK; + } + } + else + { + DWORD dwErr = GetLastError(); + hr = HRESULT_FROM_WIN32(dwErr); + } + + return hr; +} + +// +// Function: UninstallSelectedComponent +// +// Purpose: Uninstall the selected network component. +// +// Arguments: +// hwndDlg [in] Window handle of the uninstall dialog box. +// +// Returns: TRUE on success. +// +// Notes: +// + +VOID UninstallSelectedComponent (HWND hwndDlg) +{ + HWND hwndTree = NULL; + HTREEITEM hItem = NULL; + LPARAM lParam; + HCURSOR hPrevCursor = NULL; + HCURSOR hWaitCursor = NULL; + DWORD dwType; + BOOL fEnable; + HRESULT hr; + + hwndTree = GetDlgItem( hwndDlg, + IDT_COMPONENT_LIST ); + + // + // Get the selected item to get its lParam which is the + // PnpID of the network component. + // + + hItem = TreeView_GetSelection( hwndTree ); + + if ( hItem ) { + if ( GetItemInfo( hwndTree, + hItem, + &lParam, + &dwType, + &fEnable) ) { + + hWaitCursor = LoadCursor( NULL, + IDC_WAIT ); + if ( hWaitCursor ) { + hPrevCursor = SetCursor( hWaitCursor ); + } + + EnableWindow( hwndTree, FALSE ); + EnableWindow( GetDlgItem(hwndDlg,IDB_REMOVE), + FALSE ); + EnableWindow( GetDlgItem(hwndDlg,IDB_CLOSE), + FALSE ); + + // + // Uninstall the selected component. + // + + hr = UninstallComponent( (LPWSTR)lParam ); + + + if ( hWaitCursor ) { + SetCursor( hPrevCursor ); + } + + switch( hr ) { + + case S_OK: + MessageBoxW( + hwndDlg, + L"Uninstallation successful.", + L"Network Component Uninstallation", + MB_OK | MB_ICONINFORMATION ); + + CoTaskMemFree( (LPVOID)lParam ); + TreeView_DeleteItem( hwndTree, + hItem ); + + SetWindowLongPtr( hwndDlg, + DWLP_USER, + (LONG_PTR)TRUE ); + break; + + case NETCFG_S_REBOOT: + MessageBoxW( + hwndDlg, + L"Uninstallation successful: " + L"Reboot required.", + L"Network Component Uninstallation", + MB_OK | MB_ICONINFORMATION ); + + CoTaskMemFree( (LPVOID)lParam ); + TreeView_DeleteItem( hwndTree, + hItem ); + + SetWindowLongPtr( hwndDlg, + DWLP_USER, + (LONG_PTR)TRUE ); + } + + EnableWindow( hwndTree, TRUE ); + EnableWindow( GetDlgItem(hwndDlg,IDB_REMOVE), + TRUE ); + EnableWindow( GetDlgItem(hwndDlg,IDB_CLOSE), + TRUE ); + } + } + + return; +} + +// +// Function: ExpandCollapseAll +// +// Purpose: Expand or collapse a tree. +// +// Arguments: +// hwndTree [in] Window handle of the tree. +// hTreeItem [in] Handle of root item. +// uiFlag [in] Flag indicating whether to expand or collapse. +// +// Returns: None. +// +// Notes: +// + +VOID ExpandCollapseAll (HWND hwndTree, + HTREEITEM hTreeItem, + UINT uiFlag) +{ + HTREEITEM hItemChild; + + hItemChild = TreeView_GetChild( hwndTree, + hTreeItem ); + + if ( hItemChild ) { + + // + // If the root has one or more children, expand/collapse the root. + // + + TreeView_Expand( hwndTree, + hTreeItem, + uiFlag ); + } + + while ( hItemChild ) { + + // + // Expand/collapse all the children. + // + + ExpandCollapseAll( hwndTree, + hItemChild, + uiFlag ); + + // + // Expand/collapse all the siblings. + // + + hItemChild = TreeView_GetNextSibling( hwndTree, + hItemChild ); + } + + return; +} + +// +// Function: GetFileName +// +// Purpose: Prompt for a filename. +// +// Arguments: +// hwndDlg [in] Window handle of the parent. +// lpszFilter [in] See documentation for GetOpenFileName. +// lpszTitle [in] See documentation for GetOpenFileName. +// dwFlags [in] See documentation for GetOpenFileName. +// lpszFile [out] See documentation for GetOpenFileName. +// Supplied buffer must be at least MAX_PATH+1 WCHARS +// +// Returns: See documentation for GetOpenFileName. +// +// Notes: +// + +BOOL +GetFileName ( + HWND hwndDlg, + _In_opt_ LPWSTR lpszFilter, + _In_ LPWSTR lpszTitle, + DWORD dwFlags, + _Out_writes_(MAX_PATH+1) LPWSTR lpszFile, + _In_opt_ LPWSTR lpszDefExt, + BOOL fSave) +{ + OPENFILENAMEW ofn; + + lpszFile[0] = NULL; + + ZeroMemory( &ofn, sizeof(OPENFILENAMEW) ); + ofn.lStructSize = sizeof(OPENFILENAMEW); + ofn.hwndOwner = hwndDlg; + ofn.lpstrFilter = lpszFilter; + ofn.lpstrFile = lpszFile; + ofn.lpstrDefExt = lpszDefExt; + ofn.nMaxFile = MAX_PATH+1; + ofn.lpstrTitle = lpszTitle; + ofn.Flags = dwFlags; + + if ( fSave ) + { + return GetSaveFileName( &ofn ); + } + else + { + return GetOpenFileName( &ofn ); + } +} + +// +// Function: ProcessRightClick +// +// Purpose: Handle righ mouse button click. +// +// Arguments: +// lpnm [in] LPNMHDR info +// +// Returns: None. +// +// Notes: +// + +VOID ProcessRightClick (LPNMHDR lpnm) +{ + HTREEITEM hItemSelected; + LPARAM lParam; + DWORD dwItemType; + BOOL fEnabled; + + // + // Determine the item on which user clicked the right mouse button. + // + + hItemSelected = TreeView_GetDropHilight( lpnm->hwndFrom ); + + if ( !hItemSelected ) { + hItemSelected = TreeView_GetSelection( lpnm->hwndFrom ); + } + else { + + // + // User has right-clicked an unselected item, make that a selected + // item. + // + + TreeView_Select( lpnm->hwndFrom, + hItemSelected, + TVGN_CARET ); + } + + if ( hItemSelected ) { + + // + // Get the lParam of the selected node in the tree which points to inf id or + // pathtoken name depending on if the node represents a network component or + // a binding path. + // + + if ( GetItemInfo(lpnm->hwndFrom, + hItemSelected, + &lParam, + &dwItemType, + &fEnabled) ) { + + if ( dwItemType & ITEM_NET_COMPONENTS ) { + + // + // Show the shortcut menu of operations for a network component. + // + + ShowComponentMenu( lpnm->hwndFrom, + hItemSelected, + lParam); + } + else { + if ( dwItemType & ITEM_NET_BINDINGS ) { + + // + // Show the shortcut menu of operations for a binding path. + // + + ShowBindingPathMenu( lpnm->hwndFrom, + hItemSelected, + lParam, + fEnabled ); + } + } + } + } + + return; +} + +// +// Function: ShowComponentMenu +// +// Purpose: Show shortcut menu of options for a network component. +// +// Arguments: +// hwndOwner [in] Owner window. +// hItem [in] Selected item representing a network component. +// lParam [in] PnpID of the network component. +// +// Returns: None. +// +// Notes: +// + +VOID ShowComponentMenu (HWND hwndOwner, + HTREEITEM hItem, + LPARAM lParam) +{ + ULONG ulSelection; + POINT pt; + + GetCursorPos( &pt ); + ulSelection = (ULONG)TrackPopupMenu( hComponentSubMenu, + TPM_RIGHTALIGN | TPM_BOTTOMALIGN | + TPM_NONOTIFY | TPM_RETURNCMD | + TPM_RIGHTBUTTON, + pt.x, + pt.y, + 0, + hwndOwner, + NULL ); + + if ( ulSelection ) { + + // + // Do the selected action. + // + + HandleComponentOperation( hwndOwner, + ulSelection, + hItem, + lParam ); + } + + return; +} + +// +// Function: ShowBindingPathMenu +// +// Purpose: Show shortcut menu of options for a network component. +// +// Arguments: +// hwndOwner [in] Owner window. +// hItem [in] Selected item representing a binding path. +// lParam [in] PnpID of the network component. +// fEnabled [in] TRUE when the path is enabled. +// +// Returns: None. +// +// Notes: +// + + +VOID ShowBindingPathMenu (HWND hwndOwner, + HTREEITEM hItem, + LPARAM lParam, + BOOL fEnabled) +{ + MENUITEMINFOW menuItemInfo; + ULONG ulSelection; + POINT pt; + + // + // Build the shortcut menu depending on whether path is + // disabled or enabled. + // + + ZeroMemory( &menuItemInfo, + sizeof(MENUITEMINFOW) ); + + menuItemInfo.cbSize = sizeof( MENUITEMINFOW ); + menuItemInfo.fMask = MIIM_TYPE | MIIM_ID; + menuItemInfo.fType = MFT_STRING; + menuItemInfo.fState = MFS_ENABLED; + + if ( fEnabled ) { + menuItemInfo.dwTypeData = MENUITEM_DISABLE; + menuItemInfo.wID = IDI_DISABLE; + } + else { + menuItemInfo.dwTypeData = MENUITEM_ENABLE; + menuItemInfo.wID = IDI_ENABLE; + } + + SetMenuItemInfoW( hBindingPathSubMenu, + 0, + TRUE, + &menuItemInfo ); + + GetCursorPos( &pt ); + ulSelection = (ULONG)TrackPopupMenu( hBindingPathSubMenu, + TPM_RIGHTALIGN | TPM_BOTTOMALIGN | + TPM_NONOTIFY | TPM_RETURNCMD | + TPM_RIGHTBUTTON, + pt.x, + pt.y, + 0, + hwndOwner, + NULL ); + + if ( ulSelection ) { + + // + // Do the selected action. + // + + HandleBindingPathOperation( hwndOwner, + ulSelection, + hItem, + lParam ); + } + + return; +} + +// +// Function: GetItemInfo +// +// Purpose: Returns information about an item. +// +// Arguments: +// hwndTree [in] Window handle of the tree. +// hItem [in] Item handle. +// lParam [out] lParam +// lpdwItemType [out] Type, binding path or network component. +// fEnabled [out] TRUE if the binding path or component is enabled. +// +// Returns: TRUE on sucess. +// +// Notes: +// + +BOOL GetItemInfo (HWND hwndTree, + HTREEITEM hItem, + LPARAM *lParam, + LPDWORD lpdwItemType, + BOOL *fEnabled) +{ + TVITEMW tvItem; + int iImage; + BOOL fSuccess; + + + fSuccess = FALSE; + + // + // Get item's information. + // + + ZeroMemory( &tvItem, + sizeof(TVITEMW) ); + tvItem.hItem = hItem; + tvItem.mask = TVIF_PARAM | TVIF_IMAGE | TVIF_STATE; + tvItem.stateMask = TVIS_OVERLAYMASK ; + + if ( TreeView_GetItem(hwndTree, + &tvItem) ) { + + *lParam = tvItem.lParam; + + if ( SetupDiGetClassImageIndex(&ClassImageListData, + &GUID_DEVCLASS_SYSTEM, + &iImage) ) { + + // + // Is it a binding path? + // + + if ( tvItem.iImage == iImage ) { + *lpdwItemType = ITEM_NET_BINDINGS; + + *fEnabled = !(TVIS_OVERLAYMASK & tvItem.state); + + fSuccess = TRUE; + } + else { + + // + // Item is a network component. + // + + if ( SetupDiGetClassImageIndex(&ClassImageListData, + &GUID_DEVCLASS_NET, + &iImage) ) { + + if ( tvItem.iImage == iImage ) { + *lpdwItemType = ITEM_NET_ADAPTERS; + } + else { + *lpdwItemType = ITEM_NET_COMPONENTS; + } + + *fEnabled = !(TVIS_OVERLAYMASK & tvItem.state); + + fSuccess = TRUE; + } + else { + ErrMsg( HRESULT_FROM_WIN32(GetLastError()), + L"Couldn't load the images of network adapters." ); + } + } + } + else { + ErrMsg( HRESULT_FROM_WIN32(GetLastError()), + L"Couldn't load the images of system devices." ); + } + } + + return fSuccess; +} + +// +// Function: AddBindNameToTree +// +// Purpose: Adds an item representing the binding path. +// +// Arguments: +// pncbp [in] Binding path to add. +// hwndTree [in] Tree handle. +// hParent [in] Parent item. +// ulIndex [in] Index of the binding path. +// +// Returns: Handle of the item added on success, otherwise NULL. +// +// Notes: +// + +HTREEITEM AddBindNameToTree (INetCfgBindingPath *pncbp, + HWND hwndTree, + HTREEITEM hParent, + ULONG ulIndex) +{ + WCHAR lpszBindName[40]; + LPWSTR lpszPathToken; + HTREEITEM hTreeItem; + TV_INSERTSTRUCTW tvInsertStruc; + HRESULT hr; + + hTreeItem = NULL; + + // + // Store the path token as lParam. + // + + hr = pncbp->GetPathToken( &lpszPathToken ); + + if ( hr == S_OK ) { + + StringCchPrintfW (lpszBindName, + celems(lpszBindName), + L"Binding Path %d", + ulIndex ); + + ZeroMemory( + &tvInsertStruc, + sizeof(TV_INSERTSTRUCTW) ); + + tvInsertStruc.hParent = hParent; + + tvInsertStruc.hInsertAfter = TVI_LAST; + + tvInsertStruc.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | + TVIF_SELECTEDIMAGE | TVIF_STATE; + + tvInsertStruc.item.pszText = lpszBindName; + + SetupDiGetClassImageIndex( &ClassImageListData, + &GUID_DEVCLASS_SYSTEM, + &tvInsertStruc.item.iImage ); + + tvInsertStruc.item.iSelectedImage = tvInsertStruc.item.iImage; + + tvInsertStruc.item.stateMask = TVIS_OVERLAYMASK; + + if ( pncbp->IsEnabled() == S_FALSE ) { + tvInsertStruc.item.state = INDEXTOOVERLAYMASK( + IDI_DISABLED_OVL - IDI_CLASSICON_OVERLAYFIRST + 1); + } + + tvInsertStruc.item.lParam = (LPARAM)lpszPathToken; + + hTreeItem = TreeView_InsertItem( hwndTree, + &tvInsertStruc ); + + if ( !hTreeItem ) { + ErrMsg( hr, + L"Couldn't add the binding path %d to the list." + L" The binding path will not be shown.", ulIndex ); + + CoTaskMemFree( lpszPathToken ); + } + } + else { + ErrMsg( hr, + L"Couldn't get the PathToken of the binding path %d." + L" The binding path will not be shown.", ulIndex ); + } + + return hTreeItem; +} + +// +// Function: AddToTree +// +// Purpose: Adds an item representing the network component. +// +// Arguments: +// hwndTree [in] Tree handle. +// hParent [in] Parent item. +// pncc [in] Network component. +// +// Returns: Handle of the item added on success, otherwise NULL. +// +// Notes: +// + +HTREEITEM AddToTree (HWND hwndTree, + HTREEITEM hParent, + INetCfgComponent *pncc) +{ + return AddToTreeEx( hwndTree, + hParent, + pncc, + FALSE ); +} + +// +// Function: AddToTree +// +// Purpose: Adds an item representing the network component. +// +// Arguments: +// hwndTree [in] Tree handle. +// hParent [in] Parent item. +// pncc [in] Network component. +// fUsePnpId [in] if TRUE, pnp device instance id is used to indentify the component, otherwise inf id. +// +// Returns: Handle of the item added on success, otherwise NULL. +// +// Notes: +// + +HTREEITEM AddToTreeEx (HWND hwndTree, + HTREEITEM hParent, + INetCfgComponent *pncc, + BOOL fUsePnpId) +{ + LPWSTR lpszItemName; + LPWSTR lpszId; + GUID guidClass; + BOOL fEnabled; + ULONG ulStatus; + HTREEITEM hTreeItem; + TV_INSERTSTRUCTW tvInsertStruc; + HRESULT hr; + + hTreeItem = NULL; + + hr = pncc->GetDisplayName( &lpszItemName ); + + if ( hr == S_OK ) { + + // + // Get the inf id or pnp instance id of the network component. We store it at lParam + // and use it later to retrieve its interface pointer. + // + + if ( fUsePnpId ) + { + hr = pncc->GetPnpDevNodeId( &lpszId ); + } + else + { + hr = pncc->GetId( &lpszId ); + } + + if ( hr == S_OK ) { + + // + // If it is a network adapter then, find out if it enabled/disabled. + // + + hr = pncc->GetClassGuid( &guidClass ); + + if ( hr == S_OK ) { + if ( IsEqualGUID(guidClass, GUID_DEVCLASS_NET) ) { + hr = pncc->GetDeviceStatus( &ulStatus ); + fEnabled = ulStatus == 0; + } + else { + fEnabled = TRUE; + } + } + else { + + // + // We can't get the status, so assume that it is disabled. + // + + fEnabled = FALSE; + } + + ZeroMemory( + &tvInsertStruc, + sizeof(TV_INSERTSTRUCTW) ); + + tvInsertStruc.hParent = hParent; + + tvInsertStruc.hInsertAfter = TVI_LAST; + + tvInsertStruc.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | + TVIF_SELECTEDIMAGE | TVIF_STATE; + + tvInsertStruc.item.pszText = lpszItemName; + + SetupDiGetClassImageIndex( &ClassImageListData, + &guidClass, + &tvInsertStruc.item.iImage ); + + tvInsertStruc.item.iSelectedImage = tvInsertStruc.item.iImage; + + tvInsertStruc.item.stateMask = TVIS_OVERLAYMASK; + + if ( fEnabled == FALSE ) { + tvInsertStruc.item.state = INDEXTOOVERLAYMASK( + IDI_DISABLED_OVL - IDI_CLASSICON_OVERLAYFIRST + 1); + } + + tvInsertStruc.item.lParam = (LPARAM)lpszId; + + hTreeItem = TreeView_InsertItem( hwndTree, + &tvInsertStruc ); + if ( !hTreeItem ) { + ErrMsg( hr, + L"Failed to add %s to the list.", + lpszItemName ); + + CoTaskMemFree( lpszId ); + } + } + else { + ErrMsg( hr, + L"Couldn't get the inf id of %s." + L" It will not be added to the list.", + lpszItemName ); + } + + CoTaskMemFree( lpszItemName ); + } + else { + ErrMsg( hr, + L"Couldn't get the display name of a network component." + L" It will not be added to the list." ); + } + + return hTreeItem; +} + +// +// Function: RefreshAll +// +// Purpose: Refreshes the main dialog box. +// +// Arguments: +// hwndDlg [in] Dialog box handle. +// +// Returns: None. +// +// Notes: +// + +VOID RefreshAll (HWND hwndDlg) +{ + HWND hwndTypeList; + INT iSelected; + + // + // Find the selected network component type. + // + + hwndTypeList = GetDlgItem( hwndDlg, + IDL_COMPONENT_TYPES ); + + iSelected = (int)SendMessage( hwndTypeList, + CB_GETCURSEL, + 0, + 0 ); + + if ( iSelected != CB_ERR ) { + + // + // Before deleting the list in the tree, free the buffer + // associated with each item. The buffer holds either the + // INF Id or the pathtoken depending on whether it is a + // network component or a binding path. + // + + ReleaseMemory( GetDlgItem(hwndDlg, IDT_BINDINGS), + TVI_ROOT ); + + TreeView_DeleteItem ( + GetDlgItem(hwndDlg, IDT_BINDINGS), + TVI_ROOT ); + + // + // Repopulate the tree with the selected network compnent + // type. + // + + EnumNetBindings( GetDlgItem(hwndDlg, IDT_BINDINGS), + (UINT)iSelected ); + + } + + return; +} + +// +// Function: RefreshItemState +// +// Purpose: Refreshes the specified item. +// +// Arguments: +// hwndTree [in] Dialog box handle. +// hItem [in] Item to refresh. +// fEnable [in] TRUE if component is enabled. +// +// Returns: None. +// +// Notes: +// + +VOID RefreshItemState (HWND hwndTree, + HTREEITEM hItem, + BOOL fEnable) +{ + TVITEMW tvItem; + + ZeroMemory( &tvItem, + sizeof(TVITEMW) ); + + tvItem.hItem = hItem; + tvItem.mask = TVIF_STATE; + tvItem.stateMask = TVIS_OVERLAYMASK; + + if ( fEnable ) + tvItem.state = INDEXTOOVERLAYMASK( 0 ); + else + tvItem.state = INDEXTOOVERLAYMASK( + IDI_DISABLED_OVL - IDI_CLASSICON_OVERLAYFIRST + 1); + TreeView_SetItem( hwndTree, + &tvItem ); + return; +} + +// +// Function: RefreshBindings +// +// Purpose: Refreshes bindings of a specific component. +// +// Arguments: +// hwndBindUnBindDlg [in] Dialog box handle. +// lpszInfId [in] PnpID of the component whose bindings changed. +// +// Returns: None. +// +// Notes: +// + +VOID +RefreshBindings ( + HWND hwndBindUnBindDlg, + _In_ LPWSTR lpszInfId) +{ + INetCfg *pnc; + INetCfgComponent *pncc; + HWND hwndParent; + HWND hwndTree; + HTREEITEM hItem; + HRESULT hr; + + + hwndParent = GetParent( hwndBindUnBindDlg ); + hwndTree = GetDlgItem( hwndParent, + IDT_BINDINGS ); + + hItem = TreeView_GetSelection( hwndTree ); + + hr = HrGetINetCfg( FALSE, + APP_NAME, + &pnc, + NULL ); + + if ( hr == S_OK ) { + + hr = pnc->FindComponent( lpszInfId, + &pncc ); + + if ( hr == S_OK ) { + + // + // Delete all the children. + // + + ReleaseMemory( hwndTree, + hItem ); + + DeleteChildren( hwndTree, + hItem ); + + ListBindings( pncc, + hwndTree, + hItem ); + + ReleaseRef( pncc ); + } + + HrReleaseINetCfg( pnc, + FALSE ); + } + + return; +} + +// +// Function: ReleaseMemory +// +// Purpose: Free memory associated with each item in the tree. +// +// Arguments: +// hwndTree [in] Tree handle. +// hTreeItem [in] Root item. +// +// Returns: None. +// +// Notes: +// +// Each node of the tree represents a network component or a binding path. +// At each node, lParam points to an allocated buffer wherein we store the +// inf id if it is a network component or pathtoken name if it is a binding +// path. +// +// + +VOID ReleaseMemory (HWND hwndTree, + HTREEITEM hTreeItem) +{ + HTREEITEM hItemChild; + TVITEMW tvItem; + + hItemChild = TreeView_GetChild( hwndTree, + hTreeItem ); + + while ( hItemChild ) { + + ZeroMemory( + &tvItem, + sizeof(TVITEMW) ); + + tvItem.hItem = hItemChild; + tvItem.mask = TVIF_PARAM; + + TreeView_GetItem( hwndTree, + &tvItem ); + + // + // It should never be NULL but just in case... + // + + if ( tvItem.lParam ) { + CoTaskMemFree( (LPVOID)tvItem.lParam ); + + } + + ReleaseMemory( hwndTree, hItemChild ); + + hItemChild = TreeView_GetNextSibling( hwndTree, + hItemChild ); + } + + return; +} + +// +// Function: DeleteChildren +// +// Purpose: Delete childen of a specific item. +// +// Arguments: +// hwndTree [in] Tree handle. +// hTreeItem [in] Parent item. +// +// Returns: None. +// +// Notes: +// + +VOID DeleteChildren (HWND hwndTree, + HTREEITEM hTreeItem) +{ + HTREEITEM hItemChild; + HTREEITEM hItemSibling; + + hItemChild = TreeView_GetChild( hwndTree, + hTreeItem ); + + while ( hItemChild ) { + + DeleteChildren( hwndTree, + hItemChild ); + + hItemSibling = TreeView_GetNextSibling( hwndTree, + hItemChild ); + TreeView_DeleteItem( hwndTree, + hItemChild ); + + hItemChild = hItemSibling; + } + + return; +} + +// +// Function: InsertItem +// +// Purpose: Insert text for each network component type. +// +// Arguments: +// hwndTree [in] Tree handle. +// uiType [in] Item type, protocol, client, service. +// +// Returns: Item handle on success, otherwise NULL. +// +// Notes: +// + +HTREEITEM InsertItem (HWND hwndTree, + UINT uiType) +{ + TV_INSERTSTRUCTW tvInsertStruc; + + ZeroMemory( + &tvInsertStruc, + sizeof(TV_INSERTSTRUCTW) ); + + tvInsertStruc.hParent = TVI_ROOT; + + tvInsertStruc.hInsertAfter = TVI_LAST; + + tvInsertStruc.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | + TVIF_SELECTEDIMAGE; + + + switch( uiType ) { + + case CLIENTS_SELECTED: + tvInsertStruc.item.pszText = L"Client"; + break; + + case SERVICES_SELECTED: + tvInsertStruc.item.pszText = L"Service"; + break; + + default: + tvInsertStruc.item.pszText = L"Protocol"; + break; + } + + SetupDiGetClassImageIndex( &ClassImageListData, + pguidNetClass[uiType], + &tvInsertStruc.item.iImage ); + + tvInsertStruc.item.iSelectedImage = tvInsertStruc.item.iImage; + + tvInsertStruc.item.lParam = (LPARAM)uiType; + + return TreeView_InsertItem( hwndTree, + &tvInsertStruc ); + +} + +// +// Function: UpdateComponentTypeList +// +// Purpose: Insert text for each network component type. +// +// Arguments: +// hwndTypeList [in] ListView handle. +// +// Returns: TRUE on success. +// +// Notes: +// + +BOOL UpdateComponentTypeList (HWND hwndTypeList) +{ + UINT i; + + for (i=0; i < 3; ++i) { + SendMessage( hwndTypeList, + CB_ADDSTRING, + (WPARAM)0, + (LPARAM)lpszNetClass[i] ); + } + + SendMessage( hwndTypeList, + CB_SETCURSEL, + (WPARAM)DEFAULT_COMPONENT_SELECTED, + (LPARAM)0 ); + return TRUE; +} + +// +// Function: ErrMsg +// +// Purpose: Insert text for each network component type. +// +// Arguments: +// hr [in] Error code. +// +// Returns: None. +// +// Notes: +// + +VOID ErrMsg (HRESULT hr, + LPCWSTR lpFmt, + ...) +{ + + LPWSTR lpSysMsg = NULL; + WCHAR buf[400]; + size_t offset; + va_list vArgList; + + + if ( hr != 0 ) { + StringCchPrintfW ( buf, + celems(buf), + L"Error %#lx: ", + hr ); + } + else { + buf[0] = 0; + } + + offset = wcslen( buf ); + + va_start( vArgList, + lpFmt ); + StringCchVPrintfW ( buf+offset, + celems(buf) - offset, + lpFmt, + vArgList ); + + va_end( vArgList ); + + if ( hr != 0 ) { + FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + hr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&lpSysMsg, + 0, + NULL ); + if ( lpSysMsg ) { + + offset = wcslen( buf ); + + StringCchPrintfW ( buf+offset, + celems(buf) - offset, + L"\n\nPossible cause:\n\n" ); + + offset = wcslen( buf ); + + StringCchCatW ( buf+offset, + celems(buf) - offset, + lpSysMsg ); + + LocalFree( (HLOCAL)lpSysMsg ); + } + + MessageBoxW( NULL, + buf, + L"Error", + MB_ICONERROR | MB_OK ); + } + else { + MessageBoxW( NULL, + buf, + L"BindView", + MB_ICONINFORMATION | MB_OK ); + } + + return; +} diff --git a/network/config/bindview/BINDVIEW.H b/network/config/bindview/BINDVIEW.H new file mode 100644 index 00000000..2753b753 --- /dev/null +++ b/network/config/bindview/BINDVIEW.H @@ -0,0 +1,250 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 2001. +// +// File: B I N D V I E W . H +// +// Contents: Function Prototypes +// +// Notes: +// +// Author: Alok Sinha 15-May-01 +// +//---------------------------------------------------------------------------- + +#ifndef _BINDVIEW_H_INCLUDED + +#define _BINDVIEW_H_INCLUDED + +#include +#include +#include +#include +#include +#include // For common controls, e.g. Tree +#include +#include +#include + +#include "NetCfgAPI.h" +#include "resource.h" +#include + +__user_code; // Annotation to specify PreFast analysis mode + +#define celems(_x) (sizeof(_x) / sizeof(_x[0])) + +#define ID_STATUS 100 +#define APP_NAME L"BindView" + +#define CLIENTS_SELECTED 0 +#define SERVICES_SELECTED 1 +#define PROTOCOLS_SELECTED 2 +#define ADAPTERS_SELECTED 3 + +#define ITEM_NET_COMPONENTS 1 +#define ITEM_NET_BINDINGS 2 +#define ITEM_NET_ADAPTERS 4 + +#define DEFAULT_COMPONENT_SELECTED CLIENTS_SELECTED + +#define WM_NO_COMPONENTS WM_USER+1 + +#define MENUITEM_ENABLE L"Enable" +#define MENUITEM_DISABLE L"Disable" + +extern HINSTANCE hInstance; +extern const GUID *pguidNetClass []; +extern LPWSTR lpszNetClass []; + +typedef struct _BIND_UNBIND_INFO { + LPWSTR lpszInfId; + BOOL fBindTo; +} BIND_UNBIND_INFO, *LPBIND_UNBIND_INFO; + +// +// Functions defined in bindview.cpp +// + +INT_PTR CALLBACK MainDlgProc (HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK BindComponentDlg (HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK InstallDlg (HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +INT_PTR CALLBACK UninstallDlg (HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +VOID DumpBindings ( _In_ LPWSTR lpszFile); + +VOID InstallSelectedComponentType (HWND hwndDlg, + _In_opt_ LPWSTR lpszInfFile); + +HRESULT GetPnpID ( _In_ LPWSTR lpszInfFile, + _Outptr_ LPWSTR *lppszPnpID); + +HRESULT GetKeyValue (HINF hInf, + _In_ LPCWSTR lpszSection, + _In_opt_ LPCWSTR lpszKey, + DWORD dwIndex, + _Outptr_ LPWSTR *lppszValue); + +VOID UninstallSelectedComponent (HWND hwndDlg); + +VOID ExpandCollapseAll (HWND hwndTree, + HTREEITEM hTreeItem, + UINT uiFlag); + +BOOL GetFileName (HWND hwndDlg, + _In_opt_ LPWSTR lpszFilter, + _In_ LPWSTR lpszTitle, + DWORD dwFlags, + _Out_writes_(MAX_PATH+1) LPWSTR lpszFile, + _In_opt_ LPWSTR lpszDefExt, + BOOL fSave); + +VOID ProcessRightClick (LPNMHDR lpnm); + +VOID ShowComponentMenu (HWND hwndOwner, + HTREEITEM hItem, + LPARAM lParam); + +VOID ShowBindingPathMenu (HWND hwndOwner, + HTREEITEM hItem, + LPARAM lParam, + BOOL fEnabled); + +BOOL GetItemInfo (HWND hwndTree, + HTREEITEM hItem, + LPARAM *lParam, + LPDWORD lpdwItemType, + BOOL *fEnabled); + +HTREEITEM AddBindNameToTree (INetCfgBindingPath *pncbp, + HWND hwndTree, + HTREEITEM hParent, + ULONG ulIndex); + +HTREEITEM AddToTree (HWND hwndTree, + HTREEITEM hParent, + INetCfgComponent *pncc); + +HTREEITEM AddToTreeEx (HWND hwndTree, + HTREEITEM hParent, + INetCfgComponent *pncc, + BOOL fUsePnpId); + +VOID RefreshAll (HWND hwndDlg); + +VOID RefreshItemState (HWND hwndTree, + HTREEITEM hItem, + BOOL fEnable); + +VOID RefreshBindings (HWND hwndTree, + _In_ LPWSTR lpszInfId); + +VOID ReleaseMemory (HWND hwndTree, + HTREEITEM hTreeItem); + + +VOID DeleteChildren (HWND hwndTree, + HTREEITEM hTreeItem); + +HTREEITEM InsertItem (HWND hwndTree, + UINT uiType); + +BOOL UpdateComponentTypeList (HWND hwndTypeList); + + +VOID ErrMsg (HRESULT hr, + LPCWSTR lpFmt, + ...); + +// +// Functions defined in component.cpp +// + +VOID HandleComponentOperation (HWND hwndOwner, + ULONG ulSelection, + HTREEITEM hItem, + LPARAM lParam); + +VOID BindUnbindComponents( HWND hwndOwner, + HTREEITEM hItem, + _In_ LPWSTR lpszInfId, + BOOL fBindTo); + +HRESULT InstallComponent (HWND hwndDlg, + const GUID *pguidClass); + +HRESULT InstallSpecifiedComponent ( _In_ LPWSTR lpszInfFile, + _In_ LPWSTR lpszPnpID, + const GUID *pguidClass); + +DWORD ListCompToBindUnbind ( _In_ LPWSTR lpszInfId, + UINT uiType, + HWND hwndTree, + BOOL fBound); + +BOOL BindUnbind ( _In_ LPWSTR lpszInfId, + HWND hwndTree, + BOOL fBind); + +VOID ListInstalledComponents (HWND hwndTree, + const GUID *pguidClass); + +HRESULT UninstallComponent ( _In_ LPWSTR lpszInfId); + +// +// Functions defined in binding.cpp +// + +VOID WriteBindings (FILE *fp); + +VOID WriteBindingPath (FILE *fp, + INetCfgComponent *pncc); + +VOID WriteInterfaces (FILE *fp, + INetCfgBindingPath *pncbp); + +BOOL EnumNetBindings (HWND hwndTree, + UINT uiTypeSelected); + +VOID ListBindings (INetCfgComponent *pncc, + HWND hwndTree, + HTREEITEM hTreeItemRoot); + +VOID ListInterfaces (INetCfgBindingPath *pncbp, + HWND hwndTree, + HTREEITEM hTreeItemRoot); + +VOID HandleBindingPathOperation (HWND hwndOwner, + ULONG ulSelection, + HTREEITEM hItem, + LPARAM lParam); + +VOID EnableBindingPath (HWND hwndOwner, + HTREEITEM hItem, + _In_ LPWSTR lpszTokenPath, + BOOL fEnable); + +LPWSTR GetComponentId (HWND hwndTree, + HTREEITEM hItem); + +INetCfgBindingPath *FindBindingPath (INetCfg *pnc, + _In_ LPWSTR lpszInfId, + _In_ LPWSTR lpszPathTokenSelected); + +#endif diff --git a/network/config/bindview/BINDVIEW.ICO b/network/config/bindview/BINDVIEW.ICO new file mode 100644 index 00000000..47c1d817 Binary files /dev/null and b/network/config/bindview/BINDVIEW.ICO differ diff --git a/network/config/bindview/BindView.rc b/network/config/bindview/BindView.rc new file mode 100644 index 00000000..71d8f894 --- /dev/null +++ b/network/config/bindview/BindView.rc @@ -0,0 +1,194 @@ +//Microsoft Developer Studio generated resource script. +// + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +#include "resource.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAIN DIALOGEX 0, 0, 400, 215 +STYLE DS_SETFOREGROUND | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +CAPTION "Network Bindings" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Tree1",IDT_BINDINGS,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | + TVS_INFOTIP | WS_BORDER | WS_TABSTOP,7,28,385,148, + WS_EX_CLIENTEDGE + PUSHBUTTON "Install...",IDB_INSTALL,8,185,53,16 + PUSHBUTTON "Collapse All",IDB_COLLAPSE_ALL,265,185,53,16 + PUSHBUTTON "Expand All",IDB_EXPAND_ALL,177,185,53,16 + PUSHBUTTON "Uninstall...",IDB_UNINSTALL,91,185,53,16 + COMBOBOX IDL_COMPONENT_TYPES,109,7,146,61,CBS_DROPDOWNLIST | + CBS_DISABLENOSCROLL | WS_VSCROLL | WS_GROUP | WS_TABSTOP + CONTROL "Show Bindings For",IDS_COMPONENT,"Static", + SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | WS_GROUP,43,11,60,8 + PUSHBUTTON "Save Bindings",IDB_SAVE,339,185,53,16 +END + +IDD_BIND_UNBIND DIALOG DISCARDABLE 0, 0, 269, 140 +STYLE DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Bind To Network Features" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Tree3",IDT_COMPONENT_LIST,"SysTreeView32", + TVS_CHECKBOXES | TVS_INFOTIP | TVS_FULLROWSELECT | + WS_BORDER | WS_GROUP | WS_TABSTOP,16,24,236,82 + DEFPUSHBUTTON "Bind",IDB_BIND_UNBIND,19,120,50,14 + DEFPUSHBUTTON "Close",IDB_CLOSE,201,119,50,14,WS_GROUP + GROUPBOX "Select features to bind to",IDG_COMPONENT_LIST,7,7, + 253,107,WS_GROUP +END + +IDD_INSTALL DIALOG DISCARDABLE 0, 0, 234, 95 +STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +CAPTION "Select Network Feature Type" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Tree1",IDT_COMPONENT_LIST,"SysTreeView32", + TVS_SHOWSELALWAYS | TVS_FULLROWSELECT | TVS_NOSCROLL | + WS_BORDER | WS_GROUP | WS_TABSTOP,13,20,207,36 + DEFPUSHBUTTON "Install",IDB_INSTALL,17,73,50,14 + PUSHBUTTON "Browse...",IDB_BROWSE,92,73,50,14 + PUSHBUTTON "Close",IDB_CLOSE,173,72,50,14 + GROUPBOX "Select type of feature to install",IDG_COMPONENT_LIST, + 7,7,221,57 +END + +IDD_UNINSTALL DIALOG DISCARDABLE 0, 0, 258, 141 +STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +CAPTION "Uninstall Network Feature" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Tree1",IDT_COMPONENT_LIST,"SysTreeView32", + TVS_SHOWSELALWAYS | TVS_FULLROWSELECT | WS_BORDER | + WS_GROUP | WS_TABSTOP,25,28,207,76 + DEFPUSHBUTTON "Remove",IDB_REMOVE,33,120,50,14 + PUSHBUTTON "Close",IDB_CLOSE,171,120,50,14 + GROUPBOX "Select feature to uninstall",IDG_COMPONENT_LIST,15,15, + 227,97 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDM_OPTIONS MENU DISCARDABLE +BEGIN + POPUP "Features" + BEGIN + MENUITEM "Bind To...", IDI_BIND_TO + MENUITEM "Unbind From...", IDI_UNBIND_FROM + MENUITEM SEPARATOR + MENUITEM "Cancel", IDI_CANCEL + END + POPUP "Bindings" + BEGIN + MENUITEM "Enable", IDI_ENABLE + MENUITEM SEPARATOR + MENUITEM "Cancel", IDI_CANCEL + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_BINDVIEW ICON DISCARDABLE "bindview.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resrc1.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""resource.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_MAIN, DIALOG + BEGIN + RIGHTMARGIN, 392 + HORZGUIDE, 201 + END + + IDD_INSTALL, DIALOG + BEGIN + HORZGUIDE, 73 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/network/config/bindview/Component.cpp b/network/config/bindview/Component.cpp new file mode 100644 index 00000000..3f42b56b --- /dev/null +++ b/network/config/bindview/Component.cpp @@ -0,0 +1,897 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 2001. +// +// File: C O M P O N E N T . C P P +// +// Contents: Functions to illustrate +// o How to enumerate network components. +// o How to install protocols, clients and services. +// o How to uninstall protocols, clients and services. +// o How to bind/unbind network components. +// +// Notes: +// +// Author: Alok Sinha 15-May-01 +// +//---------------------------------------------------------------------------- + +#include "bindview.h" + +// +// Function: HandleComponentOperation +// +// Purpose: Do component specific functions. +// +// Arguments: +// hwndOwner [in] Owner window. +// ulSelection [in] Option selected. +// hItem [in] Item selected. +// lParam [in] lParam of the item. +// +// Returns: None. +// +// Notes: +// + +VOID HandleComponentOperation (HWND hwndOwner, + ULONG ulSelection, + HTREEITEM hItem, + LPARAM lParam) +{ + switch( ulSelection ) { + + case IDI_BIND_TO: + case IDI_UNBIND_FROM: + + // + // Bind/unbind components. + // + + BindUnbindComponents( hwndOwner, + hItem, + (LPWSTR)lParam, + ulSelection == IDI_BIND_TO ); + } + + return; +} + +// +// Function: BindUnbindComponents +// +// Purpose: Bind/unbind a network component. +// +// Arguments: +// hwndOwner [in] Owner window. +// hItem [in] Item handle of the network component. +// lpszInfId [in] PnpID of the network component. +// fBindTo [in] if TRUE, bind, otherwise unbind. +// +// Returns: None. +// +// Notes: +// + +VOID BindUnbindComponents( HWND hwndOwner, + HTREEITEM hItem, + _In_ LPWSTR lpszInfId, + BOOL fBindTo) +{ + UNREFERENCED_PARAMETER(hItem); + + BIND_UNBIND_INFO BindUnbind; + + BindUnbind.lpszInfId = lpszInfId; + BindUnbind.fBindTo = fBindTo; + + DialogBoxParam( hInstance, + MAKEINTRESOURCE(IDD_BIND_UNBIND), + hwndOwner, + BindComponentDlg, + (LPARAM)&BindUnbind ); + + return; +} + +// +// Function: InstallComponent +// +// Purpose: Install a network component. +// +// Arguments: +// hwndDlg [in] Owner window. +// pguidClass [in] Class GUID of type of network component to install. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT InstallComponent (HWND hwndDlg, + const GUID *pguidClass) +{ + INetCfg *pnc; + INetCfgClass *pncClass; + INetCfgClassSetup *pncClassSetup; + INetCfgComponent *pnccItem; + LPWSTR lpszApp; + OBO_TOKEN obo; + HRESULT hr; + + // + // Get INetCfg reference. + // + + hr = HrGetINetCfg( TRUE, + APP_NAME, + &pnc, + &lpszApp ); + + if ( hr == S_OK ) { + + // + // Get network component's class reference. + // + + hr = pnc->QueryNetCfgClass( pguidClass, + IID_INetCfgClass, + (PVOID *)&pncClass ); + + if ( hr == S_OK ) { + + // + // Get Setup class reference. + // + + hr = pncClass->QueryInterface( IID_INetCfgClassSetup, + (LPVOID *)&pncClassSetup ); + + if ( hr == S_OK ) { + + ZeroMemory( &obo, + sizeof(OBO_TOKEN) ); + + obo.Type = OBO_USER; + + // + // Let the network class installer prompt the user to select + // a network component to install. + // + + hr = pncClassSetup->SelectAndInstall( hwndDlg, + &obo, + &pnccItem ); + + if ( (hr == S_OK) || (hr == NETCFG_S_REBOOT) ) { + + hr = pnc->Apply(); + + if ( (hr != S_OK) && (hr != NETCFG_S_REBOOT) ) { + + ErrMsg( hr, + L"Couldn't apply the changes after" + L" installing the network component." ); + } + + } + else { + if ( hr != HRESULT_FROM_WIN32(ERROR_CANCELLED) ) { + ErrMsg( hr, + L"Couldn't install the network component." ); + } + } + + ReleaseRef( pncClassSetup ); + } + else { + ErrMsg( hr, + L"Couldn't get an interface to setup class." ); + } + + ReleaseRef( pncClass ); + } + else { + ErrMsg( hr, + L"Couldn't get a pointer to class interface." ); + } + + HrReleaseINetCfg( pnc, + TRUE ); + } + else { + if ( (hr == NETCFG_E_NO_WRITE_LOCK) && lpszApp ) { + ErrMsg( hr, + L"%s currently holds the lock, try later.", + lpszApp ); + + CoTaskMemFree( lpszApp ); + } + else { + ErrMsg( hr, + L"Couldn't the get notify object interface." ); + } + } + + return hr; +} + +// +// Function: InstallSpecifiedComponent +// +// Purpose: Install a network component from an INF file. +// +// Arguments: +// lpszInfFile [in] INF file. +// lpszPnpID [in] PnpID of the network component to install. +// pguidClass [in] Class GUID of the network component. +// +// Returns: None. +// +// Notes: +// + +HRESULT InstallSpecifiedComponent ( _In_ LPWSTR lpszInfFile, + _In_ LPWSTR lpszPnpID, + const GUID *pguidClass) +{ + INetCfg *pnc; + LPWSTR lpszApp; + HRESULT hr; + + hr = HrGetINetCfg( TRUE, + APP_NAME, + &pnc, + &lpszApp ); + + if ( hr == S_OK ) { + + // + // Install the network component. + // + + hr = HrInstallNetComponent( pnc, + lpszPnpID, + pguidClass, + lpszInfFile ); + if ( (hr == S_OK) || (hr == NETCFG_S_REBOOT) ) { + + hr = pnc->Apply(); + } + else { + if ( hr != HRESULT_FROM_WIN32(ERROR_CANCELLED) ) { + ErrMsg( hr, + L"Couldn't install the network component." ); + } + } + + HrReleaseINetCfg( pnc, + TRUE ); + } + else { + if ( (hr == NETCFG_E_NO_WRITE_LOCK) && lpszApp ) { + ErrMsg( hr, + L"%s currently holds the lock, try later.", + lpszApp ); + + CoTaskMemFree( lpszApp ); + } + else { + ErrMsg( hr, + L"Couldn't the get notify object interface." ); + } + } + + return hr; +} + +// +// Function: ListCompToBindUnbind +// +// Purpose: List all the components that are bound or bindable. +// +// Arguments: +// lpszInfId [in] PnpID of the network component. +// uiType [in] Type of network component. +// hwndTree [in] Tree handle in which to list. +// fBound [in] if TRUE, list components that are bound. +// +// Returns: Number of components listed. +// +// Notes: +// + +DWORD ListCompToBindUnbind ( _In_ LPWSTR lpszInfId, + UINT uiType, + HWND hwndTree, + BOOL fBound) +{ + INetCfg *pnc; + INetCfgComponent *pncc; + IEnumNetCfgComponent *pencc; + INetCfgComponentBindings *pnccb; + INetCfgComponent *pnccToBindUnbind; + LPWSTR lpszApp; + DWORD dwCount; + HRESULT hr; + + + dwCount = 0; + hr = HrGetINetCfg( TRUE, + APP_NAME, + &pnc, + &lpszApp ); + + if ( hr == S_OK ) { + + // + // Get a reference to the network component selected. + // + + hr = pnc->FindComponent( lpszInfId, + &pncc ); + + if ( hr == S_OK ) { + + // + // Get Component Enumerator Interface. + // + + hr = HrGetComponentEnum( pnc, + pguidNetClass[uiType], + &pencc ); + if ( hr == S_OK ) { + + hr = pncc->QueryInterface( IID_INetCfgComponentBindings, + (PVOID *)&pnccb ); + if ( hr == S_OK ) { + + hr = HrGetFirstComponent( pencc, &pnccToBindUnbind ); + + while( hr == S_OK ) { + + hr = pnccb->IsBoundTo( pnccToBindUnbind ); + + // + // fBound = TRUE ==> Want to list components that are + // bound. + // + + if ( fBound ) { + + if ( hr == S_OK ) { + if ( IsEqualIID( *pguidNetClass[uiType], GUID_DEVCLASS_NET) ) + { + AddToTreeEx( hwndTree, + TVI_ROOT, + pnccToBindUnbind, + TRUE ); + } + else + { + AddToTreeEx( hwndTree, + TVI_ROOT, + pnccToBindUnbind, + FALSE ); + } + + dwCount++; + } + } + else { + + // + // fBound = FALSE ==> Want to list components that + // are not bound but are bindable. + // + + if ( hr == S_FALSE ) { + + hr = pnccb->IsBindableTo( pnccToBindUnbind ); + + if ( hr == S_OK ) { + + if ( IsEqualIID( *pguidNetClass[uiType], GUID_DEVCLASS_NET) ) + { + AddToTreeEx( hwndTree, + TVI_ROOT, + pnccToBindUnbind, + TRUE ); + } + else + { + AddToTreeEx( hwndTree, + TVI_ROOT, + pnccToBindUnbind, + FALSE ); + } + + dwCount++; + } + } + } + + ReleaseRef( pnccToBindUnbind ); + + hr = HrGetNextComponent( pencc, &pnccToBindUnbind ); + } + + ReleaseRef( pnccb ); + } + else { + ErrMsg( hr, + L"Couldn't get the component binding interface " + L"of %s.", + lpszInfId ); + } + + ReleaseRef( pencc ); + } + else { + ErrMsg( hr, + L"Couldn't get the network component enumerator " + L"interface." ); + } + + ReleaseRef( pncc ); + + } + else { + ErrMsg( hr, + L"Couldn't get an interface pointer to %s.", + lpszInfId ); + } + + HrReleaseINetCfg( pnc, + TRUE ); + } + else { + if ( (hr == NETCFG_E_NO_WRITE_LOCK) && lpszApp ) { + ErrMsg( hr, + L"%s currently holds the lock, try later.", + lpszApp ); + + CoTaskMemFree( lpszApp ); + } + else { + ErrMsg( hr, + L"Couldn't get the notify object interface." ); + } + } + + return dwCount; +} + +// +// Function: BindUnbind +// +// Purpose: Bind/unbind a network component. +// +// Arguments: +// lpszInfId [in] PnpID of the network component to bind/unbind. +// hwndTree [in] Tree handle. +// fBind [in] if TRUE, bind, otherwise unbind. +// +// Returns: TRUE on success. +// +// Notes: +// + +BOOL BindUnbind ( _In_ LPWSTR lpszInfId, + HWND hwndTree, + BOOL fBind) +{ + INetCfg *pnc; + INetCfgComponent *pncc; + INetCfgComponentBindings *pnccb; + INetCfgComponent *pnccToBindUnbind; + LPWSTR lpszApp; + HTREEITEM hTreeItem; + TVITEMW tvItem; + HRESULT hr; + BOOL fChange; + + + hr = HrGetINetCfg( TRUE, + APP_NAME, + &pnc, + &lpszApp ); + + fChange = FALSE; + + if ( hr == S_OK ) { + + // + // Get a reference to the network component. + // + + hr = pnc->FindComponent( lpszInfId, + &pncc ); + if ( hr == S_OK ) { + + // + // Get a reference to the component's binding. + // + + hr = pncc->QueryInterface( IID_INetCfgComponentBindings, + (PVOID *)&pnccb ); + if ( hr == S_OK ) { + + // + // Start with the root item. + // + + hTreeItem = TreeView_GetRoot( hwndTree ); + + // + // Bind/unbind the network component with every component + // that is checked. + // + + while ( hTreeItem ) { + + ZeroMemory( &tvItem, + sizeof(TVITEMW) ); + + tvItem.hItem = hTreeItem; + tvItem.mask = TVIF_PARAM | TVIF_STATE; + tvItem.stateMask = TVIS_STATEIMAGEMASK; + + if ( TreeView_GetItem(hwndTree, + &tvItem) ) { + + // + // Is the network component selected? + // + + if ( (tvItem.state >> 12) == 2 ) { + + // + // Get a reference to the selected component. + // + // For adapters, lParam is pnp instance id. So, FindComponent will fail. In that case, search + // for a network adapter matching the pnp instance id. This will ensure that we get to the right + // network adapter in case there are multiple identical adapters in the system. + + hr = pnc->FindComponent( (LPWSTR)tvItem.lParam, + &pnccToBindUnbind ); + if ( hr != S_OK ) + { + hr = HrFindNetComponentByPnpId( pnc, + (LPWSTR)tvItem.lParam, + &pnccToBindUnbind ); + } + + if ( hr == S_OK ) { + + if ( fBind ) { + + // + // Bind the component to the selected component. + // + + hr = pnccb->BindTo( pnccToBindUnbind ); + + if ( !fChange ) { + fChange = hr == S_OK; + } + + if ( hr != S_OK ) { + ErrMsg( hr, + L"%s couldn't be bound to %s.", + lpszInfId, (LPWSTR)tvItem.lParam ); + } + } + else { + // + // Unbind the component from the selected component. + // + + hr = pnccb->UnbindFrom( pnccToBindUnbind ); + + if ( !fChange ) { + fChange = hr == S_OK; + } + + if ( hr != S_OK ) { + ErrMsg( hr, + L"%s couldn't be unbound from %s.", + lpszInfId, (LPWSTR)tvItem.lParam ); + } + } + + ReleaseRef( pnccToBindUnbind ); + } + else { + ErrMsg( hr, + L"Couldn't get an interface pointer to %s. " + L"%s will not be bound to it.", + (LPWSTR)tvItem.lParam, + lpszInfId ); + } + } + } + + // + // Get the next item. + // + + hTreeItem = TreeView_GetNextSibling( hwndTree, + hTreeItem ); + } + + ReleaseRef( pnccb ); + } + else { + ErrMsg( hr, + L"Couldn't get a binding interface of %s.", + lpszInfId ); + } + + ReleaseRef( pncc ); + } + else { + ErrMsg( hr, + L"Couldn't get an interface pointer to %s.", + lpszInfId ); + } + + // + // If one or more network components have been bound/unbound, + // apply the changes. + // + + if ( fChange ) { + hr = pnc->Apply(); + + fChange = hr == S_OK; + } + + HrReleaseINetCfg( pnc, + TRUE ); + } + else { + if ( (hr == NETCFG_E_NO_WRITE_LOCK) && lpszApp ) { + ErrMsg( hr, + L"%s currently holds the lock, try later.", + lpszApp ); + + CoTaskMemFree( lpszApp ); + } + else { + ErrMsg( hr, + L"Couldn't get the notify object interface." ); + } + } + + return fChange; +} + +// +// Function: ListInstalledComponents +// +// Purpose: List installed network components of specific class. +// +// Arguments: +// hwndTree [in] Tree handle in which to list. +// pguidClass [in] Class GUID of the network component class. +// +// Returns: None. +// +// Notes: +// + +VOID ListInstalledComponents (HWND hwndTree, + const GUID *pguidClass) +{ + INetCfg *pnc; + IEnumNetCfgComponent *pencc; + INetCfgComponent *pncc; + LPWSTR lpszApp; + HRESULT hr; + + + hr = HrGetINetCfg( FALSE, + APP_NAME, + &pnc, + &lpszApp ); + + if ( hr == S_OK ) { + + // + // Get Component Enumerator Interface. + // + + hr = HrGetComponentEnum( pnc, + pguidClass, + &pencc ); + if ( hr == S_OK ) { + + hr = HrGetFirstComponent( pencc, &pncc ); + + while( hr == S_OK ) { + + // + // Add an item to the tree for the network component. + // + + AddToTree( hwndTree, + TVI_ROOT, + pncc ); + + ReleaseRef( pncc ); + + hr = HrGetNextComponent( pencc, &pncc ); + } + + ReleaseRef( pencc ); + } + else { + ErrMsg( hr, + L"Failed to get the network component enumerator." ); + } + + HrReleaseINetCfg( pnc, FALSE ); + } + else { + if ( (hr == NETCFG_E_NO_WRITE_LOCK) && lpszApp ) { + ErrMsg( hr, + L"%s currently holds the lock, try later.", + lpszApp ); + + CoTaskMemFree( lpszApp ); + } + else { + ErrMsg( hr, + L"Couldn't get the notify object interface." ); + } + } + + return; +} + +// +// Function: UninstallComponent +// +// Purpose: Uninstall a network component. +// +// Arguments: +// lpszInfId [in] PnpID of the network component to uninstall. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT UninstallComponent ( _In_ LPWSTR lpszInfId) +{ + INetCfg *pnc; + INetCfgComponent *pncc; + INetCfgClass *pncClass; + INetCfgClassSetup *pncClassSetup; + LPWSTR lpszApp; + GUID guidClass; + OBO_TOKEN obo; + HRESULT hr; + + hr = HrGetINetCfg( TRUE, + APP_NAME, + &pnc, + &lpszApp ); + + if ( hr == S_OK ) { + + // + // Get a reference to the network component to uninstall. + // + + hr = pnc->FindComponent( lpszInfId, + &pncc ); + + if ( hr == S_OK ) { + + // + // Get the class GUID. + // + + hr = pncc->GetClassGuid( &guidClass ); + + if ( hr == S_OK ) { + + // + // Get a reference to component's class. + // + + hr = pnc->QueryNetCfgClass( &guidClass, + IID_INetCfgClass, + (PVOID *)&pncClass ); + if ( hr == S_OK ) { + + // + // Get the setup interface. + // + + hr = pncClass->QueryInterface( IID_INetCfgClassSetup, + (LPVOID *)&pncClassSetup ); + + if ( hr == S_OK ) { + + // + // Uninstall the component. + // + + ZeroMemory( &obo, + sizeof(OBO_TOKEN) ); + + obo.Type = OBO_USER; + + hr = pncClassSetup->DeInstall( pncc, + &obo, + NULL ); + if ( (hr == S_OK) || (hr == NETCFG_S_REBOOT) ) { + + hr = pnc->Apply(); + + if ( (hr != S_OK) && (hr != NETCFG_S_REBOOT) ) { + ErrMsg( hr, + L"Couldn't apply the changes after" + L" uninstalling %s.", + lpszInfId ); + } + } + else { + ErrMsg( hr, + L"Failed to uninstall %s.", + lpszInfId ); + } + + ReleaseRef( pncClassSetup ); + } + else { + ErrMsg( hr, + L"Couldn't get an interface to setup class." ); + } + + ReleaseRef( pncClass ); + } + else { + ErrMsg( hr, + L"Couldn't get a pointer to class interface " + L"of %s.", + lpszInfId ); + } + } + else { + ErrMsg( hr, + L"Couldn't get the class guid of %s.", + lpszInfId ); + } + + ReleaseRef( pncc ); + } + else { + ErrMsg( hr, + L"Couldn't get an interface pointer to %s.", + lpszInfId ); + } + + HrReleaseINetCfg( pnc, + TRUE ); + } + else { + if ( (hr == NETCFG_E_NO_WRITE_LOCK) && lpszApp ) { + ErrMsg( hr, + L"%s currently holds the lock, try later.", + lpszApp ); + + CoTaskMemFree( lpszApp ); + } + else { + ErrMsg( hr, + L"Couldn't get the notify object interface." ); + } + } + + return hr; +} diff --git a/network/config/bindview/NetCfgAPI.cpp b/network/config/bindview/NetCfgAPI.cpp new file mode 100644 index 00000000..5080bb41 --- /dev/null +++ b/network/config/bindview/NetCfgAPI.cpp @@ -0,0 +1,878 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 2001. +// +// File: N E T C F G A P I . C P P +// +// Contents: Functions to illustrate INetCfg API +// +// Notes: +// +// Author: Alok Sinha 15-May-01 +// +//---------------------------------------------------------------------------- + +#include "NetCfgAPI.h" + +// +// Function: HrGetINetCfg +// +// Purpose: Get a reference to INetCfg. +// +// Arguments: +// fGetWriteLock [in] If TRUE, Write lock.requested. +// lpszAppName [in] Application name requesting the reference. +// ppnc [out] Reference to INetCfg. +// lpszLockedBy [in] Optional. Application who holds the write lock. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrGetINetCfg (IN BOOL fGetWriteLock, + IN LPCWSTR lpszAppName, + OUT INetCfg** ppnc, + _Outptr_opt_result_maybenull_ LPWSTR *lpszLockedBy) +{ + INetCfg *pnc = NULL; + INetCfgLock *pncLock = NULL; + HRESULT hr = S_OK; + + // + // Initialize the output parameters. + // + + *ppnc = NULL; + + if ( lpszLockedBy ) + { + *lpszLockedBy = NULL; + } + // + // Initialize COM + // + + hr = CoInitialize( NULL ); + + if ( hr == S_OK ) { + + // + // Create the object implementing INetCfg. + // + + hr = CoCreateInstance( CLSID_CNetCfg, + NULL, CLSCTX_INPROC_SERVER, + IID_INetCfg, + (void**)&pnc ); + if ( hr == S_OK ) { + + if ( fGetWriteLock ) { + + // + // Get the locking reference + // + + hr = pnc->QueryInterface( IID_INetCfgLock, + (LPVOID *)&pncLock ); + if ( hr == S_OK ) { + + // + // Attempt to lock the INetCfg for read/write + // + + hr = pncLock->AcquireWriteLock( LOCK_TIME_OUT, + lpszAppName, + lpszLockedBy); + if (hr == S_FALSE ) { + hr = NETCFG_E_NO_WRITE_LOCK; + } + } + } + + if ( hr == S_OK ) { + + // + // Initialize the INetCfg object. + // + + hr = pnc->Initialize( NULL ); + + if ( hr == S_OK ) { + *ppnc = pnc; + pnc->AddRef(); + } + else { + + // + // Initialize failed, if obtained lock, release it + // + + if ( pncLock ) { + pncLock->ReleaseWriteLock(); + } + } + } + + ReleaseRef( pncLock ); + ReleaseRef( pnc ); + } + + // + // In case of error, uninitialize COM. + // + + if ( hr != S_OK ) { + CoUninitialize(); + } + } + + return hr; +} + +// +// Function: HrReleaseINetCfg +// +// Purpose: Get a reference to INetCfg. +// +// Arguments: +// pnc [in] Reference to INetCfg to release. +// fHasWriteLock [in] If TRUE, reference was held with write lock. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrReleaseINetCfg (IN INetCfg* pnc, + IN BOOL fHasWriteLock) +{ + INetCfgLock *pncLock = NULL; + HRESULT hr = S_OK; + + // + // Uninitialize INetCfg + // + + hr = pnc->Uninitialize(); + + // + // If write lock is present, unlock it + // + + if ( hr == S_OK && fHasWriteLock ) { + + // + // Get the locking reference + // + + hr = pnc->QueryInterface( IID_INetCfgLock, + (LPVOID *)&pncLock); + if ( hr == S_OK ) { + hr = pncLock->ReleaseWriteLock(); + ReleaseRef( pncLock ); + } + } + + ReleaseRef( pnc ); + + // + // Uninitialize COM. + // + + CoUninitialize(); + + return hr; +} + +// +// Function: HrInstallNetComponent +// +// Purpose: Install a network component(protocols, clients and services) +// given its INF file. +// +// Arguments: +// pnc [in] Reference to INetCfg. +// lpszComponentId [in] PnpID of the network component. +// pguidClass [in] Class GUID of the network component. +// lpszInfFullPath [in] INF file to install from. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrInstallNetComponent (IN INetCfg *pnc, + IN LPCWSTR lpszComponentId, + IN const GUID *pguidClass, + IN LPCWSTR lpszInfFullPath) +{ + DWORD dwError; + HRESULT hr = S_OK; + WCHAR* Drive = NULL; + WCHAR* Dir = NULL; + WCHAR* DirWithDrive = NULL; + + do + { + // + // If full path to INF has been specified, the INF + // needs to be copied using Setup API to ensure that any other files + // that the primary INF copies will be correctly found by Setup API + // + + if ( lpszInfFullPath ) { + + // + // Allocate memory to hold the strings + // + Drive = (WCHAR*)CoTaskMemAlloc(_MAX_DRIVE * sizeof(WCHAR)); + if (NULL == Drive) + { + hr = E_OUTOFMEMORY; + break; + } + ZeroMemory(Drive, _MAX_DRIVE * sizeof(WCHAR)); + + Dir = (WCHAR*)CoTaskMemAlloc(_MAX_DIR * sizeof(WCHAR)); + if (NULL == Dir) + { + hr = E_OUTOFMEMORY; + break; + } + ZeroMemory(Dir, _MAX_DRIVE * sizeof(WCHAR)); + + DirWithDrive = (WCHAR*)CoTaskMemAlloc((_MAX_DRIVE + _MAX_DIR) * sizeof(WCHAR)); + if (NULL == DirWithDrive) + { + hr = E_OUTOFMEMORY; + break; + } + ZeroMemory(DirWithDrive, (_MAX_DRIVE + _MAX_DIR) * sizeof(WCHAR)); + + // + // Get the path where the INF file is. + // + + _wsplitpath_s ( lpszInfFullPath, + Drive, + _MAX_DRIVE, + Dir, + _MAX_DIR, + NULL, + 0, + NULL, + 0); + + StringCchCopyW ( DirWithDrive, + _MAX_DRIVE + _MAX_DIR, + Drive ); + StringCchCatW ( DirWithDrive, + _MAX_DRIVE + _MAX_DIR, + Dir ); + + // + // Copy the INF file and other files referenced in the INF file. + // + + if ( !SetupCopyOEMInfW(lpszInfFullPath, + DirWithDrive, // Other files are in the + // same dir. as primary INF + SPOST_PATH, // First param is path to INF + 0, // Default copy style + NULL, // Name of the INF after + // it's copied to %windir%\inf + 0, // Max buf. size for the above + NULL, // Required size if non-null + NULL) ) { // Optionally get the filename + // part of Inf name after it is copied. + dwError = GetLastError(); + + hr = HRESULT_FROM_WIN32( dwError ); + } + } + + if ( S_OK == hr ) { + + // + // Install the network component. + // + + hr = HrInstallComponent( pnc, + lpszComponentId, + pguidClass ); + if ( hr == S_OK ) { + + // + // On success, apply the changes + // + + hr = pnc->Apply(); + } + } + + #pragma warning(disable:4127) /* Conditional expression is constant */ + } while (false); + + if (Drive != NULL) + { + CoTaskMemFree(Drive); + Drive = NULL; + } + if (Dir != NULL) + { + CoTaskMemFree(Dir); + Dir = NULL; + } + if (DirWithDrive != NULL) + { + CoTaskMemFree(DirWithDrive); + DirWithDrive = NULL; + } + + return hr; +} + +// +// Function: HrInstallComponent +// +// Purpose: Install a network component(protocols, clients and services) +// given its INF file. +// Arguments: +// pnc [in] Reference to INetCfg. +// lpszComponentId [in] PnpID of the network component. +// pguidClass [in] Class GUID of the network component. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrInstallComponent(IN INetCfg* pnc, + IN LPCWSTR szComponentId, + IN const GUID* pguidClass) +{ + INetCfgClassSetup *pncClassSetup = NULL; + INetCfgComponent *pncc = NULL; + OBO_TOKEN OboToken; + HRESULT hr = S_OK; + + // + // OBO_TOKEN specifies on whose behalf this + // component is being installed. + // Set it to OBO_USER so that szComponentId will be installed + // on behalf of the user. + // + + ZeroMemory( &OboToken, + sizeof(OboToken) ); + OboToken.Type = OBO_USER; + + // + // Get component's setup class reference. + // + + hr = pnc->QueryNetCfgClass ( pguidClass, + IID_INetCfgClassSetup, + (void**)&pncClassSetup ); + if ( hr == S_OK ) { + + hr = pncClassSetup->Install( szComponentId, + &OboToken, + 0, + 0, // Upgrade from build number. + NULL, // Answerfile name + NULL, // Answerfile section name + &pncc ); // Reference after the component + if ( S_OK == hr ) { // is installed. + + // + // we don't need to use pncc (INetCfgComponent), release it + // + + ReleaseRef( pncc ); + } + + ReleaseRef( pncClassSetup ); + } + + return hr; +} + +// +// Function: HrUninstallNetComponent +// +// Purpose: Uninstall a network component(protocols, clients and services). +// +// Arguments: +// pnc [in] Reference to INetCfg. +// szComponentId [in] PnpID of the network component to uninstall. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrUninstallNetComponent(IN INetCfg* pnc, + IN LPCWSTR szComponentId) +{ + INetCfgComponent *pncc = NULL; + INetCfgClass *pncClass = NULL; + INetCfgClassSetup *pncClassSetup = NULL; + OBO_TOKEN OboToken; + GUID guidClass; + HRESULT hr = S_OK; + + // + // OBO_TOKEN specifies on whose behalf this + // component is being installed. + // Set it to OBO_USER so that szComponentId will be installed + // on behalf of the user. + // + + ZeroMemory( &OboToken, + sizeof(OboToken) ); + OboToken.Type = OBO_USER; + + // + // Get the component's reference. + // + + hr = pnc->FindComponent( szComponentId, + &pncc ); + + if (S_OK == hr) { + + // + // Get the component's class GUID. + // + + hr = pncc->GetClassGuid( &guidClass ); + + if ( hr == S_OK ) { + + // + // Get component's class reference. + // + + hr = pnc->QueryNetCfgClass( &guidClass, + IID_INetCfgClass, + (void**)&pncClass ); + if ( hr == S_OK ) { + + // + // Get Setup reference. + // + + hr = pncClass->QueryInterface( IID_INetCfgClassSetup, + (void**)&pncClassSetup ); + if ( hr == S_OK ) { + + hr = pncClassSetup->DeInstall( pncc, + &OboToken, + NULL); + if ( hr == S_OK ) { + + // + // Apply the changes + // + + hr = pnc->Apply(); + } + + ReleaseRef( pncClassSetup ); + } + + ReleaseRef( pncClass ); + } + } + + ReleaseRef( pncc ); + } + + return hr; +} + +// +// Function: HrGetComponentEnum +// +// Purpose: Get network component enumerator reference. +// +// Arguments: +// pnc [in] Reference to INetCfg. +// pguidClass [in] Class GUID of the network component. +// ppencc [out] Enumerator reference. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrGetComponentEnum (INetCfg* pnc, + IN const GUID* pguidClass, + OUT IEnumNetCfgComponent **ppencc) +{ + INetCfgClass *pncclass; + HRESULT hr; + + *ppencc = NULL; + + // + // Get the class reference. + // + + hr = pnc->QueryNetCfgClass( pguidClass, + IID_INetCfgClass, + (PVOID *)&pncclass ); + + if ( hr == S_OK ) { + + // + // Get the enumerator reference. + // + + hr = pncclass->EnumComponents( ppencc ); + + // + // We don't need the class reference any more. + // + + ReleaseRef( pncclass ); + } + + return hr; +} + +// +// Function: HrGetFirstComponent +// +// Purpose: Enumerates the first network component. +// +// Arguments: +// pencc [in] Component enumerator reference. +// ppncc [out] Network component reference. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrGetFirstComponent (IN IEnumNetCfgComponent* pencc, + OUT INetCfgComponent **ppncc) +{ + HRESULT hr; + ULONG ulCount; + + *ppncc = NULL; + + pencc->Reset(); + + hr = pencc->Next( 1, + ppncc, + &ulCount ); + return hr; +} + +// +// Function: HrGetNextComponent +// +// Purpose: Enumerate the next network component. +// +// Arguments: +// pencc [in] Component enumerator reference. +// ppncc [out] Network component reference. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: The function behaves just like HrGetFirstComponent if +// it is called right after HrGetComponentEnum. +// +// + +HRESULT HrGetNextComponent (IN IEnumNetCfgComponent* pencc, + OUT INetCfgComponent **ppncc) +{ + HRESULT hr; + ULONG ulCount; + + *ppncc = NULL; + + hr = pencc->Next( 1, + ppncc, + &ulCount ); + return hr; +} + +// +// Function: HrFindNetComponentByPnpId +// +// Purpose: Get network adapter identified by a particular pnp device instance id. +// +// Arguments: +// pncc [in] Network component reference. +// lpszPnpDevNodeId [in] pnp device instance id. +// ppncc [out] pointer to network adapter reference. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrFindNetComponentByPnpId (IN INetCfg *pnc, + IN _In_ LPWSTR lpszPnpDevNodeId, + OUT INetCfgComponent **ppncc) +{ + IEnumNetCfgComponent *pencc; + LPWSTR pszPnpId; + HRESULT hr; + BOOL fFound; + + hr = HrGetComponentEnum( pnc, + &GUID_DEVCLASS_NET, + &pencc ); + if ( hr == S_OK ) { + + hr = HrGetFirstComponent( pencc, ppncc ); + fFound = FALSE; + while( hr == S_OK ) { + hr = (*ppncc)->GetPnpDevNodeId( &pszPnpId ); + if ( hr == S_OK ) { + fFound = wcscmp( pszPnpId, lpszPnpDevNodeId ) == 0; + CoTaskMemFree( pszPnpId ); + if ( fFound ) { + break; + } + } + else { + hr = S_OK; + } + + ReleaseRef( *ppncc ); + hr = HrGetNextComponent( pencc, ppncc ); + } + + ReleaseRef( pencc ); + } + + return hr; +} + +// +// Function: HrGetBindingPathEnum +// +// Purpose: Get network component's binding path enumerator reference. +// +// Arguments: +// pncc [in] Network component reference. +// dwBindingType [in] EBP_ABOVE or EBP_BELOW. +// ppencbp [out] Enumerator reference. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrGetBindingPathEnum (IN INetCfgComponent *pncc, + IN DWORD dwBindingType, + OUT IEnumNetCfgBindingPath **ppencbp) +{ + INetCfgComponentBindings *pnccb = NULL; + HRESULT hr; + + *ppencbp = NULL; + + // + // Get component's binding. + // + + hr = pncc->QueryInterface( IID_INetCfgComponentBindings, + (PVOID *)&pnccb ); + + if ( hr == S_OK ) { + + // + // Get binding path enumerator reference. + // + + hr = pnccb->EnumBindingPaths( dwBindingType, + ppencbp ); + + ReleaseRef( pnccb ); + } + + return hr; +} + +// +// Function: HrGetFirstBindingPath +// +// Purpose: Enumerates the first binding path. +// +// Arguments: +// pencc [in] Binding path enumerator reference. +// ppncc [out] Binding path reference. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrGetFirstBindingPath (IN IEnumNetCfgBindingPath *pencbp, + OUT INetCfgBindingPath **ppncbp) +{ + ULONG ulCount; + HRESULT hr; + + *ppncbp = NULL; + + pencbp->Reset(); + + hr = pencbp->Next( 1, + ppncbp, + &ulCount ); + + return hr; +} + +// +// Function: HrGetNextBindingPath +// +// Purpose: Enumerate the next binding path. +// +// Arguments: +// pencbp [in] Binding path enumerator reference. +// ppncbp [out] Binding path reference. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: The function behaves just like HrGetFirstBindingPath if +// it is called right after HrGetBindingPathEnum. +// +// + +HRESULT HrGetNextBindingPath (IN IEnumNetCfgBindingPath *pencbp, + OUT INetCfgBindingPath **ppncbp) +{ + ULONG ulCount; + HRESULT hr; + + *ppncbp = NULL; + + hr = pencbp->Next( 1, + ppncbp, + &ulCount ); + + return hr; +} + +// +// Function: HrGetBindingInterfaceEnum +// +// Purpose: Get binding interface enumerator reference. +// +// Arguments: +// pncbp [in] Binding path reference. +// ppencbp [out] Enumerator reference. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrGetBindingInterfaceEnum (IN INetCfgBindingPath *pncbp, + OUT IEnumNetCfgBindingInterface **ppencbi) +{ + HRESULT hr; + + *ppencbi = NULL; + + hr = pncbp->EnumBindingInterfaces( ppencbi ); + + return hr; +} + +// +// Function: HrGetFirstBindingInterface +// +// Purpose: Enumerates the first binding interface. +// +// Arguments: +// pencbi [in] Binding interface enumerator reference. +// ppncbi [out] Binding interface reference. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT HrGetFirstBindingInterface (IN IEnumNetCfgBindingInterface *pencbi, + OUT INetCfgBindingInterface **ppncbi) +{ + ULONG ulCount; + HRESULT hr; + + *ppncbi = NULL; + + pencbi->Reset(); + + hr = pencbi->Next( 1, + ppncbi, + &ulCount ); + + return hr; +} + +// +// Function: HrGetNextBindingInterface +// +// Purpose: Enumerate the next binding interface. +// +// Arguments: +// pencbi [in] Binding interface enumerator reference. +// ppncbi [out] Binding interface reference. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: The function behaves just like HrGetFirstBindingInterface if +// it is called right after HrGetBindingInterfaceEnum. +// +// + +HRESULT HrGetNextBindingInterface (IN IEnumNetCfgBindingInterface *pencbi, + OUT INetCfgBindingInterface **ppncbi) +{ + ULONG ulCount; + HRESULT hr; + + *ppncbi = NULL; + + hr = pencbi->Next( 1, + ppncbi, + &ulCount ); + + return hr; +} + +// +// Function: ReleaseRef +// +// Purpose: Release reference. +// +// Arguments: +// punk [in] IUnknown reference to release. +// +// Returns: Reference count. +// +// Notes: +// + +VOID ReleaseRef (IN IUnknown* punk) +{ + if ( punk ) { + punk->Release(); + } + + return; +} + diff --git a/network/config/bindview/NetCfgAPI.h b/network/config/bindview/NetCfgAPI.h new file mode 100644 index 00000000..b4d6f552 --- /dev/null +++ b/network/config/bindview/NetCfgAPI.h @@ -0,0 +1,92 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 2001. +// +// File: N E T C F G A P I . H +// +// Contents: Functions Prototypes +// +// Notes: +// +// Author: Alok Sinha 15-May-01 +// +//---------------------------------------------------------------------------- + +#ifndef _NETCFGAPI_H_INCLUDED + +#define _NETCFGAPI_H_INCLUDED + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define celems(_x) (sizeof(_x) / sizeof(_x[0])) + +#define LOCK_TIME_OUT 5000 + +HRESULT HrGetINetCfg (IN BOOL fGetWriteLock, + IN LPCWSTR lpszAppName, + OUT INetCfg** ppnc, + _Outptr_opt_result_maybenull_ LPWSTR *lpszLockedBy); + +HRESULT HrReleaseINetCfg (INetCfg* pnc, + BOOL fHasWriteLock); + +HRESULT HrInstallNetComponent (IN INetCfg *pnc, + IN LPCWSTR szComponentId, + IN const GUID *pguildClass, + IN LPCWSTR lpszInfFullPath); + +HRESULT HrInstallComponent(IN INetCfg* pnc, + IN LPCWSTR szComponentId, + IN const GUID* pguidClass); + +HRESULT HrUninstallNetComponent(IN INetCfg* pnc, + IN LPCWSTR szComponentId); + +HRESULT HrGetComponentEnum (INetCfg* pnc, + IN const GUID* pguidClass, + IEnumNetCfgComponent **ppencc); + +HRESULT HrGetFirstComponent (IEnumNetCfgComponent* pencc, + INetCfgComponent **ppncc); + +HRESULT HrGetNextComponent (IEnumNetCfgComponent* pencc, + INetCfgComponent **ppncc); + +HRESULT HrFindNetComponentByPnpId (IN INetCfg *pnc, + IN _In_ LPWSTR lpszPnpDevNodeId, + OUT INetCfgComponent **ppncc); + +HRESULT HrGetBindingPathEnum (INetCfgComponent *pncc, + DWORD dwBindingType, + IEnumNetCfgBindingPath **ppencbp); + +HRESULT HrGetFirstBindingPath (IEnumNetCfgBindingPath *pencbp, + INetCfgBindingPath **ppncbp); + +HRESULT HrGetNextBindingPath (IEnumNetCfgBindingPath *pencbp, + INetCfgBindingPath **ppncbp); + +HRESULT HrGetBindingInterfaceEnum (INetCfgBindingPath *pncbp, + IEnumNetCfgBindingInterface **ppencbi); + +HRESULT HrGetFirstBindingInterface (IEnumNetCfgBindingInterface *pencbi, + INetCfgBindingInterface **ppncbi); + +HRESULT HrGetNextBindingInterface (IEnumNetCfgBindingInterface *pencbi, + INetCfgBindingInterface **ppncbi); + +VOID ReleaseRef (IUnknown* punk); + +#endif + diff --git a/network/config/bindview/RESOURCE.H b/network/config/bindview/RESOURCE.H new file mode 100644 index 00000000..3fb76696 --- /dev/null +++ b/network/config/bindview/RESOURCE.H @@ -0,0 +1,43 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by BindView.rc +// +#define IDD_MAIN 101 +#define IDM_OPTIONS 101 +#define IDI_BINDVIEW 105 +#define IDD_BIND_UNBIND 106 +#define IDD_INSTALL 107 +#define IDD_UNINSTALL 108 +#define IDT_BINDINGS 1000 +#define IDB_INSTALL 1001 +#define IDB_UNINSTALL 1002 +#define IDS_COMPONENT 1003 +#define IDL_COMPONENT_TYPES 1004 +#define IDB_EXPAND_ALL 1005 +#define IDB_COLLAPSE_ALL 1006 +#define IDB_BROWSE 1007 +#define IDS_ENABLE 1012 +#define IDE_OWNER 1013 +#define IDE_DEPTH 1014 +#define IDB_BIND_UNBIND 1016 +#define IDG_COMPONENT_LIST 1018 +#define IDB_CLOSE 1021 +#define IDB_REMOVE 1022 +#define IDT_COMPONENT_LIST 1024 +#define IDB_SAVE 1025 +#define IDI_ENABLE 40001 +#define IDI_UNBIND_FROM 40004 +#define IDI_BIND_TO 40005 +#define IDI_CANCEL 40006 +#define IDI_DISABLE 40007 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 109 +#define _APS_NEXT_COMMAND_VALUE 40008 +#define _APS_NEXT_CONTROL_VALUE 1028 +#define _APS_NEXT_SYMED_VALUE 102 +#endif +#endif diff --git a/network/config/bindview/ReadMe.md b/network/config/bindview/ReadMe.md new file mode 100644 index 00000000..01e1cd8d --- /dev/null +++ b/network/config/bindview/ReadMe.md @@ -0,0 +1,7 @@ +Bindview Network Configuration Utility +====================================== + +The Bindview sample demonstrates how to use INetCfg APIs to enumerate, install, uninstall, bind and unbind network components. + +For more information on the INetCfg interface, see [Network Configuration Interfaces](http://msdn.microsoft.com/en-us/library/windows/hardware/ff559080). + diff --git a/network/config/bindview/bindview.htm b/network/config/bindview/bindview.htm new file mode 100644 index 00000000..c28f6190 --- /dev/null +++ b/network/config/bindview/bindview.htm @@ -0,0 +1,345 @@ + + + + + + + + +BINDVIEW: Network Configuration/Installation Sample + + + + + + + +

+ +

BINDVIEW: Network +Configuration/Installation Sample

+ +

SUMMARY

+ +

This sample demonstrates +how to use INetCfg APIs to enumerate, install, +uninstall, bind and unbind network components.

+ +

The sample compiles +properly for 64-bit systems and builds properly with Microsoft® Visual C® 6.0.

+ +

BUILDING THE SAMPLE

+ +

To build the sample, type build. This command produces the binary +bindview.exe.

+ +

INSTALLING THE SAMPLE

+ +

Copy the +binary bindview.exe to the directory from which you want to run the sample.

+ +

RUNNING THE SAMPLE

+ +

Type bindview.exe at the command prompt to run the program. You can perform the following +operations.

+ +

 

+ +
    +
  • Install a network protocol, + service or client component.
  • +
  • Uninstall a network protocol, + service or client component.
  • +
  • By clicking the right mouse + button on a network protocol, service or client, you can perform the + following operations.
  • +
      +
    • Bind the network component + to another component.
    • +
    • Unbind the network component + from another component that is bound to it.
    • +
    +
  • By clicking the right mouse + button on a binding path, you can perform the following operations.
  • +
      +
    • Disable the binding path if + it is enabled.
    • +
    • Enable the binding path if + it is disabled.
    • +
    +
  • Save the binding information + to a file.
  • +
+ +

CODE TOUR

+ +

File Manifest

+ +
File           Description
BINDVIEW.CPP   Contains WinMain and dialog box related functions.
NetCfgAPI.cpp  Contains INetCfg functions.
BINDING.CPP    Contains binding path related functions. 
Component.cpp  Contains network component related functions.
RESOURCE.H     Resource header.
BINDVIEW.H     Contains function prototypes.
NetCfgAPI.h    Contains function prototypes for NetCfgAPI.cpp
BindView.rc    Resources for Bindview
BindView.ico   Icon for the sample.
 
 
 
+ +

Top of page

+ + + + + +
+

 

+
+ +

© 2004 Microsoft Corporation

+ +
+ + + + diff --git a/network/config/bindview/bindview.sln b/network/config/bindview/bindview.sln new file mode 100644 index 00000000..021f4efb --- /dev/null +++ b/network/config/bindview/bindview.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bindview", "bindview.vcxproj", "{C8EC04C0-EF77-4CD4-8072-F7F8A9125822}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C8EC04C0-EF77-4CD4-8072-F7F8A9125822}.Debug|Win32.ActiveCfg = Debug|Win32 + {C8EC04C0-EF77-4CD4-8072-F7F8A9125822}.Debug|Win32.Build.0 = Debug|Win32 + {C8EC04C0-EF77-4CD4-8072-F7F8A9125822}.Release|Win32.ActiveCfg = Release|Win32 + {C8EC04C0-EF77-4CD4-8072-F7F8A9125822}.Release|Win32.Build.0 = Release|Win32 + {C8EC04C0-EF77-4CD4-8072-F7F8A9125822}.Debug|x64.ActiveCfg = Debug|x64 + {C8EC04C0-EF77-4CD4-8072-F7F8A9125822}.Debug|x64.Build.0 = Debug|x64 + {C8EC04C0-EF77-4CD4-8072-F7F8A9125822}.Release|x64.ActiveCfg = Release|x64 + {C8EC04C0-EF77-4CD4-8072-F7F8A9125822}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/network/config/bindview/bindview.vcxproj b/network/config/bindview/bindview.vcxproj new file mode 100644 index 00000000..767d2a89 --- /dev/null +++ b/network/config/bindview/bindview.vcxproj @@ -0,0 +1,211 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {C8EC04C0-EF77-4CD4-8072-F7F8A9125822} + $(MSBuildProjectName) + Debug + Win32 + {224CF8FD-5538-4B7E-81AF-44805B4722EA} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + bindview + + + bindview + + + bindview + + + bindview + + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + true + Level4 + %(DisableSpecificWarnings);4127 + MultiThreaded + MultiThreadedDebug + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + %(AdditionalOptions) -N + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib;comctl32.lib;comdlg32.lib;setupapi.lib;user32.lib;kernel32.lib;gdi32.lib;uuid.lib + + + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + true + Level4 + %(DisableSpecificWarnings);4127 + MultiThreaded + MultiThreadedDebug + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + %(AdditionalOptions) -N + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib;comctl32.lib;comdlg32.lib;setupapi.lib;user32.lib;kernel32.lib;gdi32.lib;uuid.lib + + + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + true + Level4 + %(DisableSpecificWarnings);4127 + MultiThreaded + MultiThreadedDebug + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + %(AdditionalOptions) -N + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib;comctl32.lib;comdlg32.lib;setupapi.lib;user32.lib;kernel32.lib;gdi32.lib;uuid.lib + + + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + true + Level4 + %(DisableSpecificWarnings);4127 + MultiThreaded + MultiThreadedDebug + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32;UNICODE;_UNICODE + %(AdditionalOptions) -N + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib;comctl32.lib;comdlg32.lib;setupapi.lib;user32.lib;kernel32.lib;gdi32.lib;uuid.lib + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/config/bindview/bindview.vcxproj.Filters b/network/config/bindview/bindview.vcxproj.Filters new file mode 100644 index 00000000..964d777e --- /dev/null +++ b/network/config/bindview/bindview.vcxproj.Filters @@ -0,0 +1,36 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {4372001E-614D-43C8-ACA0-0ACF46A23F6E} + + + h;hpp;hxx;hm;inl;inc;xsd + {98620252-1A3C-4A35-8A10-A2376A0D90BF} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {CC73B9F9-A9E1-4AA1-88AE-951314EA9903} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/modem/fakemodem/ReadMe.md b/network/modem/fakemodem/ReadMe.md new file mode 100644 index 00000000..6d6cdbc1 --- /dev/null +++ b/network/modem/fakemodem/ReadMe.md @@ -0,0 +1,8 @@ +Fakemodem Driver +================ + +The Fakemodem sample demonstrates a simple controller-less modem driver. This driver supports sending and receiving AT commands using the `ReadFile`/`WriteFile` calls or via a TAPI interface using an application such as *HyperTerminal.* + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + diff --git a/network/modem/fakemodem/driver.c b/network/modem/fakemodem/driver.c new file mode 100644 index 00000000..f482c890 --- /dev/null +++ b/network/modem/fakemodem/driver.c @@ -0,0 +1,474 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Driver.c + +Abstract: + + This is a simple form of function driver for fakemodem device. The driver + doesn't handle any PnP and Power events because the framework provides + default behaviour for those events. This driver has enough support to + allow an user application (toast/notify.exe) to open the device + interface registered by the driver and send read, write or ioctl requests. + +Environment: + + Kernel mode + +--*/ + +#include "fakemodem.h" + +#ifdef ALLOC_PRAGMA + #pragma alloc_text (INIT, DriverEntry) + #pragma alloc_text (PAGE, FmEvtDeviceAdd) + #pragma alloc_text (PAGE, FmCreateDosDevicesSymbolicLink) + #pragma alloc_text (PAGE, FmDeviceCleanup) +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. DriverEntry specifies the other entry + points in the function driver, such as FmAddDevice and FmUnload. + +Parameters Description: + + DriverObject - represents the instance of the function driver that is loaded + into memory. DriverEntry must initialize members of DriverObject before it + returns to the caller. DriverObject is allocated by the system before the + driver is loaded, and it is released by the system after the system unloads + the function driver from memory. + + RegistryPath - represents the driver specific path in the Registry. + The function driver can use the path to store driver related data between + reboots. The path does not store hardware instance specific data. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG config; + + KdPrint(("Fakemode Function Driver Sample - Driver Framework Edition.\n")); + KdPrint(("Built %s %s\n", __DATE__, __TIME__)); + + WDF_DRIVER_CONFIG_INIT( &config, FmEvtDeviceAdd ); + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate( + DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, // Driver Config Info + WDF_NO_HANDLE + ); + + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDriverCreate failed with status 0x%x\n", status)); + } + + return status; +} + + +NTSTATUS +FmEvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + FmEvtDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. We create and initialize a device object to + represent a new instance of Fm device. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PFM_DEVICE_DATA fmDeviceData; + WDF_IO_QUEUE_CONFIG queueConfig; + WDF_OBJECT_ATTRIBUTES fdoAttributes; + WDFDEVICE hDevice; + WDFQUEUE defQueue; + + UNREFERENCED_PARAMETER(Driver); + + KdPrint( ("FmEvtDeviceAdd routine \n")); + + PAGED_CODE(); + + // + // Modem type is serial port. + // + WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_SERIAL_PORT); + + // + // Use Buffered IO. + // + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered); + + // + // Specify the size of device extension where we track per device + // context. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FM_DEVICE_DATA); + // + // Register a cleanup callback on the device to free up some resources at the + // time the device is deleted. + // + fdoAttributes.EvtCleanupCallback = FmDeviceCleanup; + // + // By opting for SynchronizationScopeDevice, we tell the framework to + // synchronize callbacks events of all the objects directly associated + // with the device. In this driver, we will associate queues. + // By doing that we don't have to worrry about synchronizing + // access to device-context by various io Events. + // Framework will serialize them by using an internal device-lock. + // + fdoAttributes.SynchronizationScope = WdfSynchronizationScopeDevice; + // + // Create a framework device object.This call will inturn create + // a WDM deviceobject, attach to the lower stack and set the + // appropriate flags and attributes. + // + + status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &hDevice); + + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDeviceCreate failed with Status code 0x%x\n", status)); + return status; + } + + // + // Get the DeviceExtension and initialize it. + // + fmDeviceData = FmDeviceDataGet(hDevice); + + // + // Tell the Framework that this device will need an interface + // + status = WdfDeviceCreateDeviceInterface( + hDevice, + (LPGUID) &GUID_DEVINTERFACE_MODEM, + NULL + ); + + if (!NT_SUCCESS (status)) { + KdPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status)); + return status; + } + + fmDeviceData->Flags = 0; + status = FmCreateDosDevicesSymbolicLink(hDevice, fmDeviceData); + + if (!NT_SUCCESS(status)) { + KdPrint( ("FmCreateDosDevicesSymbolicLink failed with Status code 0x%x\n", status)); + return status; + } + + // + // Initialize the context + // + fmDeviceData->BaudRate=1200; + fmDeviceData->LineControl = SERIAL_7_DATA | SERIAL_EVEN_PARITY | SERIAL_NONE_PARITY; + + // + // Register I/O callbacks to tell the framework that you are interested + // in handling IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL requests. + // In case a specific handler is not specified for one of these, + // the request will be dispatched to the EvtIoDefault handler, if any. + // If there is no EvtIoDefault handler, the request will be failed with + // STATUS_INVALID_DEVICE_REQUEST. + // WdfIoQueueDispatchParallel means that we are capable of handling + // all the I/O request simultaneously and we are responsible for protecting + // data that could be accessed by these callbacks simultaneously. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, + WdfIoQueueDispatchParallel); + + queueConfig.EvtIoRead = FmEvtIoRead; + queueConfig.EvtIoWrite = FmEvtIoWrite; + queueConfig.EvtIoDeviceControl = FmEvtIoDeviceControl; + + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate( + hDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &defQueue // pointer to default queue + ); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if (!NT_SUCCESS (status)) { + + // + // We don't need to cleanup symbolic link here. The destroy callback for + // the device object will do it. + // + return status; + } + + // + // Create a manual queue to hold pending read requests. By keeping + // them in the queue, framework takes care of cancelling them if the app exits + // + WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, + WdfIoQueueDispatchManual); + + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(hDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &fmDeviceData->FmReadQueue + ); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if (!NT_SUCCESS (status)) { + KdPrint( ("WdfIoQueueCreate failed 0x%x\n", status)); + return status; + } + + // + // Create a manual queue to hold pending ioctl wait mask requests. By keeping + // them in the queue, framework takes care of cancelling them if the app exits + // + WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, + WdfIoQueueDispatchManual); + + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(hDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &fmDeviceData->FmMaskWaitQueue + ); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if (!NT_SUCCESS (status)) { + KdPrint( ("WdfIoQueueCreate failed 0x%x\n", status)); + return status; + } + + return status; +} + + +VOID +FmDeviceCleanup( + WDFOBJECT Device + ) +/*++ +Routine Description: + + This event is called when the device object is destroyed. + Cleanup any associated data. + +Arguments: + +Return Value: + + VOID + +--*/ +{ + PFM_DEVICE_DATA fmData; + + PAGED_CODE(); + + fmData = FmDeviceDataGet((WDFDEVICE)Device); + + if (fmData->Flags & REG_VALUE_CREATED_FLAG) { + RtlDeleteRegistryValue( + RTL_REGISTRY_DEVICEMAP, + L"SERIALCOMM", + fmData->PdoName.Buffer + ); + } +} + +NTSTATUS +FmCreateDosDevicesSymbolicLink( + WDFDEVICE Device, + PFM_DEVICE_DATA FmDeviceData + ) +{ + NTSTATUS status; + UNICODE_STRING comPort; + UNICODE_STRING pdoName; + UNICODE_STRING symbolicLink; + WDFKEY hKey = NULL; + DECLARE_CONST_UNICODE_STRING(valueName, L"PortName"); + WDFSTRING string = NULL; + WDFMEMORY memory; + WDF_OBJECT_ATTRIBUTES memoryAttributes; + size_t bufferLength; + + + PAGED_CODE(); + + symbolicLink.Buffer = NULL; + + // + // Open the device registry and read the "PortName" value written by the + // class installer. + // + status = WdfDeviceOpenRegistryKey(Device, + PLUGPLAY_REGKEY_DEVICE, + STANDARD_RIGHTS_ALL, + NULL, // PWDF_OBJECT_ATTRIBUTES + &hKey); + + if (!NT_SUCCESS (status)) { + goto Error; + } + status = WdfStringCreate( + NULL, + WDF_NO_OBJECT_ATTRIBUTES , + &string + ); + + if (!NT_SUCCESS(status)) { + goto Error; + } + + // + // Retrieve the value of ValueName from registry + // + status = WdfRegistryQueryString( + hKey, + &valueName, + string + ); + + + if (!NT_SUCCESS (status)) { + goto Error; + } + + // + // Retrieve the UNICODE_STRING from string object + // + WdfStringGetUnicodeString( + string, + &comPort + ); + + WdfRegistryClose(hKey); + hKey = NULL; + + symbolicLink.Length=0; + symbolicLink.MaximumLength = sizeof(OBJECT_DIRECTORY) + comPort.MaximumLength; + + symbolicLink.Buffer = ExAllocatePoolWithTag(PagedPool, + symbolicLink.MaximumLength + sizeof(WCHAR), + 'wkaF'); + + if (symbolicLink.Buffer == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Error; + } + RtlZeroMemory(symbolicLink.Buffer, symbolicLink.MaximumLength); + RtlAppendUnicodeToString(&symbolicLink, OBJECT_DIRECTORY); + RtlAppendUnicodeStringToString(&symbolicLink, &comPort); + // + // This DDI will get the underlying PDO name and create a symbolic to that + // because our FDO doesn't have a name. + // + status = WdfDeviceCreateSymbolicLink(Device, + &symbolicLink); + + if (!NT_SUCCESS(status)) { + goto Error; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&memoryAttributes); + memoryAttributes.ParentObject = Device; + + status = WdfDeviceAllocAndQueryProperty(Device, + DevicePropertyPhysicalDeviceObjectName, + PagedPool, + &memoryAttributes, + &memory); + if (!NT_SUCCESS(status)) { + // + // We expect a zero length buffer. Anything else is fatal. + // + goto Error; + } + + pdoName.Buffer = WdfMemoryGetBuffer(memory, &bufferLength); + + if (pdoName.Buffer == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Error; + + } + pdoName.MaximumLength = (USHORT) bufferLength; + pdoName.Length = (USHORT) bufferLength - sizeof(UNICODE_NULL); + + status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP, + L"SERIALCOMM", + pdoName.Buffer, + REG_SZ, + comPort.Buffer, + comPort.Length); + + if (!NT_SUCCESS(status)) { + goto Error; + } + FmDeviceData->Flags |= REG_VALUE_CREATED_FLAG; + // + // Store it so it can be deleted later. + // + + FmDeviceData->PdoName = pdoName; + + Error: + + if (symbolicLink.Buffer != NULL) { + ExFreePool(symbolicLink.Buffer); + } + + if (hKey != NULL) { + WdfRegistryClose(hKey); + } + if (string != NULL) { + WdfObjectDelete(string); + } + + return status; +} + diff --git a/network/modem/fakemodem/fakemodem.h b/network/modem/fakemodem/fakemodem.h new file mode 100644 index 00000000..bb06c2fe --- /dev/null +++ b/network/modem/fakemodem/fakemodem.h @@ -0,0 +1,156 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + fakemodem.h + +Abstract: + + Header file for the toaster driver modules. + +Environment: + + Kernel mode + +--*/ + + +#if !defined(_FAKEMODEM_H_) +#define _FAKEMODEM_H_ + +#include +#include +#include +#include +#define NTSTRSAFE_LIB +#include +#include + +#ifdef DEFINE_GUID + +DEFINE_GUID(GUID_DEVINTERFACE_MODEM,0x2c7089aa, 0x2e0e,0x11d1,0xb1, 0x14, 0x00, 0xc0, 0x4f, 0xc2, 0xaa, 0xe4); + +#endif //DEFINE_GUID + + +#define OBJECT_DIRECTORY L"\\DosDevices\\" + +#define READ_BUFFER_SIZE 128 + +#define COMMAND_MATCH_STATE_IDLE 0 +#define COMMAND_MATCH_STATE_GOT_A 1 +#define COMMAND_MATCH_STATE_GOT_T 2 + +// +// This defines the bit used to control whether the device is sending +// a break. When this bit is set the device is sending a space (logic 0). +// +// Most protocols will assume that this is a hangup. + + +#define SERIAL_LCR_BREAK 0x40 + +// +// These defines are used to define the line control register +// + +#define SERIAL_5_DATA ((UCHAR)0x00) +#define SERIAL_6_DATA ((UCHAR)0x01) +#define SERIAL_7_DATA ((UCHAR)0x02) +#define SERIAL_8_DATA ((UCHAR)0x03) +#define SERIAL_DATA_MASK ((UCHAR)0x03) + +#define SERIAL_1_STOP ((UCHAR)0x00) +#define SERIAL_1_5_STOP ((UCHAR)0x04) // Only valid for 5 data bits +#define SERIAL_2_STOP ((UCHAR)0x04) // Not valid for 5 data bits +#define SERIAL_STOP_MASK ((UCHAR)0x04) + +#define SERIAL_NONE_PARITY ((UCHAR)0x00) +#define SERIAL_ODD_PARITY ((UCHAR)0x08) +#define SERIAL_EVEN_PARITY ((UCHAR)0x18) +#define SERIAL_MARK_PARITY ((UCHAR)0x28) +#define SERIAL_SPACE_PARITY ((UCHAR)0x38) +#define SERIAL_PARITY_MASK ((UCHAR)0x38) + +#define REG_VALUE_CREATED_FLAG 0x1 +// +// The device extension for the device object +// +typedef struct _FM_DEVICE_DATA +{ + + UNICODE_STRING PdoName; //save this so that we can use it to delete the registry value later + WDFQUEUE FmReadQueue; // Staging area for pending Read requests + WDFQUEUE FmMaskWaitQueue; + ULONG CurrentMask; + SERIAL_TIMEOUTS CurrentTimeouts; + ULONG ReadBufferBegin; + ULONG ReadBufferEnd; + ULONG BytesInReadBuffer; + UCHAR CommandMatchState; + BOOLEAN ConnectCommand; + BOOLEAN IgnoreNextChar; + BOOLEAN CapsQueried; + ULONG ModemStatus; + BOOLEAN CurrentlyConnected; + BOOLEAN ConnectionStateChanged; + UCHAR ReadBuffer[READ_BUFFER_SIZE]; + ULONG BaudRate; + UCHAR LineControl; + UCHAR ValidDataMask; + UCHAR Flags; + +} FM_DEVICE_DATA, *PFM_DEVICE_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FM_DEVICE_DATA, FmDeviceDataGet) + +#define FM_COM_PORT_STRING_LENGTH 80 + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD FmEvtDeviceAdd; +EVT_WDF_DEVICE_CONTEXT_CLEANUP FmDeviceCleanup; + +EVT_WDF_IO_QUEUE_IO_READ FmEvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE FmEvtIoWrite; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL FmEvtIoDeviceControl; + +NTSTATUS +FmCreateDosDevicesSymbolicLink( + WDFDEVICE Device, + PFM_DEVICE_DATA FmDeviceData + ); + +VOID +ProcessConnectionStateChange( + IN PFM_DEVICE_DATA FmDeviceData + ); +VOID +ProcessWriteBytes( + PFM_DEVICE_DATA FmDeviceData, + PUCHAR Characters, + ULONG Length + ); + + +VOID +PutCharInReadBuffer( + PFM_DEVICE_DATA FmDeviceData, + UCHAR Character + ); + + + +VOID +ProcessReadBuffer( + IN PFM_DEVICE_DATA FmDeviceData, + IN PUCHAR SystemBuffer, + IN ULONG Length, + OUT PULONG ByesToMove + ); + +#endif // _FAKEMODEM_H + + diff --git a/network/modem/fakemodem/fakemodem.sln b/network/modem/fakemodem/fakemodem.sln new file mode 100644 index 00000000..6804feb5 --- /dev/null +++ b/network/modem/fakemodem/fakemodem.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fakemodem", "fakemodem.vcxproj", "{3744C5BD-A12A-429F-9312-DFD04AC69078}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3744C5BD-A12A-429F-9312-DFD04AC69078}.Debug|Win32.ActiveCfg = Debug|Win32 + {3744C5BD-A12A-429F-9312-DFD04AC69078}.Debug|Win32.Build.0 = Debug|Win32 + {3744C5BD-A12A-429F-9312-DFD04AC69078}.Release|Win32.ActiveCfg = Release|Win32 + {3744C5BD-A12A-429F-9312-DFD04AC69078}.Release|Win32.Build.0 = Release|Win32 + {3744C5BD-A12A-429F-9312-DFD04AC69078}.Debug|x64.ActiveCfg = Debug|x64 + {3744C5BD-A12A-429F-9312-DFD04AC69078}.Debug|x64.Build.0 = Debug|x64 + {3744C5BD-A12A-429F-9312-DFD04AC69078}.Release|x64.ActiveCfg = Release|x64 + {3744C5BD-A12A-429F-9312-DFD04AC69078}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/network/modem/fakemodem/fakemodem.vcxproj b/network/modem/fakemodem/fakemodem.vcxproj new file mode 100644 index 00000000..c2a2dd8d --- /dev/null +++ b/network/modem/fakemodem/fakemodem.vcxproj @@ -0,0 +1,168 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {3744C5BD-A12A-429F-9312-DFD04AC69078} + $(MSBuildProjectName) + 1 + Debug + Win32 + {74C5BB58-0CB8-4E8E-8FD7-065CBE6051A7} + + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + $(InfArch) + true + .\$(IntDir)\mdmfake.inf + + + + fakemodem + + + fakemodem + + + fakemodem + + + fakemodem + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/modem/fakemodem/fakemodem.vcxproj.Filters b/network/modem/fakemodem/fakemodem.vcxproj.Filters new file mode 100644 index 00000000..7c8d71ab --- /dev/null +++ b/network/modem/fakemodem/fakemodem.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {66A2BE8B-87F0-4F4B-AB28-9C66471A8B7B} + + + h;hpp;hxx;hm;inl;inc;xsd + {F31D838F-78A7-4140-B15C-4C9485DDB150} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {8A726BCF-D9F0-4948-8219-B0CCAC8AA786} + + + inf;inv;inx;mof;mc; + {A5962758-E097-4C00-A4FB-3A630B0E5405} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/network/modem/fakemodem/ioctl.c b/network/modem/fakemodem/ioctl.c new file mode 100644 index 00000000..445910bd --- /dev/null +++ b/network/modem/fakemodem/ioctl.c @@ -0,0 +1,790 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + ioctl.c + +Abstract: + + This is the ioctl handler for the fakemodem. +Environment: + + Kernel mode + +--*/ + +#include "fakemodem.h" + + + +VOID +FmEvtIoDeviceControl( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t OutputBufferLength, + IN size_t InputBufferLength, + IN ULONG IoControlCode + ) +/*++ +Routine Description: + + This event is called when the framework receives IRP_MJ_DEVICE_CONTROL + requests from the system. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + Request - Handle to a framework request object. + + OutputBufferLength - length of the request's output buffer, + if an output buffer is available. + InputBufferLength - length of the request's input buffer, + if an input buffer is available. + + IoControlCode - the driver-defined or system-defined I/O control code + (IOCTL) that is associated with the request. + +Return Value: + + VOID + +--*/ +{ + + PFM_DEVICE_DATA fmDeviceData = FmDeviceDataGet(WdfIoQueueGetDevice(Queue)); + NTSTATUS status ; + PVOID requestBuffer; + ULONG information = 0; + size_t bufSize; + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + status = STATUS_SUCCESS; + + switch (IoControlCode) { + + case IOCTL_SERIAL_GET_WAIT_MASK: { + + status = WdfRequestRetrieveOutputBuffer ( Request, + sizeof(ULONG), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(( "Could not get request memory buffer status %X\n", status)); + information = 0; + break; + } + + *((PULONG)requestBuffer)= fmDeviceData->CurrentMask; + + information = sizeof(ULONG); + + break; + } + + case IOCTL_SERIAL_SET_WAIT_MASK: { + + WDFREQUEST currentWaitRequest=NULL; + ULONG newMask; + + status = WdfRequestRetrieveInputBuffer (Request, + sizeof(ULONG), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(("Could not get request memory buffer status %X\n", status)); + information = 0; + break; + + } + else { + NTSTATUS tempStatus; + // + // get rid of the current wait + // + + newMask = *((ULONG *)requestBuffer); + + fmDeviceData->CurrentMask = newMask; + + KdPrint(("FAKEMODEM: set wait mask, %08lx\n", newMask)); + + tempStatus = WdfIoQueueRetrieveNextRequest(fmDeviceData->FmMaskWaitQueue, + ¤tWaitRequest); + // save the new mask + + if (NT_SUCCESS(tempStatus)) {//currentWaitRequest != NULL) { + + PULONG outBuffer; + + KdPrint(("FAKEMODEM: set wait mask- complete wait\n")); + + // + // The length was validated already. + // + tempStatus = WdfRequestRetrieveOutputBuffer(currentWaitRequest, + sizeof(ULONG), + &outBuffer, + &bufSize); + if (NT_SUCCESS(tempStatus)) { + if (outBuffer) { // FIXME SAL + *outBuffer = 0; + } + + WdfRequestCompleteWithInformation(currentWaitRequest, + STATUS_SUCCESS, + sizeof(ULONG)); + } else { + WdfRequestComplete(currentWaitRequest, tempStatus); + } + } + information = sizeof(ULONG); + } + break; + } + + case IOCTL_SERIAL_WAIT_ON_MASK: { + + WDFREQUEST currentWaitRequest = NULL; + NTSTATUS tempStatus; + + status = WdfRequestRetrieveOutputBuffer (Request, + sizeof(ULONG), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + + KdPrint(("Could not get request memory buffer status %X\n", status)); + information = 0; + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + KdPrint(("FAKEMODEM: wait on mask\n")); + // + // remove the current request if any + // + tempStatus = WdfIoQueueRetrieveNextRequest(fmDeviceData->FmMaskWaitQueue, + ¤tWaitRequest); + + if (NT_SUCCESS(tempStatus)) { //currentWaitRequest != NULL) { + + PULONG outBuffer; + + KdPrint(("FAKEMODEM: wait on mask- complete wait\n")); + + // + // The length was validated already. + // + + tempStatus = WdfRequestRetrieveOutputBuffer(currentWaitRequest, + sizeof(ULONG), + &outBuffer, + &bufSize); + if(NT_SUCCESS(tempStatus)) { + if (outBuffer) { // FIXME SAL + *((PULONG)outBuffer) = 0; + } + + WdfRequestCompleteWithInformation(currentWaitRequest, + STATUS_SUCCESS, + sizeof(ULONG)); + } else { + WdfRequestComplete(currentWaitRequest, tempStatus); + } + + } + + if (fmDeviceData->CurrentMask == 0) { + // + // can only set if mask is not zero + // + status=STATUS_UNSUCCESSFUL; + + } else { + + // + // add the current request to the wait queue + // + status = WdfRequestForwardToIoQueue(Request, fmDeviceData->FmMaskWaitQueue); + if (!NT_SUCCESS(status)) { + WdfRequestCompleteWithInformation(Request, STATUS_UNSUCCESSFUL, 0); + return; + } + + status=STATUS_PENDING; + } + + break; + } + + case IOCTL_SERIAL_PURGE: { + + ULONG mask; + status = WdfRequestRetrieveInputBuffer (Request, + sizeof(ULONG), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(("Could not get request memory buffer status %X\n", status)); + information = 0; + break; + + } + mask=*((PULONG)requestBuffer); + + if (mask & SERIAL_PURGE_RXABORT) { + + WdfIoQueuePurge( fmDeviceData->FmReadQueue, + WDF_NO_EVENT_CALLBACK, + WDF_NO_CONTEXT ); + + WdfIoQueueStart(fmDeviceData->FmReadQueue); + } + information = sizeof(ULONG); + break; + } + + + case IOCTL_SERIAL_GET_MODEMSTATUS: { + + status = WdfRequestRetrieveOutputBuffer ( Request, + sizeof(ULONG), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(( "Could not get request memory buffer status %X\n", status)); + information = 0; + break; + } + + information = sizeof(ULONG); + + *((PULONG)requestBuffer) = fmDeviceData->ModemStatus; + + break; + } + + + case IOCTL_SERIAL_SET_TIMEOUTS: { + PSERIAL_TIMEOUTS NewTimeouts; + + status = WdfRequestRetrieveInputBuffer (Request, + sizeof(SERIAL_TIMEOUTS), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(("Could not get request memory buffer status %X\n", status)); + information = 0; + break; + + } + NewTimeouts= ((PSERIAL_TIMEOUTS)(requestBuffer)); + + if ((NewTimeouts->ReadIntervalTimeout == MAXULONG) && + (NewTimeouts->ReadTotalTimeoutMultiplier == MAXULONG) && + (NewTimeouts->ReadTotalTimeoutConstant == MAXULONG)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + information = sizeof(SERIAL_TIMEOUTS); + + break; + } + + case IOCTL_SERIAL_GET_TIMEOUTS: { + + status = WdfRequestRetrieveOutputBuffer ( Request, + sizeof(SERIAL_TIMEOUTS), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(( "Could not get request memory buffer status %X\n", status)); + information = 0; + break; + } + + *((PSERIAL_TIMEOUTS)requestBuffer) = + fmDeviceData->CurrentTimeouts; + + information = sizeof(SERIAL_TIMEOUTS); + + break; + } + + case IOCTL_SERIAL_GET_COMMSTATUS: { + + PSERIAL_STATUS serialStatus ; + status = WdfRequestRetrieveOutputBuffer ( Request, + sizeof(SERIAL_STATUS), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(( "Could not get request memory buffer status %X\n", status)); + information = 0; + break; + } + + serialStatus = (PSERIAL_STATUS) requestBuffer; + + RtlZeroMemory( serialStatus, sizeof(*serialStatus) ); // FIXME SAL + + serialStatus->AmountInInQueue = fmDeviceData->BytesInReadBuffer; + + information = sizeof(SERIAL_STATUS); + + break; + } + + case IOCTL_SERIAL_SET_DTR: + case IOCTL_SERIAL_CLR_DTR: { + + if (IoControlCode == IOCTL_SERIAL_SET_DTR) { + + // + // raising DTR + // + + fmDeviceData->ModemStatus=SERIAL_DTR_STATE | SERIAL_DSR_STATE; + + KdPrint(("FAKEMODEM: Set DTR\n")); + + } else { + // + // dropping DTR, drop connection if there is one + // + KdPrint(("FAKEMODEM: Clear DTR\n")); + + if (fmDeviceData->CurrentlyConnected == TRUE) { + // + // not connected any more + // + fmDeviceData->CurrentlyConnected=FALSE; + + fmDeviceData->ConnectionStateChanged=TRUE; + } + } + + ProcessConnectionStateChange( fmDeviceData); + + information = sizeof(ULONG); + + break; + } + + case IOCTL_SERIAL_SET_QUEUE_SIZE: { + + status = WdfRequestRetrieveInputBuffer (Request, + sizeof(SERIAL_QUEUE_SIZE), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(("Could not get request memory buffer status %X\n", status)); + information = 0; + break; + + } + + // + // This ioctl doesn't do anyhing except test for the size of the + // buffer passed in. + // + information = sizeof(SERIAL_QUEUE_SIZE); + break; + } + + + case IOCTL_SERIAL_SET_BAUD_RATE: { + + status = WdfRequestRetrieveInputBuffer (Request, + sizeof(SERIAL_BAUD_RATE), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(("Could not get request memory buffer status %X\n", status)); + information = 0; + break; + } + else { + fmDeviceData->BaudRate = ((PSERIAL_BAUD_RATE)requestBuffer)->BaudRate; + } + information = sizeof(SERIAL_BAUD_RATE); + break; + } + + case IOCTL_SERIAL_GET_BAUD_RATE: { + + PSERIAL_BAUD_RATE pBaudRate ; + + status = WdfRequestRetrieveOutputBuffer ( Request, + sizeof(SERIAL_BAUD_RATE), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(( "Could not get request memory buffer status %X\n", status)); + information = 0; + break; + } + + pBaudRate = (PSERIAL_BAUD_RATE)requestBuffer; + pBaudRate->BaudRate = fmDeviceData->BaudRate; + information = sizeof(SERIAL_BAUD_RATE); + + break; + } + + case IOCTL_SERIAL_SET_LINE_CONTROL: { + + PSERIAL_LINE_CONTROL pLineControl ; + UCHAR LData = 0; + UCHAR LStop = 0; + UCHAR LParity = 0; + UCHAR Mask = 0xff; + + status = WdfRequestRetrieveInputBuffer (Request, + sizeof(SERIAL_LINE_CONTROL), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(("Could not get request memory buffer status %X\n", status)); + information = 0; + break; + + } + pLineControl = ((PSERIAL_LINE_CONTROL)requestBuffer); + + switch(pLineControl->WordLength) + { + case 5: { + + LData = SERIAL_5_DATA; + Mask = 0x1f; + break; + + } + case 6: { + + LData = SERIAL_6_DATA; + Mask = 0x3f; + break; + + } + case 7: { + + LData = SERIAL_7_DATA; + Mask = 0x7f; + break; + + } + case 8: { + + LData = SERIAL_8_DATA; + break; + + } + default: { + + status = STATUS_INVALID_PARAMETER; + + } + } + + if (status != STATUS_SUCCESS) + { + break; + } + + switch (pLineControl->Parity) { + + case NO_PARITY: { + LParity = SERIAL_NONE_PARITY; + break; + + } + case EVEN_PARITY: { + LParity = SERIAL_EVEN_PARITY; + break; + + } + case ODD_PARITY: { + LParity = SERIAL_ODD_PARITY; + break; + + } + case SPACE_PARITY: { + LParity = SERIAL_SPACE_PARITY; + break; + + } + case MARK_PARITY: { + LParity = SERIAL_MARK_PARITY; + break; + + } + default: { + + status = STATUS_INVALID_PARAMETER; + break; + } + + } + + if (status != STATUS_SUCCESS) + { + break; + } + + switch (pLineControl->StopBits) { + + case STOP_BIT_1: { + + LStop = SERIAL_1_STOP; + break; + } + + case STOP_BITS_1_5: { + + if (LData != SERIAL_5_DATA) { + + status = STATUS_INVALID_PARAMETER; + break; + } + LStop = SERIAL_1_5_STOP; + break; + + } + case STOP_BITS_2: { + + if (LData == SERIAL_5_DATA) { + + status = STATUS_INVALID_PARAMETER; + break; + } + + LStop = SERIAL_2_STOP; + break; + } + + default: { + + status = STATUS_INVALID_PARAMETER; + } + + } + + if (status != STATUS_SUCCESS) + { + break; + } + + fmDeviceData->LineControl = + (UCHAR)((fmDeviceData->LineControl & SERIAL_LCR_BREAK) | + (LData | LParity | LStop)); + + fmDeviceData->ValidDataMask = Mask; + information = sizeof(SERIAL_LINE_CONTROL); + break; + } + + case IOCTL_SERIAL_GET_LINE_CONTROL: { + PSERIAL_LINE_CONTROL pLineControl ; + + status = WdfRequestRetrieveOutputBuffer ( Request, + sizeof(SERIAL_LINE_CONTROL), + &requestBuffer, + &bufSize ); + if( !NT_SUCCESS(status) ) { + KdPrint(( "Could not get request memory buffer status %X\n", status)); + information = 0; + break; + } + + + pLineControl = + (PSERIAL_LINE_CONTROL)requestBuffer; + + RtlZeroMemory(requestBuffer, + bufSize); + + + switch (fmDeviceData->LineControl & SERIAL_DATA_MASK) { + case SERIAL_5_DATA: + pLineControl->WordLength = 5; + break; + case SERIAL_6_DATA: + pLineControl->WordLength = 6; + break; + case SERIAL_7_DATA: + pLineControl->WordLength = 7; + break; + case SERIAL_8_DATA: + pLineControl->WordLength = 8; + break; + default: + break; + + } + + switch (fmDeviceData->LineControl & SERIAL_PARITY_MASK) { + case SERIAL_NONE_PARITY: + pLineControl->Parity = NO_PARITY; + break; + case SERIAL_ODD_PARITY: + pLineControl->Parity = ODD_PARITY; + break; + case SERIAL_EVEN_PARITY: + pLineControl->Parity = EVEN_PARITY; + break; + case SERIAL_MARK_PARITY: + pLineControl->Parity = MARK_PARITY; + break; + case SERIAL_SPACE_PARITY: + pLineControl->Parity = SPACE_PARITY; + break; + default: + break; + + } + + if (fmDeviceData->LineControl & SERIAL_2_STOP) { + + if (pLineControl->WordLength == 5) { + + pLineControl->StopBits = STOP_BITS_1_5; + + } else { + + pLineControl->StopBits = STOP_BITS_2; + + } + + } else { + + pLineControl->StopBits = STOP_BIT_1; + } + + information = sizeof(SERIAL_LINE_CONTROL); + + break; + } + + case IOCTL_SERIAL_SET_RTS: + case IOCTL_SERIAL_CLR_RTS: + case IOCTL_SERIAL_SET_XON: + case IOCTL_SERIAL_SET_XOFF: + case IOCTL_SERIAL_SET_CHARS: + case IOCTL_SERIAL_GET_CHARS: + case IOCTL_SERIAL_GET_HANDFLOW: + case IOCTL_SERIAL_SET_HANDFLOW: + case IOCTL_SERIAL_RESET_DEVICE: { + // + // NOTE: The application expects STATUS_SUCCESS for these ioctsl. + // so don't merge this with default. + // + break; + } + default: + status=STATUS_NOT_SUPPORTED; + break; + + } + + if (status != STATUS_PENDING) { + // + // complete now if not pending + // + WdfRequestCompleteWithInformation(Request, status, information); + } +} + + + +VOID +ProcessConnectionStateChange( + IN PFM_DEVICE_DATA FmDeviceData + ) +{ + WDFREQUEST currentWaitRequest = NULL; + NTSTATUS status; + + if (FmDeviceData->ConnectionStateChanged) { + + // + // state changed + // + + FmDeviceData->ConnectionStateChanged=FALSE; + + if (FmDeviceData->CurrentlyConnected) { + // + // now it is connected, raise CD + // + FmDeviceData->ModemStatus |= SERIAL_DCD_STATE; + + + } else { + // + // not connected any more, clear CD + // + FmDeviceData->ModemStatus &= ~(SERIAL_DCD_STATE); + + } + + + if (FmDeviceData->CurrentMask & SERIAL_EV_RLSD) { + + // + // app want's to know about these changes, tell it + // + status = WdfIoQueueRetrieveNextRequest(FmDeviceData->FmMaskWaitQueue, + ¤tWaitRequest); + if(!NT_SUCCESS(status)){ + ASSERT(status == STATUS_NO_MORE_ENTRIES); + } + } + + } + + + if (currentWaitRequest != NULL) { + + PULONG outBuffer; + size_t bufSize; + + KdPrint(("FAKEMODEM: ProcessConectionState\n")); + + // + // The length was validated already. + // + + (VOID)WdfRequestRetrieveOutputBuffer(currentWaitRequest, + sizeof(ULONG), + &outBuffer, + &bufSize); + + if (outBuffer) { // FIXME SAL + *outBuffer = SERIAL_EV_RLSD; + } + + WdfRequestCompleteWithInformation(currentWaitRequest, + STATUS_SUCCESS, + sizeof(ULONG)); + + } + + return; + +} + + diff --git a/network/modem/fakemodem/mdmfake.inx b/network/modem/fakemodem/mdmfake.inx new file mode 100644 index 00000000..4342404a --- /dev/null +++ b/network/modem/fakemodem/mdmfake.inx @@ -0,0 +1,267 @@ +; +; "Fakemodem" Controllerless driver illustrative example +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +; ******************************************************************* +; * * +; * The following INF is used in order to load this sample driver. * +; * Further information about INF design can be found in the DDK * +; * * +; ******************************************************************* + +; ------------------------------------------------------------------------------ +; Check final modem INF with CINF tool then test with +; NDIS Test prior to final distribution. +;------------------------------------------------------------------------------- + +;------------------------------------------------------------------------------------------------------ +;This section must specify the following entries with the indicated values: +;Signature entry as the $Windows NT$ value. This value indicates that the INF is only valid for NT-based operating systems. +;Class entry as "MODEM". +;ClassGUID entry as {4D36E96D-E325-11CE-BFC1-08002BE10318}. +;Provider is the company responsible for the provision of the INF. +;DriverVer must be in the following format = mm/dd/yyyy[,x.y.v.z] + +[Version] +Signature="$WINDOWS NT$" +Class=Modem +ClassGUID={4D36E96D-E325-11CE-BFC1-08002BE10318} +Provider=%Mfg% +DriverVer=11/11/2002,5.1.3711 + +;INF files that are not distributed with the OS should contain the following line: +CatalogFile=KmdfSamples.cat + +;INF files that are to be distributed with the OS that load a service and or files on the CD, should contain the following line: +;Layoutfile=layout.inf + +;----------------------------------------------------------------------------------------------------------------------------; Below is list of manufacturers that will appear in the +; Install New Modem wizard's list of manufacturers as well as define what sections to install ID's from +; The vendor will be required to change the provider key before a +; driver submission is made + +[Manufacturer] +%Generic% = Generic,NT$ARCH$ + + +;------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +;This section references the INF-writer-defined DDInstall and DDInstall.Services sections for the Modem device, and specifies the hardware identifier for the Modem device. + +[Generic.NT$ARCH$] +%ModemX% = ModemX, {b85b7c50-6a01-11d2-b841-00c04fad5171}\fakemodem + +;------------------------------------------------------------------------------------------------------- +; For detailed explanation of DDInstall.Services section, please reference DDK. +; Installation section references other INF sections to be installed for a specific modem. +; The AddReg line points to sections of the INF file that list registry entries to be added when +; installing this modem. For example, the line below instructs the installer to add the registry +; entries listed in the following sections of this INF file: +; [All], [MfgAddReg], [ExtraCRLFResponses], [ModemX.AddReg], [INTERNAL] +; It is not necessary to break up the registry additions into different sections in the +; INF. However, breaking out common entries into sections can help to reduce the size of +; the INF if multiple modems are installed from the same INF and have common registry +; entries. + +[ModemX.NT] +CopyFiles = CopyFileSection +AddReg = All, MfgAddReg, ExtraCRLFResponses, ModemX.AddReg, INTERNAL + +[ModemX.NT.Services] +AddService = fakemdm, 0x00000000, FakeModm_Service_Inst, FakeModm_Logging_Inst + +[ModemX.NT.HW] +AddReg = LowerFilterAddReg + +[LowerFilterAddReg] +HKR,,"LowerFilters",0x00010000,fakemdm + +[FakeModm_Service_Inst] +DisplayName = %ModemX% +ServiceType = 1 +StartType = 3 +ErrorControl = 0 +ServiceBinary = %12%\fakemodem.sys + +;-------------------------------------------------------------------------------------------------------------------- +; An event-log-install-section + +[FakeModm_Logging_Inst] +AddReg = FakeModm_Logging_Inst_AddReg + +[FakeModm_Logging_Inst_AddReg] +HKR,,EventMessageFile,0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll;%%SystemRoot%%\System32\drivers\Fakemodem.sys" +HKR,,TypesSupported,0x00010001,7 + +[SourceDisksNames] +99=%FakeDisk%, disk1,,"" + +[SourceDisksFiles] +fakemodem.sys = 99 + +;------------------------------------------------------------------------------------------------------------------- +; Section used to copy files required for device to function + +[CopyFileSection] +fakemodem.sys + +;-------------------------------------------------------------------------------------------------------------------------- +; This section lists the Default location to copy the files listed in the copyfiles directive +; 12 = %Windows%\System32\Drivers + +[DestinationDirs] +CopyFileSection=12 +DefaultDestDir=12 + + +; +;--- ModemX Coinstaller installation ------ +; +[DestinationDirs] +ModemX_CoInstaller_CopyFiles = 11 + +[ModemX.NT.CoInstallers] +AddReg=ModemX_CoInstaller_AddReg +CopyFiles=ModemX_CoInstaller_CopyFiles + +[ModemX_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[ModemX_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=99 ; make sure the number matches with SourceDisksNames + +[ModemX.NT.Wdf] +KmdfService = fakemdm, fakemdm_wdfsect +[fakemdm_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + + + +;------------------------------------------------------------------------------------------------------- +; DDK procalc tool can be used to check properties settings which describes modem' properties. +; Generally, customization entries are added here which override commands from the All section + +[ModemX.AddReg] +HKR,,Properties, 1, 80,01,00,00, ff,00,00,00, ff,00,00,00, 07,00,00,00, 0f,00,00,00, f7,03,00,00, 00,c2,01,00, 40,38,00,00 +HKR,,InactivityScale, 1, 0a,00,00,00 +HKR, Settings, InactivityTimeout,, "S30=<#>" +HKR, Settings, ErrorControl_On,, "\N3" +HKR, Settings, ErrorControl_Forced,, "\N2" +HKR, Settings, Compression_On,, "%%C3" +HKR, Settings, SpeedNegotiation_Off,, "N0" +HKR, Settings, SpeedNegotiation_On,, "N1" + +;-------------------------------------------------------------------------------------------------------------------------- +;This section will define the type of modem and thus present the appropriate icon in the Device manager + +[INTERNAL] +HKR,, DeviceType, 1, 02 + +;------------------------------------------------------------------------------------------------------------------------- +; Responses section contains all of the appropriate and required responses in order for the O/S to understand the +; Modem responses to system queries. + +[All] +HKR,,ConfigDialog,,modemui.dll +HKR,,EnumPropPages,,"modemui.dll,EnumPropPages" +HKR,,PortSubClass,1,02 +HKR,Init,1,,"AT" +HKR,Init, 2,, "AT &F E0 V1 &D2 &C1 S0=0 W2 S95=47" + +[MfgAddReg] +HKR,, InactivityScale, 1, 01,00,00,00 +HKR, Monitor, 1,, "ATS0=0" +HKR, Monitor, 2,, "None" +HKR, Hangup, 1,, "ATH" +HKR, Answer, 1,, "ATA" +HKR,, Reset,, "ATZ" +HKR, Settings, Prefix,, "AT" +HKR, Settings, Terminator,, "" +HKR, Settings, DialPrefix,, "D" +HKR, Settings, DialSuffix,, ";" +HKR, Settings, SpeakerVolume_Low,, "L1" +HKR, Settings, SpeakerVolume_Med,, "L2" +HKR, Settings, SpeakerVolume_High,, "L3" +HKR, Settings, SpeakerMode_Off,, "M0" +HKR, Settings, SpeakerMode_Dial,, "M1" +HKR, Settings, SpeakerMode_On,, "M2" +HKR, Settings, SpeakerMode_Setup,, "M3" +HKR, Settings, FlowControl_Off,, "&K0" +HKR, Settings, FlowControl_Hard,, "&K3" +HKR, Settings, FlowControl_Soft,, "&K4" +HKR, Settings, ErrorControl_On,, "\N5" +HKR, Settings, ErrorControl_Off,, "\N0" +HKR, Settings, ErrorControl_Forced,, "\N4" +HKR, Settings, Compression_On,, "%%C1" +HKR, Settings, Compression_Off,, "%%C0" +HKR, Settings, Modulation_CCITT,, "B0 " +HKR, Settings, Modulation_Bell,, "B1 " +HKR, Settings, SpeedNegotiation_Off,, "*S0" +HKR, Settings, SpeedNegotiation_On,, "*S1" +HKR, Settings, Pulse,, "P" +HKR, Settings, Tone,, "T" +HKR, Settings, Blind_Off,, "X4" +HKR, Settings, Blind_On,, "X3" +HKR, Settings, CallSetupFailTimer,, "S7=<#>" + +HKR, Responses, "+FCERROR", 1, 03, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "BLACKLISTED", 1, 03, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "BUSY", 1, 06, 00, 00,00,00,00, 00,00,00,00 + +HKR, Responses, "CONNECT", 1, 02, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "DATA", 1, 03, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "DELAYED", 1, 03, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "ERROR", 1, 03, 00, 00,00,00,00, 00,00,00,00 + +HKR, Responses, "FAX", 1, 03, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "NO ANSWER", 1, 07, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "NO CARRIER", 1, 04, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "NO DIALTONE", 1, 05, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "NOTUSED", 1, 00, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "OK", 1, 00, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "RING", 1, 08, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "RINGING", 1, 01, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "0", 1, 00, 00, 00,00,00,00, 00,00,00,00 ; OK +HKR, Responses, "1", 1, 02, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "10", 1, 02, 00, 60,09,00,00, 00,00,00,00 +HKR, Responses, "11", 1, 02, 00, c0,12,00,00, 00,00,00,00 +HKR, Responses, "12", 1, 02, 00, 80,25,00,00, 00,00,00,00 +HKR, Responses, "13", 1, 02, 00, 20,1c,00,00, 00,00,00,00 +HKR, Responses, "14", 1, 02, 00, e0,2e,00,00, 00,00,00,00 +HKR, Responses, "15", 1, 02, 00, 40,38,00,00, 00,00,00,00 ; connect at 14400 +HKR, Responses, "2", 1, 08, 00, 00,00,00,00, 00,00,00,00 ; RING +HKR, Responses, "3", 1, 04, 00, 00,00,00,00, 00,00,00,00 ; NO CARRIER +HKR, Responses, "4", 1, 03, 00, 00,00,00,00, 00,00,00,00 ; ERROR +HKR, Responses, "5", 1, 02, 00, b0,04,00,00, 00,00,00,00 +HKR, Responses, "6", 1, 05, 00, 00,00,00,00, 00,00,00,00 ; NO DIALTONE +HKR, Responses, "69", 1, 01, 03, 00,00,00,00, 00,00,00,00 +HKR, Responses, "7", 1, 06, 00, 00,00,00,00, 00,00,00,00 ; BUSY +HKR, Responses, "70", 1, 01, 01, 00,00,00,00, 00,00,00,00 ; Protocol:None +HKR, Responses, "77", 1, 01, 02, 00,00,00,00, 00,00,00,00 ; Protocol:LAPM +HKR, Responses, "78", 1, 01, 03, 00,00,00,00, 00,00,00,00 ; Connect V42BIS +HKR, Responses, "8", 1, 07, 00, 00,00,00,00, 00,00,00,00 ; NO ANSWER +HKR, Responses, "80", 1, 01, 02, 00,00,00,00, 00,00,00,00 ; Protocol:ALT +HKR, Responses, "81", 1, 01, 02, 00,00,00,00, 00,00,00,00 + +[ExtraCRLFResponses] +HKR, Responses, "OK", 1, 00, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "ERROR", 1, 03, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "BUSY", 1, 06, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "NO ANSWER", 1, 07, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "NO CARRIER", 1, 04, 00, 00,00,00,00, 00,00,00,00 +HKR, Responses, "NO DIALTONE", 1, 05, 00, 00,00,00,00, 00,00,00,00 + +;------------------------------------------------------------------------------------------------------- +;This section defines each %strkey% token specified in the INF and lists strings that are used by the Modems +;control panel applet and the Install New Modem wizard. +; For example, ModemX will appear as "FakeModem DDK Sample controllerless driver" + +[Strings] +mfg = "Microsoft" +FakeDisk = "Fake Modem Install Disk" +Generic = "(Standard Modem Types)" +ModemX = "FakeModem DDK Sample controllerless driver" +ServiceName = "Fakemodem" diff --git a/network/modem/fakemodem/readwrit.c b/network/modem/fakemodem/readwrit.c new file mode 100644 index 00000000..af6da937 --- /dev/null +++ b/network/modem/fakemodem/readwrit.c @@ -0,0 +1,465 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + ioctl.c + +Abstract: + + This is a simple form of function driver for Fm device. The driver + doesn't handle any PnP and Power events because the framework provides + default behaviour for those events. This driver has enough support to + allow an user application (toast/notify.exe) to open the device + interface registered by the driver and send read, write or ioctl requests. + +Environment: + + Kernel mode + +--*/ + +#include "fakemodem.h" + + +VOID +FmEvtIoRead( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ +Routine Description: + + This event is called when the framework receives IRP_MJ_READ + requests from the system. + This routines defers read for later processing if there is no data to read. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + Request - Handle to a framework request object. + + Length - Length of the IO operation + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + VOID + +--*/ +{ + PFM_DEVICE_DATA fmDeviceData = FmDeviceDataGet(WdfIoQueueGetDevice(Queue)); + ULONG information; + NTSTATUS status; + PUCHAR systemBuffer; + size_t bufLen; + + status = WdfRequestRetrieveOutputBuffer(Request, Length, &systemBuffer, &bufLen); + if (!NT_SUCCESS(status)) { + WdfRequestComplete(Request, status); + return; + } + + if (fmDeviceData->BytesInReadBuffer > 0) { + + ProcessReadBuffer(fmDeviceData, + systemBuffer, + (ULONG) Length, + &information); + + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, information); + + return; + + } else { + + // + // No data to read. Queue the request for later processing. + // + + status = WdfRequestForwardToIoQueue(Request, fmDeviceData->FmReadQueue); + if (!NT_SUCCESS(status)) { + WdfRequestCompleteWithInformation(Request, status, 0); + return; + } + } +} + +VOID +FmEvtIoWrite( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ +Routine Description: + + This event is called when the framework receives IRP_MJ_WRITE + requests from the system. + This routine will also drain the read queue if there are pending reads. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + Request - Handle to a framework request object. + + Length - Length of the IO operation + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + VOID + +--*/ +{ + PFM_DEVICE_DATA fmDeviceData = FmDeviceDataGet(WdfIoQueueGetDevice(Queue)); + PUCHAR systemBuffer; + NTSTATUS status; + size_t length; + WDFREQUEST readRequest; + + status = WdfRequestRetrieveInputBuffer(Request, Length, &systemBuffer, &length); + if (!NT_SUCCESS(status)) { + WdfRequestComplete(Request, status); + return; + } + + ProcessWriteBytes( fmDeviceData, systemBuffer, (ULONG) Length); + + // + // Process read requests and complete them here. + // + + while (fmDeviceData->BytesInReadBuffer > 0) { + ULONG bytesToMove = 0; + + status = WdfIoQueueRetrieveNextRequest(fmDeviceData->FmReadQueue, &readRequest); + if (!NT_SUCCESS(status)) { + break; + } + + status = WdfRequestRetrieveOutputBuffer(readRequest, 0, &systemBuffer, &length); + if (NT_SUCCESS(status)) { + ProcessReadBuffer(fmDeviceData, + systemBuffer, + (ULONG) length, + &bytesToMove); + } + WdfRequestCompleteWithInformation(readRequest, status, bytesToMove); + } + + ProcessConnectionStateChange( fmDeviceData); + + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, Length); +} + +VOID +ProcessWriteBytes( + PFM_DEVICE_DATA FmDeviceData, + PUCHAR Characters, + ULONG Length + ) +/*++ +Routine Description: + + This function is called when the framework receives IRP_MJ_WRITE + requests from the system. The write event handler(FmEvtIoWrite) calls ProcessWriteBytes. + It parses the Characters passed in and looks for the for sequences "AT" -ok , + "ATA" --CONNECT, ATD -- CONNECT and sets the state of the device appropriately. + These bytes are placed in the read Buffer to be processed later since this device + works in a loopback fashion. + + +Arguments: + + FmDeviceData - Handle to the framework queue object that is associated + with the I/O request. + Characters - Pointer to the write IRP's system buffer. + + Length - Length of the IO operation + The default property of the queue is to not dispatch + zero lenght read & write requests to the driver and + complete is with status success. So we will never get + a zero length request. + +Return Value: + + VOID + +--*/ + +{ + + UCHAR currentCharacter; + + while (Length != 0) { + + currentCharacter=*Characters++; + Length--; + + if(currentCharacter == '\0') + { + continue; + } + + PutCharInReadBuffer( FmDeviceData, currentCharacter); + + switch (FmDeviceData->CommandMatchState) { + + case COMMAND_MATCH_STATE_IDLE: + + if ((currentCharacter == 'a') || (currentCharacter == 'A')) { + // got an A + FmDeviceData->CommandMatchState=COMMAND_MATCH_STATE_GOT_A; + + FmDeviceData->ConnectCommand=FALSE; + + FmDeviceData->IgnoreNextChar=FALSE; + + } + + break; + + case COMMAND_MATCH_STATE_GOT_A: + + if ((currentCharacter == 't') || (currentCharacter == 'T')) { + // got an T + FmDeviceData->CommandMatchState=COMMAND_MATCH_STATE_GOT_T; + + } else { + + if (currentCharacter == '\r') { + + FmDeviceData->CommandMatchState=COMMAND_MATCH_STATE_IDLE; + } + } + + break; + + case COMMAND_MATCH_STATE_GOT_T: + + if (!FmDeviceData->IgnoreNextChar) { + // the last char was not a special char + // check for CONNECT command + if ((currentCharacter == 'A') || (currentCharacter == 'a')) { + + FmDeviceData->ConnectCommand=TRUE; + } + + if ((currentCharacter == 'D') || (currentCharacter == 'd')) { + + FmDeviceData->ConnectCommand=TRUE; + } + } + + FmDeviceData->IgnoreNextChar=TRUE; + + if (currentCharacter == '\r') { + // + // got a CR, send a response to the command + // + FmDeviceData->CommandMatchState=COMMAND_MATCH_STATE_IDLE; + + if (FmDeviceData->ConnectCommand) { + // + // place CONNECT in the buffer + // + PutCharInReadBuffer(FmDeviceData,'\r'); + PutCharInReadBuffer(FmDeviceData,'\n'); + + PutCharInReadBuffer(FmDeviceData,'C'); + PutCharInReadBuffer(FmDeviceData,'O'); + PutCharInReadBuffer(FmDeviceData,'N'); + PutCharInReadBuffer(FmDeviceData,'N'); + PutCharInReadBuffer(FmDeviceData,'E'); + PutCharInReadBuffer(FmDeviceData,'C'); + PutCharInReadBuffer(FmDeviceData,'T'); + + PutCharInReadBuffer(FmDeviceData,'\r'); + PutCharInReadBuffer(FmDeviceData,'\n'); + + // + // connected now raise CD + // + FmDeviceData->CurrentlyConnected=TRUE; + + FmDeviceData->ConnectionStateChanged=TRUE; + + } else { + + // place OK in the buffer + + PutCharInReadBuffer(FmDeviceData,'\r'); + PutCharInReadBuffer(FmDeviceData,'\n'); + PutCharInReadBuffer(FmDeviceData,'O'); + PutCharInReadBuffer(FmDeviceData,'K'); + PutCharInReadBuffer(FmDeviceData,'\r'); + PutCharInReadBuffer(FmDeviceData,'\n'); + } + } + + + break; + + default: + + break; + + } + } + + return; + +} + +VOID +PutCharInReadBuffer( + PFM_DEVICE_DATA FmDeviceData, + UCHAR Character + ) +/*++ +Routine Description: + + This routine puts the charcter into the circular read buffer checking for overflows while doing it. + +Arguments: + + FmDeviceData - Handle to the framework queue object that is associated + with the I/O request. + Characters - Handle to a framework request object. + + +Return Value: + + VOID + +--*/ + +{ + + if (FmDeviceData->BytesInReadBuffer < READ_BUFFER_SIZE) { + + // room in buffer + FmDeviceData->ReadBuffer[FmDeviceData->ReadBufferEnd]=Character; + FmDeviceData->ReadBufferEnd++; + FmDeviceData->ReadBufferEnd %= READ_BUFFER_SIZE; + FmDeviceData->BytesInReadBuffer++; + + } + + return; + +} + +VOID +ProcessReadBuffer( + IN PFM_DEVICE_DATA FmDeviceData, + IN PUCHAR SystemBuffer, + IN ULONG Length, + OUT PULONG BytesToMove + ) +/*++ +Routine Description: + + This event is called when the framework receives IRP_MJ_READ + requests from the system. It is called by the read event handler. + It copies data from the IRp's system buffer to the device read buffer. + if the size of data from the Irp's system buffer is greater than the size of the + read buffer the number of bytes remaining is passed back in BytesToMove. + + +Arguments: + + FmDeviceData - Handle to the framework queue object that is associated + with the I/O request. + SystemBuffer - The buffer passed in the IRP which contains the read data. + + Length - Length of data in the systembuffer + BytesToMove - Remaining bytes not copied into the read buffer. +Return Value: + + VOID + +--*/ +{ + ULONG firstHalf; + ULONG secondHalf; + ULONG bytesToMove; + NTSTATUS status; + ULONG safeBoundsCheck; + + // + // there is an IRP and there are characters waiting + // + + + bytesToMove = (Length < FmDeviceData->BytesInReadBuffer) ? Length + : FmDeviceData->BytesInReadBuffer; + + status = RtlULongAdd (FmDeviceData->ReadBufferBegin, + bytesToMove, + &safeBoundsCheck); + + if (!NT_SUCCESS(status)) { + return; + } + + if (safeBoundsCheck > READ_BUFFER_SIZE) { + + // + // the buffer is wrapped around, have move in two pieces + // + + firstHalf = READ_BUFFER_SIZE - FmDeviceData->ReadBufferBegin; + + secondHalf= bytesToMove - firstHalf; + + RtlCopyMemory( + SystemBuffer, + &FmDeviceData->ReadBuffer[FmDeviceData->ReadBufferBegin], + firstHalf); + + RtlCopyMemory( + (SystemBuffer + firstHalf), + &FmDeviceData->ReadBuffer[0], + secondHalf); + + } else { + + // + // can do it all at once + // + + RtlCopyMemory( + SystemBuffer, + &FmDeviceData->ReadBuffer[FmDeviceData->ReadBufferBegin], + bytesToMove); + } + + // + // fix up queue pointers + // + FmDeviceData->BytesInReadBuffer -= bytesToMove; + + FmDeviceData->ReadBufferBegin += bytesToMove; + + FmDeviceData->ReadBufferBegin %= READ_BUFFER_SIZE; + + *BytesToMove = bytesToMove; +} + diff --git a/network/ndis/extension/ReadMe.md b/network/ndis/extension/ReadMe.md new file mode 100644 index 00000000..dc3b6b92 --- /dev/null +++ b/network/ndis/extension/ReadMe.md @@ -0,0 +1,21 @@ +Hyper-V Extensible Switch extension filter driver +================================================= + +This sample contains a base library used to implement a Hyper-V Extensible Switch extension filter driver. This sample also contains two different extension filter drivers that were developed by using the library. + +Hyper-V Extensible Switch extension filter drivers use the NDIS filter model. Every Hyper-V Extensible Switch has a corresponding Extension Protocol and Miniport instance. NDIS OIDs are leveraged to inform filter drivers on the driver stack about switch/port/NIC information. All packets originating from a switch port (External NIC, Synthetic NIC, Emulated NIC and Internal NIC) are first issued as a send from the Extension Protocol, which populates the packet with source information. This corresponds to the ingress data flow. Source based filtering, packet modification, packet queuing, and destination definition can occur at this stage. If the packets arrive at the Extension Miniport, they pass through the built in filtering/forwarding logic. If the packets pass filtering and must be delivered to any destination ports, they are issued as an indication (receive) from the Extension Miniport, which populate the packet with the source and destination information. This corresponds to the egress data flow. Destination based filtering can occur at this stage. If the packets arrive at the Extension Protocol, they are delivered to the defined destinations that were not marked as excluded by filtering logic. The packets then are completed back in the reverse order, as a receive completion first and then a send completion. Extension filter drivers are free to generate new packets on the ingress data path by issuing a send from their filter. + +The base library provided, *SxBase.lib*, implements the necessary NDIS functionality common to all types of extension filter drivers. It is not necessary to make any changes to this base library. To implement your own extension filter driver, you only need to define all global variables and implement all functions that are found in *SxApi.h*. + +MsPassthroughExt is a basic filtering extension filter driver that is implemented by using SxBase.lib. This demonstrates the bare minimum that must be implemented to use *SxBase.lib*. Installing and enabling MsPassthroughExt on the Hyper-V Extensible Switch does not affect switch behavior. + +MsForwardExt is a basic forwarding extension filter driver that is implemented by using *SxBase.lib*. MsForwardExt uses basic MAC forwarding and custom switch policy to allow sends from given MAC addresses. This forwarding sample implements Hybrid Forwarding, which means that the destination table is not populated by this sample if the packet is flagged as a Hyper-V Network Virtualization (HNV) packet. HNV flagged packets' destination tables are computed by the vSwitch HNV policies instead. If this extension filter driver is unconfigured, it will block sends from all VMs, but will maintain connectivity to the host. Each switch policy, which is defined in *MsForwardExtPolicy.mof*, is a MAC address. Applying a switch policy to MsForwardExt allows packets to be sent from the MAC address that is defined in the policy. + + +Installation +------------ + +Use the *install.cmd* script provided with each extension filter driver. The *install.cmd* uses **netcfg** to install the extension and **mofcomp** to register any required mof files. The PowerShell cmdlet *Enable-VmSwitchExtension* can then be used to enable the extension filter driver on a Hyper-V Extensible Switch. + +For more information on Hyper-V Extensible Switch extensions, see [Hyper-V Extensible Switch](http://msdn.microsoft.com/en-us/library/windows/hardware/hh598161). + diff --git a/network/ndis/extension/base/SxApi.h b/network/ndis/extension/base/SxApi.h new file mode 100644 index 00000000..64dca5f7 --- /dev/null +++ b/network/ndis/extension/base/SxApi.h @@ -0,0 +1,1196 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved. + +Module Name: + + SxApi.h + +Abstract: + + This file contains the API that must be implemented to + create a switch extension using the SxBase library. + + +--*/ + +// +// The major version of NDIS the driver is using. +// For NDIS 6.30, use NDIS_FILTER_MAJOR_VERSION. +// +extern UCHAR SxExtMajorNdisVersion; + +// +// The minor version of NDIS the driver is using. +// For NDIS 6.30, use NDIS_FILTER_MINOR_VERSION. +// +extern UCHAR SxExtMinorNdisVersion; + +// +// The friendly name of the extension. +// +extern PWCHAR SxExtFriendlyName; + +// +// The unique name of the extension. +// +extern PWCHAR SxExtUniqueName; + +// +// The service name of the extension from the INF. +// +extern PWCHAR SxExtServiceName; + +// +// The memory pool tag using in the extension. +// +extern ULONG SxExtAllocationTag; + +// +// The request ID used to identify OIDs initiated from this extension. +// +extern ULONG SxExtOidRequestId; + + +/*++ + +SxExtInitialize + +Routine Description: + This function is called from the SxBase Library during DriverEntry. + An extension should allocate/initalize all global data in this function. + +Arguments: + NULL + +Return Value: + NDIS_STATUS_SUCCESS succeeds driver entry. + + NDIS_STATUS_*** fails driver entry. + +--*/ +NDIS_STATUS +SxExtInitialize(); + + +/*++ + +SxExtUninitialize + +Routine Description: + This function is called from the SxBase Library during DriverUnload. + An extension should free/reset all global data in this function. + +Arguments: + NULL + +Return Value: + VOID + +--*/ +VOID +SxExtUninitialize(); + + +/*++ + +SxExtCreateSwitch + +Routine Description: + This function is called when an extension binds to a new switch. + All switch specific data should be allocated during this function. + OIDs cannot be sent from this function, and both the control + and data paths are inactive. + +Arguments: + Switch - the Switch Object currently being created + + ExtensionContext - Extension context specific to the switch being + createf. This context will be passed back to the + extension for all function calls in SxApi + +Return Value: + NDIS_STATUS_SUCCESS succeeds switch creation. + + NDIS_STATUS_RESOURCES fails switch creation because of insufficient + resources. + + NDIS_STATUS_FAILURE fails switch creation. + +--*/ +NDIS_STATUS +SxExtCreateSwitch( + _In_ PSX_SWITCH_OBJECT Switch, + _Outptr_result_maybenull_ PNDIS_HANDLE *ExtensionContext + ); + + +/*++ + +SxExtDeleteSwitch + +Routine Description: + This function is called when an extension binds to a new switch. + All switch specific data should be allocated/initialized during + this function. + +Arguments: + Switch - the Switch being deleted + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch being deleted. + +Return Value: + VOID + +--*/ +VOID +SxExtDeleteSwitch( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext + ); + + +/*++ + +SxExtActivateSwitch + +Routine Description: + This function is called to activate a switch. The function can be called + while the switch is Running or Paused and should be used to bootstrap + the switch if it was not Active when it was created. + +Arguments: + Switch - the Switch being activated + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch being restarted. + +Return Value: + VOID + +--*/ +VOID +SxExtActivateSwitch( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext + ); + + +/*++ + +SxExtRestartSwitch + +Routine Description: + This function is called to restart a switch from a paused state. + +Arguments: + Switch - the Switch being restarted + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch being restarted. + +Return Value: + NDIS_STATUS_SUCCESS succeeds switch restart. + + NDIS_STATUS_RESOURCES fails switch restart because of insufficient + resources. + + NDIS_STATUS_FAILURE fails switch restart. + +--*/ +NDIS_STATUS +SxExtRestartSwitch( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext + ); + + +/*++ + +SxExtPauseSwitch + +Routine Description: + This function is called to pause a switch from a running state. + +Arguments: + Switch - the Switch being paused + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch being paused + +Return Value: + VOID + +--*/ +VOID +SxExtPauseSwitch( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext + ); + + +/*++ + +SxExtCreatePort + +Routine Description: + This function is called to create a new port on a switch. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + Port - the Port being created + +Return Value: + NDIS_STATUS_SUCCESS to succeed port creation + + NDIS_STATUS_*** to fail port creation + +--*/ +NDIS_STATUS +SxExtCreatePort( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_PORT_PARAMETERS Port + ); + + +/*++ + +SxExtUpdatePort + +Routine Description: + This function is called to update an already created port. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + Port - the port being updated + +Return Value: + VOID + +--*/ +VOID +SxExtUpdatePort( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_PORT_PARAMETERS Port + ); + + +/*++ + +SxExtCreateNic + +Routine Description: + This function is called to create a new NIC to be connected + to a switch. + The extension may allocate context for this NIC, and traffic may + start to flow from this NIC, but it may not be used as a destination + until SxExtConnectNic has been called. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + Nic - the NIC being created + +Return Value: + NDIS_STATUS_SUCCESS to succeed NIC creation + + NDIS_STATUS_*** to fail NIC creation + +--*/ +NDIS_STATUS +SxExtCreateNic( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_NIC_PARAMETERS Nic + ); + + +/*++ + +SxExtConnectNic + +Routine Description: + This function is called to connect a NIC to a switch. + After returning from this function the extension can use this NIC + as a destination. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + Nic - the NIC being connected + +Return Value: + VOID + +--*/ +VOID +SxExtConnectNic( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_NIC_PARAMETERS Nic + ); + + +/*++ + +SxExtUpdateNic + +Routine Description: + This function is called to update an already created NIC. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + Nic - the NIC being updated + +Return Value: + VOID + +--*/ +VOID +SxExtUpdateNic( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_NIC_PARAMETERS Nic + ); + + +/*++ + +SxExtDisconnectNic + +Routine Description: + This function is called to disconnect a NIC from a switch. + After returning from this function the extension cannot use + this NIC as a destination. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + Nic - the NIC being disconnected + +Return Value: + VOID + +--*/ +VOID +SxExtDisconnectNic( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_NIC_PARAMETERS Nic + ); + + +/*++ + +SxExtDeleteNic + +Routine Description: + This function is called to delete a NIC from a switch. + No futher traffic/control will be recieved for this NIC. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + Nic - the NIC being deleted + +Return Value: + VOID + +--*/ +VOID +SxExtDeleteNic( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_NIC_PARAMETERS Nic + ); + + +/*++ + +SxExtTeardownPort + +Routine Description: + This function is called to start deletion of a port on a switch. + Upon recieving this call, no further references may be taken + on the given port. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + Port - the Port being deleted + +Return Value: + VOID + +--*/ +VOID +SxExtTeardownPort( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_PORT_PARAMETERS Port + ); + + +/*++ + +SxExtDeletePort + +Routine Description: + This function is called to finish deletion of a port on a switch. + Upon recieving this call, no traffic/control will be recieved + for this port. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + Port - the Port being deleted + +Return Value: + VOID + +--*/ +VOID +SxExtDeletePort( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_PORT_PARAMETERS Port + ); + + +/*++ + +SxExtSaveNic + +Routine Description: + This function is called to retrieve save data for a given NIC. + This function will be called until all extensions have finished + saving data. + + An new save for this NIC will not start until SxExtSaveNicComplete + has been received. + + If returning NDIS_STATUS_SUCCESS from this function, and + BytesWritten > 0 you must write to the ExtensionId, + ExtensionFriendlyName, SaveDataSize and SaveData fields in + SaveState. SxExtUniqueName MUST be written to ExtensionId. + SxExtFriendlyName should be written to ExtensionFriendlyName. + + If returning NDIS_STATUS_SUCCESS with BytesWritten == 0, + DO NOT write any data to any fields. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + SaveState - the save information and buffer to save to + + BytesWritten - the # of bytes written to the save buffer + + BytesNeeded - the length of the save buffer needed + +Return Value: + NDIS_STATUS_SUCCESS - if the buffer was successfully written, or not + needed and BytesWritten is set to 0 + + NDIS_STATUS_BUFFER_TOO_SHORT - if the buffer is too short for the + necessary save, write the length needed + in BytesNeeded + + NDIS_STATUS_*** - to fail the save operation + +--*/ +NDIS_STATUS +SxExtSaveNic( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _Inout_ PNDIS_SWITCH_NIC_SAVE_STATE SaveState, + _Out_ PULONG BytesWritten, + _Out_ PULONG BytesNeeded + ); + + +/*++ + +SxExtSaveNicComplete + +Routine Description: + This function is called to notify the extension that saving + the given NIC has been completed by all extensions. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + SaveState - the save information + +Return Value: + VOID + +--*/ +VOID +SxExtSaveNicComplete( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_NIC_SAVE_STATE SaveState + ); + + +/*++ + +SxExtNicRestore + +Routine Description: + This function is called to restore previously saved data. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + SaveState - the save information + + BytesRestored - the number of bytes restored from the saved data + +Return Value: + NDIS_STATUS_SUCCESS - if this data belongs to this extension, and was + successfully restored (BytesRestored written) + OR + this data does not belong to this extension + (BytesRestored == 0) + + NDIS_STATUS_*** - there was an error while attempting to restore + this data + +--*/ +NDIS_STATUS +SxExtNicRestore( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_NIC_SAVE_STATE SaveState, + _Out_ PULONG BytesRestored + ); + + +/*++ + +SxExtNicRestoreComplete + +Routine Description: + This function is called to signify the end of a restore operation. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + SaveState - the save information + +Return Value: + VOID + +--*/ +VOID +SxExtNicRestoreComplete( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_NIC_SAVE_STATE SaveState + ); + + +/*++ + +SxExtAddSwitchProperty + +Routine Description: + This function is called to add a property on the given switch. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + SwitchProperty - the property to be applied + +Return Value: + NDIS_STATUS_NOT_SUPPORTED - if the policy is not consumed by this extension + + NDIS_STATUS_SUCCESS - if the policy is consumed by this extension, and + can successfully be enforced + + STATUS_DATA_NOT_ACCEPTED - if the policy is consumed by this + extension, but cannot be enforced + + NDIS_STATUS_*** - if the policy is consumed by this extension, and + setting the valid policy failed + +--*/ +NDIS_STATUS +SxExtAddSwitchProperty( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_PROPERTY_PARAMETERS SwitchProperty + ); + + +/*++ + +SxExtUpdateSwitchProperty + +Routine Description: + This function is called to update a property on the given switch. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + SwitchProperty - the property to be updated + +Return Value: + NDIS_STATUS_NOT_SUPPORTED - if the policy is not consumed by this extension + + NDIS_STATUS_SUCCESS - if the policy is consumed by this extension, and + can successfully be enforced + + STATUS_DATA_NOT_ACCEPTED - if the policy is consumed by this + extension, but cannot be enforced + + NDIS_STATUS_*** - if the policy is consumed by this extension, and + setting the valid policy failed + +--*/ +NDIS_STATUS +SxExtUpdateSwitchProperty( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_PROPERTY_PARAMETERS SwitchProperty + ); + + +/*++ + +SxExtDeleteSwitchProperty + +Routine Description: + This function is called to delete a property on the given switch. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + SwitchProperty - the property to be deleted + +Return Value: + TRUE - if the policy belongs to this extension + + FALSE - otherwise + +--*/ +BOOLEAN +SxExtDeleteSwitchProperty( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_PROPERTY_DELETE_PARAMETERS SwitchProperty + ); + + +/*++ + +SxExtAddPortProperty + +Routine Description: + This function is called to add a property on the given port, + on the given switch. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + PortProperty - the property to be applied + +Return Value: + NDIS_STATUS_NOT_SUPPORTED - if the policy is not consumed by this extension + + NDIS_STATUS_SUCCESS - if the policy is consumed by this extension, and + can successfully be enforced + + STATUS_DATA_NOT_ACCEPTED - if the policy is consumed by this + extension, but cannot be enforced + + NDIS_STATUS_*** - if the policy is consumed by this extension, and + setting the valid policy failed + +--*/ +NDIS_STATUS +SxExtAddPortProperty( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_PORT_PROPERTY_PARAMETERS PortProperty + ); + + +/*++ + +SxExtUpdatePortProperty + +Routine Description: + This function is called to update a property on the given port, + on the given switch. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + PortProperty - the property to be applied + +Return Value: + NDIS_STATUS_NOT_SUPPORTED - if the policy is not consumed by this extension + + NDIS_STATUS_SUCCESS - if the policy is consumed by this extension, and + can successfully be enforced + + STATUS_DATA_NOT_ACCEPTED - if the policy is consumed by this + extension, but cannot be enforced + + NDIS_STATUS_*** - if the policy is consumed by this extension, and + setting the valid policy failed + +--*/ +NDIS_STATUS +SxExtUpdatePortProperty( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_PORT_PROPERTY_PARAMETERS PortProperty + ); + + +/*++ + +SxExtDeletePortProperty + +Routine Description: + This function is called to delete a property on the given port, + on the given switch. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + SwitchProperty - the property to be deleted + +Return Value: + TRUE - if the policy is not consumed by this extension + + FALSE - otherwise + +--*/ +BOOLEAN +SxExtDeletePortProperty( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_SWITCH_PORT_PROPERTY_DELETE_PARAMETERS PortProperty + ); + + +/*++ + +SxExtQuerySwitchFeatureStatus + +Routine Description: + This function is called to query the status of a custom property + on the given switch. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + SwitchFeatureStatus - the property buffer + + BytesNeeded - if SwitchFeatureStatus is too small, this should be set + to the size buffer needed + +Return Value: + TRUE - return true if this property belongs to this extension, if + BytesNeeded > 0, the buffer will be reallocated and + this function will be called again + + FALSE - otherwise + +--*/ +BOOLEAN +SxExtQuerySwitchFeatureStatus( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _Inout_ PNDIS_SWITCH_FEATURE_STATUS_PARAMETERS SwitchFeatureStatus, + _Inout_ PULONG BytesNeeded + ); + + +/*++ + +SxExtQueryPortFeatureStatus + +Routine Description: + This function is called to query the status of a custom property + on the given port, on the given switch. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + PortFeatureStatus - the property buffer + + BytesNeeded - if PortFeatureStatus is too small, this should be set + to the size buffer needed + +Return Value: + TRUE - return true if this property belongs to this extension, if + BytesNeeded > 0, the buffer will be reallocated and + this function will be called again + + FALSE - otherwise + +--*/ +BOOLEAN +SxExtQueryPortFeatureStatus( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _Inout_ PNDIS_SWITCH_PORT_FEATURE_STATUS_PARAMETERS PortFeatureStatus, + _Inout_ PULONG BytesNeeded + ); + + +/*++ + +SxExtProcessNicRequest + +Routine Description: + This function is called upon the reciept of an OID_SWITCH_NIC_REQUEST + to the extension. + If an extension wishes to redirect the OID, it must return a valid + DestinationPortId and DestinationNicIndex, which it has taken a + reference on. + If an extension wishes to set source information, it must return + a valid SourcePortId and SourceNicIndex, which it has taken a + reference on. + The extension can change the OidRequest if it needs to. + + !! This function should only be used by forwarding extensions. !! + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + OidRequest - the OID wrapped by the NIC request + + SourcePortId - the source PortId to set + + SourceNicIndex - the source NicIndex to set + + DestinationPortId - the destination PortId to set + + DestinationNicIndex - the destination NicIndex to set + +Return Value: + NDIS_STATUS_SUCCESS - sends OID + + NDIS_STATUS_*** - complete OID with given status + +--*/ +NDIS_STATUS +SxExtProcessNicRequest( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _Inout_ PNDIS_OID_REQUEST OidRequest, + _Inout_ PNDIS_SWITCH_PORT_ID SourcePortId, + _Inout_ PNDIS_SWITCH_NIC_INDEX SourceNicIndex, + _Inout_ PNDIS_SWITCH_PORT_ID DestinationPortId, + _Inout_ PNDIS_SWITCH_NIC_INDEX DestinationNicIndex + ); + + +/*++ + +SxExtProcessNicRequestComplete + +Routine Description: + This function is called upon the completion of an OID_SWITCH_NIC_REQUEST + that this extension has previously altered. + The extension must derefernce all NICs it had previously taken references + on. + If the OidRequest buffer was changed (along with underlying buffers), + the previous data must be replaced. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + NicOidRequest - the OID buffer, encapsulated with source/destination info + + Status - the status the OID completed with + +Return Value: + NDIS_STATUS - the status to complete the OID request with + +--*/ +NDIS_STATUS +SxExtProcessNicRequestComplete( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _Inout_ PNDIS_OID_REQUEST OidRequest, + _In_ NDIS_SWITCH_PORT_ID SourcePortId, + _In_ NDIS_SWITCH_NIC_INDEX SourceNicIndex, + _In_ NDIS_SWITCH_PORT_ID DestinationPortId, + _In_ NDIS_SWITCH_NIC_INDEX DestinationNicIndex, + _In_ NDIS_STATUS Status + ); + + +/*++ + +SxExtProcessNicStatus + +Routine Description: + This function is called upon the reciept of an NDIS_STATUS_SWITCH_NIC_STATUS + to the extension. + If the extension wishes to modify the status indication, it should + send its own status indication using NdisFIndicateStatus and return a + failure status. + If the extension wishes to drop the status indiction, it should return + failure status, though this should be done very sparingly and carefully. + + !! This function should only be used by forwarding extensions. !! + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + StatusIndication - the indication wrapped by the NIC status indication + + SourcePortId - the source PortId of the indication + + SourceNicIndex - the source NicIndex of the indication + +Return Value: + NDIS_STATUS_SUCCESS - return to continue forwarding this indication + + NDIS_STATUS_*** - if the extension wants to modify the status + if modifying the status the extension should indicate + its own modified status using + SxLibIssueNicStatusIndicationUnsafe as soon as possible + +--*/ +NDIS_STATUS +SxExtProcessNicStatus( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNDIS_STATUS_INDICATION StatusIndication, + _In_ NDIS_SWITCH_PORT_ID SourcePortId, + _In_ NDIS_SWITCH_NIC_INDEX SourceNicIndex + ); + + +/*++ + +SxExtStartNetBufferListsIngress + +Routine Description: + This function is called upon the receipt on an NBL on ingress. + The extension should call SxLibSendNetBufferListsIngress to continue + the send of the NBL on ingress. + The extension should call SxLibCompleteNetBufferListsIngress to + drop the NBL. + This function may also be call from egress to inject an NBL. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + NetBufferLists - the NBL to be sent + + SendFlags - the send flags from NDIS, equivalent to NDIS send flags for + NdisFSendNetBufferLists + +Return Value: + VOID + +--*/ +VOID +SxExtStartNetBufferListsIngress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG SendFlags + ); + + +/*++ + +SxExtStartNetBufferListsEgress + +Routine Description: + This function is called upon the receipt on an NBL on egress. + The extension should call SxLibSendNetBufferListsEgress to continue + the send of the NBL on egress. + The extension should call SxLibSendNetBufferListsEgressComplete to + drop the NBL. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + NetBufferLists - the NBL to be sent + + NumberOfNetBufferLists - the number of NBLs in NetBufferLists + + ReceiveFlags - the receive flags from NDIS, equivalent to NDIS receive flags for + NdisFIndicateReceiveNetBufferLists + +Return Value: + VOID + +--*/ +VOID +SxExtStartNetBufferListsEgress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG NumberOfNetBufferLists, + _In_ ULONG ReceiveFlags + ); + + +/*++ + +SxExtStartCompleteNetBufferListsEgress + +Routine Description: + This function is called upon the completion of an NBL on egress. + The extension must call SxLibCompleteNetBufferListsEgress + once it has finished processing the NBL. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + NetBufferLists - the NBL being completed + + ReturnFlags - the return flags from NDIS, equivalent to NDIS return flags for + NdisFReturnNetBufferLists + +Return Value: + VOID + +--*/ +VOID +SxExtStartCompleteNetBufferListsEgress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG ReturnFlags + ); + + +/*++ + +SxExtStartCompleteNetBufferListsIngress + +Routine Description: + This function is called upon the completion of an NBL on ingress. + The extension must call SxLibCompleteNetBufferListsIngress + once it has finished processing the NBL. + + If there are NBLs injected by this extension in NetBufferLists, + the extension must NOT call SxLibCompleteNetBufferListsIngress, and + instead call SxLibCompletedInjectedNetBufferLists with the number + of injected NBLs completed. + +Arguments: + Switch - the Switch context + + ExtensionContext - The extension context allocated in SxExtCreateSwitch + for the switch + + NetBufferLists - the NBL being completed + + SendCompleteFlags - the send complete flags from NDIS, equivalent to + NDIS send complete flags for + NdisFSendNetBufferListsComplete + +Return Value: + VOID + +--*/ +VOID +SxExtStartCompleteNetBufferListsIngress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_HANDLE ExtensionContext, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG SendCompleteFlags + ); + diff --git a/network/ndis/extension/base/SxBase.c b/network/ndis/extension/base/SxBase.c new file mode 100644 index 00000000..b76ef7a4 --- /dev/null +++ b/network/ndis/extension/base/SxBase.c @@ -0,0 +1,1395 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved. + +Module Name: + + SxBase.c + +Abstract: + + This file contains the common code for building a switch extension. This + file includes all the standard NDIS handling and exposes a function based + interface for the control path and data path. This also has reusable code + for basic operations such as pause/resume handling etc. + + +--*/ + +#include "precomp.h" + +ULONG SxDebugLevel; +NDIS_HANDLE SxDriverHandle = NULL; +NDIS_HANDLE SxDriverObject; +NDIS_SPIN_LOCK SxExtensionListLock; +LIST_ENTRY SxExtensionList; + +NDIS_STRING SxExtensionFriendlyName; +NDIS_STRING SxExtensionGuid; + +NDIS_STATUS +SxpNdisProcessSetOid( + __in PSX_SWITCH_OBJECT Switch, + __inout PNDIS_OID_REQUEST OidRequest, + __out PBOOLEAN Complete + ); + +NDIS_STATUS +SxpNdisProcessMethodOid( + __in PSX_SWITCH_OBJECT Switch, + __inout PNDIS_OID_REQUEST OidRequest, + __out PBOOLEAN Complete, + __out PULONG BytesNeeded + ); + + +// +// DriverEntry +// http://msdn.microsoft.com/en-us/library/ff544113(v=VS.85).aspx +// +_Use_decl_annotations_ +NTSTATUS +DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) +{ + NDIS_STATUS status; + NDIS_FILTER_DRIVER_CHARACTERISTICS fChars; + NDIS_STRING serviceName; + + UNREFERENCED_PARAMETER(RegistryPath); + + // + // Initialize extension specific data. + // + status = SxExtInitialize(); + if (status != NDIS_STATUS_SUCCESS) + { + goto Cleanup; + } + + RtlInitUnicodeString(&serviceName, SxExtServiceName); + RtlInitUnicodeString(&SxExtensionFriendlyName, SxExtFriendlyName); + RtlInitUnicodeString(&SxExtensionGuid, SxExtUniqueName); + SxDriverObject = DriverObject; + + NdisZeroMemory(&fChars, sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS)); + fChars.Header.Type = NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS; + fChars.Header.Size = sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS); + fChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_2; + fChars.MajorNdisVersion = SxExtMajorNdisVersion; + fChars.MinorNdisVersion = SxExtMinorNdisVersion; + fChars.MajorDriverVersion = 1; + fChars.MinorDriverVersion = 0; + fChars.Flags = 0; + fChars.FriendlyName = SxExtensionFriendlyName; + fChars.UniqueName = SxExtensionGuid; + fChars.ServiceName = serviceName; + + fChars.SetOptionsHandler = SxNdisSetOptions; + fChars.SetFilterModuleOptionsHandler = SxNdisSetFilterModuleOptions; + + fChars.AttachHandler = SxNdisAttach; + fChars.DetachHandler = SxNdisDetach; + fChars.PauseHandler = SxNdisPause; + fChars.RestartHandler = SxNdisRestart; + + fChars.SendNetBufferListsHandler = SxNdisSendNetBufferLists; + fChars.SendNetBufferListsCompleteHandler = SxNdisSendNetBufferListsComplete; + fChars.CancelSendNetBufferListsHandler = SxNdisCancelSendNetBufferLists; + fChars.ReceiveNetBufferListsHandler = SxNdisReceiveNetBufferLists; + fChars.ReturnNetBufferListsHandler = SxNdisReturnNetBufferLists; + + fChars.OidRequestHandler = SxNdisOidRequest; + fChars.OidRequestCompleteHandler = SxNdisOidRequestComplete; + fChars.CancelOidRequestHandler = SxNdisCancelOidRequest; + + fChars.NetPnPEventHandler = SxNdisNetPnPEvent; + fChars.StatusHandler = SxNdisStatus; + + NdisAllocateSpinLock(&SxExtensionListLock); + InitializeListHead(&SxExtensionList); + + DriverObject->DriverUnload = SxNdisUnload; + + status = NdisFRegisterFilterDriver(DriverObject, + (NDIS_HANDLE)SxDriverObject, + &fChars, + &SxDriverHandle); + +Cleanup: + + if (status != NDIS_STATUS_SUCCESS) + { + if (SxDriverHandle != NULL) + { + NdisFDeregisterFilterDriver(SxDriverHandle); + SxDriverHandle = NULL; + } + + NdisFreeSpinLock(&SxExtensionListLock); + + SxExtUninitialize(); + } + + return status; +} + + +// +// Unload Routine +// http://msdn.microsoft.com/en-us/library/ff564886(v=VS.85).aspx +// +_Use_decl_annotations_ +VOID +SxNdisUnload( + PDRIVER_OBJECT DriverObject + ) +{ + UNREFERENCED_PARAMETER(DriverObject); + + SxExtUninitialize(); + + NdisFDeregisterFilterDriver(SxDriverHandle); + NdisFreeSpinLock(&SxExtensionListLock); +} + + +// +// FilterSetOptions Function +// http://msdn.microsoft.com/en-us/library/ff549972(v=VS.85).aspx +// +_Use_decl_annotations_ +NDIS_STATUS +SxNdisSetOptions( + NDIS_HANDLE NdisDriverHandle, + NDIS_HANDLE DriverContext + ) +{ + UNREFERENCED_PARAMETER(NdisDriverHandle); + UNREFERENCED_PARAMETER(DriverContext); + return NDIS_STATUS_SUCCESS; +} + + +// +// FilterSetModuleOptions Function +// http://msdn.microsoft.com/en-us/library/ff549970(v=VS.85).aspx +// +_Use_decl_annotations_ +NDIS_STATUS +SxNdisSetFilterModuleOptions( + NDIS_HANDLE FilterModuleContext + ) +{ + UNREFERENCED_PARAMETER(FilterModuleContext); + return NDIS_STATUS_SUCCESS; +} + + +// +// FilterAttach Function +// http://msdn.microsoft.com/en-us/library/ff549905(v=VS.85).aspx +// +_Use_decl_annotations_ +NDIS_STATUS +SxNdisAttach( + NDIS_HANDLE NdisFilterHandle, + NDIS_HANDLE SxDriverContext, + PNDIS_FILTER_ATTACH_PARAMETERS AttachParameters + ) +{ + NDIS_STATUS status; + NDIS_FILTER_ATTRIBUTES sxAttributes; + ULONG switchObjectSize; + NDIS_SWITCH_CONTEXT switchContext; + NDIS_SWITCH_OPTIONAL_HANDLERS switchHandler; + PSX_SWITCH_OBJECT switchObject; + + UNREFERENCED_PARAMETER(SxDriverContext); + + DEBUGP(DL_TRACE, ("===>SxAttach: NdisFilterHandle %p\n", NdisFilterHandle)); + + status = NDIS_STATUS_SUCCESS; + switchObject = NULL; + + NT_ASSERT(SxDriverContext == (NDIS_HANDLE)SxDriverObject); + + if (AttachParameters->MiniportMediaType != NdisMedium802_3) + { + status = NDIS_STATUS_INVALID_PARAMETER; + goto Cleanup; + } + + switchHandler.Header.Type = NDIS_OBJECT_TYPE_SWITCH_OPTIONAL_HANDLERS; + switchHandler.Header.Size = NDIS_SIZEOF_SWITCH_OPTIONAL_HANDLERS_REVISION_1; + switchHandler.Header.Revision = NDIS_SWITCH_OPTIONAL_HANDLERS_REVISION_1; + + status = NdisFGetOptionalSwitchHandlers(NdisFilterHandle, + &switchContext, + &switchHandler); + + if (status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_ERROR, + ("SxAttach: Extension is running in non-switch environment.\n")); + goto Cleanup; + } + + switchObjectSize = sizeof(SX_SWITCH_OBJECT); + switchObject = ExAllocatePoolWithTag(NonPagedPoolNx, + switchObjectSize, + SxExtAllocationTag); + + if (switchObject == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + RtlZeroMemory(switchObject, switchObjectSize); + + // + // Initialize NDIS related information. + // + switchObject->NdisFilterHandle = NdisFilterHandle; + switchObject->NdisSwitchContext = switchContext; + RtlCopyMemory(&switchObject->NdisSwitchHandlers, + &switchHandler, + sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS)); + + // + // Let the extension create its own context. + // + status = SxExtCreateSwitch(switchObject, + &(switchObject->ExtensionContext)); + + if (status != NDIS_STATUS_SUCCESS) + { + goto Cleanup; + } + + // + // Register the object with NDIS because NDIS passes this object when it + // calls into the driver. + // + NdisZeroMemory(&sxAttributes, sizeof(NDIS_FILTER_ATTRIBUTES)); + sxAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1; + sxAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES); + sxAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES; + sxAttributes.Flags = 0; + + NDIS_DECLARE_FILTER_MODULE_CONTEXT(SX_SWITCH_OBJECT); + status = NdisFSetAttributes(NdisFilterHandle, switchObject, &sxAttributes); + + if (status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_ERROR, ("SxBase: Failed to set attributes.\n")); + goto Cleanup; + } + + switchObject->ControlFlowState = SxSwitchAttached; + switchObject->DataFlowState = SxSwitchPaused; + + NdisAcquireSpinLock(&SxExtensionListLock); + InsertHeadList(&SxExtensionList, &switchObject->Link); + NdisReleaseSpinLock(&SxExtensionListLock); + +Cleanup: + + if (status != NDIS_STATUS_SUCCESS) + { + if (switchObject != NULL) + { + ExFreePool(switchObject); + } + } + + DEBUGP(DL_TRACE, ("<===SxAttach: status %x\n", status)); + + return status; +} + + +// +// FilterDetach Function +// http://msdn.microsoft.com/en-us/library/ff549918(v=VS.85).aspx +// +_Use_decl_annotations_ +VOID +SxNdisDetach( + NDIS_HANDLE FilterModuleContext + ) +{ + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)FilterModuleContext; + + DEBUGP(DL_TRACE, ("===>SxDetach: SxInstance %p\n", FilterModuleContext)); + + // + // The extension must be in paused state. + // + NT_ASSERT(switchObject->DataFlowState == SxSwitchPaused); + switchObject->ControlFlowState = SxSwitchDetached; + + KeMemoryBarrier(); + + while(switchObject->PendingOidCount > 0) + { + NdisMSleep(1000); + } + + SxExtDeleteSwitch(switchObject, switchObject->ExtensionContext); + + NdisAcquireSpinLock(&SxExtensionListLock); + RemoveEntryList(&switchObject->Link); + NdisReleaseSpinLock(&SxExtensionListLock); + + ExFreePool(switchObject); + + // + // Alway return success. + // + DEBUGP(DL_TRACE, ("<===SxDetach Successfully\n")); + + return; +} + + +// +// FilterRestart Function +// http://msdn.microsoft.com/en-us/library/ff549962(v=VS.85).aspx +// +_Use_decl_annotations_ +NDIS_STATUS +SxNdisPause( + NDIS_HANDLE FilterModuleContext, + PNDIS_FILTER_PAUSE_PARAMETERS PauseParameters + ) +{ + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)(FilterModuleContext); + + UNREFERENCED_PARAMETER(PauseParameters); + + DEBUGP(DL_TRACE, + ("===>NDISLWF SxPause: SxInstance %p\n", FilterModuleContext)); + + SxExtPauseSwitch(switchObject, switchObject->ExtensionContext); + + // + // Set the flag that the filter is going to pause. + // + NT_ASSERT(switchObject->DataFlowState == SxSwitchRunning); + switchObject->DataFlowState = SxSwitchPaused; + + KeMemoryBarrier(); + + while(switchObject->PendingInjectedNblCount > 0) + { + NdisMSleep(1000); + } + + DEBUGP(DL_TRACE, ("<===SxPause: status %x\n", NDIS_STATUS_SUCCESS)); + + return NDIS_STATUS_SUCCESS; +} + + +// +// FilterPause Function +// http://msdn.microsoft.com/en-us/library/ff549957(v=VS.85).aspx +// +_Use_decl_annotations_ +NDIS_STATUS +SxNdisRestart( + NDIS_HANDLE FilterModuleContext, + PNDIS_FILTER_RESTART_PARAMETERS RestartParameters + ) +{ + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)FilterModuleContext; + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(RestartParameters); + + DEBUGP(DL_TRACE, + ("===>SxRestart: FilterModuleContext %p\n", FilterModuleContext)); + + status = SxExtRestartSwitch(switchObject, + switchObject->ExtensionContext); + if (status != NDIS_STATUS_SUCCESS) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + NT_ASSERT(switchObject->DataFlowState == SxSwitchPaused); + switchObject->DataFlowState = SxSwitchRunning; + + DEBUGP(DL_TRACE, + ("<===SxRestart: FilterModuleContext %p, status %x\n", + FilterModuleContext, + NDIS_STATUS_SUCCESS)); + +Cleanup: + return status; +} + + +// +// FilterOidRequest Function +// http://msdn.microsoft.com/en-us/library/ff549954(v=VS.85).aspx +// +_Use_decl_annotations_ +NDIS_STATUS +SxNdisOidRequest( + NDIS_HANDLE FilterModuleContext, + PNDIS_OID_REQUEST OidRequest + ) +{ + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)FilterModuleContext; + NDIS_STATUS status; + PNDIS_OID_REQUEST clonedRequest=NULL; + PVOID *cloneRequestContext; + BOOLEAN completeOid = FALSE; + ULONG bytesNeeded = 0; + + status = NDIS_STATUS_SUCCESS; + + DEBUGP(DL_TRACE, ("===>SxOidRequest: OidRequest %p.\n", OidRequest)); + + NdisInterlockedIncrement(&switchObject->PendingOidCount); + + status = NdisAllocateCloneOidRequest(switchObject->NdisFilterHandle, + OidRequest, + SxExtAllocationTag, + &clonedRequest); + if (status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_WARN, ("FilerOidRequest: Cannot Clone OidRequest\n")); + goto Cleanup; + } + + cloneRequestContext = (PVOID*)(&clonedRequest->SourceReserved[0]); + *cloneRequestContext = OidRequest; + + switch (clonedRequest->RequestType) + { + case NdisRequestSetInformation: + status = SxpNdisProcessSetOid(switchObject, + clonedRequest, + &completeOid); + break; + + case NdisRequestMethod: + status = SxpNdisProcessMethodOid(switchObject, + clonedRequest, + &completeOid, + &bytesNeeded); + + break; + } + + if (completeOid) + { + NdisFreeCloneOidRequest(switchObject->NdisFilterHandle, clonedRequest); + OidRequest->DATA.METHOD_INFORMATION.BytesNeeded = bytesNeeded; + NdisInterlockedDecrement(&switchObject->PendingOidCount); + goto Cleanup; + } + + status = NdisFOidRequest(switchObject->NdisFilterHandle, clonedRequest); + + if (status != NDIS_STATUS_PENDING) + { + SxNdisOidRequestComplete(switchObject, clonedRequest, status); + + // + // We must still return status as pending because we complete the + // request using NdisFOidRequestComplete() in SxOidRequestComplete(). + // + status = NDIS_STATUS_PENDING; + } + +Cleanup: + + DEBUGP(DL_TRACE, ("<===SxOidRequest: status %8x.\n", status)); + return status; +} + + +// +// FilterCancelOidRequest Function +// http://msdn.microsoft.com/en-us/library/ff549911(v=VS.85).aspx +// +_Use_decl_annotations_ +VOID +SxNdisCancelOidRequest( + NDIS_HANDLE FilterModuleContext, + PVOID RequestId + ) +{ + UNREFERENCED_PARAMETER(FilterModuleContext); + UNREFERENCED_PARAMETER(RequestId); +} + + +// +// FilterOidRequestComplete Function +// http://msdn.microsoft.com/en-us/library/ff549956(v=VS.85).aspx +// +_Use_decl_annotations_ +VOID +SxNdisOidRequestComplete( + NDIS_HANDLE FilterModuleContext, + PNDIS_OID_REQUEST NdisOidRequest, + NDIS_STATUS Status + ) +{ + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)FilterModuleContext; + PNDIS_OID_REQUEST originalRequest; + PVOID *oidRequestContext; + PNDIS_SWITCH_NIC_OID_REQUEST nicOidRequestBuf; + PNDIS_OBJECT_HEADER header; + + DEBUGP(DL_TRACE, + ("===>SxOidRequestComplete, NdisOidRequest %p.\n", NdisOidRequest)); + + oidRequestContext = (PVOID*)(&NdisOidRequest->SourceReserved[0]); + originalRequest = (*oidRequestContext); + + // + // This is the internal request + // + if (originalRequest == NULL) + { + SxpNdisCompleteInternalOidRequest(switchObject, NdisOidRequest, Status); + goto Cleanup; + } + + // + // Copy the information from the returned request to the original request + // + switch(NdisOidRequest->RequestType) + { + case NdisRequestMethod: + originalRequest->DATA.METHOD_INFORMATION.OutputBufferLength = + NdisOidRequest->DATA.METHOD_INFORMATION.OutputBufferLength; + originalRequest->DATA.METHOD_INFORMATION.BytesRead = + NdisOidRequest->DATA.METHOD_INFORMATION.BytesRead; + originalRequest->DATA.METHOD_INFORMATION.BytesNeeded = + NdisOidRequest->DATA.METHOD_INFORMATION.BytesNeeded; + originalRequest->DATA.METHOD_INFORMATION.BytesWritten = + NdisOidRequest->DATA.METHOD_INFORMATION.BytesWritten; + + if (NdisOidRequest->DATA.METHOD_INFORMATION.Oid == OID_SWITCH_NIC_REQUEST && + switchObject->OldNicRequest != NULL) + { + nicOidRequestBuf = NdisOidRequest->DATA.METHOD_INFORMATION.InformationBuffer; + Status = SxExtProcessNicRequestComplete(switchObject, + switchObject->ExtensionContext, + nicOidRequestBuf->OidRequest, + nicOidRequestBuf->SourcePortId, + nicOidRequestBuf->SourceNicIndex, + nicOidRequestBuf->DestinationPortId, + nicOidRequestBuf->DestinationNicIndex, + Status); + + originalRequest->DATA.METHOD_INFORMATION.InformationBuffer = + switchObject->OldNicRequest; + switchObject->OldNicRequest = NULL; + ExFreePoolWithTag(nicOidRequestBuf, SxExtAllocationTag); + } + + break; + + case NdisRequestSetInformation: + header = originalRequest->DATA.SET_INFORMATION.InformationBuffer; + + originalRequest->DATA.SET_INFORMATION.BytesRead = + NdisOidRequest->DATA.SET_INFORMATION.BytesRead; + originalRequest->DATA.SET_INFORMATION.BytesNeeded = + NdisOidRequest->DATA.SET_INFORMATION.BytesNeeded; + + if (NdisOidRequest->DATA.METHOD_INFORMATION.Oid == OID_SWITCH_PORT_CREATE && + Status != NDIS_STATUS_SUCCESS) + { + SxExtDeletePort(switchObject, + switchObject->ExtensionContext, + (PNDIS_SWITCH_PORT_PARAMETERS)header); + } + else if (NdisOidRequest->DATA.METHOD_INFORMATION.Oid == OID_SWITCH_PORT_CREATE && + Status != NDIS_STATUS_SUCCESS) + { + SxExtDeleteNic(switchObject, + switchObject->ExtensionContext, + (PNDIS_SWITCH_NIC_PARAMETERS)header); + + } + + break; + + case NdisRequestQueryInformation: + case NdisRequestQueryStatistics: + default: + originalRequest->DATA.QUERY_INFORMATION.BytesWritten = + NdisOidRequest->DATA.QUERY_INFORMATION.BytesWritten; + originalRequest->DATA.QUERY_INFORMATION.BytesNeeded = + NdisOidRequest->DATA.QUERY_INFORMATION.BytesNeeded; + break; + } + + (*oidRequestContext) = NULL; + + NdisFreeCloneOidRequest(switchObject->NdisFilterHandle, NdisOidRequest); + + NdisFOidRequestComplete(switchObject->NdisFilterHandle, + originalRequest, + Status); + + DEBUGP(DL_TRACE, ("<===SxOidRequestComplete.\n")); + +Cleanup: + NdisInterlockedDecrement(&switchObject->PendingOidCount); +} + + +// +// FilterSendNetBufferLists Function +// http://msdn.microsoft.com/en-us/library/ff549966(v=VS.85).aspx +// +_Use_decl_annotations_ +VOID +SxNdisSendNetBufferLists( + NDIS_HANDLE FilterModuleContext, + PNET_BUFFER_LIST NetBufferLists, + NDIS_PORT_NUMBER PortNumber, + ULONG SendFlags + ) +{ + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)FilterModuleContext; + + UNREFERENCED_PARAMETER(PortNumber); + + SxExtStartNetBufferListsIngress(switchObject, + switchObject->ExtensionContext, + NetBufferLists, + SendFlags); +} + + +// +// FilterSendNetBufferListsComplete Function +// http://msdn.microsoft.com/en-us/library/ff549967(v=VS.85).aspx +// +_Use_decl_annotations_ +VOID +SxNdisSendNetBufferListsComplete( + NDIS_HANDLE FilterModuleContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG SendCompleteFlags + ) +{ + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)FilterModuleContext; + + SxExtStartCompleteNetBufferListsIngress(switchObject, + switchObject->ExtensionContext, + NetBufferLists, + SendCompleteFlags); +} + + +// +// FilterReceiveNetBufferLists Function +// http://msdn.microsoft.com/en-us/library/ff549960(v=VS.85).aspx +// +_Use_decl_annotations_ +VOID +SxNdisReceiveNetBufferLists( + NDIS_HANDLE FilterModuleContext, + PNET_BUFFER_LIST NetBufferLists, + NDIS_PORT_NUMBER PortNumber, + ULONG NumberOfNetBufferLists, + ULONG ReceiveFlags + ) +{ + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)FilterModuleContext; + + UNREFERENCED_PARAMETER(PortNumber); + + SxExtStartNetBufferListsEgress(switchObject, + switchObject->ExtensionContext, + NetBufferLists, + NumberOfNetBufferLists, + ReceiveFlags); +} + + +// +// FilterReturnNetBufferLists Function +// http://msdn.microsoft.com/en-us/library/ff549964(v=VS.85).aspx +// +_Use_decl_annotations_ +VOID +SxNdisReturnNetBufferLists( + NDIS_HANDLE FilterModuleContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG ReturnFlags + ) +{ + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)FilterModuleContext; + + SxExtStartCompleteNetBufferListsEgress(switchObject, + switchObject->ExtensionContext, + NetBufferLists, + ReturnFlags); +} + + +// +// FilterCancelSendNetBufferLists Function +// http://msdn.microsoft.com/en-us/library/ff549915(v=VS.85).aspx +// +_Use_decl_annotations_ +VOID +SxNdisCancelSendNetBufferLists( + NDIS_HANDLE FilterModuleContext, + PVOID CancelId + ) +{ + UNREFERENCED_PARAMETER(FilterModuleContext); + UNREFERENCED_PARAMETER(CancelId); +} + + +// +// FilterNetPnPEvent Function +// http://msdn.microsoft.com/en-us/library/ff549952(v=vs.85).aspx +// +_Use_decl_annotations_ +NDIS_STATUS +SxNdisNetPnPEvent( + NDIS_HANDLE FilterModuleContext, + PNET_PNP_EVENT_NOTIFICATION NetPnPEvent + ) +{ + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)FilterModuleContext; + + if (NetPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) + { + // + // Switch Activation must be passed along regardless of successful + // initialization. + // + SxExtActivateSwitch(switchObject, + switchObject->ExtensionContext); + } + + return NdisFNetPnPEvent(switchObject->NdisFilterHandle, + NetPnPEvent); +} + + +// +// FilterStatus Function +// http://msdn.microsoft.com/en-us/library/ff549973(v=VS.85).aspx +// +_Use_decl_annotations_ +VOID +SxNdisStatus( + NDIS_HANDLE FilterModuleContext, + PNDIS_STATUS_INDICATION StatusIndication + ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + PSX_SWITCH_OBJECT switchObject = (PSX_SWITCH_OBJECT)FilterModuleContext; + PNDIS_SWITCH_NIC_STATUS_INDICATION nicIndication; + PNDIS_STATUS_INDICATION originalIndication; + + if (StatusIndication->Header.Type != NDIS_OBJECT_TYPE_STATUS_INDICATION || + StatusIndication->Header.Revision != NDIS_STATUS_INDICATION_REVISION_1 || + StatusIndication->Header.Size < NDIS_SIZEOF_STATUS_INDICATION_REVISION_1) + { + goto Cleanup; + } + + // + // Only NDIS_STATUS_SWITCH_NIC_STAUTUS indications need to be processed + // by switch extensions. + // + if (StatusIndication->StatusCode != NDIS_STATUS_SWITCH_NIC_STATUS) + { + goto Cleanup; + } + + nicIndication = StatusIndication->StatusBuffer; + + if (nicIndication->Header.Type != NDIS_OBJECT_TYPE_STATUS_INDICATION || + nicIndication->Header.Revision != NDIS_SWITCH_NIC_STATUS_INDICATION_REVISION_1 || + nicIndication->Header.Size < NDIS_SIZEOF_SWITCH_NIC_STATUS_REVISION_1) + { + goto Cleanup; + } + + originalIndication = nicIndication->StatusIndication; + + status = SxExtProcessNicStatus(switchObject, + switchObject->ExtensionContext, + originalIndication, + nicIndication->SourcePortId, + nicIndication->SourceNicIndex); + +Cleanup: + if (status == NDIS_STATUS_SUCCESS) + { + NdisFIndicateStatus(switchObject->NdisFilterHandle, + StatusIndication); + } + + return; + +} + + +NDIS_STATUS +SxpNdisProcessSetOid( + __in PSX_SWITCH_OBJECT Switch, + __inout PNDIS_OID_REQUEST OidRequest, + __out PBOOLEAN Complete + ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + NDIS_OID oid = OidRequest->DATA.SET_INFORMATION.Oid; + PNDIS_OBJECT_HEADER header; + ULONG bytesRestored = 0; + + *Complete = FALSE; + + header = OidRequest->DATA.SET_INFORMATION.InformationBuffer; + + if (OidRequest->DATA.SET_INFORMATION.InformationBufferLength != 0 && + OidRequest->DATA.SET_INFORMATION.InformationBufferLength < + sizeof(NDIS_OBJECT_HEADER)) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + if (OidRequest->DATA.SET_INFORMATION.InformationBufferLength == 0) + { + *Complete = FALSE; + goto Cleanup; + } + + switch(oid) + { + case OID_SWITCH_PROPERTY_ADD: + case OID_SWITCH_PROPERTY_UPDATE: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_PROPERTY_PARAMETERS_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_PROPERTY_PARAMETERS_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + if (oid == OID_SWITCH_PROPERTY_ADD) + { + status = SxExtAddSwitchProperty(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PROPERTY_PARAMETERS)header); + } + else + { + status = SxExtUpdateSwitchProperty(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PROPERTY_PARAMETERS)header); + } + + if (status == NDIS_STATUS_NOT_SUPPORTED) + { + status = NDIS_STATUS_SUCCESS; + } + else + { + *Complete = TRUE; + goto Cleanup; + } + + break; + case OID_SWITCH_PROPERTY_DELETE: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_PROPERTY_DELETE_PARAMETERS_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_PROPERTY_DELETE_PARAMETERS_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + *Complete = SxExtDeleteSwitchProperty(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PROPERTY_DELETE_PARAMETERS)header); + + break; + + case OID_SWITCH_PORT_PROPERTY_ADD: + case OID_SWITCH_PORT_PROPERTY_UPDATE: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_PORT_PROPERTY_PARAMETERS_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_PORT_PROPERTY_PARAMETERS_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + if (oid == OID_SWITCH_PORT_PROPERTY_ADD) + { + status = SxExtAddPortProperty(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PORT_PROPERTY_PARAMETERS)header); + } + else + { + status = SxExtUpdatePortProperty(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PORT_PROPERTY_PARAMETERS)header); + } + + if (status == NDIS_STATUS_NOT_SUPPORTED) + { + status = NDIS_STATUS_SUCCESS; + } + else + { + *Complete = TRUE; + goto Cleanup; + } + + break; + + case OID_SWITCH_PORT_PROPERTY_DELETE: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_PORT_PROPERTY_DELETE_PARAMETERS_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_PORT_PROPERTY_DELETE_PARAMETERS_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + *Complete = SxExtDeletePortProperty(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PORT_PROPERTY_DELETE_PARAMETERS)header); + + break; + + case OID_SWITCH_PORT_CREATE: + case OID_SWITCH_PORT_UPDATED: + case OID_SWITCH_PORT_TEARDOWN: + case OID_SWITCH_PORT_DELETE: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_PORT_PARAMETERS_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_PORT_PARAMETERS_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + if (oid == OID_SWITCH_PORT_CREATE) + { + status = SxExtCreatePort(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PORT_PARAMETERS)header); + + if (status != NDIS_STATUS_SUCCESS) + { + *Complete = TRUE; + } + } + else if (oid == OID_SWITCH_PORT_UPDATED) + { + SxExtUpdatePort(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PORT_PARAMETERS)header); + } + else if (oid == OID_SWITCH_PORT_TEARDOWN) + { + SxExtTeardownPort(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PORT_PARAMETERS)header); + } + else + { + SxExtDeletePort(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PORT_PARAMETERS)header); + } + + break; + + case OID_SWITCH_NIC_CREATE: + case OID_SWITCH_NIC_CONNECT: + case OID_SWITCH_NIC_UPDATED: + case OID_SWITCH_NIC_DISCONNECT: + case OID_SWITCH_NIC_DELETE: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_NIC_PARAMETERS_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_NIC_PARAMETERS_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + if (oid == OID_SWITCH_NIC_CREATE) + { + status = SxExtCreateNic(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_NIC_PARAMETERS)header); + if (status != NDIS_STATUS_SUCCESS) + { + *Complete = TRUE; + } + } + else if (oid == OID_SWITCH_NIC_CONNECT) + { + SxExtConnectNic(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_NIC_PARAMETERS)header); + } + else if (oid == OID_SWITCH_NIC_UPDATED) + { + SxExtUpdateNic(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_NIC_PARAMETERS)header); + } + else if (oid == OID_SWITCH_NIC_DISCONNECT) + { + SxExtDisconnectNic(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_NIC_PARAMETERS)header); + } + else + { + SxExtDeleteNic(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_NIC_PARAMETERS)header); + } + + break; + + case OID_SWITCH_NIC_RESTORE: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_NIC_SAVE_STATE_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_NIC_SAVE_STATE_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + goto Cleanup; + } + + status = SxExtNicRestore(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_NIC_SAVE_STATE)header, + &bytesRestored); + + if (status != NDIS_STATUS_SUCCESS) + { + *Complete = TRUE; + } + else if (bytesRestored > 0) + { + *Complete = TRUE; + } + + break; + + case OID_SWITCH_NIC_SAVE_COMPLETE: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_NIC_SAVE_STATE_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_NIC_SAVE_STATE_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + SxExtSaveNicComplete(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_NIC_SAVE_STATE)header); + + break; + + case OID_SWITCH_NIC_RESTORE_COMPLETE: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_NIC_SAVE_STATE_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_NIC_SAVE_STATE_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + SxExtNicRestoreComplete(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_NIC_SAVE_STATE)header); + + break; + + default: + break; + } + +Cleanup: + return status; +} + + +NDIS_STATUS +SxpNdisProcessMethodOid( + __in PSX_SWITCH_OBJECT Switch, + __inout PNDIS_OID_REQUEST OidRequest, + __out PBOOLEAN Complete, + __out PULONG BytesNeeded + ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + NDIS_OID oid = OidRequest->DATA.SET_INFORMATION.Oid; + PNDIS_OBJECT_HEADER header; + PNDIS_SWITCH_NIC_OID_REQUEST nicOidRequest; + PNDIS_SWITCH_NIC_OID_REQUEST newNicOidRequest = NULL; + NDIS_SWITCH_PORT_ID destPort, sourcePort; + NDIS_SWITCH_NIC_INDEX destNic, sourceNic; + ULONG bytesWritten = 0; + ULONG bytesNeeded = 0; + + *Complete = FALSE; + *BytesNeeded = 0; + + header = OidRequest->DATA.METHOD_INFORMATION.InformationBuffer; + + switch(oid) + { + case OID_SWITCH_FEATURE_STATUS_QUERY: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_FEATURE_STATUS_PARAMETERS_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_FEATURE_STATUS_PARAMETERS_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + *Complete = SxExtQuerySwitchFeatureStatus(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_FEATURE_STATUS_PARAMETERS)header, + BytesNeeded); + + if (*BytesNeeded > 0) + { + status = NDIS_STATUS_BUFFER_TOO_SHORT; + } + + break; + + case OID_SWITCH_PORT_FEATURE_STATUS_QUERY: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_FEATURE_STATUS_PARAMETERS_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_FEATURE_STATUS_PARAMETERS_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + *Complete = SxExtQueryPortFeatureStatus(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_PORT_FEATURE_STATUS_PARAMETERS)header, + BytesNeeded); + + if (*BytesNeeded > 0) + { + status = NDIS_STATUS_BUFFER_TOO_SHORT; + } + + break; + + case OID_SWITCH_NIC_REQUEST: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_NIC_OID_REQUEST_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_NIC_OID_REQUEST_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + nicOidRequest = (PNDIS_SWITCH_NIC_OID_REQUEST)header; + + sourcePort = nicOidRequest->SourcePortId; + sourceNic = nicOidRequest->SourceNicIndex; + destPort = nicOidRequest->DestinationPortId; + destNic = nicOidRequest->DestinationNicIndex; + + status = SxExtProcessNicRequest(Switch, + Switch->ExtensionContext, + nicOidRequest->OidRequest, + &sourcePort, + &sourceNic, + &destPort, + &destNic); + + if (status != NDIS_STATUS_SUCCESS) + { + *Complete = TRUE; + goto Cleanup; + } + + if (sourcePort != nicOidRequest->SourcePortId || + sourceNic != nicOidRequest->SourceNicIndex || + destPort != nicOidRequest->DestinationPortId || + destNic != nicOidRequest->DestinationNicIndex) + { + ASSERT(Switch->OldNicRequest == NULL); + Switch->OldNicRequest = nicOidRequest; + + newNicOidRequest = (PNDIS_SWITCH_NIC_OID_REQUEST)ExAllocatePoolWithTag( + NonPagedPoolNx, + sizeof(NDIS_SWITCH_NIC_OID_REQUEST), + SxExtAllocationTag); + + if (newNicOidRequest == NULL) + { + status = NDIS_STATUS_RESOURCES; + *Complete = TRUE; + goto Cleanup; + } + + newNicOidRequest->Header = nicOidRequest->Header; + newNicOidRequest->SourcePortId = sourcePort; + newNicOidRequest->SourceNicIndex = sourceNic; + newNicOidRequest->DestinationPortId = destPort; + newNicOidRequest->DestinationNicIndex = destNic; + newNicOidRequest->OidRequest = nicOidRequest->OidRequest; + + OidRequest->DATA.METHOD_INFORMATION.InformationBuffer = newNicOidRequest; + } + + break; + + case OID_SWITCH_NIC_SAVE: + if (header->Type != NDIS_OBJECT_TYPE_DEFAULT || + header->Revision < NDIS_SWITCH_NIC_SAVE_STATE_REVISION_1 || + header->Size < NDIS_SIZEOF_NDIS_SWITCH_NIC_SAVE_STATE_REVISION_1) + { + status = NDIS_STATUS_NOT_SUPPORTED; + *Complete = TRUE; + goto Cleanup; + } + + status = SxExtSaveNic(Switch, + Switch->ExtensionContext, + (PNDIS_SWITCH_NIC_SAVE_STATE)header, + &bytesWritten, + &bytesNeeded); + + if (status == NDIS_STATUS_SUCCESS && + bytesWritten > 0) + { + *Complete = TRUE; + } + else if (status == NDIS_STATUS_BUFFER_TOO_SHORT) + { + *BytesNeeded = ((PNDIS_SWITCH_NIC_SAVE_STATE)header)->SaveDataOffset + + bytesNeeded; + *Complete = TRUE; + } + else if (status != NDIS_STATUS_SUCCESS) + { + *Complete = TRUE; + } + + break; + + default: + break; + } + +Cleanup: + return status; +} + + +VOID +SxpNdisCompleteInternalOidRequest( + __in PSX_SWITCH_OBJECT Switch, + __in PNDIS_OID_REQUEST NdisRequest, + __in NDIS_STATUS Status + ) +/*++ + +Routine Description: + + NDIS entry point indicating completion of a pended NDIS_OID_REQUEST. + +Arguments: + + Switch - pointer to switch object. + + NdisRequest - pointer to NDIS request + + Status - Status of request completion + +Return Value: + + None + +--*/ +{ + PSX_OID_REQUEST oidRequest; + ULONG bytesNeeded; + + UNREFERENCED_PARAMETER(Switch); + + bytesNeeded = 0; + oidRequest = NULL; + + switch (NdisRequest->RequestType) + { + case NdisRequestSetInformation: + bytesNeeded = NdisRequest->DATA.SET_INFORMATION.BytesNeeded; + break; + + case NdisRequestQueryInformation: + bytesNeeded = NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded; + break; + + case NdisRequestMethod: + bytesNeeded = NdisRequest->DATA.METHOD_INFORMATION.BytesNeeded; + break; + } + + // + // Get at the request context. + // + oidRequest = CONTAINING_RECORD(NdisRequest, SX_OID_REQUEST, NdisOidRequest); + + // + // Save away the completion status. + // + oidRequest->Status = Status; + + // + // Save bytesNeeded + // + oidRequest->BytesNeeded = bytesNeeded; + + // + // Wake up the thread blocked for this request to complete. + // + NdisSetEvent(&oidRequest->ReqEvent); +} + diff --git a/network/ndis/extension/base/SxBase.h b/network/ndis/extension/base/SxBase.h new file mode 100644 index 00000000..1e57b0f1 --- /dev/null +++ b/network/ndis/extension/base/SxBase.h @@ -0,0 +1,168 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved. + +Module Name: + + SxBase.c + +Abstract: + + This file contains the common code for building a switch extension. This + file includes all the standard NDIS handling and exposes a function based + interface for the control path and data path. This also has reusable code + for basic operations such as pause/resume handling etc. + + +--*/ + +extern NDIS_STRING SxExtensionFriendlyName; +extern NDIS_STRING SxExtensionGuid; + +typedef enum _SX_SWITCH_DATAFLOW_STATE +{ + SxSwitchPaused, + SxSwitchRunning +} SX_SWITCH_DATAFLOW_STATE, *PSX_SWITCH_DATAFLOW_STATE; + +typedef enum _SX_SWITCH_CONTROFLOW_STATE +{ + SxSwitchUnknown, + SxSwitchAttached, + SxSwitchDetached +} SX_SWITCH_CONTROLFLOW_STATE, *PSX_SWITCH_CONTROLFLOW_STATE; + +typedef struct _SX_SWITCH_OBJECT +{ + // + // The Link *must* always be the first field. + // + LIST_ENTRY Link; + + // + // The extension context is the context used in the specific logic + // of this extension. + // This is allocated and returned in SxExtSwitchCreate + // + PNDIS_HANDLE ExtensionContext; + + // + // Ndis related fields. + // + NDIS_HANDLE NdisFilterHandle; + NDIS_SWITCH_CONTEXT NdisSwitchContext; + NDIS_SWITCH_OPTIONAL_HANDLERS NdisSwitchHandlers; + + // + // Switch state. + // + SX_SWITCH_DATAFLOW_STATE DataFlowState; + SX_SWITCH_CONTROLFLOW_STATE ControlFlowState; + + // + // Management fields. + // + volatile LONG PendingInjectedNblCount; + volatile LONG PendingOidCount; + + // + // Control Path Management. + // + PNDIS_SWITCH_NIC_OID_REQUEST OldNicRequest; + +} SX_SWITCH_OBJECT, *PSX_SWITCH_OBJECT; + +typedef struct _SX_OID_REQUEST +{ + NDIS_OID_REQUEST NdisOidRequest; + NDIS_EVENT ReqEvent; + NDIS_STATUS Status; + ULONG BytesNeeded; + +} SX_OID_REQUEST, *PSX_OID_REQUEST; + +typedef struct _FILTER_DEVICE_EXTENSION +{ + ULONG Signature; + NDIS_HANDLE Handle; +} FILTER_DEVICE_EXTENSION, *PFILTER_DEVICE_EXTENSION; + + +// +// Function prototypes +// +DRIVER_INITIALIZE DriverEntry; + +DRIVER_UNLOAD SxNdisUnload; + +FILTER_SET_OPTIONS SxNdisSetOptions; + +FILTER_SET_MODULE_OPTIONS SxNdisSetFilterModuleOptions; + +FILTER_ATTACH SxNdisAttach; + +FILTER_DETACH SxNdisDetach; + +FILTER_PAUSE SxNdisPause; + +FILTER_RESTART SxNdisRestart; + +FILTER_OID_REQUEST SxNdisOidRequest; + +FILTER_CANCEL_OID_REQUEST SxNdisCancelOidRequest; + +FILTER_OID_REQUEST_COMPLETE SxNdisOidRequestComplete; + +FILTER_SEND_NET_BUFFER_LISTS SxNdisSendNetBufferLists; + +FILTER_RETURN_NET_BUFFER_LISTS SxNdisReturnNetBufferLists; + +FILTER_SEND_NET_BUFFER_LISTS_COMPLETE SxNdisSendNetBufferListsComplete; + +FILTER_RECEIVE_NET_BUFFER_LISTS SxNdisReceiveNetBufferLists; + +FILTER_CANCEL_SEND_NET_BUFFER_LISTS SxNdisCancelSendNetBufferLists; + +FILTER_STATUS SxNdisStatus; + +FILTER_NET_PNP_EVENT SxNdisNetPnPEvent; + +VOID +SxpNdisCompleteInternalOidRequest( + __in PSX_SWITCH_OBJECT Switch, + __in PNDIS_OID_REQUEST NdisRequest, + __in NDIS_STATUS Status + ); + + +// +// Some debug stuff. +// +#define DL_EXTRA_LOUD 20 +#define DL_VERY_LOUD 10 +#define DL_LOUD 8 +#define DL_INFO 6 +#define DL_TRACE 5 +#define DL_WARN 4 +#define DL_ERROR 2 +#define DL_FATAL 0 + +#if DBG + +extern ULONG SxDebugLevel; + +#define DEBUGP(lev, stmt) \ + { \ + if ((lev) <= SxDebugLevel) \ + { \ + DbgPrint("%S: ",SxExtServiceName); DbgPrint stmt; \ + } \ + } + +#else + +#define DEBUGP(lev, stmt) + +#endif + + diff --git a/network/ndis/extension/base/SxLibrary.c b/network/ndis/extension/base/SxLibrary.c new file mode 100644 index 00000000..ea2f53b6 --- /dev/null +++ b/network/ndis/extension/base/SxLibrary.c @@ -0,0 +1,839 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved. + +Module Name: + + SxLibrary.c + +Abstract: + + This file contains the common library functions that can be used + by any extension using the SxBase library. + + +--*/ + +#include "precomp.h" + +VOID +SxLibSendNetBufferListsIngress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG SendFlags, + _In_ ULONG NumInjectedNetBufferLists + ) +{ + BOOLEAN dispatch; + BOOLEAN sameSource; + ULONG sendCompleteFlags; + PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; + PNET_BUFFER_LIST curNbl, nextNbl; + ULONG numNbls = 0; + PNET_BUFFER_LIST dropNbl = NULL; + PNET_BUFFER_LIST *curDropNbl = &dropNbl; + NDIS_SWITCH_PORT_ID curSourcePort; + NDIS_STRING filterReason; + + dispatch = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags); + sameSource = NDIS_TEST_SEND_FLAG(SendFlags, NDIS_SEND_FLAGS_SWITCH_SINGLE_SOURCE); + + InterlockedAdd(&Switch->PendingInjectedNblCount, NumInjectedNetBufferLists); + KeMemoryBarrier(); + + if (Switch->DataFlowState != SxSwitchRunning) + { + RtlInitUnicodeString(&filterReason, L"Extension Paused"); + + sendCompleteFlags = (dispatch) ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0; + sendCompleteFlags |= (sameSource) ? NDIS_SEND_COMPLETE_FLAGS_SWITCH_SINGLE_SOURCE : 0; + + fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(NetBufferLists); + + if (sameSource) + { + for (curNbl = NetBufferLists; curNbl != NULL; curNbl = curNbl->Next) + { + ++numNbls; + } + + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + fwdDetail->SourcePortId, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + numNbls, + NetBufferLists, + &filterReason); + + SxExtStartCompleteNetBufferListsIngress(Switch, + Switch->ExtensionContext, + NetBufferLists, + sendCompleteFlags); + } + else + { + curSourcePort = fwdDetail->SourcePortId; + for (curNbl = NetBufferLists; curNbl != NULL; curNbl = nextNbl) + { + nextNbl = curNbl->Next; + curNbl->Next = NULL; + + fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl); + + if(curSourcePort == fwdDetail->SourcePortId) + { + *curDropNbl = curNbl; + curDropNbl = &(curNbl->Next); + ++numNbls; + } + else + { + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + curSourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + numNbls, + dropNbl, + &filterReason); + + SxExtStartCompleteNetBufferListsIngress(Switch, + Switch->ExtensionContext, + dropNbl, + sendCompleteFlags); + + numNbls = 1; + dropNbl = curNbl; + curDropNbl = &(curNbl->Next); + curSourcePort = fwdDetail->SourcePortId; + } + } + + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + curSourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + numNbls, + dropNbl, + &filterReason); + + SxExtStartCompleteNetBufferListsIngress(Switch, + Switch->ExtensionContext, + dropNbl, + sendCompleteFlags); + } + + goto Cleanup; + } + + NdisFSendNetBufferLists(Switch->NdisFilterHandle, + NetBufferLists, + NDIS_DEFAULT_PORT_NUMBER, + SendFlags); + +Cleanup: + return; +} + + +VOID +SxLibSendNetBufferListsEgress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG NumberOfNetBufferLists, + _In_ ULONG ReceiveFlags + ) +{ + BOOLEAN dispatch, sameSource; + NDIS_SWITCH_PORT_ID sourcePortId; + PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; + ULONG returnFlags; + NDIS_SWITCH_PORT_ID curSourcePort; + PNET_BUFFER_LIST curNbl, nextNbl; + ULONG numNbls; + PNET_BUFFER_LIST dropNbl = NULL; + PNET_BUFFER_LIST *curDropNbl = &dropNbl; + NDIS_STRING filterReason; + + dispatch = NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags); + sameSource = NDIS_TEST_RECEIVE_FLAG(ReceiveFlags, NDIS_RECEIVE_FLAGS_SWITCH_SINGLE_SOURCE); + + if (Switch->DataFlowState != SxSwitchRunning) + { + RtlInitUnicodeString(&filterReason, L"Extension Paused"); + + returnFlags = (dispatch) ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0; + returnFlags |= NDIS_RETURN_FLAGS_SWITCH_SINGLE_SOURCE; + + fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(NetBufferLists); + + if (sameSource) + { + sourcePortId = fwdDetail->SourcePortId; + + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + sourcePortId, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + NumberOfNetBufferLists, + NetBufferLists, + &filterReason); + + SxExtStartCompleteNetBufferListsEgress(Switch, + Switch->ExtensionContext, + NetBufferLists, + returnFlags); + } + else + { + curSourcePort = fwdDetail->SourcePortId; + numNbls = 0; + for (curNbl = NetBufferLists; curNbl != NULL; curNbl = nextNbl) + { + nextNbl = curNbl->Next; + curNbl->Next = NULL; + + fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl); + + if(curSourcePort == fwdDetail->SourcePortId) + { + *curDropNbl = curNbl; + curDropNbl = &(curNbl->Next); + ++numNbls; + } + else + { + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + curSourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + numNbls, + dropNbl, + &filterReason); + + SxExtStartCompleteNetBufferListsEgress(Switch, + Switch->ExtensionContext, + dropNbl, + returnFlags); + + numNbls = 1; + dropNbl = curNbl; + curDropNbl = &(curNbl->Next); + curSourcePort = fwdDetail->SourcePortId; + } + } + + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + curSourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + numNbls, + dropNbl, + &filterReason); + + SxExtStartCompleteNetBufferListsEgress(Switch, + Switch->ExtensionContext, + dropNbl, + returnFlags); + } + + goto Cleanup; + } + + NdisFIndicateReceiveNetBufferLists(Switch->NdisFilterHandle, + NetBufferLists, + NDIS_DEFAULT_PORT_NUMBER, + NumberOfNetBufferLists, + ReceiveFlags); + +Cleanup: + return; +} + +VOID +SxLibCompleteNetBufferListsEgress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG ReturnFlags + ) +{ + NdisFReturnNetBufferLists(Switch->NdisFilterHandle, + NetBufferLists, + ReturnFlags); +} + + +VOID +SxLibCompleteNetBufferListsIngress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG SendCompleteFlags + ) +{ + NdisFSendNetBufferListsComplete(Switch->NdisFilterHandle, + NetBufferLists, + SendCompleteFlags); +} + +VOID +SxLibCompletedInjectedNetBufferLists( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ ULONG NumInjectedNetBufferLists + ) +{ + LONG subtract = -(LONG)NumInjectedNetBufferLists; + InterlockedAdd(&Switch->PendingInjectedNblCount, subtract); +} + + +NDIS_STATUS +SxLibIssueOidRequest( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_REQUEST_TYPE RequestType, + _In_ NDIS_OID Oid, + _In_opt_ PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _In_ ULONG OutputBufferLength, + _In_ ULONG MethodId, + _In_ UINT Timeout, + _Out_ PULONG BytesNeeded + ) +{ + NDIS_STATUS status; + PSX_OID_REQUEST oidRequest; + PNDIS_OID_REQUEST ndisOidRequest; + ULONG bytesNeeded; + BOOLEAN asyncCompletion; + + status = NDIS_STATUS_SUCCESS; + oidRequest = NULL; + bytesNeeded = 0; + asyncCompletion = FALSE; + + NdisInterlockedIncrement(&Switch->PendingOidCount); + + if (Switch->ControlFlowState != SxSwitchAttached) + { + status = NDIS_STATUS_CLOSING; + goto Cleanup; + } + + // + // Dynamically allocate filter request so that we can handle asynchronous + // completion. + // + oidRequest = (PSX_OID_REQUEST)ExAllocatePoolWithTag(NonPagedPoolNx, + sizeof(SX_OID_REQUEST), + SxExtAllocationTag); + if (oidRequest == NULL) + { + goto Cleanup; + } + + NdisZeroMemory(oidRequest, sizeof(SX_OID_REQUEST)); + ndisOidRequest = &oidRequest->NdisOidRequest; + NdisInitializeEvent(&oidRequest->ReqEvent); + + ndisOidRequest->Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST; + ndisOidRequest->Header.Revision = NDIS_OID_REQUEST_REVISION_1; + ndisOidRequest->Header.Size = sizeof(NDIS_OID_REQUEST); + ndisOidRequest->RequestType = RequestType; + ndisOidRequest->Timeout = Timeout; + + switch (RequestType) + { + case NdisRequestQueryInformation: + ndisOidRequest->DATA.QUERY_INFORMATION.Oid = Oid; + ndisOidRequest->DATA.QUERY_INFORMATION.InformationBuffer = + InformationBuffer; + ndisOidRequest->DATA.QUERY_INFORMATION.InformationBufferLength = + InformationBufferLength; + break; + + case NdisRequestSetInformation: + ndisOidRequest->DATA.SET_INFORMATION.Oid = Oid; + ndisOidRequest->DATA.SET_INFORMATION.InformationBuffer = + InformationBuffer; + ndisOidRequest->DATA.SET_INFORMATION.InformationBufferLength = + InformationBufferLength; + break; + + case NdisRequestMethod: + ndisOidRequest->DATA.METHOD_INFORMATION.Oid = Oid; + ndisOidRequest->DATA.METHOD_INFORMATION.MethodId = MethodId; + ndisOidRequest->DATA.METHOD_INFORMATION.InformationBuffer = + InformationBuffer; + ndisOidRequest->DATA.METHOD_INFORMATION.InputBufferLength = + InformationBufferLength; + ndisOidRequest->DATA.METHOD_INFORMATION.OutputBufferLength = + OutputBufferLength; + break; + + default: + NT_ASSERT(FALSE); + break; + } + + ndisOidRequest->RequestId = (PVOID)SxExtOidRequestId; + status = NdisFOidRequest(Switch->NdisFilterHandle, ndisOidRequest); + + if (status == NDIS_STATUS_PENDING) + { + asyncCompletion = TRUE; + NdisWaitEvent(&oidRequest->ReqEvent, 0); + } + else + { + SxpNdisCompleteInternalOidRequest(Switch, ndisOidRequest, status); + } + + bytesNeeded = oidRequest->BytesNeeded; + status = oidRequest->Status; + +Cleanup: + + if (BytesNeeded != NULL) + { + *BytesNeeded = bytesNeeded; + } + + if (!asyncCompletion) + { + NdisInterlockedDecrement(&Switch->PendingOidCount); + } + + if (oidRequest != NULL) + { + ExFreePoolWithTag(oidRequest, SxExtAllocationTag); + } + + return status; +} + + +NDIS_STATUS +SxLibGetSwitchParametersUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _Out_ PNDIS_SWITCH_PARAMETERS SwitchParameters + ) +{ + NDIS_STATUS status; + + SwitchParameters->Header.Revision = NDIS_SWITCH_PARAMETERS_REVISION_1; + SwitchParameters->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + SwitchParameters->Header.Size = sizeof(NDIS_SWITCH_PARAMETERS); + + status = SxLibIssueOidRequest(Switch, + NdisRequestQueryInformation, + OID_SWITCH_PARAMETERS, + SwitchParameters, + sizeof(NDIS_SWITCH_PARAMETERS), + 0, + 0, + 0, + NULL); + + return status; +} + + +NDIS_STATUS +SxLibGetPortArrayUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _Out_ PNDIS_SWITCH_PORT_ARRAY *PortArray + ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + ULONG BytesNeeded = 0; + PNDIS_SWITCH_PORT_ARRAY portArray = NULL; + ULONG arrayLength = 0; + + do + { + if (portArray != NULL) + { + ExFreePoolWithTag(portArray, SxExtAllocationTag); + } + + if (BytesNeeded != 0) + { + arrayLength = BytesNeeded; + portArray = ExAllocatePoolWithTag(NonPagedPoolNx, + arrayLength, + SxExtAllocationTag); + + if (portArray == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + portArray->Header.Revision = NDIS_SWITCH_PORT_ARRAY_REVISION_1; + portArray->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + portArray->Header.Size = (USHORT)arrayLength; + } + + status = SxLibIssueOidRequest(Switch, + NdisRequestQueryInformation, + OID_SWITCH_PORT_ARRAY, + portArray, + arrayLength, + 0, + 0, + 0, + &BytesNeeded); + + } while(status == NDIS_STATUS_INVALID_LENGTH); + + *PortArray = portArray; +Cleanup: + if (status != NDIS_STATUS_SUCCESS && + portArray != NULL) + { + ExFreePoolWithTag(portArray, SxExtAllocationTag); + } + + return status; +} + + +NDIS_STATUS +SxLibGetNicArrayUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _Out_ PNDIS_SWITCH_NIC_ARRAY *NicArray + ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + ULONG BytesNeeded = 0; + PNDIS_SWITCH_NIC_ARRAY nicArray = NULL; + ULONG arrayLength = 0; + + do + { + if (nicArray != NULL) + { + ExFreePoolWithTag(nicArray, SxExtAllocationTag); + } + + if (BytesNeeded != 0) + { + arrayLength = BytesNeeded; + nicArray = ExAllocatePoolWithTag(NonPagedPoolNx, + arrayLength, + SxExtAllocationTag); + + if (nicArray == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + nicArray->Header.Revision = NDIS_SWITCH_PORT_ARRAY_REVISION_1; + nicArray->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + nicArray->Header.Size = (USHORT)arrayLength; + } + + status = SxLibIssueOidRequest(Switch, + NdisRequestQueryInformation, + OID_SWITCH_NIC_ARRAY, + nicArray, + arrayLength, + 0, + 0, + 0, + &BytesNeeded); + + } while(status == NDIS_STATUS_INVALID_LENGTH); + + *NicArray = nicArray; +Cleanup: + if (status != NDIS_STATUS_SUCCESS && + nicArray != NULL) + { + ExFreePoolWithTag(nicArray, SxExtAllocationTag); + } + + return status; +} + + +NDIS_STATUS +SxLibGetSwitchPropertyUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_SWITCH_PROPERTY_TYPE PropertyType, + _In_opt_ PNDIS_SWITCH_OBJECT_ID PropertyId, + _Outptr_ PNDIS_SWITCH_PROPERTY_ENUM_PARAMETERS *SwitchPropertyEnumParameters + ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + NDIS_SWITCH_PROPERTY_ENUM_PARAMETERS propertyParameters; + ULONG bytesNeeded = 0; + PNDIS_SWITCH_PROPERTY_ENUM_PARAMETERS outputBuffer = NULL; + USHORT outputBufferLength = sizeof(NDIS_SWITCH_PROPERTY_ENUM_PARAMETERS); + + propertyParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + propertyParameters.Header.Revision = NDIS_SWITCH_PROPERTY_ENUM_PARAMETERS_REVISION_1; + + propertyParameters.PropertyType = PropertyType; + propertyParameters.SerializationVersion = NDIS_SWITCH_OBJECT_SERIALIZATION_VERSION_1; + + // + // For Built-in properties, the ID is unnecessary. + // + if (PropertyId != NULL) + { + NdisMoveMemory(&propertyParameters.PropertyId, + PropertyId, + sizeof(NDIS_SWITCH_OBJECT_ID)); + } + else + { + ASSERT(PropertyType != NdisSwitchPropertyTypeCustom); + } + + outputBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, + outputBufferLength, + SxExtAllocationTag); + + if (outputBuffer == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + do + { + if (bytesNeeded != 0) + { + ExFreePoolWithTag(outputBuffer, SxExtAllocationTag); + + outputBufferLength = (USHORT)bytesNeeded; + outputBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, + outputBufferLength, + SxExtAllocationTag); + + if (outputBuffer == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + } + + if(outputBufferLength >= sizeof(propertyParameters)) + { + NdisMoveMemory(outputBuffer, &propertyParameters, sizeof(propertyParameters)); + + } + + status = SxLibIssueOidRequest(Switch, + NdisRequestMethod, + OID_SWITCH_PROPERTY_ENUM, + outputBuffer, + sizeof(propertyParameters), + outputBufferLength, + 0, + 0, + &bytesNeeded); + + } while(status == NDIS_STATUS_INVALID_LENGTH); + +Cleanup: + if (status != NDIS_STATUS_SUCCESS && + outputBuffer != NULL) + { + ExFreePoolWithTag(outputBuffer, SxExtAllocationTag); + outputBuffer = NULL; + } + + *SwitchPropertyEnumParameters = outputBuffer; + + return status; +} + + +NDIS_STATUS +SxLibGetPortPropertyUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_SWITCH_PORT_ID PortId, + _In_ NDIS_SWITCH_PORT_PROPERTY_TYPE PropertyType, + _In_opt_ PNDIS_SWITCH_OBJECT_ID PropertyId, + _Outptr_ PNDIS_SWITCH_PORT_PROPERTY_ENUM_PARAMETERS *PortPropertyEnumParameters + ) +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + NDIS_SWITCH_PORT_PROPERTY_ENUM_PARAMETERS propertyParameters; + ULONG bytesNeeded = 0; + PNDIS_SWITCH_PORT_PROPERTY_ENUM_PARAMETERS outputBuffer = NULL; + USHORT outputBufferLength = sizeof(NDIS_SWITCH_PORT_PROPERTY_ENUM_PARAMETERS); + + propertyParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + propertyParameters.Header.Revision = NDIS_SWITCH_PORT_PROPERTY_ENUM_PARAMETERS_REVISION_1; + + propertyParameters.PortId = PortId; + propertyParameters.PropertyType = PropertyType; + propertyParameters.SerializationVersion = NDIS_SWITCH_OBJECT_SERIALIZATION_VERSION_1; + + // + // For Built-in properties, the ID is unnecessary. + // + if (PropertyId != NULL) + { + NdisMoveMemory(&propertyParameters.PropertyId, + PropertyId, + sizeof(NDIS_SWITCH_OBJECT_ID)); + } + else + { + ASSERT(PropertyType != NdisSwitchPortPropertyTypeCustom); + } + + outputBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, + outputBufferLength, + SxExtAllocationTag); + + if (outputBuffer == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + do + { + if (bytesNeeded != 0) + { + ExFreePoolWithTag(outputBuffer, SxExtAllocationTag); + + outputBufferLength = (USHORT)bytesNeeded; + outputBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, + outputBufferLength, + SxExtAllocationTag); + + if (outputBuffer == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + } + + if(outputBufferLength >= sizeof(propertyParameters)) + { + outputBuffer->Header.Size = outputBufferLength; + NdisMoveMemory(outputBuffer, &propertyParameters, sizeof(propertyParameters)); + } + + status = SxLibIssueOidRequest(Switch, + NdisRequestMethod, + OID_SWITCH_PORT_PROPERTY_ENUM, + outputBuffer, + sizeof(propertyParameters), + outputBufferLength, + 0, + 0, + &bytesNeeded); + + } while(status == NDIS_STATUS_INVALID_LENGTH); + +Cleanup: + if (status != NDIS_STATUS_SUCCESS && + outputBuffer != NULL) + { + ExFreePoolWithTag(outputBuffer, SxExtAllocationTag); + outputBuffer = NULL; + } + + *PortPropertyEnumParameters = outputBuffer; + + return status; +} + + +VOID +SxLibRevokeVfUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_SWITCH_PORT_ID PortId + ) +{ + SxLibIssueNicStatusIndicationUnsafe(Switch, + NDIS_STATUS_SWITCH_PORT_REMOVE_VF, + PortId, + NDIS_SWITCH_DEFAULT_NIC_INDEX, + TRUE, + NULL, + 0); +} + + +VOID +SxLibIssueNicStatusIndicationUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_STATUS StatusCode, + _In_ NDIS_SWITCH_PORT_ID PortId, + _In_ NDIS_SWITCH_NIC_INDEX NicIndex, + _In_ BOOLEAN IsDestination, + _In_opt_ PVOID StatusBuffer, + _In_ ULONG StatusBufferSize + ) +{ + NDIS_STATUS_INDICATION statusIndication; + NDIS_STATUS_INDICATION wrappedIndication; + NDIS_SWITCH_NIC_STATUS_INDICATION nicIndication; + + NdisZeroMemory(&wrappedIndication, sizeof(wrappedIndication)); + + wrappedIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION; + wrappedIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1; + wrappedIndication.Header.Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1; + + wrappedIndication.SourceHandle = Switch->NdisFilterHandle; + wrappedIndication.PortNumber = NDIS_DEFAULT_PORT_NUMBER; + + wrappedIndication.StatusCode = StatusCode; + wrappedIndication.StatusBuffer = StatusBuffer; + wrappedIndication.StatusBufferSize = StatusBufferSize; + + NdisZeroMemory(&nicIndication, sizeof(nicIndication)); + + nicIndication.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + nicIndication.Header.Revision = NDIS_SWITCH_NIC_STATUS_INDICATION_REVISION_1; + nicIndication.Header.Size = NDIS_SIZEOF_SWITCH_NIC_STATUS_REVISION_1; + nicIndication.StatusIndication = &wrappedIndication; + + if (IsDestination) + { + nicIndication.DestinationPortId = PortId; + nicIndication.DestinationNicIndex = NicIndex; + } + else + { + nicIndication.SourcePortId = PortId; + nicIndication.SourceNicIndex = NicIndex; + } + + NdisZeroMemory(&statusIndication, sizeof(statusIndication)); + + statusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION; + statusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1; + statusIndication.Header.Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1; + + statusIndication.SourceHandle = Switch->NdisFilterHandle; + statusIndication.PortNumber = NDIS_DEFAULT_PORT_NUMBER; + + statusIndication.StatusCode = NDIS_STATUS_SWITCH_NIC_STATUS; + statusIndication.StatusBuffer = &nicIndication; + statusIndication.StatusBufferSize = sizeof(nicIndication); + + NdisFIndicateStatus(Switch->NdisFilterHandle, + &statusIndication); +} + diff --git a/network/ndis/extension/base/SxLibrary.h b/network/ndis/extension/base/SxLibrary.h new file mode 100644 index 00000000..2d9a30af --- /dev/null +++ b/network/ndis/extension/base/SxLibrary.h @@ -0,0 +1,464 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved. + +Module Name: + + SxLibrary.h + +Abstract: + + This file contains the common library function headers that can be + used by any extension using the SxBase library. + + +--*/ + + +/*++ + +SxLibSendNetBufferListsIngress + +Routine Description: + This function is called to forward NBLs on ingress. + The extension MUST call this function, or call + SxLibCompleteNetBufferListsIngress for every NBL in NetBufferLists, + recieved in SxExtStartNetBufferListsIngress. + + This function can also be called to inject NBLs. + If there are NBLs in NetBufferLists that are initiated by the + extension, NumInjectedNetBufferLists must be the number of new NBLs. + +Arguments: + + Switch - the Switch context + + NetBufferLists - the NBLs to send + + SendFlags - the SendFlags equivalent to NDIS flags for + NdisFSendNetBufferLists + + NumInjectedNetBufferLists - the number of NBLs in NetBufferLists initiated + by the extension + +Return Value: + VOID + +--*/ +VOID +SxLibSendNetBufferListsIngress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG SendFlags, + _In_ ULONG NumInjectedNetBufferLists + ); + + +/*++ + +SxLibSendNetBufferListsEgress + +Routine Description: + This function is called to forward NBLs on egress. + The extension MUST call this function, or call + SxLibCompleteNetBufferListsEgress for every NBL in NetBufferLists + recieved in SxExtStartNetBufferListsEgress. + +Arguments: + + Switch - the Switch context + + NetBufferLists - the NBLs to send + + NumberOfNetBufferLists - the number of NBLs in NetBufferLists + + ReceiveFlags - the ReceiveFlags equivalent to NDIS flags for + NdisFIndicateReceiveNetBufferLists + +Return Value: + VOID + +--*/ +VOID +SxLibSendNetBufferListsEgress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG NumberOfNetBufferLists, + _In_ ULONG ReceiveFlags + ); + + +/*++ + +SxLibCompleteNetBufferListsEgress + +Routine Description: + This function is called to complete NBLs on egress. + The extension MUST call this function for all NBLs recieved + in SxExtStartCompleteNetBufferListsEgress. + +Arguments: + + Switch - the Switch context + + NetBufferLists - the NBLs to send + + ReturnFlags - the ReceiveFlags equivalent to NDIS flags for + NdisFReturnNetBufferLists + +Return Value: + VOID + +--*/ +VOID +SxLibCompleteNetBufferListsEgress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG ReturnFlags + ); + +/*++ + +SxLibCompleteNetBufferListsIngress + +Routine Description: + This function is called to complete NBLs on ingress. + The extension MUST call this function, or + SxLibCompletedInjectedNetBufferLists for all NBLs recieved in + SxExtStartCompleteNetBufferListsEgress. + +Arguments: + + Switch - the Switch context + + NetBufferLists - the NBLs to send + + SendCompleteFlags - the ReceiveFlags equivalent to NDIS flags for + NdisFSendNetBufferListsComplete + +Return Value: + VOID + +--*/ +VOID +SxLibCompleteNetBufferListsIngress( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG SendCompleteFlags + ); + + +/*++ + +SxLibCompletedInjectedNetBufferLists + +Routine Description: + This function is called after completing NBLs injected + by the extension. + +Arguments: + + Switch - the Switch context + + NumInjectedNetBufferLists - the number of NBLs completed + +Return Value: + VOID + +--*/ +VOID +SxLibCompletedInjectedNetBufferLists( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ ULONG NumInjectedNetBufferLists + ); + + +/*++ + +SxLibIssueOidRequest + +Routine Description: + + Utility routine that forms and sends an NDIS_OID_REQUEST to the + miniport, waits for it to complete, and returns status + to the caller. + + NOTE: this assumes that the calling routine ensures validity + of the filter handle until this returns. + + This function can only be called at PASSIVE_LEVEL. + +Arguments: + + Switch - pointer to our switch object. + + RequestType - NdisRequest[Set|Query|method]Information. + + Oid - the object being set/queried. + + InformationBuffer - data for the request. + + InformationBufferLength - length of the above. + + OutputBufferLength - valid only for method request. + + MethodId - valid only for method request. + + Timeout - The timeout in seconds for the OID. + + BytesNeeded - place to return bytes read/written. + +Return Value: + + NDIS_STATUS_*** + +--*/ +NDIS_STATUS +SxLibIssueOidRequest( + _In_ PSX_SWITCH_OBJECT SxSwitch, + _In_ NDIS_REQUEST_TYPE RequestType, + _In_ NDIS_OID Oid, + _In_opt_ PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _In_ ULONG OutputBufferLength, + _In_ ULONG MethodId, + _In_ UINT Timeout, + _Out_ PULONG BytesNeeded + ); + + +/*++ + +SxLibGetSwitchParametersUnsafe + +Routine Description: + This function is called to get the current state of the switch. + +Arguments: + + Switch - the Switch context + + SwitchParameters - the returned switch parameters + +Return Value: + NDIS_STATUS_SUCCESS - if SwitchParameters was successfully returned + + NDIS_STATUS_*** - otherwise + +--*/ +NDIS_STATUS +SxLibGetSwitchParametersUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _Out_ PNDIS_SWITCH_PARAMETERS SwitchParameters + ); + + +/*++ + +SxLibGetPortArrayUnsafe + +Routine Description: + This function is called to get the current array + of ports. + + NOTE: It is necessary to synchonize this with SxExtPortCreate + and SxExtPortTeardown. + +Arguments: + + Switch - the Switch context + + PortArray - the returned port array + +Return Value: + NDIS_STATUS_SUCCESS - if PortArray was successfully allocated + and returned + + NDIS_STATUS_*** - otherwise + +--*/ +NDIS_STATUS +SxLibGetPortArrayUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _Out_ PNDIS_SWITCH_PORT_ARRAY *PortArray + ); + + +/*++ + +SxLibGetNicArrayUnsafe + +Routine Description: + This function is called to get the current array + of NICs. + + NOTE: It is necessary to synchonize this with SxExtNicConnect + and SxExtNicDisconnect. + +Arguments: + + Switch - the Switch context + + NicArray - the returned NIC array + +Return Value: + NDIS_STATUS_SUCCESS - if NicArray was successfully allocated + and returned + + NDIS_STATUS_*** - otherwise + +--*/ +NDIS_STATUS +SxLibGetNicArrayUnsafe( + _In_ PSX_SWITCH_OBJECT SxSwitch, + _Out_ PNDIS_SWITCH_NIC_ARRAY *NicArray + ); + +/*++ + +SxLibGetSwitchPropertyUnsafe + +Routine Description: + This function is called to get the current array of the switch + property queried. + + NOTE: It is necessary to synchonize this with SxExtAddSwitchProperty + and SxExtDeleteSwitchProperty. + +Arguments: + + Switch - the Switch context + + PropertyType - the PropertyType to query for + + PropertyId - the GUID of the property (from mof file) + + PropertyVersion - the version of the property + + SwitchPropertyEnumParameters - the returned property enum + +Return Value: + NDIS_STATUS_SUCCESS - if SwitchPropertyEnumParameters was + successfully allocated and returned + + NDIS_STATUS_*** - otherwise + +--*/ +NDIS_STATUS +SxLibGetSwitchPropertyUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_SWITCH_PROPERTY_TYPE PropertyType, + _In_opt_ PNDIS_SWITCH_OBJECT_ID PropertyId, + _Outptr_ PNDIS_SWITCH_PROPERTY_ENUM_PARAMETERS *SwitchPropertyEnumParameters + ); + +/*++ + +SxLibGetPortPropertyUnsafe + +Routine Description: + This function is called to get the current array of the switch + property queried. + + NOTE: It is necessary to synchonize this with SxExtAddPortProperty + and SxExtDeletePortProperty. + +Arguments: + + Switch - the Switch context + + PortId - the port to query from + + PropertyType - the PropertyType to query for + + PropertyId - the GUID of the property (from mof file) + + PortPropertyEnumParameters - the returned property enum + +Return Value: + NDIS_STATUS_SUCCESS - if PortPropertyEnumParameters was + successfully allocated and returned + + NDIS_STATUS_*** - otherwise + +--*/ +NDIS_STATUS +SxLibGetPortPropertyUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_SWITCH_PORT_ID PortId, + _In_ NDIS_SWITCH_PORT_PROPERTY_TYPE PropertyType, + _In_opt_ PNDIS_SWITCH_OBJECT_ID PropertyId, + _Outptr_ PNDIS_SWITCH_PORT_PROPERTY_ENUM_PARAMETERS *PortPropertyEnumParameters + ); + +/*++ + +SxLibRevokeVfUnsafe + +Routine Description: + This function is called revoke the VF assignment for the + given VM. + + NOTE: This must be synchonized with SxExtNicConnect and + SxExtNicDisconnect for the PortId given, and ReferenceSwitchNic + must have been successfully called. + +Arguments: + + Switch - the Switch context + + PortId - the port the VM is connected to + +Return Value: + VOID + +--*/ +VOID +SxLibRevokeVfUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_SWITCH_PORT_ID PortId + ); + +/*++ + +SxLibIssueNicStatusIndicationUnsafe + +Routine Description: + This function is called issue a NIC status indication. + + NOTE: This must be synchonized with SxExtNicConnect and + SxExtNicDisconnect for the PortId given, and ReferenceSwitchNic + must have been successfully called. + +Arguments: + + Switch - the Switch context + + StatusCode - the status code to indicate + + PortId - the port to indicate to/from + + NicIndex - the nic index to indicate to/from + + IsDestination - TRUE if PortId/NicIndex is destination info + FALSE if PortId/NicIndex is source info + + StatusBuffer - the StatusBuffer for the indication + + StatusBufferSize - the size of StatusBuffer + +Return Value: + VOID + +--*/ +VOID +SxLibIssueNicStatusIndicationUnsafe( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ NDIS_STATUS StatusCode, + _In_ NDIS_SWITCH_PORT_ID PortId, + _In_ NDIS_SWITCH_NIC_INDEX NicIndex, + _In_ BOOLEAN IsDestination, + _In_opt_ PVOID StatusBuffer, + _In_ ULONG StatusBufferSize + ); + diff --git a/network/ndis/extension/base/precomp.h b/network/ndis/extension/base/precomp.h new file mode 100644 index 00000000..b0bd41c8 --- /dev/null +++ b/network/ndis/extension/base/precomp.h @@ -0,0 +1,11 @@ +// +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// + +#include +#include +#include +#include +#include "SxBase.h" +#include "SxApi.h" +#include "SxLibrary.h" diff --git a/network/ndis/extension/base/precompsrc.c b/network/ndis/extension/base/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/network/ndis/extension/base/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/network/ndis/extension/base/sxbase.vcxproj b/network/ndis/extension/base/sxbase.vcxproj new file mode 100644 index 00000000..4c81ea79 --- /dev/null +++ b/network/ndis/extension/base/sxbase.vcxproj @@ -0,0 +1,130 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {63FF1428-95CA-4751-A701-244D54A31BD9} + $(MSBuildProjectName) + Debug + x64 + {8C4C832A-4638-47E9-B2AC-6FA6FC1D958B} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + StaticLibrary + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + StaticLibrary + + + + $(IntDir) + + + + + + + + + + sxbase + + + sxbase + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);NDIS630=1;NDIS_WDM=1 + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);NDIS630=1;NDIS_WDM=1 + true + Level4 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);NDIS630=1;NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);NDIS630=1;NDIS_WDM=1 + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);NDIS630=1;NDIS_WDM=1 + true + Level4 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);NDIS630=1;NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/extension/base/sxbase.vcxproj.Filters b/network/ndis/extension/base/sxbase.vcxproj.Filters new file mode 100644 index 00000000..1291527a --- /dev/null +++ b/network/ndis/extension/base/sxbase.vcxproj.Filters @@ -0,0 +1,32 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {74637802-97A6-4EEE-A780-7AFC24D63060} + + + h;hpp;hxx;hm;inl;inc;xsd + {7417E375-1992-4C4B-A4AB-C0C8E00F9AAA} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {73A4206C-20D4-4AB0-B133-03D222981702} + + + inf;inv;inx;mof;mc; + {CB4FE68B-83B4-4336-A241-94F6955D198A} + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/network/ndis/extension/extensions.sln b/network/ndis/extension/extensions.sln new file mode 100644 index 00000000..cc6ab362 --- /dev/null +++ b/network/ndis/extension/extensions.sln @@ -0,0 +1,55 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Base", "Base", "{1C3E1DE9-A703-46CB-B4E4-8CD906C9E6FE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Forward", "Forward", "{433D0832-2B1B-46AB-94E8-0A7979ACA4CA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{BC0A3E55-CAE0-4CCE-BA15-0784AEF76215}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Passthrough", "Passthrough", "{7C717732-18C9-4892-A6FF-B4DD1D72C12B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sxbase", "base\sxbase.vcxproj", "{63FF1428-95CA-4751-A701-244D54A31BD9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msforwardext", "samples\forward\msforwardext.vcxproj", "{404E1399-EA48-4CEE-A59E-EA0D4C01928A}" + ProjectSection(ProjectDependencies) = postProject + {63FF1428-95CA-4751-A701-244D54A31BD9} = {63FF1428-95CA-4751-A701-244D54A31BD9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mspassthroughext", "samples\passthrough\mspassthroughext.vcxproj", "{2339B4A8-60AF-4172-AC68-3159E6D001EC}" + ProjectSection(ProjectDependencies) = postProject + {63FF1428-95CA-4751-A701-244D54A31BD9} = {63FF1428-95CA-4751-A701-244D54A31BD9} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {63FF1428-95CA-4751-A701-244D54A31BD9}.Debug|x64.ActiveCfg = Debug|x64 + {63FF1428-95CA-4751-A701-244D54A31BD9}.Debug|x64.Build.0 = Debug|x64 + {63FF1428-95CA-4751-A701-244D54A31BD9}.Release|x64.ActiveCfg = Release|x64 + {63FF1428-95CA-4751-A701-244D54A31BD9}.Release|x64.Build.0 = Release|x64 + {404E1399-EA48-4CEE-A59E-EA0D4C01928A}.Debug|x64.ActiveCfg = Debug|x64 + {404E1399-EA48-4CEE-A59E-EA0D4C01928A}.Debug|x64.Build.0 = Debug|x64 + {404E1399-EA48-4CEE-A59E-EA0D4C01928A}.Release|x64.ActiveCfg = Release|x64 + {404E1399-EA48-4CEE-A59E-EA0D4C01928A}.Release|x64.Build.0 = Release|x64 + {2339B4A8-60AF-4172-AC68-3159E6D001EC}.Debug|x64.ActiveCfg = Debug|x64 + {2339B4A8-60AF-4172-AC68-3159E6D001EC}.Debug|x64.Build.0 = Debug|x64 + {2339B4A8-60AF-4172-AC68-3159E6D001EC}.Release|x64.ActiveCfg = Release|x64 + {2339B4A8-60AF-4172-AC68-3159E6D001EC}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {63FF1428-95CA-4751-A701-244D54A31BD9} = {1C3E1DE9-A703-46CB-B4E4-8CD906C9E6FE} + {404E1399-EA48-4CEE-A59E-EA0D4C01928A} = {433D0832-2B1B-46AB-94E8-0A7979ACA4CA} + {2339B4A8-60AF-4172-AC68-3159E6D001EC} = {7C717732-18C9-4892-A6FF-B4DD1D72C12B} + {433D0832-2B1B-46AB-94E8-0A7979ACA4CA} = {BC0A3E55-CAE0-4CCE-BA15-0784AEF76215} + {7C717732-18C9-4892-A6FF-B4DD1D72C12B} = {BC0A3E55-CAE0-4CCE-BA15-0784AEF76215} + EndGlobalSection +EndGlobal diff --git a/network/ndis/extension/samples/forward/MSForwardExtPolicy.mof b/network/ndis/extension/samples/forward/MSForwardExtPolicy.mof new file mode 100644 index 00000000..88105b2f --- /dev/null +++ b/network/ndis/extension/samples/forward/MSForwardExtPolicy.mof @@ -0,0 +1,32 @@ +// +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// +// Custom property for MsForwardExt +// MAC Address Allow Policy +// +#pragma namespace("\\\\.\\root\\virtualization\\v2") + +[ Dynamic, + UUID("EB29F0F2-F5DC-45C6-81BB-3CD9F219BBBB"), + ExtensionId("37d9eae6-5bae-48c5-bff1-63a7cdd7e4f4"), + Provider("VmmsWmiInstanceAndMethodProvider"), + Locale(0x409), + InterfaceVersion("1"), + InterfaceRevision("0"), +DisplayName("MSForwardExt MAC Address Policy") : Amended, +Description("Source MAC Addresses to allow sends on MSForwardExt.") : Amended] +class MSForwardExt_MacAddressRule : Msvm_EthernetSwitchFeatureSettingData { + + // + // MAC Address set on switch to allow sends from. + // + [ Read, + Write, + WmiDataId(1), + Max(6), + InterfaceVersion(1), + InterfaceRevision(0), + Description ( + "Mac Address") : Amended] + uint8 MacAddress[] = {}; +}; diff --git a/network/ndis/extension/samples/forward/MSForwardExtPolicyStatus.mof b/network/ndis/extension/samples/forward/MSForwardExtPolicyStatus.mof new file mode 100644 index 00000000..c9d509e8 --- /dev/null +++ b/network/ndis/extension/samples/forward/MSForwardExtPolicyStatus.mof @@ -0,0 +1,31 @@ +// +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// +// Custom property status for MSForwardExt +// MAC Address Allow Policy Status +// +#pragma namespace("\\\\.\\root\\virtualization\\v2") + +[ Dynamic, + UUID("A3E2AFF5-E6FA-4E52-AB74-13250BF7E8CF"), + ExtensionId("37d9eae6-5bae-48c5-bff1-63a7cdd7e4f4"), + Provider("VmmsWmiInstanceAndMethodProvider"), + Applicability("2"), + Locale(0x409), + InterfaceVersion("1"), + InterfaceRevision("0"), +DisplayName("MSForwardExt MAC Address Policy Status") : Amended, +Description("The current array of PortIds with policy set to allow sends") : Amended] +class MSForwardExt_MacAddressRuleStatus : Msvm_EthernetSwitchData { + + // + // Array of PortId's currently allowing sends. + // + [ Read, + WmiDataId(1), + InterfaceVersion(1), + InterfaceRevision(0), + Description ( + "Currently Allowed Port Ids") : Amended] + uint32 AllowedPortIds[] = {}; +}; diff --git a/network/ndis/extension/samples/forward/MsForwardExt.c b/network/ndis/extension/samples/forward/MsForwardExt.c new file mode 100644 index 00000000..147a3694 --- /dev/null +++ b/network/ndis/extension/samples/forward/MsForwardExt.c @@ -0,0 +1,2584 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved. + +Module Name: + + MsForwardExt.c + +Abstract: + + This file contains the implementation of a forwarding extension + (MSForardExt) using the SxBase library. + The forwarding extension does basic MAC forwarding, blocking and allowing + source MAC addresses based on custom set policy. + + +--*/ + +#include "precomp.h" +#include "MsForwardExt.h" + +UCHAR SxExtMajorNdisVersion = NDIS_FILTER_MAJOR_VERSION; +UCHAR SxExtMinorNdisVersion = NDIS_FILTER_MINOR_VERSION; +PWCHAR SxExtFriendlyName = L"Microsoft Sample Forwarding Extension"; +PWCHAR SxExtUniqueName = L"{37d9eae6-5bae-48c5-bff1-63a7cdd7e4f4}"; +PWCHAR SxExtServiceName = L"MSForwardExt"; +ULONG SxExtAllocationTag = 'wSxS'; +ULONG SxExtOidRequestId = 'wSxS'; + +// {37d9eae6-5bae-48c5-bff1-63a7cdd7e4f4} +const GUID MsForwardExtGuid = { + 0x37d9eae6, + 0x5bae, + 0x48c5, + {0xbf, 0xf1, 0x63, 0xa7, 0xcd, 0xd7, 0xe4, 0xf4} + }; + +// {EB29F0F2-F5DC-45C6-81BB-3CD9F219BBBB} +const NDIS_SWITCH_OBJECT_ID MacAddressPolicyGuid = { + 0xEB29F0F2, + 0xF5DC, + 0x45C6, + {0x81, 0xBB, 0x3C, 0xD9, 0xF2, 0x19, 0xBB, 0xBB} + }; + +// {A3E2AFF5-E6FA-4E52-AB74-13250BF7E8CF} +const NDIS_SWITCH_OBJECT_ID MacAddressPolicyStatusGuid = { + 0xA3E2AFF5, + 0xE6FA, + 0x4E52, + {0xAB, 0x74, 0x13, 0x25, 0x0B, 0xF7, 0xE8, 0xCF} + }; + + +NDIS_STATUS +SxExtInitialize() +/*++ + +Routine Description: + No global information needed. + +--*/ +{ + return NDIS_STATUS_SUCCESS; +} + + +VOID +SxExtUninitialize() +/*++ + +Routine Description: + No global information needed. + +--*/ +{ + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtCreateSwitch( + PSX_SWITCH_OBJECT Switch, + PNDIS_HANDLE *ExtensionContext + ) +/*++ + +Routine Description: + This function allocated the switch context, and initializes + its necessary members. + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + PMSFORWARD_CONTEXT switchContext; + + switchContext = ExAllocatePoolWithTag(NonPagedPoolNx, + sizeof(MSFORWARD_CONTEXT), + SxExtAllocationTag); + + if (switchContext == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + NdisZeroMemory(switchContext, sizeof(MSFORWARD_CONTEXT)); + InitializeListHead(&switchContext->NicList); + InitializeListHead(&switchContext->PropertyList); + + switchContext->DispatchLock = NdisAllocateRWLock(Switch->NdisFilterHandle); + if (switchContext->DispatchLock == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + switchContext->IsInitialRestart = TRUE; + + *ExtensionContext = (NDIS_HANDLE)switchContext; + +Cleanup: + if (status != NDIS_STATUS_SUCCESS) + { + if (switchContext != NULL) + { + ExFreePoolWithTag(switchContext, SxExtAllocationTag); + } + } + + return status; +} + + +_Use_decl_annotations_ +VOID +SxExtDeleteSwitch( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext + ) +/*++ + +Routine Description: + This function deletes the switch by freeing all memory + previously allocated. + +--*/ +{ + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + + UNREFERENCED_PARAMETER(Switch); + + MsForwardClearNicListUnsafe(switchContext); + MsForwardClearPropertyListUnsafe(switchContext); + NdisFreeRWLock(switchContext->DispatchLock); + ExFreePoolWithTag(ExtensionContext, SxExtAllocationTag); +} + + +_Use_decl_annotations_ +VOID +SxExtActivateSwitch( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext + ) +{ + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + MsForwardInitSwitch(Switch, switchContext); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtRestartSwitch( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext + ) +/*++ + +Routine Description: + This function initializes the switch if it is the first restart. + First it queries all of the MAC addresses set as custom + switch policy to allow sends from, and adds tme to the property + list. + Then it queries the NIC list and verifies it can support + all of the NICs currently connected to the switch, and + adds the NICs to the NIC list. + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + NDIS_SWITCH_PARAMETERS switchParameters; + + if (switchContext->IsInitialRestart) + { + status = SxLibGetSwitchParametersUnsafe(Switch, &switchParameters); + + if (status != NDIS_STATUS_SUCCESS) + { + goto Cleanup; + } + + if (switchParameters.IsActive) + { + status = MsForwardInitSwitch(Switch, switchContext); + if (status != NDIS_STATUS_SUCCESS) + { + goto Cleanup; + } + } + + switchContext->IsInitialRestart = FALSE; + } + +Cleanup: + return status; +} + + +_Use_decl_annotations_ +VOID +SxExtPauseSwitch( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext + ) +/*++ + +Routine Description: + No pause funtionality required. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtCreatePort( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PARAMETERS Port + ) +/*++ + +Routine Description: + This extension does not track ports, only NICs. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Port); + + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtUpdatePort( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PARAMETERS Port + ) +/*++ + +Routine Description: + This extension does not track ports, only NICs. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Port); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtCreateNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_PARAMETERS Nic + ) +/*++ + +Routine Description: + Allocate NIC, add to NIC list, and correlate with policy. + +--*/ +{ + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + LOCK_STATE_EX lockState; + + UNREFERENCED_PARAMETER(Switch); + + // + // Wait for lists to be initialized. + // + while (switchContext->IsInitialRestart) + { + NdisMSleep(100); + } + + NdisAcquireRWLockWrite(switchContext->DispatchLock, &lockState, 0); + + status = MsForwardAddNicUnsafe(switchContext, + Nic->PermanentMacAddress, + Nic->PortId, + Nic->NicIndex, + Nic->NicType, + FALSE); + + + NdisReleaseRWLock(switchContext->DispatchLock, &lockState); + + return status; +} + + +_Use_decl_annotations_ +VOID +SxExtConnectNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_PARAMETERS Nic + ) +/*++ + +Routine Description: + Mark already created NIC as connected. + +--*/ +{ + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + PMSFORWARD_NIC_LIST_ENTRY nicEntry = NULL; + LOCK_STATE_EX lockState; + + UNREFERENCED_PARAMETER(Switch); + + // + // Wait for lists to be initialized. + // + while (switchContext->IsInitialRestart) + { + NdisMSleep(100); + } + + NdisAcquireRWLockWrite(switchContext->DispatchLock, &lockState, 0); + if (Nic->NicType == NdisSwitchNicTypeExternal && + Nic->NicIndex != 0 && + switchContext->ExternalPortId == 0) + { + switchContext->ExternalPortId = Nic->PortId; + switchContext->ExternalNicIndex = Nic->NicIndex; + switchContext->ExternalNicConnected = TRUE; + ++(switchContext->NumDestinations); + } + else if (Nic->NicType != NdisSwitchNicTypeExternal) + { + nicEntry = MsForwardFindNicByPortIdUnsafe(switchContext, + Nic->PortId, + Nic->NicIndex); + + if(nicEntry != NULL) + { + nicEntry->Connected = TRUE; + ++(switchContext->NumDestinations); + } + else + { + ASSERT(FALSE); + } + } + NdisReleaseRWLock(switchContext->DispatchLock, &lockState); +} + + +_Use_decl_annotations_ +VOID +SxExtUpdateNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_PARAMETERS Nic + ) +/*++ + +Routine Description: + This extension doesn't use any of the fields that can be updated. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Nic); + + return; +} + + +_Use_decl_annotations_ +VOID +SxExtDisconnectNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_PARAMETERS Nic + ) +/*++ + +Routine Description: + Mark already created NIC as disconnected. + +--*/ +{ + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + PMSFORWARD_NIC_LIST_ENTRY nicEntry = NULL; + LOCK_STATE_EX lockState; + + UNREFERENCED_PARAMETER(Switch); + + // + // Wait for lists to be initialized. + // + while (switchContext->IsInitialRestart) + { + NdisMSleep(100); + } + + NdisAcquireRWLockWrite(switchContext->DispatchLock, &lockState, 0); + if (Nic->NicType == NdisSwitchNicTypeExternal) + { + if (Nic->NicIndex == switchContext->ExternalNicIndex) + { + --(switchContext->NumDestinations); + switchContext->ExternalNicConnected = FALSE; + } + } + else + { + nicEntry = MsForwardFindNicByPortIdUnsafe(switchContext, + Nic->PortId, + Nic->NicIndex); + + if(nicEntry != NULL) + { + nicEntry->Connected = FALSE; + --(switchContext->NumDestinations); + } + else + { + ASSERT(FALSE); + } + } + + NdisReleaseRWLock(switchContext->DispatchLock, &lockState); +} + + +_Use_decl_annotations_ +VOID +SxExtDeleteNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_PARAMETERS Nic + ) +/*++ + +Routine Description: + Delete created NIC, free related memory and remove + from NIC list. + +--*/ +{ + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + LOCK_STATE_EX lockState; + + UNREFERENCED_PARAMETER(Switch); + + // + // Wait for lists to be initialized. + // + while (switchContext->IsInitialRestart) + { + NdisMSleep(100); + } + + NdisAcquireRWLockWrite(switchContext->DispatchLock, &lockState, 0); + if (Nic->NicType == NdisSwitchNicTypeExternal && + Nic->NicIndex == switchContext->ExternalNicIndex) + { + ASSERT(switchContext->ExternalNicConnected == FALSE); + switchContext->ExternalPortId = 0; + switchContext->ExternalNicIndex = 0; + } + else if (Nic->NicType != NdisSwitchNicTypeExternal) + { + MsForwardDeleteNicUnsafe(switchContext, + Nic->PortId, + Nic->NicIndex); + } + + NdisReleaseRWLock(switchContext->DispatchLock, &lockState); + return; +} + + +_Use_decl_annotations_ +VOID +SxExtTeardownPort( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PARAMETERS Port + ) +/*++ + +Routine Description: + This extension does not track port state. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Port); +} + + +_Use_decl_annotations_ +VOID +SxExtDeletePort( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PARAMETERS Port + ) +/*++ + +Routine Description: + This extension does not track port state. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Port); +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtSaveNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_SAVE_STATE SaveState, + PULONG BytesWritten, + PULONG BytesNeeded + ) +/*++ + +Routine Description: + This extension does not save any data. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SaveState); + + *BytesWritten = 0; + *BytesNeeded = 0; + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtSaveNicComplete( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_SAVE_STATE SaveState + ) +/*++ + +Routine Description: + This extension does not save any data. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SaveState); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtNicRestore( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_SAVE_STATE SaveState, + PULONG BytesRestored + ) +/*++ + +Routine Description: + This extension does not save any data. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SaveState); + + *BytesRestored = 0; + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtNicRestoreComplete( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_SAVE_STATE SaveState + ) +/*++ + +Routine Description: + This extension does not save any data. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SaveState); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtAddSwitchProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PROPERTY_PARAMETERS SwitchProperty + ) +/*++ + +Routine Description: + This extension enforces one custom switch policy. + The function verifies the switch property is our MAC policy + and then adds it to the property list. + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_NOT_SUPPORTED; + PNDIS_SWITCH_PROPERTY_CUSTOM customPolicy; + PMSFORWARD_MAC_ADDRESS_POLICY macPolicy; + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + LOCK_STATE_EX lockState; + + UNREFERENCED_PARAMETER(Switch); + + if (SwitchProperty->PropertyType != NdisSwitchPropertyTypeCustom) + { + goto Cleanup; + } + + // + // Check if MAC Address Policy for this extension. + // + if (!RtlEqualMemory(&SwitchProperty->PropertyId, + &MacAddressPolicyGuid, + sizeof(NDIS_SWITCH_OBJECT_ID))) + { + goto Cleanup; + } + + if (SwitchProperty->PropertyVersion != MAC_ADDRESS_POLICY_VERSION) + { + goto Cleanup; + } + + if (SwitchProperty->SerializationVersion != MAC_ADDRESS_POLICY_SERIALIZATION_VERSION) + { + goto Cleanup; + } + + // + // Wait for lists to be initialized. + // + while (switchContext->IsInitialRestart) + { + NdisMSleep(100); + } + + customPolicy = NDIS_SWITCH_PROPERTY_PARAMETERS_GET_PROPERTY(SwitchProperty); + macPolicy = NDIS_SWITCH_PROPERTY_CUSTOM_GET_BUFFER(customPolicy); + + if (macPolicy->MacAddressLength != 6) + { + status = NDIS_STATUS_DATA_NOT_ACCEPTED; + } + else + { + NdisAcquireRWLockWrite(switchContext->DispatchLock, &lockState, 0); + + status = MsForwardAddMacPolicyUnsafe(switchContext, + macPolicy, + &SwitchProperty->PropertyInstanceId); + + NdisReleaseRWLock(switchContext->DispatchLock, &lockState); + } + +Cleanup: + return status; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtUpdateSwitchProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PROPERTY_PARAMETERS SwitchProperty + ) +/*++ + +Routine Description: + This extension enforces one custom switch policy, but + does not allow updates for that policy. + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_NOT_SUPPORTED; + + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + + if (SwitchProperty->PropertyType != NdisSwitchPropertyTypeCustom) + { + goto Cleanup; + } + + // + // Check if MAC Address Policy for this extension. + // + if (!RtlEqualMemory(&SwitchProperty->PropertyId, + &MacAddressPolicyGuid, + sizeof(NDIS_SWITCH_OBJECT_ID))) + { + goto Cleanup; + } + + if (SwitchProperty->PropertyVersion != MAC_ADDRESS_POLICY_VERSION) + { + goto Cleanup; + } + + if (SwitchProperty->SerializationVersion != MAC_ADDRESS_POLICY_SERIALIZATION_VERSION) + { + goto Cleanup; + } + + status = NDIS_STATUS_DATA_NOT_ACCEPTED; + +Cleanup: + return status; +} + + +_Use_decl_annotations_ +BOOLEAN +SxExtDeleteSwitchProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PROPERTY_DELETE_PARAMETERS SwitchProperty + ) +/*++ + +Routine Description: + This extension enforces one custom switch policy. + The function verifies the switch property is our MAC policy + and then deletes it from the property list. + +--*/ +{ + BOOLEAN delete = FALSE; + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + LOCK_STATE_EX lockState; + + UNREFERENCED_PARAMETER(Switch); + + if (SwitchProperty->PropertyType != NdisSwitchPropertyTypeCustom) + { + goto Cleanup; + } + + // + // Check if MAC Address Policy for this extension. + // + if (!RtlEqualMemory(&SwitchProperty->PropertyId, + &MacAddressPolicyGuid, + sizeof(NDIS_SWITCH_OBJECT_ID))) + { + goto Cleanup; + } + + // + // Wait for lists to be initialized. + // + while (switchContext->IsInitialRestart) + { + NdisMSleep(100); + } + + + delete = TRUE; + + NdisAcquireRWLockWrite(switchContext->DispatchLock, &lockState, 0); + + MsForwardDeleteMacPolicyUnsafe(switchContext, + &SwitchProperty->PropertyInstanceId); + + NdisReleaseRWLock(switchContext->DispatchLock, &lockState); + +Cleanup: + return delete; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtAddPortProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PROPERTY_PARAMETERS PortProperty + ) +/*++ + +Routine Description: + This extension does not enforce VLAN. Because of this + the extension fails the adding of these policies. + + !! REAL FORWARDING EXTENSIONS SHOULD SUPPORT THESE PROPERTIES !! + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_NOT_SUPPORTED; + + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + + switch(PortProperty->PropertyType) + { + case NdisSwitchPortPropertyTypeCustom: + // + // No Custom Port Properties. + // + break; + + case NdisSwitchPortPropertyTypeSecurity: + // + // This extension does need to look at security policy, pass it down. + // An extension must always pass through Hyper-V security policy. + // + break; + + case NdisSwitchPortPropertyTypeVlan: + // + // Forwarding extensions must either enforce VLAN, or fail + // setting VLAN policy. + // This extension does not enforce VLAN. + // + status = NDIS_STATUS_DATA_NOT_ACCEPTED; + break; + + case NdisSwitchPortPropertyTypeProfile: + // + // No Processing of Port Profile. + // + break; + } + + return status; +} + +_Use_decl_annotations_ +NDIS_STATUS +SxExtUpdatePortProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PROPERTY_PARAMETERS PortProperty + ) +/*++ + +Routine Description: + This extension does not enforce VLAN. Because of this + the extension fails the updating of these policies. + + !! REAL FORWARDING EXTENSIONS SHOULD SUPPORT THESE PROPERTIES !! + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_NOT_SUPPORTED; + + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + + switch(PortProperty->PropertyType) + { + case NdisSwitchPortPropertyTypeCustom: + // + // No Custom Port Properties. + // + break; + + case NdisSwitchPortPropertyTypeSecurity: + // + // This extension does need to look at security policy, pass it down. + // An extension must always pass through Hyper-V security policy. + // + break; + + case NdisSwitchPortPropertyTypeVlan: + // + // Forwarding extensions must either enforce VLAN, or fail + // setting VLAN policy. + // This extension does not enforce VLAN. + // + status = NDIS_STATUS_DATA_NOT_ACCEPTED; + break; + + case NdisSwitchPortPropertyTypeProfile: + // + // No Processing of Port Profile. + // + break; + } + + return status; +} + +_Use_decl_annotations_ +BOOLEAN +SxExtDeletePortProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PROPERTY_DELETE_PARAMETERS PortProperty + ) +/*++ + +Routine Description: + This extension does not enforce VLAN. + These policies are policies that should be supported by this + extension, so it returns TRUE to complete the deletion. + + !! REAL FORWARDING EXTENSIONS SHOULD SUPPORT THESE PROPERTIES !! + +--*/ +{ + BOOLEAN delete = FALSE; + + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + + switch(PortProperty->PropertyType) + { + case NdisSwitchPortPropertyTypeCustom: + // + // No Custom Port Properties. + // + break; + + case NdisSwitchPortPropertyTypeSecurity: + // + // This extension does need to look at security policy, pass it down. + // An extension must always pass through Hyper-V security policy. + // + break; + + case NdisSwitchPortPropertyTypeVlan: + // + // Forwarding extensions must either enforce VLAN, or fail + // setting VLAN policy. + // This extension does not enforce VLAN. + // + delete = TRUE; + break; + + case NdisSwitchPortPropertyTypeProfile: + // + // No Processing of Port Profile. + // + break; + } + + return delete; +} + + +_Use_decl_annotations_ +BOOLEAN +SxExtQuerySwitchFeatureStatus( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_FEATURE_STATUS_PARAMETERS SwitchFeatureStatus, + PULONG BytesNeeded + ) +/*++ + +Routine Description: + This extension reports the status of its custom MAC policy + by returning the list of PortId's currently allowing sends. + +--*/ +{ + BOOLEAN consumed = FALSE; + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + LOCK_STATE_EX lockState; + BOOLEAN lockHeld = FALSE; + ULONG sizeNeeded = 0; + ULONG numAllowedSourcePorts = 0; + PLIST_ENTRY nicList = &switchContext->NicList; + PLIST_ENTRY curEntry = NULL; + PMSFORWARD_NIC_LIST_ENTRY nic = NULL; + PNDIS_SWITCH_FEATURE_STATUS_CUSTOM customStatusBuffer; + PMSFORWARD_MAC_ADDRESS_POLICY_STATUS macAddressPolicyBuffer; + PNDIS_SWITCH_PORT_ID portIdArray; + ULONG arrayIndex = 0; + ULONG customBufferLength = 0; + + UNREFERENCED_PARAMETER(Switch); + + if (SwitchFeatureStatus->FeatureStatusType != NdisSwitchFeatureStatusTypeCustom) + { + goto Cleanup; + } + + if (!RtlEqualMemory(&SwitchFeatureStatus->FeatureStatusId, + &MacAddressPolicyStatusGuid, + sizeof(NDIS_SWITCH_OBJECT_ID))) + { + goto Cleanup; + } + + if (SwitchFeatureStatus->SerializationVersion != MAC_ADDRESS_POLICY_STATUS_SERIALIZATION_VERSION) + { + goto Cleanup; + } + + // + // Wait for lists to be initialized. + // + while (switchContext->IsInitialRestart) + { + NdisMSleep(100); + } + + NdisAcquireRWLockRead(switchContext->DispatchLock, &lockState, 0); + lockHeld = TRUE; + + if (switchContext->ExternalPortId != NDIS_SWITCH_DEFAULT_PORT_ID) + { + ++numAllowedSourcePorts; + } + + if (!IsListEmpty(nicList)) + { + curEntry = nicList->Flink; + + do { + nic = CONTAINING_RECORD(curEntry, + MSFORWARD_NIC_LIST_ENTRY, + ListEntry); + + if (nic->AllowSends) + { + ++numAllowedSourcePorts; + } + + curEntry = curEntry->Flink; + + } while(curEntry != nicList); + } + + customBufferLength = sizeof(MSFORWARD_MAC_ADDRESS_POLICY_STATUS) + + (sizeof(NDIS_SWITCH_PORT_ID) * numAllowedSourcePorts); + sizeNeeded = NDIS_SIZEOF_NDIS_SWITCH_FEATURE_STATUS_PARAMETERS_REVISION_1 + + NDIS_SIZEOF_NDIS_SWITCH_FEATURE_STATUS_CUSTOM_REVISION_1 + + customBufferLength; + + consumed = TRUE; + + if (SwitchFeatureStatus->FeatureStatusBufferLength < sizeof(NDIS_SWITCH_FEATURE_STATUS_CUSTOM)) + { + *BytesNeeded = sizeNeeded; + goto Cleanup; + } + + customStatusBuffer = (PNDIS_SWITCH_FEATURE_STATUS_CUSTOM) + (((PUINT8)SwitchFeatureStatus) + + SwitchFeatureStatus->FeatureStatusBufferOffset); + + if (customStatusBuffer->Header.Type != NDIS_OBJECT_TYPE_DEFAULT || + customStatusBuffer->Header.Revision != NDIS_SWITCH_FEATURE_STATUS_CUSTOM_REVISION_1 || + customStatusBuffer->Header.Size != NDIS_SIZEOF_NDIS_SWITCH_FEATURE_STATUS_CUSTOM_REVISION_1) + { + consumed = FALSE; + goto Cleanup; + } + + + + if (customStatusBuffer->FeatureStatusCustomBufferLength < customBufferLength) + { + *BytesNeeded = sizeNeeded; + goto Cleanup; + } + + macAddressPolicyBuffer = (PMSFORWARD_MAC_ADDRESS_POLICY_STATUS) + (((PUINT8)customStatusBuffer) + + customStatusBuffer->FeatureStatusCustomBufferOffset); + + macAddressPolicyBuffer->PortArrayLength = numAllowedSourcePorts; + macAddressPolicyBuffer->PortArrayOffset = sizeof(MSFORWARD_MAC_ADDRESS_POLICY_STATUS); + + portIdArray = (PNDIS_SWITCH_PORT_ID) + (((PUINT8)macAddressPolicyBuffer) + + macAddressPolicyBuffer->PortArrayOffset); + + + if (switchContext->ExternalPortId != NDIS_SWITCH_DEFAULT_PORT_ID) + { + portIdArray[arrayIndex] = switchContext->ExternalPortId; + ++arrayIndex; + } + + if (!IsListEmpty(nicList)) + { + curEntry = nicList->Flink; + + do { + nic = CONTAINING_RECORD(curEntry, + MSFORWARD_NIC_LIST_ENTRY, + ListEntry); + + if (nic->AllowSends) + { + portIdArray[arrayIndex] = nic->PortId; + ++arrayIndex; + } + + curEntry = curEntry->Flink; + + } while(curEntry != nicList); + } + + SwitchFeatureStatus->FeatureStatusVersion = MAC_ADDRESS_POLICY_STATUS_VERSION; + +Cleanup: + if (lockHeld) + { + NdisReleaseRWLock(switchContext->DispatchLock, &lockState); + } + + return consumed; +} + + +_Use_decl_annotations_ +BOOLEAN +SxExtQueryPortFeatureStatus( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_FEATURE_STATUS_PARAMETERS PortFeatureStatus, + PULONG BytesNeeded + ) +/*++ + +Routine Description: + This extension has no custom port properties. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(PortFeatureStatus); + UNREFERENCED_PARAMETER(BytesNeeded); + + return FALSE; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtProcessNicRequest( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_OID_REQUEST OidRequest, + PNDIS_SWITCH_PORT_ID SourcePortId, + PNDIS_SWITCH_NIC_INDEX SourceNicIndex, + PNDIS_SWITCH_PORT_ID DestinationPortId, + PNDIS_SWITCH_NIC_INDEX DestinationNicIndex + ) +/*++ + +Routine Description: + The only NIC request this extension cares about is + OID_NIC_SWITCH_ALLOCATE_VF. We must fail all VF allocations + so that traffic flows through the extension and we can enforce + policy. + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SourcePortId); + UNREFERENCED_PARAMETER(SourceNicIndex); + UNREFERENCED_PARAMETER(DestinationPortId); + UNREFERENCED_PARAMETER(DestinationNicIndex); + + // + // Do not allow VF allocations, as all VM traffic must flow + // through our extension. + // + if (OidRequest->RequestType == NdisRequestSetInformation && + OidRequest->DATA.SET_INFORMATION.Oid == OID_NIC_SWITCH_ALLOCATE_VF) + { + status = NDIS_STATUS_FAILURE; + } + + return status; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtProcessNicRequestComplete( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_OID_REQUEST OidRequest, + NDIS_SWITCH_PORT_ID SourcePortId, + NDIS_SWITCH_NIC_INDEX SourceNicIndex, + NDIS_SWITCH_PORT_ID DestinationPortId, + NDIS_SWITCH_NIC_INDEX DestinationNicIndex, + NDIS_STATUS Status + ) +/*++ + +Routine Description: + This function will never be called because we do not + redirect or edit any NIC requests. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(OidRequest); + UNREFERENCED_PARAMETER(SourcePortId); + UNREFERENCED_PARAMETER(SourceNicIndex); + UNREFERENCED_PARAMETER(DestinationPortId); + UNREFERENCED_PARAMETER(DestinationNicIndex); + UNREFERENCED_PARAMETER(Status); + + // + // This function should never be called as we don't set any + // source/destination info in SxExtProcessNicRequest. + // + ASSERT(FALSE); + + return Status; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtProcessNicStatus( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_STATUS_INDICATION StatusIndication, + NDIS_SWITCH_PORT_ID SourcePortId, + NDIS_SWITCH_NIC_INDEX SourceNicIndex + ) +/*++ + +Routine Description: + This extension does not need to process any status + indications. + +--*/ +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(StatusIndication); + UNREFERENCED_PARAMETER(SourcePortId); + UNREFERENCED_PARAMETER(SourceNicIndex); + + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtStartNetBufferListsIngress( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG SendFlags + ) +/*++ + +Routine Description: + The function sets the destination lists of the NBLs forwarded + through the switch. + The extension only set destinations for NBLs originated from the External NIC, + Internal NIC, or NICs with MAC Policy set. + The extension determines the source by searching for the source + MAC address in the NIC list. + + The extension sets destinations by looking at the destination MAC address. + If the destination MAC address is a multicast or broadcast address, + the extension broadcasts the NBL to all ports, except the source. + If the destination MAC is a VM, the extension sets the VM as the destitation. + Otherwise the extension sets the External port as the destination. + +--*/ +{ + PMSFORWARD_CONTEXT switchContext = (PMSFORWARD_CONTEXT)ExtensionContext; + NDIS_SWITCH_PORT_ID sourcePort = 0, prevDestinationPort = 0, curDestinationPort = 0; + NDIS_SWITCH_NIC_INDEX sourceIndex = 0, prevDestinationIndex = 0, curDestinationIndex = 0; + PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; + PMSFORWARD_NIC_LIST_ENTRY sourceNicEntry = NULL; + PMSFORWARD_NIC_LIST_ENTRY destinationNicEntry = NULL; + BOOLEAN sameSource; + PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL; + PNET_BUFFER_LIST sendNbl = NULL, dropNbl = NULL; + PNET_BUFFER_LIST *nextDropNbl = &dropNbl; + PNET_BUFFER_LIST *nextSendNbl = &sendNbl; + PMSFORWARD_ETHERNET_HEADER curHeader; + UINT8 prevMacAddress[6] = {0}; + ULONG sendCompleteFlags = 0; + BOOLEAN dispatch; + PMDL curMdl; + PUINT8 curBuffer; + BOOLEAN broadcast = FALSE; + NDIS_SWITCH_PORT_DESTINATION newDestination = {0}; + PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY broadcastArray; + LOCK_STATE_EX lockState; + NDIS_STATUS status; + NDIS_STRING filterReason; + ULONG numDropNbls; + PNET_BUFFER_LIST extForwardedNbls = NULL; + PNET_BUFFER_LIST nativeForwardedNbls = NULL; + PNET_BUFFER_LIST *nextExtForwardNbl = &extForwardedNbls; + PNET_BUFFER_LIST *nextNativeForwardedNbl = &nativeForwardedNbls; + + dispatch = NDIS_TEST_SEND_FLAG(SendFlags, NDIS_SEND_FLAGS_DISPATCH_LEVEL); + sameSource = NDIS_TEST_SEND_FLAG(SendFlags, NDIS_SEND_FLAGS_SWITCH_SINGLE_SOURCE); + + sendCompleteFlags |= (dispatch) ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0; + SendFlags |= NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP; + + // + // Take DispatchLock so no NICs disconnect while we're setting destinations. + // + NdisAcquireRWLockRead(switchContext->DispatchLock, &lockState, dispatch); + if (sameSource) + { + fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(NetBufferLists); + sourcePort = fwdDetail->SourcePortId; + sourceIndex = (NDIS_SWITCH_NIC_INDEX)fwdDetail->SourceNicIndex; + + sendCompleteFlags |= NDIS_SEND_COMPLETE_FLAGS_SWITCH_SINGLE_SOURCE; + + sourceNicEntry = MsForwardFindNicByPortIdUnsafe(switchContext, + sourcePort, + sourceIndex); + + if (sourceNicEntry != NULL && !sourceNicEntry->AllowSends) + { + numDropNbls = 0; + for (curNbl = NetBufferLists; curNbl != NULL; curNbl = curNbl->Next) + { + ++numDropNbls; + } + + *nextDropNbl = NetBufferLists; + + RtlInitUnicodeString(&filterReason, L"Blocked by Source MAC Policy"); + + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + sourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + numDropNbls, + dropNbl, + &filterReason); + + goto Cleanup; + } + + // + // If nicEntry is not found, and is not external port, + // we must have failed to allocate this port. + // + else if(sourceNicEntry == NULL && + sourcePort != switchContext->ExternalPortId) + { + numDropNbls = 0; + for (curNbl = NetBufferLists; curNbl != NULL; curNbl = curNbl->Next) + { + ++numDropNbls; + } + + *nextDropNbl = NetBufferLists; + + RtlInitUnicodeString(&filterReason, L"Low Resources"); + + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + sourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + numDropNbls, + dropNbl, + &filterReason); + + goto Cleanup; + } + } + + // + // Split NBL list into NBLs to be forwarded by us, and those that require + // native forwarding. + // + for (curNbl = NetBufferLists; curNbl != NULL; curNbl = nextNbl) + { + nextNbl = curNbl->Next; + curNbl->Next = NULL; + fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl); + if (fwdDetail->NativeForwardingRequired) + { + *nextNativeForwardedNbl = curNbl; + nextNativeForwardedNbl = &(curNbl->Next); + } + else + { + *nextExtForwardNbl = curNbl; + nextExtForwardNbl = &(curNbl->Next); + } + } + + for (curNbl = extForwardedNbls; curNbl != NULL; curNbl = nextNbl) + { + nextNbl = curNbl->Next; + curNbl->Next = NULL; + + fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl); + + // + // First check for allowed source if not same source. + // + if (!sameSource) + { + sourcePort = fwdDetail->SourcePortId; + sourceIndex = (NDIS_SWITCH_NIC_INDEX)fwdDetail->SourceNicIndex; + + sourceNicEntry = MsForwardFindNicByPortIdUnsafe(switchContext, + sourcePort, + sourceIndex); + + if (sourceNicEntry != NULL && !sourceNicEntry->AllowSends) + { + RtlInitUnicodeString(&filterReason, L"Blocked by Source MAC Policy"); + + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + sourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + 1, + curNbl, + &filterReason); + + *nextDropNbl = curNbl; + nextDropNbl = &curNbl->Next; + continue; + } + else if(sourceNicEntry == NULL && + sourcePort != switchContext->ExternalPortId) + { + RtlInitUnicodeString(&filterReason, L"Low Resources"); + + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + sourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + 1, + curNbl, + &filterReason); + + *nextDropNbl = curNbl; + nextDropNbl = &curNbl->Next; + continue; + } + } + + // + // Ethernet Header is a guaranteed safe access. + // + curMdl = (NET_BUFFER_LIST_FIRST_NB(curNbl))->CurrentMdl; + curBuffer = MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); + curHeader = (PMSFORWARD_ETHERNET_HEADER) + (curBuffer + (NET_BUFFER_LIST_FIRST_NB(curNbl))->CurrentMdlOffset); + + // + // Check for broadcast. (Broadcast if multicast) + // + if (ETH_IS_BROADCAST(curHeader->Destination) || + ETH_IS_MULTICAST(curHeader->Destination)) + { + if(sendNbl != NULL && !broadcast) + { + // + // Send first nbl list, set destinations of this one, continue to work on next. + // + SxLibSendNetBufferListsIngress(Switch, + sendNbl, + SendFlags, + 0); + + sendNbl = NULL; + nextSendNbl = &sendNbl; + } + + if (fwdDetail->NumAvailableDestinations < (switchContext->NumDestinations - 1)) + { + status = Switch->NdisSwitchHandlers.GrowNetBufferListDestinations( + Switch->NdisSwitchContext, + curNbl, + (switchContext->NumDestinations - 1 - fwdDetail->NumAvailableDestinations), + &broadcastArray); + + if (status != NDIS_STATUS_SUCCESS) + { + RtlInitUnicodeString(&filterReason, L"Failed to grow destination list."); + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + sourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + 1, + curNbl, + &filterReason); + + *nextDropNbl = curNbl; + nextDropNbl = &curNbl->Next; + continue; + } + } + else + { + Switch->NdisSwitchHandlers.GetNetBufferListDestinations( + Switch->NdisSwitchContext, + curNbl, + &broadcastArray); + } + + MsForwardMakeBroadcastArrayUnsafe(switchContext, + broadcastArray, + sourcePort, + sourceIndex); + + if (switchContext->NumDestinations == 1) + { + RtlInitUnicodeString(&filterReason, L"Zero destinations for broadcast."); + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + sourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + 1, + curNbl, + &filterReason); + + *nextDropNbl = curNbl; + nextDropNbl = &curNbl->Next; + continue; + } + + status = Switch->NdisSwitchHandlers.UpdateNetBufferListDestinations( + Switch->NdisSwitchContext, + curNbl, + (switchContext->NumDestinations - 1), + broadcastArray); + ASSERT(status == NDIS_STATUS_SUCCESS); + + *nextSendNbl = curNbl; + nextSendNbl = &(curNbl->Next); + broadcast = TRUE; + + continue; + } + + + if (RtlEqualMemory(prevMacAddress, + curHeader->Destination, + sizeof(prevMacAddress))) + { + curDestinationPort = prevDestinationPort; + curDestinationIndex = prevDestinationIndex; + } + else + { + destinationNicEntry = MsForwardFindNicByMacAddressUnsafe(switchContext, + curHeader->Destination); + // + // Not a VM or host, send to external. + // + if (destinationNicEntry == NULL) + { + // + // If no external, or source is external, drop. + // + if (switchContext->ExternalPortId == 0) + { + RtlInitUnicodeString(&filterReason, L"No external NIC"); + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + sourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + 1, + curNbl, + &filterReason); + + *nextDropNbl = curNbl; + nextDropNbl = &curNbl->Next; + continue; + } + + if (sourcePort == switchContext->ExternalPortId) + { + RtlInitUnicodeString(&filterReason, L"Destination == Source"); + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + sourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + 1, + curNbl, + &filterReason); + + *nextDropNbl = curNbl; + nextDropNbl = &curNbl->Next; + continue; + } + + curDestinationPort = switchContext->ExternalPortId; + curDestinationIndex = switchContext->ExternalNicIndex; + } + else if (destinationNicEntry->Connected) + { + curDestinationPort = destinationNicEntry->PortId; + curDestinationIndex = destinationNicEntry->NicIndex; + } + else + { + RtlInitUnicodeString(&filterReason, L"Destination is NOT connected."); + Switch->NdisSwitchHandlers.ReportFilteredNetBufferLists( + Switch->NdisSwitchContext, + &SxExtensionGuid, + &SxExtensionFriendlyName, + sourcePort, + NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, + 1, + curNbl, + &filterReason); + + *nextDropNbl = curNbl; + nextDropNbl = &curNbl->Next; + continue; + } + } + + RtlMoveMemory(prevMacAddress, curHeader->Destination, sizeof(prevMacAddress)); + + newDestination.PortId = curDestinationPort; + newDestination.NicIndex = curDestinationIndex; + newDestination.PreserveVLAN = 0; + + ASSERT(fwdDetail->NumAvailableDestinations > 0); + status = Switch->NdisSwitchHandlers.AddNetBufferListDestination( + Switch->NdisSwitchContext, + curNbl, + &newDestination); + ASSERT(status == NDIS_STATUS_SUCCESS); + + if(sendNbl != NULL && + (broadcast || + (prevDestinationPort != curDestinationPort || + prevDestinationIndex != curDestinationIndex))) + { + SxLibSendNetBufferListsIngress(Switch, + sendNbl, + SendFlags, + 0); + + sendNbl = NULL; + nextSendNbl = &sendNbl; + } + + *nextSendNbl = curNbl; + nextSendNbl = &(curNbl->Next); + + // + // Done processing this NBL. + // + prevDestinationPort = curDestinationPort; + prevDestinationIndex = curDestinationIndex; + broadcast = FALSE; + } + +Cleanup: + NdisReleaseRWLock(switchContext->DispatchLock, &lockState); + + if (sendNbl != NULL) + { + SxLibSendNetBufferListsIngress(Switch, + sendNbl, + SendFlags, + 0); + } + + if (nativeForwardedNbls != NULL) + { + SxLibSendNetBufferListsIngress(Switch, + nativeForwardedNbls, + SendFlags, + 0); + } + + if (dropNbl != NULL) + { + SxLibCompleteNetBufferListsIngress(Switch, + dropNbl, + sendCompleteFlags); + } +} + + +_Use_decl_annotations_ +VOID +SxExtStartNetBufferListsEgress( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG NumberOfNetBufferLists, + ULONG ReceiveFlags + ) +/*++ + +Routine Description: + No egress processing necessary. + +--*/ +{ + UNREFERENCED_PARAMETER(ExtensionContext); + + SxLibSendNetBufferListsEgress(Switch, + NetBufferLists, + NumberOfNetBufferLists, + ReceiveFlags); +} + + +_Use_decl_annotations_ +VOID +SxExtStartCompleteNetBufferListsEgress( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG ReturnFlags + ) +/*++ + +Routine Description: + No egress processing necessary. + +--*/ +{ + UNREFERENCED_PARAMETER(ExtensionContext); + + SxLibCompleteNetBufferListsEgress(Switch, + NetBufferLists, + ReturnFlags); +} + + +_Use_decl_annotations_ +VOID +SxExtStartCompleteNetBufferListsIngress( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG SendCompleteFlags + ) +/*++ + +Routine Description: + No ingress complete processing necessary. + +--*/ +{ + UNREFERENCED_PARAMETER(ExtensionContext); + + SxLibCompleteNetBufferListsIngress(Switch, + NetBufferLists, + SendCompleteFlags); +} + + +NDIS_STATUS +MsForwardAddNicUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_reads_bytes_(6) PUCHAR MacAddress, + _In_ NDIS_SWITCH_PORT_ID PortId, + _In_ NDIS_SWITCH_NIC_INDEX NicIndex, + _In_ NDIS_SWITCH_NIC_TYPE NicType, + _In_ BOOLEAN Connected + ) +/*++ + +Routine Description: + Add given NIC to the NIC list and correlate with + MAC policy. + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + PMSFORWARD_NIC_LIST_ENTRY nicEntry = NULL; + PLIST_ENTRY nicList = &SwitchContext->NicList; + + if (NicType == NdisSwitchNicTypeExternal) + { + goto Cleanup; + } + + nicEntry = MsForwardFindNicByPortIdUnsafe(SwitchContext, + PortId, + NicIndex); + + if (nicEntry == NULL) + { + nicEntry = ExAllocatePoolWithTag(NonPagedPoolNx, + sizeof(MSFORWARD_NIC_LIST_ENTRY), + SxExtAllocationTag); + + if (nicEntry == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + NdisZeroMemory(nicEntry, sizeof(MSFORWARD_NIC_LIST_ENTRY)); + NdisMoveMemory(nicEntry->MacAddress, MacAddress, MSFORWARD_MAC_LENGTH); + + nicEntry->PortId = PortId; + nicEntry->NicIndex = NicIndex; + nicEntry->NicType = NicType; + nicEntry->Connected = Connected; + + if (NicType == NdisSwitchNicTypeInternal) + { + nicEntry->AllowSends = TRUE; + } + else + { + nicEntry->AllowSends = MsForwardNicHasPolicy(SwitchContext, MacAddress); + } + + InsertHeadList(nicList, &nicEntry->ListEntry); + } + +Cleanup: + return status; +} + + +NDIS_STATUS +MsForwardAddMacPolicyUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ PMSFORWARD_MAC_ADDRESS_POLICY MacPolicyBuffer, + _In_ PNDIS_SWITCH_OBJECT_INSTANCE_ID PropertyInstanceId + ) +/*++ + +Routine Description: + Add the given policy to the policy list and correlate with + the NIC list. + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + PMSFORWARD_MAC_POLICY_LIST_ENTRY newPolicy; + PMSFORWARD_NIC_LIST_ENTRY nic; + + newPolicy = MsForwardFindPolicyByMacAddressUnsafe(SwitchContext, + MacPolicyBuffer->MacAddress); + + if (newPolicy == NULL) + { + newPolicy = ExAllocatePoolWithTag(NonPagedPoolNx, + sizeof(MSFORWARD_MAC_POLICY_LIST_ENTRY), + SxExtAllocationTag); + + if (newPolicy == NULL) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + NdisMoveMemory(&newPolicy->MacAddress, + MacPolicyBuffer->MacAddress, + sizeof(newPolicy->MacAddress)); + + NdisMoveMemory(&newPolicy->PropertyInstanceId, + PropertyInstanceId, + sizeof(NDIS_SWITCH_OBJECT_INSTANCE_ID)); + + InsertHeadList(&SwitchContext->PropertyList, + &newPolicy->ListEntry); + + nic = MsForwardFindNicByMacAddressUnsafe(SwitchContext, + MacPolicyBuffer->MacAddress); + + if (nic != NULL) + { + nic->AllowSends = TRUE; + } + } + else + { + status = NDIS_STATUS_DATA_NOT_ACCEPTED; + } + +Cleanup: + return status; +} + + +VOID +MsForwardDeleteMacPolicyUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ PNDIS_SWITCH_OBJECT_INSTANCE_ID PropertyInstanceId + ) +/*++ + +Routine Description: + Delete the given MAC policy, and coorelate with + the NIC list. + +--*/ +{ + PMSFORWARD_MAC_POLICY_LIST_ENTRY deletePolicy; + PMSFORWARD_NIC_LIST_ENTRY nic; + + deletePolicy = MsForwardFindPolicyByPropertyInstanceIdUnsafe( + SwitchContext, + PropertyInstanceId); + + if (deletePolicy != NULL) + { + nic = MsForwardFindNicByMacAddressUnsafe(SwitchContext, + deletePolicy->MacAddress); + + if (nic != NULL) + { + nic->AllowSends = FALSE; + } + + RemoveEntryList(&deletePolicy->ListEntry); + ExFreePoolWithTag(deletePolicy, SxExtAllocationTag); + } +} + + +PMSFORWARD_NIC_LIST_ENTRY +MsForwardFindNicByPortIdUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ NDIS_SWITCH_PORT_ID PortId, + _In_ NDIS_SWITCH_NIC_INDEX NicIndex + ) +/*++ + +Routine Description: + Search for the NIC needed by port ID. + +--*/ +{ + PLIST_ENTRY nicList = &SwitchContext->NicList; + PLIST_ENTRY curEntry = nicList->Flink; + PMSFORWARD_NIC_LIST_ENTRY nic = NULL; + + if (IsListEmpty(nicList)) + { + goto Cleanup; + } + + do { + nic = CONTAINING_RECORD(curEntry, + MSFORWARD_NIC_LIST_ENTRY, + ListEntry); + + if (nic->PortId == PortId && + nic->NicIndex == NicIndex) + { + goto Cleanup; + } + + curEntry = curEntry->Flink; + + } while(curEntry != nicList); + + nic = NULL; + +Cleanup: + return nic; +} + + +PMSFORWARD_NIC_LIST_ENTRY +MsForwardFindNicByMacAddressUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_reads_bytes_(6) PUCHAR MacAddress + ) +/*++ + +Routine Description: + Search for the NIC needed by MAC Address. + +--*/ +{ + PLIST_ENTRY nicList = &SwitchContext->NicList; + PLIST_ENTRY curEntry = nicList->Flink; + PMSFORWARD_NIC_LIST_ENTRY nic = NULL; + + if (IsListEmpty(nicList)) + { + goto Cleanup; + } + + do { + nic = CONTAINING_RECORD(curEntry, + MSFORWARD_NIC_LIST_ENTRY, + ListEntry); + + if (RtlEqualMemory(MacAddress, + nic->MacAddress, + sizeof(nic->MacAddress))) + { + goto Cleanup; + } + + curEntry = curEntry->Flink; + + } while(curEntry != nicList); + + nic = NULL; + +Cleanup: + return nic; +} + + +PMSFORWARD_MAC_POLICY_LIST_ENTRY +MsForwardFindPolicyByMacAddressUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_reads_bytes_(6) PUCHAR MacAddress + ) +/*++ + +Routine Description: + Search for the policy needed by MAC address. + +--*/ +{ + PLIST_ENTRY propertyList = &SwitchContext->PropertyList; + PLIST_ENTRY curEntry = propertyList->Flink; + PMSFORWARD_MAC_POLICY_LIST_ENTRY policy = NULL; + + if (IsListEmpty(propertyList)) + { + goto Cleanup; + } + + do { + policy = CONTAINING_RECORD(curEntry, + MSFORWARD_MAC_POLICY_LIST_ENTRY, + ListEntry); + + if (RtlEqualMemory(MacAddress, + policy->MacAddress, + sizeof(policy->MacAddress))) + { + goto Cleanup; + } + + curEntry = curEntry->Flink; + + } while(curEntry != propertyList); + + policy = NULL; + +Cleanup: + return policy; +} + + +PMSFORWARD_MAC_POLICY_LIST_ENTRY +MsForwardFindPolicyByPropertyInstanceIdUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ PNDIS_SWITCH_OBJECT_INSTANCE_ID PropertyInstanceId + ) +/*++ + +Routine Description: + Search for the policy needed by PropertyInstanceId. + +--*/ +{ + PLIST_ENTRY propertyList = &SwitchContext->PropertyList; + PLIST_ENTRY curEntry = propertyList->Flink; + PMSFORWARD_MAC_POLICY_LIST_ENTRY policy = NULL; + + if (IsListEmpty(propertyList)) + { + goto Cleanup; + } + + do { + policy = CONTAINING_RECORD(curEntry, + MSFORWARD_MAC_POLICY_LIST_ENTRY, + ListEntry); + + if (RtlEqualMemory(PropertyInstanceId, + &policy->PropertyInstanceId, + sizeof(policy->PropertyInstanceId))) + { + goto Cleanup; + } + + curEntry = curEntry->Flink; + + } while(curEntry != propertyList); + + policy = NULL; + +Cleanup: + return policy; +} + + +NDIS_STATUS +MsForwardDeleteNicUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ NDIS_SWITCH_PORT_ID PortId, + _In_ NDIS_SWITCH_NIC_INDEX NicIndex + ) +/*++ + +Routine Description: + Remove the NIC represented by the PortId and NicIndex + from the NIC list and free its memory. + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + + PMSFORWARD_NIC_LIST_ENTRY nicEntry = MsForwardFindNicByPortIdUnsafe(SwitchContext, + PortId, + NicIndex); + + if (nicEntry == NULL) + { + ASSERT(FALSE); + goto Cleanup; + } + + RemoveEntryList(&nicEntry->ListEntry); + ExFreePoolWithTag(nicEntry, SxExtAllocationTag); + +Cleanup: + return status; +} + + +VOID +MsForwardClearNicListUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext + ) +/*++ + +Routine Description: + Remove all NICs from the list and free all memory. + +--*/ +{ + PMSFORWARD_NIC_LIST_ENTRY nic; + PLIST_ENTRY nicList = &SwitchContext->NicList; + PLIST_ENTRY headList = NULL; + + while (!IsListEmpty(nicList)) + { + headList = RemoveHeadList(nicList); + + nic = CONTAINING_RECORD(headList, + MSFORWARD_NIC_LIST_ENTRY, + ListEntry); + + ExFreePoolWithTag(nic, SxExtAllocationTag); + } + + return; +} + + +VOID +MsForwardClearPropertyListUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext + ) +/*++ + +Routine Description: + Remove all properties from the list and free all memory. + +--*/ +{ + PMSFORWARD_MAC_POLICY_LIST_ENTRY policy; + PLIST_ENTRY propertyList = &SwitchContext->PropertyList; + PLIST_ENTRY headList = NULL; + + while (!IsListEmpty(propertyList)) + { + headList = RemoveHeadList(propertyList); + + policy = CONTAINING_RECORD(headList, + MSFORWARD_MAC_POLICY_LIST_ENTRY, + ListEntry); + + ExFreePoolWithTag(policy, SxExtAllocationTag); + } + + return; +} + + +BOOLEAN +MsForwardNicHasPolicy( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_reads_bytes_(6) PUCHAR MacAddress + ) +/*++ + +Routine Description: + Returns TRUE if there is a correlated policy to the MAC + address given. + +--*/ +{ + return (MsForwardFindPolicyByMacAddressUnsafe(SwitchContext, + MacAddress) != NULL); +} + + +VOID +MsForwardMakeBroadcastArrayUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY BroadcastArray, + _In_ NDIS_SWITCH_PORT_ID SourcePortId, + _In_ NDIS_SWITCH_NIC_INDEX SourceNicIndex + ) +/*++ + +Routine Description: + Creates the destination array of all connected NICs + excluding the source given. + +--*/ +{ + PLIST_ENTRY nicList = &SwitchContext->NicList; + PLIST_ENTRY curEntry = nicList->Flink; + PMSFORWARD_NIC_LIST_ENTRY nic = NULL; + UINT32 index = BroadcastArray->NumDestinations; + PNDIS_SWITCH_PORT_DESTINATION destination; + + if (IsListEmpty(nicList)) + { + goto Cleanup; + } + + do { + nic = CONTAINING_RECORD(curEntry, + MSFORWARD_NIC_LIST_ENTRY, + ListEntry); + + if ((SourcePortId == nic->PortId && + SourceNicIndex == nic->NicIndex) || + !nic->Connected) + { + if(curEntry->Flink == nicList) + { + break; + } + else + { + curEntry = curEntry->Flink; + continue; + } + } + + destination = NDIS_SWITCH_PORT_DESTINATION_AT_ARRAY_INDEX(BroadcastArray, index); + NdisZeroMemory(destination, sizeof(NDIS_SWITCH_PORT_DESTINATION)); + + destination->PortId = nic->PortId; + destination->NicIndex = nic->NicIndex; + + ++index; + curEntry = curEntry->Flink; + + } while(curEntry != nicList); + + if (SourcePortId != SwitchContext->ExternalPortId && + SwitchContext->ExternalNicConnected) + { + destination = NDIS_SWITCH_PORT_DESTINATION_AT_ARRAY_INDEX(BroadcastArray, index); + destination->PortId = SwitchContext->ExternalPortId; + destination->NicIndex = SwitchContext->ExternalNicIndex; + } + +Cleanup: + return; +} + + +NDIS_STATUS +MsForwardInitSwitch( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PMSFORWARD_CONTEXT SwitchContext + ) +/*++ + +Routine Description: + Initializes the switch state. + +--*/ +{ + NDIS_STATUS status = NDIS_STATUS_SUCCESS; + PNDIS_SWITCH_NIC_ARRAY nicArray = NULL; + ULONG arrIndex; + PNDIS_SWITCH_NIC_PARAMETERS curNic; + PNDIS_SWITCH_PROPERTY_ENUM_PARAMETERS switchPropertyParameters = NULL; + PNDIS_SWITCH_PROPERTY_ENUM_INFO switchPropertyInfo = NULL; + PNDIS_SWITCH_PROPERTY_CUSTOM customPropertyInfo = NULL; + PMSFORWARD_MAC_ADDRESS_POLICY macAddressPolicy; + PNDIS_SWITCH_PORT_PROPERTY_ENUM_PARAMETERS portPropertyParameters = NULL; + PNDIS_SWITCH_PORT_PROPERTY_ENUM_INFO portPropertyInfo = NULL; + PNDIS_SWITCH_PORT_PROPERTY_VLAN vlanProperty; + + ASSERT(!SwitchContext->IsActive); + + // + // Get Property List. + // + status = SxLibGetSwitchPropertyUnsafe(Switch, + NdisSwitchPropertyTypeCustom, + (PNDIS_SWITCH_OBJECT_ID)&MacAddressPolicyGuid, + &switchPropertyParameters); + + if (status != NDIS_STATUS_SUCCESS) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + switchPropertyInfo = + NDIS_SWITCH_PROPERTY_ENUM_PARAMETERS_GET_FIRST_INFO(switchPropertyParameters); + + for (arrIndex = 0; + arrIndex < switchPropertyParameters->NumProperties; + ++arrIndex) + { + // + // Should always get back v1 or later. It is safe to access the + // v1 version of the structure if newer property is retrieved. + // + ASSERT(switchPropertyInfo->PropertyVersion >= + MAC_ADDRESS_POLICY_VERSION); + + customPropertyInfo = + NDIS_SWITCH_PROPERTY_ENUM_INFO_GET_PROPERTY(switchPropertyInfo); + + macAddressPolicy = + NDIS_SWITCH_PROPERTY_CUSTOM_GET_BUFFER(customPropertyInfo); + + status = MsForwardAddMacPolicyUnsafe(SwitchContext, + macAddressPolicy, + &switchPropertyInfo->PropertyInstanceId); + + if (status != NDIS_STATUS_SUCCESS) + { + goto Cleanup; + } + + switchPropertyInfo = + NDIS_SWITCH_PROPERTY_ENUM_INFO_GET_NEXT(switchPropertyInfo); + } + + // + // Now, get NIC list. + // + status = SxLibGetNicArrayUnsafe(Switch, &nicArray); + if (status != NDIS_STATUS_SUCCESS) + { + goto Cleanup; + } + + for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) + { + curNic = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex); + + status = Switch->NdisSwitchHandlers.ReferenceSwitchPort( + Switch->NdisSwitchContext, + curNic->PortId); + + ASSERT(status == NDIS_STATUS_SUCCESS); + + // + // Get VLAN Port property to ensure no VLAN set. + // + status = SxLibGetPortPropertyUnsafe(Switch, + curNic->PortId, + NdisSwitchPortPropertyTypeVlan, + NULL, + &portPropertyParameters); + + if (status != NDIS_STATUS_SUCCESS) + { + status = NDIS_STATUS_RESOURCES; + goto Cleanup; + } + + portPropertyInfo = + NDIS_SWITCH_PORT_PROPERTY_ENUM_PARAMETERS_GET_FIRST_INFO(portPropertyParameters); + + // + // Should always get back v1 or later. It is safe to access the v1 + // version of the structure if newer property is retrieved. + // + ASSERT(portPropertyInfo->PropertyVersion >= + NDIS_SWITCH_PORT_PROPERTY_VLAN_REVISION_1); + + vlanProperty = + NDIS_SWITCH_PORT_PROPERTY_ENUM_INFO_GET_PROPERTY(portPropertyInfo); + + // + // Real production code should support VLAN, + // and not fail SxExtRestartSwitch. + // + if (vlanProperty->OperationMode != NdisSwitchPortVlanModeAccess || + vlanProperty->VlanProperties.AccessVlanId != 0) + { + status = NDIS_STATUS_FAILURE; + goto Cleanup; + } + + status = Switch->NdisSwitchHandlers.DereferenceSwitchPort( + Switch->NdisSwitchContext, + curNic->PortId); + + ASSERT(status == NDIS_STATUS_SUCCESS); + + // + // If a VF is assigned to a NIC, then the traffic + // flows through the VF and not the switch. This means + // we have to revoke the VF to enforce our policy. + // + if (curNic->VFAssigned) + { + status = Switch->NdisSwitchHandlers.ReferenceSwitchNic( + Switch->NdisSwitchContext, + curNic->PortId, + curNic->NicIndex); + + ASSERT(status == NDIS_STATUS_SUCCESS); + + SxLibRevokeVfUnsafe(Switch, curNic->PortId); + + status = Switch->NdisSwitchHandlers.DereferenceSwitchNic( + Switch->NdisSwitchContext, + curNic->PortId, + curNic->NicIndex); + + ASSERT(status == NDIS_STATUS_SUCCESS); + } + + // + // Now we've verified we can support the NIC, so + // check if there's a property for it, and add it to + // the NIC list. + // + status = MsForwardAddNicUnsafe(SwitchContext, + curNic->PermanentMacAddress, + curNic->PortId, + curNic->NicIndex, + curNic->NicType, + (curNic->NicState == NdisSwitchNicStateConnected)); + + if (status != NDIS_STATUS_SUCCESS) + { + goto Cleanup; + } + + if (curNic->NicType == NdisSwitchNicTypeExternal && + curNic->NicIndex != 0 && + SwitchContext->ExternalPortId == 0) + { + SwitchContext->ExternalPortId = curNic->PortId; + SwitchContext->ExternalNicIndex = curNic->NicIndex; + SwitchContext->ExternalNicConnected = TRUE; + ++(SwitchContext->NumDestinations); + } + else if (curNic->NicType != NdisSwitchNicTypeExternal) + { + ++(SwitchContext->NumDestinations); + } + } + + SwitchContext->IsActive = TRUE; + +Cleanup: + if (switchPropertyParameters != NULL) + { + ExFreePoolWithTag(switchPropertyParameters, SxExtAllocationTag); + } + + if (portPropertyParameters != NULL) + { + ExFreePoolWithTag(portPropertyParameters, SxExtAllocationTag); + } + + if (nicArray != NULL) + { + ExFreePoolWithTag(nicArray, SxExtAllocationTag); + } + + return status; +} + diff --git a/network/ndis/extension/samples/forward/MsForwardExt.h b/network/ndis/extension/samples/forward/MsForwardExt.h new file mode 100644 index 00000000..ed6ac629 --- /dev/null +++ b/network/ndis/extension/samples/forward/MsForwardExt.h @@ -0,0 +1,219 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved. + +Module Name: + + MsForwardExt.h + +Abstract: + + This file contains structures and function definitions + necessary for MsForwardExt. + + +--*/ + + +#define MSFORWARD_MAC_LENGTH 6 + +// +// MSFORWARD_CONTEXT +// The context allocated per switch. +// +typedef struct _MSFORWARD_CONTEXT +{ + BOOLEAN IsActive; + + NDIS_SWITCH_PORT_ID ExternalPortId; + NDIS_SWITCH_NIC_INDEX ExternalNicIndex; + BOOLEAN ExternalNicConnected; + + // + // This sample uses linked lists for the NICs and property + // lookup. THIS IS NOT RECOMMENDED. + // + LIST_ENTRY NicList; + LIST_ENTRY PropertyList; + PNDIS_RW_LOCK_EX DispatchLock; + + UINT32 NumDestinations; + BOOLEAN IsInitialRestart; +} MSFORWARD_CONTEXT, *PMSFORWARD_CONTEXT; + +// +// MSFORWARD_NIC_LIST_ENTRY +// The context allocated per NIC. +// +typedef struct _MSFORWARD_NIC_LIST_ENTRY +{ + LIST_ENTRY ListEntry; + UINT8 MacAddress[MSFORWARD_MAC_LENGTH]; + NDIS_SWITCH_PORT_ID PortId; + NDIS_SWITCH_NIC_INDEX NicIndex; + NDIS_SWITCH_NIC_TYPE NicType; + BOOLEAN AllowSends; + BOOLEAN Connected; +} MSFORWARD_NIC_LIST_ENTRY, *PMSFORWARD_NIC_LIST_ENTRY; + +// +// MSFORWARD_MAC_POLICY_LIST_ENTRY +// The context allocated per switch policy. +// +typedef struct _MSFORWARD_MAC_POLICY_LIST_ENTRY +{ + LIST_ENTRY ListEntry; + UINT8 MacAddress[MSFORWARD_MAC_LENGTH]; + NDIS_SWITCH_OBJECT_INSTANCE_ID PropertyInstanceId; +} MSFORWARD_MAC_POLICY_LIST_ENTRY, *PMSFORWARD_MAC_POLICY_LIST_ENTRY; + +// +// MSFORWARD_MAC_ADDRESS_POLICY +// The serialization of the policy structure found +// in MsForwardExtPolicy.mof +// +typedef struct _MSFORWARD_MAC_ADDRESS_POLICY +{ + UINT32 MacAddressLength; + UINT8 MacAddress[MSFORWARD_MAC_LENGTH]; +} MSFORWARD_MAC_ADDRESS_POLICY, *PMSFORWARD_MAC_ADDRESS_POLICY; + +// +// MSFORWARD_MAC_ADDRESS_POLICY_STATUS +// The serialization of the policy status structure +// found in MsForwardExtPolicyStatus.mof +// +typedef struct _MSFORWARD_MAC_ADDRESS_POLICY_STATUS +{ + UINT32 PortArrayLength; + UINT32 PortArrayOffset; +} MSFORWARD_MAC_ADDRESS_POLICY_STATUS, *PMSFORWARD_MAC_ADDRESS_POLICY_STATUS; + +// +// MSFORWARD_ETHERNET_HEADER +// Ethernet header definition. +// +typedef struct _MSFORWARD_ETHERNET_HEADER +{ + UINT8 Destination[MSFORWARD_MAC_LENGTH]; + UINT8 Source[MSFORWARD_MAC_LENGTH]; + UINT16 Type; +} MSFORWARD_ETHERNET_HEADER, *PMSFORWARD_ETHERNET_HEADER; + +// +// MacAddressPolicyGuid +// +// The GUID representing the switch policy owned +// by MsForwardExt. +// +extern const NDIS_SWITCH_OBJECT_ID MacAddressPolicyGuid; + +// +// MacAddressPolicyStatusGuid +// +// The GUID representing the switch policy status +// indicated for the status of the switch policy +// owned by MsForwardExt. +// +extern const NDIS_SWITCH_OBJECT_ID MacAddressPolicyStatusGuid; + +// +// Switch Property Macros +// +#define MAC_ADDRESS_POLICY_VERSION 0x0100 +#define MAC_ADDRESS_POLICY_SERIALIZATION_VERSION NDIS_SWITCH_OBJECT_SERIALIZATION_VERSION_1 + +// +// Switch Property Status Macros +// +#define MAC_ADDRESS_POLICY_STATUS_VERSION 0x0100 +#define MAC_ADDRESS_POLICY_STATUS_SERIALIZATION_VERSION NDIS_SWITCH_OBJECT_SERIALIZATION_VERSION_1 + + +// +// Private functions used by MsForwardExt +// +NDIS_STATUS +MsForwardAddNicUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_reads_bytes_(6) PUCHAR MacAddress, + _In_ NDIS_SWITCH_PORT_ID PortId, + _In_ NDIS_SWITCH_NIC_INDEX NicIndex, + _In_ NDIS_SWITCH_NIC_TYPE NicType, + _In_ BOOLEAN Connected + ); + +NDIS_STATUS +MsForwardDeleteNicUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ NDIS_SWITCH_PORT_ID PortId, + _In_ NDIS_SWITCH_NIC_INDEX NicIndex + ); + +VOID +MsForwardClearNicListUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext + ); + +PMSFORWARD_NIC_LIST_ENTRY +MsForwardFindNicByPortIdUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ NDIS_SWITCH_PORT_ID PortId, + _In_ NDIS_SWITCH_NIC_INDEX NicIndex + ); + +PMSFORWARD_NIC_LIST_ENTRY +MsForwardFindNicByMacAddressUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_reads_bytes_(6) PUCHAR MacAddress + ); + +NDIS_STATUS +MsForwardAddMacPolicyUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ PMSFORWARD_MAC_ADDRESS_POLICY MacPolicyBuffer, + _In_ PNDIS_SWITCH_OBJECT_INSTANCE_ID PropertyInstanceId + ); + +VOID +MsForwardDeleteMacPolicyUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ PNDIS_SWITCH_OBJECT_INSTANCE_ID PropertyInstanceId + ); + +VOID +MsForwardClearPropertyListUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext + ); + +PMSFORWARD_MAC_POLICY_LIST_ENTRY +MsForwardFindPolicyByMacAddressUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_reads_bytes_(6) PUCHAR MacAddress + ); + +PMSFORWARD_MAC_POLICY_LIST_ENTRY +MsForwardFindPolicyByPropertyInstanceIdUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ PNDIS_SWITCH_OBJECT_INSTANCE_ID PropertyInstanceId + ); + +BOOLEAN +MsForwardNicHasPolicy( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_reads_bytes_(6) PUCHAR MacAddress + ); + +VOID +MsForwardMakeBroadcastArrayUnsafe( + _In_ PMSFORWARD_CONTEXT SwitchContext, + _In_ PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY BroadcastArray, + _In_ NDIS_SWITCH_PORT_ID SourcePortId, + _In_ NDIS_SWITCH_NIC_INDEX SourceNicIndex + ); + +NDIS_STATUS +MsForwardInitSwitch( + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PMSFORWARD_CONTEXT SwitchContext + ); diff --git a/network/ndis/extension/samples/forward/MsForwardExt.rc b/network/ndis/extension/samples/forward/MsForwardExt.rc new file mode 100644 index 00000000..25e0b2db --- /dev/null +++ b/network/ndis/extension/samples/forward/MsForwardExt.rc @@ -0,0 +1,24 @@ +// +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// + +#include +#include + +/*-----------------------------------------------*/ +/* the following lines are specific to this file */ +/*-----------------------------------------------*/ + +/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR + * and VER_INTERNALNAME_STR must be defined before including COMMON.VER + * The strings don't need a '\0', since common.ver has them. + */ +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "Microsoft Sample Forwarding Extension" +#define VER_INTERNALNAME_STR "msforwardext.SYS" +#define VER_ORIGINALFILENAME_STR "msforwardext.SYS" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/network/ndis/extension/samples/forward/install.cmd b/network/ndis/extension/samples/forward/install.cmd new file mode 100644 index 00000000..009eed36 --- /dev/null +++ b/network/ndis/extension/samples/forward/install.cmd @@ -0,0 +1,6 @@ +netcfg -l .\MSForwardExt.inf -c s -i ms_forwardext + +net stop vmms +mofcomp -N:root\virtualization\v2 .\MSForwardExtPolicy.mof +mofcomp -N:root\virtualization\v2 .\MSForwardExtPolicyStatus.mof +net start vmms diff --git a/network/ndis/extension/samples/forward/msforwardext.inf b/network/ndis/extension/samples/forward/msforwardext.inf new file mode 100644 index 00000000..dd849d8b --- /dev/null +++ b/network/ndis/extension/samples/forward/msforwardext.inf @@ -0,0 +1,85 @@ +; +; Copyright (c) Microsoft Corporation. All Rights Reserved. +; + +[version] +Signature = "$Windows NT$" +Class = NetService +ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} +Provider = %Msft% +CatalogFile = msforwardext.cat +DriverVer = 07/29/2011,2.0 + +[Manufacturer] +%Msft%=MSFT,NTx86,NTia64,NTamd64 + +[MSFT.NTx86] +%MSForwardExt_Desc%=Install, MS_forwardext + +[MSFT.NTia64] +%MSForwardExt_Desc%=Install, MS_forwardext + +[MSFT.NTamd64] +%MSForwardExt_Desc%=Install, MS_forwardext + +;------------------------------------------------------------------------- +; Installation Section +;------------------------------------------------------------------------- +[Install] +AddReg=Inst_Ndi +Characteristics=0x40000 +NetCfgInstanceId="{37d9eae6-5bae-48c5-bff1-63a7cdd7e4f4}" +Copyfiles = MSForwardExt.copyfiles.sys + +[SourceDisksNames] +1=%MSForwardExt_Desc%,"",, + +[SourceDisksFiles] +MSForwardExt.sys=1 + +[DestinationDirs] +DefaultDestDir=12 +MSForwardExt.copyfiles.sys=12 + +[MSForwardExt.copyfiles.sys] +MSForwardExt.sys,,,2 + + +;------------------------------------------------------------------------- +; Ndi installation support +;------------------------------------------------------------------------- +[Inst_Ndi] +HKR, Ndi,Service,,"MSForwardExt" +HKR, Ndi,CoServices,0x00010000,"MSForwardExt" +HKR, Ndi,HelpText,,%MSForwardExt_HelpText% +HKR, Ndi,FilterClass,,"ms_switch_forward" +HKR, Ndi,FilterType,0x00010001,0x00000002 +HKR, Ndi\Interfaces,UpperRange,,"noupper" +HKR, Ndi\Interfaces,LowerRange,,"nolower" +HKR, Ndi\Interfaces, FilterMediaTypes,,"vmnetextension" +HKR, Ndi,FilterRunType, 0x00010001, 2 ; optional + +;------------------------------------------------------------------------- +; Service installation support, common.EventLog here is to demonstrate how to +; write an enent log +;------------------------------------------------------------------------- +[Install.Services] +AddService=MSForwardExt,,MSForwardExt_Service_Inst;, common.EventLog + +[MSForwardExt_Service_Inst] +DisplayName = %MSForwardExt_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 1 ;SERVICE_SYSTEM_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\MSForwardExt.sys +LoadOrderGroup = NDIS +Description = %MSForwardExt_Desc% +AddReg = Common.Params.reg + +[Install.Remove.Services] +DelService=MSForwardExt,0x200 + +[Strings] +Msft = "Microsoft" +MSForwardExt_Desc = "Microsoft Sample Forwarding Extension" +MSForwardExt_HelpText = "Sample for forwarding switch extension" diff --git a/network/ndis/extension/samples/forward/msforwardext.vcxproj b/network/ndis/extension/samples/forward/msforwardext.vcxproj new file mode 100644 index 00000000..af65e5ba --- /dev/null +++ b/network/ndis/extension/samples/forward/msforwardext.vcxproj @@ -0,0 +1,127 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {404E1399-EA48-4CEE-A59E-EA0D4C01928A} + $(MSBuildProjectName) + Debug + x64 + {E7755C69-DA3A-4E6B-9C38-0435AC07863F} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + msforwardext + + + msforwardext + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;.\..\..\base\$(IntDir)\sxbase.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;.\..\..\base\$(IntDir)\sxbase.lib + + + true + Level4 + + + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/extension/samples/forward/msforwardext.vcxproj.Filters b/network/ndis/extension/samples/forward/msforwardext.vcxproj.Filters new file mode 100644 index 00000000..a4de6bd4 --- /dev/null +++ b/network/ndis/extension/samples/forward/msforwardext.vcxproj.Filters @@ -0,0 +1,34 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {AE574627-04F4-423B-B254-44BD4A41BF5D} + + + h;hpp;hxx;hm;inl;inc;xsd + {50E67A30-537B-4C27-BDFD-110A25C9A725} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {83FDCB52-42AC-4B2D-9A09-F917F36CFA49} + + + inf;inv;inx;mof;mc; + {362EB8F8-E693-45BC-9EE5-1ED0A9776739} + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/extension/samples/forward/precomp.h b/network/ndis/extension/samples/forward/precomp.h new file mode 100644 index 00000000..ca260479 --- /dev/null +++ b/network/ndis/extension/samples/forward/precomp.h @@ -0,0 +1,11 @@ +// +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// + +#include +#include +#include +#include +#include "..\..\base\SxBase.h" +#include "..\..\base\SxApi.h" +#include "..\..\base\SxLibrary.h" \ No newline at end of file diff --git a/network/ndis/extension/samples/forward/precompsrc.c b/network/ndis/extension/samples/forward/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/network/ndis/extension/samples/forward/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/network/ndis/extension/samples/forward/removeRoute.ps1 b/network/ndis/extension/samples/forward/removeRoute.ps1 new file mode 100644 index 00000000..e03f9d9f --- /dev/null +++ b/network/ndis/extension/samples/forward/removeRoute.ps1 @@ -0,0 +1,12 @@ +# +# Copyright (c) Microsoft Corporation. All Rights Reserved. +# + +Import-Module Hyper-V + +$switch = Get-VmSwitch -Name "CorpNet" +$features = Get-VmSwitchExtensionSwitchFeature -VmSwitch $switch -FeatureName "MSForwardExt Mac Address Policy" +if ($features -ne $null) +{ + Remove-VmSwitchExtensionSwitchFeature -VmSwitch $switch -VmSwitchExtensionFeature $features +} \ No newline at end of file diff --git a/network/ndis/extension/samples/forward/setRoute.ps1 b/network/ndis/extension/samples/forward/setRoute.ps1 new file mode 100644 index 00000000..71899087 --- /dev/null +++ b/network/ndis/extension/samples/forward/setRoute.ps1 @@ -0,0 +1,54 @@ +# +# Copyright (c) Microsoft Corporation. All Rights Reserved. +# + +function StringToMacAddress([String]$MacString) +{ + $zeroVal = [int]("0"[0]); + $aVal = [int]("A"[0]); + + $macArray = @(); + $byteOne = 0; + $byteTwo = 0; + + for($i = 0; $i -lt 6; $i++) + { + $byteChars = $MacString.ToCharArray(2*$i, 2); + + $byteOne = $byteChars[0] - $zeroVal; + $byteTwo = $byteChars[1] - $zeroVal; + + if($byteOne -gt 9) + { + $byteOne = $byteChars[0] - $aVal + 10; + } + if($byteTwo -gt 9) + { + $byteTwo = $byteChars[1] - $aVal + 10; + } + + $byteVal = 16*($byteOne) + ($byteTwo); + $macArray = $macArray + $byteVal; + } + + return $macArray +} + +Import-Module Hyper-V + +$vmArr = Get-VM +$switch = Get-VmSwitch -Name "CorpNet" +$policy = Get-VmSystemSwitchExtensionSwitchFeature -FeatureName "MSForwardExt Mac Address Policy" + +foreach($vm in $vmArr) +{ + $vmName = $vm.Name + $adapters = Get-VmNetworkAdapter -VmName $vmName + + foreach($adapter in $adapters) + { + Write-Host "Setting Policy for $vmName..." + $policy.SettingData.MacAddress = StringToMacAddress($adapter.MacAddress) + Add-VmSwitchExtensionSwitchFeature -VmSwitch $switch -VMSwitchExtensionFeature $policy + } +} diff --git a/network/ndis/extension/samples/forward/uninstall.cmd b/network/ndis/extension/samples/forward/uninstall.cmd new file mode 100644 index 00000000..fe8ffe28 --- /dev/null +++ b/network/ndis/extension/samples/forward/uninstall.cmd @@ -0,0 +1,3 @@ +powershell .\removeRoute.ps1 + +netcfg -u ms_forwardext \ No newline at end of file diff --git a/network/ndis/extension/samples/passthrough/MsPassthroughExt.c b/network/ndis/extension/samples/passthrough/MsPassthroughExt.c new file mode 100644 index 00000000..e99eeac5 --- /dev/null +++ b/network/ndis/extension/samples/passthrough/MsPassthroughExt.c @@ -0,0 +1,597 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved. + +Module Name: + + MsPassthroughExt.c + +Abstract: + + This file contains the implementation of a passthrough filter + utilizing SxBase.lib. + + +--*/ + +#include "precomp.h" + +UCHAR SxExtMajorNdisVersion = NDIS_FILTER_MAJOR_VERSION; +UCHAR SxExtMinorNdisVersion = NDIS_FILTER_MINOR_VERSION; +PWCHAR SxExtFriendlyName = L"Microsoft Sample Passthrough Extension"; +PWCHAR SxExtUniqueName = L"{2A06F1CB-1B9B-43A8-ADF7-B7D44A9EE71C}"; +PWCHAR SxExtServiceName = L"MsPassthroughExt"; +ULONG SxExtAllocationTag = 'tPsM'; +ULONG SxExtOidRequestId = 'tPsM'; + + +NDIS_STATUS +SxExtInitialize() +{ + return NDIS_STATUS_SUCCESS; +} + + +VOID +SxExtUninitialize() +{ + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtCreateSwitch( + PSX_SWITCH_OBJECT Switch, + PNDIS_HANDLE *ExtensionContext + ) +{ + UNREFERENCED_PARAMETER(Switch); + + *ExtensionContext = NULL; + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtDeleteSwitch( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + + return; +} + + +_Use_decl_annotations_ +VOID +SxExtActivateSwitch( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtRestartSwitch( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtPauseSwitch( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtCreatePort( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PARAMETERS Port + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Port); + + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtUpdatePort( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PARAMETERS Port + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Port); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtCreateNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_PARAMETERS Nic + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Nic); + + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtConnectNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_PARAMETERS Nic + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Nic); + + return; +} + + +_Use_decl_annotations_ +VOID +SxExtUpdateNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_PARAMETERS Nic + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Nic); + + return; +} + + +_Use_decl_annotations_ +VOID +SxExtDisconnectNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_PARAMETERS Nic + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Nic); + + return; +} + + +_Use_decl_annotations_ +VOID +SxExtDeleteNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_PARAMETERS Nic + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Nic); + + return; +} + + +_Use_decl_annotations_ +VOID +SxExtTeardownPort( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PARAMETERS Port + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Port); + + return; +} + + +_Use_decl_annotations_ +VOID +SxExtDeletePort( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PARAMETERS Port + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(Port); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtSaveNic( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_SAVE_STATE SaveState, + PULONG BytesWritten, + PULONG BytesNeeded + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SaveState); + + *BytesWritten = 0; + *BytesNeeded = 0; + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtSaveNicComplete( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_SAVE_STATE SaveState + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SaveState); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtNicRestore( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_SAVE_STATE SaveState, + PULONG BytesRestored + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SaveState); + + *BytesRestored = 0; + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtNicRestoreComplete( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_NIC_SAVE_STATE SaveState + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SaveState); + + return; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtAddSwitchProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PROPERTY_PARAMETERS SwitchProperty + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SwitchProperty); + + return NDIS_STATUS_NOT_SUPPORTED; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtUpdateSwitchProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PROPERTY_PARAMETERS SwitchProperty + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SwitchProperty); + + return NDIS_STATUS_NOT_SUPPORTED; +} + + +_Use_decl_annotations_ +BOOLEAN +SxExtDeleteSwitchProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PROPERTY_DELETE_PARAMETERS SwitchProperty + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SwitchProperty); + + return FALSE; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtAddPortProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PROPERTY_PARAMETERS PortProperty + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(PortProperty); + + return NDIS_STATUS_NOT_SUPPORTED; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtUpdatePortProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PROPERTY_PARAMETERS PortProperty + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(PortProperty); + + return NDIS_STATUS_NOT_SUPPORTED; +} + + +_Use_decl_annotations_ +BOOLEAN +SxExtDeletePortProperty( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_PROPERTY_DELETE_PARAMETERS PortProperty + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(PortProperty); + + return FALSE; +} + + +_Use_decl_annotations_ +BOOLEAN +SxExtQuerySwitchFeatureStatus( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_FEATURE_STATUS_PARAMETERS SwitchFeatureStatus, + PULONG BytesNeeded + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(SwitchFeatureStatus); + UNREFERENCED_PARAMETER(BytesNeeded); + + return FALSE; +} + + +_Use_decl_annotations_ +BOOLEAN +SxExtQueryPortFeatureStatus( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_SWITCH_PORT_FEATURE_STATUS_PARAMETERS PortFeatureStatus, + PULONG BytesNeeded + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(PortFeatureStatus); + UNREFERENCED_PARAMETER(BytesNeeded); + + return FALSE; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtProcessNicRequest( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_OID_REQUEST OidRequest, + PNDIS_SWITCH_PORT_ID SourcePortId, + PNDIS_SWITCH_NIC_INDEX SourceNicIndex, + PNDIS_SWITCH_PORT_ID DestinationPortId, + PNDIS_SWITCH_NIC_INDEX DestinationNicIndex + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(OidRequest); + UNREFERENCED_PARAMETER(SourcePortId); + UNREFERENCED_PARAMETER(SourceNicIndex); + UNREFERENCED_PARAMETER(DestinationPortId); + UNREFERENCED_PARAMETER(DestinationNicIndex); + + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtProcessNicRequestComplete( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_OID_REQUEST OidRequest, + NDIS_SWITCH_PORT_ID SourcePortId, + NDIS_SWITCH_NIC_INDEX SourceNicIndex, + NDIS_SWITCH_PORT_ID DestinationPortId, + NDIS_SWITCH_NIC_INDEX DestinationNicIndex, + NDIS_STATUS Status + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(OidRequest); + UNREFERENCED_PARAMETER(SourcePortId); + UNREFERENCED_PARAMETER(SourceNicIndex); + UNREFERENCED_PARAMETER(DestinationPortId); + UNREFERENCED_PARAMETER(DestinationNicIndex); + UNREFERENCED_PARAMETER(Status); + + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +NDIS_STATUS +SxExtProcessNicStatus( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNDIS_STATUS_INDICATION StatusIndication, + NDIS_SWITCH_PORT_ID SourcePortId, + NDIS_SWITCH_NIC_INDEX SourceNicIndex + ) +{ + UNREFERENCED_PARAMETER(Switch); + UNREFERENCED_PARAMETER(ExtensionContext); + UNREFERENCED_PARAMETER(StatusIndication); + UNREFERENCED_PARAMETER(SourcePortId); + UNREFERENCED_PARAMETER(SourceNicIndex); + + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +VOID +SxExtStartNetBufferListsIngress( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG SendFlags + ) +{ + UNREFERENCED_PARAMETER(ExtensionContext); + + SxLibSendNetBufferListsIngress(Switch, + NetBufferLists, + SendFlags, + 0); +} + + +_Use_decl_annotations_ +VOID +SxExtStartNetBufferListsEgress( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG NumberOfNetBufferLists, + ULONG ReceiveFlags + ) +{ + UNREFERENCED_PARAMETER(ExtensionContext); + + SxLibSendNetBufferListsEgress(Switch, + NetBufferLists, + NumberOfNetBufferLists, + ReceiveFlags); +} + + +_Use_decl_annotations_ +VOID +SxExtStartCompleteNetBufferListsEgress( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG ReturnFlags + ) +{ + UNREFERENCED_PARAMETER(ExtensionContext); + + SxLibCompleteNetBufferListsEgress(Switch, + NetBufferLists, + ReturnFlags); +} + + +_Use_decl_annotations_ +VOID +SxExtStartCompleteNetBufferListsIngress( + PSX_SWITCH_OBJECT Switch, + NDIS_HANDLE ExtensionContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG SendCompleteFlags + ) +{ + UNREFERENCED_PARAMETER(ExtensionContext); + + SxLibCompleteNetBufferListsIngress(Switch, + NetBufferLists, + SendCompleteFlags); +} + diff --git a/network/ndis/extension/samples/passthrough/MsPassthroughExt.rc b/network/ndis/extension/samples/passthrough/MsPassthroughExt.rc new file mode 100644 index 00000000..c76ed707 --- /dev/null +++ b/network/ndis/extension/samples/passthrough/MsPassthroughExt.rc @@ -0,0 +1,24 @@ +// +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// + +#include +#include + +/*-----------------------------------------------*/ +/* the following lines are specific to this file */ +/*-----------------------------------------------*/ + +/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR + * and VER_INTERNALNAME_STR must be defined before including COMMON.VER + * The strings don't need a '\0', since common.ver has them. + */ +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "Microsoft Sample Passthrough Extension" +#define VER_INTERNALNAME_STR "mspassthroughext.SYS" +#define VER_ORIGINALFILENAME_STR "mspassthroughext.SYS" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/network/ndis/extension/samples/passthrough/install.cmd b/network/ndis/extension/samples/passthrough/install.cmd new file mode 100644 index 00000000..0bacc039 --- /dev/null +++ b/network/ndis/extension/samples/passthrough/install.cmd @@ -0,0 +1 @@ +netcfg -l .\MSPassthroughExt.inf -c s -i ms_passthroughext \ No newline at end of file diff --git a/network/ndis/extension/samples/passthrough/mspassthroughext.inf b/network/ndis/extension/samples/passthrough/mspassthroughext.inf new file mode 100644 index 00000000..6a6b941d --- /dev/null +++ b/network/ndis/extension/samples/passthrough/mspassthroughext.inf @@ -0,0 +1,89 @@ +; +; Copyright (c) Microsoft Corporation. All Rights Reserved. +; + +[version] +Signature = "$Windows NT$" +Class = NetService +ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} +Provider = %Msft% +CatalogFile = mspassthroughext.cat +DriverVer = 08/29/2011,1.0 + +[Manufacturer] +%Msft%=MSFT,NTx86,NTia64,NTamd64 + +[MSFT.NTx86] +%MSPassthroughExt_Desc%=Install, MS_passthroughext + +[MSFT.NTia64] +%MSPassthroughExt_Desc%=Install, MS_passthroughext + +[MSFT.NTamd64] +%MSPassthroughExt_Desc%=Install, MS_passthroughext + +;------------------------------------------------------------------------- +; Installation Section +;------------------------------------------------------------------------- +[Install] +AddReg=Inst_Ndi +Characteristics=0x40000 +NetCfgInstanceId="{2A06F1CB-1B9B-43A8-ADF7-B7D44A9EE71C}" +Copyfiles = MSPassthroughExt.copyfiles.sys + +[SourceDisksNames] +1=%MSPassthroughExt_Desc%,"",, + +[SourceDisksFiles] +MSPassthroughExt.sys=1 + +[DestinationDirs] +DefaultDestDir=12 +MSPassthroughExt.copyfiles.sys=12 + +[MSPassthroughExt.copyfiles.sys] +MSPassthroughExt.sys,,,2 + + +;------------------------------------------------------------------------- +; Ndi installation support +;------------------------------------------------------------------------- +[Inst_Ndi] +HKR, Ndi,Service,,"MSPassthroughExt" +HKR, Ndi,CoServices,0x00010000,"MSPassthroughExt" +HKR, Ndi,HelpText,,%MSPassthroughExt_HelpText% +HKR, Ndi,FilterClass,,"ms_switch_filter" +HKR, Ndi,FilterType,0x00010001,0x00000002 +HKR, Ndi\Interfaces,UpperRange,,"noupper" +HKR, Ndi\Interfaces,LowerRange,,"nolower" +HKR, Ndi\Interfaces, FilterMediaTypes,,"vmnetextension" +HKR, Ndi,FilterRunType, 0x00010001, 2 ; optional + +;------------------------------------------------------------------------- +; Service installation support, common.EventLog here is to demonstrate how to +; write an enent log +;------------------------------------------------------------------------- +[Install.Services] +AddService=MSPassthroughExt,,MSPassthroughExt_Service_Inst;, common.EventLog + +[MSPassthroughExt_Service_Inst] +DisplayName = %MSPassthroughExt_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 1 ;SERVICE_SYSTEM_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\MSPassthroughExt.sys +LoadOrderGroup = NDIS +Description = %MSPassthroughExt_Desc% +AddReg = Common.Params.reg + +[Install.Remove.Services] +DelService=MSPassthroughExt,0x200 + +[Strings] +Msft = "Microsoft" +MSPassthroughExt_Desc = "Microsoft Sample Passthrough Extension" +MSPassthroughExt_HelpText = "Sample for filtering extension using SxBase" + + + + diff --git a/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj b/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj new file mode 100644 index 00000000..cda7cbbf --- /dev/null +++ b/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj @@ -0,0 +1,127 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {2339B4A8-60AF-4172-AC68-3159E6D001EC} + $(MSBuildProjectName) + Debug + x64 + {DA3F14BC-0746-46C8-99E7-339173543E27} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + mspassthroughext + + + mspassthroughext + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;.\..\..\base\$(IntDir)\sxbase.lib + + + true + Level4 + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;.\..\..\base\$(IntDir)\sxbase.lib + + + true + Level4 + + + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj.Filters b/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj.Filters new file mode 100644 index 00000000..b43daf1e --- /dev/null +++ b/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj.Filters @@ -0,0 +1,34 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {555B7D63-D8B9-4382-8D6E-A97AC09CE282} + + + h;hpp;hxx;hm;inl;inc;xsd + {797B07B8-0B76-4BA6-BDF4-45786B031900} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {D95F4A4C-E2E8-4108-86FB-C568FA437FA1} + + + inf;inv;inx;mof;mc; + {F05BF5D9-0765-4293-B2AB-41B06C544956} + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/extension/samples/passthrough/precomp.h b/network/ndis/extension/samples/passthrough/precomp.h new file mode 100644 index 00000000..ca260479 --- /dev/null +++ b/network/ndis/extension/samples/passthrough/precomp.h @@ -0,0 +1,11 @@ +// +// Copyright (c) Microsoft Corporation. All Rights Reserved. +// + +#include +#include +#include +#include +#include "..\..\base\SxBase.h" +#include "..\..\base\SxApi.h" +#include "..\..\base\SxLibrary.h" \ No newline at end of file diff --git a/network/ndis/extension/samples/passthrough/precompsrc.c b/network/ndis/extension/samples/passthrough/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/network/ndis/extension/samples/passthrough/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/network/ndis/extension/samples/passthrough/uninstall.cmd b/network/ndis/extension/samples/passthrough/uninstall.cmd new file mode 100644 index 00000000..369b6665 --- /dev/null +++ b/network/ndis/extension/samples/passthrough/uninstall.cmd @@ -0,0 +1 @@ +netcfg -u ms_passthroughext \ No newline at end of file diff --git a/network/ndis/filter/ReadMe.md b/network/ndis/filter/ReadMe.md new file mode 100644 index 00000000..4202c41a --- /dev/null +++ b/network/ndis/filter/ReadMe.md @@ -0,0 +1,117 @@ +NDIS 6.0 Filter Driver +====================== + +The Ndislwf sample is a do-nothing pass-through NDIS 6 filter driver that demonstrates the basic principles underlying an NDIS 6.0 Filter driver. The sample replaces the NDIS 5 Sample Intermediate Driver (Passthru driver). + +Although this sample filter driver is installed as a modifying filter driver, it doesn't modify any packets; it only repackages and sends down all OID requests. You can modify this filter driver to change packets before passing them along. Or you can use the filter to originate new packets to send or receive. For example, the filter could encrypt/compress outgoing and decrypt/decompress incoming data. + + +For more information, see [NDIS Filter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff565492) in the network devices design guide. + + +Automatic deployment +-------------------- + +Before you automatically deploy a driver, you must provision the target computer. For instructions, see [Configuring a Computer for Driver Deployment, Testing, and Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/). After you have provisioned the target computer, continue with these steps: + +1. On the host computer, in **Visual Studio**, in **Solution Explorer**, right click **package** (lower case), and choose **Properties**. Navigate to **Configuration Properties \> Driver Install \> Deployment**. +2. Check **Enable deployment**, and check **Remove previous driver versions before deployment**. For **Target Computer Name**, select the name of a target computer that you provisioned previously. Select **Do not install**. Click **OK**. +3. On the **Build** menu, choose **Build Solution**. +4. On the target computer, open **Control Panel**. Click **Network and Internet** and then open **Network and Sharing Center**. +5. Under **View your active networks**, click the connection listed under **Connections:** and click **Properties**. If you have previously installed this sample, highlight it in the list. +6. Click **Install**, then **Service**, then **Add**. + **Note**  You may see multiple instances of the **NDIS Sample LightWeight Filter** service. If so, highlight the newest one. +7. Click **Have Disk**. +8. In the **Install from Disk** dialog, browse to the DriverTest\\Drivers directory. Highlight the netlwf.inf file and click **Open**, then click OK. This should show **NDIS Sample LightWeight Filter** in a list of **Network Services**. Highlight **NDIS Sample LightWeight Filter** and click **OK**. Click **OK**. Click **Close**. Click **Close**. This installs the Ndislwf filter driver service. + +**Note**   + +If you've installed the Ndislwf sample on the target computer before, you can use the [PnPUtil](http://msdn.microsoft.com/en-us/library/windows/hardware/ff550419) tool to delete the older versions from the driver store. + +Manual deployment +----------------- + +Before you manually deploy a driver, you must turn on test signing and install a certificate on the target computer. You also need to copy the [DevCon](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544707) tool to the target computer. For instructions, see [Preparing a Computer for Manual Driver Deployment](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265571). + +Ndislwf is installed as a service (called **NDIS Sample LightWeight Filter** in the supplied INF). To install it, do the following: + +1. Prepare an installation directory on the target computer and copy these files from the host computer into the directory: + netlwf.cat + netlwf.inf + ndislwf.sys +2. Open **Control Panel**. +3. Click **Network and Internet** and then open **Network and Sharing Center**. Under **View your active networks**, click the connection listed under **Connections**: and click **Properties**. +4. If you have previously installed this sample, highlight it in the list. +5. Click **Install**, then **Service**, then **Add**, then **Have Disk**. +6. Browse to the installation directory. Highlight the netlwf.inf file and click **Open**, then click OK. This should show **NDIS Sample LightWeight Filter** in a list of Network Services. Highlight this and click OK. Click OK. This installs the Ndislwf filter driver. + +**Note**   + +If you've installed the Ndislwf sample on the target computer before, you can use the [PnPUtil](http://msdn.microsoft.com/en-us/library/windows/hardware/ff550419) tool to delete the older versions from the driver store. + +Viewing sample output in the debugger +------------------------------------- + +### Setting up kernel-mode debugging automatically + +If you chose to deploy your driver automatically, then kernel debugging is already set up for you. + +On the host computer, in **Visual Studio**, in the **Debug** menu, choose **Attach to Process**. For **Transport**, choose **Windows Kernel Mode Debugger**. For **Qualifier**, choose the name of your target computer. Click **Attach**. + +**Note**  If you see a dialog box that asks you to allow the debugger to communicate through the firewall, click the boxes for all types of networks. Click **Allow Access**. + +For more information, see [Setting Up Kernel-Mode Debugging in Visual Studio](http://msdn.microsoft.com/en-us/library/windows/hardware/hh439376). + +### Setting up kernel-mode debugging manually + +If you chose to deploy your driver manually, then you need to set up kernel debugging manually. For instructions, see [Setting Up Kernel-Mode Debugging Manually](http://msdn.microsoft.com/en-us/library/windows/hardware/hh439378). + +The kernel-mode debuggers (WinDbg.exe and Kd.exe) are included in the WDK. + +On the host computer, locate and open a kernel-mode debugger (example: c:\\Program Files (x86)\\Windows Kits\\8.1\\Debuggers\\x64\\windbg.exe). Establish a kernel-mode debugging session between the host and target computers. The details of how to do this depend on the type of debug cable you are using. For information about how to start a debugging session, see [Setting Up Kernel-Mode Debugging Manually](http://msdn.microsoft.com/en-us/library/windows/hardware/hh439378). + +Setting kd\_default\_mask +------------------------- + +This sample calls [**DbgPrint**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff543632) to send trace messages to the kernel-mode debugger. To see the trace messages, you must set the value of the **kd\_default\_mask** variable. + +On the host computer, break in to the debugger if you are not already broken in. (In the **Debug** menu, choose **Break** or **Break All**, or press **CTRL-Break**). At the debugger command line, enter this command: **ed kd\_default\_mask 0x8**. + +To resume execution of the target computer, enter the **g** command in the debugger. (In the **Debug** menu, choose **Continue**.) + +Viewing trace messages +---------------------- + +On the host computer, in the kernel-mode debugger, verify that you see trace messages similar to these: + + +++ + + + + + +
NDISLWF: ===>DriverEntry...
+NDISLWF: ===>FilterRegisterOptions
+NDISLWF: <===FilterRegisterOptions
+NDISLWF: ==>FilterRegisterDevice
+NDISLWF: <==FilterRegisterDevice: 0
+NDISLWF: <===DriverEntry, Status =        0
+NDISLWF: ===>FilterAttach: NdisFilterHandle FFFFE00000F73650
+NDISLWF: <===FilterAttach:    Status 0
+ +What the Ndislwf sample driver does: +------------------------------------ + +1. During [*DriverEntry*](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544113), the ndislwf driver registers as an NDIS 6 filter driver. +2. Later on, NDIS calls Ndislwf's [*FilterAttach*](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549905) handler, for each underlying NDIS adapter on which it is configured to attach. +3. In the context of [*FilterAttach*](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549905) Handler, the filter driver calls [**NdisFSetAttributes**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562619) to register its filter module context with NDIS. After that, the filter driver can read its own setting in registry by calling [**NdisOpenConfigurationEx**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563717), and call other `NdisXxx` functions. +4. After [*FilterAttach*](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549905) successfully returns, NDIS restarts the filter later by calling its [*FilterRestart*](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549962) handler. *FilterRestart* should prepare to handle send/receive data. After restart return successfully, filter driver should be able to process send/receive. +5. All requests and sends coming from overlying drivers for the Ndislwf filter driver are repackaged if necessary and sent down to NDIS, to be passed to the underlying NDIS driver. +6. All indications arriving from an underlying NDIS driver are forwarded up by Ndislwf filter driver. +7. NDIS calls the filter's [*FilterPause*](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549957) handler when NDIS needs to detach the filter from the stack or there is some configuration changes in the stack. In processing the pause request from NDIS, the Ndislwf driver waits for all its own outstanding requests to be completed before it completes the pause request. +8. NDIS calls the Ndislwf driver’s [*FilterDetach*](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549918) entry point when NDIS needs to detach a filter module from NDIS stack. The *FilterDetach* handler should free all the memory allocation done in [*FilterAttach*](http://msdn.microsoft.com/en-us/library/windows/hardware/ff549905), and undo the operations it did in *FilterAttach* Handler. + + diff --git a/network/ndis/filter/device.c b/network/ndis/filter/device.c new file mode 100644 index 00000000..48a04876 --- /dev/null +++ b/network/ndis/filter/device.c @@ -0,0 +1,289 @@ +/*++ + * + * The file contains the routines to create a device and handle ioctls + * +-- */ + +#include "precomp.h" + + +#pragma NDIS_INIT_FUNCTION(FilterRegisterDevice) + + +_IRQL_requires_max_(PASSIVE_LEVEL) +NDIS_STATUS +FilterRegisterDevice( + VOID + ) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + UNICODE_STRING DeviceName; + UNICODE_STRING DeviceLinkUnicodeString; + PDRIVER_DISPATCH DispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1]; + NDIS_DEVICE_OBJECT_ATTRIBUTES DeviceAttribute; + PFILTER_DEVICE_EXTENSION FilterDeviceExtension; + + DEBUGP(DL_TRACE, "==>FilterRegisterDevice\n"); + + NdisZeroMemory(DispatchTable, (IRP_MJ_MAXIMUM_FUNCTION+1) * sizeof(PDRIVER_DISPATCH)); + + DispatchTable[IRP_MJ_CREATE] = FilterDispatch; + DispatchTable[IRP_MJ_CLEANUP] = FilterDispatch; + DispatchTable[IRP_MJ_CLOSE] = FilterDispatch; + DispatchTable[IRP_MJ_DEVICE_CONTROL] = FilterDeviceIoControl; + + + NdisInitUnicodeString(&DeviceName, NTDEVICE_STRING); + NdisInitUnicodeString(&DeviceLinkUnicodeString, LINKNAME_STRING); + + // + // Create a device object and register our dispatch handlers + // + NdisZeroMemory(&DeviceAttribute, sizeof(NDIS_DEVICE_OBJECT_ATTRIBUTES)); + + DeviceAttribute.Header.Type = NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES; + DeviceAttribute.Header.Revision = NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1; + DeviceAttribute.Header.Size = sizeof(NDIS_DEVICE_OBJECT_ATTRIBUTES); + + DeviceAttribute.DeviceName = &DeviceName; + DeviceAttribute.SymbolicName = &DeviceLinkUnicodeString; + DeviceAttribute.MajorFunctions = &DispatchTable[0]; + DeviceAttribute.ExtensionSize = sizeof(FILTER_DEVICE_EXTENSION); + + Status = NdisRegisterDeviceEx( + FilterDriverHandle, + &DeviceAttribute, + &DeviceObject, + &NdisFilterDeviceHandle + ); + + + if (Status == NDIS_STATUS_SUCCESS) + { + FilterDeviceExtension = NdisGetDeviceReservedExtension(DeviceObject); + + FilterDeviceExtension->Signature = 'FTDR'; + FilterDeviceExtension->Handle = FilterDriverHandle; + } + + + DEBUGP(DL_TRACE, "<==FilterRegisterDevice: %x\n", Status); + + return (Status); + +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +FilterDeregisterDevice( + VOID + ) + +{ + if (NdisFilterDeviceHandle != NULL) + { + NdisDeregisterDeviceEx(NdisFilterDeviceHandle); + } + + NdisFilterDeviceHandle = NULL; + +} + +_Use_decl_annotations_ +NTSTATUS +FilterDispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +{ + PIO_STACK_LOCATION IrpStack; + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(DeviceObject); + + IrpStack = IoGetCurrentIrpStackLocation(Irp); + + switch (IrpStack->MajorFunction) + { + case IRP_MJ_CREATE: + break; + + case IRP_MJ_CLEANUP: + break; + + case IRP_MJ_CLOSE: + break; + + default: + break; + } + + Irp->IoStatus.Status = Status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +FilterDeviceIoControl( + PDEVICE_OBJECT DeviceObject, + PIRP Irp + ) +{ + PIO_STACK_LOCATION IrpSp; + NTSTATUS Status = STATUS_SUCCESS; + PFILTER_DEVICE_EXTENSION FilterDeviceExtension; + PUCHAR InputBuffer; + PUCHAR OutputBuffer; + ULONG InputBufferLength, OutputBufferLength; + PLIST_ENTRY Link; + PUCHAR pInfo; + ULONG InfoLength = 0; + PMS_FILTER pFilter = NULL; + BOOLEAN bFalse = FALSE; + + + UNREFERENCED_PARAMETER(DeviceObject); + + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + + if (IrpSp->FileObject == NULL) + { + return(STATUS_UNSUCCESSFUL); + } + + + FilterDeviceExtension = (PFILTER_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(DeviceObject); + + ASSERT(FilterDeviceExtension->Signature == 'FTDR'); + + Irp->IoStatus.Information = 0; + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) + { + + case IOCTL_FILTER_RESTART_ALL: + break; + + case IOCTL_FILTER_RESTART_ONE_INSTANCE: + InputBuffer = OutputBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer; + InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + pFilter = filterFindFilterModule (InputBuffer, InputBufferLength); + + if (pFilter == NULL) + { + + break; + } + + NdisFRestartFilter(pFilter->FilterHandle); + + break; + + case IOCTL_FILTER_ENUERATE_ALL_INSTANCES: + + InputBuffer = OutputBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer; + InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + + pInfo = OutputBuffer; + + FILTER_ACQUIRE_LOCK(&FilterListLock, bFalse); + + Link = FilterModuleList.Flink; + + while (Link != &FilterModuleList) + { + pFilter = CONTAINING_RECORD(Link, MS_FILTER, FilterModuleLink); + + + InfoLength += (pFilter->FilterModuleName.Length + sizeof(USHORT)); + + if (InfoLength <= OutputBufferLength) + { + *(PUSHORT)pInfo = pFilter->FilterModuleName.Length; + NdisMoveMemory(pInfo + sizeof(USHORT), + (PUCHAR)(pFilter->FilterModuleName.Buffer), + pFilter->FilterModuleName.Length); + + pInfo += (pFilter->FilterModuleName.Length + sizeof(USHORT)); + } + + Link = Link->Flink; + } + + FILTER_RELEASE_LOCK(&FilterListLock, bFalse); + if (InfoLength <= OutputBufferLength) + { + + Status = NDIS_STATUS_SUCCESS; + } + // + // Buffer is small + // + else + { + Status = STATUS_BUFFER_TOO_SMALL; + } + break; + + + default: + break; + } + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = InfoLength; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return Status; + + +} + + +_IRQL_requires_max_(DISPATCH_LEVEL) +PMS_FILTER +filterFindFilterModule( + _In_reads_bytes_(BufferLength) + PUCHAR Buffer, + _In_ ULONG BufferLength + ) +{ + + PMS_FILTER pFilter; + PLIST_ENTRY Link; + BOOLEAN bFalse = FALSE; + + FILTER_ACQUIRE_LOCK(&FilterListLock, bFalse); + + Link = FilterModuleList.Flink; + + while (Link != &FilterModuleList) + { + pFilter = CONTAINING_RECORD(Link, MS_FILTER, FilterModuleLink); + + if (BufferLength >= pFilter->FilterModuleName.Length) + { + if (NdisEqualMemory(Buffer, pFilter->FilterModuleName.Buffer, pFilter->FilterModuleName.Length)) + { + FILTER_RELEASE_LOCK(&FilterListLock, bFalse); + return pFilter; + } + } + + Link = Link->Flink; + } + + FILTER_RELEASE_LOCK(&FilterListLock, bFalse); + return NULL; +} + + + + diff --git a/network/ndis/filter/filter.c b/network/ndis/filter/filter.c new file mode 100644 index 00000000..0ab6f1ab --- /dev/null +++ b/network/ndis/filter/filter.c @@ -0,0 +1,1903 @@ +/*++ + +Copyright (c) Microsoft Corporation + +Module Name: + + Filter.c + +Abstract: + + Sample NDIS Lightweight filter driver + +--*/ + +#include "precomp.h" + +#define __FILENUMBER 'PNPF' + +// This directive puts the DriverEntry function into the INIT segment of the +// driver. To conserve memory, the code will be discarded when the driver's +// DriverEntry function returns. You can declare other functions used only +// during initialization here. +#pragma NDIS_INIT_FUNCTION(DriverEntry) + +// +// Global variables +// +NDIS_HANDLE FilterDriverHandle; // NDIS handle for filter driver +NDIS_HANDLE FilterDriverObject; +NDIS_HANDLE NdisFilterDeviceHandle = NULL; +PDEVICE_OBJECT DeviceObject = NULL; + +FILTER_LOCK FilterListLock; +LIST_ENTRY FilterModuleList; + +NDIS_FILTER_PARTIAL_CHARACTERISTICS DefaultChars = { +{ 0, 0, 0}, + 0, + FilterSendNetBufferLists, + FilterSendNetBufferListsComplete, + NULL, + FilterReceiveNetBufferLists, + FilterReturnNetBufferLists +}; + + +_Use_decl_annotations_ +NTSTATUS +DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + First entry point to be called, when this driver is loaded. + Register with NDIS as a filter driver and create a device + for communication with user-mode. + +Arguments: + + DriverObject - pointer to the system's driver object structure + for this driver + + RegistryPath - system's registry path for this driver + +Return Value: + + STATUS_SUCCESS if all initialization is successful, STATUS_XXX + error code if not. + +--*/ +{ + NDIS_STATUS Status; + NDIS_FILTER_DRIVER_CHARACTERISTICS FChars; + NDIS_STRING ServiceName = RTL_CONSTANT_STRING(FILTER_SERVICE_NAME); + NDIS_STRING UniqueName = RTL_CONSTANT_STRING(FILTER_UNIQUE_NAME); + NDIS_STRING FriendlyName = RTL_CONSTANT_STRING(FILTER_FRIENDLY_NAME); + BOOLEAN bFalse = FALSE; + + UNREFERENCED_PARAMETER(RegistryPath); + + DEBUGP(DL_TRACE, "===>DriverEntry...\n"); + + FilterDriverObject = DriverObject; + + do + { + NdisZeroMemory(&FChars, sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS)); + FChars.Header.Type = NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS; + FChars.Header.Size = sizeof(NDIS_FILTER_DRIVER_CHARACTERISTICS); +#if NDIS_SUPPORT_NDIS61 + FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_2; +#else + FChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_1; +#endif + + FChars.MajorNdisVersion = FILTER_MAJOR_NDIS_VERSION; + FChars.MinorNdisVersion = FILTER_MINOR_NDIS_VERSION; + FChars.MajorDriverVersion = 1; + FChars.MinorDriverVersion = 0; + FChars.Flags = 0; + + FChars.FriendlyName = FriendlyName; + FChars.UniqueName = UniqueName; + FChars.ServiceName = ServiceName; + + // + // TODO: Most handlers are optional, however, this sample includes them + // all for illustrative purposes. If you do not need a particular + // handler, set it to NULL and NDIS will more efficiently pass the + // operation through on your behalf. + // + FChars.SetOptionsHandler = FilterRegisterOptions; + FChars.AttachHandler = FilterAttach; + FChars.DetachHandler = FilterDetach; + FChars.RestartHandler = FilterRestart; + FChars.PauseHandler = FilterPause; + FChars.SetFilterModuleOptionsHandler = FilterSetModuleOptions; + FChars.OidRequestHandler = FilterOidRequest; + FChars.OidRequestCompleteHandler = FilterOidRequestComplete; + FChars.CancelOidRequestHandler = FilterCancelOidRequest; + + FChars.SendNetBufferListsHandler = FilterSendNetBufferLists; + FChars.ReturnNetBufferListsHandler = FilterReturnNetBufferLists; + FChars.SendNetBufferListsCompleteHandler = FilterSendNetBufferListsComplete; + FChars.ReceiveNetBufferListsHandler = FilterReceiveNetBufferLists; + FChars.DevicePnPEventNotifyHandler = FilterDevicePnPEventNotify; + FChars.NetPnPEventHandler = FilterNetPnPEvent; + FChars.StatusHandler = FilterStatus; + FChars.CancelSendNetBufferListsHandler = FilterCancelSendNetBufferLists; + + DriverObject->DriverUnload = FilterUnload; + + FilterDriverHandle = NULL; + + // + // Initialize spin locks + // + FILTER_INIT_LOCK(&FilterListLock); + + InitializeListHead(&FilterModuleList); + + Status = NdisFRegisterFilterDriver(DriverObject, + (NDIS_HANDLE)FilterDriverObject, + &FChars, + &FilterDriverHandle); + if (Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_WARN, "Register filter driver failed.\n"); + break; + } + + Status = FilterRegisterDevice(); + + if (Status != NDIS_STATUS_SUCCESS) + { + NdisFDeregisterFilterDriver(FilterDriverHandle); + FILTER_FREE_LOCK(&FilterListLock); + DEBUGP(DL_WARN, "Register device for the filter driver failed.\n"); + break; + } + + + } + while(bFalse); + + + DEBUGP(DL_TRACE, "<===DriverEntry, Status = %8x\n", Status); + return Status; + +} + +_Use_decl_annotations_ +NDIS_STATUS +FilterRegisterOptions( + NDIS_HANDLE NdisFilterDriverHandle, + NDIS_HANDLE FilterDriverContext + ) +/*++ + +Routine Description: + + Register optional handlers with NDIS. This sample does not happen to + have any optional handlers to register, so this routine does nothing + and could simply have been omitted. However, for illustrative purposes, + it is presented here. + +Arguments: + + NdisFilterDriverHandle - pointer the driver handle received from + NdisFRegisterFilterDriver + + FilterDriverContext - pointer to our context passed into + NdisFRegisterFilterDriver + +Return Value: + + NDIS_STATUS_SUCCESS + +--*/ +{ + DEBUGP(DL_TRACE, "===>FilterRegisterOptions\n"); + + ASSERT(NdisFilterDriverHandle == FilterDriverHandle); + ASSERT(FilterDriverContext == (NDIS_HANDLE)FilterDriverObject); + + if ((NdisFilterDriverHandle != (NDIS_HANDLE)FilterDriverHandle) || + (FilterDriverContext != (NDIS_HANDLE)FilterDriverObject)) + { + return NDIS_STATUS_INVALID_PARAMETER; + } + + DEBUGP(DL_TRACE, "<===FilterRegisterOptions\n"); + + return NDIS_STATUS_SUCCESS; +} + + +_Use_decl_annotations_ +NDIS_STATUS +FilterAttach( + NDIS_HANDLE NdisFilterHandle, + NDIS_HANDLE FilterDriverContext, + PNDIS_FILTER_ATTACH_PARAMETERS AttachParameters + ) +/*++ + +Routine Description: + + Filter attach routine. + Create filter's context, allocate NetBufferLists and NetBuffer pools and any + other resources, and read configuration if needed. + +Arguments: + + NdisFilterHandle - Specify a handle identifying this instance of the filter. FilterAttach + should save this handle. It is a required parameter in subsequent calls + to NdisFxxx functions. + FilterDriverContext - Filter driver context passed to NdisFRegisterFilterDriver. + + AttachParameters - attach parameters + +Return Value: + + NDIS_STATUS_SUCCESS: FilterAttach successfully allocated and initialize data structures + for this filter instance. + NDIS_STATUS_RESOURCES: FilterAttach failed due to insufficient resources. + NDIS_STATUS_FAILURE: FilterAttach could not set up this instance of this filter and it has called + NdisWriteErrorLogEntry with parameters specifying the reason for failure. + +N.B.: FILTER can use NdisRegisterDeviceEx to create a device, so the upper + layer can send Irps to the filter. + +--*/ +{ + PMS_FILTER pFilter = NULL; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NDIS_FILTER_ATTRIBUTES FilterAttributes; + ULONG Size; + BOOLEAN bFalse = FALSE; + + DEBUGP(DL_TRACE, "===>FilterAttach: NdisFilterHandle %p\n", NdisFilterHandle); + + do + { + ASSERT(FilterDriverContext == (NDIS_HANDLE)FilterDriverObject); + if (FilterDriverContext != (NDIS_HANDLE)FilterDriverObject) + { + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + // Verify the media type is supported. This is a last resort; the + // the filter should never have been bound to an unsupported miniport + // to begin with. If this driver is marked as a Mandatory filter (which + // is the default for this sample; see the INF file), failing to attach + // here will leave the network adapter in an unusable state. + // + // Your setup/install code should not bind the filter to unsupported + // media types. + if ((AttachParameters->MiniportMediaType != NdisMedium802_3) + && (AttachParameters->MiniportMediaType != NdisMediumWan) + && (AttachParameters->MiniportMediaType != NdisMediumWirelessWan)) + { + DEBUGP(DL_ERROR, "Unsupported media type.\n"); + + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + Size = sizeof(MS_FILTER) + + AttachParameters->FilterModuleGuidName->Length + + AttachParameters->BaseMiniportInstanceName->Length + + AttachParameters->BaseMiniportName->Length; + + pFilter = (PMS_FILTER)FILTER_ALLOC_MEM(NdisFilterHandle, Size); + if (pFilter == NULL) + { + DEBUGP(DL_WARN, "Failed to allocate context structure.\n"); + Status = NDIS_STATUS_RESOURCES; + break; + } + + NdisZeroMemory(pFilter, sizeof(MS_FILTER)); + + pFilter->FilterModuleName.Length = pFilter->FilterModuleName.MaximumLength = AttachParameters->FilterModuleGuidName->Length; + pFilter->FilterModuleName.Buffer = (PWSTR)((PUCHAR)pFilter + sizeof(MS_FILTER)); + NdisMoveMemory(pFilter->FilterModuleName.Buffer, + AttachParameters->FilterModuleGuidName->Buffer, + pFilter->FilterModuleName.Length); + + + + pFilter->MiniportFriendlyName.Length = pFilter->MiniportFriendlyName.MaximumLength = AttachParameters->BaseMiniportInstanceName->Length; + pFilter->MiniportFriendlyName.Buffer = (PWSTR)((PUCHAR)pFilter->FilterModuleName.Buffer + pFilter->FilterModuleName.Length); + NdisMoveMemory(pFilter->MiniportFriendlyName.Buffer, + AttachParameters->BaseMiniportInstanceName->Buffer, + pFilter->MiniportFriendlyName.Length); + + + pFilter->MiniportName.Length = pFilter->MiniportName.MaximumLength = AttachParameters->BaseMiniportName->Length; + pFilter->MiniportName.Buffer = (PWSTR)((PUCHAR)pFilter->MiniportFriendlyName.Buffer + + pFilter->MiniportFriendlyName.Length); + NdisMoveMemory(pFilter->MiniportName.Buffer, + AttachParameters->BaseMiniportName->Buffer, + pFilter->MiniportName.Length); + + pFilter->MiniportIfIndex = AttachParameters->BaseMiniportIfIndex; + // + // The filter should initialize TrackReceives and TrackSends properly. For this + // driver, since its default characteristic has both a send and a receive handler, + // these fields are initialized to TRUE. + // + pFilter->TrackReceives = TRUE; + pFilter->TrackSends = TRUE; + pFilter->FilterHandle = NdisFilterHandle; + + + NdisZeroMemory(&FilterAttributes, sizeof(NDIS_FILTER_ATTRIBUTES)); + FilterAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1; + FilterAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES); + FilterAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES; + FilterAttributes.Flags = 0; + + NDIS_DECLARE_FILTER_MODULE_CONTEXT(MS_FILTER); + Status = NdisFSetAttributes(NdisFilterHandle, + pFilter, + &FilterAttributes); + if (Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_WARN, "Failed to set attributes.\n"); + break; + } + + + pFilter->State = FilterPaused; + + FILTER_ACQUIRE_LOCK(&FilterListLock, bFalse); + InsertHeadList(&FilterModuleList, &pFilter->FilterModuleLink); + FILTER_RELEASE_LOCK(&FilterListLock, bFalse); + + } + while (bFalse); + + if (Status != NDIS_STATUS_SUCCESS) + { + if (pFilter != NULL) + { + FILTER_FREE_MEM(pFilter); + } + } + + DEBUGP(DL_TRACE, "<===FilterAttach: Status %x\n", Status); + return Status; +} + +_Use_decl_annotations_ +NDIS_STATUS +FilterPause( + NDIS_HANDLE FilterModuleContext, + PNDIS_FILTER_PAUSE_PARAMETERS PauseParameters + ) +/*++ + +Routine Description: + + Filter pause routine. + Complete all the outstanding sends and queued sends, + wait for all the outstanding recvs to be returned + and return all the queued receives. + +Arguments: + + FilterModuleContext - pointer to the filter context stucture + PauseParameters - additional information about the pause + +Return Value: + + NDIS_STATUS_SUCCESS if filter pauses successfully, NDIS_STATUS_PENDING + if not. No other return value is allowed (pause must succeed, eventually). + +N.B.: When the filter is in Pausing state, it can still process OID requests, + complete sending, and returning packets to NDIS, and also indicate status. + After this function completes, the filter must not attempt to send or + receive packets, but it may still process OID requests and status + indications. + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)(FilterModuleContext); + NDIS_STATUS Status; + BOOLEAN bFalse = FALSE; + + UNREFERENCED_PARAMETER(PauseParameters); + + DEBUGP(DL_TRACE, "===>NDISLWF FilterPause: FilterInstance %p\n", FilterModuleContext); + + // + // Set the flag that the filter is going to pause + // + FILTER_ASSERT(pFilter->State == FilterRunning); + + FILTER_ACQUIRE_LOCK(&pFilter->Lock, bFalse); + pFilter->State = FilterPausing; + FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); + + // + // Do whatever work is required to bring the filter into the Paused state. + // + // If you have diverted and queued any send or receive NBLs, return them + // now. + // + // If you send or receive original NBLs, stop doing that and wait for your + // NBLs to return to you now. + // + + + Status = NDIS_STATUS_SUCCESS; + + pFilter->State = FilterPaused; + + DEBUGP(DL_TRACE, "<===FilterPause: Status %x\n", Status); + return Status; +} + +_Use_decl_annotations_ +NDIS_STATUS +FilterRestart( + NDIS_HANDLE FilterModuleContext, + PNDIS_FILTER_RESTART_PARAMETERS RestartParameters + ) +/*++ + +Routine Description: + + Filter restart routine. + Start the datapath - begin sending and receiving NBLs. + +Arguments: + + FilterModuleContext - pointer to the filter context stucture. + RestartParameters - additional information about the restart operation. + +Return Value: + + NDIS_STATUS_SUCCESS: if filter restarts successfully + NDIS_STATUS_XXX: Otherwise. + +--*/ +{ + NDIS_STATUS Status; + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + NDIS_HANDLE ConfigurationHandle = NULL; + + + PNDIS_RESTART_GENERAL_ATTRIBUTES NdisGeneralAttributes; + PNDIS_RESTART_ATTRIBUTES NdisRestartAttributes; + NDIS_CONFIGURATION_OBJECT ConfigObject; + + DEBUGP(DL_TRACE, "===>FilterRestart: FilterModuleContext %p\n", FilterModuleContext); + + FILTER_ASSERT(pFilter->State == FilterPaused); + + ConfigObject.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT; + ConfigObject.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1; + ConfigObject.Header.Size = sizeof(NDIS_CONFIGURATION_OBJECT); + ConfigObject.NdisHandle = FilterDriverHandle; + ConfigObject.Flags = 0; + + Status = NdisOpenConfigurationEx(&ConfigObject, &ConfigurationHandle); + if (Status != NDIS_STATUS_SUCCESS) + { + // + // Filter driver can choose to fail the restart if it cannot open the configuration + // + +#if 0 + // + // The code is here just to demonstrate how to call NDIS to write an + // event to the eventlog. + // + PWCHAR ErrorString = L"Ndislwf"; + + DEBUGP(DL_WARN, "FilterRestart: Cannot open configuration.\n"); + NdisWriteEventLogEntry(FilterDriverObject, + EVENT_NDIS_DRIVER_FAILURE, + 0, + 1, + &ErrorString, + sizeof(Status), + &Status); +#endif + + } + + // + // This sample doesn't actually do anything with the configuration handle; + // it is opened here for illustrative purposes. If you do not need to + // read configuration, you may omit the code manipulating the + // ConfigurationHandle. + // + + if (Status == NDIS_STATUS_SUCCESS) + { + NdisCloseConfiguration(ConfigurationHandle); + } + + NdisRestartAttributes = RestartParameters->RestartAttributes; + + // + // If NdisRestartAttributes is not NULL, then the filter can modify generic + // attributes and add new media specific info attributes at the end. + // Otherwise, if NdisRestartAttributes is NULL, the filter should not try to + // modify/add attributes. + // + if (NdisRestartAttributes != NULL) + { + PNDIS_RESTART_ATTRIBUTES NextAttributes; + + ASSERT(NdisRestartAttributes->Oid == OID_GEN_MINIPORT_RESTART_ATTRIBUTES); + + NdisGeneralAttributes = (PNDIS_RESTART_GENERAL_ATTRIBUTES)NdisRestartAttributes->Data; + + // + // Check to see if we need to change any attributes. For example, the + // driver can change the current MAC address here. Or the driver can add + // media specific info attributes. + // + NdisGeneralAttributes->LookaheadSize = 128; + + // + // Check each attribute to see whether the filter needs to modify it. + // + NextAttributes = NdisRestartAttributes->Next; + + while (NextAttributes != NULL) + { + // + // If somehow the filter needs to change a attributes which requires more space then + // the current attributes: + // 1. Remove the attribute from the Attributes list: + // TempAttributes = NextAttributes; + // NextAttributes = NextAttributes->Next; + // 2. Free the memory for the current attributes: NdisFreeMemory(TempAttributes, 0 , 0); + // 3. Dynamically allocate the memory for the new attributes by calling + // NdisAllocateMemoryWithTagPriority: + // NewAttributes = NdisAllocateMemoryWithTagPriority(Handle, size, Priority); + // 4. Fill in the new attribute + // 5. NewAttributes->Next = NextAttributes; + // 6. NextAttributes = NewAttributes; // Just to make the next statement work. + // + NextAttributes = NextAttributes->Next; + } + + // + // Add a new attributes at the end + // 1. Dynamically allocate the memory for the new attributes by calling + // NdisAllocateMemoryWithTagPriority. + // 2. Fill in the new attribute + // 3. NextAttributes->Next = NewAttributes; + // 4. NewAttributes->Next = NULL; + + + + } + + // + // If everything is OK, set the filter in running state. + // + pFilter->State = FilterRunning; // when successful + + + Status = NDIS_STATUS_SUCCESS; + + + // + // Ensure the state is Paused if restart failed. + // + + if (Status != NDIS_STATUS_SUCCESS) + { + pFilter->State = FilterPaused; + } + + + DEBUGP(DL_TRACE, "<===FilterRestart: FilterModuleContext %p, Status %x\n", FilterModuleContext, Status); + return Status; +} + + +_Use_decl_annotations_ +VOID +FilterDetach( + NDIS_HANDLE FilterModuleContext + ) +/*++ + +Routine Description: + + Filter detach routine. + This is a required function that will deallocate all the resources allocated during + FilterAttach. NDIS calls FilterAttach to remove a filter instance from a filter stack. + +Arguments: + + FilterModuleContext - pointer to the filter context area. + +Return Value: + None. + +NOTE: Called at PASSIVE_LEVEL and the filter is in paused state + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + BOOLEAN bFalse = FALSE; + + + DEBUGP(DL_TRACE, "===>FilterDetach: FilterInstance %p\n", FilterModuleContext); + + + // + // Filter must be in paused state + // + FILTER_ASSERT(pFilter->State == FilterPaused); + + + // + // Detach must not fail, so do not put any code here that can possibly fail. + // + + // + // Free filter instance name if allocated. + // + if (pFilter->FilterName.Buffer != NULL) + { + FILTER_FREE_MEM(pFilter->FilterName.Buffer); + } + + + FILTER_ACQUIRE_LOCK(&FilterListLock, bFalse); + RemoveEntryList(&pFilter->FilterModuleLink); + FILTER_RELEASE_LOCK(&FilterListLock, bFalse); + + + // + // Free the memory allocated + FILTER_FREE_MEM(pFilter); + + DEBUGP(DL_TRACE, "<===FilterDetach Successfully\n"); + return; +} + +_Use_decl_annotations_ +VOID +FilterUnload( + PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + Filter driver's unload routine. + Deregister the driver from NDIS. + +Arguments: + + DriverObject - pointer to the system's driver object structure + for this driver + +Return Value: + + NONE + +--*/ +{ +#if DBG + BOOLEAN bFalse = FALSE; +#endif + + UNREFERENCED_PARAMETER(DriverObject); + + DEBUGP(DL_TRACE, "===>FilterUnload\n"); + + // + // Should free the filter context list + // + FilterDeregisterDevice(); + NdisFDeregisterFilterDriver(FilterDriverHandle); + +#if DBG + FILTER_ACQUIRE_LOCK(&FilterListLock, bFalse); + ASSERT(IsListEmpty(&FilterModuleList)); + + FILTER_RELEASE_LOCK(&FilterListLock, bFalse); + +#endif + + FILTER_FREE_LOCK(&FilterListLock); + + DEBUGP(DL_TRACE, "<===FilterUnload\n"); + + return; + +} + +_Use_decl_annotations_ +NDIS_STATUS +FilterOidRequest( + NDIS_HANDLE FilterModuleContext, + PNDIS_OID_REQUEST Request + ) +/*++ + +Routine Description: + + Request handler + Handle requests from upper layers + +Arguments: + + FilterModuleContext - our filter + Request - the request passed down + + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_PENDING + NDIS_STATUS_XXX + +NOTE: Called at <= DISPATCH_LEVEL (unlike a miniport's MiniportOidRequest) + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + NDIS_STATUS Status; + PNDIS_OID_REQUEST ClonedRequest=NULL; + BOOLEAN bSubmitted = FALSE; + PFILTER_REQUEST_CONTEXT Context; + BOOLEAN bFalse = FALSE; + + + DEBUGP(DL_TRACE, "===>FilterOidRequest: Request %p.\n", Request); + + // + // Most of the time, a filter will clone the OID request and pass down + // the clone. When the clone completes, the filter completes the original + // OID request. + // + // If your filter needs to modify a specific request, it can modify the + // request before or after sending down the cloned request. Or, it can + // complete the original request on its own without sending down any + // clone at all. + // + // If your filter driver does not need to modify any OID requests, then + // you may simply omit this routine entirely; NDIS will pass OID requests + // down on your behalf. This is more efficient than implementing a + // routine that does nothing but clone all requests, as in the sample here. + // + + do + { + Status = NdisAllocateCloneOidRequest(pFilter->FilterHandle, + Request, + FILTER_TAG, + &ClonedRequest); + if (Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_WARN, "FilerOidRequest: Cannot Clone Request\n"); + break; + } + + Context = (PFILTER_REQUEST_CONTEXT)(&ClonedRequest->SourceReserved[0]); + *Context = Request; + + bSubmitted = TRUE; + + // + // Use same request ID + // + ClonedRequest->RequestId = Request->RequestId; + + pFilter->PendingOidRequest = ClonedRequest; + + + Status = NdisFOidRequest(pFilter->FilterHandle, ClonedRequest); + + if (Status != NDIS_STATUS_PENDING) + { + + + FilterOidRequestComplete(pFilter, ClonedRequest, Status); + Status = NDIS_STATUS_PENDING; + } + + + }while (bFalse); + + if (bSubmitted == FALSE) + { + switch(Request->RequestType) + { + case NdisRequestMethod: + Request->DATA.METHOD_INFORMATION.BytesRead = 0; + Request->DATA.METHOD_INFORMATION.BytesNeeded = 0; + Request->DATA.METHOD_INFORMATION.BytesWritten = 0; + break; + + case NdisRequestSetInformation: + Request->DATA.SET_INFORMATION.BytesRead = 0; + Request->DATA.SET_INFORMATION.BytesNeeded = 0; + break; + + case NdisRequestQueryInformation: + case NdisRequestQueryStatistics: + default: + Request->DATA.QUERY_INFORMATION.BytesWritten = 0; + Request->DATA.QUERY_INFORMATION.BytesNeeded = 0; + break; + } + + } + DEBUGP(DL_TRACE, "<===FilterOidRequest: Status %8x.\n", Status); + + return Status; + +} + +_Use_decl_annotations_ +VOID +FilterCancelOidRequest( + NDIS_HANDLE FilterModuleContext, + PVOID RequestId + ) +/*++ + +Routine Description: + + Cancels an OID request + + If your filter driver does not intercept and hold onto any OID requests, + then you do not need to implement this routine. You may simply omit it. + Furthermore, if the filter only holds onto OID requests so it can pass + down a clone (the most common case) the filter does not need to implement + this routine; NDIS will then automatically request that the lower-level + filter/miniport cancel your cloned OID. + + Most filters do not need to implement this routine. + +Arguments: + + FilterModuleContext - our filter + RequestId - identifies the request(s) to cancel + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + PNDIS_OID_REQUEST Request = NULL; + PFILTER_REQUEST_CONTEXT Context; + PNDIS_OID_REQUEST OriginalRequest = NULL; + BOOLEAN bFalse = FALSE; + + FILTER_ACQUIRE_LOCK(&pFilter->Lock, bFalse); + + Request = pFilter->PendingOidRequest; + + if (Request != NULL) + { + Context = (PFILTER_REQUEST_CONTEXT)(&Request->SourceReserved[0]); + + OriginalRequest = (*Context); + } + + if ((OriginalRequest != NULL) && (OriginalRequest->RequestId == RequestId)) + { + FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); + + NdisFCancelOidRequest(pFilter->FilterHandle, RequestId); + } + else + { + FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); + } + + +} + +_Use_decl_annotations_ +VOID +FilterOidRequestComplete( + NDIS_HANDLE FilterModuleContext, + PNDIS_OID_REQUEST Request, + NDIS_STATUS Status + ) +/*++ + +Routine Description: + + Notification that an OID request has been completed + + If this filter sends a request down to a lower layer, and the request is + pended, the FilterOidRequestComplete routine is invoked when the request + is complete. Most requests we've sent are simply clones of requests + received from a higher layer; all we need to do is complete the original + higher request. + + However, if this filter driver sends original requests down, it must not + attempt to complete a pending request to the higher layer. + +Arguments: + + FilterModuleContext - our filter context area + NdisRequest - the completed request + Status - completion status + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + PNDIS_OID_REQUEST OriginalRequest; + PFILTER_REQUEST_CONTEXT Context; + BOOLEAN bFalse = FALSE; + + DEBUGP(DL_TRACE, "===>FilterOidRequestComplete, Request %p.\n", Request); + + Context = (PFILTER_REQUEST_CONTEXT)(&Request->SourceReserved[0]); + OriginalRequest = (*Context); + + // + // This is an internal request + // + if (OriginalRequest == NULL) + { + filterInternalRequestComplete(pFilter, Request, Status); + return; + } + + + FILTER_ACQUIRE_LOCK(&pFilter->Lock, bFalse); + + ASSERT(pFilter->PendingOidRequest == Request); + pFilter->PendingOidRequest = NULL; + + FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); + + + // + // Copy the information from the returned request to the original request + // + switch(Request->RequestType) + { + case NdisRequestMethod: + OriginalRequest->DATA.METHOD_INFORMATION.OutputBufferLength = Request->DATA.METHOD_INFORMATION.OutputBufferLength; + OriginalRequest->DATA.METHOD_INFORMATION.BytesRead = Request->DATA.METHOD_INFORMATION.BytesRead; + OriginalRequest->DATA.METHOD_INFORMATION.BytesNeeded = Request->DATA.METHOD_INFORMATION.BytesNeeded; + OriginalRequest->DATA.METHOD_INFORMATION.BytesWritten = Request->DATA.METHOD_INFORMATION.BytesWritten; + break; + + case NdisRequestSetInformation: + OriginalRequest->DATA.SET_INFORMATION.BytesRead = Request->DATA.SET_INFORMATION.BytesRead; + OriginalRequest->DATA.SET_INFORMATION.BytesNeeded = Request->DATA.SET_INFORMATION.BytesNeeded; + break; + + case NdisRequestQueryInformation: + case NdisRequestQueryStatistics: + default: + OriginalRequest->DATA.QUERY_INFORMATION.BytesWritten = Request->DATA.QUERY_INFORMATION.BytesWritten; + OriginalRequest->DATA.QUERY_INFORMATION.BytesNeeded = Request->DATA.QUERY_INFORMATION.BytesNeeded; + break; + } + + + (*Context) = NULL; + + NdisFreeCloneOidRequest(pFilter->FilterHandle, Request); + + NdisFOidRequestComplete(pFilter->FilterHandle, OriginalRequest, Status); + + DEBUGP(DL_TRACE, "<===FilterOidRequestComplete.\n"); +} + + +_Use_decl_annotations_ +VOID +FilterStatus( + NDIS_HANDLE FilterModuleContext, + PNDIS_STATUS_INDICATION StatusIndication + ) +/*++ + +Routine Description: + + Status indication handler + +Arguments: + + FilterModuleContext - our filter context + StatusIndication - the status being indicated + +NOTE: called at <= DISPATCH_LEVEL + + FILTER driver may call NdisFIndicateStatus to generate a status indication to + all higher layer modules. + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; +#if DBG + BOOLEAN bFalse = FALSE; +#endif + + DEBUGP(DL_TRACE, "===>FilterStatus, IndicateStatus = %8x.\n", StatusIndication->StatusCode); + + + // + // The filter may do processing on the status indication here, including + // intercepting and dropping it entirely. However, the sample does nothing + // with status indications except pass them up to the higher layer. It is + // more efficient to omit the FilterStatus handler entirely if it does + // nothing, but it is included in this sample for illustrative purposes. + // + +#if DBG + FILTER_ACQUIRE_LOCK(&pFilter->Lock, bFalse); + ASSERT(pFilter->bIndicating == FALSE); + pFilter->bIndicating = TRUE; + FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); +#endif // DBG + + NdisFIndicateStatus(pFilter->FilterHandle, StatusIndication); + +#if DBG + FILTER_ACQUIRE_LOCK(&pFilter->Lock, bFalse); + ASSERT(pFilter->bIndicating == TRUE); + pFilter->bIndicating = FALSE; + FILTER_RELEASE_LOCK(&pFilter->Lock, bFalse); +#endif // DBG + + DEBUGP(DL_TRACE, "<===FilterStatus.\n"); + +} + +_Use_decl_annotations_ +VOID +FilterDevicePnPEventNotify( + NDIS_HANDLE FilterModuleContext, + PNET_DEVICE_PNP_EVENT NetDevicePnPEvent + ) +/*++ + +Routine Description: + + Device PNP event handler + +Arguments: + + FilterModuleContext - our filter context + NetDevicePnPEvent - a Device PnP event + +NOTE: called at PASSIVE_LEVEL + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + NDIS_DEVICE_PNP_EVENT DevicePnPEvent = NetDevicePnPEvent->DevicePnPEvent; +#if DBG + BOOLEAN bFalse = FALSE; +#endif + + DEBUGP(DL_TRACE, "===>FilterDevicePnPEventNotify: NetPnPEvent = %p.\n", NetDevicePnPEvent); + + // + // The filter may do processing on the event here, including intercepting + // and dropping it entirely. However, the sample does nothing with Device + // PNP events, except pass them down to the next lower* layer. It is more + // efficient to omit the FilterDevicePnPEventNotify handler entirely if it + // does nothing, but it is included in this sample for illustrative purposes. + // + // * Trivia: Device PNP events percolate DOWN the stack, instead of upwards + // like status indications and Net PNP events. So the next layer is the + // LOWER layer. + // + + switch (DevicePnPEvent) + { + + case NdisDevicePnPEventQueryRemoved: + case NdisDevicePnPEventRemoved: + case NdisDevicePnPEventSurpriseRemoved: + case NdisDevicePnPEventQueryStopped: + case NdisDevicePnPEventStopped: + case NdisDevicePnPEventPowerProfileChanged: + case NdisDevicePnPEventFilterListChanged: + + break; + + default: + DEBUGP(DL_ERROR, "FilterDevicePnPEventNotify: Invalid event.\n"); + FILTER_ASSERT(bFalse); + + break; + } + + NdisFDevicePnPEventNotify(pFilter->FilterHandle, NetDevicePnPEvent); + + DEBUGP(DL_TRACE, "<===FilterDevicePnPEventNotify\n"); + +} + +_Use_decl_annotations_ +NDIS_STATUS +FilterNetPnPEvent( + NDIS_HANDLE FilterModuleContext, + PNET_PNP_EVENT_NOTIFICATION NetPnPEventNotification + ) +/*++ + +Routine Description: + + Net PNP event handler + +Arguments: + + FilterModuleContext - our filter context + NetPnPEventNotification - a Net PnP event + +NOTE: called at PASSIVE_LEVEL + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + // + // The filter may do processing on the event here, including intercepting + // and dropping it entirely. However, the sample does nothing with Net PNP + // events, except pass them up to the next higher layer. It is more + // efficient to omit the FilterNetPnPEvent handler entirely if it does + // nothing, but it is included in this sample for illustrative purposes. + // + + Status = NdisFNetPnPEvent(pFilter->FilterHandle, NetPnPEventNotification); + + return Status; +} + +_Use_decl_annotations_ +VOID +FilterSendNetBufferListsComplete( + NDIS_HANDLE FilterModuleContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG SendCompleteFlags + ) +/*++ + +Routine Description: + + Send complete handler + + This routine is invoked whenever the lower layer is finished processing + sent NET_BUFFER_LISTs. If the filter does not need to be involved in the + send path, you should remove this routine and the FilterSendNetBufferLists + routine. NDIS will pass along send packets on behalf of your filter more + efficiently than the filter can. + +Arguments: + + FilterModuleContext - our filter context + NetBufferLists - a chain of NBLs that are being returned to you + SendCompleteFlags - flags (see documentation) + +Return Value: + + NONE + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + ULONG NumOfSendCompletes = 0; + BOOLEAN DispatchLevel; + PNET_BUFFER_LIST CurrNbl; + + DEBUGP(DL_TRACE, "===>SendNBLComplete, NetBufferList: %p.\n", NetBufferLists); + + + // + // If your filter injected any send packets into the datapath to be sent, + // you must identify their NBLs here and remove them from the chain. Do not + // attempt to send-complete your NBLs up to the higher layer. + // + + // + // If your filter has modified any NBLs (or NBs, MDLs, etc) in your + // FilterSendNetBufferLists handler, you must undo the modifications here. + // In general, NBLs must be returned in the same condition in which you had + // you received them. (Exceptions: the NBLs can be re-ordered on the linked + // list, and the scratch fields are don't-care). + // + + if (pFilter->TrackSends) + { + CurrNbl = NetBufferLists; + while (CurrNbl) + { + NumOfSendCompletes++; + CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl); + + } + DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendCompleteFlags); + FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); + pFilter->OutstandingSends -= NumOfSendCompletes; + FILTER_LOG_SEND_REF(2, pFilter, PrevNbl, pFilter->OutstandingSends); + FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); + } + + // Send complete the NBLs. If you removed any NBLs from the chain, make + // sure the chain isn't empty (i.e., NetBufferLists!=NULL). + + NdisFSendNetBufferListsComplete(pFilter->FilterHandle, NetBufferLists, SendCompleteFlags); + + DEBUGP(DL_TRACE, "<===SendNBLComplete.\n"); +} + + +_Use_decl_annotations_ +VOID +FilterSendNetBufferLists( + NDIS_HANDLE FilterModuleContext, + PNET_BUFFER_LIST NetBufferLists, + NDIS_PORT_NUMBER PortNumber, + ULONG SendFlags + ) +/*++ + +Routine Description: + + Send Net Buffer List handler + This function is an optional function for filter drivers. If provided, NDIS + will call this function to transmit a linked list of NetBuffers, described by a + NetBufferList, over the network. If this handler is NULL, NDIS will skip calling + this filter when sending a NetBufferList and will call the next lower + driver in the stack. A filter that doesn't provide a FilerSendNetBufferList + handler can not originate a send on its own. + +Arguments: + + FilterModuleContext - our filter context area + NetBufferLists - a List of NetBufferLists to send + PortNumber - Port Number to which this send is targeted + SendFlags - specifies if the call is at DISPATCH_LEVEL + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + PNET_BUFFER_LIST CurrNbl; + BOOLEAN DispatchLevel; + BOOLEAN bFalse = FALSE; + + DEBUGP(DL_TRACE, "===>SendNetBufferList: NBL = %p.\n", NetBufferLists); + + do + { + + DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags); +#if DBG + // + // we should never get packets to send if we are not in running state + // + + FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); + // + // If the filter is not in running state, fail the send + // + if (pFilter->State != FilterRunning) + { + FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); + + CurrNbl = NetBufferLists; + while (CurrNbl) + { + NET_BUFFER_LIST_STATUS(CurrNbl) = NDIS_STATUS_PAUSED; + CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl); + } + NdisFSendNetBufferListsComplete(pFilter->FilterHandle, + NetBufferLists, + DispatchLevel ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0); + break; + + } + FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); +#endif + if (pFilter->TrackSends) + { + FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); + CurrNbl = NetBufferLists; + while (CurrNbl) + { + pFilter->OutstandingSends++; + FILTER_LOG_SEND_REF(1, pFilter, CurrNbl, pFilter->OutstandingSends); + + CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl); + } + FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); + } + + // + // If necessary, queue the NetBufferLists in a local structure for later + // processing. However, do not queue them for "too long", or else the + // system's performance may be degraded. If you need to hold onto an + // NBL for an unbounded amount of time, then allocate memory, perform a + // deep copy, and complete the original NBL. + // + + NdisFSendNetBufferLists(pFilter->FilterHandle, NetBufferLists, PortNumber, SendFlags); + + + } + while (bFalse); + + DEBUGP(DL_TRACE, "<===SendNetBufferList. \n"); +} + +_Use_decl_annotations_ +VOID +FilterReturnNetBufferLists( + NDIS_HANDLE FilterModuleContext, + PNET_BUFFER_LIST NetBufferLists, + ULONG ReturnFlags + ) +/*++ + +Routine Description: + + FilterReturnNetBufferLists handler. + FilterReturnNetBufferLists is an optional function. If provided, NDIS calls + FilterReturnNetBufferLists to return the ownership of one or more NetBufferLists + and their embedded NetBuffers to the filter driver. If this handler is NULL, NDIS + will skip calling this filter when returning NetBufferLists to the underlying + miniport and will call the next lower driver in the stack. A filter that doesn't + provide a FilterReturnNetBufferLists handler cannot originate a receive indication + on its own. + +Arguments: + + FilterInstanceContext - our filter context area + NetBufferLists - a linked list of NetBufferLists that this + filter driver indicated in a previous call to + NdisFIndicateReceiveNetBufferLists + ReturnFlags - flags specifying if the caller is at DISPATCH_LEVEL + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + PNET_BUFFER_LIST CurrNbl = NetBufferLists; + UINT NumOfNetBufferLists = 0; + BOOLEAN DispatchLevel; + ULONG Ref; + + DEBUGP(DL_TRACE, "===>ReturnNetBufferLists, NetBufferLists is %p.\n", NetBufferLists); + + + // + // If your filter injected any receive packets into the datapath to be + // received, you must identify their NBLs here and remove them from the + // chain. Do not attempt to receive-return your NBLs down to the lower + // layer. + // + + // + // If your filter has modified any NBLs (or NBs, MDLs, etc) in your + // FilterReceiveNetBufferLists handler, you must undo the modifications here. + // In general, NBLs must be returned in the same condition in which you had + // you received them. (Exceptions: the NBLs can be re-ordered on the linked + // list, and the scratch fields are don't-care). + // + + if (pFilter->TrackReceives) + { + while (CurrNbl) + { + NumOfNetBufferLists ++; + CurrNbl = NET_BUFFER_LIST_NEXT_NBL(CurrNbl); + } + } + + + // Return the received NBLs. If you removed any NBLs from the chain, make + // sure the chain isn't empty (i.e., NetBufferLists!=NULL). + + NdisFReturnNetBufferLists(pFilter->FilterHandle, NetBufferLists, ReturnFlags); + + if (pFilter->TrackReceives) + { + DispatchLevel = NDIS_TEST_RETURN_AT_DISPATCH_LEVEL(ReturnFlags); + FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); + + pFilter->OutstandingRcvs -= NumOfNetBufferLists; + Ref = pFilter->OutstandingRcvs; + FILTER_LOG_RCV_REF(3, pFilter, NetBufferLists, Ref); + FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); + } + + + DEBUGP(DL_TRACE, "<===ReturnNetBufferLists.\n"); + + +} + + +_Use_decl_annotations_ +VOID +FilterReceiveNetBufferLists( + NDIS_HANDLE FilterModuleContext, + PNET_BUFFER_LIST NetBufferLists, + NDIS_PORT_NUMBER PortNumber, + ULONG NumberOfNetBufferLists, + ULONG ReceiveFlags + ) +/*++ + +Routine Description: + + FilerReceiveNetBufferLists is an optional function for filter drivers. + If provided, this function processes receive indications made by underlying + NIC or lower level filter drivers. This function can also be called as a + result of loopback. If this handler is NULL, NDIS will skip calling this + filter when processing a receive indication and will call the next higher + driver in the stack. A filter that doesn't provide a + FilterReceiveNetBufferLists handler cannot provide a + FilterReturnNetBufferLists handler and cannot a initiate an original receive + indication on its own. + +Arguments: + + FilterModuleContext - our filter context area. + NetBufferLists - a linked list of NetBufferLists + PortNumber - Port on which the receive is indicated + ReceiveFlags - + +N.B.: It is important to check the ReceiveFlags in NDIS_TEST_RECEIVE_CANNOT_PEND. + This controls whether the receive indication is an synchronous or + asynchronous function call. + +--*/ +{ + + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + BOOLEAN DispatchLevel; + ULONG Ref; + BOOLEAN bFalse = FALSE; +#if DBG + ULONG ReturnFlags; +#endif + + DEBUGP(DL_TRACE, "===>ReceiveNetBufferList: NetBufferLists = %p.\n", NetBufferLists); + do + { + + DispatchLevel = NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags); +#if DBG + FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); + + if (pFilter->State != FilterRunning) + { + FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); + + if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags)) + { + ReturnFlags = 0; + if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags)) + { + NDIS_SET_RETURN_FLAG(ReturnFlags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL); + } + + NdisFReturnNetBufferLists(pFilter->FilterHandle, NetBufferLists, ReturnFlags); + } + break; + } + FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); +#endif + + ASSERT(NumberOfNetBufferLists >= 1); + + // + // If you would like to drop a received packet, then you must carefully + // modify the NBL chain as follows: + // + // if NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags): + // For each NBL that is NOT dropped, temporarily unlink it from + // the linked list, and indicate it up alone with + // NdisFIndicateReceiveNetBufferLists and the + // NDIS_RECEIVE_FLAGS_RESOURCES flag set. Then immediately + // relink the NBL back into the chain. When all NBLs have been + // indicated up, you may return from this function. + // otherwise (NDIS_TEST_RECEIVE_CANNOT_PEND is FALSE): + // Divide the linked list of NBLs into two chains: one chain + // of packets to drop, and everything else in another chain. + // Return the first chain with NdisFReturnNetBufferLists, and + // indicate up the rest with NdisFIndicateReceiveNetBufferLists. + // + // Note: on the receive path for Ethernet packets, one NBL will have + // exactly one NB. So (assuming you are receiving on Ethernet, or are + // attached above Native WiFi) you do not need to worry about dropping + // one NB, but trying to indicate up the remaining NBs on the same NBL. + // In other words, if the first NB should be dropped, drop the whole NBL. + // + + // + // If you would like to modify a packet, and can do so quickly, you may + // do it here. However, make sure you save enough information to undo + // your modification in the FilterReturnNetBufferLists handler. + // + + // + // If necessary, queue the NetBufferLists in a local structure for later + // processing. However, do not queue them for "too long", or else the + // system's performance may be degraded. If you need to hold onto an + // NBL for an unbounded amount of time, then allocate memory, perform a + // deep copy, and return the original NBL. + // + + if (pFilter->TrackReceives) + { + FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); + pFilter->OutstandingRcvs += NumberOfNetBufferLists; + Ref = pFilter->OutstandingRcvs; + + FILTER_LOG_RCV_REF(1, pFilter, NetBufferLists, Ref); + FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); + } + + NdisFIndicateReceiveNetBufferLists( + pFilter->FilterHandle, + NetBufferLists, + PortNumber, + NumberOfNetBufferLists, + ReceiveFlags); + + + if (NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags) && + pFilter->TrackReceives) + { + FILTER_ACQUIRE_LOCK(&pFilter->Lock, DispatchLevel); + pFilter->OutstandingRcvs -= NumberOfNetBufferLists; + Ref = pFilter->OutstandingRcvs; + FILTER_LOG_RCV_REF(2, pFilter, NetBufferLists, Ref); + FILTER_RELEASE_LOCK(&pFilter->Lock, DispatchLevel); + } + + } while (bFalse); + + DEBUGP(DL_TRACE, "<===ReceiveNetBufferList: Flags = %8x.\n", ReceiveFlags); + +} + + +_Use_decl_annotations_ +VOID +FilterCancelSendNetBufferLists( + NDIS_HANDLE FilterModuleContext, + PVOID CancelId + ) +/*++ + +Routine Description: + + This function cancels any NET_BUFFER_LISTs pended in the filter and then + calls the NdisFCancelSendNetBufferLists to propagate the cancel operation. + + If your driver does not queue any send NBLs, you may omit this routine. + NDIS will propagate the cancelation on your behalf more efficiently. + +Arguments: + + FilterModuleContext - our filter context area. + CancelId - an identifier for all NBLs that should be dequeued + +Return Value: + + None + +*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + + NdisFCancelSendNetBufferLists(pFilter->FilterHandle, CancelId); +} + + +_Use_decl_annotations_ +NDIS_STATUS +FilterSetModuleOptions( + NDIS_HANDLE FilterModuleContext + ) +/*++ + +Routine Description: + + This function set the optional handlers for the filter + +Arguments: + + FilterModuleContext: The FilterModuleContext given to NdisFSetAttributes + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_RESOURCES + NDIS_STATUS_FAILURE + +--*/ +{ + PMS_FILTER pFilter = (PMS_FILTER)FilterModuleContext; + NDIS_FILTER_PARTIAL_CHARACTERISTICS OptionalHandlers; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + BOOLEAN bFalse = FALSE; + + // + // Demonstrate how to change send/receive handlers at runtime. + // + if (bFalse) + { + UINT i; + + + pFilter->CallsRestart++; + + i = pFilter->CallsRestart % 8; + + pFilter->TrackReceives = TRUE; + pFilter->TrackSends = TRUE; + + NdisMoveMemory(&OptionalHandlers, &DefaultChars, sizeof(OptionalHandlers)); + OptionalHandlers.Header.Type = NDIS_OBJECT_TYPE_FILTER_PARTIAL_CHARACTERISTICS; + OptionalHandlers.Header.Size = sizeof(OptionalHandlers); + switch (i) + { + + case 0: + OptionalHandlers.ReceiveNetBufferListsHandler = NULL; + pFilter->TrackReceives = FALSE; + break; + + case 1: + + OptionalHandlers.ReturnNetBufferListsHandler = NULL; + pFilter->TrackReceives = FALSE; + break; + + case 2: + OptionalHandlers.SendNetBufferListsHandler = NULL; + pFilter->TrackSends = FALSE; + break; + + case 3: + OptionalHandlers.SendNetBufferListsCompleteHandler = NULL; + pFilter->TrackSends = FALSE; + break; + + case 4: + OptionalHandlers.ReceiveNetBufferListsHandler = NULL; + OptionalHandlers.ReturnNetBufferListsHandler = NULL; + break; + + case 5: + OptionalHandlers.SendNetBufferListsHandler = NULL; + OptionalHandlers.SendNetBufferListsCompleteHandler = NULL; + break; + + case 6: + + OptionalHandlers.ReceiveNetBufferListsHandler = NULL; + OptionalHandlers.ReturnNetBufferListsHandler = NULL; + OptionalHandlers.SendNetBufferListsHandler = NULL; + OptionalHandlers.SendNetBufferListsCompleteHandler = NULL; + break; + + case 7: + break; + } + Status = NdisSetOptionalHandlers(pFilter->FilterHandle, (PNDIS_DRIVER_OPTIONAL_HANDLERS)&OptionalHandlers ); + } + return Status; +} + + + +_IRQL_requires_max_(DISPATCH_LEVEL) +NDIS_STATUS +filterDoInternalRequest( + _In_ PMS_FILTER FilterModuleContext, + _In_ NDIS_REQUEST_TYPE RequestType, + _In_ NDIS_OID Oid, + _Inout_updates_bytes_to_(InformationBufferLength, *pBytesProcessed) + PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _In_opt_ ULONG OutputBufferLength, + _In_ ULONG MethodId, + _Out_ PULONG pBytesProcessed + ) +/*++ + +Routine Description: + + Utility routine that forms and sends an NDIS_OID_REQUEST to the + miniport, waits for it to complete, and returns status + to the caller. + + NOTE: this assumes that the calling routine ensures validity + of the filter handle until this returns. + +Arguments: + + FilterModuleContext - pointer to our filter module context + RequestType - NdisRequest[Set|Query|method]Information + Oid - the object being set/queried + InformationBuffer - data for the request + InformationBufferLength - length of the above + OutputBufferLength - valid only for method request + MethodId - valid only for method request + pBytesProcessed - place to return bytes read/written + +Return Value: + + Status of the set/query request + +--*/ +{ + FILTER_REQUEST FilterRequest; + PNDIS_OID_REQUEST NdisRequest = &FilterRequest.Request; + NDIS_STATUS Status; + BOOLEAN bFalse; + + + bFalse = FALSE; + *pBytesProcessed = 0; + NdisZeroMemory(NdisRequest, sizeof(NDIS_OID_REQUEST)); + + NdisInitializeEvent(&FilterRequest.ReqEvent); + + NdisRequest->Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST; + NdisRequest->Header.Revision = NDIS_OID_REQUEST_REVISION_1; + NdisRequest->Header.Size = sizeof(NDIS_OID_REQUEST); + NdisRequest->RequestType = RequestType; + + switch (RequestType) + { + case NdisRequestQueryInformation: + NdisRequest->DATA.QUERY_INFORMATION.Oid = Oid; + NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer = + InformationBuffer; + NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength = + InformationBufferLength; + break; + + case NdisRequestSetInformation: + NdisRequest->DATA.SET_INFORMATION.Oid = Oid; + NdisRequest->DATA.SET_INFORMATION.InformationBuffer = + InformationBuffer; + NdisRequest->DATA.SET_INFORMATION.InformationBufferLength = + InformationBufferLength; + break; + + case NdisRequestMethod: + NdisRequest->DATA.METHOD_INFORMATION.Oid = Oid; + NdisRequest->DATA.METHOD_INFORMATION.MethodId = MethodId; + NdisRequest->DATA.METHOD_INFORMATION.InformationBuffer = + InformationBuffer; + NdisRequest->DATA.METHOD_INFORMATION.InputBufferLength = + InformationBufferLength; + NdisRequest->DATA.METHOD_INFORMATION.OutputBufferLength = OutputBufferLength; + break; + + + + default: + FILTER_ASSERT(bFalse); + break; + } + + NdisRequest->RequestId = (PVOID)FILTER_REQUEST_ID; + + Status = NdisFOidRequest(FilterModuleContext->FilterHandle, + NdisRequest); + + + if (Status == NDIS_STATUS_PENDING) + { + + NdisWaitEvent(&FilterRequest.ReqEvent, 0); + Status = FilterRequest.Status; + } + + + if (Status == NDIS_STATUS_SUCCESS) + { + if (RequestType == NdisRequestSetInformation) + { + *pBytesProcessed = NdisRequest->DATA.SET_INFORMATION.BytesRead; + } + + if (RequestType == NdisRequestQueryInformation) + { + *pBytesProcessed = NdisRequest->DATA.QUERY_INFORMATION.BytesWritten; + } + + if (RequestType == NdisRequestMethod) + { + *pBytesProcessed = NdisRequest->DATA.METHOD_INFORMATION.BytesWritten; + } + + // + // The driver below should set the correct value to BytesWritten + // or BytesRead. But now, we just truncate the value to InformationBufferLength + // + if (RequestType == NdisRequestMethod) + { + if (*pBytesProcessed > OutputBufferLength) + { + *pBytesProcessed = OutputBufferLength; + } + } + else + { + + if (*pBytesProcessed > InformationBufferLength) + { + *pBytesProcessed = InformationBufferLength; + } + } + } + + + return Status; +} + +VOID +filterInternalRequestComplete( + _In_ NDIS_HANDLE FilterModuleContext, + _In_ PNDIS_OID_REQUEST NdisRequest, + _In_ NDIS_STATUS Status + ) +/*++ + +Routine Description: + + NDIS entry point indicating completion of a pended NDIS_OID_REQUEST. + +Arguments: + + FilterModuleContext - pointer to filter module context + NdisRequest - pointer to NDIS request + Status - status of request completion + +Return Value: + + None + +--*/ +{ + PFILTER_REQUEST FilterRequest; + + + UNREFERENCED_PARAMETER(FilterModuleContext); + + // + // Get at the request context. + // + FilterRequest = CONTAINING_RECORD(NdisRequest, FILTER_REQUEST, Request); + + // + // Save away the completion status. + // + FilterRequest->Status = Status; + + // + // Wake up the thread blocked for this request to complete. + // + NdisSetEvent(&FilterRequest->ReqEvent); +} + diff --git a/network/ndis/filter/filter.h b/network/ndis/filter/filter.h new file mode 100644 index 00000000..42c5d913 --- /dev/null +++ b/network/ndis/filter/filter.h @@ -0,0 +1,456 @@ +/*++ + +Copyright (c) Microsoft Corporation + +Module Name: + + Filter.h + +Abstract: + + This module contains all prototypes and macros for filter code. + +Notes: + +--*/ +#ifndef _FILT_H +#define _FILT_H + +#pragma warning(disable:28930) // Unused assignment of pointer, by design in samples +#pragma warning(disable:28931) // Unused assignment of variable, by design in samples + +// TODO: Customize these to hint at your component for memory leak tracking. +// These should be treated like a pooltag. +#define FILTER_REQUEST_ID 'RTLF' +#define FILTER_ALLOC_TAG 'tliF' +#define FILTER_TAG 'dnTF' + +// TODO: Specify which version of the NDIS contract you will use here. +// In many cases, 6.0 is the best choice. You only need to select a later +// version if you need a feature that is not available in 6.0. +// +// Legal values include: +// 6.0 Available starting with Windows Vista RTM +// 6.1 Available starting with Windows Vista SP1 / Windows Server 2008 +// 6.20 Available starting with Windows 7 / Windows Server 2008 R2 +// 6.30 Available starting with Windows 8 / Windows Server "8" +// Or, just use NDIS_FILTER_MAJOR_VERSION / NDIS_FILTER_MINOR_VERSION +// to pick up whatever version is defined by your build system +// (for example, "-DNDIS630"). +#define FILTER_MAJOR_NDIS_VERSION NDIS_FILTER_MAJOR_VERSION +#define FILTER_MINOR_NDIS_VERSION NDIS_FILTER_MINOR_VERSION + + +// +// Global variables +// +extern NDIS_HANDLE FilterDriverHandle; // NDIS handle for filter driver +extern NDIS_HANDLE FilterDriverObject; +extern NDIS_HANDLE NdisFilterDeviceHandle; +extern PDEVICE_OBJECT DeviceObject; + +extern FILTER_LOCK FilterListLock; +extern LIST_ENTRY FilterModuleList; + + + + +#if NDISLWF +#define FILTER_FRIENDLY_NAME L"NDIS Sample LightWeight Filter" +// TODO: Customize this to match the GUID in the INF +#define FILTER_UNIQUE_NAME L"{5cbf81bd-5055-47cd-9055-a76b2b4e3697}" //unique name, quid name +// TODO: Customize this to match the service name in the INF +#define FILTER_SERVICE_NAME L"NDISLWF" +// +// The filter needs to handle IOCTLs +// +#define LINKNAME_STRING L"\\DosDevices\\NDISLWF" +#define NTDEVICE_STRING L"\\Device\\NDISLWF" +#endif + + +#if NDISLWF1 +#define FILTER_FRIENDLY_NAME L"NDIS Sample LightWeight Filter 1" +#define FILTER_UNIQUE_NAME L"{5cbf81be-5055-47cd-9055-a76b2b4e3697}" //unique name, quid name +#define FILTER_SERVICE_NAME L"NDISLWF1" +// +// The filter needs to handle IOCTRLs +// +#define LINKNAME_STRING L"\\DosDevices\\NDISLWF1" +#define NTDEVICE_STRING L"\\Device\\NDISLWF1" +#endif + +#if NDISMON +#define FILTER_FRIENDLY_NAME L"NDIS Sample Monitor LightWeight Filter" +#define FILTER_UNIQUE_NAME L"{5cbf81bf-5055-47cd-9055-a76b2b4e3697}" //unique name, quid name +#define FILTER_SERVICE_NAME L"NDISMON" +// +// The filter needs to handle IOCTRLs +// +#define LINKNAME_STRING L"\\DosDevices\\NDISMON" +#define NTDEVICE_STRING L"\\Device\\NDISMON" +#endif + +#if NDISMON1 +#define FILTER_FRIENDLY_NAME L"NDIS Sample Monitor 1 LightWeight Filter" +#define FILTER_UNIQUE_NAME L"{5cbf81c0-5055-47cd-9055-a76b2b4e3697}" //unique name, quid name +#define FILTER_SERVICE_NAME L"NDISMON1" +// +// The filter needs to handle IOCTRLs +// +#define LINKNAME_STRING L"\\DosDevices\\NDISMON1" +#define NTDEVICE_STRING L"\\Device\\NDISMON1" +#endif + + +// +// Types and macros to manipulate packet queue +// +typedef struct _QUEUE_ENTRY +{ + struct _QUEUE_ENTRY * Next; +}QUEUE_ENTRY, *PQUEUE_ENTRY; + +typedef struct _QUEUE_HEADER +{ + PQUEUE_ENTRY Head; + PQUEUE_ENTRY Tail; +} QUEUE_HEADER, PQUEUE_HEADER; + + +#if TRACK_RECEIVES +UINT filterLogReceiveRefIndex = 0; +ULONG_PTR filterLogReceiveRef[0x10000]; +#endif + +#if TRACK_SENDS +UINT filterLogSendRefIndex = 0; +ULONG_PTR filterLogSendRef[0x10000]; +#endif + +#if TRACK_RECEIVES +#define FILTER_LOG_RCV_REF(_O, _Instance, _NetBufferList, _Ref) \ + {\ + filterLogReceiveRef[filterLogReceiveRefIndex++] = (ULONG_PTR)(_O); \ + filterLogReceiveRef[filterLogReceiveRefIndex++] = (ULONG_PTR)(_Instance); \ + filterLogReceiveRef[filterLogReceiveRefIndex++] = (ULONG_PTR)(_NetBufferList); \ + filterLogReceiveRef[filterLogReceiveRefIndex++] = (ULONG_PTR)(_Ref); \ + if (filterLogReceiveRefIndex >= (0x10000 - 5)) \ + { \ + filterLogReceiveRefIndex = 0; \ + } \ + } +#else +#define FILTER_LOG_RCV_REF(_O, _Instance, _NetBufferList, _Ref) +#endif + +#if TRACK_SENDS +#define FILTER_LOG_SEND_REF(_O, _Instance, _NetBufferList, _Ref) \ + {\ + filterLogSendRef[filterLogSendRefIndex++] = (ULONG_PTR)(_O); \ + filterLogSendRef[filterLogSendRefIndex++] = (ULONG_PTR)(_Instance); \ + filterLogSendRef[filterLogSendRefIndex++] = (ULONG_PTR)(_NetBufferList); \ + filterLogSendRef[filterLogSendRefIndex++] = (ULONG_PTR)(_Ref); \ + if (filterLogSendRefIndex >= (0x10000 - 5)) \ + { \ + filterLogSendRefIndex = 0; \ + } \ + } + +#else +#define FILTER_LOG_SEND_REF(_O, _Instance, _NetBufferList, _Ref) +#endif + + +// +// DEBUG related macros. +// +#if DBG +#define FILTER_ALLOC_MEM(_NdisHandle, _Size) \ + filterAuditAllocMem( \ + _NdisHandle, \ + _Size, \ + __FILENUMBER, \ + __LINE__); + +#define FILTER_FREE_MEM(_pMem) \ + filterAuditFreeMem(_pMem); + +#else +#define FILTER_ALLOC_MEM(_NdisHandle, _Size) \ + NdisAllocateMemoryWithTagPriority(_NdisHandle, _Size, FILTER_ALLOC_TAG, LowPoolPriority) + +#define FILTER_FREE_MEM(_pMem) NdisFreeMemory(_pMem, 0, 0) + +#endif //DBG + +#if DBG_SPIN_LOCK +#define FILTER_INIT_LOCK(_pLock) \ + filterAllocateSpinLock(_pLock, __FILENUMBER, __LINE__) + +#define FILTER_FREE_LOCK(_pLock) filterFreeSpinLock(_pLock) + + +#define FILTER_ACQUIRE_LOCK(_pLock, DispatchLevel) \ + filterAcquireSpinLock(_pLock, __FILENUMBER, __LINE__, DisaptchLevel) + +#define FILTER_RELEASE_LOCK(_pLock, DispatchLevel) \ + filterReleaseSpinLock(_pLock, __FILENUMBER, __LINE__, DispatchLevel) + +#else +#define FILTER_INIT_LOCK(_pLock) NdisAllocateSpinLock(_pLock) + +#define FILTER_FREE_LOCK(_pLock) NdisFreeSpinLock(_pLock) + +#define FILTER_ACQUIRE_LOCK(_pLock, DispatchLevel) \ + { \ + if (DispatchLevel) \ + { \ + NdisDprAcquireSpinLock(_pLock); \ + } \ + else \ + { \ + NdisAcquireSpinLock(_pLock); \ + } \ + } + +#define FILTER_RELEASE_LOCK(_pLock, DispatchLevel) \ + { \ + if (DispatchLevel) \ + { \ + NdisDprReleaseSpinLock(_pLock); \ + } \ + else \ + { \ + NdisReleaseSpinLock(_pLock); \ + } \ + } +#endif //DBG_SPIN_LOCK + + +#define NET_BUFFER_LIST_LINK_TO_ENTRY(_pNBL) ((PQUEUE_ENTRY)(NET_BUFFER_LIST_NEXT_NBL(_pNBL))) +#define ENTRY_TO_NET_BUFFER_LIST(_pEnt) (CONTAINING_RECORD((_pEnt), NET_BUFFER_LIST, Next)) + +#define InitializeQueueHeader(_QueueHeader) \ +{ \ + (_QueueHeader)->Head = (_QueueHeader)->Tail = NULL; \ +} + +// +// Macros for queue operations +// +#define IsQueueEmpty(_QueueHeader) ((_QueueHeader)->Head == NULL) + +#define RemoveHeadQueue(_QueueHeader) \ + (_QueueHeader)->Head; \ + { \ + PQUEUE_ENTRY pNext; \ + ASSERT((_QueueHeader)->Head); \ + pNext = (_QueueHeader)->Head->Next; \ + (_QueueHeader)->Head = pNext; \ + if (pNext == NULL) \ + (_QueueHeader)->Tail = NULL; \ + } + +#define InsertHeadQueue(_QueueHeader, _QueueEntry) \ + { \ + ((PQUEUE_ENTRY)(_QueueEntry))->Next = (_QueueHeader)->Head; \ + (_QueueHeader)->Head = (PQUEUE_ENTRY)(_QueueEntry); \ + if ((_QueueHeader)->Tail == NULL) \ + (_QueueHeader)->Tail = (PQUEUE_ENTRY)(_QueueEntry); \ + } + +#define InsertTailQueue(_QueueHeader, _QueueEntry) \ + { \ + ((PQUEUE_ENTRY)(_QueueEntry))->Next = NULL; \ + if ((_QueueHeader)->Tail) \ + (_QueueHeader)->Tail->Next = (PQUEUE_ENTRY)(_QueueEntry); \ + else \ + (_QueueHeader)->Head = (PQUEUE_ENTRY)(_QueueEntry); \ + (_QueueHeader)->Tail = (PQUEUE_ENTRY)(_QueueEntry); \ + } + + +// +// Enum of filter's states +// Filter can only be in one state at one time +// +typedef enum _FILTER_STATE +{ + FilterStateUnspecified, + FilterInitialized, + FilterPausing, + FilterPaused, + FilterRunning, + FilterRestarting, + FilterDetaching +} FILTER_STATE; + + +typedef struct _FILTER_REQUEST +{ + NDIS_OID_REQUEST Request; + NDIS_EVENT ReqEvent; + NDIS_STATUS Status; +} FILTER_REQUEST, *PFILTER_REQUEST; + +// +// Define the filter struct +// +typedef struct _MS_FILTER +{ + LIST_ENTRY FilterModuleLink; + //Reference to this filter + ULONG RefCount; + + NDIS_HANDLE FilterHandle; + NDIS_STRING FilterModuleName; + NDIS_STRING MiniportFriendlyName; + NDIS_STRING MiniportName; + NET_IFINDEX MiniportIfIndex; + + NDIS_STATUS Status; + NDIS_EVENT Event; + ULONG BackFillSize; + FILTER_LOCK Lock; // Lock for protection of state and outstanding sends and recvs + + FILTER_STATE State; // Which state the filter is in + ULONG OutstandingSends; + ULONG OutstandingRequest; + ULONG OutstandingRcvs; + FILTER_LOCK SendLock; + FILTER_LOCK RcvLock; + QUEUE_HEADER SendNBLQueue; + QUEUE_HEADER RcvNBLQueue; + + + NDIS_STRING FilterName; + ULONG CallsRestart; + BOOLEAN TrackReceives; + BOOLEAN TrackSends; +#if DBG + BOOLEAN bIndicating; +#endif + + PNDIS_OID_REQUEST PendingOidRequest; + +}MS_FILTER, * PMS_FILTER; + + +typedef struct _FILTER_DEVICE_EXTENSION +{ + ULONG Signature; + NDIS_HANDLE Handle; +} FILTER_DEVICE_EXTENSION, *PFILTER_DEVICE_EXTENSION; + + +#define FILTER_READY_TO_PAUSE(_Filter) \ + ((_Filter)->State == FilterPausing) + +// +// The driver should maintain a list of NDIS filter handles +// +typedef struct _FL_NDIS_FILTER_LIST +{ + LIST_ENTRY Link; + NDIS_HANDLE ContextHandle; + NDIS_STRING FilterInstanceName; +} FL_NDIS_FILTER_LIST, *PFL_NDIS_FILTER_LIST; + +// +// The context inside a cloned request +// +typedef struct _NDIS_OID_REQUEST *FILTER_REQUEST_CONTEXT,**PFILTER_REQUEST_CONTEXT; + + +// +// function prototypes +// +DRIVER_INITIALIZE DriverEntry; + +FILTER_SET_OPTIONS FilterRegisterOptions; + +FILTER_ATTACH FilterAttach; + +FILTER_DETACH FilterDetach; + +DRIVER_UNLOAD FilterUnload; + +FILTER_RESTART FilterRestart; + +FILTER_PAUSE FilterPause; + +FILTER_OID_REQUEST FilterOidRequest; + +FILTER_CANCEL_OID_REQUEST FilterCancelOidRequest; + +FILTER_STATUS FilterStatus; + +FILTER_DEVICE_PNP_EVENT_NOTIFY FilterDevicePnPEventNotify; + +FILTER_NET_PNP_EVENT FilterNetPnPEvent; + +FILTER_OID_REQUEST_COMPLETE FilterOidRequestComplete; + +FILTER_SEND_NET_BUFFER_LISTS FilterSendNetBufferLists; + +FILTER_RETURN_NET_BUFFER_LISTS FilterReturnNetBufferLists; + +FILTER_SEND_NET_BUFFER_LISTS_COMPLETE FilterSendNetBufferListsComplete; + +FILTER_RECEIVE_NET_BUFFER_LISTS FilterReceiveNetBufferLists; + +FILTER_CANCEL_SEND_NET_BUFFER_LISTS FilterCancelSendNetBufferLists; + +FILTER_SET_MODULE_OPTIONS FilterSetModuleOptions; + + +_IRQL_requires_max_(PASSIVE_LEVEL) +NDIS_STATUS +FilterRegisterDevice( + VOID + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +FilterDeregisterDevice( + VOID + ); + +DRIVER_DISPATCH FilterDispatch; + +DRIVER_DISPATCH FilterDeviceIoControl; + +_IRQL_requires_max_(DISPATCH_LEVEL) +PMS_FILTER +filterFindFilterModule( + _In_reads_bytes_(BufferLength) + PUCHAR Buffer, + _In_ ULONG BufferLength + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +NDIS_STATUS +filterDoInternalRequest( + _In_ PMS_FILTER FilterModuleContext, + _In_ NDIS_REQUEST_TYPE RequestType, + _In_ NDIS_OID Oid, + _Inout_updates_bytes_to_(InformationBufferLength, *pBytesProcessed) + PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _In_opt_ ULONG OutputBufferLength, + _In_ ULONG MethodId, + _Out_ PULONG pBytesProcessed + ); + +VOID +filterInternalRequestComplete( + _In_ NDIS_HANDLE FilterModuleContext, + _In_ PNDIS_OID_REQUEST NdisRequest, + _In_ NDIS_STATUS Status + ); + + +#endif //_FILT_H + + diff --git a/network/ndis/filter/filter.rc b/network/ndis/filter/filter.rc new file mode 100644 index 00000000..a2fd2df6 --- /dev/null +++ b/network/ndis/filter/filter.rc @@ -0,0 +1,40 @@ +#include +#include + +/*-----------------------------------------------*/ +/* the following lines are specific to this file */ +/*-----------------------------------------------*/ + +/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR + * and VER_INTERNALNAME_STR must be defined before including COMMON.VER + * The strings don't need a '\0', since common.ver has them. + */ +#define VER_FILETYPE VFT_DRV +/* possible values: VFT_UNKNOWN + VFT_APP + VFT_DLL + VFT_DRV + VFT_FONT + VFT_VXD + VFT_STATIC_LIB +*/ +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +/* possible values VFT2_UNKNOWN + VFT2_DRV_PRINTER + VFT2_DRV_KEYBOARD + VFT2_DRV_LANGUAGE + VFT2_DRV_DISPLAY + VFT2_DRV_MOUSE + VFT2_DRV_NETWORK + VFT2_DRV_SYSTEM + VFT2_DRV_INSTALLABLE + VFT2_DRV_SOUND + VFT2_DRV_COMM +*/ +#define VER_FILEDESCRIPTION_STR "Sample NDIS 6.0 Filter Driver" +#define VER_INTERNALNAME_STR "NDISLWF.SYS" +#define VER_ORIGINALFILENAME_STR "NDISLWF.SYS" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/network/ndis/filter/filter.sln b/network/ndis/filter/filter.sln new file mode 100644 index 00000000..90e2be1f --- /dev/null +++ b/network/ndis/filter/filter.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ndislwf", "ndislwf.vcxproj", "{D6A797EC-4BD1-4448-8C69-AAD0F18252E3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D6A797EC-4BD1-4448-8C69-AAD0F18252E3}.Debug|Win32.ActiveCfg = Debug|Win32 + {D6A797EC-4BD1-4448-8C69-AAD0F18252E3}.Debug|Win32.Build.0 = Debug|Win32 + {D6A797EC-4BD1-4448-8C69-AAD0F18252E3}.Release|Win32.ActiveCfg = Release|Win32 + {D6A797EC-4BD1-4448-8C69-AAD0F18252E3}.Release|Win32.Build.0 = Release|Win32 + {D6A797EC-4BD1-4448-8C69-AAD0F18252E3}.Debug|x64.ActiveCfg = Debug|x64 + {D6A797EC-4BD1-4448-8C69-AAD0F18252E3}.Debug|x64.Build.0 = Debug|x64 + {D6A797EC-4BD1-4448-8C69-AAD0F18252E3}.Release|x64.ActiveCfg = Release|x64 + {D6A797EC-4BD1-4448-8C69-AAD0F18252E3}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/network/ndis/filter/filteruser.h b/network/ndis/filter/filteruser.h new file mode 100644 index 00000000..2a028752 --- /dev/null +++ b/network/ndis/filter/filteruser.h @@ -0,0 +1,89 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// +#ifndef __FILTERUSER_H__ +#define __FILTERUSER_H__ + +// +// Temp file to test filter +// + +#define _NDIS_CONTROL_CODE(request,method) \ + CTL_CODE(FILE_DEVICE_PHYSICAL_NETCARD, request, method, FILE_ANY_ACCESS) + +#define IOCTL_FILTER_RESTART_ALL _NDIS_CONTROL_CODE(0, METHOD_BUFFERED) +#define IOCTL_FILTER_RESTART_ONE_INSTANCE _NDIS_CONTROL_CODE(1, METHOD_BUFFERED) +#define IOCTL_FILTER_ENUERATE_ALL_INSTANCES _NDIS_CONTROL_CODE(2, METHOD_BUFFERED) +#define IOCTL_FILTER_QUERY_ALL_STAT _NDIS_CONTROL_CODE(3, METHOD_BUFFERED) +#define IOCTL_FILTER_CLEAR_ALL_STAT _NDIS_CONTROL_CODE(4, METHOD_BUFFERED) +#define IOCTL_FILTER_SET_OID_VALUE _NDIS_CONTROL_CODE(5, METHOD_BUFFERED) +#define IOCTL_FILTER_QUERY_OID_VALUE _NDIS_CONTROL_CODE(6, METHOD_BUFFERED) +#define IOCTL_FILTER_CANCEL_REQUEST _NDIS_CONTROL_CODE(7, METHOD_BUFFERED) +#define IOCTL_FILTER_READ_DRIVER_CONFIG _NDIS_CONTROL_CODE(8, METHOD_BUFFERED) +#define IOCTL_FILTER_WRITE_DRIVER_CONFIG _NDIS_CONTROL_CODE(9, METHOD_BUFFERED) +#define IOCTL_FILTER_READ_ADAPTER_CONFIG _NDIS_CONTROL_CODE(10, METHOD_BUFFERED) +#define IOCTL_FILTER_WRITE_ADAPTER_CONFIG _NDIS_CONTROL_CODE(11, METHOD_BUFFERED) +#define IOCTL_FILTER_READ_INSTANCE_CONFIG _NDIS_CONTROL_CODE(12, METHOD_BUFFERED) +#define IOCTL_FILTER_WRITE_INSTANCE_CONFIG _NDIS_CONTROL_CODE(13, METHOD_BUFFERED) + + +#define MAX_FILTER_INSTANCE_NAME_LENGTH 256 +#define MAX_FILTER_CONFIG_KEYWORD_LENGTH 256 +typedef struct _FILTER_DRIVER_ALL_STAT +{ + ULONG AttachCount; + ULONG DetachCount; + ULONG ExternalRequestFailedCount; + ULONG ExternalRequestSuccessCount; + ULONG InternalRequestFailedCount; +} FILTER_DRIVER_ALL_STAT, * PFILTER_DRIVER_ALL_STAT; + + +typedef struct _FILTER_SET_OID +{ + WCHAR InstanceName[MAX_FILTER_INSTANCE_NAME_LENGTH]; + ULONG InstanceNameLength; + NDIS_OID Oid; + NDIS_STATUS Status; + UCHAR Data[sizeof(ULONG)]; + +}FILTER_SET_OID, *PFILTER_SET_OID; + +typedef struct _FILTER_QUERY_OID +{ + WCHAR InstanceName[MAX_FILTER_INSTANCE_NAME_LENGTH]; + ULONG InstanceNameLength; + NDIS_OID Oid; + NDIS_STATUS Status; + UCHAR Data[sizeof(ULONG)]; + +}FILTER_QUERY_OID, *PFILTER_QUERY_OID; + +typedef struct _FILTER_READ_CONFIG +{ + _Field_size_bytes_part_(MAX_FILTER_INSTANCE_NAME_LENGTH,InstanceNameLength) + WCHAR InstanceName[MAX_FILTER_INSTANCE_NAME_LENGTH]; + ULONG InstanceNameLength; + _Field_size_bytes_part_(MAX_FILTER_CONFIG_KEYWORD_LENGTH,KeywordLength) + WCHAR Keyword[MAX_FILTER_CONFIG_KEYWORD_LENGTH]; + ULONG KeywordLength; + NDIS_PARAMETER_TYPE ParameterType; + NDIS_STATUS Status; + UCHAR Data[sizeof(ULONG)]; +}FILTER_READ_CONFIG, *PFILTER_READ_CONFIG; + +typedef struct _FILTER_WRITE_CONFIG +{ + _Field_size_bytes_part_(MAX_FILTER_INSTANCE_NAME_LENGTH,InstanceNameLength) + WCHAR InstanceName[MAX_FILTER_INSTANCE_NAME_LENGTH]; + ULONG InstanceNameLength; + _Field_size_bytes_part_(MAX_FILTER_CONFIG_KEYWORD_LENGTH,KeywordLength) + WCHAR Keyword[MAX_FILTER_CONFIG_KEYWORD_LENGTH]; + ULONG KeywordLength; + NDIS_PARAMETER_TYPE ParameterType; + NDIS_STATUS Status; + UCHAR Data[sizeof(ULONG)]; +}FILTER_WRITE_CONFIG, *PFILTER_WRITE_CONFIG; + +#endif //__FILTERUSER_H__ + diff --git a/network/ndis/filter/flt_dbg.c b/network/ndis/filter/flt_dbg.c new file mode 100644 index 00000000..1d52071d --- /dev/null +++ b/network/ndis/filter/flt_dbg.c @@ -0,0 +1,409 @@ +/*++ + +Copyright (c) 2001 Microsoft Corporation + +Module Name: + + debug.c + +Abstract: + + This module contains all debug-related code. + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + +Notes: + +--*/ + +#include "precomp.h" + +#define __FILENUMBER 'GBED' + +#if DBG + +INT filterDebugLevel = DL_WARN; + +NDIS_SPIN_LOCK filterDbgLogLock; + +PFILTERD_ALLOCATION filterdMemoryHead = (PFILTERD_ALLOCATION)NULL; +PFILTERD_ALLOCATION filterdMemoryTail = (PFILTERD_ALLOCATION)NULL; +ULONG filterdAllocCount = 0; // how many allocated so far (unfreed) + +NDIS_SPIN_LOCK filterdMemoryLock; +BOOLEAN filterdInitDone = FALSE; + + +PVOID +filterAuditAllocMem( + NDIS_HANDLE NdisHandle, + ULONG Size, + ULONG FileNumber, + ULONG LineNumber +) +{ + PVOID pBuffer; + PFILTERD_ALLOCATION pAllocInfo; + + if (!filterdInitDone) + { + NdisAllocateSpinLock(&(filterdMemoryLock)); + filterdInitDone = TRUE; + } + + // + // Integer overflow check + // + if ((Size + (ULONG) sizeof(FILTERD_ALLOCATION)) < Size) + { + DEBUGP(DL_VERY_LOUD+50, + "filterAuditAllocMem: Integer overflow error file %d, line %d, Size %d \n", + FileNumber, LineNumber, Size); + + pBuffer = NULL; + } + else + { + + pAllocInfo = NdisAllocateMemoryWithTagPriority( + NdisHandle, + Size+sizeof(FILTERD_ALLOCATION), + (ULONG)'gdTF', + LowPoolPriority + ); + + if (pAllocInfo == (PFILTERD_ALLOCATION)NULL) + { + DEBUGP(DL_VERY_LOUD+50, + "filterAuditAllocMem: file %d, line %d, Size %d failed!\n", + FileNumber, LineNumber, Size); + pBuffer = NULL; + } + else + { + pBuffer = (PVOID)&(pAllocInfo->UserData); + NdisFillMemory(pBuffer, Size, 0xaf); + pAllocInfo->Signature = FILTERD_MEMORY_SIGNATURE; + pAllocInfo->FileNumber = FileNumber; + pAllocInfo->LineNumber = LineNumber; + pAllocInfo->Size = Size; + pAllocInfo->OwnerHandle = NdisHandle; + pAllocInfo->Next = (PFILTERD_ALLOCATION)NULL; + + NdisAcquireSpinLock(&(filterdMemoryLock)); + + pAllocInfo->Prev = filterdMemoryTail; + if (filterdMemoryTail == (PFILTERD_ALLOCATION)NULL) + { + // + // empty list + // + filterdMemoryHead = filterdMemoryTail = pAllocInfo; + } + else + { + filterdMemoryTail->Next = pAllocInfo; + } + filterdMemoryTail = pAllocInfo; + + filterdAllocCount++; + NdisReleaseSpinLock(&(filterdMemoryLock)); + } + } + + DEBUGP(DL_VERY_LOUD+100, + "filterAuditAllocMem: file %c%c%c%c, line %d, %d bytes, OwnerHandle %p, Memory 0x%p\n", + (CHAR)(FileNumber & 0xff), + (CHAR)((FileNumber >> 8) & 0xff), + (CHAR)((FileNumber >> 16) & 0xff), + (CHAR)((FileNumber >> 24) & 0xff), + LineNumber, Size, NdisHandle, pBuffer); + + return (pBuffer); + +} + + +VOID +filterAuditFreeMem( + PVOID Pointer +) +{ + PFILTERD_ALLOCATION pAllocInfo; + + NdisAcquireSpinLock(&(filterdMemoryLock)); + + pAllocInfo = CONTAINING_RECORD(Pointer, FILTERD_ALLOCATION, UserData); + + if (pAllocInfo->Signature != FILTERD_MEMORY_SIGNATURE) + { + DEBUGP(DL_ERROR, + "filterAuditFreeMem: unknown buffer 0x%p!\n", Pointer); + NdisReleaseSpinLock(&(filterdMemoryLock)); +#if DBG + DbgBreakPoint(); +#endif + return; + } + + pAllocInfo->Signature = (ULONG)'DEAD'; + if (pAllocInfo->Prev != (PFILTERD_ALLOCATION)NULL) + { + pAllocInfo->Prev->Next = pAllocInfo->Next; + } + else + { + filterdMemoryHead = pAllocInfo->Next; + } + if (pAllocInfo->Next != (PFILTERD_ALLOCATION)NULL) + { + pAllocInfo->Next->Prev = pAllocInfo->Prev; + } + else + { + filterdMemoryTail = pAllocInfo->Prev; + } + filterdAllocCount--; + NdisReleaseSpinLock(&(filterdMemoryLock)); + + NdisFreeMemory(pAllocInfo, 0, 0); +} + + +VOID +filterAuditShutdown( + VOID +) +{ + if (filterdInitDone) + { + if (filterdAllocCount != 0) + { + DEBUGP(DL_ERROR, "AuditShutdown: unfreed memory, %d blocks!\n", + filterdAllocCount); + DEBUGP(DL_ERROR, "MemoryHead: 0x%p, MemoryTail: 0x%p\n", + filterdMemoryHead, filterdMemoryTail); + DbgBreakPoint(); + { + PFILTERD_ALLOCATION pAllocInfo; + + while (filterdMemoryHead != (PFILTERD_ALLOCATION)NULL) + { + pAllocInfo = filterdMemoryHead; + DEBUGP(DL_INFO, "AuditShutdown: will free 0x%p\n", pAllocInfo); + filterAuditFreeMem(&(pAllocInfo->UserData)); + } + } + } + filterdInitDone = FALSE; + } +} + +#define MAX_HD_LENGTH 128 + +VOID +DbgPrintHexDump( + IN PUCHAR pBuffer, + IN ULONG Length +) +/*++ + +Routine Description: + + Print a hex dump of the given contiguous buffer. If the length + is too long, we truncate it. + +Arguments: + + pBuffer - Points to start of data to be dumped + Length - Length of above. + +Return Value: + + None + +--*/ +{ + ULONG i; + + if (Length > MAX_HD_LENGTH) + { + Length = MAX_HD_LENGTH; + } + + for (i = 0; i < Length; i++) + { + // + // Check if we are at the end of a line + // + if ((i > 0) && ((i & 0xf) == 0)) + { + DbgPrint("\n"); + } + + // + // Print addr if we are at start of a new line + // + if ((i & 0xf) == 0) + { + DbgPrint("%08p ", pBuffer); + } + + DbgPrint(" %02x", *pBuffer++); + } + + // + // Terminate the last line. + // + if (Length > 0) + { + DbgPrint("\n"); + } +} +#endif // DBG + + +#if DBG_SPIN_LOCK +ULONG filterdSpinLockInitDone = 0; +NDIS_SPIN_LOCK filterdLockLock; + +VOID +filterAllocateSpinLock( + _In_ PFILTER_LOCK pLock, + _In_ ULONG FileNumber, + _In_ ULONG LineNumber +) +{ + if (filterdSpinLockInitDone == 0) + { + filterdSpinLockInitDone = 1; + NdisAllocateSpinLock(&(filterdLockLock)); + } + + NdisAcquireSpinLock(&(filterdLockLock)); + pLock->Signature = FILT_LOCK_SIG; + pLock->TouchedByFileNumber = FileNumber; + pLock->TouchedInLineNumber = LineNumber; + pLock->IsAcquired = 0; + pLock->OwnerThread = 0; + NdisAllocateSpinLock(&(pLock->NdisLock)); + NdisReleaseSpinLock(&(filterdLockLock)); +} + +VOID +filterFreeSpinLock( + _In_ PFILTER_LOCK pLock + ) +{ + ASSERT(filterdSpinLockInitDone == 1); + + + NdisFreeSpinLock(&(filterdLockLock)); + filterdSpinLockInitDone = 0; + NdisFreeSpinLock(&(pLock->NdisLock)); + +} + + + +VOID +filterAcquireSpinLock( + _In_ PFILTER_LOCK pLock, + _In_ ULONG FileNumber, + _In_ ULONG LineNumber, + _In_ BOOLEAN DispatchLevel +) +{ + if (DispatchLevel) + { + NdisDprAcquireSpinLock(&(filterdLockLock)); + } + else + { + NdisAcquireSpinLock(&(filterdLockLock)); + } + if (pLock->Signature != FILT_LOCK_SIG) + { + DbgPrint("Trying to acquire uninited lock 0x%x, File %c%c%c%c, Line %d\n", + pLock, + (CHAR)(FileNumber & 0xff), + (CHAR)((FileNumber >> 8) & 0xff), + (CHAR)((FileNumber >> 16) & 0xff), + (CHAR)((FileNumber >> 24) & 0xff), + LineNumber); + DbgBreakPoint(); + } + + + pLock->IsAcquired++; + + if (DispatchLevel) + { + NdisDprReleaseSpinLock(&(filterdLockLock)); + NdisDprAcquireSpinLock(&(pLock->NdisLock)); + } + else + { + NdisReleaseSpinLock(&(filterdLockLock)); + NdisAcquireSpinLock(&(pLock->NdisLock)); + } + + // + // Mark this lock. + // + pLock->TouchedByFileNumber = FileNumber; + pLock->TouchedInLineNumber = LineNumber; +} + + +VOID +filterReleaseSpinLock( + _In_ PFILTER_LOCK pLock, + _In_ ULONG FileNumber, + _In_ ULONG LineNumber, + _In_ BOOLEAN DispatchLevel +) +{ + NdisDprAcquireSpinLock(&(filterdLockLock)); + if (pLock->Signature != FILT_LOCK_SIG) + { + DbgPrint("Trying to release uninited lock 0x%x, File %c%c%c%c, Line %d\n", + pLock, + (CHAR)(FileNumber & 0xff), + (CHAR)((FileNumber >> 8) & 0xff), + (CHAR)((FileNumber >> 16) & 0xff), + (CHAR)((FileNumber >> 24) & 0xff), + LineNumber); + DbgBreakPoint(); + } + + if (pLock->IsAcquired == 0) + { + DbgPrint("Detected release of unacquired lock 0x%x, File %c%c%c%c, Line %d\n", + pLock, + (CHAR)(FileNumber & 0xff), + (CHAR)((FileNumber >> 8) & 0xff), + (CHAR)((FileNumber >> 16) & 0xff), + (CHAR)((FileNumber >> 24) & 0xff), + LineNumber); + DbgBreakPoint(); + } + pLock->TouchedByFileNumber = FileNumber; + pLock->TouchedInLineNumber = LineNumber; + pLock->IsAcquired--; + NdisDprReleaseSpinLock(&(filterdLockLock)); + + if (DispatchLevel) + { + NdisDprReleaseSpinLock(&(pLock->NdisLock)); + } + else + { + NdisReleaseSpinLock(&(pLock->NdisLock)); + } +} +#endif // DBG_SPIN_LOCK + diff --git a/network/ndis/filter/flt_dbg.h b/network/ndis/filter/flt_dbg.h new file mode 100644 index 00000000..f3513588 --- /dev/null +++ b/network/ndis/filter/flt_dbg.h @@ -0,0 +1,195 @@ +/*++ + +Copyright (c) 2001 Microsoft Corporation + +Module Name: + + debug.h + +Abstract: + + This module contains all debug related prototypes for MS FILTER + +Revision History: + + +Notes: + +--*/ + +// disable warnings + + +#ifndef _FILTDEBUG__H +#define _FILTDEBUG__H + +// +// Message verbosity: lower values indicate higher urgency +// +#define DL_EXTRA_LOUD 20 +#define DL_VERY_LOUD 10 +#define DL_LOUD 8 +#define DL_INFO 6 +#define DL_TRACE 5 +#define DL_WARN 4 +#define DL_ERROR 2 +#define DL_FATAL 0 + +#if DBG_SPIN_LOCK + +typedef struct _FILTER_LOCK +{ + ULONG Signature; + ULONG IsAcquired; + ULONG TouchedByFileNumber; + ULONG TouchedInLineNumber; + NDIS_SPIN_LOCK NdisLock; +} FILTER_LOCK, *PFILTER_LOCK; + +#define FILT_LOCK_SIG 'kcoL' + +extern NDIS_SPIN_LOCK filterDbgLogLock; + +extern +VOID +filterAllocateSpinLock( + IN PFILTER_LOCK pLock, + IN ULONG FileNumber, + IN ULONG LineNumber +); + +extern +VOID +filterFreeSpinLock( + IN PFILTER_LOCK pLock + +); + + +extern +VOID +filterAcquireSpinLock( + IN PFILTER_LOCK pLock, + IN ULONG FileNumber, + IN ULONG LineNumber, + IN BOOLEAN DispatchLevel +); + +extern +VOID +filterReleaseSpinLock( + IN PFILTER_LOCK pLock, + IN ULONG FileNumber, + IN ULONG LineNumber, + IN BOOLEAN DispatchLevel +); + + +#else + +typedef NDIS_SPIN_LOCK FILTER_LOCK; +typedef PNDIS_SPIN_LOCK PFILTER_LOCK; + +#endif // DBG_SPIN_LOCK + +#if DBG + +extern INT filterDebugLevel; + + +#define DEBUGP(lev, ...) \ + { \ + if ((lev) <= filterDebugLevel) \ + { \ + DbgPrint("NDISLWF: "); DbgPrint(__VA_ARGS__); \ + } \ + } + +#define DEBUGPDUMP(lev, pBuf, Len) \ + { \ + if ((lev) <= filterDebugLevel) \ + { \ + DbgPrintHexDump((PUCHAR)(pBuf), (ULONG)(Len)); \ + } \ + } + +#define FILTER_ASSERT(exp) \ + { \ + if (!(exp)) \ + { \ + DbgPrint("Filter: assert " #exp " failed in" \ + " file %s, line %d\n", __FILE__, __LINE__); \ + DbgBreakPoint(); \ + } \ + } + + + +// +// Memory Allocation/Freeing Audit: +// + +// +// The FILTER_ALLOCATION structure stores all info about one allocation +// +typedef struct _FILTERD_ALLOCATION { + + ULONG Signature; + struct _FILTERD_ALLOCATION *Next; + struct _FILTERD_ALLOCATION *Prev; + ULONG FileNumber; + ULONG LineNumber; + ULONG Size; + NDIS_HANDLE OwnerHandle; + union + { + ULONGLONG Alignment; + UCHAR UserData; + }; + +} FILTERD_ALLOCATION, *PFILTERD_ALLOCATION; + +#define FILTERD_MEMORY_SIGNATURE (ULONG)'TFSM' + +extern +PVOID +filterAuditAllocMem ( + NDIS_HANDLE NdisHandle, + ULONG Size, + ULONG FileNumber, + ULONG LineNumber +); + +extern +VOID +filterAuditFreeMem( + PVOID Pointer +); + +extern +VOID +filterAuditShutdown( + VOID +); + +extern +VOID +DbgPrintHexDump( + PUCHAR pBuffer, + ULONG Length +); + +#else + +// +// No debug +// +#define DEBUGP(lev, ...) +#define DEBUGPDUMP(lev, pBuf, Len) + +#define FILTER_ASSERT(exp) + +#endif // DBG + + +#endif // _FILTDEBUG__H diff --git a/network/ndis/filter/ndislwf.vcxproj b/network/ndis/filter/ndislwf.vcxproj new file mode 100644 index 00000000..6998e55b --- /dev/null +++ b/network/ndis/filter/ndislwf.vcxproj @@ -0,0 +1,283 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D6A797EC-4BD1-4448-8C69-AAD0F18252E3} + $(MSBuildProjectName) + Debug + Win32 + {D42F08CB-C626-4709-A3FB-F3A6AC78F6BE} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + ndislwf + + + ndislwf + + + ndislwf + + + ndislwf + + + + true + Level4 + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + %(DisableSpecificWarnings);4201;4214 + + + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + + + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + %(DisableSpecificWarnings);4201;4214 + + + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + + + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + %(DisableSpecificWarnings);4201;4214 + + + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + + + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + + + + + true + Level4 + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + %(DisableSpecificWarnings);4201;4214 + + + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + + + %(AdditionalIncludeDirectories);..;. + %(PreprocessorDefinitions);NDIS60=1 + %(PreprocessorDefinitions);NDIS620=1 + %(PreprocessorDefinitions);NDIS630=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(PreprocessorDefinitions);NDISLWF=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/filter/ndislwf.vcxproj.Filters b/network/ndis/filter/ndislwf.vcxproj.Filters new file mode 100644 index 00000000..1258cc3b --- /dev/null +++ b/network/ndis/filter/ndislwf.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {CCCEB33C-30D7-4D55-9C51-2503E8422C74} + + + h;hpp;hxx;hm;inl;inc;xsd + {F2D27727-5C77-4D5A-8C78-D4977DA98B1E} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {D2E50D90-0B4D-449A-8527-9EA78D1C23F7} + + + inf;inv;inx;mof;mc; + {3EF75BEF-85CD-4689-B551-9CFC67497380} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/filter/netlwf.inf b/network/ndis/filter/netlwf.inf new file mode 100644 index 00000000..1f55291e --- /dev/null +++ b/network/ndis/filter/netlwf.inf @@ -0,0 +1,202 @@ +;------------------------------------------------------------------------- +; NdisLwf.INF -- Sample NDIS LightWeight Filter Driver +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +; TODO: Search for comments marked "TODO:", and follow their instructions to +; customize this INF for your driver. Then delete the "TODO:" comments. +;------------------------------------------------------------------------- + +[version] +; Do not change these values +Signature = "$Windows NT$" +Class = NetService +ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} +CatalogFile = netlwf.cat + +; TODO: Customize this string for your company name +Provider = %Msft% + +; TODO: Customize this string for the driver version +DriverVer = 10/01/2002,6.0.5019.0 + + +; TODO: Include each architecture for which your driver package contains a +; compiled binary. If you do not supply a driver compiled for ia64, delete the +; NTia64 section. +[Manufacturer] +%Msft%=MSFT,NTx86,NTia64,NTamd64 + +; TODO: Change the "MS_NdisLwf" name below to identify your component. +; This name can be used with netcfg.exe to install/uninstall the driver. +[MSFT.NTx86] +%NdisLwf_Desc%=Install, MS_NdisLwf + +[MSFT.NTia64] +%NdisLwf_Desc%=Install, MS_NdisLwf + +[MSFT.NTamd64] +%NdisLwf_Desc%=Install, MS_NdisLwf + +;------------------------------------------------------------------------- +; Installation Section +;------------------------------------------------------------------------- +[Install] +AddReg=Inst_Ndi +; All LWFs must include the 0x40000 bit (NCF_LW_FILTER). Unlike miniports, you +; don't usually need to customize this value. +Characteristics=0x40000 + +; TODO: You must generate your own random GUID and insert it here. Then, update +; FILTER_UNIQUE_NAME in filter.h to match this GUID identically. Both should +; have {curly braces}. +NetCfgInstanceId="{5cbf81bd-5055-47cd-9055-a76b2b4e3697}" + +Copyfiles = ndislwf.copyfiles.sys + +[SourceDisksNames] +1=%NdisLwf_Desc%,"",, + +[SourceDisksFiles] +; TODO: Customize the name of your binary here. +; Also include any related files that should be installed with your driver. +ndislwf.sys=1 + +[DestinationDirs] +DefaultDestDir=12 +ndislwf.copyfiles.sys=12 + +[ndislwf.copyfiles.sys] +; TODO: Customize the name of your binary here. +ndislwf.sys,,,2 + + +;------------------------------------------------------------------------- +; Ndi installation support +;------------------------------------------------------------------------- +[Inst_Ndi] +; TODO: Customize the name of the NT service that will start your driver. +; Then, update FILTER_SERVICE_NAME in filter.h. +HKR, Ndi,Service,,"NdisLwf" +; TODO: Repeat the NT service name above here. Optionally, you may control +; other services alongside the primary service. +HKR, Ndi,CoServices,0x00010000,"NdisLwf" +HKR, Ndi,HelpText,,%NdisLwf_HelpText% +; TODO: Set the FilterClass here. The FilterClass controls the order in which +; filters are bound to the underlying miniport. Possible options include: +; Custom, Diagnostic, Failover, Loadbalance, Vpn, Compression, Encryption, Scheduler +; See MSDN for a description of each. +HKR, Ndi,FilterClass,, compression +; TODO: Specify whether you have a Modifying or Monitoring filter. +; For a Monitoring filter, use this: +; HKR, Ndi,FilterType,0x00010001, 1 ; Monitoring filter +; For a Modifying filter, use this: +; HKR, Ndi,FilterType,0x00010001, 2 ; Modifying filter +HKR, Ndi,FilterType,0x00010001,2 +; Do not change these values +HKR, Ndi\Interfaces,UpperRange,,"noupper" +HKR, Ndi\Interfaces,LowerRange,,"nolower" +; TODO: Ensure that the list of media types below is correct. Typically, +; filters include "ethernet". Filters may also include "ppip" to include +; native WWAN stacks, but you must be prepared to handle the packet framing. +; Possible values are listed on MSDN, but common values include: +; ethernet, wan, ppip, wlan +HKR, Ndi\Interfaces, FilterMediaTypes,,"ethernet, wan, ppip" +; TODO: Specify whether you have a Mandatory or Optional filter. +; For a Mandatory filter, use this: +; HKR, Ndi,FilterRunType,0x00010001, 1 ; Mandatory filter +; For an Optional filter, use this: +; HKR, Ndi,FilterRunType,0x00010001, 2 ; Optional filter +HKR, Ndi,FilterRunType,0x00010001, 1 ; Mandatory filter + +; By default, Mandatory filters unbind all protocols when they are +; installed/uninstalled, while Optional filters merely pause the stack. If you +; would like to override this behavior, you can include these options. These +; options only take effect with 6.30 filters on Windows "8" or later. +; To prevent a full unbind, and merely pause/restart protocols: +; HKR, Ndi,UnbindOnAttach,0x00010001, 0 ; Do not unbind during FilterAttach +; HKR, Ndi,UnbindOnDetach,0x00010001, 0 ; Do not unbind during FilterDetach +; To force a full unbind/bind (which includes pause/restart, of course): +; HKR, Ndi,UnbindOnAttach,0x00010001, 1 ; Unbind during FilterAttach +; HKR, Ndi,UnbindOnDetach,0x00010001, 1 ; Unbind during FilterDetach +; + +; NDIS can start a miniport stack even if some Optional filters are +; missing. However, NDIS reserves the right to wait for the Optional +; filters to be registered, since it's faster to attach a filter if +; the protocols haven't been bound yet. +; If your Optional filter is unlikely to be started at boot, you can +; use this hint to inform NDIS to spend less time waiting for your +; filter. Note this setting is ignored for Mandatory filters; NDIS +; always waits forever for all Mandatory filters. +; HKR, Ndi,NdisBootStart,0x00010001, 0 ; Don't wait for this driver to start at boot +; Enable this setting only if your filter will be bound, but the driver +; is not usually started at boot. If your driver will typically start +; at boot, then enabling this setting would defeat the NDIS heuristic +; and slightly slow down boot. + +;------------------------------------------------------------------------- +; Service installation support +;------------------------------------------------------------------------- +[Install.Services] +; TODO: Customize the name of your NT service here. +; You may also want to add the SPSVCINST_STARTSERVICE flag, like this: +; AddService=NdisLwf,0x800,NdisLwf_Service_Inst ; SPSVCINST_STARTSERVICE +AddService=NdisLwf,,NdisLwf_Service_Inst + +[NdisLwf_Service_Inst] +DisplayName = %NdisLwf_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +; Typically you will want your filter driver to start with SERVICE_SYSTEM_START. +; If it is an Optional filter, you may also use 3;SERVICE_DEMAND_START. +StartType = 1 ;SERVICE_SYSTEM_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +; TODO: Customize this to your binary's filename. +ServiceBinary = %12%\ndislwf.sys +LoadOrderGroup = NDIS +Description = %NdisLwf_Desc% +AddReg = Common.Params.reg, NdisImPlatformBindingOptions.reg + +[Install.Remove.Services] +; TODO: Customize the name of your NT service here. +; The SPSVCINST_STOPSERVICE flag instructs SCM to stop the NT service +; before uninstalling the driver. +DelService=NdisLwf,0x200 ; SPSVCINST_STOPSERVICE + +[Common.Params.reg] +; TODO: You can add any sort of NDIS parameters here. Filter drivers +; don't always need NDIS parameters, so it's okay to have nothing here. + +; TODO: Remove the sample parameters below. + +; Sample 1: "DriverParam" is a per-driver parameter. +HKR, FilterDriverParams\DriverParam, ParamDesc, , "Driverparam for lwf" +HKR, FilterDriverParams\DriverParam, default, , "5" +HKR, FilterDriverParams\DriverParam, type, , "int" + +; Sample 2: "AdapterParam" is a per-module parameter. +HKR, FilterAdapterParams\AdapterParam, ParamDesc, , "Adapterparam for lwf" +HKR, FilterAdapterParams\AdapterParam, default, , "10" +HKR, FilterAdapterParams\AdapterParam, type, , "int" + +[NdisImPlatformBindingOptions.reg] +; By default, when an LBFO team or Bridge is created, all filters will be +; unbound from the underlying members and bound to the TNic(s). This keyword +; allows a component to opt out of the default behavior +; To prevent binding this filter to the TNic(s): +; HKR, Parameters, NdisImPlatformBindingOptions,0x00010001,1 ; Do not bind to TNic +; To prevent unbinding this filter from underlying members: +; HKR, Parameters, NdisImPlatformBindingOptions,0x00010001,2 ; Do not unbind from Members +; To prevent both binding to TNic and unbinding from members: +; HKR, Parameters, NdisImPlatformBindingOptions,0x00010001,3 ; Do not bind to TNic or unbind from Members +HKR, Parameters, NdisImPlatformBindingOptions,0x00010001,0 ; Subscribe to default behavior + +[Strings] +; TODO: Customize these strings. +Msft = "Your Company" +NdisLwf_Desc = "NDIS Sample LightWeight Filter" +NdisLwf_HelpText = "Sample to demonstrate NDIS LightWeight Filters" + + + + diff --git a/network/ndis/filter/precomp.h b/network/ndis/filter/precomp.h new file mode 100644 index 00000000..96cdb488 --- /dev/null +++ b/network/ndis/filter/precomp.h @@ -0,0 +1,6 @@ +#pragma warning(disable:4201) //nonstandard extension used : nameless struct/union +#include +#include +#include "flt_dbg.h" +#include "filter.h" + diff --git a/network/ndis/filter/precompsrc.c b/network/ndis/filter/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/network/ndis/filter/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/network/ndis/mux/ReadMe.md b/network/ndis/mux/ReadMe.md new file mode 100644 index 00000000..e22050fe --- /dev/null +++ b/network/ndis/mux/ReadMe.md @@ -0,0 +1,174 @@ +NDIS MUX Intermediate Driver and Notify Object +============================================== + +The MUX Intermediate Miniport (IM) driver is an NDIS 6.0 driver that demonstrates the operation of an “N:1†MUX driver.The sample demonstrates creating multiple virtual network devices on top of a single lower adapter. Protocols bind to these virtual adapters as if they are real adapters. Examples of Intermediate Miniport drivers that can use this framework are Virtual LAN (VLAN) drivers. Included in the project is a sample Notify Object that demonstrates how to write a notify object for installing and configuring an NDIS MUX intermediate miniport (IM) driver that implements an N:1 relationship between upper and lower bindings, i.e. it creates multiple virtual network devices on top of a single lower adapter. Protocols bind to these virtual adapters as if they are real adapters. Examples of Intermediate Miniport drivers that can use this type of notify object are Virtual LAN (VLAN) drivers. + +For more information, see [NDIS Intermediate Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff565773) in the network devices design guide. + +INSTALLING THE SAMPLE +--------------------- + +MUX is installed as a protocol (called *Sample Mux-IM Protocol Driver* in the supplied INFs/notification object). + +To install, follow the steps below: + +1. Prepare an installation directory that contains these files: muxp.inf, mux\_mp.inf, mux.sys and mux.dll (notification object DLL, built in this DDK at network\\ndis\\mux\\notifyob). +2. On the desktop, right-click the **My Network Places** icon and choose **Properties**. +3. Right-click on the relevant **Local Area Connection** icon and choose **Properties**. +4. Click **Install**, then **Protocol**, then **Add**, then **Have Disk**. +5. Browse to the drive/directory containing the files listed above. Click **OK**. This should show **Sample Mux-IM Protocol Driver** in a list of Network Protocols. Highlight this and click **OK**. This should install the MUX driver. +6. Click **OK** or **Yes** each time the system prompts with a warning regarding installation of unsigned files. This is necessary because binaries generated via the DDK build environment are not signed. + +Two .INF files are needed rather than one because MUX is installed both as a protocol and a miniport. + +NDIS MUX Intermediate Driver +---------------------------- + +The driver binds to Ethernet (NdisMedium802\_3) adapters as a protocol, and exposes one or more virtual Ethernet devices over each lower adapter, based on its configuration. The term “VELAN†is used to denote a Virtual Ethernet LAN adapter implemented by this driver. + +When it binds to a lower adapter, MUX reads the standard “UpperBind†key to obtain a list of VELANs configured over this adapter. For each such VELAN, it calls NdisIMInitializeDeviceInstanceEx() to instantiate the NDIS miniport for the VELAN. NDIS then calls the driver’s MiniportInitialize (MPInitialize) routine to start the VELAN miniport. + +The MUX driver supports configuring the MAC address for each VELAN miniport using the standard “NetworkAddress†key that it reads from its MiniportInitialize routine. If this is not configured, it computes a “locally significant†MAC address for the VELAN using the MAC address of the lower adapter. The MUX driver sets its lower adapter to promiscuous mode in order to be able to receive frames directed to any of the VELAN MAC addresses. However it does implement packet-filtering (and multicast address filtering) logic for all its VELAN miniports so that it only passes up relevant frames on each VELAN. This aspect of the driver may be modified if, for example, your driver design uses the same MAC address as that of the lower adapter on all VELANs. With such a modification, it is not required to set the lower adapter to promiscuous mode and incur the costs of receiving all packets on the network. + +It supports dynamic addition and deletion of VELANs in conjunction with its notify object (related sample). If a VELAN is deleted, the virtual device corresponding to the VELAN is stopped and removed, which in turn results in NDIS halting the miniport instance for the VELAN (see MPHalt). If a VELAN is added, NDIS sends a global reconfiguration event to the protocol edge of this driver. The handler function for this event, PtPNPHandler, goes through all lower adapters to see if any new VELANs have been added, i.e. if any of the “UpperBind†keys have been modified. + +Since the driver implements a virtual device, it does not simply pass through most NDIS queries/sets. It keeps its own device view that is reflected in its responses to queries/sets. However it does pass through queries/sets for certain OIDs that are best handled by the lower adapter driver. + +The driver supports Power Management in the sense that it allows Wake-On-LAN and related functionality, if supported by the lower adapter, to continue to function. It does so by appropriately forwarding OID\_PNP\_XXX queries/sets to the lower adapter. + +### IEEE 802.1Q VLAN Operation + +The driver supports configuring a VLAN ID on each VELAN. It then inserts a tag header containing this VLAN ID on all outgoing frames. For incoming frames that contain a tag header, it verifies that a matching VLAN ID is present before indicating it up to protocols. It removes the tag header, if present, from all indicated frames. In all cases, received frames that do not contain tag headers are always handed up to protocols. + +With the default configured VLAN ID of zero, the driver does not insert tag header information on sent packets, except for sent packets that contain non-zero Ieee8021QInfo per-packet information, for which the driver does insert corresponding tag headers. Receive-side filtering on VLAN ID is enabled only with a non-zero configured VLAN ID, in which case only received frames containing a matching VLAN ID are passed up. With the default configured VLAN ID of zero, the driver does not check the VLAN ID on received frames. + +### Configuring VLANs + +The VLAN ID for each VELAN (virtual miniport) can be configured as follows. Right-click on the virtual miniport Local Area Connection icon and choose Properties. Click on the Configure button to bring up the Device Manager UI for the virtual device. Select the Advanced property sheet – this should contain a “VLAN ID†parameter that is configurable to the desired VLAN ID. Choosing a value of 0 (zero) disables receive-side filtering based on VLAN ID. + +### Programming Tour + +When it loads, i.e. from its DriverEntry function, the MUX driver registers as an Intermediate miniport driver and as a protocol, in that order. + +### Binding and VELAN Creation + +NDIS calls MUX’s BindAdapter function, `PtBindAdapter`, for each underlying NDIS adapter to which it is configured to bind. This function allocates an `ADAPT` structure to represent the lower adapter, and calls `NdisOpenAdapter` to set up a binding to it. In the context of `BindAdapterHandler`, after successfully opening a binding to the underlying adapter, the driver queries the reserved keyword "UpperBindings" to get a list of device names for the virtual adapters that this particular binding is to expose – see `PtBootStrapVElans` for more details. Note that the MUX driver does not create bindings (i.e. call `NdisOpenAdapter`) from any context other than its BindAdapter function – this is recommended behavior for all drivers of this type. + +For each device name specified in the “UpperBindings†key, the MUX driver allocates a VELAN data structure to represent the virtual miniport, calls `NdisIMInitializeDeviceInstanceEx`. In response, NDIS eventually calls the MUX miniport’s MiniportInitialize entry point, MPInitialize, for each VELAN. After MPInitialize successfully returns, NDIS takes care of getting upper-layer protocols to bind to the newly created virtual adapter(s). + +### Unbinding and Halting + +NDIS calls MUX’s `UnbindAdapter` handler, `PtUnbindAdapter`, to request it to unbind from a lower adapter. In processing this, MUX calls `NdisIMDeInitializeDeviceInstance` for each VELAN instantiated on the indicated adapter – see `PtStopVElan` for details. This call results in NDIS first unbinding any protocols bound to the indicated VELAN, and then calling the MiniportHalt routine, `MPHalt`, for that VELAN. `MPHalt` waits for any outstanding receives/sends on the VELAN to finish before unlinking the VELAN from the ADAPT. + +`PtUnbindAdapter` itself blocks until all VELANs associated with the ADAPT structure have been unlinked from it. This is to make sure that no thread running in the context of a miniport-edge entry point for a VELAN will ever access an invalid lower binding handle. Once all VELANs have been unlinked, `PtUnbindAdapter` closes the lower binding by calling `NdisCloseAdapter`. Note that the MUX driver does not close its lower binding from any context other than its `UnbindAdapter` function – this is recommended behavior for all drivers of this type. + +`MPHalt` may also be called if the VELAN device is disabled, e.g. from the Network Connections Folder. There is no special code within `MPHalt` to handle this condition. However, `PtUnbindAdapter` takes care to not attempt to deinitialize a VELAN miniport (via `NdisIMDeInitializeDeviceInstance`) that has already been halted. + +### Handling Queries + +`MPRequest` is the MUX driver’s function that handles queries for OID values on VELAN miniports. Most of the “Ethernet†type information for the virtual miniport is stored in the VELAN structure itself, and the driver returns information from this structure. The queries that are forwarded are **OID\_GEN\_MEDIA\_CONNECT\_STATUS**, **OID\_PNP\_CAPABILITIES** and **OID\_PNP\_WAKE\_UP\_PATTERN\_LIST**. See “Handling Power Management†below for more information about the latter two OIDs. + +### Handling Sets + +`MPRequest` handles setting OID values on VELAN miniports. Data management OIDs handled by the MUX driver are **OID\_802\_3\_MULTICAST\_LIST** and **OID\_GEN\_CURRENT\_PACKET\_FILTER**. The multicast list is handled entirely within the MUX driver – it just stores the set of multicast addresses in the VELAN structure, for reference during receive-side data processing. The packet filter is handled in a different way – the MUX driver combines the packet filter settings (bitwise OR) of all VELANs associated with the same lower adapter. If the combined packet filter is non-zero, MUX sends a Set request with a value of **NDIS\_PACKET\_TYPE\_PROMISCUOUS** for **OID\_GEN\_CURRENT\_PACKET\_FILTER** to start receives on the lower adapter. If the combined packet filter is zero, MUX sets the lower adapter’s packet filter to 0 (turns off all receives if there aren’t any interested protocols). + +Note that setting the lower adapter to promiscuous mode is only done here in order to be able to receive unicast frames directed to multiple MAC addresses. If, for example, all VELANs are assigned the same MAC address (which is identical to the address of the lower adapter), then the MUX driver should only pass down the combined (bitwise OR) setting of packet filter settings of all VELANs. + +Some power management OIDs are forwarded to the lower miniport. See “Handling Power Management†below for details. + +### Sending Data + +Data sent down on a VELAN miniport is forwarded to the lower adapter. The MUX driver itself does not generate any data of its own. The MUX driver clones a `NET_BUFFER_LIST` for each `NetBufferList` passed to its `MPSendNetBufferLists` function, and saves a pointer to the original `NET_BUFFER_LIST` in the reserved area of the `NET_BUFFER_LIST` structure. When the lower adapter completes the send (`PtSendNBLComplete`), MUX picks up the original packet and calls `NdisMSendNetBufferListsComplete` to complete the original send request. + +If a non-zero VLAN ID is configured for the VELAN, and/or the packet has non-zero Ieee8021QInfo per-packet information, then the MUX driver inserts an NDIS buffer containing a tag header to the front of the packet before sending it down – see function `MPHandleSendTagging` for details. + +### Receiving Data + +Data received from a lower adapter is indicated up on zero or more VELANs. The `PtReceiveNBL` function is called for each `NetBufferList` received from the lower adapter. The received data is checked for matches with the packet filter and multicast list for each VELAN associated with the adapter (see `PtMatchPacketToVElan`). Whenever a match is found, a new `NET_BUFFER_LIST` is allocated and set to point to the received data. A pointer to the original received `NET_BUFFER_LIST` (if any) is also stored in the new `NET_BUFFER_LIST`’s reserved area. This packet is indicated up via `NdisMIndicateReceiveNetBufferLists` to all interested protocols on that VELAN. + +The driver’s `MPReturnNetBufferLists` function is called either by NDIS or by MUX itself when protocols are done with a received `NET_BUFFER_LIST`. This function returns the original `NET_BUFFER_LIST` indicated by the lower driver, if any, by calling `NdisReturnNetBufferLists.` + +The driver indicates up received frames that do not have an IEEE 802.1Q tag header in them – see function `PtHandleRcvTagging`. It always strips off tag headers, if present, on received frames. If a non-zero VLAN ID is configured, then it checks received frames that contain tag headers for matching VLAN Ids – only matching frames are indicated up to protocols. Any VLAN/priority information present in incoming frames is copied to per-packet information fields of indicated `NET_BUFFER_LIST` structures. + +### Status Indications + +The only status indications that are forwarded up by MUX are media connect status indications. See `PtStatus` for more details. + +### Handling Power Management + +During initialization (`MPInitialize`), the MUX miniport sets the attribute **NDIS\_ATTRIBUTE\_NO\_HALT\_ON\_SUSPEND** in its call to `NdisMSetMiniportAttributes`. When the MUX miniport is requested to report its Plug and Play capabilities (**OID\_PNP\_CAPABILITIES**), the MUX miniport forwards the request to the underlying miniport. If this request succeeds, then the MUX miniport overwrites the following fields before successfully completing the original request: + + +++ + + + + + +
NDIS_DEVICE_POWER_STATE          MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
+NDIS_DEVICE_POWER_STATE          MinPatternWakeUp= NdisDeviceStateUnspecified;
+NDIS_DEVICE_POWER_STATE          MinLinkChangeWakeUp=NdisDeviceStateUnspecified
+ +See `PtPostProcessPnPCapabilities` for details. + +**OID\_PNP\_SET\_POWER** and **OID\_PNP\_QUERY\_POWER** are not passed to the lower adapter, since the lower layer miniport will receive independent requests from NDIS. + +NDIS calls the MUX driver’s `ProtocolPnPEvent` function (`PtPNPHandler`) whenever the underlying adapter is transitioned to a different power state. If the underlying adapter is transitioning to a low power state, the driver waits for all outstanding sends and requests to complete. + +Queries/sets received on a VELAN miniport that are to be forwarded to the underlying adapter are queued on the VELAN if the underlying adapter is at a low power state. These are picked up for processing on receiving a notification that the underlying adapter is back to a powered-up state. + +### Handling Global Reconfiguration + +All modifications to VELAN configuration are accompanied by PnP reconfigure notifications, i.e. `NetEventReconfigure` events passed to the MUX’s `PnPEventHandler`, `PtPNPHandler`. This driver takes a broad approach to handling reconfiguration, which is to simply re-examine all the “UpperBindings†keys for all currently bound adapters, and start off VELANs for any that do not exist – see `PtBootStrapVElans` for details. + +### Canceling Sends + +MUX propagates send cancellations from protocols above it to lower miniports. + +Sample Notify Object +-------------------- + +### Preprocessor Flags: + +### DISABLE\_PROTOCOLS\_TO\_PHYSICAL + +When this flag is defined in the Sources file, the notify object disables the bindings of other protocols such as TCP/IP to the physical adapters during the installation. When all the virtual adapters are removed either through the custom property page or as a result of uninstalling the MUX driver, the notify object re-enables those bindings. + +### PASSTHRU\_NOTIFY + +This flag is defined to allow the MUX driver to be used in a passthru mode. When this flag is defined, the notify object: + +1. Creates only one virtual miniport for every physical adapter the MUX protocol edge binds to. +2. Disables the property page to prevent adding of additional virtual miniports. +3. Stores the device name of the virtual adapter in REG\_SZ registry value under HKLM\\System\\CurrentControlSet\\Services\\muxp\\Parameters\\Adapters\\{PhysicalAdaptersInstanceGuid}\\UpperBindings, because there is one to one binding. In the MUX mode (when this flag is not defined), the notify object stores the device name in a REG\_MULTI\_SZ registry value as there could be more than one virtual miniports. + +You can also use this notify object with the Passthru driver by doing the following: + +1. Change the protocol name in file src\\network\\ndis\\passthru\\passthru.c from **PASSTHRU** to **MUXP.** +2. Change the driver name from Passthru to MUX in the sources file. +3. Rebuild the driver to obtain a mux.sys driver binary. +4. Build the MUX notify object with **PASSTHRU\_NOTIFY** defined. +5. Use the MUX inf files, muxp.inf and mux\_mp.inf, to install the driver and dll. + +The benefit of using techniques in the MUX notify object for a 1:1 intermediate driver (e.g. Passthru) is to be able to exercise higher level of control over the bindings of MUX with other components in the system, which is not possible with the IM filter driver. + +### CUSTOM\_EVENTS + +When this macro is defined, the notify object shows how to send custom events to the MUX IM driver when a virtual miniport is added or removed. + +### Notify Object Operation + +During installation, the notify object performs the following operations: + +- It creates one virtual adapter for each physical adapter the MUX protocol edge binds to. +- It disables the bindings of other protocols such as TCP/IP to physical adapters if it has been compiled with DISABLE\_PROTOCOLS\_TO\_PHYSICAL defined in the Sources file. This is the most commonly desired behavior for N:1 MUX drivers. +- It disables the bindings of the protocol edge of the MUX IM driver with all its virtual adapters. + +The notify object provides a custom property page for the MUX IM driver. The custom property page allows the user to add one or more virtual adapters on top of a physical adapter or delete an existing virtual adapter. + +When the MUX IM driver is uninstalled, or binding is disabled, or the user deletes all the virtual adapters on top of a physical adapter, the notify object restores the bindings of other protocols to the physical adapter if it has been compiled with the preprocessor flag **DISABLE\_PROTOCOLS\_TO\_PHYSICAL** defined in the Sources file. + + + diff --git a/network/ndis/mux/driver/60/miniport.c b/network/ndis/mux/driver/60/miniport.c new file mode 100644 index 00000000..45cdef53 --- /dev/null +++ b/network/ndis/mux/driver/60/miniport.c @@ -0,0 +1,2953 @@ +/*++ +Copyright (c) 1992-2000 Microsoft Corporation + +Module Name: + + miniport.c + +Abstract: + + NDIS Miniport Entry points and utility functions for the NDIS + MUX Intermediate Miniport sample. The driver exposes zero or more + Virtual Ethernet LANs (VELANs) as NDIS miniport instances over + each lower (protocol-edge) binding to an underlying adapter. + +Environment: + + Kernel mode. + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#define MODULE_NUMBER MODULE_MINI + +NDIS_OID VElanSupportedOids[] = +{ + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_DRIVER_VERSION, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_PROTOCOL_OPTIONS, + OID_GEN_MAC_OPTIONS, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + OID_GEN_RCV_CRC_ERROR, + OID_GEN_TRANSMIT_QUEUE_LENGTH, + OID_GEN_STATISTICS, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, + OID_802_3_XMIT_DEFERRED, + OID_802_3_XMIT_MAX_COLLISIONS, + OID_802_3_RCV_OVERRUN, + OID_802_3_XMIT_UNDERRUN, + OID_802_3_XMIT_HEARTBEAT_FAILURE, + OID_802_3_XMIT_TIMES_CRS_LOST, + OID_802_3_XMIT_LATE_COLLISIONS, + OID_PNP_CAPABILITIES, + OID_PNP_SET_POWER, + OID_PNP_QUERY_POWER, + OID_PNP_ADD_WAKE_UP_PATTERN, + OID_PNP_REMOVE_WAKE_UP_PATTERN, +#if IEEE_VLAN_SUPPORT + OID_GEN_VLAN_ID, +#endif + OID_PNP_ENABLE_WAKE_UP +}; + + +NDIS_STATUS +MPInitialize( + IN NDIS_HANDLE MiniportAdapterHandle, + IN NDIS_HANDLE MiniportDriverContext, + IN PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters + ) +/*++ + +Routine Description: + + This is the Miniport Initialize routine which gets called as a + result of our call to NdisIMInitializeDeviceInstanceEx. + The context parameter which we pass there is the VELan structure + which we retrieve here. + +Arguments: + + MiniportAdapterHandle NDIS handle for this miniport + MiniportDriverContext Handle passed to NDIS when we registered the driver + MiniportInitParameters Miniport initialization parameters such + as our device context, resources, etc. + +Return Value: + + NDIS_STATUS_SUCCESS unless something goes wrong + +--*/ +{ + PVELAN pVElan; + UINT i; + NDIS_STATUS Status = NDIS_STATUS_FAILURE; + NDIS_HANDLE ConfigurationHandle; + PVOID NetworkAddress; + LOCK_STATE LockState; + NET_IFINDEX HigherLayerIfIndex, LowerLayerIfIndex; + NDIS_MINIPORT_ADAPTER_ATTRIBUTES MiniportAttributesContent; + const PNDIS_MINIPORT_ADAPTER_ATTRIBUTES MiniportAttributes = &MiniportAttributesContent; + + + +#if IEEE_VLAN_SUPPORT + NDIS_STRING strVlanId = NDIS_STRING_CONST("VlanID"); + PNDIS_CONFIGURATION_PARAMETER Params; +#endif + + + //NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES RegistrationAttributes; + //NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES GeneralAttributes; + NDIS_CONFIGURATION_OBJECT ConfigObject; + + + UNREFERENCED_PARAMETER(MiniportDriverContext); + + // + // Start off by retrieving our virtual miniport context (VELAN) and + // storing the Miniport handle in it. + // + + pVElan = MiniportInitParameters->IMDeviceInstanceContext; + + DBGPRINT(MUX_LOUD, ("==> MPInitialize: VELAN %p\n", pVElan)); + + ASSERT(pVElan != NULL); + ASSERT(pVElan->pAdapt != NULL); + NdisZeroMemory(MiniportAttributes,sizeof(NDIS_MINIPORT_ADAPTER_ATTRIBUTES)); + + do + { + pVElan->MiniportAdapterHandle = MiniportAdapterHandle; + // + // Create an ioctl interface + // + (VOID)PtRegisterDevice(); + + // + // register this miniport with NDIS + // + + //NdisZeroMemory(&RegistrationAttributes, sizeof(NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES)); + //NdisZeroMemory(&GeneralAttributes, sizeof(NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES)); + + // + // setting registration attributes + // + MiniportAttributesContent.RegistrationAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES; + MiniportAttributesContent.RegistrationAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1; + MiniportAttributesContent.RegistrationAttributes.Header.Size = sizeof(NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES); + + MiniportAttributesContent.RegistrationAttributes.MiniportAdapterContext = (NDIS_HANDLE)pVElan; + + + MiniportAttributesContent.RegistrationAttributes.AttributeFlags = NDIS_MINIPORT_ATTRIBUTES_NO_HALT_ON_SUSPEND; + + MiniportAttributesContent.RegistrationAttributes.CheckForHangTimeInSeconds = 0; + MiniportAttributesContent.RegistrationAttributes.InterfaceType = 0; + + + + NDIS_DECLARE_MINIPORT_ADAPTER_CONTEXT(VELAN); + Status = NdisMSetMiniportAttributes(MiniportAdapterHandle, + MiniportAttributes); + + + + if (Status != NDIS_STATUS_SUCCESS) + { + break; + } + + + // + // Access configuration parameters for this miniport. + // + ConfigObject.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT; + ConfigObject.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1; + ConfigObject.Header.Size = sizeof(NDIS_CONFIGURATION_OBJECT); + ConfigObject.NdisHandle = pVElan->MiniportAdapterHandle; + ConfigObject.Flags = 0; + + Status = NdisOpenConfigurationEx( + &ConfigObject, + &ConfigurationHandle); + + if (Status != NDIS_STATUS_SUCCESS) + { + break; + } + + + NdisReadNetworkAddress( + &Status, + &NetworkAddress, + &i, + ConfigurationHandle); + + // + // If there is a NetworkAddress override, use it + // + if (((Status == NDIS_STATUS_SUCCESS) + && (i == ETH_LENGTH_OF_ADDRESS)) + && ((!ETH_IS_MULTICAST(NetworkAddress)) + && (ETH_IS_LOCALLY_ADMINISTERED (NetworkAddress)))) + { + + ETH_COPY_NETWORK_ADDRESS( + pVElan->CurrentAddress, + NetworkAddress); + } + else + { + MPGenerateMacAddr(pVElan); + } + + // + // ignore error reading the network address + // + Status = NDIS_STATUS_SUCCESS; + +#if IEEE_VLAN_SUPPORT + // + // Read VLAN ID + // + NdisReadConfiguration( + &Status, + &Params, + ConfigurationHandle, + &strVlanId, + NdisParameterInteger); + if (Status == NDIS_STATUS_SUCCESS) + { + // + // Check for out of bound + // + if (Params->ParameterData.IntegerData > VLAN_ID_MAX) + { + pVElan->VlanId = VLANID_DEFAULT; + } + else + { + pVElan->VlanId = Params->ParameterData.IntegerData; + } + } + + else + { + + pVElan->VlanId = VLANID_DEFAULT; + Status = NDIS_STATUS_SUCCESS; + } +#endif + + + NdisCloseConfiguration(ConfigurationHandle); + + + + // + // set up generic attributes + // + + MiniportAttributesContent.GeneralAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES; + MiniportAttributesContent.GeneralAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1; + MiniportAttributesContent.GeneralAttributes.Header.Size = sizeof(NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES); + + MiniportAttributesContent.GeneralAttributes.MediaType = VELAN_MEDIA_TYPE; + MiniportAttributesContent.GeneralAttributes.MtuSize = pVElan->pAdapt->BindParameters.MtuSize; + MiniportAttributesContent.GeneralAttributes.MaxXmitLinkSpeed = pVElan->pAdapt->BindParameters.MaxXmitLinkSpeed; + MiniportAttributesContent.GeneralAttributes.MaxRcvLinkSpeed = pVElan->pAdapt->BindParameters.MaxRcvLinkSpeed; + MiniportAttributesContent.GeneralAttributes.XmitLinkSpeed = pVElan->pAdapt->BindParameters.XmitLinkSpeed; + MiniportAttributesContent.GeneralAttributes.RcvLinkSpeed = pVElan->pAdapt->BindParameters.RcvLinkSpeed; + + + MUX_ACQUIRE_ADAPT_READ_LOCK(pVElan->pAdapt, &LockState); + + // + // Miniport below has indicated some status indication + // + MiniportAttributesContent.GeneralAttributes.MediaConnectState = pVElan->pAdapt->LastIndicatedLinkState.MediaConnectState; + MiniportAttributesContent.GeneralAttributes.MediaDuplexState = pVElan->pAdapt->LastIndicatedLinkState.MediaDuplexState; + MiniportAttributesContent.GeneralAttributes.XmitLinkSpeed = pVElan->pAdapt->LastIndicatedLinkState.XmitLinkSpeed; + MiniportAttributesContent.GeneralAttributes.RcvLinkSpeed = pVElan->pAdapt->LastIndicatedLinkState.RcvLinkSpeed; + + pVElan->LastIndicatedStatus = NDIS_STATUS_LINK_STATE; + + pVElan->LastIndicatedLinkState = pVElan->pAdapt->LastIndicatedLinkState; + + + MiniportAttributesContent.GeneralAttributes.LookaheadSize = pVElan->pAdapt->BindParameters.LookaheadSize; + MiniportAttributesContent.GeneralAttributes.MaxMulticastListSize = pVElan->pAdapt->BindParameters.MaxMulticastListSize; + MiniportAttributesContent.GeneralAttributes.MacAddressLength = pVElan->pAdapt->BindParameters.MacAddressLength; + + MiniportAttributesContent.GeneralAttributes.PhysicalMediumType = pVElan->pAdapt->BindParameters.PhysicalMediumType ; + MiniportAttributesContent.GeneralAttributes.AccessType = pVElan->pAdapt->BindParameters.AccessType ; + MiniportAttributesContent.GeneralAttributes.DirectionType = pVElan->pAdapt->BindParameters.DirectionType; + MiniportAttributesContent.GeneralAttributes.ConnectionType = pVElan->pAdapt->BindParameters.ConnectionType ; + MiniportAttributesContent.GeneralAttributes.IfType = pVElan->pAdapt->BindParameters.IfType ; + MiniportAttributesContent.GeneralAttributes.IfConnectorPresent = FALSE; // RFC 2665 TRUE if physical adapter + + if (pVElan->pAdapt->BindParameters.RcvScaleCapabilities) + { + MiniportAttributesContent.GeneralAttributes.RecvScaleCapabilities = pVElan->pAdapt->BindParameters.RcvScaleCapabilities; + } + else + { + MiniportAttributesContent.GeneralAttributes.RecvScaleCapabilities = NULL; + } + + MiniportAttributesContent.GeneralAttributes.MacOptions = NDIS_MAC_OPTION_NO_LOOPBACK; + + +#if IEEE_VLAN_SUPPORT + MiniportAttributesContent.GeneralAttributes.MacOptions |= (NDIS_MAC_OPTION_8021P_PRIORITY | + NDIS_MAC_OPTION_8021Q_VLAN); + +#endif + + MiniportAttributesContent.GeneralAttributes.SupportedPacketFilters = pVElan->pAdapt->BindParameters.SupportedPacketFilters; + + MiniportAttributesContent.GeneralAttributes.SupportedStatistics = NDIS_STATISTICS_XMIT_OK_SUPPORTED | + NDIS_STATISTICS_RCV_OK_SUPPORTED | + NDIS_STATISTICS_XMIT_ERROR_SUPPORTED | + NDIS_STATISTICS_RCV_ERROR_SUPPORTED | + NDIS_STATISTICS_RCV_CRC_ERROR_SUPPORTED | + NDIS_STATISTICS_RCV_NO_BUFFER_SUPPORTED | + NDIS_STATISTICS_TRANSMIT_QUEUE_LENGTH_SUPPORTED | + NDIS_STATISTICS_GEN_STATISTICS_SUPPORTED; + + + NdisMoveMemory(&MiniportAttributesContent.GeneralAttributes.CurrentMacAddress, + &pVElan->CurrentAddress, + ETH_LENGTH_OF_ADDRESS); + + NdisMoveMemory(&MiniportAttributesContent.GeneralAttributes.PermanentMacAddress, + &pVElan->PermanentAddress, + ETH_LENGTH_OF_ADDRESS); + + MiniportAttributesContent.GeneralAttributes.PowerManagementCapabilities = &pVElan->pAdapt->PowerManagementCapabilities; + MiniportAttributesContent.GeneralAttributes.SupportedOidList = VElanSupportedOids; + MiniportAttributesContent.GeneralAttributes.SupportedOidListLength = sizeof(VElanSupportedOids); + MUX_RELEASE_ADAPT_READ_LOCK(pVElan->pAdapt, &LockState); + Status = NdisMSetMiniportAttributes(MiniportAdapterHandle,MiniportAttributes); + + pVElan->MiniportInitPending = FALSE; + } while (FALSE); + + + // + // If we had received an UnbindAdapter notification on the underlying + // adapter, we would have blocked that thread waiting for the IM Init + // process to complete. Wake up any such thread. + // + // See PtUnbindAdapter for more details. + // + // + + if (Status == NDIS_STATUS_SUCCESS) + { + // + // we should set this to FALSE only if we successfully initialized the adapter + // otherwise the unbind routine will wait forever for this VElan to go away + // + pVElan->MiniportInitPending = FALSE; + // + // Save the IfIndex for this VELAN + // + HigherLayerIfIndex = MiniportInitParameters->IfIndex; + LowerLayerIfIndex = pVElan->pAdapt->BindParameters.BoundIfIndex; + + Status = NdisIfAddIfStackEntry(HigherLayerIfIndex, + LowerLayerIfIndex); + + if (Status == NDIS_STATUS_SUCCESS) + { + pVElan->IfIndex = HigherLayerIfIndex; + } + + // + // Ignore if the add fails + // + Status = NDIS_STATUS_SUCCESS; + + } + else + { + pVElan->MiniportAdapterHandle = NULL; + } + + if (pVElan->MiniportInitPending == TRUE) + { + pVElan->MiniportInitPending = FALSE; + } + + // TODO: check to see if we can set the init event in a failure case? + + NdisSetEvent(&pVElan->MiniportInitEvent); + + DBGPRINT(MUX_LOUD, ("<== MPInitialize: VELAN %p, Status %x\n", pVElan, Status)); + + return Status; +} + +NDIS_STATUS +MPQueryInformation( + IN PVELAN pVElan, + IN PNDIS_OID_REQUEST NdisRequest + ) +/*++ + +Routine Description: + + This function is called to handle the query request specified by NdisRequest + All query requests are first handled right here, since this is a virtual + device (not pass-through). + +Arguments: + + MiniportAdapterContext Pointer to the adapter structure + NdisRequest Specify the query request. + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_NOT_SUPPORTED + Return code from the MPForwardOidRequest below. + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NDIS_HARDWARE_STATUS HardwareStatus = NdisHardwareStatusReady; + NDIS_MEDIUM Medium = VELAN_MEDIA_TYPE; + UCHAR VendorDesc[] = VELAN_VENDOR_DESC; + ULONG ulInfo; + ULONG64 ulInfo64; + USHORT usInfo; + PVOID pInfo = (PVOID)&ulInfo; + ULONG ulInfoLen = sizeof(ulInfo), NeededLength = 0; + // Should we forward the request to the miniport below? + BOOLEAN bForwardRequest = FALSE; + + NDIS_OID Oid; + PVOID InformationBuffer; + ULONG InformationBufferLength; + PULONG BytesWritten; + PULONG BytesNeeded; + NDIS_STATISTICS_INFO StatisticsInfo; + + + DBGPRINT(MUX_LOUD, ("==> MPQueryInformation: VElan %p, Request %p\n",pVElan, NdisRequest)); + + Oid = NdisRequest->DATA.QUERY_INFORMATION.Oid; + InformationBuffer = NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer; + InformationBufferLength = NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength; + BytesWritten = (ULONG*) &(NdisRequest->DATA.QUERY_INFORMATION.BytesWritten); + BytesNeeded = (ULONG*) &(NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded); + + + // Initialize the result + *BytesWritten = 0; + *BytesNeeded = 0; + + switch (Oid) + { + case OID_GEN_SUPPORTED_LIST: + pInfo = (PVOID) VElanSupportedOids; + ulInfoLen = sizeof(VElanSupportedOids); + break; + + case OID_GEN_SUPPORTED_GUIDS: + // + // Do NOT forward this down, otherwise we will + // end up with spurious instances of private WMI + // classes supported by the lower driver(s). + // + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + + case OID_GEN_HARDWARE_STATUS: + pInfo = (PVOID) &HardwareStatus; + ulInfoLen = sizeof(NDIS_HARDWARE_STATUS); + break; + + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + pInfo = (PVOID) &Medium; + ulInfoLen = sizeof(NDIS_MEDIUM); + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + case OID_GEN_MAXIMUM_LOOKAHEAD: + ulInfo = pVElan->LookAhead; + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_MAXIMUM_FRAME_SIZE: + ulInfo = ETH_MAX_PACKET_SIZE - ETH_HEADER_SIZE; +#if IEEE_VLAN_SUPPORT + ulInfo -= VLAN_TAG_HEADER_SIZE; + +#endif + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_MAXIMUM_TOTAL_SIZE: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + ulInfo = (ULONG) ETH_MAX_PACKET_SIZE; +#if IEEE_VLAN_SUPPORT + ulInfo -= VLAN_TAG_HEADER_SIZE; +#endif + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_MAC_OPTIONS: + ulInfo = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | + NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | + NDIS_MAC_OPTION_NO_LOOPBACK; +#if IEEE_VLAN_SUPPORT + ulInfo |= (NDIS_MAC_OPTION_8021P_PRIORITY | + NDIS_MAC_OPTION_8021Q_VLAN); +#endif + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_LINK_SPEED: + bForwardRequest = TRUE; + break; + + case OID_GEN_TRANSMIT_BUFFER_SPACE: + ulInfo = ETH_MAX_PACKET_SIZE * pVElan->MaxBusySends; +#if IEEE_VLAN_SUPPORT + ulInfo -= VLAN_TAG_HEADER_SIZE * pVElan->MaxBusySends; +#endif + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_RECEIVE_BUFFER_SPACE: + ulInfo = ETH_MAX_PACKET_SIZE * pVElan->MaxBusyRecvs; +#if IEEE_VLAN_SUPPORT + ulInfo -= VLAN_TAG_HEADER_SIZE * pVElan->MaxBusyRecvs; +#endif + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_VENDOR_ID: + ulInfo = VELAN_VENDOR_ID; + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_VENDOR_DESCRIPTION: + pInfo = VendorDesc; + ulInfoLen = sizeof(VendorDesc); + break; + + case OID_GEN_VENDOR_DRIVER_VERSION: + ulInfo = VELAN_VENDOR_ID; + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_DRIVER_VERSION: + usInfo = (USHORT) VELAN_DRIVER_VERSION; + pInfo = (PVOID) &usInfo; + ulInfoLen = sizeof(USHORT); + break; + + case OID_802_3_PERMANENT_ADDRESS: + pInfo = pVElan->PermanentAddress; + ulInfoLen = ETH_LENGTH_OF_ADDRESS; + break; + + case OID_802_3_CURRENT_ADDRESS: + pInfo = pVElan->CurrentAddress; + ulInfoLen = ETH_LENGTH_OF_ADDRESS; + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + ulInfo = VELAN_MAX_MCAST_LIST; + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_MAXIMUM_SEND_PACKETS: + ulInfo = VELAN_MAX_SEND_PKTS; + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_MEDIA_CONNECT_STATUS: + // + // Get this from the adapter below. + // + bForwardRequest = TRUE; + break; + + case OID_PNP_QUERY_POWER: + // simply succeed this. + ulInfoLen = sizeof(ULONG); + ulInfo = 0; + break; + + case OID_PNP_CAPABILITIES: + case OID_PNP_WAKE_UP_PATTERN_LIST: + // + // Pass down these power management/PNP OIDs. + // + bForwardRequest = TRUE; + break; + + case OID_GEN_XMIT_OK: + ulInfo64 = pVElan->GoodTransmits; + pInfo = &ulInfo64; + if (InformationBufferLength >= sizeof(ULONG64) || + InformationBufferLength == 0) + { + ulInfoLen = sizeof(ULONG64); + } + else + { + ulInfoLen = sizeof(ULONG); + } + NeededLength = sizeof(ulInfo64); + + break; + + case OID_GEN_RCV_OK: + ulInfo64 = pVElan->GoodReceives; + pInfo = &ulInfo64; + if (InformationBufferLength >= sizeof(ULONG64) || + InformationBufferLength == 0) + { + ulInfoLen = sizeof(ULONG64); + } + else + { + ulInfoLen = sizeof(ULONG); + } + + NeededLength = sizeof(ulInfo64); + + break; + + case OID_GEN_XMIT_ERROR: + ulInfo = pVElan->TxAbortExcessCollisions + + pVElan->TxDmaUnderrun + + pVElan->TxLostCRS + + pVElan->TxLateCollisions+ + pVElan->TransmitFailuresOther; + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_RCV_ERROR: + ulInfo = pVElan->RcvCrcErrors + + pVElan->RcvAlignmentErrors + + pVElan->RcvDmaOverrunErrors + + pVElan->RcvRuntErrors; +#if IEEE_VLAN_SUPPORT + ulInfo += + (pVElan->RcvVlanIdErrors + + pVElan->RcvFormatErrors); +#endif + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_RCV_NO_BUFFER: + ulInfo = pVElan->RcvResourceErrors; + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_RCV_CRC_ERROR: + ulInfo = pVElan->RcvCrcErrors; + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + ulInfo = pVElan->RegNumTcb; + pInfo = (PVOID) &ulInfo; + break; + + case OID_GEN_STATISTICS: + ulInfoLen = sizeof (NDIS_STATISTICS_INFO); + NdisZeroMemory(&StatisticsInfo, sizeof(NDIS_STATISTICS_INFO)); + + StatisticsInfo.Header.Revision = NDIS_OBJECT_REVISION_1; + StatisticsInfo.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + StatisticsInfo.Header.Size = sizeof(NDIS_STATISTICS_INFO); + StatisticsInfo.SupportedStatistics = NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS | + NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR | + NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR; + + StatisticsInfo.ifInDiscards = pVElan->RcvCrcErrors + + pVElan->RcvAlignmentErrors + + pVElan->RcvResourceErrors + + pVElan->RcvDmaOverrunErrors + + pVElan->RcvRuntErrors; + +#if IEEE_VLAN_SUPPORT + StatisticsInfo.ifInDiscards += (pVElan->RcvVlanIdErrors + + pVElan->RcvFormatErrors); +#endif + + StatisticsInfo.ifInErrors = StatisticsInfo.ifInDiscards - + pVElan->RcvResourceErrors; + + + StatisticsInfo.ifOutErrors = pVElan->TxAbortExcessCollisions + + pVElan->TxDmaUnderrun + + pVElan->TxLostCRS + + pVElan->TxLateCollisions; + + pInfo = &StatisticsInfo; + break; + + case OID_802_3_RCV_ERROR_ALIGNMENT: + ulInfo = pVElan->RcvAlignmentErrors; + pInfo = (PVOID) &ulInfo; + break; + + case OID_802_3_XMIT_ONE_COLLISION: + ulInfo = pVElan->OneRetry; + pInfo = (PVOID) &ulInfo; + break; + + case OID_802_3_XMIT_MORE_COLLISIONS: + ulInfo = pVElan->MoreThanOneRetry; + pInfo = (PVOID) &ulInfo; + break; + + case OID_802_3_XMIT_DEFERRED: + ulInfo = pVElan->TxOKButDeferred; + pInfo = (PVOID) &ulInfo; + break; + + case OID_802_3_XMIT_MAX_COLLISIONS: + ulInfo = pVElan->TxAbortExcessCollisions; + pInfo = (PVOID) &ulInfo; + break; + + case OID_802_3_RCV_OVERRUN: + ulInfo = pVElan->RcvDmaOverrunErrors; + pInfo = (PVOID) &ulInfo; + break; + + case OID_802_3_XMIT_UNDERRUN: + ulInfo = pVElan->TxDmaUnderrun; + pInfo = (PVOID) &ulInfo; + break; + + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + ulInfo = pVElan->TxLostCRS; + pInfo = (PVOID) &ulInfo; + break; + + case OID_802_3_XMIT_TIMES_CRS_LOST: + ulInfo = pVElan->TxLostCRS; + pInfo = (PVOID) &ulInfo; + break; + + case OID_802_3_XMIT_LATE_COLLISIONS: + ulInfo = pVElan->TxLateCollisions; + pInfo = (PVOID) &ulInfo; + break; + +#if IEEE_VLAN_SUPPORT + case OID_GEN_VLAN_ID: + ulInfo = pVElan->VlanId; + pInfo = (PVOID) &ulInfo; + break; + +#endif + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + if (bForwardRequest == FALSE) + { + // + // No need to forward this request down. + // + if (Status == NDIS_STATUS_SUCCESS) + { + if (ulInfoLen <= InformationBufferLength) + { + // Copy result into InformationBuffer + *BytesWritten = ulInfoLen; + if(ulInfoLen) + { + NdisMoveMemory(InformationBuffer, pInfo, ulInfoLen); + + if (NeededLength > ulInfoLen) + { + *BytesNeeded = NeededLength; + } + } + + + } + else + { + // too short + *BytesNeeded = (NeededLength > ulInfoLen ? NeededLength : ulInfoLen); + + Status = NDIS_STATUS_BUFFER_TOO_SHORT; + } + } + } + else + { + + + // + // Send this request to the binding below. + // + Status = MPForwardOidRequest(pVElan,NdisRequest); + } + + if ((Status != NDIS_STATUS_SUCCESS) && + (Status != NDIS_STATUS_PENDING)) + { + DBGPRINT(MUX_WARN, ("MPQueryInformation VELAN %p, OID 0x%08x, Status = 0x%08x\n", + pVElan, Oid, Status)); + } + + + + DBGPRINT(MUX_LOUD, ("<== MPQueryInformation: VElan %p, Request %p returning %08lx\n",pVElan, NdisRequest, Status)); + + return(Status); + +} + + +NDIS_STATUS +MPSetInformation( + IN PVELAN pVElan, + IN PNDIS_OID_REQUEST NdisRequest + ) +/*++ + +Routine Description: + + This is the handler for an set request operation. Relevant + requests are forwarded down to the lower miniport for handling. + +Arguments: + + MiniportAdapterContext Pointer to the adapter structure + NdisRequest Specify the set request + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_NOT_SUPPORTED + NDIS_STATUS_INVALID_LENGTH + Return code from the MPForwardOidRequest below. + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + ULONG PacketFilter; + NDIS_DEVICE_POWER_STATE NewDeviceState; + + NDIS_OID Oid; + PVOID InformationBuffer; + ULONG InformationBufferLength; + PULONG BytesRead; + PULONG BytesNeeded; + + // Should we forward the request to the miniport below? + BOOLEAN bForwardRequest = FALSE; + NDIS_STATUS_INDICATION StatusIndication; + + DBGPRINT(MUX_LOUD, ("==> MPSetInformation: VElan %p, Request %p\n", pVElan, NdisRequest)); + + NdisZeroMemory(&StatusIndication, sizeof(NDIS_STATUS_INDICATION)); + Oid = NdisRequest->DATA.SET_INFORMATION.Oid; + InformationBuffer = NdisRequest->DATA.SET_INFORMATION.InformationBuffer; + InformationBufferLength = NdisRequest->DATA.SET_INFORMATION.InformationBufferLength; + BytesRead = (ULONG*) &(NdisRequest->DATA.SET_INFORMATION.BytesRead); + BytesNeeded = (ULONG*) &(NdisRequest->DATA.SET_INFORMATION.BytesNeeded); + + *BytesRead = 0; + *BytesNeeded = 0; + + switch (Oid) + { + // + // Let the miniport below handle these OIDs: + // + case OID_PNP_ADD_WAKE_UP_PATTERN: + case OID_PNP_REMOVE_WAKE_UP_PATTERN: + case OID_PNP_ENABLE_WAKE_UP: + bForwardRequest = TRUE; + break; + + case OID_PNP_SET_POWER: + // + // Store new power state and succeed the request. + // + *BytesNeeded = sizeof(NDIS_DEVICE_POWER_STATE); + if (InformationBufferLength < *BytesNeeded) + { + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + NewDeviceState = (*(PNDIS_DEVICE_POWER_STATE)InformationBuffer); + + // + // Check if the VELAN adapter goes from lower power state to D0 + // + if ((MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) + && (!MUX_IS_LOW_POWER_STATE(NewDeviceState))) + { + // + // Indicate the media status is necessary + // + if (pVElan->LastIndicatedStatus != pVElan->LatestUnIndicateStatus) + { + + StatusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION; + StatusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1; + StatusIndication.Header.Size = sizeof(NDIS_STATUS_INDICATION); + + StatusIndication.SourceHandle = pVElan->MiniportAdapterHandle; + StatusIndication.StatusCode = pVElan->LatestUnIndicateStatus; + if (pVElan->LatestUnIndicateStatus == NDIS_STATUS_LINK_STATE) + { + StatusIndication.StatusBuffer = &pVElan->LatestUnIndicateLinkState; + StatusIndication.StatusBufferSize = sizeof(NDIS_LINK_STATE); + + } + else + { + StatusIndication.StatusBuffer = NULL; + StatusIndication.StatusBufferSize = 0; + } + + NdisMIndicateStatusEx(pVElan->MiniportAdapterHandle, &StatusIndication); + + pVElan->LastIndicatedStatus = pVElan->LatestUnIndicateStatus; + + if (pVElan->LatestUnIndicateStatus == NDIS_STATUS_LINK_STATE) + { + pVElan->LastIndicatedLinkState = pVElan->LatestUnIndicateLinkState; + } + + } + else + { + if (pVElan->LastIndicatedStatus == NDIS_STATUS_LINK_STATE) + { + if (!NdisEqualMemory(&pVElan->LatestUnIndicateLinkState, + &pVElan->LastIndicatedLinkState, + sizeof(NDIS_LINK_STATE))) + { + + StatusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION; + StatusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1; + StatusIndication.Header.Size = sizeof(NDIS_STATUS_INDICATION); + + StatusIndication.SourceHandle = pVElan->MiniportAdapterHandle; + StatusIndication.StatusCode = pVElan->LatestUnIndicateStatus; + StatusIndication.StatusBuffer = &pVElan->LatestUnIndicateLinkState; + StatusIndication.StatusBufferSize = sizeof(NDIS_LINK_STATE); + + NdisMIndicateStatusEx(pVElan->MiniportAdapterHandle, &StatusIndication); + + pVElan->LastIndicatedStatus = pVElan->LatestUnIndicateStatus; + pVElan->LastIndicatedLinkState = pVElan->LatestUnIndicateLinkState; + } + } + } + } + // + // Check if the VELAN adapter goes from D0 to lower power state + // + if ((!MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) + && (MUX_IS_LOW_POWER_STATE(NewDeviceState))) + { + // + // Initialize LastUnIndicateStatus + // + pVElan->LatestUnIndicateStatus = pVElan->LastIndicatedStatus; + + if (pVElan->LastIndicatedStatus == NDIS_STATUS_LINK_STATE) + { + pVElan->LatestUnIndicateLinkState = pVElan->LastIndicatedLinkState; + } + } + + NdisMoveMemory(&pVElan->MPDevicePowerState, + InformationBuffer, + *BytesNeeded); + + DBGPRINT(MUX_INFO, ("SetInfo: VElan %p, new miniport power state --- %d\n", + pVElan, pVElan->MPDevicePowerState)); + + break; + + case OID_802_3_MULTICAST_LIST: + Status = MPSetMulticastList(pVElan, + InformationBuffer, + InformationBufferLength, + BytesRead, + BytesNeeded); + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + if (InformationBufferLength != sizeof(ULONG)) + { + Status = NDIS_STATUS_INVALID_LENGTH; + *BytesNeeded = sizeof(ULONG); + break; + } + + NdisMoveMemory(&PacketFilter, InformationBuffer, sizeof(ULONG)); + *BytesRead = sizeof(ULONG); + + Status = MPSetPacketFilter(pVElan, + PacketFilter); + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + if (InformationBufferLength < sizeof(ULONG)) + { + Status = NDIS_STATUS_INVALID_LENGTH; + *BytesNeeded = sizeof(ULONG); + break; + } +#if IEEE_VLAN_SUPPORT + // + // In order to simplify parsing and to avoid excessive + // copying, we need the tag header also to be present in the + // lookahead buffer. Make sure that the driver below + // includes that. + // + if (*(UNALIGNED PULONG)InformationBuffer < VLAN_TAG_HEADER_SIZE) + { + pVElan->RestoreLookaheadSize = TRUE; + *(UNALIGNED PULONG)InformationBuffer += VLAN_TAG_HEADER_SIZE; + } +#endif + bForwardRequest = TRUE; + break; + +#if IEEE_VLAN_SUPPORT + case OID_GEN_VLAN_ID: + if (InformationBufferLength == sizeof(ULONG)) + { + NdisMoveMemory((&pVElan->VlanId), InformationBuffer, sizeof(ULONG)); + + } + else + { + *BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + } + + break; +#endif + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + + } + + if (bForwardRequest == FALSE) + { + if (Status == NDIS_STATUS_SUCCESS) + { + *BytesRead = InformationBufferLength; + } + } + else + { + // + // Send this request to the binding below. + // + Status = MPForwardOidRequest(pVElan,NdisRequest); + } + + DBGPRINT(MUX_LOUD, ("<== MPSetInformation: VElan %p, Request %p returning %08lx\n",pVElan, NdisRequest, Status)); + + return(Status); +} + +NDIS_STATUS +MPMethodRequest( + IN PVELAN pVElan, + IN PNDIS_OID_REQUEST NdisRequest + ) +/*++ +Routine Description: + + WMI method request handler + +Arguments: + + MiniportAdapterContext Pointer to the adapter structure + NdisRequest Pointer to the request sent down by NDIS + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_NOT_SUPPORTED + + +--*/ +{ + NDIS_OID Oid; + ULONG MethodId; + PVOID InformationBuffer; + ULONG InputBufferLength; + ULONG OutputBufferLength; + ULONG BytesNeeded; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(pVElan); + + DBGPRINT(MUX_LOUD, ("==> MPMethodRequest: VElan %p, Request %p\n", pVElan, NdisRequest)); + + + Oid = NdisRequest->DATA.METHOD_INFORMATION.Oid; + InformationBuffer = (PVOID)(NdisRequest->DATA.METHOD_INFORMATION.InformationBuffer); + InputBufferLength = NdisRequest->DATA.METHOD_INFORMATION.InputBufferLength; + OutputBufferLength = NdisRequest->DATA.METHOD_INFORMATION.OutputBufferLength; + MethodId = NdisRequest->DATA.METHOD_INFORMATION.MethodId; + UNREFERENCED_PARAMETER(Oid); + UNREFERENCED_PARAMETER(InformationBuffer); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(MethodId); + + + BytesNeeded = 0; + + switch(Oid) + { + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + + DBGPRINT(MUX_LOUD, ("<== MPMethodRequest: VElan %p, Request %p returning %08lx\n",pVElan, NdisRequest, Status)); + + return Status; + +} + + + +NDIS_STATUS +MPOidRequest( + IN NDIS_HANDLE MiniportAdapterContext, + IN PNDIS_OID_REQUEST NdisRequest + ) +/*++ +Routine Description: + + MiniportRequest dispatch handler + +Arguments: + + MiniportAdapterContext Pointer to the adapter structure + NdisRequest Pointer to NDIS_OID_REQUEST sent down by NDIS. + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_NOT_SUPPORTED + NDIS_STATUS_XXX + +--*/ +{ + PVELAN pVElan = (PVELAN)MiniportAdapterContext; + NDIS_REQUEST_TYPE RequestType; + NDIS_STATUS Status; + + DBGPRINT(MUX_LOUD,("==> MPOidRequest: Request %p\n", NdisRequest)); + + RequestType = NdisRequest->RequestType; + + switch(RequestType) + { + case NdisRequestMethod: + Status = MPMethodRequest(pVElan, NdisRequest); + break; + + case NdisRequestSetInformation: + Status = MPSetInformation(pVElan, NdisRequest); + break; + + case NdisRequestQueryInformation: + case NdisRequestQueryStatistics: + Status = MPQueryInformation(pVElan, NdisRequest); + break; + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + DBGPRINT(MUX_LOUD,("<== MPOidRequest: Request %p, Status %08lx\n", NdisRequest, Status)); + + return Status; +} + + + +VOID +MPHalt( + IN NDIS_HANDLE MiniportAdapterContext, + IN NDIS_HALT_ACTION HaltAction + ) +/*++ + +Routine Description: + + Halt handler. Add any further clean-up for the VELAN to this + function. + + We wait for all pending I/O on the VELAN to complete and then + unlink the VELAN from the adapter. + +Arguments: + + MiniportAdapterContext Pointer to the pVElan + HaltAction The reason adapter is being halted + +Return Value: + + None. + +--*/ +{ + PVELAN pVElan = (PVELAN)MiniportAdapterContext; + NET_IFINDEX LowerLayerIfIndex; + + UNREFERENCED_PARAMETER(HaltAction); + + + DBGPRINT(MUX_LOUD, ("==> MPHalt: VELAN %p\n", pVElan)); + + // + // Mark the VELAN so that we don't send down any new requests or + // sends to the adapter below, or new receives/indications to + // protocols above. + // + pVElan->MiniportHalting = TRUE; + + // + // Update the packet filter on the underlying adapter if needed. + // + if (pVElan->PacketFilter != 0) + { + MPSetPacketFilter(pVElan, 0); + } + + // + // Wait for any outstanding sends or requests to complete. + // + while (pVElan->OutstandingSends) + { + DBGPRINT(MUX_INFO, ("MPHalt: VELAN %p has %d outstanding sends\n", + pVElan, pVElan->OutstandingSends)); + NdisMSleep(20000); + } + + // + // Wait for all outstanding indications to be completed and + // any pended receive packets to be returned to us. + // + while (pVElan->OutstandingReceives) + { + DBGPRINT(MUX_INFO, ("MPHalt: VELAN %p has %d outstanding receives\n", + pVElan, pVElan->OutstandingReceives)); + NdisMSleep(20000); + } + + + // + // Delete the ioctl interface that was created when the miniport + // was created. + // + (VOID)PtDeregisterDevice(); + + + // + // Delete stack entry for this Velan + // + if (pVElan->IfIndex != 0) + { + LowerLayerIfIndex = pVElan->pAdapt->BindParameters.BoundIfIndex; + + NdisIfDeleteIfStackEntry(pVElan->IfIndex, + LowerLayerIfIndex); + pVElan->IfIndex = 0; + } + + + // + // Unlink the VELAN from its parent ADAPT structure. This will + // dereference the VELAN. + // + pVElan->MiniportAdapterHandle = NULL; + PtUnlinkVElanFromAdapter(pVElan); + + DBGPRINT(MUX_LOUD, ("<== MPHalt: pVElan %p\n", pVElan)); +} + + +NDIS_STATUS +MPForwardOidRequest( + IN PVELAN pVElan, + IN PNDIS_OID_REQUEST Request + ) +/*++ + +Routine Description: + + Utility routine that forwards an NDIS request made on a VELAN to the + lower binding. Since at most a single request can be pended on a VELAN, + we use the pre-allocated request structure embedded in the VELAN struct. + +Arguments: + + pVElan Pointer to a VElan Adapter + Request Pointer to an NDIS request to be forwarded to the below adapter. + +Return Value: + + NDIS_STATUS_PENDING if a request was sent down. + NDIS_STATUS_XXX Otherwise. + +--*/ +{ + NDIS_STATUS Status; + PMUX_NDIS_REQUEST pMuxNdisRequest = &pVElan->Request; + + PADAPT pAdapt = pVElan->pAdapt; + + + DBGPRINT(MUX_LOUD, ("==> MPForwardOidRequest: VELAN %p, Request %p\n", pVElan, Request)); + + do + { + MUX_INCR_PENDING_SENDS(pVElan); + + // + // If the miniport below is going away, fail the request + // + NdisAcquireSpinLock(&pVElan->Lock); + if (pVElan->DeInitializing == TRUE) + { + NdisReleaseSpinLock(&pVElan->Lock); + MUX_DECR_PENDING_SENDS(pVElan); + Status = NDIS_STATUS_FAILURE; + break; + } + NdisReleaseSpinLock(&pVElan->Lock); + + // + // If the virtual miniport edge is at a low power + // state, fail this request. + // + if (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) + { + MUX_DECR_PENDING_SENDS(pVElan); + Status = NDIS_STATUS_ADAPTER_NOT_READY; + break; + } + + NdisAcquireSpinLock(&pVElan->Lock); + pMuxNdisRequest->Cancelled = FALSE; + pMuxNdisRequest->OrigRequest = Request; + pMuxNdisRequest->pCallback = PtCompleteForwardedRequest; + pMuxNdisRequest->Request.RequestType = Request->RequestType; + pMuxNdisRequest->Refcount = 1; + NdisReleaseSpinLock(&pVElan->Lock); + + pMuxNdisRequest->Request.Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST; + pMuxNdisRequest->Request.Header.Revision = NDIS_OID_REQUEST_REVISION_1; + pMuxNdisRequest->Request.Header.Size = sizeof(NDIS_OID_REQUEST); + + switch (Request->RequestType) + { + case NdisRequestQueryInformation: + case NdisRequestQueryStatistics: + pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.Oid = Request->DATA.QUERY_INFORMATION.Oid; + pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.InformationBuffer = + Request->DATA.QUERY_INFORMATION.InformationBuffer; + pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength = + Request->DATA.QUERY_INFORMATION.InformationBufferLength; + break; + + case NdisRequestSetInformation: + pMuxNdisRequest->Request.DATA.SET_INFORMATION.Oid = Request->DATA.SET_INFORMATION.Oid; + pMuxNdisRequest->Request.DATA.SET_INFORMATION.InformationBuffer = + Request->DATA.SET_INFORMATION.InformationBuffer; + pMuxNdisRequest->Request.DATA.SET_INFORMATION.InformationBufferLength = + Request->DATA.SET_INFORMATION.InformationBufferLength; + break; + + case NdisRequestMethod: + default: + ASSERT(FALSE); + break; + } + + // + // If the miniport below is going away + // + NdisAcquireSpinLock(&pVElan->Lock); + if (pVElan->DeInitializing == TRUE) + { + pMuxNdisRequest->OrigRequest = NULL; + NdisReleaseSpinLock(&pVElan->Lock); + MUX_DECR_PENDING_SENDS(pVElan); + Status = NDIS_STATUS_FAILURE; + break; + } + + // If the lower binding has been notified of a low + // power state, queue this request; it will be picked + // up again when the lower binding returns to D0. + // + if (MUX_IS_LOW_POWER_STATE(pVElan->pAdapt->PtDevicePowerState)) + { + DBGPRINT(MUX_INFO, ("ForwardRequest: VELAN %p, Adapt %p power" + " state is %d, queueing OID %x\n", + pVElan, pVElan->pAdapt, + pVElan->pAdapt->PtDevicePowerState, + Request->DATA.QUERY_INFORMATION.Oid)); + + pVElan->QueuedRequest = TRUE; + NdisReleaseSpinLock(&pVElan->Lock); + Status = NDIS_STATUS_PENDING; + break; + } + + if (pMuxNdisRequest->Cancelled == TRUE) + { + NdisReleaseSpinLock(&pVElan->Lock); + Status = NDIS_STATUS_REQUEST_ABORTED; + PtRequestComplete(pVElan->pAdapt, &pMuxNdisRequest->Request, Status); + break; + } + NdisReleaseSpinLock(&pVElan->Lock); + + NdisAcquireSpinLock(&pAdapt->Lock); + + pAdapt->OutstandingRequests ++; + + if ((pAdapt->Flags & MUX_BINDING_CLOSING)== MUX_BINDING_CLOSING) + { + NdisReleaseSpinLock(&pAdapt->Lock); + Status = NDIS_STATUS_CLOSING; + } + else + { + NdisReleaseSpinLock(&pAdapt->Lock); + Status = NdisOidRequest(pVElan->BindingHandle, + &pMuxNdisRequest->Request); + } + if (Status != NDIS_STATUS_PENDING) + { + PtRequestComplete(pVElan->pAdapt, &pMuxNdisRequest->Request, Status); + Status = NDIS_STATUS_PENDING; + break; + } + } + while (FALSE); + + DBGPRINT(MUX_LOUD, ("<== MPForwardOidRequest: VELAN %p, Request %p, Status %8x\n", pVElan, Request, Status)); + +#if IEEE_VELAN_SUPPORT + if ((Status != NDIS_STATUS_PENDING) + && (((Request->RequestType == NdisRequestSetInformation) + && (Request->DATA.SET_INFORMATION.Oid == OID_GEN_CURRENT_LOOKAHEAD)) + && (pVElan->RestoreLookaheadSize == TRUE))) + { + pVElan->RestoreLookaheadSize = FALSE; + *(UNALIGNED PULONG)(Request->DATA.SET_INFORMATION.InformationBuffer) -= VLAN_TAG_HEADER_SIZE; + } +#endif + + return (Status); +} + +NDIS_STATUS +MPSetPacketFilter( + IN PVELAN pVElan, + IN ULONG PacketFilter + ) +/*++ +Routine Description: + + This routine will set up the VELAN so that it accepts packets + that match the specified packet filter. The only filter bits + that can truly be toggled are for broadcast and promiscuous. + + The MUX driver always sets the lower binding to promiscuous + mode, but we do some optimization here to avoid turning on + receives too soon. That is, we set the packet filter on the lower + binding to a non-zero value iff at least one of the VELANs + has a non-zero filter value. + + NOTE: setting the lower binding to promiscuous mode can + impact CPU utilization. The only reason we set the lower binding + to promiscuous mode in this sample is that we need to be able + to receive unicast frames directed to MAC address(es) that do not + match the local adapter's MAC address. If VELAN MAC addresses + are set to be equal to that of the adapter below, it is sufficient + to set the lower packet filter to the bitwise OR'ed value of + packet filter settings on all VELANs. + + +Arguments: + + pVElan - pointer to VELAN + PacketFilter - the new packet filter + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_NOT_SUPPORTED + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PADAPT pAdapt; + PVELAN pTmpVElan; + PLIST_ENTRY p; + ULONG AdapterFilter; + BOOLEAN bSendUpdate = FALSE; + LOCK_STATE LockState; + + DBGPRINT(MUX_LOUD, ("==> MPSetPacketFilter VELAN %p, Filter %x\n", pVElan, PacketFilter)); + + do + { + // + // Any bits not supported? + // + if (PacketFilter & ~VELAN_SUPPORTED_FILTERS) + { + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + AdapterFilter = 0; + pAdapt = pVElan->pAdapt; + + // + // Grab a Write lock on the adapter so that this operation + // does not interfere with any receives that might be accessing + // filter information. + // + MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState); + + // + // Save the new packet filter value + // + pVElan->PacketFilter = PacketFilter; + + // + // Compute the new combined filter for all VELANs on this + // adapter. + // + for (p = pAdapt->VElanList.Flink; + p != &pAdapt->VElanList; + p = p->Flink) + { + pTmpVElan = CONTAINING_RECORD(p, VELAN, Link); + AdapterFilter |= pTmpVElan->PacketFilter; + } + + // + // If all VELANs have packet filters set to 0, turn off + // receives on the lower adapter, if not already done. + // + if ((AdapterFilter == 0) && (pAdapt->PacketFilter != 0)) + { + bSendUpdate = TRUE; + pAdapt->PacketFilter = 0; + } + else + // + // If receives had been turned off on the lower adapter, and + // the new filter is non-zero, turn on the lower adapter. + // We set the adapter to promiscuous mode in this sample + // so that we are able to receive packets directed to + // any of the VELAN MAC addresses. + // + if ((AdapterFilter != 0) && (pAdapt->PacketFilter == 0)) + { + bSendUpdate = TRUE; + pAdapt->PacketFilter = MUX_ADAPTER_PACKET_FILTER; + } + + MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState); + + if (bSendUpdate) + { + PtRequestAdapterAsync( + pAdapt, + NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, + &pAdapt->PacketFilter, + sizeof(pAdapt->PacketFilter), + PtDiscardCompletedRequest); + } + + } + while (FALSE); + + DBGPRINT(MUX_LOUD, ("<== MPSetPacketFilter VELAN %p, Status %x\n", pVElan, Status)); + + return(Status); +} + + +NDIS_STATUS +MPSetMulticastList( + IN PVELAN pVElan, + _In_reads_bytes_(InformationBufferLength) IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + OUT PULONG pBytesRead, + OUT PULONG pBytesNeeded + ) +/*++ + +Routine Description: + + Set the multicast list on the specified VELAN miniport. + We simply validate all information and copy in the multicast + list. + + We don't forward the multicast list information down since + we set the lower binding to promisc. mode. + +Arguments: + + pVElan - VELAN on which to set the multicast list + InformationBuffer - pointer to new multicast list + InformationBufferLength - length in bytes of above list + pBytesRead - place to return # of bytes read from the above + pBytesNeeded - place to return expected min # of bytes + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_INVALID_LENGTH + NDIS_STATUS_MULTICAST_FULL + +--*/ +{ + NDIS_STATUS Status; + PADAPT pAdapt; + LOCK_STATE LockState; + + DBGPRINT(MUX_LOUD, ("==> MPSetMulticastList VELAN %p\n", pVElan)); + // + // Initialize. + // + *pBytesNeeded = sizeof(MUX_MAC_ADDRESS); + *pBytesRead = 0; + Status = NDIS_STATUS_SUCCESS; + + do + { + if (InformationBufferLength % sizeof(MUX_MAC_ADDRESS)) + { + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (InformationBufferLength > (VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS))) + { + Status = NDIS_STATUS_MULTICAST_FULL; + *pBytesNeeded = VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS); + break; + } + + pAdapt = pVElan->pAdapt; + + // + // Grab a Write lock on the adapter so that this operation + // does not interfere with any receives that might be accessing + // multicast list information. + // + MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState); + + NdisZeroMemory(pVElan->McastAddrs, + VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS)); + + NdisMoveMemory(&pVElan->McastAddrs[0], + InformationBuffer, + InformationBufferLength); + + pVElan->McastAddrCount = InformationBufferLength / sizeof(MUX_MAC_ADDRESS); + + MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState); + } + while (FALSE); + + DBGPRINT(MUX_LOUD, ("<== MPSetMulticastList VELAN %p, Status %8x\n", pVElan, Status)); + + return (Status); +} + + + +PUCHAR +MacAddrToString( + PVOID In + ) +/*++ + +Routine Description: + + Careful! Uses static storage for string. Used to simplify DbgPrints + of MAC addresses. + +Arguments: + + IN Pointer to MAC address array + +Return Value: + + A string format of the given mac address + +--*/ +{ + static UCHAR String[20]; + static PCHAR HexChars = "0123456789abcdef"; + PUCHAR EthAddr = (PUCHAR) In; + UINT i; + PUCHAR s; + + for (i = 0, s = String; i < 6; i++, EthAddr++) + { +#pragma prefast(suppress: __WARNING_POTENTIAL_BUFFER_OVERFLOW, "s is bounded by check above"); + *s++ = HexChars[(*EthAddr) >> 4]; + *s++ = HexChars[(*EthAddr) & 0xf]; + } + *s = '\0'; + + return String; +} + + +VOID +MPGenerateMacAddr( + PVELAN pVElan +) +/*++ + +Routine Description: + + Generates a "virtual" MAC address for a VELAN. + NOTE: this is only a sample implementation of selecting + a MAC address for the VELAN. Other implementations are possible, + including using the MAC address of the underlying adapter as + the MAC address of the VELAN. + +Arguments: + + pVElan - Pointer to velan structure + +Return Value: + + None + +--*/ +{ + ETH_COPY_NETWORK_ADDRESS( + pVElan->CurrentAddress, + pVElan->PermanentAddress); + + DBGPRINT(MUX_LOUD, ("%d CurrentAddress %s\n", + pVElan->VElanNumber, MacAddrToString(&pVElan->CurrentAddress))); + DBGPRINT(MUX_LOUD, ("%d PermanentAddress %s\n", + pVElan->VElanNumber, MacAddrToString(&pVElan->PermanentAddress))); + +} + + + +VOID +MPDevicePnPEvent( + IN NDIS_HANDLE MiniportAdapterContext, + IN PNET_DEVICE_PNP_EVENT NetDevicePnPEvent + ) +/*++ + +Routine Description: + + This handler is called to notify us of PnP events directed to + our miniport device object. + +Arguments: + + MiniportAdapterContext - pointer to VELAN structure + DevicePnPEvent - the event + InformationBuffer - Points to additional event-specific information + InformationBufferLength - length of above + +Return Value: + + None +--*/ +{ + // TBD - add code/comments about processing this. + // + + DBGPRINT(MUX_LOUD, ("==> MPDevicePnPEvent: AdapterContext %08lp, DevicePnPEvent %x\n",MiniportAdapterContext, NetDevicePnPEvent->DevicePnPEvent)); + + UNREFERENCED_PARAMETER(MiniportAdapterContext); + UNREFERENCED_PARAMETER(NetDevicePnPEvent); + + + DBGPRINT(MUX_LOUD, ("<== MPDevicePnPEvent: AdapterContext %08lp, DevicePnPEvent %x\n",MiniportAdapterContext, NetDevicePnPEvent->DevicePnPEvent)); + + return; +} + + +VOID +MPAdapterShutdown( + IN NDIS_HANDLE MiniportAdapterContext, + IN NDIS_SHUTDOWN_ACTION ShutdownAction + ) +/*++ + +Routine Description: + + This handler is called to notify us of an impending system shutdown. + Since this is not a hardware driver, there isn't anything specific + we need to do about this. + +Arguments: + + MiniportAdapterContext pointer to VELAN structure + ShutdownAction Specify the reason to shut down the adapter + +Return Value: + + None +--*/ +{ + PVELAN pVElan = (PVELAN)MiniportAdapterContext; + + DBGPRINT(MUX_LOUD,("==> MPAdapterShutdown: VElan %p, ShutdwonAction %x\n", pVElan, ShutdownAction)); + + UNREFERENCED_PARAMETER(pVElan); + UNREFERENCED_PARAMETER(ShutdownAction); + + DBGPRINT(MUX_LOUD,("<== MPAdapterShutdown: VElan %p, ShutdwonAction %x\n", pVElan, ShutdownAction)); + + return; +} + + +VOID +MPUnload( + IN PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + This handler is used to unload the miniport + +Arguments: + DriverObject Pointer to the system's driver object structure + for this driver. + +Return Value: + None + + +--*/ +{ + +#if !DBG + UNREFERENCED_PARAMETER(DriverObject); +#endif + + DBGPRINT(MUX_LOUD, ("==> MPUnload: DriverObj %p\n", DriverObject)); + if (ProtHandle != NULL) + { + NdisDeregisterProtocolDriver(ProtHandle); + } + NdisMDeregisterMiniportDriver(DriverHandle); + + NdisFreeSpinLock(&GlobalLock); + + DBGPRINT(MUX_LOUD, ("<== MPUnload: DriverObj %p\n", DriverObject)); +} + +NDIS_STATUS +MPPause( + IN NDIS_HANDLE MiniportAdapterContext, + IN PNDIS_MINIPORT_PAUSE_PARAMETERS MiniportPauseParameters + ) +/*++ + +Routine Description: + This handler is used to pause the miniport. During which, no NET_BUFFER_LIST + will be indicated to the upper binding as well as status indications. + +Arguments: + MiniportAdapterContext Pointer to our VELAN + MiniportPauseParameters Specify the pause parameters + +Return Value: + NDIS_STATUS_SUCCESS + +--*/ +{ + PVELAN pVElan = (PVELAN)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + DBGPRINT(MUX_LOUD, ("==> MPPause: VElan %p\n", pVElan)); + + UNREFERENCED_PARAMETER(MiniportPauseParameters); + + DBGPRINT(MUX_LOUD,("==>MPPause Adapter %08lp\n",MiniportAdapterContext)); + + // Whilst the miniport is being paused, it cannot be restart + + NdisAcquireSpinLock(&pVElan->PauseLock); + + pVElan->Paused = TRUE; + + NdisReleaseSpinLock(&pVElan->PauseLock); + + + + DBGPRINT(MUX_LOUD,("<== MPPause,VElan %p, Status %8x\n", pVElan, Status)); + + return Status; +} + + +NDIS_STATUS +MPRestart( + IN NDIS_HANDLE MiniportAdapterContext, + IN PNDIS_MINIPORT_RESTART_PARAMETERS MiniportRestartParameters + ) +/*++ + +Routine Description: + This handler is used to restart the miniport. When the miniport is + back in the restart state, it can indicate NET_BUFFER_LISTs to the + upper binding + +Arguments: + MiniportAdapterContext Pointer to our VELAN + MiniportRestartParameters + +Return Value: + NDIS_STATUS_SUCCESS + +--*/ +{ + PVELAN pVElan = (PVELAN)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PNDIS_RESTART_ATTRIBUTES NdisRestartAttributes; + PNDIS_RESTART_GENERAL_ATTRIBUTES NdisGeneralAttributes; + + UNREFERENCED_PARAMETER(MiniportRestartParameters); + + DBGPRINT(MUX_LOUD,("==> MPRestart Adapter %p\n",MiniportAdapterContext)); + + + // + // Here the driver can change its restart attributes + // + NdisRestartAttributes = MiniportRestartParameters->RestartAttributes; + + // + // If NdisRestartAttributes is not NULL, then miniport can modify generic attributes and add + // new media specific info attributes at the end. Otherwise, NDIS restarts the miniport because + // of other reason, miniport should not try to modify/add attributes + // + if (NdisRestartAttributes != NULL) + { + + ASSERT(NdisRestartAttributes->Oid == OID_GEN_MINIPORT_RESTART_ATTRIBUTES); + + NdisGeneralAttributes = (PNDIS_RESTART_GENERAL_ATTRIBUTES)NdisRestartAttributes->Data; + UNREFERENCED_PARAMETER(NdisGeneralAttributes); + + // + // Check to see if we need to change any attributes, for example, the driver can change the current + // MAC address here. Or the driver can add media specific info attributes. + // + } + + NdisAcquireSpinLock(&pVElan->PauseLock); + pVElan->Paused = FALSE; + + NdisReleaseSpinLock(&pVElan->PauseLock); + + + DBGPRINT(MUX_LOUD,("<== MPRestart: Adapter %p, Status %8x\n", MiniportAdapterContext, Status)); + + return Status; +} + + +VOID +MPSendNetBufferLists( + IN NDIS_HANDLE MiniportAdapterContext, + IN PNET_BUFFER_LIST NetBufferLists, + IN NDIS_PORT_NUMBER PortNumber, + IN ULONG SendFlags + ) +/*++ + +Routine Description: + + Send NET_BUFFER_LISTs to the lower binding + +Arguments: + MiniportAdapterContext Pointer to our VELAN + NetBufferLists Set of NET_BUFFER_LISTs to send + SendFlags Specify the send flags + DispatchLevel TRUE if IRQL == DISPATCH_LEVEL + +Return Value: + None + +--*/ +{ + PVELAN pVElan = (PVELAN)MiniportAdapterContext; + PADAPT pAdapt = pVElan->pAdapt; + PNET_BUFFER_LIST CurrentNetBufferList = NULL; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PIM_NBL_ENTRY SendContext; + ULONG SendCompleteFlags = 0; + BOOLEAN DispatchLevel = FALSE; + + DBGPRINT(MUX_VERY_LOUD,("==> MPSendNetBufferLists: MiniportAdapterContext %p, NetBufferLists %p\n",MiniportAdapterContext,NetBufferLists)); + + DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags); + + while(NetBufferLists != NULL) + { + CurrentNetBufferList = NetBufferLists; + NetBufferLists = NET_BUFFER_LIST_NEXT_NBL(NetBufferLists); + NET_BUFFER_LIST_NEXT_NBL(CurrentNetBufferList) = NULL; + + MUX_ACQUIRE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel); + + if (pAdapt->BindingState != MuxAdapterBindingRunning) + { + Status = NDIS_STATUS_REQUEST_ABORTED; + MUX_RELEASE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel); + + break; + } + + pAdapt->OutstandingSends ++; + + MUX_RELEASE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel); + + do + { + Status = NdisAllocateNetBufferListContext(CurrentNetBufferList, + sizeof(IM_NBL_ENTRY), + 0, + MUX_TAG); + + if (Status != NDIS_STATUS_SUCCESS) + { + break; + } + + SendContext = (PIM_NBL_ENTRY)NET_BUFFER_LIST_CONTEXT_DATA_START(CurrentNetBufferList); + NdisZeroMemory(SendContext, sizeof(IM_NBL_ENTRY)); + SendContext->PreviousSourceHandle = CurrentNetBufferList->SourceHandle; + SendContext->pVElan = pVElan; + + +#ifdef IEEE_VLAN_SUPPORT + SendContext->Flags = 0; + + Status = MPHandleSendTaggingNB(pVElan, CurrentNetBufferList); + + if (Status != NDIS_STATUS_SUCCESS) + { + NdisFreeNetBufferListContext(CurrentNetBufferList, + sizeof(IM_NBL_ENTRY)); + break; + } + +#endif + + CurrentNetBufferList->SourceHandle = pAdapt->BindingHandle; + + MUX_INCR_PENDING_SENDS(pVElan); + + // + // Remove this flag, so NDIS will not try to loopback the packets to mux + // + SendFlags &= ~NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK; + + NdisSendNetBufferLists(pAdapt->BindingHandle, + CurrentNetBufferList, + PortNumber, + SendFlags); + + } while(FALSE); + + if (Status != NDIS_STATUS_SUCCESS) + { + MUX_ACQUIRE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel); + pAdapt->OutstandingSends --; + + if ((pAdapt->OutstandingSends == 0) && (pAdapt->PauseEvent != NULL)) + { + NdisSetEvent(pAdapt->PauseEvent); + pAdapt->PauseEvent = NULL; + } + + MUX_RELEASE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel); + // + // Handle failure case + // + NET_BUFFER_LIST_STATUS(CurrentNetBufferList) = Status; + + if (NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags)) + { + NDIS_SET_SEND_COMPLETE_FLAG(SendCompleteFlags, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); + } + + NdisMSendNetBufferListsComplete(pVElan->MiniportAdapterHandle, + CurrentNetBufferList, + SendCompleteFlags); + + Status = NDIS_STATUS_SUCCESS; + } + } + + if (Status != NDIS_STATUS_SUCCESS) + { + PNET_BUFFER_LIST TempNetBufferList; + + for (TempNetBufferList = CurrentNetBufferList; + TempNetBufferList != NULL; + TempNetBufferList = NET_BUFFER_LIST_NEXT_NBL(TempNetBufferList)) + { + NET_BUFFER_LIST_STATUS(TempNetBufferList) = Status; + } + if (NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags)) + { + NDIS_SET_SEND_COMPLETE_FLAG(SendCompleteFlags, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); + } + + NdisMSendNetBufferListsComplete(pVElan->MiniportAdapterHandle, + CurrentNetBufferList, + SendCompleteFlags); + } + + DBGPRINT(MUX_VERY_LOUD,("<== MPSendNetBufferLists, MiniportAdapterContext %p, NetBufferLists %p\n",MiniportAdapterContext,NetBufferLists)); +} + +VOID +MPReturnNetBufferLists( + IN NDIS_HANDLE MiniportAdapterContext, + IN PNET_BUFFER_LIST NetBufferLists, + IN ULONG ReturnFlags + ) +/*++ + +Routine Description: + NDIS Miniport entry point called whenever protocols are done with + a packet that we had indicated up and they had queued up for returning + later. + +Arguments: + MiniportAdapterContext Pointer to VELAN structure + NetBufferLists NetBufferLists being returned + Dispatch TRUE if IRQL == DISPATCH_LEVEL + +Return Value: + None + +--*/ +{ + PVELAN pVElan = (PVELAN)MiniportAdapterContext; + PNET_BUFFER_LIST CurrentNetBufferList = NULL; + ULONG NumberOfNetBufferLists = 0; +#ifdef IEEE_VLAN_SUPPORT + NDIS_STATUS Status; +#endif + + DBGPRINT(MUX_VERY_LOUD,("==> MPReturnNetBufferLists: MiniportAdapterContext %p, NetBufferList %p\n",MiniportAdapterContext,NetBufferLists)); + + CurrentNetBufferList = NetBufferLists; + + while (CurrentNetBufferList) + { + NumberOfNetBufferLists++; + +#ifdef IEEE_VLAN_SUPPORT + // + // Retreat the NBL before returning it to the miniport + // + Status = PtRestoreReceiveNBL(CurrentNetBufferList); + + ASSERT(Status == NDIS_STATUS_SUCCESS); + + // + // Free the context that was allocated in PtReceiveNBL + // + NdisFreeNetBufferListContext(CurrentNetBufferList, + sizeof(RECV_NBL_ENTRY)); +#endif + + CurrentNetBufferList = NET_BUFFER_LIST_NEXT_NBL(CurrentNetBufferList); + } + + NdisReturnNetBufferLists(pVElan->BindingHandle, + NetBufferLists, + ReturnFlags); + + MUX_DECR_MULTIPLE_PENDING_RECEIVES(pVElan, NumberOfNetBufferLists); + + DBGPRINT(MUX_VERY_LOUD,("<== MPReturnNetBufferLists: MiniportAdapterContext %p, NetBufferList %p\n",MiniportAdapterContext,NetBufferLists)); +} + + +VOID +MPCancelSendNetBufferLists( + IN NDIS_HANDLE MiniportAdapterContext, + IN PVOID CancelId + ) +/*++ + +Routine Description: + + The miniport entry point to hanadle cancellation of all send packets that + match the given CancelId. If we have queued any packets that match this, + then we should dequeue them and call NdisMSendCompleteNetBufferLists for + all such packets, with a status of NDIS_STATUS_REQUEST_ABORTED. + + We should also call NdisCancelSendPackets in turn, on each lower binding + that this adapter corresponds to. This is to let miniports below cancel + any matching packets. + +Arguments: + + MiniportAdapterContext Pointer to VELAN structure + CancelID ID of NetBufferLists to be cancelled + +Return Value: + None + +--*/ +{ + PVELAN pVElan = (PVELAN)MiniportAdapterContext; + + DBGPRINT(MUX_LOUD,("==> MPCancelSendNetBufferLists: VElan %p, CancelId %p\n", pVElan, CancelId)); + + NdisCancelSendNetBufferLists(pVElan->pAdapt->BindingHandle,CancelId); + + DBGPRINT(MUX_LOUD,("<== MPCancelSendNetBufferLists: VElan %p, CancelId %p\n", pVElan, CancelId)); +} + +VOID +MPCancelOidRequest( + IN NDIS_HANDLE MiniportAdapterContext, + IN PVOID RequestId + ) +/*++ + +Routine Description: + + The miniport entry point to hanadle cancellation of a request. This function + checks to see if the CancelRequest should be terminated at this level + or passed down to the next driver. + +Arguments: + + MiniportAdapterContext Pointer to VELAN structure + RequestId RequestId to be cancelled + +Return Value: + None + +--*/ +{ + PVELAN pVElan = (PVELAN)MiniportAdapterContext; + PMUX_NDIS_REQUEST pMuxNdisRequest = &pVElan->Request; + BOOLEAN fCancelRequest = FALSE; + + DBGPRINT(MUX_LOUD, ("==> MPCancelOidRequest: VELAN %p, RequestId %p\n", pVElan, RequestId)); + + NdisAcquireSpinLock(&pVElan->Lock); + if (pMuxNdisRequest->OrigRequest != NULL) + { + if (pMuxNdisRequest->OrigRequest->RequestId == RequestId) + { + pMuxNdisRequest->Cancelled = TRUE; + fCancelRequest = TRUE; + pMuxNdisRequest->Refcount++; + } + + } + + NdisReleaseSpinLock(&pVElan->Lock); + + // + // If we find the request, just send down the cancel, otherwise return because there is only + // one request pending from upper layer on the miniport + // + if (fCancelRequest) + { + NdisCancelOidRequest(pVElan->pAdapt->BindingHandle, &pMuxNdisRequest->Request); + + PtCompleteForwardedRequest(pVElan->pAdapt, + pMuxNdisRequest, + NDIS_STATUS_REQUEST_ABORTED); + } + + DBGPRINT(MUX_LOUD, ("<== MPCancelOidRequest: VELAN %p, RequestId %p\n", pVElan, RequestId)); +} + + + +#ifdef IEEE_VLAN_SUPPORT + +PMDL +MuxAllocateMdl( + IN OUT PULONG BufferSize + ) +/*++ + +Routine Description: + This function is called by NDIS in order to allocate an MDL and memory when + there isn't unused data space in the net buffer when NdisRetreatNetBufferDataStart + is called + +Arguments: + BufferSize Pointer to allocation size being requested + +Return Value: + +NOTE: This function always returns NULL. This is so that MUX can allocate memory and MDL + and save the required context about the NetBuffer in the allocated memory + +--*/ +{ + UNREFERENCED_PARAMETER(BufferSize); + + return NULL; +} + +NDIS_STATUS +MPHandleSendTaggingNB( + IN PVELAN pVElan, + IN PNET_BUFFER_LIST NetBufferList + ) +/*++ + +Routine Description: + This function is called when the driver supports IEEE 802.1Q taggng. It checks + the netbuffer to be sent on a VELAN and inserts a tag header if necessary. + +Arguments: + pVElan Pointer to VELAN structure + NetBufferList A pointer to a NET_BUFFER_LIST + +Return Value: + NDIS_STATUS_SUCCESS + NDIS_STATUS_XXX + +NOTE: This functio doesn't handle vlan tagging in an efficient way, please wait for the next release + to get a better implementation. + +--*/ +{ + NDIS_STATUS Status; + NDIS_NET_BUFFER_LIST_8021Q_INFO NdisPacket8021qInfo; + PUCHAR pEthFrame = NULL; + PUCHAR pEthFrameNew = NULL; + PUSHORT pTpid; + PVLAN_TAG_HEADER pTagHeader; + PIM_NBL_ENTRY SendContext; + PNET_BUFFER CurrentNetBuffer; + PIM_SEND_NB_ENTRY pNetBufferContext, LastNetBufferContext; + PVOID pVa; + PMDL Mdl, FirstMdl, SecondMdl, PrevMdl; + ULONG BytesToSkip; + ULONG BufferLength; + PVOID Storage; + PNET_BUFFER MdlAllocatedNetBuffers = NULL; + + DBGPRINT(MUX_LOUD, ("==> MPHandleSendTaggingNB: VELAN %p, NetBufferList %p\n", pVElan, NetBufferList)); + + NdisPacket8021qInfo.Value = NET_BUFFER_LIST_INFO(NetBufferList, Ieee8021QNetBufferListInfo); + SendContext = (PIM_NBL_ENTRY)NET_BUFFER_LIST_CONTEXT_DATA_START(NetBufferList); + + do + { + Status = NDIS_STATUS_SUCCESS; + + // If the vlan ID of the virtual miniport is 0, the miniport should act like it doesn't + // support VELAN tag processing + + if (MuxRecognizedVlanId(pVElan,0)) + { + break; + } + + // + // Insert a tag only if we have a configured VLAN ID. Note that we do not + // support E-RIF + // + + if (NdisPacket8021qInfo.TagHeader.CanonicalFormatId) + { + // + // Skip the packet, return NDIS_STATUS_FAILURE + // + Status = NDIS_STATUS_INVALID_PACKET; + break; + } + + + // + // If the there is a tag header and it doesn't match the VLAN ID + // then the packet is invalid .. ignore! + // + if ((NdisPacket8021qInfo.TagHeader.VlanId) + && (! MuxRecognizedVlanId(pVElan,NdisPacket8021qInfo.TagHeader.VlanId))) + { + Status = NDIS_STATUS_INVALID_PACKET; + break; + } + + CurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); + LastNetBufferContext = NULL; + + while(CurrentNetBuffer) + { + // + // Find the start address of the frame + // + Storage = NULL; + pEthFrame = NdisGetDataBuffer(CurrentNetBuffer, + ETH_HEADER_SIZE, + Storage, + 1, + 0); + + if (pEthFrame == NULL) + { + Status = NDIS_STATUS_INVALID_PACKET; + break; + } + + Mdl = NET_BUFFER_CURRENT_MDL(CurrentNetBuffer); + PrevMdl = NULL; + + // + // Retreat the net buffer list + // + Status = NdisRetreatNetBufferDataStart(CurrentNetBuffer, + VLAN_TAG_HEADER_SIZE, + 0, + MuxAllocateMdl); + + if (Status == NDIS_STATUS_SUCCESS) + { + // + // If there was a MDL ahead of the current MDL, this could result + // in the retreat being successful, but the retreated bytes being in + // a different MDL. But we need to make sure that the ethernet header + // with the VLAN tag is in contiguous memory + // + if (Mdl != NET_BUFFER_CURRENT_MDL(CurrentNetBuffer)) + { + // + // Advance the NetBuffer so that we can allocate MDLs instead + // + NdisAdvanceNetBufferDataStart(CurrentNetBuffer, + VLAN_TAG_HEADER_SIZE, + FALSE, + NULL); + + Status = NDIS_STATUS_RESOURCES; + } + } + + if (Status == NDIS_STATUS_RESOURCES) + { + do + { + // + // There is no more unused data space in the NetBuffer, need to allocate + // a new MDL and memory + // + BytesToSkip = ETH_HEADER_SIZE; + Mdl = NET_BUFFER_CURRENT_MDL(CurrentNetBuffer); + + // + // Assume the Ethernet Header is in the first buffer of the packet. + // The following loop is to find the start address of the data after + // the ethernet header. This may be either in the first MDL + // or in the second. + // + while (TRUE) + { + pVa = NULL; + NdisQueryMdl(Mdl, &pVa, &BufferLength, NormalPagePriority); + + if (pVa == NULL) + { + break; + } + + // + // Have we gone far enough into the packet? + // + if (BytesToSkip == 0) + { + break; + } + + // + // Does the current buffer contain bytes past the Ethernet + // header? If so, stop. + // + if (BufferLength > BytesToSkip) + { + pVa = (PVOID)((PUCHAR)pVa + BytesToSkip); + BufferLength -= BytesToSkip; + break; + } + + // + // We haven't gone past the Ethernet header yet, so go + // to the next buffer. + // + BytesToSkip -= BufferLength; + Mdl = NDIS_MDL_LINKAGE(Mdl); + } + + if (pVa == NULL) + { + Status = NDIS_STATUS_RESOURCES; + break; + } + + // + // AllocateSpace for Ethernet + VLAN tag header + Netbuffer context + // + pNetBufferContext = (PIM_SEND_NB_ENTRY) NdisAllocateFromNPagedLookasideList(&pVElan->TagLookaside); + + // + // Memory allocation failed + // + if (pNetBufferContext == NULL) + { + Status = NDIS_STATUS_RESOURCES; + break; + } + + NdisZeroMemory((PVOID)pNetBufferContext, sizeof(IM_SEND_NB_ENTRY)); + + pEthFrameNew = ((PUCHAR) pNetBufferContext) + sizeof(IM_SEND_NB_ENTRY); + + // + // Allocate MDLs for the Ethernet + VLAN tag header and + // the data that follow these. + // + SecondMdl = NdisAllocateMdl(pVElan->MiniportAdapterHandle, + pVa, // byte following the Eth+tag headers + BufferLength); + + FirstMdl = NdisAllocateMdl(pVElan->MiniportAdapterHandle, + pEthFrameNew, + ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE); + + if (!FirstMdl || !SecondMdl) + { + // + // One of the buffer allocations failed + // + if (FirstMdl) + { + NdisFreeMdl(FirstMdl); + } + + if (SecondMdl) + { + NdisFreeMdl(SecondMdl); + } + + NdisFreeToNPagedLookasideList(&pVElan->TagLookaside, (PVOID) pNetBufferContext); + + Status = NDIS_STATUS_RESOURCES; + break; + } + + // + // All allocations are successful. + // Copy the Ethernet header to the newly allocated memory + // Leave space for the VLAN tag + // + NdisMoveMemory(pEthFrameNew, pEthFrame, 2 * ETH_LENGTH_OF_ADDRESS); + + NdisMoveMemory(pEthFrameNew + (2 * ETH_LENGTH_OF_ADDRESS) + VLAN_TAG_HEADER_SIZE, + pEthFrame + (2 * ETH_LENGTH_OF_ADDRESS), + 2); + + // + // Save the context for the NetBuffer + // + pNetBufferContext->CurrentMdl = NET_BUFFER_CURRENT_MDL(CurrentNetBuffer); + + // + // If the current MDL is not the first on in the chain, we need to adjust the MDL chain + // + if (NET_BUFFER_FIRST_MDL(CurrentNetBuffer) != NET_BUFFER_CURRENT_MDL(CurrentNetBuffer)) + { + PrevMdl = NET_BUFFER_FIRST_MDL(CurrentNetBuffer); + while (NDIS_MDL_LINKAGE(PrevMdl) != NET_BUFFER_CURRENT_MDL(CurrentNetBuffer)) + { + PrevMdl = NDIS_MDL_LINKAGE(PrevMdl); + } + + pNetBufferContext->PrevMdl = PrevMdl; + } + + pNetBufferContext->CurrentMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(CurrentNetBuffer); + + // + // Link this NB to the NBL context to be restored + // This is so that MPRestoreSendNBL can free the MDLs that were allocated + // for this NB + // + if (MdlAllocatedNetBuffers == NULL) + { + MdlAllocatedNetBuffers = CurrentNetBuffer; + } + else + { + ASSERT(LastNetBufferContext); + LastNetBufferContext->NextNetBuffer = CurrentNetBuffer; + } + + LastNetBufferContext = pNetBufferContext; + + // + // Adjust the NetBuffer to use the new Mdls + // + NDIS_MDL_LINKAGE(FirstMdl) = SecondMdl; + + NDIS_MDL_LINKAGE(SecondMdl) = NDIS_MDL_LINKAGE(Mdl); + + NET_BUFFER_DATA_OFFSET(CurrentNetBuffer) = NET_BUFFER_DATA_OFFSET(CurrentNetBuffer) - + NET_BUFFER_CURRENT_MDL_OFFSET(CurrentNetBuffer); + + NET_BUFFER_DATA_LENGTH(CurrentNetBuffer) += VLAN_TAG_HEADER_SIZE; + + NET_BUFFER_CURRENT_MDL_OFFSET(CurrentNetBuffer) = 0; + + NET_BUFFER_CURRENT_MDL(CurrentNetBuffer) = FirstMdl; + + // + // If there are any MDLs in the MDL chain ahead of the current MDL, + // adjust the linkage + // + if (PrevMdl) + { + NDIS_MDL_LINKAGE(PrevMdl) = FirstMdl; + } + else + { + NET_BUFFER_FIRST_MDL(CurrentNetBuffer) = FirstMdl; + } + + Status = NDIS_STATUS_SUCCESS; + } + while (FALSE); + } + else if (Status == NDIS_STATUS_SUCCESS) + { + // + // There was enough unused space in the NetBuffer to + // accomodate the VLAN tag. + // Get new start address of frame + // + Storage = NULL; + pEthFrameNew = NdisGetDataBuffer(CurrentNetBuffer, + VLAN_TAG_HEADER_SIZE, + Storage, + 1, + 0); + + if (pEthFrameNew == NULL) + { + NdisAdvanceNetBufferDataStart(CurrentNetBuffer, + VLAN_TAG_HEADER_SIZE, + FALSE, + NULL); + + Status = NDIS_STATUS_INVALID_PACKET; + } + else + { + // + // Adjust the header to insert the VLAN tag in the packet frame + // + NdisMoveMemory(pEthFrameNew, pEthFrame, 2 * ETH_LENGTH_OF_ADDRESS); + } + + } + + if (Status != NDIS_STATUS_SUCCESS) + { + break; + } + + pTpid = (PUSHORT)((PUCHAR)pEthFrameNew + 2 * ETH_LENGTH_OF_ADDRESS); + *pTpid = TPID; + pTagHeader = (PVLAN_TAG_HEADER)(pTpid + 1); + + // + // Write IEEE 802.1Q info to packet frame + // + + INITIALIZE_TAG_HEADER_TO_ZERO(pTagHeader); + + if (NdisPacket8021qInfo.Value) + { + SET_USER_PRIORITY_TO_TAG(pTagHeader, NdisPacket8021qInfo.TagHeader.UserPriority); + } + else + { + SET_USER_PRIORITY_TO_TAG(pTagHeader, 0); + } + + SET_CANONICAL_FORMAT_ID_TO_TAG(pTagHeader, 0); + + if (NdisPacket8021qInfo.TagHeader.VlanId) + { + SET_VLAN_ID_TO_TAG(pTagHeader, NdisPacket8021qInfo.TagHeader.VlanId); + } + else + { + SET_VLAN_ID_TO_TAG(pTagHeader, pVElan->VlanId); + } + + CurrentNetBuffer = NET_BUFFER_NEXT_NB(CurrentNetBuffer); + } + + if(Status == NDIS_STATUS_SUCCESS) + { + SendContext->Flags |= MUX_RETREAT_DATA; + SendContext->MdlAllocatedNetBuffers = MdlAllocatedNetBuffers; + NET_BUFFER_LIST_INFO(NetBufferList, Ieee8021QNetBufferListInfo) = 0; + } + else + { + // + // In case of failure, restore the NetBuffers to their original state + // Only the NetBuffers in the NBL upto CurrentNetBuffer needs to be restored + // + MPRestoreSendNBL(pVElan, NetBufferList, CurrentNetBuffer, MdlAllocatedNetBuffers); + } + + } while(FALSE); + + DBGPRINT(MUX_LOUD, ("<== MPHandleSendTaggingNB: VELAN %p, NetBufferList %p, Status %8x\n", pVElan, NetBufferList, Status)); + return Status; +} + +VOID +MPRestoreSendNBL( + IN PVELAN pVElan, + IN PNET_BUFFER_LIST NetBufferList, + IN PNET_BUFFER LastNetBuffer, + IN PNET_BUFFER MdlAllocatedNetBuffers + ) +/*++ + +Routine Description: + Restore the NBL that was modified during Send + +Arguments: + pVElan Pointer to VELAN structure + NetBufferList A pointer to a NET_BUFFER_LIST + +Return Value: + +--*/ + +{ + PNET_BUFFER CurrentNetBuffer; + PNET_BUFFER CurrentMdlAllocatedNetBuffer, SavedMdlAllocatedNetBuffer; + PIM_SEND_NB_ENTRY NetBufferContext; + PVOID pVa = NULL; + ULONG BufferLength; + PUCHAR pFrame = NULL, pDst = NULL; + PMDL FirstMdl, SecondMdl; + PVOID Storage; + + CurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); + CurrentMdlAllocatedNetBuffer = MdlAllocatedNetBuffers; + + while (CurrentNetBuffer != LastNetBuffer) + { + SavedMdlAllocatedNetBuffer = CurrentMdlAllocatedNetBuffer; + + // + // Free the MDLs and the memory allocated for the NET_BUFFER + // + if (CurrentMdlAllocatedNetBuffer) + { + NdisQueryMdl(NET_BUFFER_CURRENT_MDL(CurrentMdlAllocatedNetBuffer), + &pVa, + &BufferLength, + NormalPagePriority); + if( pVa == NULL ){ + //you may do something + } + NetBufferContext = (PIM_SEND_NB_ENTRY) ((PUCHAR) pVa - sizeof(IM_SEND_NB_ENTRY)); + ASSERT(NetBufferContext != NULL); + + // + // Save the MDLs to be freed in temporary variables + // + FirstMdl = NET_BUFFER_CURRENT_MDL(CurrentMdlAllocatedNetBuffer); + + SecondMdl = NDIS_MDL_LINKAGE(FirstMdl); + + // + // Adjust the offsets and length + // + if( NetBufferContext == NULL ){ + //check why NetBufferContext is NULL + } + else{ + NET_BUFFER_DATA_OFFSET(CurrentMdlAllocatedNetBuffer) = NET_BUFFER_DATA_OFFSET(CurrentMdlAllocatedNetBuffer) + + NetBufferContext->CurrentMdlOffset; + + NET_BUFFER_DATA_LENGTH(CurrentMdlAllocatedNetBuffer) -= VLAN_TAG_HEADER_SIZE; + + NET_BUFFER_CURRENT_MDL_OFFSET(CurrentMdlAllocatedNetBuffer) = NetBufferContext->CurrentMdlOffset; + + NET_BUFFER_CURRENT_MDL(CurrentMdlAllocatedNetBuffer) = NetBufferContext->CurrentMdl; + + if (NetBufferContext->PrevMdl) + { + NDIS_MDL_LINKAGE(NetBufferContext->PrevMdl) = NetBufferContext->CurrentMdl; + } + else + { + NET_BUFFER_FIRST_MDL(CurrentMdlAllocatedNetBuffer) = NetBufferContext->CurrentMdl; + } + + CurrentMdlAllocatedNetBuffer = NetBufferContext->NextNetBuffer; + + // + // Free the MDLs and the memory allocated + // + NdisFreeMdl(SecondMdl); + + NdisFreeMdl(FirstMdl); + + NdisFreeToNPagedLookasideList(&pVElan->TagLookaside, (PVOID) NetBufferContext); + } + } + + // + // Advance the NET_BUFFERs until the NET_BUFFER for which + // the MDLs were allocated + // + while ((CurrentNetBuffer != SavedMdlAllocatedNetBuffer) && + (CurrentNetBuffer != LastNetBuffer)) + { + Storage = NULL; + pFrame = NdisGetDataBuffer(CurrentNetBuffer, + (2 * ETH_LENGTH_OF_ADDRESS) + VLAN_TAG_HEADER_SIZE, + Storage, + 1, + 0); + + if (pFrame == NULL) + { + ASSERT(FALSE); + } + else + { + // + // Restore the original header + // + pDst = pFrame + VLAN_TAG_HEADER_SIZE; + + RtlMoveMemory(pDst, pFrame, (2 * ETH_LENGTH_OF_ADDRESS)); + } + + NdisAdvanceNetBufferDataStart(CurrentNetBuffer, + VLAN_TAG_HEADER_SIZE, + FALSE, + NULL); + + CurrentNetBuffer = NET_BUFFER_NEXT_NB(CurrentNetBuffer); + } + + if (SavedMdlAllocatedNetBuffer) + { + CurrentNetBuffer = NET_BUFFER_NEXT_NB(SavedMdlAllocatedNetBuffer); + } + } +} + +#endif + + diff --git a/network/ndis/mux/driver/60/mux.c b/network/ndis/mux/driver/60/mux.c new file mode 100644 index 00000000..1ff8698b --- /dev/null +++ b/network/ndis/mux/driver/60/mux.c @@ -0,0 +1,516 @@ +/*++ +Copyright (c) 1992-2000 Microsoft Corporation + +Module Name: + + mux.c + +Abstract: + + DriverEntry and NT dispatch functions for the NDIS MUX Intermediate + Miniport driver sample. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + + +#include "precomp.h" +#pragma hdrstop + +#define MODULE_NUMBER MODULE_MUX + +#pragma NDIS_INIT_FUNCTION(DriverEntry) + + +#if DBG +// +// Debug level for mux driver +// +INT muxDebugLevel = MUX_WARN; + +#endif //DBG +// +// G L O B A L V A R I A B L E S +// ----------- ----------------- +// + +NDIS_MEDIUM MediumArray[1] = + { + NdisMedium802_3, // Ethernet + }; + +NDIS_SPIN_LOCK GlobalLock; + +// +// Global Mutex protects the AdapterList; +// see macros MUX_ACQUIRE/RELEASE_MUTEX +// +MUX_MUTEX GlobalMutex = {0}; + +// +// List of all bound adapters. +// +LIST_ENTRY AdapterList; + + +// +// list all virtual adapters +// +LIST_ENTRY VElanList; + + +// +// Total number of VELAN miniports in existance: +// +LONG MiniportCount = 0; + +// +// Used to assign VELAN numbers (which are used to generate MAC +// addresses). +// +ULONG NextVElanNumber = 0; // monotonically increasing count + +// +// Some global NDIS handles: +// +NDIS_HANDLE ProtHandle = NULL; // From NdisRegisterProtocolDriver +NDIS_HANDLE DriverHandle = NULL; // From NdisMRegisterMiniportDriver +NDIS_HANDLE NdisDeviceHandle = NULL; // From NdisMRegisterDeviceEx + +PDEVICE_OBJECT ControlDeviceObject = NULL; // Device for IOCTLs +MUX_MUTEX ControlDeviceMutex; + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + First entry point to be called, when this driver is loaded. + Register with NDIS as an intermediate driver. + +Arguments: + + DriverObject - pointer to the system's driver object structure + for this driver + + RegistryPath - system's registry path for this driver + +Return Value: + + STATUS_SUCCESS if all initialization is successful, STATUS_XXX + error code if not. + +--*/ +{ + NDIS_STATUS Status; + NDIS_PROTOCOL_DRIVER_CHARACTERISTICS PChars; + NDIS_MINIPORT_DRIVER_CHARACTERISTICS MChars; + NDIS_HANDLE MiniportDriverContext; + NDIS_HANDLE ProtocolDriverContext; + NDIS_STRING Name; + + + NdisInitializeListHead(&AdapterList); + NdisInitializeListHead(&VElanList); + MiniportDriverContext=NULL; + ProtocolDriverContext=NULL; + + MUX_INIT_MUTEX(&GlobalMutex); + MUX_INIT_MUTEX(&ControlDeviceMutex); + NdisAllocateSpinLock(&GlobalLock); + + + do + { + + // + // Register the miniport with NDIS. Note that it is the + // miniport which was started as a driver and not the protocol. + // Also the miniport must be registered prior to the protocol + // since the protocol's BindAdapter handler can be initiated + // anytime and when it is, it must be ready to + // start driver instances. + // + NdisZeroMemory(&MChars, sizeof(NDIS_MINIPORT_DRIVER_CHARACTERISTICS)); + + MChars.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + MChars.Header.Size = sizeof(NDIS_MINIPORT_DRIVER_CHARACTERISTICS); + MChars.Header.Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_1; + + MChars.MajorNdisVersion = MUX_MAJOR_NDIS_VERSION; + MChars.MinorNdisVersion = MUX_MINOR_NDIS_VERSION; + + MChars.MajorDriverVersion = MUX_MAJOR_DRIVER_VERSION; + MChars.MinorDriverVersion = MUX_MINOR_DRIVER_VERSION; + + MChars.SetOptionsHandler = MpSetOptions; + MChars.InitializeHandlerEx = MPInitialize; + MChars.UnloadHandler = MPUnload; + MChars.HaltHandlerEx = MPHalt; + + MChars.OidRequestHandler = MPOidRequest; + + MChars.CancelSendHandler = MPCancelSendNetBufferLists; + MChars.DevicePnPEventNotifyHandler = MPDevicePnPEvent; + MChars.ShutdownHandlerEx = MPAdapterShutdown; + MChars.CancelOidRequestHandler = MPCancelOidRequest; + + // + // We will disable the check for hang timeout so we do not + // need a check for hang handler! + // + MChars.CheckForHangHandlerEx = NULL; + + MChars.ReturnNetBufferListsHandler = MPReturnNetBufferLists; + MChars.SendNetBufferListsHandler = MPSendNetBufferLists; + + MChars.PauseHandler = MPPause; + MChars.RestartHandler = MPRestart; + + MChars.Flags = NDIS_INTERMEDIATE_DRIVER; + Status = NdisMRegisterMiniportDriver(DriverObject, + RegistryPath, + MiniportDriverContext, + &MChars, + &DriverHandle); + + if (Status != NDIS_STATUS_SUCCESS) + { + break; + } + + // + // Now register the protocol. + // + NdisZeroMemory(&PChars, sizeof(NDIS_PROTOCOL_DRIVER_CHARACTERISTICS)); + + PChars.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + PChars.Header.Size = sizeof(NDIS_PROTOCOL_DRIVER_CHARACTERISTICS); + PChars.Header.Revision = NDIS_PROTOCOL_DRIVER_CHARACTERISTICS_REVISION_1; + + PChars.MajorNdisVersion = MUX_PROT_MAJOR_NDIS_VERSION; + PChars.MinorNdisVersion = MUX_PROT_MINOR_NDIS_VERSION; + + PChars.MajorDriverVersion = MUX_MAJOR_DRIVER_VERSION; + PChars.MinorDriverVersion = MUX_MINOR_DRIVER_VERSION; + + PChars.SetOptionsHandler = PtSetOptions; + + // + // Make sure the protocol-name matches the service-name + // (from the INF) under which this protocol is installed. + // This is needed to ensure that NDIS can correctly determine + // the binding and call us to bind to miniports below. + // + NdisInitUnicodeString(&Name, L"MUXP"); // Protocol name + PChars.Name = Name; + PChars.OpenAdapterCompleteHandlerEx = PtOpenAdapterComplete; + PChars.CloseAdapterCompleteHandlerEx = PtCloseAdapterComplete; + + PChars.ReceiveNetBufferListsHandler = PtReceiveNBL; + PChars.SendNetBufferListsCompleteHandler = PtSendNBLComplete; + PChars.OidRequestCompleteHandler = PtRequestComplete; + PChars.StatusHandlerEx = PtStatus; + PChars.BindAdapterHandlerEx = PtBindAdapter; + PChars.UnbindAdapterHandlerEx = PtUnbindAdapter; + PChars.NetPnPEventHandler= PtPNPHandler; + Status = NdisRegisterProtocolDriver(ProtocolDriverContext, + &PChars, + &ProtHandle); + + if (Status != NDIS_STATUS_SUCCESS) + { + NdisMDeregisterMiniportDriver(DriverHandle); + break; + } + // + // Let NDIS know of the association between our protocol + // and miniport entities. + // + NdisIMAssociateMiniport(DriverHandle, ProtHandle); + }while (FALSE); + + return(Status); +} + +NDIS_STATUS +MpSetOptions( + IN NDIS_HANDLE NdisDriverHandle, + IN NDIS_HANDLE DriverContext + ) +/*++ + +Routine Description: + This routine registers the optional handlers for the MUX MINIPORT driver + with NDIS. + +Arguments: + + NdisDriverHandle Mux miniport driver handle + DriverContext Specifies a handle to a driver-allocated context area where the driver + maintains state and configuration information + +Return Value: + + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + UNREFERENCED_PARAMETER(DriverContext); + UNREFERENCED_PARAMETER(NdisDriverHandle); + return Status; +} + + +NDIS_STATUS +PtSetOptions( + IN NDIS_HANDLE NdisDriverHandle, + IN NDIS_HANDLE DriverContext + ) +/*++ + +Routine Description: + This routine registers the optional handlers for the MUX PROTOCOL driver + with NDIS. + +Arguments: + + NdisDriverHandle Mux protocol driver handle + DriverContext Specifies a handle to a driver-allocated context area where the driver + maintains state and configuration information + +Return Value: + + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + UNREFERENCED_PARAMETER(DriverContext); + UNREFERENCED_PARAMETER(NdisDriverHandle); + + return Status; +} + + +NDIS_STATUS +PtRegisterDevice( + VOID + ) +/*++ + +Routine Description: + + Register an ioctl interface - a device object to be used for this + purpose is created by NDIS when we call NdisMRegisterDevice. + + This routine is called whenever a new miniport instance is + initialized. However, we only create one global device object, + when the first miniport instance is initialized. This routine + handles potential race conditions with PtDeregisterDevice via + the ControlDeviceMutex. + + NOTE: do not call this from DriverEntry; it will prevent the driver + from being unloaded (e.g. on uninstall). + +Arguments: + + None + +Return Value: + + NDIS_STATUS_SUCCESS if we successfully register a device object. + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + UNICODE_STRING DeviceName; + UNICODE_STRING DeviceLinkUnicodeString; + NDIS_DEVICE_OBJECT_ATTRIBUTES DeviceObjectAttributes; + PDRIVER_DISPATCH DispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1]; + + + + DBGPRINT(MUX_LOUD, ("==>PtRegisterDevice\n")); + + MUX_ACQUIRE_MUTEX(&ControlDeviceMutex); + + ++MiniportCount; + + if (1 == MiniportCount) + { + NdisZeroMemory(DispatchTable, (IRP_MJ_MAXIMUM_FUNCTION+1) * sizeof(PDRIVER_DISPATCH)); + + DispatchTable[IRP_MJ_CREATE] = PtDispatch; + DispatchTable[IRP_MJ_CLEANUP] = PtDispatch; + DispatchTable[IRP_MJ_CLOSE] = PtDispatch; + DispatchTable[IRP_MJ_DEVICE_CONTROL] = PtDispatch; + + + NdisInitUnicodeString(&DeviceName, NTDEVICE_STRING); + NdisInitUnicodeString(&DeviceLinkUnicodeString, GLOBAL_LINKNAME_STRING); + + NdisZeroMemory(&DeviceObjectAttributes, sizeof(NDIS_DEVICE_OBJECT_ATTRIBUTES)); + + DeviceObjectAttributes.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; // type implicit from the context + DeviceObjectAttributes.Header.Revision = NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1; + DeviceObjectAttributes.Header.Size = sizeof(NDIS_DEVICE_OBJECT_ATTRIBUTES); + DeviceObjectAttributes.DeviceName = &DeviceName; + DeviceObjectAttributes.SymbolicName = &DeviceLinkUnicodeString; + DeviceObjectAttributes.MajorFunctions = &DispatchTable[0]; + DeviceObjectAttributes.ExtensionSize = 0; + DeviceObjectAttributes.DefaultSDDLString = NULL; + DeviceObjectAttributes.DeviceClassGuid = 0; + + Status = NdisRegisterDeviceEx( + DriverHandle, + &DeviceObjectAttributes, + &ControlDeviceObject, + &NdisDeviceHandle); + + } + + MUX_RELEASE_MUTEX(&ControlDeviceMutex); + + DBGPRINT(MUX_INFO, ("<==PtRegisterDevice: %x\n", Status)); + + return (Status); +} + + +NTSTATUS +PtDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +/*++ +Routine Description: + + Process IRPs sent to this device. + +Arguments: + + DeviceObject pointer to a device object + Irp pointer to an I/O Request Packet + +Return Value: + + NTSTATUS - STATUS_SUCCESS always - change this when adding + real code to handle ioctls. + +--*/ +{ + PIO_STACK_LOCATION irpStack; + NTSTATUS status = STATUS_SUCCESS; + ULONG inlen; + PVOID buffer; + + UNREFERENCED_PARAMETER(DeviceObject); + + irpStack = IoGetCurrentIrpStackLocation(Irp); + DBGPRINT(MUX_LOUD, ("==>PtDispatch %d\n", irpStack->MajorFunction)); + + switch (irpStack->MajorFunction) + { + case IRP_MJ_CREATE: + break; + + case IRP_MJ_CLEANUP: + break; + + case IRP_MJ_CLOSE: + break; + + case IRP_MJ_DEVICE_CONTROL: + { + + buffer = Irp->AssociatedIrp.SystemBuffer; + inlen = irpStack->Parameters.DeviceIoControl.InputBufferLength; + UNREFERENCED_PARAMETER(buffer); + UNREFERENCED_PARAMETER(inlen); + + switch (irpStack->Parameters.DeviceIoControl.IoControlCode) + { + // + // Add code here to handle ioctl commands. + // + } + break; + } + default: + break; + } + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + DBGPRINT(MUX_LOUD, ("<== Pt Dispatch\n")); + + return status; + +} + + +NDIS_STATUS +PtDeregisterDevice( + VOID + ) +/*++ + +Routine Description: + + Deregister the ioctl interface. This is called whenever a miniport + instance is halted. When the last miniport instance is halted, we + request NDIS to delete the device object + +Arguments: + + NdisDeviceHandle Handle returned by NdisMRegisterDevice + +Return Value: + + NDIS_STATUS_SUCCESS if everything worked ok + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + DBGPRINT(MUX_LOUD, ("==>PtDeregisterDevice\n")); + + MUX_ACQUIRE_MUTEX(&ControlDeviceMutex); + + ASSERT(MiniportCount > 0); + + --MiniportCount; + + if (0 == MiniportCount) + { + // + // All VELAN miniport instances have been halted. + // Deregister the control device. + // + + if (NdisDeviceHandle != NULL) + { + NdisDeregisterDeviceEx(NdisDeviceHandle); + NdisDeviceHandle = NULL; + } + } + + MUX_RELEASE_MUTEX(&ControlDeviceMutex); + + DBGPRINT(MUX_INFO, ("<== PtDeregisterDevice: %x\n", Status)); + return Status; + +} + diff --git a/network/ndis/mux/driver/60/mux.h b/network/ndis/mux/driver/60/mux.h new file mode 100644 index 00000000..dcdef937 --- /dev/null +++ b/network/ndis/mux/driver/60/mux.h @@ -0,0 +1,963 @@ +/*++ +Copyright (c) 1992-2001 Microsoft Corporation + +Module Name: + + mux.h + +Abstract: + + Data structures, defines and function prototypes for the MUX driver. + +Environment: + + Kernel mode only. + +Revision History: + + +--*/ + +// disable warnings + +#if _MSC_VER >= 1200 + +#pragma warning(push) + +#endif + +#pragma warning(disable:4200) // zero-sized array in struct/union + +#define MUX_MAJOR_NDIS_VERSION 6 +#define MUX_MINOR_NDIS_VERSION 0 + +#define MUX_MAJOR_DRIVER_VERSION 3 +#define MUX_MINOR_DRIVER_VERSION 0 + +#define MUX_PROT_MAJOR_NDIS_VERSION 6 +#define MUX_PROT_MINOR_NDIS_VERSION 0 + +#define MUX_TAG 'SxuM' +#define WAIT_INFINITE 0 + +extern LONG MiniportCount; + +#if DBG +// +// Debug levels: lower values indicate higher urgency +// +#define MUX_EXTRA_LOUD 20 +#define MUX_VERY_LOUD 10 +#define MUX_LOUD 8 +#define MUX_INFO 6 +#define MUX_WARN 4 +#define MUX_ERROR 2 +#define MUX_FATAL 0 + +extern INT muxDebugLevel; + + +#define DBGPRINT(lev, Fmt) \ + { \ + if ((lev) <= muxDebugLevel) \ + { \ + DbgPrint("MUX-IM: "); \ + DbgPrint Fmt; \ + } \ + } +#else + +#define DBGPRINT(lev, Fmt) + +#endif //DBG + + + +#define ETH_IS_LOCALLY_ADMINISTERED(Address) \ + (BOOLEAN)(((PUCHAR)(Address))[0] & ((UCHAR)0x02)) + +// forward declarations +typedef struct _ADAPT ADAPT, *PADAPT; +typedef struct _VELAN VELAN, *PVELAN; +typedef struct _MUX_NDIS_REQUEST MUX_NDIS_REQUEST, *PMUX_NDIS_REQUEST; + + +typedef +VOID +(*PMUX_REQ_COMPLETE_HANDLER) ( + IN PADAPT pAdapt, + IN struct _MUX_NDIS_REQUEST * pMuxRequest, + IN NDIS_STATUS Status + ); + +// This OID specifies the current driver version. +// The high byte is the major version. +// The low byte is the minor version. +#define VELAN_DRIVER_VERSION ((MUX_MAJOR_DRIVER_VERSION << 8) + \ + (MUX_MINOR_DRIVER_VERSION)) + +// media type, we use ethernet, change if necessary +#define VELAN_MEDIA_TYPE NdisMedium802_3 + +// change to your company name instead of using Microsoft +#define VELAN_VENDOR_DESC "Microsoft" + +// Highest byte is the NIC byte plus three vendor bytes, they are normally +// obtained from the NIC +#define VELAN_VENDOR_ID 0x00FFFFFF + +#define VELAN_MAX_MCAST_LIST 32 +#define VELAN_MAX_SEND_PKTS 5 + +#define ETH_MAX_PACKET_SIZE 1514 +#define ETH_MIN_PACKET_SIZE 60 +#define ETH_HEADER_SIZE 14 + + +#define VELAN_SUPPORTED_FILTERS ( \ + NDIS_PACKET_TYPE_DIRECTED | \ + NDIS_PACKET_TYPE_MULTICAST | \ + NDIS_PACKET_TYPE_BROADCAST | \ + NDIS_PACKET_TYPE_PROMISCUOUS | \ + NDIS_PACKET_TYPE_ALL_MULTICAST) + +#define MUX_ADAPTER_PACKET_FILTER \ + NDIS_PACKET_TYPE_PROMISCUOUS + + + +#define MIN_PACKET_POOL_SIZE 255 +#define MAX_PACKET_POOL_SIZE 4096 + +typedef UCHAR MUX_MAC_ADDRESS[6]; + + + +// +// Default values: +// +#define MUX_DEFAULT_LINK_SPEED 100000 // in 100s of bits/sec +#define MUX_DEFAULT_LOOKAHEAD_SIZE 512 + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +DRIVER_DISPATCH PtDispatch; +NTSTATUS +PtDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NDIS_STATUS +PtRegisterDevice( + VOID + ); + +NDIS_STATUS +PtDeregisterDevice( + VOID + ); +// +// Protocol proto-types +// + +PROTOCOL_SET_OPTIONS PtSetOptions; + +PROTOCOL_OPEN_ADAPTER_COMPLETE_EX PtOpenAdapterComplete; + +PROTOCOL_CLOSE_ADAPTER_COMPLETE_EX PtCloseAdapterComplete; + +PROTOCOL_OID_REQUEST_COMPLETE PtRequestComplete; + +PROTOCOL_STATUS_EX PtStatus; + +PROTOCOL_BIND_ADAPTER_EX PtBindAdapter; + +PROTOCOL_UNBIND_ADAPTER_EX PtUnbindAdapter; + +PROTOCOL_NET_PNP_EVENT PtPNPHandler; + +PROTOCOL_RECEIVE_NET_BUFFER_LISTS PtReceiveNBL; + +PROTOCOL_SEND_NET_BUFFER_LISTS_COMPLETE PtSendNBLComplete; + +VOID +PtQueryAdapterInfo( + IN PADAPT pAdapt + ); + + +VOID +PtRequestAdapterSync( + IN PADAPT pAdapt, + IN NDIS_REQUEST_TYPE RequestType, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength + ); + + +VOID +PtRequestAdapterAsync( + IN PADAPT pAdapt, + IN NDIS_REQUEST_TYPE RequestType, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + IN PMUX_REQ_COMPLETE_HANDLER pCallback + ); + +VOID +PtCompleteForwardedRequest( + IN PADAPT pAdapt, + IN PMUX_NDIS_REQUEST pMuxNdisRequest, + IN NDIS_STATUS Status + ); + +VOID +PtPostProcessPnPCapabilities( + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength + ); + +VOID +PtCompleteBlockingRequest( + IN PADAPT pAdapt, + IN PMUX_NDIS_REQUEST pMuxNdisRequest, + IN NDIS_STATUS Status + ); + +VOID +PtDiscardCompletedRequest( + IN PADAPT pAdapt, + IN PMUX_NDIS_REQUEST pMuxNdisRequest, + IN NDIS_STATUS Status + ); + +NDIS_STATUS +PtCreateAndStartVElan( + IN PADAPT pAdapt, + IN PNDIS_STRING pVElanKey + ); + +PVELAN +PtAllocateAndInitializeVElan( + IN PADAPT pAdapt, + IN PNDIS_STRING pVElanKey + ); + +VOID +PtDeallocateVElan( + IN PVELAN pVElan + ); + +VOID +PtStopVElan( + IN PVELAN pVElan + ); + +VOID +PtUnlinkVElanFromAdapter( + IN PVELAN pVElan + ); + +PVELAN +PtFindVElan( + IN PADAPT pAdapt, + IN PNDIS_STRING pElanKey + ); + + +NDIS_STATUS +PtBootStrapVElans( + IN PADAPT pAdapt, + IN PUNICODE_STRING InstanceName OPTIONAL + ); + +VOID +PtReferenceVElan( + IN PVELAN pVElan, + IN PUCHAR String + ); + +ULONG +PtDereferenceVElan( + IN PVELAN pVElan, + IN PUCHAR String + ); + +BOOLEAN +PtReferenceAdapter( + IN PADAPT pAdapt, + IN PUCHAR String + ); + +ULONG +PtDereferenceAdapter( + IN PADAPT pAdapt, + IN PUCHAR String + ); + + +VOID +PtCloseAdapter( + IN PADAPT Adapt + ); + +// +// Miniport proto-types +// + + +MINIPORT_SET_OPTIONS MpSetOptions; + +MINIPORT_INITIALIZE MPInitialize; + +MINIPORT_HALT MPHalt; + +MINIPORT_UNLOAD MPUnload; + +MINIPORT_PAUSE MPPause; + +MINIPORT_RESTART MPRestart; + +MINIPORT_OID_REQUEST MPOidRequest; + +MINIPORT_SEND_NET_BUFFER_LISTS MPSendNetBufferLists; + +MINIPORT_RETURN_NET_BUFFER_LISTS MPReturnNetBufferLists; + +MINIPORT_CANCEL_SEND MPCancelSendNetBufferLists; + +MINIPORT_DEVICE_PNP_EVENT_NOTIFY MPDevicePnPEvent; + +MINIPORT_SHUTDOWN MPAdapterShutdown; + +MINIPORT_CANCEL_OID_REQUEST MPCancelOidRequest; + +NDIS_STATUS +MPQueryInformation( + IN PVELAN pVElan, + IN PNDIS_OID_REQUEST NdisRequest + ); + +NDIS_STATUS +MPSetInformation( + IN PVELAN pVElan, + IN PNDIS_OID_REQUEST NdisRequest + ); + +NDIS_STATUS +MPMethodRequest( + IN PVELAN pVElan, + IN PNDIS_OID_REQUEST NdisRequest + ); + +NDIS_STATUS +MPSetPacketFilter( + IN PVELAN pVElan, + IN ULONG PacketFilter + ); + +NDIS_STATUS +MPSetMulticastList( + IN PVELAN pVElan, + _In_reads_bytes_(InformationBufferLength) IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + OUT PULONG pBytesRead, + OUT PULONG pBytesNeeded + ); + +PUCHAR +MacAddrToString(PVOID In + ); + +VOID +MPGenerateMacAddr( + PVELAN pVElan +); + + +NDIS_STATUS +MPForwardOidRequest( + IN PVELAN pVElan, + IN PNDIS_OID_REQUEST NdisRequest + ); + +// +// Super-structure for NDIS_REQUEST, to allow us to keep context +// about requests sent down to a lower binding. +// +typedef struct _MUX_NDIS_REQUEST +{ + PVELAN pVElan; // Set iff this is a forwarded + // request from a VELAN. + NDIS_STATUS Status; // Completion status + NDIS_EVENT Event; // Used to block for completion. + PMUX_REQ_COMPLETE_HANDLER pCallback; // Called on completion of request + PNDIS_OID_REQUEST OrigRequest; //Request that originated this request + NDIS_OID_REQUEST Request; + ULONG Refcount; // Refcount + BOOLEAN Cancelled; + +} MUX_NDIS_REQUEST, *PMUX_NDIS_REQUEST; + + +typedef enum _MUX_ADAPTER_BINDING_STATE +{ + MuxAdapterBindingPaused, + MuxAdapterBindingPausing, + MuxAdapterBindingRunning +}MUX_ADAPTER_BINDING_STATE, *PMUX_ADAPTER_BINDING_STATE; + +#define MUX_BINDING_ACTIVE 0x00000001 +#define MUX_BINDING_CLOSING 0x00000002 + +// +// The ADAPT object represents a binding to a lower adapter by +// the protocol edge of this driver. Based on the configured +// Upper bindings, zero or more virtual miniport devices (VELANs) +// are created above this binding. +// +typedef struct _ADAPT +{ + // Chain adapters. Access to this is protected by the global lock. + LIST_ENTRY Link; + + // References to this adapter. + ULONG RefCount; + + // Handle to the lower adapter, used in NDIS calls referring + // to this adapter. + NDIS_HANDLE BindingHandle; + + // List of all the virtual ELANs created on this lower binding + LIST_ENTRY VElanList; + + // Length of above list. + ULONG VElanCount; + + // String used to access configuration for this binding. + NDIS_STRING ConfigString; + + // Open Status. Used by bind/halt for Open/Close Adapter status. + NDIS_STATUS Status; + + NDIS_EVENT Event; + + // + // Packet filter set to the underlying adapter. This is + // a combination (union) of filter bits set on all + // attached VELAN miniports. + // + ULONG PacketFilter; + + // Power state of the underlying adapter + NDIS_DEVICE_POWER_STATE PtDevicePowerState; + + // + // NDIS Medium ofr VELAN taken from the miniport below + // + NDIS_MEDIUM Medium ; + + // + // BindParameters passed to protocol giving it information on + // the miniport below + // + NDIS_BIND_PARAMETERS BindParameters; + NDIS_PNP_CAPABILITIES PowerManagementCapabilities; + NDIS_RECEIVE_SCALE_CAPABILITIES RcvScaleCapabilities; + NDIS_LINK_STATE LastIndicatedLinkState; + MUX_ADAPTER_BINDING_STATE BindingState; + + ULONG OutstandingSends; + PNDIS_EVENT PauseEvent; + NDIS_SPIN_LOCK Lock; +#ifndef WIN9X + // + // Read/Write lock: allows multiple readers but only a single + // writer. Used to protect the VELAN list and fields (e.g. packet + // filter) shared on an ADAPT by multiple VELANs. Code that + // needs to traverse the VELAN list safely acquires a READ lock. + // Code that needs to safely modify the VELAN list or shared + // fields acquires a WRITE lock (which also excludes READers). + // + // See macros MUX_ACQUIRE_ADAPT_xxx/MUX_RELEASE_ADAPT_xxx below. + // + // TBD - if we want to support this on Win9X, reimplement this! + // + NDIS_RW_LOCK ReadWriteLock; +#endif // WIN9X + + ULONG OutstandingRequests; + PNDIS_EVENT CloseEvent; + ULONG Flags; +} ADAPT, *PADAPT; + + +// +// VELAN object represents a virtual ELAN instance and its +// corresponding virtual miniport adapter. +// +typedef struct _VELAN +{ + // Link into parent adapter's VELAN list. + LIST_ENTRY Link; + + // link inot global VELAN list + LIST_ENTRY GlobalLink; + + // References to this VELAN. + ULONG RefCount; + + // Parent ADAPT. + PADAPT pAdapt; + + // Copy of BindingHandle from ADAPT. + NDIS_HANDLE BindingHandle; + + // Adapter handle for NDIS up-calls related to this virtual miniport. + NDIS_HANDLE MiniportAdapterHandle; + + // Virtual miniport's power state. + NDIS_DEVICE_POWER_STATE MPDevicePowerState; + + // Has our Halt entry point been called? + BOOLEAN MiniportHalting; + + // Do we need to indicate receive complete? + BOOLEAN IndicateRcvComplete; + + // Do we need to indicate status complete? + BOOLEAN IndicateStatusComplete; + + // Synchronization fields + BOOLEAN MiniportInitPending; + NDIS_EVENT MiniportInitEvent; + + // Uncompleted Sends/Requests to the adapter below. + ULONG OutstandingSends; + + // Count outstanding indications, including received + // packets, passed up to protocols on this VELAN. + ULONG OutstandingReceives; + + // A request block that is used to forward a request presented + // to the virtual miniport, to the lower binding. Since NDIS + // serializes requests to a miniport, we only need one of these + // per VELAN. + // + MUX_NDIS_REQUEST Request; + // Have we queued a request because the lower binding is + // at a low power state? + BOOLEAN QueuedRequest; + + // Have we started to deinitialize this VELAN? + BOOLEAN DeInitializing; + + // configuration + UCHAR PermanentAddress[ETH_LENGTH_OF_ADDRESS]; + UCHAR CurrentAddress[ETH_LENGTH_OF_ADDRESS]; + + NDIS_STRING CfgDeviceName; // used as the unique + // ID for the VELAN + ULONG VElanNumber; // logical Elan number + + + // + // ----- Buffer Management: Header buffers and Protocol buffers ---- + // + + // Some standard miniport parameters (OID values). + ULONG PacketFilter; + ULONG LookAhead; + ULONG64 LinkSpeed; + + ULONG MaxBusySends; + ULONG MaxBusyRecvs; + + // Packet counts + ULONG64 GoodTransmits; + ULONG64 GoodReceives; + ULONG NumTxSinceLastAdjust; + + // Count of transmit errors + ULONG TxAbortExcessCollisions; + ULONG TxLateCollisions; + ULONG TxDmaUnderrun; + ULONG TxLostCRS; + ULONG TxOKButDeferred; + ULONG OneRetry; + ULONG MoreThanOneRetry; + ULONG TotalRetries; + ULONG TransmitFailuresOther; + + // Count of receive errors + ULONG RcvCrcErrors; + ULONG RcvAlignmentErrors; + ULONG RcvResourceErrors; + ULONG RcvDmaOverrunErrors; + ULONG RcvCdtFrames; + ULONG RcvRuntErrors; + ULONG RegNumTcb; + + // Multicast list + MUX_MAC_ADDRESS McastAddrs[VELAN_MAX_MCAST_LIST]; + ULONG McastAddrCount; + + + NDIS_STATUS LastIndicatedStatus; + NDIS_STATUS LatestUnIndicateStatus; + NDIS_SPIN_LOCK Lock; + + // Miniport Pause/Restart functionality + BOOLEAN Paused; + NDIS_SPIN_LOCK PauseLock; + NDIS_LINK_STATE LatestUnIndicateLinkState; + NDIS_LINK_STATE LastIndicatedLinkState; + +#if IEEE_VLAN_SUPPORT + ULONG VlanId; + ULONG RcvFormatErrors; + ULONG RcvVlanIdErrors; + BOOLEAN RestoreLookaheadSize; + NPAGED_LOOKASIDE_LIST TagLookaside; +#endif + + NET_IFINDEX IfIndex; +} VELAN, *PVELAN; + + +#define MUX_ACQUIRE_SPIN_LOCK(_pLock, DispatchLevel) \ + { \ + if (DispatchLevel) \ + { \ + NdisDprAcquireSpinLock(_pLock); \ + } \ + else \ + { \ + NdisAcquireSpinLock(_pLock); \ + } \ + } + +#define MUX_RELEASE_SPIN_LOCK(_pLock, DispatchLevel) \ + { \ + if (DispatchLevel) \ + { \ + NdisDprReleaseSpinLock(_pLock); \ + } \ + else \ + { \ + NdisReleaseSpinLock(_pLock); \ + } \ + } + + + +#if IEEE_VLAN_SUPPORT + +#define TPID 0x0081 +// +// Define tag_header structure +// +typedef struct _VLAN_TAG_HEADER +{ + UCHAR TagInfo[2]; +} VLAN_TAG_HEADER, *PVLAN_TAG_HEADER; + + +// +// Macro definitions for VLAN support +// +#define VLAN_TAG_HEADER_SIZE 4 + +#define VLANID_DEFAULT 0 +#define VLAN_ID_MAX 0xfff +#define VLAN_ID_MIN 0x0 + +#define USER_PRIORITY_MASK 0xe0 +#define CANONICAL_FORMAT_ID_MASK 0x10 +#define HIGH_VLAN_ID_MASK 0x0F + +// +// Get information for tag headre +// +#define GET_CANONICAL_FORMAT_ID_FROM_TAG(_pTagHeader) \ + ((_pTagHeader)->TagInfo[0] & CANONICAL_FORMAT_ID_MASK) + +#define GET_USER_PRIORITY_FROM_TAG(_pTagHeader) \ + ((_pTagHeader)->TagInfo[0] & USER_PRIORITY_MASK) + +#define GET_VLAN_ID_FROM_TAG(_pTagHeader) \ + (ULONG)(((USHORT)((_pTagHeader)->TagInfo[0] & HIGH_VLAN_ID_MASK) << 8) |(USHORT)((_pTagHeader)->TagInfo[1])) + +// +// Clear the tag header struct +// +#define INITIALIZE_TAG_HEADER_TO_ZERO(_pTagHeader) \ +{ \ + (_pTagHeader)->TagInfo[0] = 0; \ + (_pTagHeader)->TagInfo[1] = 0; \ +} + +// +// Set VLAN information to tag header +// Before we called all the set macro, first we need to initialize pTagHeader to be 0 +// +#define SET_CANONICAL_FORMAT_ID_TO_TAG(_pTagHeader, _CanonicalFormatId) \ + (_pTagHeader)->TagInfo[0] |= ((UCHAR)(_CanonicalFormatId) << 4) + +#define SET_USER_PRIORITY_TO_TAG(_pTagHeader, _UserPriority) \ + (_pTagHeader)->TagInfo[0] |= ((UCHAR)(_UserPriority) << 5) + +#define SET_VLAN_ID_TO_TAG(_pTagHeader, _VlanId) \ + { \ + (_pTagHeader)->TagInfo[0] |= (((UCHAR)((_VlanId) >> 8)) & 0x0f); \ + (_pTagHeader)->TagInfo[1] |= (UCHAR)(_VlanId); \ + } + + +// +// Copy tagging information in the indicated frame to per packet info +// +#define COPY_TAG_INFO_FROM_HEADER_TO_PACKET_INFO(_Ieee8021qInfo, _pTagHeader) \ +{ \ + (_Ieee8021qInfo).TagHeader.UserPriority = ((_pTagHeader->TagInfo[0] & USER_PRIORITY_MASK) >> 5); \ + (_Ieee8021qInfo).TagHeader.CanonicalFormatId = ((_pTagHeader->TagInfo[0] & CANONICAL_FORMAT_ID_MASK) >> 4); \ + (_Ieee8021qInfo).TagHeader.VlanId = (((USHORT)(_pTagHeader->TagInfo[0] & HIGH_VLAN_ID_MASK) << 8)| (USHORT)(_pTagHeader->TagInfo[1])); \ +} + +// +// Flags used by VELAN supports +// +#define MUX_RETREAT_DATA 0x00000001 + +// +// Flags used by VELAN on Receive code path +// +#define MUX_ADVANCE_DATA 0x00000001 + +// +// Every NBL that is indicated up to a protocol needs to advance the buffer +// in case the VLAN tag is present. It should be restored before returning the +// packet to the miniport. This structure is used for that purpose +// +typedef struct _RECV_NBL_ENTRY +{ + ULONG Flags; + VLAN_TAG_HEADER TagHeader; + + DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) + UCHAR Pad[]; +} RECV_NBL_ENTRY, *PRECV_NBL_ENTRY; + +// +// This structure is used to save context in the NET_BUFFER on the send path, +// if the ethernet header and VLAN tag is allocated by MUX +// +typedef struct _IM_SEND_NB_ENTRY +{ + PMDL CurrentMdl; + PMDL PrevMdl; + ULONG CurrentMdlOffset; + PNET_BUFFER NextNetBuffer; +} IM_SEND_NB_ENTRY, *PIM_SEND_NB_ENTRY; + +#endif //IEEE_VLAN_SUPPORT + +typedef struct _IM_NBL_ENTRY +{ + NDIS_HANDLE PreviousSourceHandle; + PVELAN pVElan; +#if IEEE_VLAN_SUPPORT + ULONG Flags; + PNET_BUFFER MdlAllocatedNetBuffers; +#endif + DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) + UCHAR Pad[]; +} IM_NBL_ENTRY, *PIM_NBL_ENTRY; + +// +// Macro definitions for others. +// + +// +// Is a given power state a low-power state? +// +#define MUX_IS_LOW_POWER_STATE(_PwrState) \ + ((_PwrState) > NdisDeviceStateD0) + +#define MUX_INIT_ADAPT_RW_LOCK(_pAdapt) \ + NdisInitializeReadWriteLock(&(_pAdapt)->ReadWriteLock) + + +#define MUX_ACQUIRE_ADAPT_READ_LOCK(_pAdapt, _pLockState) \ + NdisAcquireReadWriteLock(&(_pAdapt)->ReadWriteLock, \ + FALSE, \ + _pLockState) + +#define MUX_RELEASE_ADAPT_READ_LOCK(_pAdapt, _pLockState) \ + NdisReleaseReadWriteLock(&(_pAdapt)->ReadWriteLock, \ + _pLockState) + +#define MUX_ACQUIRE_ADAPT_WRITE_LOCK(_pAdapt, _pLockState) \ + NdisAcquireReadWriteLock(&(_pAdapt)->ReadWriteLock, \ + TRUE, \ + _pLockState) + +#define MUX_RELEASE_ADAPT_WRITE_LOCK(_pAdapt, _pLockState) \ + NdisReleaseReadWriteLock(&(_pAdapt)->ReadWriteLock, \ + _pLockState) + +#define MUX_INCR_PENDING_RECEIVES(_pVElan) \ + NdisInterlockedIncrement((PLONG)&pVElan->OutstandingReceives) + +#define MUX_DECR_PENDING_RECEIVES(_pVElan) \ + NdisInterlockedDecrement((PLONG)&pVElan->OutstandingReceives) + +#define MUX_INCR_PENDING_SENDS(_pVElan) \ + NdisInterlockedIncrement((PLONG)&pVElan->OutstandingSends) + +#define MUX_DECR_PENDING_SENDS(_pVElan) \ + NdisInterlockedDecrement((PLONG)&pVElan->OutstandingSends) + +#define MUX_DECR_MULTIPLE_PENDING_RECEIVES(_pVElan, _NumReceives) \ + InterlockedExchangeAdd((PLONG)&_pVElan->OutstandingReceives, \ + 0 - (LONG) _NumReceives) + + + + +#define MUX_INCR_STATISTICS(_pUlongVal) \ + NdisInterlockedIncrement((PLONG)_pUlongVal) + +#define MUX_INCR_STATISTICS64(_pUlong64Val) \ +{ \ + PLARGE_INTEGER _pLargeInt = (PLARGE_INTEGER)_pUlong64Val;\ + if (NdisInterlockedIncrement((PLONG)&_pLargeInt->LowPart) == 0) \ + { \ + NdisInterlockedIncrement(&_pLargeInt->HighPart); \ + } \ +} + +#define ASSERT_AT_PASSIVE() \ + ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL) + +#define ASSERT_AT_DISPATCH() \ + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL) + + + +// +// Simple Mutual Exclusion constructs used in preference to +// using KeXXX calls since we don't have Mutex calls in NDIS. +// These can only be called at passive IRQL. +// + +typedef struct _MUX_MUTEX +{ + NDIS_MUTEX Mutex; + ULONG ModuleAndLine; // useful for debugging + +} MUX_MUTEX, *PMUX_MUTEX; + +#define MUX_INIT_MUTEX(_pMutex) \ +{ \ + NDIS_INIT_MUTEX(&(_pMutex)->Mutex); \ + (_pMutex)->ModuleAndLine = 0; \ +} + +#define MUX_ACQUIRE_MUTEX(_pMutex) \ +{ \ + NDIS_WAIT_FOR_MUTEX(&(_pMutex)->Mutex); \ + (_pMutex)->ModuleAndLine = (MODULE_NUMBER << 16) | __LINE__;\ +} + +#define MUX_RELEASE_MUTEX(_pMutex) \ +{ \ + (_pMutex)->ModuleAndLine = 0; \ + NDIS_RELEASE_MUTEX(&(_pMutex)->Mutex); \ +} + + +// +// Global variables +// +extern NDIS_HANDLE ProtHandle, DriverHandle; +extern NDIS_MEDIUM MediumArray[1]; +extern NDIS_SPIN_LOCK GlobalLock; +extern MUX_MUTEX GlobalMutex; +extern LIST_ENTRY AdapterList; +extern LIST_ENTRY VElanList; +extern ULONG NextVElanNumber; + +// +// Module numbers for debugging +// +#define MODULE_MUX 'X' +#define MODULE_PROT 'P' +#define MODULE_MINI 'M' +#define MODULE_MUX_TEST 'T' + + +#ifdef IEEE_VLAN_SUPPORT + +PMDL +MuxAllocateMdl( + IN OUT PULONG BufferSize + ); + +NDIS_STATUS +MPHandleSendTaggingNB( + IN PVELAN pVElan, + IN PNET_BUFFER_LIST NetBufferList + ); + +VOID +MPRestoreSendNBL( + IN PVELAN pVElan, + IN PNET_BUFFER_LIST NetBufferList, + IN PNET_BUFFER LastNetBuffer, + IN PNET_BUFFER MdlAllocatedNetBuffers + ); + +NDIS_STATUS +PtHandleReceiveTaggingNB( + IN PVELAN pVElan, + IN PNET_BUFFER_LIST NetBufferList, + IN PNDIS_NET_BUFFER_LIST_8021Q_INFO NdisPacket8021qInfo + ); + +NDIS_STATUS +PtStripVlanTagNB( + IN PNET_BUFFER_LIST NetBufferList, + OUT PNDIS_NET_BUFFER_LIST_8021Q_INFO NdisPacket8021qInfo, + OUT PRECV_NBL_ENTRY RecvContext + ); + +NDIS_STATUS +PtRestoreReceiveNBL( + IN PNET_BUFFER_LIST NetBufferList + ); + +#define MuxRecognizedVlanId(_pVElan, _VlanId) ((_pVElan)->VlanId == (_VlanId)) + +#endif + + + +#if _MSC_VER >= 1200 + +#pragma warning(pop) + +#else + +#endif + + + diff --git a/network/ndis/mux/driver/60/mux.htm b/network/ndis/mux/driver/60/mux.htm new file mode 100644 index 00000000..8a590061 --- /dev/null +++ b/network/ndis/mux/driver/60/mux.htm @@ -0,0 +1,617 @@ + + + + + + + + +MUX Intermediate Miniport Driver Help + + + + + + + + + + + + +
+ +

MUX.SYS - Sample NDIS +MUX Intermediate Driver

+ +

SUMMARY

+ +

MUX Intermediate Miniport Driver

+ +

The MUX Intermediate +Miniport (IM) driver is an NDIS 6.0 driver that demonstrates the operation of +an “N:1” MUX driver, i.e. one which creates multiple +virtual network devices on top of a single lower adapter. Protocols bind to +these virtual adapters as if they are real adapters. Examples of Intermediate Miniport drivers that can use this +framework are Virtual LAN (VLAN) drivers.

+ +

This sample implements +IEEE 802.1Q VLAN tagging, which is enabled by changing the default VLAN ID to a +non-zero valid value (see “Configuring VLANs” below).

+ +

Operation

+ +

The driver binds to +Ethernet (NdisMedium802_3) adapters as a protocol, and exposes one or more +virtual Ethernet devices over each lower adapter, based on its configuration. +The term “VELAN” is used to denote a Virtual Ethernet LAN adapter implemented +by this driver.

+ +

When it binds to a lower +adapter, MUX reads the standard “UpperBind” key to obtain a list of VELANs +configured over this adapter. For each such VELAN, it calls NdisIMInitializeDeviceInstanceEx() +to instantiate the NDIS miniport for the VELAN. NDIS then calls the driver’s +MiniportInitialize (MPInitialize) routine to start the VELAN miniport.

+ +

The MUX driver supports +configuring the MAC address for each VELAN miniport using the standard +“NetworkAddress” key that it reads from its MiniportInitialize routine. If this +is not configured, it computes a “locally significant” MAC address for the +VELAN using the MAC address of the lower adapter. The MUX driver sets its lower +adapter to promiscuous mode in order to be able to receive frames directed to +any of the VELAN MAC addresses. However it does implement +packet-filtering (and multicast address filtering) logic for all its VELAN +miniports so that it only passes up relevant frames on each VELAN. This +aspect of the driver may be modified if, for example, your driver design uses +the same MAC address as that of the lower adapter on all VELANs. With such a +modification, it is not required to set the lower adapter to promiscuous mode +and incur the costs of receiving all packets on the network.

+ +

It supports dynamic +addition and deletion of VELANs in conjunction with its +notify object (related sample). If a VELAN is deleted, the virtual device +corresponding to the VELAN is stopped and removed, which in turn results in +NDIS halting the miniport instance for the VELAN (see MPHalt). If a +VELAN is added, NDIS sends a global reconfiguration event to the protocol edge +of this driver. The handler function for this event, PtPNPHandler, goes through +all lower adapters to see if any new VELANs have been added, i.e. if any of the +“UpperBind” keys have been modified.

+ +

Since the driver +implements a virtual device, it does not simply pass through most NDIS +queries/sets. It keeps its own device view that is reflected in its responses +to queries/sets. However it does pass through queries/sets for certain OIDs +that are best handled by the lower adapter driver.

+ +

The driver supports Power +Management in the sense that it allows Wake-On-LAN and related functionality, +if supported by the lower adapter, to continue to function. It does so by +appropriately forwarding OID_PNP_XXX queries/sets to the lower adapter.

+ +

IEEE +802.1Q VLAN Operation

+ +

The driver supports +configuring a VLAN ID on each VELAN. It then inserts a tag header containing +this VLAN ID on all outgoing frames. For incoming frames that contain a tag +header, it verifies that a matching VLAN ID is present before indicating it up +to protocols. It removes the tag header, if present, from all indicated frames. +In all cases, received frames that do not contain tag headers are always handed +up to protocols.

+ +

With the default +configured VLAN ID of zero, the driver does not insert tag header information +on sent packets, except for sent packets that contain non-zero Ieee8021QInfo +per-packet information, for which the driver does insert corresponding tag +headers. Receive-side filtering on VLAN ID is enabled only with a non-zero +configured VLAN ID, in which case only received frames containing a matching +VLAN ID are passed up. With the default configured VLAN ID of zero, the driver +does not check the VLAN ID on received frames.

+ +

BUILDING THE SAMPLE

+ +

Run the build +command from this directory to build the sample—it creates the binary mux.sys. +To disable IEEE VLAN support, comment out the following line in the sources +file before building:

+ +

C_DEFINES=$(C_DEFINES) +–DIEEE_VLAN_SUPPORT=1

+ +

To install this driver on +Windows® codename Whistler, use the MUX sample notification object and INFs, +also found in this DDK.

+ +

INSTALLING THE SAMPLE

+ +

MUX is installed as a +protocol (called “Sample Mux-IM Protocol Driver” in +the supplied INFs/notification object). To install, follow the steps below.

+ +

Prepare a floppy disk (or +installation directory) that contains these files: muxp.inf, +mux_mp.inf, mux.sys and mux.dll (notification object +DLL, built in this DDK at network\ndis\mux\notifyob).

+ +

On the desktop, +right-click the My Network Places icon and choose Properties.

+ +

Right-click on the +relevant Local Area Connection icon and choose Properties.

+ +

Click Install, +then Protocol, then Add, then Have +Disk.

+ +

Browse to the +drive/directory containing the files listed above. Click OK. This should +show “Sample Mux-IM Protocol Driver” in a list of +Network Protocols. Highlight this and click OK. This should install the +MUX driver.

+ +

Click OK or Yes each time the system prompts with a warning +regarding installation of unsigned files. This is necessary because binaries +generated via the DDK build environment are not signed.

+ +

Two .INF files are needed +rather than one because MUX is installed both as a protocol and a miniport.

+ +

Configuring +VLANs

+ +

The VLAN ID for each +VELAN (virtual miniport) can be configured as follows. Right-click on the +virtual miniport Local Area Connection icon and choose Properties. Click on the Configure +button to bring up the Device Manager UI for the virtual device. Select the Advanced property sheet – this should +contain a “VLAN ID” parameter that is configurable to the desired VLAN ID. +Choosing a value of 0 (zero) disables receive-side filtering based on VLAN ID.

+ +

CODE TOUR

+ +

File Manifest

+ +
File           Description
 
Makefile       Used during compilation to create the object and sys files
Miniport.c     Miniport related routines for the MUX driver
Mux.c          DriverEntry routine and any routines common to the MUX miniport and protocol 
Mux.h          Prototypes of all functions and data structures used by the MUX driver
Mux.htm        Documentation for the MUX driver (this file)
Mux.rc         Resource file for the MUX driver
Muxp.inf       Installation INF for the service (protocol side installation)
Mux_mp.inf     Installation INF for the miniport (virtual device installation)
Precomp.h      Precompile header file
Protocol.c     Protocol related routines for the MUX driver
Sources        List of source files that are compiled and linked to create the MUX driver. This can be modified to create binaries that operate on previous Windows versions (e.g. Windows 2000).
+ +

Programming Tour

+ +

When it loads, i.e. from its DriverEntry +function, the MUX driver registers as an Intermediate miniport driver and as a +protocol, in that order.

+ +

Binding and VELAN Creation

+ +

NDIS calls MUX’s BindAdapter +function, PtBindAdapter, for each underlying NDIS adapter to which it is +configured to bind. This function allocates an ADAPT structure to represent the +lower adapter, and calls NdisOpenAdapter to set up a binding to it. In the +context of BindAdapterHandler, after successfully opening a binding to the +underlying adapter, the driver queries the reserved keyword +"UpperBindings" to get a list of device names for the virtual +adapters that this particular binding is to expose – see PtBootStrapVElans +for more details. Note that the MUX driver does not create bindings (i.e. call +NdisOpenAdapter) from any context other than its BindAdapter function – this is +recommended behavior for all drivers of this type.

+ +

For each device name specified in +the “UpperBindings” key, the MUX driver allocates a VELAN data structure to +represent the virtual miniport, calls NdisIMInitializeDeviceInstanceEx. In +response, NDIS eventually calls the MUX miniport’s MiniportInitialize entry +point, MPInitialize, for each VELAN. After MPInitialize +successfully returns, NDIS takes care of getting upper-layer protocols to bind +to the newly created virtual adapter(s).

+ +

Unbinding and Halting

+ +

NDIS calls MUX’s UnbindAdapter +handler, PtUnbindAdapter, to request it to unbind from a lower adapter. +In processing this, MUX calls NdisIMDeInitializeDeviceInstance for each VELAN +instantiated on the indicated adapter – see PtStopVElan for details. +This call results in NDIS first unbinding any protocols bound to the indicated +VELAN, and then calling the MiniportHalt routine, MPHalt, for that +VELAN. MPHalt waits for any outstanding receives/sends on the VELAN to +finish before unlinking the VELAN from the ADAPT.

+ +

PtUnbindAdapter itself blocks until all VELANs +associated with the ADAPT structure have been unlinked from it. This is to make +sure that no thread running in the context of a miniport-edge entry point for a +VELAN will ever access an invalid lower binding handle. Once all VELANs have +been unlinked, PtUnbindAdapter closes the lower binding by calling +NdisCloseAdapter. Note that the MUX driver does not close its lower binding +from any context other than its UnbindAdapter function – this is recommended +behavior for all drivers of this type.

+ +

MPHalt may also be called if the VELAN +device is disabled, e.g. from the Network Connections Folder. There is no +special code within MPHalt to handle this condition. However, PtUnbindAdapter +takes care to not attempt to deinitialize a VELAN miniport (via +NdisIMDeInitializeDeviceInstance) that has already been halted.

+ +

Handling Queries

+ +

MPRequest is the MUX driver’s function that +handles queries for OID values on VELAN miniports. Most of the “Ethernet” type +information for the virtual miniport is stored in the VELAN structure itself, +and the driver returns information from this structure. The queries that are +forwarded are OID_GEN_MEDIA_CONNECT_STATUS, OID_PNP_CAPABILITIES and +OID_PNP_WAKE_UP_PATTERN_LIST. See “Handling Power Management” below for more +information about the latter two OIDs.

+ +

Handling Sets

+ +

MPRequest handles setting OID values on +VELAN miniports. Data management OIDs handled by the MUX driver are +OID_802_3_MULTICAST_LIST and OID_GEN_CURRENT_PACKET_FILTER. The multicast list +is handled entirely within the MUX driver – it just stores the set of multicast +addresses in the VELAN structure, for reference during receive-side data +processing. The packet filter is handled in a different way – the MUX driver +combines the packet filter settings (bitwise OR) of all VELANs associated with +the same lower adapter. If the combined packet filter is non-zero, MUX sends a +Set request with a value of NDIS_PACKET_TYPE_PROMISCUOUS for +OID_GEN_CURRENT_PACKET_FILTER to start receives on the lower adapter. If the +combined packet filter is zero, MUX sets the lower adapter’s packet filter to 0 +(turns off all receives if there aren’t any interested protocols).

+ +

Note that setting the lower +adapter to promiscuous mode is only done here in order to be able to receive unicast frames directed to multiple MAC addresses. If, for +example, all VELANs are assigned the same MAC address (which is identical to +the address of the lower adapter), then the MUX driver should only pass down +the combined (bitwise OR) setting of packet filter settings of all VELANs.

+ +

Some power management OIDs are +forwarded to the lower miniport. See “Handling Power Management” below for +details.

+ +

Sending Data

+ +

Data sent down on a VELAN miniport +is forwarded to the lower adapter. The MUX driver itself does not generate any +data of its own. The MUX driver clones a NET_BUFFER_LIST for each NetBufferList passed to its MPSendNetBufferLists +function, and saves a pointer to the original NET_BUFFER_LIST in the reserved +area of the NET_BUFFER_LIST structure. When the lower adapter completes the +send (PtSendNBLComplete), MUX picks up the +original packet and calls NdisMSendNetBufferListsComplete +to complete the original send request.

+ +

If a non-zero VLAN ID is +configured for the VELAN, and/or the packet has non-zero Ieee8021QInfo +per-packet information, then the MUX driver inserts an NDIS buffer containing a +tag header to the front of the packet before sending it down – see function MPHandleSendTagging for details.

+ +

Receiving Data

+ +

Data received from a lower adapter +is indicated up on zero or more VELANs. The PtReceiveNBL +function is called for each NetBufferList received +from the lower adapter. The received data is checked for matches with the +packet filter and multicast list for each VELAN associated with the adapter +(see PtMatchPacketToVElan). Whenever a match is found, a new NET_BUFFER_LIST +is allocated and set to point to the received data. A pointer to the original +received NET_BUFFER_LIST (if any) is also stored in the new NET_BUFFER_LIST’s +reserved area. This packet is indicated up via NdisMIndicateReceiveNetBufferLists +to all interested protocols on that VELAN.

+ +

The driver’s MPReturnNetBufferLists +function is called either by NDIS or by MUX itself when protocols are done with +a received NET_BUFFER_LIST. This function returns the original NET_BUFFER_LIST +indicated by the lower driver, if any, by calling NdisReturnNetBufferLists.

+ +

The driver indicates up received +frames that do not have an IEEE 802.1Q tag header in them – see function PtHandleRcvTagging. It always strips off +tag headers, if present, on received frames. If a non-zero VLAN ID is +configured, then it checks received frames that contain tag headers for +matching VLAN Ids – only matching frames are indicated up to protocols. Any +VLAN/priority information present in incoming frames is copied to per-packet +information fields of indicated NET_BUFFER_LIST structures.

+ +

Status Indications

+ +

The only status indications that +are forwarded up by MUX are media connect status indications. See PtStatus +for more details.

+ +

Handling Power Management

+ +

During initialization (MPInitialize), +the MUX miniport sets the attribute NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND in +its call to NdisMSetMiniportAttributes. When the MUX +miniport is requested to report its Plug and Play capabilities +(OID_PNP_CAPABILITIES), the MUX miniport forwards the request to the underlying +miniport. If this request succeeds, then the MUX miniport overwrites the +following fields before successfully completing the original request:

+ +

NDIS_DEVICE_POWER_STATE          MinMagicPacketWakeUp = +NdisDeviceStateUnspecified;

+ +

NDIS_DEVICE_POWER_STATE          MinPatternWakeUp= +NdisDeviceStateUnspecified;

+ +

NDIS_DEVICE_POWER_STATE          MinLinkChangeWakeUp=NdisDeviceStateUnspecified

+ +

See PtPostProcessPnPCapabilities +for details.

+ +

OID_PNP_SET_POWER and +OID_PNP_QUERY_POWER are not passed to the lower adapter, since the lower layer +miniport will receive independent requests from NDIS.

+ +

NDIS calls the MUX driver’s +ProtocolPnPEvent function (PtPNPHandler) whenever the underlying adapter +is transitioned to a different power state. If the underlying adapter is +transitioning to a low power state, the driver waits for all outstanding sends +and requests to complete.

+ +

Queries/sets received on a VELAN +miniport that are to be forwarded to the underlying adapter are queued on the +VELAN if the underlying adapter is at a low power state. These are picked up +for processing on receiving a notification that the underlying adapter is back +to a powered-up state.

+ +

Handling Global Reconfiguration

+ +

All modifications to VELAN +configuration are accompanied by PnP reconfigure notifications, i.e. +NetEventReconfigure events passed to the MUX’s PnPEventHandler, PtPNPHandler. +This driver takes a broad approach to handling reconfiguration, which is to +simply re-examine all the “UpperBindings” keys for all currently bound +adapters, and start off VELANs for any that do not exist – see PtBootStrapVElans +for details.

+ +

Canceling Sends: MUX propagates send +cancellations from protocols above it to lower miniports.

+ +

 

+ +

Top of page

+ + + + + +
+

 

+
+ +

© 1999 Microsoft +Corporation

+ +
+ + + + diff --git a/network/ndis/mux/driver/60/mux.rc b/network/ndis/mux/driver/60/mux.rc new file mode 100644 index 00000000..4d2da7e5 --- /dev/null +++ b/network/ndis/mux/driver/60/mux.rc @@ -0,0 +1,50 @@ +#include +#include + +/*-----------------------------------------------*/ +/* the following lines are specific to this file */ +/*-----------------------------------------------*/ + +/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR + * and VER_INTERNALNAME_STR must be defined before including COMMON.VER + * The strings don't need a '\0', since common.ver has them. + */ +#define VER_FILETYPE VFT_DRV +/* possible values: VFT_UNKNOWN + VFT_APP + VFT_DLL + VFT_DRV + VFT_FONT + VFT_VXD + VFT_STATIC_LIB +*/ +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +/* possible values VFT2_UNKNOWN + VFT2_DRV_PRINTER + VFT2_DRV_KEYBOARD + VFT2_DRV_LANGUAGE + VFT2_DRV_DISPLAY + VFT2_DRV_MOUSE + VFT2_DRV_NETWORK + VFT2_DRV_SYSTEM + VFT2_DRV_INSTALLABLE + VFT2_DRV_SOUND + VFT2_DRV_COMM +*/ + +#define VER_FILEVERSION 0xFF,0xFFFF,00,0000 +#define VER_FILEVERSION_STR "0xFF.0xFFFF.00.0000" + +#undef VER_PRODUCTVERSION +#define VER_PRODUCTVERSION VER_FILEVERSION + +#undef VER_PRODUCTVERSION_STR +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + +#define VER_FILEDESCRIPTION_STR "Sample NDIS 6.0 Intermediate Miniport Driver" +#define VER_INTERNALNAME_STR "MUX.SYS" +#define VER_ORIGINALFILENAME_STR "MUX.SYS" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/network/ndis/mux/driver/60/mux_mp.inf b/network/ndis/mux/driver/60/mux_mp.inf new file mode 100644 index 00000000..5d4d8c2d --- /dev/null +++ b/network/ndis/mux/driver/60/mux_mp.inf @@ -0,0 +1,69 @@ +; -- MUXMP.INF -- +; +; Sample MUX INF file +; +; Copyright (c) 1993-2004, Microsoft Corporation + +[Version] +Signature = "$Windows NT$" +Class = Net +ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318} +Provider = %Msft% +DriverVer =10/01/2002,6.0.5019.0 + +[ControlFlags] +ExcludeFromSelect = MS_MUXMP + +[Manufacturer] +%Msft% = MSFT,NTx86,NTia64,NTamd64 + +[MSFT.NTx86] +%MUXMP_Desc% = MUXMP.ndi, MS_MUXMP + +[MSFT.NTia64] +%MUXMP_Desc% = MUXMP.ndi, MS_MUXMP + +[MSFT.NTamd64] +%MUXMP_Desc% = MUXMP.ndi, MS_MUXMP + +[MUXMP.ndi] +AddReg = MUXMP.ndi.AddReg +Characteristics = 0xA1 ;NCF_NOT_USER_REMOVABLE | NCF_VIRTUAL | NCF_HAS_UI +*IfType = 6 ; IF_TYPE_ETHERNET_CSMACD +*MediaType = 0 ; NdisMedium802_3 +*PhysicalMediaType = 0 ; NdisPhysicalMediumUnspecified + + +[MUXMP.ndi.AddReg] +HKR, Ndi, Service, 0, MUXMP +HKR, Ndi, HelpText, 0, "%MUXMP_HELP%" +HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" +HKR, Ndi\Interfaces, LowerRange, 0, "ethernet" + +HKR, Ndi\params\VlanID, ParamDesc, 0, %VlanID% +HKR, Ndi\params\VlanID, default, 0, "0" +HKR, Ndi\params\VlanID, type, 0, "long" +HKR, Ndi\params\VlanID, min, 0, "0" +HKR, Ndi\params\VlanID, max, 0, "4095" +HKR, Ndi\params\VlanID, step, 0, "1" +HKR, Ndi\params\VlanID, ParamDesc, 0, %VlanID% + +[MUXMP.ndi.Services] +AddService = MUXMP, 2, MUXMP.AddService + +[MUXMP.AddService] +DisplayName = %MUXMP_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\mux.sys +Description = %MUXMP_Desc% + + +[Strings] +Msft = "Your Company" +MUXMP_Desc = "Sample MUX-IM Virtual Miniport Driver" +MUXMP_HELP = "Sample MUX-IM Virtual Miniport Instance" +VlanID = "VLAN ID" + + diff --git a/network/ndis/mux/driver/60/muxp.inf b/network/ndis/mux/driver/60/muxp.inf new file mode 100644 index 00000000..eeeb6f8a --- /dev/null +++ b/network/ndis/mux/driver/60/muxp.inf @@ -0,0 +1,100 @@ +; -- MUXP.INF -- +; +; Sample MUX INF file +; +; Copyright (c) 1993-2004, Microsoft Corporation + +[Version] +Signature = "$Windows NT$" +Class = NetTrans +ClassGUID = {4D36E975-E325-11CE-BFC1-08002BE10318} +Provider = %Msft% +DriverVer =10/01/2002,6.0.5019.0 + +[Manufacturer] +%Msft% = MSFT,NTx86,NTia64,NTamd64 + +[MSFT.NTx86] +%MUXP_Desc% = MUXP.ndi, MS_MUXP + +[MSFT.NTia64] +%MUXP_Desc% = MUXP.ndi, MS_MUXP + +[MSFT.NTamd64] +%MUXP_Desc% = MUXP.ndi, MS_MUXP + +; Note: +; 1. The NCF_* flags are defined in netcfgx.h +; 2. NCF_HAS_UI (0x80) is required for custom property pages. +; + +[MUXP.ndi] +AddReg = MUXP.ndi.AddReg +Characteristics = 0x80 ; NCF_HAS_UI +copyfiles = MUXP.CopyFiles.DLL, MUXP.CopyFiles.sys +CopyInf = mux_mp.inf + +[SourceDisksNames] +1=%DiskDescription%,"",, + +[SourceDisksFiles] +mux.sys = 1 +mux.dll = 1 + + +[DestinationDirs] +DefaultDestDir = 12 +MUXP.CopyFiles.DLL = 11 ; %windir%\System32 +MUXP.CopyFiles.Sys = 12 ; %windir%\System32\drivers + +[MUXP.CopyFiles.Sys] +mux.sys,,,2 + +[MUXP.CopyFiles.DLL] +mux.dll,,,2 + + +;----------------------------------------------------------------------- +; Install parameters +; + +[MUXP.ndi.AddReg] +HKR, Ndi, ClsID, 0, {ee6cd6fc-a32b-4a26-8a1c-dbc97988ad7a} +HKR, Ndi, ComponentDll, , mux.dll +HKR, Ndi, HelpText, 0, "%MUXP_HELP%" +HKR, Ndi, Service, 0, "MUXP" +HKR, Ndi\Interfaces, UpperRange, 0, "noupper" +HKR, Ndi\Interfaces, LowerRange, 0, "ndis5" + + +[MUXP.ndi.Services] +AddService = MUXP, , MUXP.AddService, MUXP.AddEventLog + +[MUXP.AddService] +DisplayName = %MUXP_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\mux.sys +Description = %MUXP_Desc% + +[MUXP.AddEventLog] +AddReg = MUXP.AddEventLog.AddReg + +[MUXP.AddEventLog.AddReg] +HKR, , EventMessageFile, 0x20000, "%%SystemRoot%%\System32\netevent.dll" +HKR, , TypesSupported, 0x10001, 7 + +[MUXP.ndi.Remove.Services] +DelService = MUXP + +[MUXP.ndi.Remove] +DelFiles = MUXP.CopyFiles.DLL, MUXP.CopyFiles.sys + +[Strings] +Msft = "Your Company" +MUXP_Desc = "Sample Mux-IM Protocol Driver" +MUXP_HELP = "Sample Mux-IM Protocol" +DiskDescription = "Microsoft MUX Sample Driver Disk" + + diff --git a/network/ndis/mux/driver/60/novlan/mux.vcxproj b/network/ndis/mux/driver/60/novlan/mux.vcxproj new file mode 100644 index 00000000..fdc926c1 --- /dev/null +++ b/network/ndis/mux/driver/60/novlan/mux.vcxproj @@ -0,0 +1,287 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {3316ED07-27E3-4AE6-BE12-E8A5F9ADE789} + $(MSBuildProjectName) + Debug + Win32 + {D85A6A77-174C-4C80-8646-06E04ECCBAE8} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + mux + + + mux + + + mux + + + mux + + + + %(AdditionalOptions) /GS + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + + + + + %(AdditionalOptions) /GS + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + + + + + %(AdditionalOptions) /GS + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + + + + + %(AdditionalOptions) /GS + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/mux/driver/60/novlan/mux.vcxproj.Filters b/network/ndis/mux/driver/60/novlan/mux.vcxproj.Filters new file mode 100644 index 00000000..3a8f8458 --- /dev/null +++ b/network/ndis/mux/driver/60/novlan/mux.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {DFD5078D-1185-4EEB-97B6-950B984EBE1A} + + + h;hpp;hxx;hm;inl;inc;xsd + {4B2DF463-FC38-42F8-A082-E219782CD292} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {80BDAADE-F802-425A-A59E-5A93D4FA068E} + + + inf;inv;inx;mof;mc; + {D133C63A-21C1-48F5-AFC1-F852277E2B49} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/mux/driver/60/novlan/precompsrc.c b/network/ndis/mux/driver/60/novlan/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/network/ndis/mux/driver/60/novlan/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/network/ndis/mux/driver/60/precomp.h b/network/ndis/mux/driver/60/precomp.h new file mode 100644 index 00000000..e7bea3a3 --- /dev/null +++ b/network/ndis/mux/driver/60/precomp.h @@ -0,0 +1,14 @@ +#pragma warning(disable:4214) // bit field types other than int + +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4115) // named type definition in parentheses +#pragma warning(disable:4127) // conditional expression is constant +#pragma warning(disable:4054) // cast of function pointer to PVOID +#pragma warning(disable:4244) // conversion from 'int' to 'BOOLEAN', possible loss of data + + +#include +#include "mux.h" +#include "public.h" + + diff --git a/network/ndis/mux/driver/60/protocol.c b/network/ndis/mux/driver/60/protocol.c new file mode 100644 index 00000000..908e4de2 --- /dev/null +++ b/network/ndis/mux/driver/60/protocol.c @@ -0,0 +1,3353 @@ +/*++ +Copyright(c) 1992-2000 Microsoft Corporation + +Module Name: + + protocol.c + +Abstract: + + NDIS Protocol Entry points and utility functions for the NDIS + MUX Intermediate Miniport sample. + + The protocol edge binds to Ethernet (NdisMedium802_3) adapters, + and initiates creation of zero or more Virtual Ethernet LAN (VELAN) + miniport instances by calling NdisIMInitializeDeviceInstanceEx once + for each VELAN configured over a lower binding. + +Environment: + + Kernel mode. + +Revision History: + + +--*/ + + +#include "precomp.h" +#pragma hdrstop + + +#define MODULE_NUMBER MODULE_PROT + + +NDIS_STATUS +PtBindAdapter( + IN NDIS_HANDLE ProtocolDriverContext, + IN NDIS_HANDLE BindContext, + IN PNDIS_BIND_PARAMETERS BindParameters + ) +/*++ + +Routine Description: + + Called by NDIS to bind to a miniport below. This routine + creates a binding by calling NdisOpenAdapterEx, and then + initiates creation of all configured VELANs on this binding. + +Arguments: + ProtocolDriverContext A pointer to the driver context + BindContext A pointer to the bind context + BindParameters Pointing to related information about this new binding. + +Return Value: + + Return Status is set to NDIS_STATUS_SUCCESS if no failure occurred + while handling this call, otherwise an error code. + +--*/ +{ + PADAPT pAdapt = NULL; + UINT MediumIndex = 0; + PNDIS_STRING pConfigString; + ULONG Length; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NDIS_OPEN_PARAMETERS OpenParameters; + + UNREFERENCED_PARAMETER(ProtocolDriverContext); + UNREFERENCED_PARAMETER(BindContext); + + pConfigString = (PNDIS_STRING)BindParameters->ProtocolSection; + + DBGPRINT(MUX_LOUD, ("==> Protocol BindAdapter: %ws\n", pConfigString->Buffer)); + + do + { + if (BindParameters->Header.Type != NDIS_OBJECT_TYPE_BIND_PARAMETERS || + BindParameters->Header.Revision != NDIS_BIND_PARAMETERS_REVISION_1) + { + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + // + // Allocate memory for Adapter struct plus the config + // string with two extra WCHARs for NULL termination. + // + Length = sizeof(ADAPT) + + pConfigString->MaximumLength + sizeof(WCHAR); + pAdapt = NdisAllocateMemoryWithTagPriority(ProtHandle, Length , MUX_TAG, LowPoolPriority); + + if (pAdapt == NULL) + { + Status = NDIS_STATUS_RESOURCES; + break; + } + + // + // Initialize the adapter structure + // + NdisZeroMemory(pAdapt, sizeof(ADAPT)); + + (VOID)PtReferenceAdapter(pAdapt, (PUCHAR)"openadapter"); + + + // + // Copy in the Config string - we will use this to open the + // registry section for this adapter at a later point. + // + pAdapt->ConfigString.MaximumLength = pConfigString->MaximumLength; + pAdapt->ConfigString.Length = pConfigString->Length; + pAdapt->ConfigString.Buffer = (PWCHAR)((PUCHAR)pAdapt + + sizeof(ADAPT)); + + NdisMoveMemory(pAdapt->ConfigString.Buffer, + pConfigString->Buffer, + pConfigString->Length); + pAdapt->ConfigString.Buffer[pConfigString->Length/sizeof(WCHAR)] = + ((WCHAR)0); + + NdisInitializeEvent(&pAdapt->Event); + NdisInitializeListHead(&pAdapt->VElanList); + + pAdapt->PtDevicePowerState = NdisDeviceStateD0; + + // + // Copy the Link state, this could be updated by PtStatus soon after + // open operation is complete + // + pAdapt->LastIndicatedLinkState.Header.Revision = NDIS_LINK_STATE_REVISION_1; + pAdapt->LastIndicatedLinkState.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + pAdapt->LastIndicatedLinkState.Header.Size = sizeof(NDIS_LINK_STATE); + pAdapt->LastIndicatedLinkState.MediaConnectState = BindParameters->MediaConnectState; + pAdapt->LastIndicatedLinkState.MediaDuplexState = BindParameters->MediaDuplexState; + pAdapt->LastIndicatedLinkState.XmitLinkSpeed = BindParameters->XmitLinkSpeed; + pAdapt->LastIndicatedLinkState.RcvLinkSpeed = BindParameters->RcvLinkSpeed; + pAdapt->Flags = 0; + + MUX_INIT_ADAPT_RW_LOCK(pAdapt); + NdisAllocateSpinLock(&pAdapt->Lock); + // + // Now open the adapter below and complete the initialization + // + NdisZeroMemory(&OpenParameters, sizeof(NDIS_OPEN_PARAMETERS)); + + OpenParameters.Header.Type = NDIS_OBJECT_TYPE_OPEN_PARAMETERS; + OpenParameters.Header.Revision = NDIS_OPEN_PARAMETERS_REVISION_1; + OpenParameters.Header.Size = sizeof(NDIS_OPEN_PARAMETERS); + OpenParameters.AdapterName = BindParameters->AdapterName; + OpenParameters.MediumArray = MediumArray; + OpenParameters.MediumArraySize = sizeof(MediumArray) / sizeof(NDIS_MEDIUM); + OpenParameters.SelectedMediumIndex = &MediumIndex; + + OpenParameters.FrameTypeArray = NULL; + OpenParameters.FrameTypeArraySize = 0; + + NDIS_DECLARE_PROTOCOL_OPEN_CONTEXT(ADAPT); + Status = NdisOpenAdapterEx(ProtHandle, + pAdapt, + &OpenParameters, + BindContext, + &pAdapt->BindingHandle); + + if (Status == NDIS_STATUS_PENDING) + { + NdisWaitEvent(&pAdapt->Event, 0); + Status = pAdapt->Status; + } + + if (Status != NDIS_STATUS_SUCCESS) + { + pAdapt->BindingHandle = NULL; + break; + } + pAdapt->Flags |= MUX_BINDING_ACTIVE; + + pAdapt->BindingState = MuxAdapterBindingPaused; + + pAdapt->Medium = MediumArray[MediumIndex]; + + // + // Add this adapter to the global AdapterList + // + MUX_ACQUIRE_MUTEX(&GlobalMutex); + + InsertTailList(&AdapterList, &pAdapt->Link); + + MUX_RELEASE_MUTEX(&GlobalMutex); + + // + // Copy all the relevant information about the Adapter into + // the local structure + // + pAdapt->BindParameters = *BindParameters; + + if (BindParameters->RcvScaleCapabilities) + { + pAdapt->RcvScaleCapabilities = (*BindParameters->RcvScaleCapabilities); + pAdapt->BindParameters.RcvScaleCapabilities = &pAdapt->RcvScaleCapabilities; + } + + pAdapt->PowerManagementCapabilities = (*BindParameters->PowerManagementCapabilities); + + + PtPostProcessPnPCapabilities(&pAdapt->PowerManagementCapabilities, + sizeof(pAdapt->PowerManagementCapabilities)); + + // + // Zeroing out fields that are not needed by the MUX driver + // + pAdapt->BindParameters.ProtocolSection= NULL; + pAdapt->BindParameters.AdapterName = NULL; + pAdapt->BindParameters.PhysicalDeviceObject = NULL; + + // + // Start all VELANS configured on this adapter. + // + Status = PtBootStrapVElans(pAdapt, NULL); + + if (Status != NDIS_STATUS_SUCCESS) + { + break; + } + + + } while(FALSE); + + if (Status != NDIS_STATUS_SUCCESS) + { + + if (pAdapt != NULL) + { + // + // For some reason, the driver cannot create velan for the binding + // + if (pAdapt->BindingHandle != NULL) + { + // + // Close the binding the driver opened above + // + PtCloseAdapter(pAdapt); + + MUX_ACQUIRE_MUTEX(&GlobalMutex); + + RemoveEntryList(&pAdapt->Link); + + MUX_RELEASE_MUTEX(&GlobalMutex); + } + PtDereferenceAdapter(pAdapt, (PUCHAR)"openadapter"); + pAdapt = NULL; + } + } + + + + DBGPRINT(MUX_INFO, ("<== PtBindAdapter: pAdapt %p, Status %x\n", pAdapt, Status)); + + return Status; +} + + +VOID +PtOpenAdapterComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN NDIS_STATUS Status + ) +/*++ + +Routine Description: + + Completion routine for NdisOpenAdapter issued from within the + PtBindAdapter. Simply unblock the caller. + +Arguments: + + ProtocolBindingContext Pointer to the adapter + Status Status of the NdisOpenAdapter call + +Return Value: + + None + +--*/ +{ + PADAPT pAdapt =(PADAPT)ProtocolBindingContext; + + DBGPRINT(MUX_LOUD, ("==> PtOpenAdapterComplete: Adapt %p, Status %x\n", pAdapt, Status)); + + pAdapt->Status = Status; + NdisSetEvent(&pAdapt->Event); + + DBGPRINT(MUX_LOUD, ("<== PtOpenAdapterComplete: Adapt %p, Status %x\n", pAdapt, Status)); +} + + +VOID +PtQueryAdapterInfo( + IN PADAPT pAdapt + ) +/*++ + +Routine Description: + + Query the adapter we are bound to for some standard OID values + which we cache. + +Arguments: + + pAdapt Pointer to the adapter + + +Return Value: + + None +--*/ +{ + // + // Insert code here to query Adapter info if needed + // + UNREFERENCED_PARAMETER(pAdapt); + +} + + +VOID +PtRequestAdapterSync( + IN PADAPT pAdapt, + IN NDIS_REQUEST_TYPE RequestType, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength + ) +/*++ + +Routine Description: + + Utility routine to query the adapter for a single OID value. This + blocks for the query to complete. + +Arguments: + + pAdapt Pointer to the adapter + RequestType The type of the NDIS request + Oid OID to query for + InformationBuffer Place for the result + InformationBufferLength Length of the above + +Return Value: + + None. + +--*/ +{ + PMUX_NDIS_REQUEST pMuxNdisRequest = NULL; + NDIS_STATUS Status = NDIS_STATUS_FAILURE; + + DBGPRINT(MUX_LOUD, ("==> PtRequestAdapterSync: Adapt %p, OID %8x\n", pAdapt, Oid)); + do + { + pMuxNdisRequest = NdisAllocateMemoryWithTagPriority(pAdapt->BindingHandle, sizeof(MUX_NDIS_REQUEST), MUX_TAG, LowPoolPriority); + if (pMuxNdisRequest == NULL) + { + break; + } + + pMuxNdisRequest->pVElan = NULL; // internal request + + // + // Set up completion routine. + // + pMuxNdisRequest->pCallback = PtCompleteBlockingRequest; + NdisInitializeEvent(&pMuxNdisRequest->Event); + + pMuxNdisRequest->Request.Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST; + pMuxNdisRequest->Request.Header.Revision = NDIS_OID_REQUEST_REVISION_1; + pMuxNdisRequest->Request.Header.Size = sizeof(NDIS_OID_REQUEST); + + pMuxNdisRequest->Request.RequestType = RequestType; + pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.Oid = Oid; + pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.InformationBuffer = + InformationBuffer; + pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength = + InformationBufferLength; + + NdisAcquireSpinLock(&pAdapt->Lock); + + pAdapt->OutstandingRequests ++; + + if ((pAdapt->Flags & MUX_BINDING_CLOSING)== MUX_BINDING_CLOSING) + { + Status = NDIS_STATUS_CLOSING; + + NdisReleaseSpinLock(&pAdapt->Lock); + } + else + { + NdisReleaseSpinLock(&pAdapt->Lock); + Status = NdisOidRequest(pAdapt->BindingHandle, + &pMuxNdisRequest->Request); + } + + if (Status != NDIS_STATUS_PENDING) + { + NdisAcquireSpinLock(&pAdapt->Lock); + pAdapt->OutstandingRequests --; + + if ((pAdapt->OutstandingRequests == 0) && (pAdapt->CloseEvent != NULL)) + { + NdisSetEvent(pAdapt->CloseEvent); + pAdapt->CloseEvent = NULL; + } + NdisReleaseSpinLock(&pAdapt->Lock); + } + else + { + NdisWaitEvent(&pMuxNdisRequest->Event, 0); + Status = pMuxNdisRequest->Status; + } + } + while (FALSE); + + if (NULL != pMuxNdisRequest) + { + NdisFreeMemory(pMuxNdisRequest, sizeof(MUX_NDIS_REQUEST), 0); + } + + DBGPRINT(MUX_LOUD, ("<== PtRequestAdapterSync: Adapt %p, OID %8x, Status %8x\n", pAdapt, Oid, Status)); +} + + + +VOID +PtRequestAdapterAsync( + IN PADAPT pAdapt, + IN NDIS_REQUEST_TYPE RequestType, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + IN PMUX_REQ_COMPLETE_HANDLER pCallback + ) +/*++ + +Routine Description: + + Utility routine to query the adapter for a single OID value. + This completes asynchronously, i.e. the calling thread is + not blocked until the request completes. + +Arguments: + + pAdapt Pointer to the adapter + RequestType NDIS request type + Oid OID to set/query + InformationBuffer Input/output buffer + InformationBufferLength Length of the above + pCallback Function to call on request completion + +Return Value: + + None. + +--*/ +{ + PMUX_NDIS_REQUEST pMuxNdisRequest = NULL; + PNDIS_OID_REQUEST pNdisRequest; + NDIS_STATUS Status = NDIS_STATUS_FAILURE; + + DBGPRINT(MUX_LOUD, ("==> PtRequestAdapterAsync: Adapt %p, OID %8x\n", pAdapt, Oid)); + do + { + pMuxNdisRequest = NdisAllocateMemoryWithTagPriority(pAdapt->BindingHandle, sizeof(MUX_NDIS_REQUEST), MUX_TAG, LowPoolPriority); + if (pMuxNdisRequest == NULL) + { + break; + } + + pMuxNdisRequest->pVElan = NULL; // internal request + + // + // Set up completion routine. + // + pMuxNdisRequest->pCallback = pCallback; + + pNdisRequest = &pMuxNdisRequest->Request; + + pNdisRequest->RequestType = RequestType; + pNdisRequest->Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST; + pNdisRequest->Header.Revision = NDIS_OID_REQUEST_REVISION_1; + pNdisRequest->Header.Size = sizeof(NDIS_OID_REQUEST); + + switch (RequestType) + { + case NdisRequestQueryInformation: + pNdisRequest->DATA.QUERY_INFORMATION.Oid = Oid; + pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer = + InformationBuffer; + pNdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength = + InformationBufferLength; + + break; + + case NdisRequestSetInformation: + pNdisRequest->DATA.SET_INFORMATION.Oid = Oid; + pNdisRequest->DATA.SET_INFORMATION.InformationBuffer = + InformationBuffer; + pNdisRequest->DATA.SET_INFORMATION.InformationBufferLength = + InformationBufferLength; + + break; + + default: + ASSERT(FALSE); + break; + } + + NdisAcquireSpinLock(&pAdapt->Lock); + + pAdapt->OutstandingRequests ++; + + if ((pAdapt->Flags & MUX_BINDING_CLOSING)== MUX_BINDING_CLOSING) + { + NdisReleaseSpinLock(&pAdapt->Lock); + Status = NDIS_STATUS_CLOSING; + } + else + { + NdisReleaseSpinLock(&pAdapt->Lock); + Status = NdisOidRequest( + pAdapt->BindingHandle, + pNdisRequest); + } + + if (Status != NDIS_STATUS_PENDING) + { + PtRequestComplete( + (NDIS_HANDLE)pAdapt, + pNdisRequest, + Status); + } + } + while (FALSE); + + DBGPRINT(MUX_LOUD, ("<== PtRequestAdapterAsync: Adapt %p, OID %8x, Status %8x\n", pAdapt, Oid, Status)); +} + + + +VOID +PtCloseAdapter( + IN PADAPT pAdapt + ) +/*++ + +Routine Description: + + Call either when the protocol is unbinding or the miniport is halting to set + the packet filters back to zero and multicast filter back to zero + +Arguments: + + pAdapter Pointer to a virtual adapter + +Return Value: + None + +--*/ + +{ + ULONG PacketFilter = 0; + PVOID MCastBuf = NULL; + ULONG MCastBufSize = 0; + NDIS_STATUS Status; + NDIS_EVENT CloseEvent; + + DBGPRINT(MUX_LOUD, ("==> PtCloseAdapter: Adapt %p\n", pAdapt)); + + ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); + + // + // Clear out the packet filter and multicast list before unbinding + // from the adapter below is required for NDIS 6.0 protocols + // + PtRequestAdapterSync(pAdapt, + NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, + &PacketFilter, + sizeof(PacketFilter)); + + PtRequestAdapterSync(pAdapt, + NdisRequestSetInformation, + OID_802_3_MULTICAST_LIST, + MCastBuf, + MCastBufSize); + // + // Stop sending requests and wait for outstanding requests to complete + // + NdisAcquireSpinLock(&pAdapt->Lock); + pAdapt->Flags |= MUX_BINDING_CLOSING; + + ASSERT(pAdapt->CloseEvent == NULL); + + if (pAdapt->OutstandingRequests != 0) + { + NdisInitializeEvent(&CloseEvent); + pAdapt->CloseEvent = &CloseEvent; + NdisReleaseSpinLock(&pAdapt->Lock); + NdisWaitEvent(&CloseEvent, 0); + NdisAcquireSpinLock(&pAdapt->Lock); + } + NdisReleaseSpinLock(&pAdapt->Lock); + // + // Now Close the binding with the adapter below + // + + NdisResetEvent(&pAdapt->Event); + + Status = NdisCloseAdapterEx(pAdapt->BindingHandle); + + if (Status == NDIS_STATUS_PENDING) + { + // + // Wait for it to complete. + // + NdisWaitEvent(&pAdapt->Event, 0); + } + + pAdapt->BindingHandle = NULL; + + DBGPRINT(MUX_LOUD, ("<== PtCloseAdapter: Adapt %p\n", pAdapt)); +} + +NDIS_STATUS +PtUnbindAdapter( + IN NDIS_HANDLE UnbindContext, + IN NDIS_HANDLE ProtocolBindingContext + ) +/*++ + +Routine Description: + + Called by NDIS when we are required to unbind to the adapter below. + Go through all VELANs on the adapter and shut them down. + +Arguments: + + Status Placeholder for return status + ProtocolBindingContext Pointer to the adapter structure + UnbindContext Context for NdisUnbindComplete() if this pends + +Return Value: + + Status from closing the binding. + +--*/ +{ + PADAPT pAdapt =(PADAPT)ProtocolBindingContext; + PLIST_ENTRY p; + PVELAN pVElan = NULL; + LOCK_STATE LockState; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(UnbindContext); + + DBGPRINT(MUX_LOUD, ("==> PtUnbindAdapter: Adapt %p\n", pAdapt)); + + // + // Stop all VELANs associated with the adapter. + // Repeatedly find the first unprocessed VELAN on + // the adapter, mark it, and stop it. + // + MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); + + do + { + for (p = pAdapt->VElanList.Flink; + p != &pAdapt->VElanList; + p = p->Flink) + { + pVElan = CONTAINING_RECORD(p, VELAN, Link); + if (!pVElan->DeInitializing) + { + pVElan->DeInitializing = TRUE; + break; + } + } + + if (p != &pAdapt->VElanList) + { + ASSERT(pVElan == CONTAINING_RECORD(p, VELAN, Link)); + + // + // Got a VELAN to stop. Add a temp ref + // so that the VELAN won't go away when + // we release the ADAPT lock below. + // + PtReferenceVElan(pVElan, (PUCHAR)"UnbindTemp"); + + // + // Release the read lock because we want to + // run StopVElan at passive IRQL. + // + MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); + + PtStopVElan(pVElan); + + PtDereferenceVElan(pVElan, (PUCHAR)"UnbindTemp"); + + MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); + } + else + { + // + // No unmarked VELAN, so exit. + // + break; + } + } + while (TRUE); + + // + // Wait until all VELANs are unlinked from the adapter. + // This is so that we don't attempt to forward down packets + // and/or requests from VELANs after calling NdisCloseAdapter. + // + while (!IsListEmpty(&pAdapt->VElanList)) + { + MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); + + DBGPRINT(MUX_INFO, ("PtUnbindAdapter: pAdapt %p, VELANlist not yet empty\n", + pAdapt)); + + NdisMSleep(2000); + + MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); + } + + MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); + + // + // Close the binding to the lower adapter. + // + if (pAdapt->BindingHandle != NULL) + { + PtCloseAdapter(pAdapt); + } + else + { + // + // Binding Handle should not be NULL. + // + Status = NDIS_STATUS_FAILURE; + ASSERT(0); + } + + // + // Remove the adapter from the global AdapterList + // + + MUX_ACQUIRE_MUTEX(&GlobalMutex); + + RemoveEntryList(&pAdapt->Link); + + MUX_RELEASE_MUTEX(&GlobalMutex); + + NdisFreeSpinLock(&pAdapt->Lock); + + // + // Free all the resources associated with this Adapter except the + // ADAPT struct itself, because that will be freed by + // PtDereferenceAdapter call when the reference drops to zero. + // Note: Every VELAN associated with this Adapter takes a ref count + // on it. So the adapter memory wouldn't be freed until all the VELANs + // are shutdown. + // + + PtDereferenceAdapter(pAdapt, (PUCHAR)"Unbind"); + + DBGPRINT(MUX_LOUD, ("<== PtUnbindAdapter: Adapt %p, Status=%08lx\n", pAdapt, Status)); + + return Status; +} + + + +VOID +PtCloseAdapterComplete( + IN NDIS_HANDLE ProtocolBindingContext + ) +/*++ + +Routine Description: + + Completion for the CloseAdapter call. + +Arguments: + + ProtocolBindingContext Pointer to the adapter structure + +Return Value: + + None. + +--*/ +{ + PADAPT pAdapt =(PADAPT)ProtocolBindingContext; + + DBGPRINT(MUX_LOUD, ("==> PtCloseAdapterComplete: Adapt %p\n", + pAdapt)); + + NdisSetEvent(&pAdapt->Event); + + DBGPRINT(MUX_LOUD, ("<== PtCloseAdapterComplete: Adapt %p\n", + pAdapt)); +} + + +VOID +PtRequestComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_OID_REQUEST NdisRequest, + IN NDIS_STATUS Status + ) +/*++ + +Routine Description: + + Completion handler for an NDIS request sent to a lower + miniport. + +Arguments: + + ProtocolBindingContext Pointer to the adapter structure + NdisRequest The completed request, must be an element of an instance of MUX_NDIS_REQUEST + Status Completion status + +Return Value: + + None + +--*/ +{ + PADAPT pAdapt = (PADAPT)ProtocolBindingContext; + PMUX_NDIS_REQUEST pMuxNdisRequest; + + + DBGPRINT(MUX_LOUD, ("==> PtRequestComplete: Adapt %p, Request %p, Status %8x\n", + pAdapt, NdisRequest, Status)); + + //get the Super-structure for NDIS_REQUEST before getting the callback functions + //so make sure NdisRequest is a filled into a MUX_NDIS_REQUEST before using this function + pMuxNdisRequest = CONTAINING_RECORD(NdisRequest, MUX_NDIS_REQUEST, Request); + + ASSERT(pMuxNdisRequest->pCallback != NULL); + + + // + // Completion is handled by the callback routine: + // + (*pMuxNdisRequest->pCallback)(pAdapt, + pMuxNdisRequest, + Status); + + NdisAcquireSpinLock(&pAdapt->Lock); + + pAdapt->OutstandingRequests --; + + if ((pAdapt->OutstandingRequests == 0) && (pAdapt->CloseEvent != NULL)) + { + NdisSetEvent(pAdapt->CloseEvent); + pAdapt->CloseEvent = NULL; + } + + NdisReleaseSpinLock(&pAdapt->Lock); + + DBGPRINT(MUX_LOUD, ("<== PtRequestComplete: Adapt %p, Request %p, Status %8x\n", + pAdapt, NdisRequest, Status)); + +} + + +VOID +PtCompleteForwardedRequest( + IN PADAPT pAdapt, + IN PMUX_NDIS_REQUEST pMuxNdisRequest, + IN NDIS_STATUS Status + ) +/*++ + +Routine Description: + + Handle completion of an NDIS request that was originally + submitted to our VELAN miniport and was forwarded down + to the lower binding. + + We do some postprocessing, to cache the results of + certain queries. + +Arguments: + + pAdapt - Adapter on which the request was forwarded + pMuxNdisRequest - super-struct for request + Status - request completion status + +Return Value: + + None + +--*/ +{ + PVELAN pVElan = NULL; + PNDIS_OID_REQUEST pNdisRequest = &pMuxNdisRequest->Request; + NDIS_OID Oid; + PNDIS_OID_REQUEST OrigRequest = NULL; + BOOLEAN fCompleteRequest = FALSE; + + UNREFERENCED_PARAMETER(pAdapt); + + + DBGPRINT(MUX_LOUD, ("==> PtCompleteForwardedRequest: Adapt %p, MuxRequest %p, Status %8x\n", + pAdapt, pMuxNdisRequest, Status)); + // + // Get the originating VELAN. The VELAN will not be dereferenced + // away until the pended request is completed. + // + pVElan = pMuxNdisRequest->pVElan; + + ASSERT(pVElan != NULL); + ASSERT(pMuxNdisRequest == &pVElan->Request); + + if (Status != NDIS_STATUS_SUCCESS) + { + DBGPRINT(MUX_WARN, ("PtCompleteForwardedRequest: pVElan %p, OID %x, Status %x\n", + pVElan, + pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.Oid, + Status)); + } + + NdisAcquireSpinLock(&pVElan->Lock); + + pMuxNdisRequest->Refcount --; + if (pMuxNdisRequest->Refcount == 0) + { + fCompleteRequest = TRUE; + OrigRequest = pMuxNdisRequest->OrigRequest; + pMuxNdisRequest->OrigRequest = NULL; + } + + + NdisReleaseSpinLock(&pVElan->Lock); + + if (fCompleteRequest == FALSE) + { + return; + } + + // + // Complete the original request. + // + switch (pNdisRequest->RequestType) + { + case NdisRequestQueryInformation: + case NdisRequestQueryStatistics: + + OrigRequest->DATA.QUERY_INFORMATION.BytesWritten = + pNdisRequest->DATA.QUERY_INFORMATION.BytesWritten; + OrigRequest->DATA.QUERY_INFORMATION.BytesNeeded = + pNdisRequest->DATA.QUERY_INFORMATION.BytesNeeded; + + // + // Before completing the request, do any necessary + // post-processing. + // + Oid = pNdisRequest->DATA.QUERY_INFORMATION.Oid; + if (Status == NDIS_STATUS_SUCCESS) + { + if (Oid == OID_GEN_LINK_SPEED) + { + NdisMoveMemory (&pVElan->LinkSpeed, + pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer, + sizeof(ULONG)); + } + else if (Oid == OID_PNP_CAPABILITIES) + { + PtPostProcessPnPCapabilities(pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer, + pNdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength); + } + } + + break; + + case NdisRequestSetInformation: + + OrigRequest->DATA.SET_INFORMATION.BytesRead= + pNdisRequest->DATA.SET_INFORMATION.BytesRead; + OrigRequest->DATA.QUERY_INFORMATION.BytesNeeded= + pNdisRequest->DATA.SET_INFORMATION.BytesNeeded; + +#if IEEE_VELAN_SUPPORT + if ((pNdisRequest->DATA.SET_INFORMATION.Oid == OID_GEN_CURRENT_LOOKAHEAD) + && (pVElan->RestoreLookaheadSize == TRUE)) + { + pVElan->RestoreLookaheadSize = FALSE; + *(UNALIGNED PULONG)(Request->DATA.SET_INFORMATION.InformationBuffer) -= VLAN_TAG_HEADER_SIZE; + } +#endif + // + // Before completing the request, cache relevant information + // in our structure. + // + if (Status == NDIS_STATUS_SUCCESS) + { + Oid = pNdisRequest->DATA.SET_INFORMATION.Oid; + switch (Oid) + { + case OID_GEN_CURRENT_LOOKAHEAD: + + + NdisMoveMemory(&pVElan->LookAhead, + pNdisRequest->DATA.SET_INFORMATION.InformationBuffer, + sizeof(ULONG)); + break; + + default: + break; + } + } + + break; + + default: + ASSERT(FALSE); + break; + } + + NdisMOidRequestComplete(pVElan->MiniportAdapterHandle,OrigRequest,Status); + + MUX_DECR_PENDING_SENDS(pVElan); + + DBGPRINT(MUX_LOUD, ("<== PtCompleteForwardedRequest: Adapt %p, MuxRequest %p, Status %8x\n", + pAdapt, pMuxNdisRequest, Status)); +} + + + +VOID +PtPostProcessPnPCapabilities( + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength + ) +/*++ + +Routine Description: + + Postprocess a successfully completed query for OID_PNP_CAPABILITIES. + We modify the returned information slightly before completing + it to the VELAN above. + +Arguments: + + InformationBuffer - points to buffer for the OID + InformationBufferLength - byte length of the above. + +Return Value: + + None + +--*/ +{ + PNDIS_PNP_CAPABILITIES pPNPCapabilities; + PNDIS_PM_WAKE_UP_CAPABILITIES pPMstruct; + + DBGPRINT(MUX_LOUD, ("==> PtPostProcessPnPCapabilities\n")); + + if (InformationBufferLength >= sizeof(NDIS_PNP_CAPABILITIES)) + { + pPNPCapabilities = (PNDIS_PNP_CAPABILITIES)InformationBuffer; + + // + // The following fields must be overwritten by an IM driver. + // + pPMstruct= &pPNPCapabilities->WakeUpCapabilities; + pPMstruct->MinMagicPacketWakeUp = NdisDeviceStateUnspecified; + pPMstruct->MinPatternWakeUp = NdisDeviceStateUnspecified; + pPMstruct->MinLinkChangeWakeUp = NdisDeviceStateUnspecified; + } + DBGPRINT(MUX_LOUD, ("<== PtPostProcessPnPCapabilities\n")); +} + +VOID +PtCompleteBlockingRequest( + IN PADAPT pAdapt, + IN PMUX_NDIS_REQUEST pMuxNdisRequest, + IN NDIS_STATUS Status + ) +/*++ + +Routine Description: + + Handle completion of an NDIS request that was originated + by this driver and the calling thread is blocked waiting + for completion. + +Arguments: + + pAdapt - Adapter on which the request was forwarded + pMuxNdisRequest - super-struct for request + Status - request completion status + +Return Value: + + None + +--*/ +{ + UNREFERENCED_PARAMETER(pAdapt); + + DBGPRINT(MUX_LOUD, ("==> PtCompleteBlockingRequest: Adapt %p, MuxRequest %p, Status %8x\n", + pAdapt, pMuxNdisRequest, Status)); + + // + // The request was originated from this driver. Wake up the + // thread blocked for its completion. + // + pMuxNdisRequest->Status = Status; + NdisSetEvent(&pMuxNdisRequest->Event); + + DBGPRINT(MUX_LOUD, ("<== PtCompleteBlockingRequest: Adapt %p, MuxRequest %p, Status %8x\n", + pAdapt, pMuxNdisRequest, Status)); +} + + +VOID +PtDiscardCompletedRequest( + IN PADAPT pAdapt, + IN PMUX_NDIS_REQUEST pMuxNdisRequest, + IN NDIS_STATUS Status + ) +/*++ + +Routine Description: + + Handle completion of an NDIS request that was originated + by this driver - the request is to be discarded. + +Arguments: + + pAdapt - Adapter on which the request was forwarded + pMuxNdisRequest - super-struct for request + Status - request completion status + +Return Value: + + None + +--*/ +{ + UNREFERENCED_PARAMETER(pAdapt); + UNREFERENCED_PARAMETER(Status); + + NdisFreeMemory(pMuxNdisRequest, sizeof(MUX_NDIS_REQUEST), 0); +} + + +VOID +PtStatus( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_STATUS_INDICATION StatusIndication + ) +/*++ + +Routine Description: + + Handle a status indication on the lower binding (ADAPT). + If this is a media status indication, we also pass this + on to all associated VELANs. + +Arguments: + + ProtocolBindingContext Pointer to the adapter structure + GeneralStatus Status code + StatusBuffer Status buffer + StatusBufferSize Size of the status buffer + +Return Value: + + None + +--*/ +{ + PADAPT pAdapt = (PADAPT)ProtocolBindingContext; + PLIST_ENTRY p; + PVELAN pVElan; + LOCK_STATE LockState; + NDIS_STATUS GeneralStatus = StatusIndication->StatusCode; + NDIS_STATUS_INDICATION NewStatusIndication; + + DBGPRINT(MUX_LOUD, ("==> PtStatus: Adapt %p, Status %x\n", pAdapt, GeneralStatus)); + + do + { + + // + // Ignore status indications that we aren't going + // to pass up. + // + if ((GeneralStatus != NDIS_STATUS_LINK_STATE) + ) + { + break; + } + + MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); + + if (GeneralStatus == NDIS_STATUS_LINK_STATE) + { + pAdapt->LastIndicatedLinkState = *((PNDIS_LINK_STATE)(StatusIndication->StatusBuffer)); + } + for (p = pAdapt->VElanList.Flink; + p != &pAdapt->VElanList; + p = p->Flink) + { + + pVElan = CONTAINING_RECORD(p, VELAN, Link); + + MUX_INCR_PENDING_RECEIVES(pVElan); + + // + // Should the indication be sent on this VELAN? + // + if ((pVElan->MiniportInitPending) || + (pVElan->MiniportHalting) || + (pVElan->MiniportAdapterHandle == NULL) || + MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) + { + MUX_DECR_PENDING_RECEIVES(pVElan); + if (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) + { + // + // Keep track of the lastest status to indicated when VELAN power is on + // + ASSERT(GeneralStatus == NDIS_STATUS_LINK_STATE); + + pVElan->LatestUnIndicateStatus = GeneralStatus; + + if (GeneralStatus == NDIS_STATUS_LINK_STATE) + { + pVElan->LatestUnIndicateLinkState = *(PNDIS_LINK_STATE)StatusIndication->StatusBuffer; + } + } + + continue; + } + + // + // Save the last indicated status when + pVElan->LastIndicatedStatus = GeneralStatus; + if (GeneralStatus == NDIS_STATUS_LINK_STATE) + { + pVElan->LastIndicatedLinkState = *(PNDIS_LINK_STATE)StatusIndication->StatusBuffer; + } + // + // Allocate a new status indication and set the destination handle to null to ensure that the status + // is indicated to protocols bound to mux velans. Copy only the fields that makes sense to pass up. For + // instance, do not copy PortNumber, RequestId, Flags, Guid, NdisReserved. Port Number is really not + // necessary to copy and pass up for the scenario because the port number is an entity that makes sense + // between the protocol and the underlying miniport pair. + // + + NdisZeroMemory(&NewStatusIndication, sizeof(NDIS_STATUS_INDICATION)); + + NewStatusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION; + NewStatusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1; + NewStatusIndication.Header.Size = sizeof(NDIS_STATUS_INDICATION); + + NewStatusIndication.StatusCode = StatusIndication->StatusCode; + NewStatusIndication.SourceHandle = pVElan->MiniportAdapterHandle; + NewStatusIndication.DestinationHandle = NULL; + + NewStatusIndication.StatusBuffer = StatusIndication->StatusBuffer; + NewStatusIndication.StatusBufferSize = StatusIndication->StatusBufferSize; + + NdisMIndicateStatusEx(pVElan->MiniportAdapterHandle, &NewStatusIndication); + + // + // Mark this so that we forward a status complete + // indication as well. + // + pVElan->IndicateStatusComplete = TRUE; + + MUX_DECR_PENDING_RECEIVES(pVElan); + } + + MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); + } + while (FALSE); + + DBGPRINT(MUX_LOUD, ("<== PtStatus: Adapt %p, Status %x\n", pAdapt, GeneralStatus)); +} + + +BOOLEAN +PtMulticastMatch( + IN PVELAN pVElan, + IN PUCHAR pDstMac + ) +/*++ + +Routine Description: + + Check if the given multicast destination MAC address matches + any of the multicast address entries set on the VELAN. + + NOTE: the caller is assumed to hold a READ/WRITE lock + to the parent ADAPT structure. This is so that the multicast + list on the VELAN is invariant for the duration of this call. + +Arguments: + + pVElan - VELAN to look in + pDstMac - Destination MAC address to compare + +Return Value: + + TRUE iff the address matches an entry in the VELAN + +--*/ +{ + ULONG i; + UINT AddrCompareResult; + + for (i = 0; i < pVElan->McastAddrCount; i++) + { + ETH_COMPARE_NETWORK_ADDRESSES_EQ(pVElan->McastAddrs[i], + pDstMac, + &AddrCompareResult); + + if (AddrCompareResult == 0) + { + break; + } + } + + return (i != pVElan->McastAddrCount); +} + + +BOOLEAN +PtMatchPacketToVElan( + IN PVELAN pVElan, + IN PUCHAR pDstMac, + IN BOOLEAN bIsMulticast, + IN BOOLEAN bIsBroadcast + ) +/*++ + +Routine Description: + + Check if the destination address of a received packet + matches the receive criteria on the specified VELAN. + + NOTE: the caller is assumed to hold a READ/WRITE lock + to the parent ADAPT structure. + +Arguments: + + pVElan - VELAN to check on + pDstMac - Destination MAC address in received packet + bIsMulticast - is this a multicast address + bIsBroadcast - is this a broadcast address + +Return Value: + + TRUE iff this packet should be received on the VELAN + +--*/ +{ + UINT AddrCompareResult; + ULONG PacketFilter; + BOOLEAN bPacketMatch; + + DBGPRINT(MUX_VERY_LOUD, ("==> PtMatchPacketToVElan: VElan %p\n", pVElan)); + // + PacketFilter = pVElan->PacketFilter; + + // + // Handle the directed packet case first. + // + if (!bIsMulticast) + { + // + // If the VELAN is not in promisc. mode, check if + // the destination MAC address matches the local + // address. + // + if ((PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) == 0) + { + ETH_COMPARE_NETWORK_ADDRESSES_EQ(pVElan->CurrentAddress, + pDstMac, + &AddrCompareResult); + + bPacketMatch = ((AddrCompareResult == 0) && + ((PacketFilter & NDIS_PACKET_TYPE_DIRECTED) != 0)); + } + else + { + bPacketMatch = TRUE; + } + } + else + { + // + // Multicast or broadcast packet. + // + + // + // Indicate if the filter is set to promisc mode ... + // + if ((PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) + || + + // + // or if this is a broadcast packet and the filter + // is set to receive all broadcast packets... + // + (bIsBroadcast && + (PacketFilter & NDIS_PACKET_TYPE_BROADCAST)) + || + + // + // or if this is a multicast packet, and the filter is + // either set to receive all multicast packets, or + // set to receive specific multicast packets. In the + // latter case, indicate receive only if the destn + // MAC address is present in the list of multicast + // addresses set on the VELAN. + // + (!bIsBroadcast && + ((PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) || + ((PacketFilter & NDIS_PACKET_TYPE_MULTICAST) && + PtMulticastMatch(pVElan, pDstMac)))) + ) + { + bPacketMatch = TRUE; + } + else + { + // + // No protocols above are interested in this + // multicast/broadcast packet. + // + bPacketMatch = FALSE; + } + } + + DBGPRINT(MUX_VERY_LOUD, ("<== PtMatchPacketToVElan: VElan %p, PacketMatch %x\n", pVElan, bPacketMatch)); + + return (bPacketMatch); +} + + +NDIS_STATUS +PtPnPNetEventSetPower( + IN PADAPT pAdapt, + IN PNET_PNP_EVENT_NOTIFICATION pNetPnPEventNotification + ) +/*++ +Routine Description: + + This is a notification to our protocol edge of the power state + of the lower miniport. If it is going to a low-power state, we must + wait here for all outstanding sends and requests to complete. + +Arguments: + + pAdapt - Pointer to the adpater structure + pNetPnPEvent - The Net Pnp Event. this contains the new device state + +Return Value: + + NDIS_STATUS_SUCCESS + +--*/ +{ + PLIST_ENTRY p; + PVELAN pVElan; + LOCK_STATE LockState; + NDIS_STATUS Status; + + // + // Store the new power state. + // + + pAdapt->PtDevicePowerState = *(PNDIS_DEVICE_POWER_STATE)pNetPnPEventNotification->NetPnPEvent.Buffer; + + DBGPRINT(MUX_LOUD, ("==> PnPNetEventSetPower: Adapt %p, SetPower to %d\n", + pAdapt, pAdapt->PtDevicePowerState)); + + // + // Check if the miniport below is going to a low power state. + // + if (MUX_IS_LOW_POWER_STATE(pAdapt->PtDevicePowerState)) + { + ULONG i; + + // + // It is going to a low power state. Wait for outstanding + // I/O to complete on the adapter. + // + for (i = 0; i < 10000; i++) + { + MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); + + for (p = pAdapt->VElanList.Flink; + p != &pAdapt->VElanList; + p = p->Flink) + { + pVElan = CONTAINING_RECORD(p, VELAN, Link); + if ((pVElan->OutstandingSends != 0) || + (pVElan->OutstandingReceives != 0)) + { + break; + } + } + + MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); + + if (p == &pAdapt->VElanList) + { + // + // There are no VELANs with pending I/O. + // + break; + } + + DBGPRINT(MUX_INFO, ("SetPower: Adapt %p, waiting for pending IO to complete\n", + pAdapt)); + + NdisMSleep(1000); + } + + } + else + { + // + // The device below is powered on. If we had requests + // pending on any VELANs, send them down now. + // + MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); + + for (p = pAdapt->VElanList.Flink; + p != &pAdapt->VElanList; + p = p->Flink) + { + pVElan = CONTAINING_RECORD(p, VELAN, Link); + + // + // Need to make sure other threads do not try to acquire the write lock while holding + // the same spin lock + // + NdisAcquireSpinLock(&pVElan->Lock); + if (pVElan->QueuedRequest) + { + pVElan->QueuedRequest = FALSE; + NdisReleaseSpinLock(&pVElan->Lock); + + + NdisAcquireSpinLock(&pAdapt->Lock); + + pAdapt->OutstandingRequests ++; + + if ((pAdapt->Flags & MUX_BINDING_CLOSING)== MUX_BINDING_CLOSING) + { + NdisReleaseSpinLock(&pAdapt->Lock); + Status = NDIS_STATUS_CLOSING; + } + else + { + NdisReleaseSpinLock(&pAdapt->Lock); + Status = NdisOidRequest( + pAdapt->BindingHandle, + &pVElan->Request.Request); + } + if (Status != NDIS_STATUS_PENDING) + { + PtRequestComplete(pAdapt, + &pVElan->Request.Request, + Status); + } + } + else + { + NdisReleaseSpinLock(&pVElan->Lock); + } + } + + MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); + } + + DBGPRINT(MUX_LOUD, ("<== PnPNetEventSetPower: Adapt %p, SetPower to %d\n", + pAdapt, pAdapt->PtDevicePowerState)); + + return (NDIS_STATUS_SUCCESS); +} + + +NDIS_STATUS +PtPNPHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNET_PNP_EVENT_NOTIFICATION pNetPnPEventNotification + ) + +/*++ +Routine Description: + + This is called by NDIS to notify us of a PNP event related to a lower + binding. Based on the event, this dispatches to other helper routines. + +Arguments: + + ProtocolBindingContext - Pointer to our adapter structure. Can be NULL + for "global" notifications + + pNetPnPEvent - Pointer to the PNP event to be processed. + +Return Value: + + NDIS_STATUS code indicating status of event processing. + +--*/ +{ + PADAPT pAdapt =(PADAPT)ProtocolBindingContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PLIST_ENTRY p; + NDIS_EVENT PauseEvent; + + DBGPRINT(MUX_LOUD, ("==> PtPnPHandler: Adapt %p, NetPnPEvent %d\n", pAdapt, + pNetPnPEventNotification->NetPnPEvent.NetEvent)); + + switch (pNetPnPEventNotification->NetPnPEvent.NetEvent) + { + case NetEventSetPower: + + Status = PtPnPNetEventSetPower(pAdapt, pNetPnPEventNotification); + break; + + case NetEventReconfigure: + // + // Rescan configuration and bring up any VELANs that + // have been newly added. Make sure that the global + // adapter list is undisturbed while we traverse it. + // + MUX_ACQUIRE_MUTEX(&GlobalMutex); + + for (p = AdapterList.Flink; + p != &AdapterList; + p = p->Flink) + { + pAdapt = CONTAINING_RECORD(p, ADAPT, Link); + + PtBootStrapVElans(pAdapt, NULL); + } + + MUX_RELEASE_MUTEX(&GlobalMutex); + + Status = NDIS_STATUS_SUCCESS; + break; + case NetEventIMReEnableDevice: + MUX_ACQUIRE_MUTEX(&GlobalMutex); + + for (p = AdapterList.Flink; + p != &AdapterList; + p = p->Flink) + { + pAdapt = CONTAINING_RECORD(p, ADAPT, Link); + + PtBootStrapVElans(pAdapt, pNetPnPEventNotification->NetPnPEvent.Buffer); + } + + + MUX_RELEASE_MUTEX(&GlobalMutex); + + Status = NDIS_STATUS_SUCCESS; + break; + + + case NetEventPause: + NdisAcquireSpinLock(&pAdapt->Lock); + pAdapt->BindingState = MuxAdapterBindingPausing; + + ASSERT(pAdapt->PauseEvent == NULL); + + if (pAdapt->OutstandingSends != 0) + { + NdisInitializeEvent(&PauseEvent); + + pAdapt->PauseEvent = &PauseEvent; + + NdisReleaseSpinLock(&pAdapt->Lock); + + NdisWaitEvent(&PauseEvent, 0); + + NdisAcquireSpinLock(&pAdapt->Lock); + } + + pAdapt->BindingState = MuxAdapterBindingPaused; + NdisReleaseSpinLock(&pAdapt->Lock); + + Status = NDIS_STATUS_SUCCESS; + break; + + case NetEventRestart: + pAdapt->BindingState = MuxAdapterBindingRunning; + Status = NDIS_STATUS_SUCCESS; + break; + + + default: + Status = NDIS_STATUS_SUCCESS; + + break; + } + + DBGPRINT(MUX_LOUD, ("<== PtPnPHandler: Adapt %p, NetPnPEvent %d, Status %8x\n", pAdapt, + pNetPnPEventNotification->NetPnPEvent.NetEvent, Status)); + return Status; +} + +NDIS_STATUS +PtCreateAndStartVElan( + IN PADAPT pAdapt, + IN PNDIS_STRING pVElanKey +) +/*++ + +Routine Description: + + Create and start a VELAN with the given key name. Check if a VELAN + with this key name already exists; if so do nothing. + + ASSUMPTION: this is called from either the BindAdapter handler for + the underlying adapter, or from the PNP reconfig handler. Both these + routines are protected by NDIS against pre-emption by UnbindAdapter. + If this routine will be called from any other context, it should + be protected against a simultaneous call to our UnbindAdapter handler. + +Arguments: + + pAdapt - Pointer to Adapter structure + pVElanKey - Points to a Unicode string naming the VELAN to create. + +Return Value: + + NDIS_STATUS_SUCCESS if we either found a duplicate VELAN or + successfully initiated a new ELAN with the given key. + + NDIS_STATUS_XXX error code otherwise (failure initiating a new VELAN). + +--*/ +{ + NDIS_STATUS Status; + PVELAN pVElan; + + Status = NDIS_STATUS_SUCCESS; + pVElan = NULL; + + DBGPRINT(MUX_LOUD, ("=> PtCreateAndStartVElan: Adapter %p, ElanKey %ws\n", + pAdapt, pVElanKey->Buffer)); + + do + { + // + // Weed out duplicates. + // + if (pVElanKey != NULL) + { + + pVElan = PtFindVElan(pAdapt, pVElanKey); + + if (NULL != pVElan) + { + // + // Duplicate - bail out silently. + // + DBGPRINT(MUX_WARN, ("CreateElan: found duplicate pVElan %p\n", pVElan)); + + Status = NDIS_STATUS_SUCCESS; + pVElan = NULL; + break; + } + } + + pVElan = NULL; + + if (pVElanKey != NULL) + { + pVElan = PtAllocateAndInitializeVElan(pAdapt, pVElanKey); + } + + if (pVElan == NULL) + { + Status = NDIS_STATUS_RESOURCES; + break; + } + // + // Request NDIS to initialize the virtual miniport. Set + // the flag below just in case an unbind occurs before + // MiniportInitialize is called. + // + PtReferenceVElan(pVElan,(UCHAR*) "CreatVelan"); + pVElan->MiniportInitPending = TRUE; + NdisInitializeEvent(&pVElan->MiniportInitEvent); + + Status = NdisIMInitializeDeviceInstanceEx(DriverHandle, + &pVElan->CfgDeviceName, + pVElan); + + if (Status != NDIS_STATUS_SUCCESS) + { + if (pVElan->MiniportHalting == FALSE) + { + PtUnlinkVElanFromAdapter(pVElan); // IMInit failed + } + PtDereferenceVElan(pVElan, (UCHAR*) "CreatVelan"); + pVElan = NULL; + break; + } + + PtDereferenceVElan(pVElan,(UCHAR*) "CreatVelan"); + + } + while (FALSE); + + DBGPRINT(MUX_LOUD, ("<== PtCreateAndStartVElan: Adapter %p, VELAN %p, Status %8x\n", pAdapt, pVElan, Status)); + + return Status; +} + + +PVELAN +PtAllocateAndInitializeVElan( + IN PADAPT pAdapt, + IN PNDIS_STRING pVElanKey + ) +/*++ + +Routine Description: + + Allocates and initializes a VELAN structure. Also links it to + the specified ADAPT. + +Arguments: + + pAdapt - Adapter to link VELAN to + pVElanKey - Key to the VELAN + +Return Value: + + Pointer to VELAN structure if successful, NULL otherwise. + +--*/ +{ + PVELAN pVElan; + ULONG Length; + NDIS_STATUS Status; + LOCK_STATE LockState; + + DBGPRINT(MUX_LOUD, ("==> PtCreateAndStartVElan: Adapter %p, VELAN Key %ws\n", pAdapt, pVElanKey->Buffer)); + pVElan = NULL; + Status = NDIS_STATUS_SUCCESS; + + do + { + Length = sizeof(VELAN) + pVElanKey->Length + sizeof(WCHAR); + + // + // Allocate a VELAN data structure. + // + pVElan = NdisAllocateMemoryWithTagPriority(pAdapt->BindingHandle, Length, MUX_TAG, LowPoolPriority); + if (pVElan == NULL) + { + DBGPRINT(MUX_FATAL, ("AllocateVElan: Failed to allocate %d bytes for VELAN\n", + Length)); + Status = NDIS_STATUS_RESOURCES; + break; + } + + // + // Initialize it. + // + NdisZeroMemory(pVElan, Length); + NdisInitializeListHead(&pVElan->Link); + NdisInitializeListHead(&pVElan->GlobalLink); + + // + // Initialize the built-in request structure to signify + // that it is used to forward NDIS requests. + // + pVElan->Request.pVElan = pVElan; + NdisInitializeEvent(&pVElan->Request.Event); + + // + // Store in the key name. + // + pVElan->CfgDeviceName.Length = 0; + pVElan->CfgDeviceName.Buffer = (PWCHAR)((PUCHAR)pVElan + + sizeof(VELAN)); + pVElan->CfgDeviceName.MaximumLength = + pVElanKey->Length + sizeof(WCHAR); + (VOID)NdisUpcaseUnicodeString(&pVElan->CfgDeviceName, pVElanKey); + pVElan->CfgDeviceName.Buffer[pVElanKey->Length/sizeof(WCHAR)] = + ((WCHAR)0); + + // + // Initialize LastIndicatedStatus to media connect + // + pVElan->LastIndicatedStatus = NDIS_STATUS_LINK_STATE; + + // + // Set power state of virtual miniport to D0. + // + pVElan->MPDevicePowerState = NdisDeviceStateD0; + + // + // Cache the binding handle for quick reference. + // + pVElan->BindingHandle = pAdapt->BindingHandle; + pVElan->pAdapt = pAdapt; + + // + // Copy in some adapter parameters. + // + pVElan->LookAhead = pAdapt->BindParameters.LookaheadSize; + pVElan->LinkSpeed = pAdapt->BindParameters.RcvLinkSpeed; + ASSERT(pAdapt->BindParameters.MacAddressLength == 6); + + if (pAdapt->BindParameters.MacAddressLength == 6) + { + + NdisMoveMemory(pVElan->PermanentAddress, + &pAdapt->BindParameters.CurrentMacAddress, + pAdapt->BindParameters.MacAddressLength); + + NdisMoveMemory(pVElan->CurrentAddress, + &pAdapt->BindParameters.CurrentMacAddress, + pAdapt->BindParameters.MacAddressLength); + } + else + { + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + DBGPRINT(MUX_LOUD, ("Alloced VELAN %p, MAC addr %s\n", + pVElan, MacAddrToString(pVElan->CurrentAddress))); + + NdisAllocateSpinLock(&pVElan->Lock); + + NdisAllocateSpinLock(&pVElan->PauseLock); + + + +#ifdef IEEE_VLAN_SUPPORT + // + // Allocate lookaside list for tag headers. + // + NdisInitializeNPagedLookasideList ( + &pVElan->TagLookaside, + NULL, + NULL, + 0, + ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE + sizeof(IM_SEND_NB_ENTRY), + MUX_TAG, + 0); + +#endif + // + // Finally link this VELAN to the Adapter's VELAN list. + // + PtReferenceVElan(pVElan, (PUCHAR)"adapter"); + + MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState); + + PtReferenceAdapter(pAdapt, (PUCHAR)"VElan"); + InsertTailList(&pAdapt->VElanList, &pVElan->Link); + + pAdapt->VElanCount++; + pVElan->VElanNumber = NdisInterlockedIncrement((PLONG)&NextVElanNumber); + + MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState); + + NdisAcquireSpinLock(&GlobalLock); + InsertTailList(&VElanList, &pVElan->GlobalLink); + NdisReleaseSpinLock(&GlobalLock);; + } + while (FALSE); + + if (Status != NDIS_STATUS_SUCCESS) + { + if (pVElan) + { + PtDeallocateVElan(pVElan); + pVElan = NULL; + } + } + DBGPRINT(MUX_LOUD, ("<== PtCreateAndStartVElan: Adapter %p, VELAN Key %ws, VElan %p\n", pAdapt, pVElanKey->Buffer, pVElan)); + return (pVElan); +} + + +VOID +PtDeallocateVElan( + IN PVELAN pVElan + ) +/*++ + +Routine Description: + + Free up all resources allocated to a VELAN, and then the VELAN + structure itself. + +Arguments: + + pVElan - Pointer to VELAN to be deallocated. + +Return Value: + + None + +--*/ +{ + + + NdisFreeSpinLock(&pVElan->Lock); + NdisFreeSpinLock(&pVElan->PauseLock); + +#ifdef IEEE_VLAN_SUPPORT + NdisDeleteNPagedLookasideList(&pVElan->TagLookaside); +#endif + + NdisFreeMemory(pVElan, 0, 0); +} + + +VOID +PtStopVElan( + IN PVELAN pVElan +) +/*++ + +Routine Description: + + Stop a VELAN by requesting NDIS to halt the virtual miniport. + The caller has a reference on the VELAN, so it won't go away + while we are executing in this routine. + + ASSUMPTION: this is only called in the context of unbinding + from the underlying miniport. If it may be called from elsewhere, + this should protect itself from re-entrancy. + +Arguments: + + pVElan - Pointer to VELAN to be stopped. + +Return Value: + + None + +--*/ +{ + NDIS_STATUS Status; + NDIS_HANDLE MiniportAdapterHandle; + BOOLEAN bMiniportInitCancelled = FALSE; + + DBGPRINT(MUX_LOUD, ("==> PtStopVElan: VELAN %p, Adapt %p\n", pVElan, pVElan->pAdapt)); + + // + // We make blocking calls below. + // + ASSERT_AT_PASSIVE(); + + // + // If there was a queued request on this VELAN, fail it now. + // + NdisAcquireSpinLock(&pVElan->Lock); + ASSERT(pVElan->DeInitializing == TRUE); + if (pVElan->QueuedRequest) + { + pVElan->QueuedRequest = FALSE; + NdisReleaseSpinLock(&pVElan->Lock); + + PtRequestComplete(pVElan->pAdapt, + &pVElan->Request.Request, + NDIS_STATUS_FAILURE); + } + + else + { + NdisReleaseSpinLock(&pVElan->Lock); + } + // + // Check if we had called NdisIMInitializeDeviceInstanceEx and + // we are awaiting a call to MiniportInitialize. + // + if (pVElan->MiniportInitPending) + { + // + // Attempt to cancel miniport init. + // + Status = NdisIMCancelInitializeDeviceInstance( + DriverHandle, + &pVElan->CfgDeviceName); + + if (Status == NDIS_STATUS_SUCCESS) + { + // + // Successfully cancelled IM initialization; our + // Miniport Init routine will not be called for this + // VELAN miniport. + // + pVElan->MiniportInitPending = FALSE; + ASSERT(pVElan->MiniportAdapterHandle == NULL); + bMiniportInitCancelled = TRUE; + } + else + { + // + // Our Miniport Initialize routine will be called + // (may be running on another thread at this time). + // Wait for it to finish. + // + NdisWaitEvent(&pVElan->MiniportInitEvent, 0); + ASSERT(pVElan->MiniportInitPending == FALSE); + } + } + + // + // Check if Miniport Init has run. If so, deinitialize the virtual + // miniport. This will result in a call to our Miniport Halt routine, + // where the VELAN will be cleaned up. + // + MiniportAdapterHandle = pVElan->MiniportAdapterHandle; + + if ((NULL != MiniportAdapterHandle) && + (!pVElan->MiniportHalting)) + { + // + // The miniport was initialized, and has not yet halted. + // + ASSERT(bMiniportInitCancelled == FALSE); + (VOID)NdisIMDeInitializeDeviceInstance(MiniportAdapterHandle); + } + else + { + if (bMiniportInitCancelled || + ((MiniportAdapterHandle == NULL) && !pVElan->MiniportHalting)) + { + + // + // No NDIS events can come to this VELAN since it + // was never initialized as a miniport. We need to unlink + // it explicitly here. + // + PtUnlinkVElanFromAdapter(pVElan); + } + } + + DBGPRINT(MUX_LOUD, ("<== PtStopVElan: VELAN %p, Adapt %p\n", pVElan, pVElan->pAdapt)); +} + + +VOID +PtUnlinkVElanFromAdapter( + IN PVELAN pVElan +) +/*++ + +Routine Description: + + Utility routine to unlink a VELAN from its parent ADAPT structure. + +Arguments: + + pVElan - Pointer to VELAN to be unlinked. + +Return Value: + + None + +--*/ +{ + PADAPT pAdapt = pVElan->pAdapt; + LOCK_STATE LockState; + + DBGPRINT(MUX_LOUD, ("==> PtUnlinkVElanFromAdapter: VELAN %p, Adapt %p\n", pVElan, pAdapt)); + ASSERT(pAdapt != NULL); + + + // + // Remove this VELAN from the global list + // + + NdisAcquireSpinLock(&GlobalLock); + RemoveEntryList(&pVElan->GlobalLink); + NdisReleaseSpinLock(&GlobalLock); + + // + // Remove this VELAN from the Adapter list + // + MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState); + + RemoveEntryList(&pVElan->Link); + pAdapt->VElanCount--; + + MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState); + pVElan->pAdapt = NULL; + PtDereferenceVElan(pVElan, (PUCHAR)"adapter"); + + PtDereferenceAdapter(pAdapt, (PUCHAR)"VElan"); + DBGPRINT(MUX_LOUD, ("<== PtUnlinkVElanFromAdapter: VELAN %p, Adapt %p\n", pVElan, pAdapt)); +} + + +PVELAN +PtFindVElan( + IN PADAPT pAdapt, + IN PNDIS_STRING pVElanKey +) +/*++ + +Routine Description: + + Find an ELAN by bind name/key + +Arguments: + + pAdapt - Pointer to an adapter struct. + pVElanKey - The VELAN's device name + +Return Value: + + Pointer to matching VELAN or NULL if not found. + +--*/ +{ + PLIST_ENTRY p; + PVELAN pVElan; + BOOLEAN Found; + NDIS_STRING VElanKeyName = {0, 0, NULL}; + LOCK_STATE LockState; + + ASSERT_AT_PASSIVE(); + + DBGPRINT(MUX_LOUD, ("==> PtFindElan: Adapter %p, ElanKey %ws\n", pAdapt, + pVElanKey->Buffer)); + + pVElan = NULL; + Found = FALSE; + VElanKeyName.Buffer = NULL; + + do + { + // + // Make an up-cased copy of the given string. + // + VElanKeyName.Buffer = NdisAllocateMemoryWithTagPriority(pAdapt->BindingHandle, + pVElanKey->MaximumLength, + MUX_TAG, + LowPoolPriority); + if (VElanKeyName.Buffer == NULL) + { + break; + } + + VElanKeyName.Length = pVElanKey->Length; + VElanKeyName.MaximumLength = pVElanKey->MaximumLength; + + (VOID)NdisUpcaseUnicodeString(&VElanKeyName, pVElanKey); + + // + // Go through all VELANs on the ADAPT structure, looking + // for a VELAN that has a matching device name. + // + MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); + + p = pAdapt->VElanList.Flink; + while (p != &pAdapt->VElanList) + { + pVElan = CONTAINING_RECORD(p, VELAN, Link); + + if ((VElanKeyName.Length == pVElan->CfgDeviceName.Length) && + (memcmp(VElanKeyName.Buffer, pVElan->CfgDeviceName.Buffer, + VElanKeyName.Length) == 0)) + { + Found = TRUE; + break; + } + + p = p->Flink; + } + + MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); + + } + while (FALSE); + + if (!Found) + { + DBGPRINT(MUX_INFO, ( "FindElan: No match found!\n")); + pVElan = NULL; + } + + if (VElanKeyName.Buffer) + { + NdisFreeMemory(VElanKeyName.Buffer, VElanKeyName.Length, 0); + } + + DBGPRINT(MUX_LOUD, ("<== PtFindElan: Adapter %p, ElanKey %ws, VElan %p\n", pAdapt, + pVElanKey->Buffer, pVElan)); + return pVElan; +} + + +NDIS_STATUS +PtBootStrapVElans( + IN PADAPT pAdapt, + IN PNDIS_STRING InstanceName OPTIONAL + +) +/*++ + +Routine Description: + + Start up the VELANs configured for an adapter. + +Arguments: + + pAdapt - Pointer to ATMLANE Adapter structure + +Return Value: + + None + +--*/ +{ + NDIS_STATUS Status; + NDIS_HANDLE AdapterConfigHandle; + PNDIS_CONFIGURATION_PARAMETER Param; + NDIS_STRING DeviceStr = NDIS_STRING_CONST("UpperBindings"); + PWSTR buffer; + LOCK_STATE LockState; + NDIS_CONFIGURATION_OBJECT ConfigObject; + + DBGPRINT(MUX_LOUD, ("==> PtBootStrapElans: adapter %p\n", pAdapt)); + // + // Initialize. + // + Status = NDIS_STATUS_SUCCESS; + AdapterConfigHandle = NULL; + + do + { + DBGPRINT(MUX_LOUD, ("PtBootStrapElans: Starting ELANs on adapter %p\n", pAdapt)); + + // + // Open the protocol configuration section for this adapter. + // + ConfigObject.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT; + ConfigObject.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1; + ConfigObject.Header.Size = sizeof(NDIS_CONFIGURATION_OBJECT); + ConfigObject.NdisHandle = pAdapt->BindingHandle; + ConfigObject.Flags = 0; + + Status = NdisOpenConfigurationEx( + &ConfigObject, + &AdapterConfigHandle); + + if (Status != NDIS_STATUS_SUCCESS) + { + AdapterConfigHandle = NULL; + DBGPRINT(MUX_ERROR, ("PtBootStrapElans: OpenProtocolConfiguration failed\n")); + Status = NDIS_STATUS_OPEN_FAILED; + break; + } + + // + // Read the "UpperBindings" reserved key that contains a list + // of device names representing our miniport instances corresponding + // to this lower binding. The UpperBindings is a + // MULTI_SZ containing a list of device names. We will loop through + // this list and initialize the virtual miniports. + // + NdisReadConfiguration(&Status, + &Param, + AdapterConfigHandle, + &DeviceStr, + NdisParameterMultiString); + if (NDIS_STATUS_SUCCESS != Status) + { + DBGPRINT(MUX_ERROR, ("PtBootStrapElans: NdisReadConfiguration failed\n")); + break; + } + + // + // Parse the Multi_sz string to extract the device name of each VELAN. + // This is used as the key name for the VELAN. + // + buffer = (PWSTR)Param->ParameterData.StringData.Buffer; + while(*buffer != L'\0') + { + NDIS_STRING DeviceName; + + NdisInitUnicodeString(&DeviceName, buffer); + + if (InstanceName != NULL) + { + if (NdisEqualString(&DeviceName, InstanceName, TRUE)) + { + Status = PtCreateAndStartVElan(pAdapt, &DeviceName); + break; + } + } + else + { + + Status = PtCreateAndStartVElan(pAdapt, &DeviceName); + } + if (NDIS_STATUS_SUCCESS != Status) + { + DBGPRINT(MUX_ERROR, ("PtBootStrapElans: CreateVElan failed\n")); + break; + } + buffer = (PWSTR)((PUCHAR)buffer + DeviceName.Length + sizeof(WCHAR)); + }; + + } while (FALSE); + + // + // Close config handles + // + if (NULL != AdapterConfigHandle) + { + NdisCloseConfiguration(AdapterConfigHandle); + } + // + // If the driver cannot create any velan for the adapter + // + if (Status != NDIS_STATUS_SUCCESS) + { + MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState); + // + // No VElan is created for this adapter + // + if (pAdapt->VElanCount != 0) + { + Status = NDIS_STATUS_SUCCESS; + } + MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState); + } + + DBGPRINT(MUX_LOUD, ("<== PtBootStrapElans: adapter %p, Status %8x\n", pAdapt, Status)); + + return Status; +} + +VOID +PtReferenceVElan( + IN PVELAN pVElan, + IN PUCHAR String + ) +/*++ + +Routine Description: + + Add a references to an Elan structure. + +Arguments: + + pElan - Pointer to the Elan structure. + + +Return Value: + + None. + +--*/ +{ + + NdisInterlockedIncrement((PLONG)&pVElan->RefCount); + +#if !DBG + UNREFERENCED_PARAMETER(String); +#endif + + DBGPRINT(MUX_LOUD, ("ReferenceElan: Elan %p (%s) new count %d\n", + pVElan, String, pVElan->RefCount)); + + return; +} + +ULONG +PtDereferenceVElan( + IN PVELAN pVElan, + IN PUCHAR String + ) +/*++ + +Routine Description: + + Subtract a reference from an VElan structure. + If the reference count becomes zero, deallocate it. + +Arguments: + + pElan - Pointer to an VElan structure. + + +Return Value: + + None. + +--*/ +{ + ULONG rc; + +#if !DBG + UNREFERENCED_PARAMETER(String); +#endif + + ASSERT(pVElan->RefCount > 0); + + rc = NdisInterlockedDecrement((PLONG)&pVElan->RefCount); + + if (rc == 0) + { + // + // Free memory if there is no outstanding reference. + // Note: Length field is not required if the memory + // is allocated with NdisAllocateMemoryWithTagPriority. + // + PtDeallocateVElan(pVElan); + } + + DBGPRINT(MUX_LOUD, ("DereferenceElan: VElan %p (%s) new count %d\n", + pVElan, String, rc)); + return (rc); +} + + +BOOLEAN +PtReferenceAdapter( + IN PADAPT pAdapt, + IN PUCHAR String + ) +/*++ + +Routine Description: + + Add a references to an Adapter structure. + +Arguments: + + pAdapt - Pointer to the Adapter structure. + +Return Value: + + None. + +--*/ +{ + +#if !DBG + UNREFERENCED_PARAMETER(String); +#endif + + NdisInterlockedIncrement((PLONG)&pAdapt->RefCount); + + DBGPRINT(MUX_LOUD, ("ReferenceAdapter: Adapter %p (%s) new count %d\n", + pAdapt, String, pAdapt->RefCount)); + + return TRUE; +} + +ULONG +PtDereferenceAdapter( + IN PADAPT pAdapt, + IN PUCHAR String + ) +/*++ + +Routine Description: + + Subtract a reference from an Adapter structure. + If the reference count becomes zero, deallocate it. + +Arguments: + + pAdapt - Pointer to an adapter structure. + + +Return Value: + + None. + +--*/ +{ + ULONG rc; + +#if !DBG + UNREFERENCED_PARAMETER(String); +#endif + + ASSERT(pAdapt->RefCount > 0); + + + rc = NdisInterlockedDecrement ((PLONG)&pAdapt->RefCount); + + if (rc == 0) + { + // + // Free memory if there is no outstanding reference. + // Note: Length field is not required if the memory + // is allocated with NdisAllocateMemoryWithTagPriority. + // + NdisFreeMemory(pAdapt, 0, 0); + } + + DBGPRINT(MUX_LOUD, ("DereferenceAdapter: Adapter %p (%s) new count %d\n", + pAdapt, String, rc)); + + return (rc); +} + + +VOID +PtReceiveNBL( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNET_BUFFER_LIST NetBufferLists, + IN NDIS_PORT_NUMBER PortNumber, + IN ULONG NumberOfNetBufferLists, + IN ULONG ReceiveFlags + ) +/*++ + +Routine Description: + ReceiveNetBufferList handler. + +Arguments: + ProtocolBindingContext Pointer to our PADAPT structure + NetBufferLists Net Buffer Lists received + PortNumber Port on which NBLS were received + NumberOfNetBufferLists Number of Net Buffer Lists + ReceiveFlags Flags associated with the receive + +Return Value: + None + +NOTE: This receive code path is not efficient, we will optimize it later. + +--*/ +{ + PADAPT pAdapt = (PADAPT)ProtocolBindingContext; + PVELAN pVElan = NULL; + PUCHAR pDstMac; + BOOLEAN bIsMulticast; + BOOLEAN bIsBroadcast; + PNET_BUFFER_LIST CurrentNetBufferList = NULL; + PNET_BUFFER_LIST ReturnNetBufferList = NULL; + PNET_BUFFER_LIST LastReturnNetBufferList = NULL; + LOCK_STATE LockState; + PLIST_ENTRY p; + ULONG ReturnFlags; + ULONG NewReceiveFlags; + UCHAR Data[6]={0,0,0,0,0,0}; + //BOOLEAN DispatchLevel; + BOOLEAN bReturnNbl; + +#ifdef IEEE_VLAN_SUPPORT + NDIS_STATUS NdisStatus; + NDIS_NET_BUFFER_LIST_8021Q_INFO NdisPacket8021qInfo; + BOOLEAN bAllocatedContext; + PRECV_NBL_ENTRY RecvContext; +#endif + + UNREFERENCED_PARAMETER(NumberOfNetBufferLists); + + DBGPRINT(MUX_VERY_LOUD,("==>PtReceiveNBL: ProtocolBindingContext %p, NetBufferLists %p\n",ProtocolBindingContext,NetBufferLists)); + + //DispatchLevel = NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags); + + ReturnFlags = 0; + + if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags)) + { + NDIS_SET_RETURN_FLAG(ReturnFlags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL); + } + + ASSERT(NetBufferLists != NULL); + + // We could get receives in the interval between + // initiating a request to set the packet filter on + // the binding to 0 and completion of that request. + // Return immediately + + if (pAdapt->PacketFilter == 0) + { + if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags) == TRUE) + { + NdisReturnNetBufferLists(pAdapt->BindingHandle, + NetBufferLists, + ReturnFlags); + } + return; + } + + while (NetBufferLists != NULL) + { + CurrentNetBufferList = NetBufferLists; + + NetBufferLists = NET_BUFFER_LIST_NEXT_NBL(NetBufferLists); + + NET_BUFFER_LIST_NEXT_NBL(CurrentNetBufferList) = NULL; + + bReturnNbl = TRUE; + +#ifdef IEEE_VLAN_SUPPORT + bAllocatedContext = FALSE; +#endif + + do + { + // Collect some information about the packet + pDstMac = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(CurrentNetBufferList), + 6, + (PVOID)Data, + 1, + 0); + + if (pDstMac == NULL) + { + ASSERT(0); + break; + } + + // Determine if the packet is broadcast or multicast + + bIsMulticast = ETH_IS_MULTICAST(pDstMac); + bIsBroadcast = ETH_IS_BROADCAST(pDstMac); + +#ifdef IEEE_VLAN_SUPPORT + // + // Create Receive context to save information about tag + // + NdisStatus = NdisAllocateNetBufferListContext(CurrentNetBufferList, + sizeof(RECV_NBL_ENTRY), + 0, + MUX_TAG); + if (NdisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + + bAllocatedContext = TRUE; + + RecvContext = (PRECV_NBL_ENTRY) NET_BUFFER_LIST_CONTEXT_DATA_START(CurrentNetBufferList); + NdisZeroMemory(RecvContext, sizeof(RECV_NBL_ENTRY)); + + // + // Strip off the VLAN Tag if present + // + if ((NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(CurrentNetBufferList))) < (ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE)) + { + // + // If the VLAN tag is not present in the buffer, ignore this NBL + // + DBGPRINT(MUX_LOUD,("PtReceiveNBL: NBL %p size < VLAN_TAG_HEADER_SIZE\n",CurrentNetBufferList)); + break; + } + + if (NET_BUFFER_LIST_INFO(CurrentNetBufferList, Ieee8021QNetBufferListInfo) != 0) + { + // + // If the VLAN info is already in the NBL, take this information it + // + DBGPRINT(MUX_LOUD,("PtReceiveNBL: NBL %p already has Ieee8021QNetBufferListInfo\n",CurrentNetBufferList)); + + RtlCopyMemory((PVOID UNALIGNED) &NdisPacket8021qInfo, &NET_BUFFER_LIST_INFO(CurrentNetBufferList, Ieee8021QNetBufferListInfo),sizeof(NdisPacket8021qInfo)); + } + else + { + NdisStatus = PtStripVlanTagNB(CurrentNetBufferList, &NdisPacket8021qInfo, RecvContext); + + if (NdisStatus != NDIS_STATUS_SUCCESS) + { + break; + } + } +#endif + + // Lock down the VLAN list on the adapter so that no insertions + // deletions to this list happen while we loop through it. The packet + // filter will also not change during this time we hold the read lock. + + MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); + + // Set up the ref count before we start indicating the packet + + for (p = pAdapt->VElanList.Flink; + p != &pAdapt->VElanList; + p = p->Flink) + { + BOOLEAN bIndicateReceive; + + pVElan = CONTAINING_RECORD(p, VELAN, Link); + + // Should the packet be indicated up on this VELAN ? + + bIndicateReceive = PtMatchPacketToVElan(pVElan, + pDstMac, + bIsMulticast, + bIsBroadcast); + + if (!bIndicateReceive) + { + continue; + } + + MUX_INCR_PENDING_RECEIVES(pVElan); + + if ((pVElan->MiniportInitPending) + || (pVElan->MiniportHalting) + || (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState))) + { + MUX_DECR_PENDING_RECEIVES(pVElan); + continue; + } + + NdisAcquireSpinLock(&pVElan->PauseLock); + + if (!pVElan->Paused) + { + NdisReleaseSpinLock(&pVElan->PauseLock); + +#ifdef IEEE_VLAN_SUPPORT + NdisStatus = PtHandleReceiveTaggingNB(pVElan, CurrentNetBufferList, &NdisPacket8021qInfo); + + if (NdisStatus != STATUS_SUCCESS) + { + MUX_DECR_PENDING_RECEIVES(pVElan); + continue; + } +#endif + + MUX_INCR_STATISTICS64(&pVElan->GoodReceives); + + // Indicate to the protocol(s) bound to the Miniport + NewReceiveFlags = ReceiveFlags; + + // + // Indicate with RESOURCES flag if it is not the last VELAN + // and NBLs were not indicated with RESOURCES flag to us. + // + if ((p->Flink != &pAdapt->VElanList) || + (NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags) == TRUE)) + { + NDIS_SET_RECEIVE_FLAG(NewReceiveFlags, NDIS_RECEIVE_FLAGS_RESOURCES); + } + else + { + bReturnNbl = FALSE; + } + + NdisMIndicateReceiveNetBufferLists(pVElan->MiniportAdapterHandle, + CurrentNetBufferList, + PortNumber, + 1, + NewReceiveFlags); + // + // Decrement OutstandingReceives for the case where the NBL + // was indicated with RESOURCES flag. Otherwise it will be + // decremented in MPReturnNetBufferLists + // + if (bReturnNbl) + { + MUX_DECR_PENDING_RECEIVES(pVElan); + +#ifdef IEEE_VLAN_SUPPORT + // + // If the buffer was advanced, retreat it + // + NdisStatus = PtRestoreReceiveNBL(CurrentNetBufferList); + ASSERT(NdisStatus == NDIS_STATUS_SUCCESS); +#endif + } + } + else + { + NdisReleaseSpinLock(&pVElan->PauseLock); + MUX_DECR_PENDING_RECEIVES(pVElan); + } + + } + + MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); + } + while (FALSE); + + if (bReturnNbl == TRUE) + { +#ifdef IEEE_VLAN_SUPPORT + // + // Free the context only if we are returning the NBL here. + // Otherwise MPReturnNetBufferLists will free it. + // + if (bAllocatedContext) + { + NdisFreeNetBufferListContext(CurrentNetBufferList, + sizeof(RECV_NBL_ENTRY)); + } +#endif + + // + // The NetBufferList is not pending with any upper protocol. + // Return the NetBufferList back to the miniport if the miniport + // gave us ownership of it + // + if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags) == TRUE) + { + if (ReturnNetBufferList == NULL) + { + ReturnNetBufferList = CurrentNetBufferList; + } + else + { + NET_BUFFER_LIST_NEXT_NBL(LastReturnNetBufferList) = CurrentNetBufferList; + } + + LastReturnNetBufferList = CurrentNetBufferList; + NET_BUFFER_LIST_NEXT_NBL(LastReturnNetBufferList) = NULL; + } + else + { + // + // Restore the NetBufferList chain + // + NET_BUFFER_LIST_NEXT_NBL(CurrentNetBufferList) = NetBufferLists; + } + } + } + + if (ReturnNetBufferList != NULL) + { + NdisReturnNetBufferLists(pAdapt->BindingHandle, + ReturnNetBufferList, + ReturnFlags); + } + + DBGPRINT(MUX_VERY_LOUD,("<==PtReceiveNBL: ProtocolBindingContext %p, NetBufferLists %p\n",ProtocolBindingContext,NetBufferLists)); +} + + +VOID +PtSendNBLComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNET_BUFFER_LIST NetBufferLists, + IN ULONG SendCompleteFlags + ) +/*++ + +Routine Description: + Called by NDIS when the miniport below has completed a send. + We complete the corresponding upper-edge send this represents. + +Arguments: + ProtocolBindingHandle Points to our PADAPT structure + NetBufferLists Packet being completed by the lower miniport + SendCompleteFlags Is the call at DispatchLevel + +Return Value: + None + +--*/ +{ + + PVELAN pVElan; + NDIS_STATUS Status; + PIM_NBL_ENTRY SendContext; + PNET_BUFFER_LIST CurrentNetBufferList; + PADAPT pAdapt = (PADAPT)ProtocolBindingContext; + BOOLEAN DispatchLevel = FALSE; +#ifdef IEEE_VLAN_SUPPORT + PNET_BUFFER MdlAllocatedNetBuffers; + ULONG Flags = 0; +#endif + + DBGPRINT(MUX_VERY_LOUD,("==> PtSendNBLComplete: ProtocolBindingContext %p, NetBufferLists %p\n", + ProtocolBindingContext,NetBufferLists)); + + DispatchLevel = NDIS_TEST_SEND_COMPLETE_AT_DISPATCH_LEVEL(SendCompleteFlags); + + while(NetBufferLists) + { + CurrentNetBufferList = NetBufferLists; + NetBufferLists = NET_BUFFER_LIST_NEXT_NBL(NetBufferLists); + NET_BUFFER_LIST_NEXT_NBL(CurrentNetBufferList) = NULL; + + SendContext = (PIM_NBL_ENTRY)NET_BUFFER_LIST_CONTEXT_DATA_START(CurrentNetBufferList); + + pVElan = SendContext->pVElan; + + CurrentNetBufferList->SourceHandle = SendContext->PreviousSourceHandle; +#ifdef IEEE_VLAN_SUPPORT + Flags = SendContext->Flags; + MdlAllocatedNetBuffers = SendContext->MdlAllocatedNetBuffers; +#endif + + Status = NET_BUFFER_LIST_STATUS(CurrentNetBufferList); + + NdisFreeNetBufferListContext(CurrentNetBufferList, + sizeof(IM_NBL_ENTRY)); + +#ifdef IEEE_VLAN_SUPPORT + + if ((Flags & MUX_RETREAT_DATA) != 0) + { + MPRestoreSendNBL(pVElan, + CurrentNetBufferList, + NULL, + MdlAllocatedNetBuffers); + } + +#endif + + if (Status == NDIS_STATUS_SUCCESS) + { + MUX_INCR_STATISTICS64(&pVElan->GoodTransmits); + } + else + { + MUX_INCR_STATISTICS(&pVElan->TransmitFailuresOther); + } + + NdisMSendNetBufferListsComplete(pVElan->MiniportAdapterHandle, + CurrentNetBufferList, + SendCompleteFlags); + + + MUX_DECR_PENDING_SENDS(pVElan); + + MUX_ACQUIRE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel); + pAdapt->OutstandingSends --; + + if ((pAdapt->OutstandingSends == 0) && (pAdapt->PauseEvent != NULL)) + { + NdisSetEvent(pAdapt->PauseEvent); + pAdapt->PauseEvent = NULL; + } + MUX_RELEASE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel); + + + } + + DBGPRINT(MUX_VERY_LOUD,("<== PtSendNBLComplete: ProtocolBindingContext %p, NetBufferLists %p\n",ProtocolBindingContext,NetBufferLists)); +} + +#ifdef IEEE_VLAN_SUPPORT +NDIS_STATUS +PtHandleReceiveTaggingNB( + IN PVELAN pVElan, + IN PNET_BUFFER_LIST NetBufferList, + IN PNDIS_NET_BUFFER_LIST_8021Q_INFO NdisPacket8021qInfo + ) +/*++ + +Routine Description: + + Parse a received Ethernet frame for 802.1Q tag information. + If a tag header is present, copy in relevant field values to + per-packet inforation to the new NET_BUFFER_LIST used + to indicate up this frame. + +Arguments: + pVElan Pointer to the VELAN structure + NetBufferList Pointer to the indicated packet from the lower miniport + NdisPacket8021qInfo 802.1Q tag information + +Return Value: + NDIS_STATUS_SUCCESS + NDIS_STATUS_NOT_ACCEPTED + + +--*/ +{ + PVOID pFrame = NULL; + PVOID pDst; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PRECV_NBL_ENTRY RecvContext; + PVOID Storage; + USHORT UNALIGNED * pTpid; + + + DBGPRINT(MUX_VERY_LOUD,("==> PtHandleReceiveTaggingNB: VElan %p, NetBufferList %p, NdisPacket8021qInfo %p\n",pVElan,NetBufferList, NdisPacket8021qInfo)); + do + { + RecvContext = (PRECV_NBL_ENTRY) NET_BUFFER_LIST_CONTEXT_DATA_START(NetBufferList); + RecvContext->Flags = 0; + + // + // If the vlan ID of the virtual miniport is 0, the miniport should + // act like it doesn't support VELAN tag processing + // + if (MuxRecognizedVlanId(pVElan,0)) + break; + + // Check if Tag header is present + + if (NdisPacket8021qInfo->Value == 0) + { + break; + } + + // + // If E-RIF present then discard header as we do not support + // this variation + // + + if (NdisPacket8021qInfo->TagHeader.CanonicalFormatId != 0) + { + // + // Drop packet + // + + Status = NDIS_STATUS_NOT_ACCEPTED; + MUX_INCR_STATISTICS(&pVElan->RcvFormatErrors); + break; + } + + if ((NdisPacket8021qInfo->TagHeader.VlanId != 0) + && (!MuxRecognizedVlanId(pVElan, NdisPacket8021qInfo->TagHeader.VlanId))) + { + // + // Drop the packet + // + Status = NDIS_STATUS_NOT_ACCEPTED; + + MUX_INCR_STATISTICS(&pVElan->RcvVlanIdErrors); + break; + } + Storage=NULL; + pFrame = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(NetBufferList), + 2 * ETH_LENGTH_OF_ADDRESS + VLAN_TAG_HEADER_SIZE, + Storage, + 1, + 0); + + if (pFrame == NULL) + { + ASSERT(0); + Status = NDIS_STATUS_INVALID_PACKET; + break; + } + + pTpid = (USHORT UNALIGNED *)((PUCHAR)pFrame + 2 * ETH_LENGTH_OF_ADDRESS); + + // + //Strip header only if it's present in the packet + // + if (*pTpid == TPID) + { + RecvContext->Flags |= MUX_ADVANCE_DATA; + + // + // Strip off header + // + pDst = (PVOID)((PUCHAR)pFrame + VLAN_TAG_HEADER_SIZE); + + RtlMoveMemory(pDst, pFrame, 2 * ETH_LENGTH_OF_ADDRESS); + + NET_BUFFER_LIST_INFO(NetBufferList, Ieee8021QNetBufferListInfo) = NdisPacket8021qInfo->Value; + + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(NetBufferList), + VLAN_TAG_HEADER_SIZE, + FALSE, + NULL); + } + + } while(FALSE); + + DBGPRINT(MUX_VERY_LOUD,("<== PtHandleReceiveTaggingNB: VElan %p, NetBufferList %p, NdisPacket8021qInfo %p, Status %8x\n",pVElan,NetBufferList, NdisPacket8021qInfo, Status)); + return Status; +} + +NDIS_STATUS +PtStripVlanTagNB( + IN PNET_BUFFER_LIST NetBufferList, + OUT PNDIS_NET_BUFFER_LIST_8021Q_INFO NdisPacket8021qInfo, + OUT PRECV_NBL_ENTRY RecvContext + ) +/*++ + +Routine Description: + + Parse a received Ethernet frame for 802.1Q tag information. + If a tag header is present, copy in the output parameter + +Arguments: + NetBufferList Pointer to the indicated packet from the lower miniport + NdisPacket8021qInfo Pointer to the VLAN tag information + RecvContext Context for the received NBL + +Return Value: + NDIS_STATUS_SUCCESS + + +--*/ +{ + VLAN_TAG_HEADER UNALIGNED * pTagHeader; + USHORT UNALIGNED * pTpid; + PVOID pFrame = NULL; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PVOID Storage; + + + DBGPRINT(MUX_VERY_LOUD,("==> PtStripVlanTagNB: NetBufferList %p, NdisPacket8021qInfo %p\n",NetBufferList, NdisPacket8021qInfo)); + do + { + NdisPacket8021qInfo->Value = NULL; + Storage=NULL; + pFrame = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(NetBufferList), + 2 * ETH_LENGTH_OF_ADDRESS + VLAN_TAG_HEADER_SIZE, + Storage, + 1, + 0); + + if (pFrame == NULL) + { + ASSERT(0); + Status = NDIS_STATUS_INVALID_PACKET; + break; + } + + // Get at the Ethertype field + + pTpid = (USHORT UNALIGNED *)((PUCHAR)pFrame + 2 * ETH_LENGTH_OF_ADDRESS); + + // Check if Tag header is present + + if (*pTpid != TPID) + { + break; + } + + pTagHeader = (VLAN_TAG_HEADER UNALIGNED *)(pTpid + 1); + + COPY_TAG_INFO_FROM_HEADER_TO_PACKET_INFO(*NdisPacket8021qInfo, pTagHeader); + + RtlCopyMemory((PVOID UNALIGNED) &RecvContext->TagHeader, pTagHeader, 2); + + } while(FALSE); + + DBGPRINT(MUX_VERY_LOUD,("<== PtStripVlanTagNB: NetBufferList %p, NdisPacket8021qInfo %p, Status %8x\n",NetBufferList, NdisPacket8021qInfo, Status)); + return Status; +} + +NDIS_STATUS +PtRestoreReceiveNBL( + IN PNET_BUFFER_LIST NetBufferList + ) +/*++ + +Routine Description: + + Restore the received NBL if the tag header was parsed + +Arguments: + NetBufferList Pointer to the indicated packet from the lower miniport + +Return Value: + NDIS_STATUS_SUCCESS + + +--*/ +{ + PUCHAR pFrame = NULL; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PRECV_NBL_ENTRY ReceiveNblEntry; + USHORT Tpid; + PVOID Storage; + + do + { + ReceiveNblEntry = (PRECV_NBL_ENTRY) NET_BUFFER_LIST_CONTEXT_DATA_START(NetBufferList); + + // + // Check ifthe NBL was modified + // + if ((ReceiveNblEntry->Flags & MUX_ADVANCE_DATA) != MUX_ADVANCE_DATA) + { + break; + } + + // + // Retreat the net buffer list + // + Status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(NetBufferList), + VLAN_TAG_HEADER_SIZE, + 0, + NULL); + + if (Status != NDIS_STATUS_SUCCESS) + { + break; + } + + // + // Find the start address of the frame + // + Storage=NULL; + pFrame = NdisGetDataBuffer(NET_BUFFER_LIST_FIRST_NB(NetBufferList), + 2 * ETH_LENGTH_OF_ADDRESS + VLAN_TAG_HEADER_SIZE, + Storage, + 1, + 0); + + if (pFrame == NULL) + { + ASSERT(0); + Status = NDIS_STATUS_INVALID_PACKET; + break; + } + + // + // Insert the VLAN tag to restore the received frame to the + // original state + // + Tpid = TPID; + + NdisMoveMemory(pFrame, pFrame + VLAN_TAG_HEADER_SIZE, (2 * ETH_LENGTH_OF_ADDRESS)); + + NdisMoveMemory(pFrame + (2 * ETH_LENGTH_OF_ADDRESS), &Tpid, 2); + + NdisMoveMemory(pFrame + (2 * ETH_LENGTH_OF_ADDRESS) + sizeof(Tpid), &ReceiveNblEntry->TagHeader, 2); + + NET_BUFFER_LIST_INFO(NetBufferList, Ieee8021QNetBufferListInfo) = 0; + } + while (FALSE); + + return Status; +} + +#endif + + + diff --git a/network/ndis/mux/driver/60/public.h b/network/ndis/mux/driver/60/public.h new file mode 100644 index 00000000..972939c7 --- /dev/null +++ b/network/ndis/mux/driver/60/public.h @@ -0,0 +1,46 @@ +/*++ +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + public.h + +Abstract: + + This module contains the common declarations shared by driver + and user applications. + +Author: + + +Environment: + + user and kernel +Notes: + + +Revision History: + + +--*/ +#ifndef __PUBLIC_H +#define __PUBLIC_H + +// +// To support ioctls from user-mode: +// + +#define GLOBAL_LINKNAME_STRING L"\\DosDevices\\Global\\Mux" +#define NTDEVICE_STRING L"\\Device\\Mux" + +#define MUX_CUSTOM_EVENT 1 + +#define NOTIFY_SIGNATURE 0xAFCDABAB + +typedef struct _NOTIFY_CUSTOM_EVENT { + ULONG uSignature; + ULONG uEvent; + WCHAR szMiniport[1]; +} NOTIFY_CUSTOM_EVENT, *PNOTIFY_CUSTOM_EVENT; +#endif + diff --git a/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj b/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj new file mode 100644 index 00000000..83e3f351 --- /dev/null +++ b/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj @@ -0,0 +1,299 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {F277EC93-B131-4A8E-8899-C62F31AE70EC} + $(MSBuildProjectName) + Debug + Win32 + {22F44364-DB40-46E1-8E05-EBCB1DDEC8E5} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + muxvlan + + + muxvlan + + + muxvlan + + + muxvlan + + + + %(AdditionalOptions) /GS + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + + + + + %(AdditionalOptions) /GS + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + + + + + %(AdditionalOptions) /GS + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + + + + + %(AdditionalOptions) /GS + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + + + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER + %(PreprocessorDefinitions);NDIS_WDM + %(PreprocessorDefinitions);NDIS60_MINIPORT + %(PreprocessorDefinitions);NDIS60 + %(PreprocessorDefinitions);IEEE_VLAN_SUPPORT=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj.Filters b/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj.Filters new file mode 100644 index 00000000..fad75bc6 --- /dev/null +++ b/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj.Filters @@ -0,0 +1,40 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {1AADD0B1-7A47-42D2-95E3-7F106683CF72} + + + h;hpp;hxx;hm;inl;inc;xsd + {181362AF-397B-4B8F-A43F-8E9D2962680E} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {1E01B05D-68A4-4A5F-B969-3B6FD90223DF} + + + inf;inv;inx;mof;mc; + {966F89E0-3A82-4396-9FD3-3F8EB1BB58D9} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/mux/driver/60/vlan/precompsrc.c b/network/ndis/mux/driver/60/vlan/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/network/ndis/mux/driver/60/vlan/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/network/ndis/mux/mux.sln b/network/ndis/mux/mux.sln new file mode 100644 index 00000000..47d46e28 --- /dev/null +++ b/network/ndis/mux/mux.sln @@ -0,0 +1,66 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Vlan", "Vlan", "{54A94908-2C5F-4A7A-811A-853AE27B7850}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "60", "60", "{0FCA78EE-A888-441E-A320-83951CF296AC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{B3A64960-7B29-4262-9833-F1A493D5CCFC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Novlan", "Novlan", "{3D789463-134E-4028-BC7C-20B1BFA46278}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notifyob", "Notifyob", "{DAE5645C-5ADF-4048-8010-39A47184E5C9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "muxvlan", "driver\60\vlan\muxvlan.vcxproj", "{F277EC93-B131-4A8E-8899-C62F31AE70EC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mux", "driver\60\novlan\mux.vcxproj", "{3316ED07-27E3-4AE6-BE12-E8A5F9ADE789}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mux", "notifyob\mux.vcxproj", "{CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F277EC93-B131-4A8E-8899-C62F31AE70EC}.Debug|Win32.ActiveCfg = Debug|Win32 + {F277EC93-B131-4A8E-8899-C62F31AE70EC}.Debug|Win32.Build.0 = Debug|Win32 + {F277EC93-B131-4A8E-8899-C62F31AE70EC}.Release|Win32.ActiveCfg = Release|Win32 + {F277EC93-B131-4A8E-8899-C62F31AE70EC}.Release|Win32.Build.0 = Release|Win32 + {F277EC93-B131-4A8E-8899-C62F31AE70EC}.Debug|x64.ActiveCfg = Debug|x64 + {F277EC93-B131-4A8E-8899-C62F31AE70EC}.Debug|x64.Build.0 = Debug|x64 + {F277EC93-B131-4A8E-8899-C62F31AE70EC}.Release|x64.ActiveCfg = Release|x64 + {F277EC93-B131-4A8E-8899-C62F31AE70EC}.Release|x64.Build.0 = Release|x64 + {3316ED07-27E3-4AE6-BE12-E8A5F9ADE789}.Debug|Win32.ActiveCfg = Debug|Win32 + {3316ED07-27E3-4AE6-BE12-E8A5F9ADE789}.Debug|Win32.Build.0 = Debug|Win32 + {3316ED07-27E3-4AE6-BE12-E8A5F9ADE789}.Release|Win32.ActiveCfg = Release|Win32 + {3316ED07-27E3-4AE6-BE12-E8A5F9ADE789}.Release|Win32.Build.0 = Release|Win32 + {3316ED07-27E3-4AE6-BE12-E8A5F9ADE789}.Debug|x64.ActiveCfg = Debug|x64 + {3316ED07-27E3-4AE6-BE12-E8A5F9ADE789}.Debug|x64.Build.0 = Debug|x64 + {3316ED07-27E3-4AE6-BE12-E8A5F9ADE789}.Release|x64.ActiveCfg = Release|x64 + {3316ED07-27E3-4AE6-BE12-E8A5F9ADE789}.Release|x64.Build.0 = Release|x64 + {CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6}.Debug|Win32.ActiveCfg = Debug|Win32 + {CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6}.Debug|Win32.Build.0 = Debug|Win32 + {CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6}.Release|Win32.ActiveCfg = Release|Win32 + {CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6}.Release|Win32.Build.0 = Release|Win32 + {CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6}.Debug|x64.ActiveCfg = Debug|x64 + {CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6}.Debug|x64.Build.0 = Debug|x64 + {CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6}.Release|x64.ActiveCfg = Release|x64 + {CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F277EC93-B131-4A8E-8899-C62F31AE70EC} = {54A94908-2C5F-4A7A-811A-853AE27B7850} + {3316ED07-27E3-4AE6-BE12-E8A5F9ADE789} = {3D789463-134E-4028-BC7C-20B1BFA46278} + {CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6} = {DAE5645C-5ADF-4048-8010-39A47184E5C9} + {54A94908-2C5F-4A7A-811A-853AE27B7850} = {0FCA78EE-A888-441E-A320-83951CF296AC} + {0FCA78EE-A888-441E-A320-83951CF296AC} = {B3A64960-7B29-4262-9833-F1A493D5CCFC} + {3D789463-134E-4028-BC7C-20B1BFA46278} = {0FCA78EE-A888-441E-A320-83951CF296AC} + EndGlobalSection +EndGlobal diff --git a/network/ndis/mux/notifyob/adapter.cpp b/network/ndis/mux/notifyob/adapter.cpp new file mode 100644 index 00000000..ea964c7a --- /dev/null +++ b/network/ndis/mux/notifyob/adapter.cpp @@ -0,0 +1,965 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: A D A P T E R . C P P +// +// Contents: Physical adapter class definition. +// +// Notes: +// +// Author: Alok Sinha +// +//---------------------------------------------------------------------------- + +#include "adapter.h" +#include "common.h" + +#ifdef CUSTOM_EVENTS +#include "public.h" +#endif + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::CMuxPhysicalAdapter +// +// Purpose: Constructor for class CMuxPhysicalAdapter +// +// Arguments: None +// +// Returns: +// +// Notes: +// + +CMuxPhysicalAdapter::CMuxPhysicalAdapter (INetCfg *pnc, + GUID *pguidAdapter) +{ + + TraceMsg( L"-->CMuxPhysicalAdapter::CMuxPhysicalAdapter(Constructor).\n" ); + + m_pnc = pnc; + m_pnc->AddRef(); + + CopyMemory( &m_guidAdapter, + pguidAdapter, + sizeof(GUID) ); + + TraceMsg( L"<--CMuxPhysicalAdapter::CMuxPhysicalAdapter(Constructor).\n" ); +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::~CMuxPhysicalAdapter +// +// Purpose: Destructor for class CMuxPhysicalAdapter +// +// Arguments: None +// +// Returns: +// +// Notes: +// + +CMuxPhysicalAdapter::~CMuxPhysicalAdapter (VOID) +{ + CMuxVirtualMiniport *pMiniport; + DWORD dwMiniportCount; + DWORD i; + + + TraceMsg( L"-->CMuxPhysicalAdapter::~CMuxPhysicalAdapter(Destructor).\n" ); + + // + // Delete all the instances representing the virtual miniports. + // We are only deleting the class instances, not uninstalling the + // the virtual miniports. + // + + dwMiniportCount = m_MiniportList.ListCount(); + + for (i=0; i < dwMiniportCount; ++i) { + + pMiniport = NULL; + m_MiniportList.Remove( &pMiniport ); + delete pMiniport; + } + + dwMiniportCount = m_MiniportsToAdd.ListCount(); + + for (i=0; i < dwMiniportCount; ++i) { + + pMiniport = NULL; + m_MiniportsToAdd.Remove( &pMiniport ); + delete pMiniport; + } + + dwMiniportCount = m_MiniportsToRemove.ListCount(); + + for (i=0; i < dwMiniportCount; ++i) { + + pMiniport = NULL; + m_MiniportsToRemove.Remove( &pMiniport ); + delete pMiniport; + } + + ReleaseObj( m_pnc ); + + TraceMsg( L"<--CMuxPhysicalAdapter::~CMuxPhysicalAdapter(Destructor).\n" ); + +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::LoadConfiguration +// +// Purpose: Read the registry to get the device IDs of the +// virtual miniports installed on the adapter and +// crate an instance to represent each virtual miniport. +// +// Arguments: None +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT CMuxPhysicalAdapter::LoadConfiguration (VOID) +{ + HKEY hkeyAdapterGuid; + WCHAR szAdapterGuidKey[MAX_PATH+1]; + WCHAR szAdapterGuid[MAX_PATH+1]; + LPWSTR lpMiniportList; + LPWSTR lpMiniport; + LPWSTR lpMiniportGuid; + DWORD dwDisp; + CMuxVirtualMiniport *pMiniport; + GUID guidMiniport; + DWORD dwBytes; + LONG lResult; + + TraceMsg( L"-->CMuxPhysicalAdapter::LoadConfiguration.\n" ); + + // + // Build the registry key using the adapter guid under which + // device IDs of the virtual miniports are stored. + // + + StringFromGUID2( m_guidAdapter, + szAdapterGuid, + MAX_PATH+1 ); + + StringCchPrintfW ( szAdapterGuidKey, + celems(szAdapterGuidKey), + L"%s\\%s", + c_szAdapterList, + szAdapterGuid ); + + szAdapterGuidKey[MAX_PATH]='\0'; + lResult = RegCreateKeyExW( HKEY_LOCAL_MACHINE, + szAdapterGuidKey, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hkeyAdapterGuid, + &dwDisp); + + if ( lResult == ERROR_SUCCESS ) { + + // + // If dwDisp indicates that a new key is created then, we know there + // is no virtual miniport currently listed underneath and we simply + // return. + // + + if ( dwDisp != REG_CREATED_NEW_KEY ) { + + dwBytes = 0; + lResult = RegQueryValueExW( + hkeyAdapterGuid, + c_szUpperBindings, + NULL, + NULL, + NULL, + &dwBytes ); + if(lResult != ERROR_SUCCESS){ + // you may do something + } + + + + //alloc lpMiniportList with dwBytes elements and initialzes each to be 0 + lpMiniportList = (LPWSTR)calloc( dwBytes, 1 ); + + if ( lpMiniportList != NULL ) { + + lResult = RegQueryValueExW( + hkeyAdapterGuid, + c_szUpperBindings, + NULL, + NULL, + (LPBYTE)lpMiniportList, + &dwBytes ); + + if ( lResult == ERROR_SUCCESS ) { + + lpMiniport = lpMiniportList; + + +#ifndef PASSTHRU_NOTIFY + + // + // In case of mux, c_szUpperBindings is a REG_MULTI_SZ string. + // + + lpMiniport[dwBytes-1] = '\0'; + while ( wcslen(lpMiniport) ) { + + lpMiniportGuid = RemoveDevicePrefix( lpMiniport ); + + TraceMsg( L" Loading configuration for miniport %s...\n", + lpMiniportGuid ); + + if ( lpMiniportGuid != NULL ) { + + CLSIDFromString( lpMiniportGuid, + &guidMiniport ); + + // + // Create an instance representing the virtual miniport. + // + + #pragma prefast(suppress:8197, "The instance is freed in the destructor") + pMiniport = new CMuxVirtualMiniport( m_pnc, + &guidMiniport, + &m_guidAdapter ); + + if ( pMiniport ) { + + // + // Load any miniport specific configuration. + // + + pMiniport->LoadConfiguration(); + + // + // Save the miniport instance in a list. + // + + m_MiniportList.Insert( pMiniport, + guidMiniport ); + + } + + free( lpMiniportGuid ); + } + + // + // Get next miniport guid. + // + + lpMiniport += wcslen(lpMiniport) + 1; + } + +#else + + // + // In case of the passthru driver, c_szUpperBindings is + // a reg_sz string. + // + + lpMiniportGuid = RemoveDevicePrefix( lpMiniport ); + + TraceMsg( L" Loading configuration for miniport %s...\n", + lpMiniportGuid ); + + if ( lpMiniportGuid ) { + + CLSIDFromString( lpMiniportGuid, + &guidMiniport ); + + // + // Create an instance representing the virtual miniport. + // + + pMiniport = new CMuxVirtualMiniport( m_pnc, + &guidMiniport, + &m_guidAdapter ); + + if ( pMiniport ) { + + // + // Load any miniport specific configuration. + // + + pMiniport->LoadConfiguration(); + + // + // Save the miniport instance in a list. + // + + m_MiniportList.Insert( pMiniport, + guidMiniport ); + } + + free( lpMiniportGuid ); + } +#endif + } + else { + TraceMsg( L" Failed to read the registry value: %s.\n", + c_szUpperBindings ); + } + + free( lpMiniportList ); + } + else { + lResult = ERROR_NOT_ENOUGH_MEMORY; + } + } + + RegCloseKey( hkeyAdapterGuid ); + } + else { + + TraceMsg( L" Failed to open the registry key: %s.\n", + szAdapterGuidKey ); + } + + TraceMsg( L"<--CMuxPhysicalAdapter::LoadConfiguration(HRESULT = %x).\n", + HRESULT_FROM_WIN32(lResult) ); + + return HRESULT_FROM_WIN32(lResult); +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::GetAdapterGUID +// +// Purpose: Returns the adapter GUID. +// +// Arguments: +// OUT pguidAdapter: GUID of the adapter returned. +// +// Returns: None. +// +// Notes: +// + +VOID CMuxPhysicalAdapter::GetAdapterGUID (GUID *pguidAdapter) +{ + TraceMsg( L"-->CMuxPhysicalAdapter::GetAdapterGUID.\n" ); + + CopyMemory( pguidAdapter, + &m_guidAdapter, + sizeof(GUID) ); + + TraceMsg( L"<--CMuxPhysicalAdapter::GetAdapterGUID.\n" ); +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::AddMiniport +// +// Purpose: Puts the miniport instance into the list of newly added miniports. +// +// Arguments: +// IN pMiniport: A newly create miniport instance. +// +// Returns: S_OK on success, otherwize and error code. +// +// Notes: +// + +HRESULT CMuxPhysicalAdapter::AddMiniport (CMuxVirtualMiniport *pMiniport) +{ + GUID guidMiniport; + HRESULT hr; + + TraceMsg( L"-->CMuxPhysicalAdapter::AddMiniport.\n" ); + + pMiniport->GetMiniportGUID( &guidMiniport ); + + hr = m_MiniportsToAdd.Insert( pMiniport, + guidMiniport ); + + TraceMsg( L"<--CMuxPhysicalAdapter::AddMiniport(HRESULT = %x).\n", + hr ); + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::RemoveMiniport +// +// Purpose: Remove a specified miniport instance from the list and +// uninstalls the corresponding virtual miniport. +// +// Arguments: +// IN pguidMiniportToRemove: GUID of the miniport to be removed +// and uninstalled. If it is NULL then, +// the first miniport instance is removed. +// +// Returns: S_OK on success, otherwize and error code. +// +// Notes: +// + +HRESULT CMuxPhysicalAdapter::RemoveMiniport (GUID *pguidMiniportToRemove) +{ + CMuxVirtualMiniport *pMiniport; + GUID guidMiniport; + HRESULT hr; + + TraceMsg( L"-->CMuxPhysicalAdapter::RemoveMiniport.\n" ); + + // + // If miniport GUID specified then, delete that one. + // + + if ( pguidMiniportToRemove ) { + + hr = m_MiniportList.RemoveByKey( *pguidMiniportToRemove, + &pMiniport ); + } + else { + + // + // No GUID specified, so we just delete the first one. + // + + hr = m_MiniportList.Remove( &pMiniport ); + } + + if ( hr == S_OK ) { + + pMiniport->GetMiniportGUID( &guidMiniport ); + + m_MiniportsToRemove.Insert( pMiniport, + guidMiniport ); + pMiniport->DeInstall(); + } + + TraceMsg( L"<--CMuxPhysicalAdapter::RemoveMiniport(HRESULT = %x).\n", + hr ); + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::Remove +// +// Purpose: Uninstall all the instances of virtual miniports. +// +// Arguments: None +// +// Returns: S_OK. +// +// Notes: +// + +HRESULT CMuxPhysicalAdapter::Remove (VOID) +{ + CMuxVirtualMiniport *pMiniport = NULL; + GUID guidMiniport; + DWORD dwMiniportCount; + DWORD i; + + TraceMsg( L"-->CMuxPhysicalAdapter::Remove.\n" ); + + dwMiniportCount = m_MiniportList.ListCount(); + + TraceMsg ( L" Removing %d miniports.\n", + dwMiniportCount ); + + for (i=0; i < dwMiniportCount; ++i) { + + pMiniport = NULL; + m_MiniportList.Remove( &pMiniport ); + pMiniport->GetMiniportGUID( &guidMiniport ); + m_MiniportsToRemove.Insert( pMiniport, + guidMiniport ); + pMiniport->DeInstall(); + } + + TraceMsg( L"<--CMuxPhysicalAdapter::Remove(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::ApplyRegistryChanges +// +// Purpose: Update the registry depending on the actions performed. +// +// Arguments: +// IN eApplyAction: Action that was last performed. +// +// +// Returns: S_OK. +// +// Notes: +// More than one action could have been performed by the user +// but this function is called only once at the end. So, the argument +// only denotes the very last action performed. For example, if the +// user deletes one miniport and adds two miniports then, the argument +// will denote an add action. +// + +HRESULT CMuxPhysicalAdapter::ApplyRegistryChanges (ConfigAction eApplyAction) +{ + HKEY hkeyAdapterList; + HKEY hkeyAdapterGuid; + WCHAR szAdapterGuid[MAX_PATH+1]; + CMuxVirtualMiniport *pMiniport = NULL; + DWORD dwMiniportCount; + DWORD dwDisp; + DWORD i; + LONG lResult; + HRESULT hr; + + + TraceMsg( L"-->CMuxPhysicalAdapter::ApplyRegistryChanges.\n" ); + + // + // Open/create and then close the registry key to ensure that it does exist. + // + + StringFromGUID2( m_guidAdapter, + szAdapterGuid, + MAX_PATH+1 ); + + lResult = RegCreateKeyExW( HKEY_LOCAL_MACHINE, + c_szAdapterList, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hkeyAdapterList, + &dwDisp); + + + if ( lResult == ERROR_SUCCESS ) { + + lResult = RegCreateKeyExW( hkeyAdapterList, + szAdapterGuid, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hkeyAdapterGuid, + &dwDisp); + + if ( lResult == ERROR_SUCCESS ) { + + RegCloseKey( hkeyAdapterGuid ); + } + else { + TraceMsg( L" Failed to create/open the registry key: %s\\%s.\n", + c_szAdapterList, szAdapterGuid ); + } + + RegCloseKey( hkeyAdapterList ); + } + else { + + TraceMsg( L" Failed to open the registry key: %s.\n", + c_szAdapterList ); + } + + // + // Update the registry in case there were new miniports installed. + // + + hr = HRESULT_FROM_WIN32( lResult ); + if( hr != S_OK){ + // you may do something + } + + dwMiniportCount = m_MiniportsToAdd.ListCount(); + + TraceMsg( L" Applying registry changes when %d miniports added.\n", + dwMiniportCount ); + + for (i=0; i < dwMiniportCount; ++i) { + + m_MiniportsToAdd.Find( i, + &pMiniport ); + + // + // Do virtual miniport specific registry changes. + // + // We need to tell the miniport instance explicitly what the action + // is. + // + + hr = pMiniport->ApplyRegistryChanges( eActAdd ); + + if ( hr != S_OK ) { + + TraceMsg( L" Failed to apply registry changes to miniport(%d).\n", + i ); + + } + } + + + + // + // Update the registry in case one or more miniports were uninstalled. + // + + dwMiniportCount = m_MiniportsToRemove.ListCount(); + + TraceMsg( L" Applying registry changes when %d miniports removed.\n", + dwMiniportCount ); + + for (i=0; i < dwMiniportCount; ++i) { + + m_MiniportsToRemove.Find( i, + &pMiniport ); + + // + // Do virtual miniport specific registry changes. + // + // We need to tell the miniport instance explicitly what the action + // is. + // + + hr = pMiniport->ApplyRegistryChanges( eActRemove ); + + if ( hr != S_OK ) { + + TraceMsg( L" Failed to apply registry changes to miniport(%d).\n", + i ); + + } + } + + // + // If the adapter is being removed or the protocol is being uninstalled, + // delete the adatper registry key. + // + + if ( eApplyAction == eActRemove ) { + + // + // Delete the adapter key. + // + + lResult = RegCreateKeyExW( HKEY_LOCAL_MACHINE, + c_szAdapterList, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hkeyAdapterList, + &dwDisp); + + if ( lResult == ERROR_SUCCESS ) { + + TraceMsg( L" Deleting the registry key: %s.\n", szAdapterGuid ); + + RegDeleteKeyW( hkeyAdapterList, szAdapterGuid ); + RegCloseKey ( hkeyAdapterList ); + } + } + + TraceMsg( L"<--CMuxPhysicalAdapter::ApplyRegistryChanges(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::ApplyPnpChanges +// +// Purpose: Apply the PnP changes depending on the actions performed. +// +// Arguments: +// IN pfCallback : SendPnpConfig Callback interface. +// IN eApplyAction: Action that was last performed. +// +// +// Returns: S_OK. +// +// Notes: +// More than one action could have been performed by the user +// but this function is called only once at the end. So, the argument +// only denotes the very last action performed. For example, if the +// user deletes one miniport and adds two miniports then, the argument +// will denote an add action. +// + +HRESULT CMuxPhysicalAdapter::ApplyPnpChanges( + INetCfgPnpReconfigCallback *pfCallback, + ConfigAction eApplyAction) +{ + CMuxVirtualMiniport *pMiniport = NULL; + GUID guidMiniport; + DWORD dwMiniportCount; + DWORD i; + HRESULT hr; + +#ifdef CUSTOM_EVENTS + LPWSTR lpDevice; + WCHAR szMiniportGuid[MAX_PATH+1]; + DWORD dwBytes; + INetCfgComponent *pncc; + LPWSTR lpszBindName; + PNOTIFY_CUSTOM_EVENT lppnpEvent; +#endif + + UNREFERENCED_PARAMETER(eApplyAction); + TraceMsg( L"-->CMuxPhysicalAdapter::ApplyPnpChanges.\n" ); + +#ifdef CUSTOM_EVENTS + + // + // Find the instance of the adapter to get its bindname. + // + + hr = HrFindInstance( m_pnc, + m_guidAdapter, + &pncc ); + + if ( hr == S_OK ) { + + hr = pncc->GetBindName( &lpszBindName ); + + if ( hr != S_OK ) { + TraceMsg( L" GetBindName failed.(HRESULT = %x). PnP changes will not " + L"be applied and the driver will not be notified.\n", + hr ); + } + + ReleaseObj( pncc ); + } + else { + TraceMsg( L" PnP changes will not " + L"be applied and the driver will not be notified.\n", + hr ); + } + +#endif + + dwMiniportCount = m_MiniportsToAdd.ListCount(); + + TraceMsg( L" Applying PnP changes to %d new miniports.\n", + dwMiniportCount ); + + for (i=0; i < dwMiniportCount; ++i) { + + pMiniport = NULL; + m_MiniportsToAdd.Remove( &pMiniport ); + + pMiniport->GetMiniportGUID( &guidMiniport ); + + m_MiniportList.Insert( pMiniport, + guidMiniport ); + + // + // Do miniport specific Pnp Changes when they are added. + // + + hr = pMiniport->ApplyPnpChanges( pfCallback, + eActAdd ); + if( hr != S_OK){ + // you may do something + } + +#ifdef CUSTOM_EVENTS + + + // + // Notify the driver that one or more virtual miniports have been added. + // + + StringFromGUID2( guidMiniport, + szMiniportGuid, + MAX_PATH+1 ); + lpDevice = AddDevicePrefix( szMiniportGuid ); + + if ( lpDevice ) { + + dwBytes = sizeof(NOTIFY_CUSTOM_EVENT) + + ((wcslen(lpDevice) + 1) * sizeof(WCHAR)); + + lppnpEvent = (PNOTIFY_CUSTOM_EVENT)malloc( dwBytes ); + + if ( lppnpEvent ) { + + lppnpEvent->uSignature = NOTIFY_SIGNATURE; + lppnpEvent->uEvent = MUX_CUSTOM_EVENT; + wcscpy( lppnpEvent->szMiniport, + lpDevice ); + + hr = pfCallback->SendPnpReconfig( NCRL_NDIS, + c_szMuxService, + lpszBindName, + (PVOID)lppnpEvent, + dwBytes ); + + TraceMsg( L" INetCfgPnpReconfigCallback->SendPnpReconfig returned " + L"%#x.\n", + hr ); + + if ( hr != S_OK ) { + + TraceMsg( L" Failed to apply Pnp changes, miniport(%d).\n", + i ); + + } + + free( lppnpEvent ); + } + free( lpDevice ); + } +#endif + } + + dwMiniportCount = m_MiniportsToRemove.ListCount(); + + TraceMsg( L" Applying PnP changes to %d removed miniports.\n", + dwMiniportCount ); + + for (i=0; i < dwMiniportCount; ++i) { + + pMiniport = NULL; + m_MiniportsToRemove.Remove( &pMiniport ); + + pMiniport->GetMiniportGUID( &guidMiniport ); + + // + // Do miniport specific Pnp Changes when they are uninstalled. + // + + hr = pMiniport->ApplyPnpChanges( pfCallback, + eActRemove ); + if( hr != S_OK){ + // you may do something + } + + delete pMiniport; + +#ifdef CUSTOM_EVENTS + + // + // Notify the driver that one or more virtual miniports have been + // uninstalled. + // + // We can't notify the driver in case the adapter or the protocol is + // being uninstalled because the binding handle doesn't exist. + // + + if ( eApplyAction != eActRemove ) { + + StringFromGUID2( guidMiniport, + szMiniportGuid, + MAX_PATH+1 ); + lpDevice = AddDevicePrefix( szMiniportGuid ); + + if ( lpDevice ) { + + dwBytes = sizeof(NOTIFY_CUSTOM_EVENT) + + ((wcslen(lpDevice) + 1) * sizeof(WCHAR)); + + lppnpEvent = (PNOTIFY_CUSTOM_EVENT)malloc( dwBytes ); + + if ( lppnpEvent ) { + + lppnpEvent->uSignature = NOTIFY_SIGNATURE; + lppnpEvent->uEvent = MUX_CUSTOM_EVENT; + wcscpy( lppnpEvent->szMiniport, + lpDevice ); + + hr = pfCallback->SendPnpReconfig( NCRL_NDIS, + c_szMuxService, + lpszBindName, + (PVOID)lppnpEvent, + dwBytes ); + TraceMsg( L" INetCfgPnpReconfigCallback->SendPnpReconfig returned " + L"%#x.\n", + hr ); + + if ( hr != S_OK ) { + + TraceMsg( L" Failed to apply Pnp changes, miniport(%d).\n", + i ); + + } + + free( lppnpEvent ); + } + + free( lpDevice ); + } + } +#endif + + } + +#ifdef CUSTOM_EVENTS + CoTaskMemFree( lpszBindName ); +#endif + + TraceMsg( L"<--CMuxPhysicalAdapter::ApplyPnpChanges(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::CancelChanges +// +// Purpose: Cancel any changes made. +// +// Arguments: None +// +// +// Returns: S_OK. +// +// Notes: +// + +HRESULT CMuxPhysicalAdapter::CancelChanges (VOID) +{ + TraceMsg( L"-->CMuxPhysicalAdapter::CancelChanges.\n" ); + + TraceMsg( L"<--CMuxPhysicalAdapter::CancelChanges(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxPhysicalAdapter::AllMiniportsRemoved +// +// Purpose: Find out if there is no miniport installed on the adapter. +// +// Arguments: None +// +// +// Returns: TRUE if all the miniports associated with this adapter have been +// uninstalled and there is none pending to be added, otherwise FALSE. +// +// Notes: +// + +BOOL CMuxPhysicalAdapter::AllMiniportsRemoved (VOID) +{ + return (m_MiniportList.ListCount() + m_MiniportsToAdd.ListCount()) == 0; +} diff --git a/network/ndis/mux/notifyob/adapter.h b/network/ndis/mux/notifyob/adapter.h new file mode 100644 index 00000000..9bf5614f --- /dev/null +++ b/network/ndis/mux/notifyob/adapter.h @@ -0,0 +1,99 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: A D A P T E R . H +// +// Contents: Header file for physical adapter class. +// +// Notes: +// +// Author: Alok Sinha 31-Oct-2000 +// +//---------------------------------------------------------------------------- + +#ifndef ADAPTER_H_INCLUDED + +#define ADAPTER_H_INCLUDED + +#include +#include + +#include "common.h" +#include "virtual.h" +#include "list.h" + +// +// Class to represent a physical adapter. +// + +class CMuxPhysicalAdapter +{ + // + // Private member variables. + // + + GUID m_guidAdapter; + + INetCfg *m_pnc; + + // + // List of virtual miniports associated with the adapter. + // + + List m_MiniportList; + + // + // List of virtual miniports to be added. + // + + List m_MiniportsToAdd; + + // + // List of virtual miniports to be removed. + // + + List m_MiniportsToRemove; + + // + // Private member functions. + // + + public: + + // + // Public member functions + // + + CMuxPhysicalAdapter (INetCfg *pnc, + GUID *guidAdapter); + + virtual ~CMuxPhysicalAdapter (VOID); + + HRESULT LoadConfiguration (VOID); + + VOID GetAdapterGUID (GUID *guidAdapter); + + HRESULT AddMiniport (CMuxVirtualMiniport *pNewMiniport); + + HRESULT RemoveMiniport (GUID *pguidMiniport); + + HRESULT Remove (VOID); + + HRESULT ApplyRegistryChanges (ConfigAction eApplyAction); + + HRESULT ApplyPnpChanges (INetCfgPnpReconfigCallback *pfCallback, + ConfigAction eApplyAction); + + HRESULT CancelChanges (VOID); + + DWORD MiniportCount (VOID) { return m_MiniportList.ListCount(); } + + DWORD MiniportAddCount (VOID) { return m_MiniportsToAdd.ListCount(); } + + BOOL AllMiniportsRemoved (VOID); +}; + + +#endif // ADAPTER_H_INCLUDED diff --git a/network/ndis/mux/notifyob/common.cpp b/network/ndis/mux/notifyob/common.cpp new file mode 100644 index 00000000..24c7b486 --- /dev/null +++ b/network/ndis/mux/notifyob/common.cpp @@ -0,0 +1,624 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: COMMON.CPP +// +// Contents: Debug & Utility functions +// +// Notes: +// +// Author: Alok Sinha +// +//---------------------------------------------------------------------------- + +#include +#include +#include +#include "common.h" + +#ifdef DBG + +void +TraceMsg ( + _In_ LPWSTR szFormat, + ...) +{ + static WCHAR szTempBuf[4096]; + + va_list arglist; + + va_start(arglist, szFormat); + + StringCchVPrintfW ( szTempBuf, + celems(szTempBuf), + szFormat, + arglist ); + + OutputDebugStringW( szTempBuf ); + + va_end(arglist); +} + +void DumpChangeFlag (DWORD dwChangeFlag) +{ + TraceMsg( L" ChangeFlag:" ); + + if ( dwChangeFlag & NCN_ADD ) { + TraceMsg( L" NCN_ADD" ); + } + + if ( dwChangeFlag & NCN_REMOVE ) { + TraceMsg( L" NCN_REMOVE" ); + } + + if ( dwChangeFlag & NCN_UPDATE ) { + TraceMsg( L" NCN_UPDATE" ); + } + + if ( dwChangeFlag & NCN_ENABLE ) { + TraceMsg( L" NCN_ENABLE" ); + } + + if ( dwChangeFlag & NCN_DISABLE ) { + TraceMsg( L" NCN_DISABLE" ); + } + + if ( dwChangeFlag & NCN_BINDING_PATH ) { + TraceMsg( L" NCN_BINDING_PATH" ); + } + + if ( dwChangeFlag & NCN_PROPERTYCHANGE ) { + TraceMsg( L" NCN_PROPERTYCHANGE" ); + } + + if ( dwChangeFlag & NCN_NET ) { + TraceMsg( L" NCN_NET" ); + } + + if ( dwChangeFlag & NCN_NETTRANS ) { + TraceMsg( L" NCN_NETTRANS" ); + } + + if ( dwChangeFlag & NCN_NETCLIENT ) { + TraceMsg( L" NCN_NETCLIENT" ); + } + + if ( dwChangeFlag & NCN_NETSERVICE ) { + TraceMsg( L" NCN_NETSERVICE" ); + } + + TraceMsg( L"\n" ); + return; +} + +void DumpBindingPath (INetCfgBindingPath *pncbp) +{ + LPWSTR lpsz; + HRESULT hr; + +#ifdef VERBOSE_TRACE + INetCfgComponent *pncc; + IEnumNetCfgBindingInterface *pencbi; + INetCfgBindingInterface *pncbi; + DWORD dwIndex; + ULONG ulCount; +#endif + + hr = pncbp->GetPathToken( &lpsz ); + + if ( hr == S_OK ) { + + TraceMsg( L" BindingPath: %s\n", + lpsz ); + + CoTaskMemFree( lpsz ); + } + else { + + TraceMsg( L" BindingPath: GetPathToken failed(HRESULT %x).\n", + hr ); + } + +#ifdef VERBOSE_TRACE + + hr = pncbp->EnumBindingInterfaces( &pencbi ); + + if ( hr == S_OK ) { + + hr = pencbi ->Next( 1, &pncbi, &ulCount ); + + for (dwIndex=0; hr == S_OK; dwIndex++ ) { + + hr = pncbi->GetName( &lpsz ); + + if ( hr == S_OK ) { + + TraceMsg( L" BindingInterface(%d): %s\n", + dwIndex, lpsz ); + + CoTaskMemFree( lpsz ); + } + else { + + TraceMsg( L" BindingInterface(%d): GetName failed(HRESULT %x).\n", + dwIndex, hr ); + } + + hr = pncbi->GetUpperComponent( &pncc ); + + if ( hr == S_OK ) { + + TraceMsg( L" \tUpperComponent of the interface(%d)...\n", + dwIndex ); + + DumpComponent( pncc ); + + ReleaseObj( pncc ); + } + else { + + TraceMsg( L" UpperComponent: GetUpperComponent failed(HRESULT = %x).\n", + hr ); + } + hr = pncbi->GetLowerComponent( &pncc ); + + if ( hr == S_OK ) { + + TraceMsg( L" \tLowerComponent of the interface(%d)...\n", + dwIndex ); + DumpComponent( pncc ); + + ReleaseObj( pncc ); + } + else { + + TraceMsg( L" LowerComponent: GetLowerComponent failed(HRESULT = %x).\n", + hr ); + } + + ReleaseObj( pncbi ); + + hr = pencbi ->Next( 1, + &pncbi, + &ulCount ); + } + + ReleaseObj( pencbi ); + } + else { + + TraceMsg( L" EnumBindingInterfaces failed, (HRESULT = %x)\n", + hr ); + } +#endif + return; +} + +void DumpComponent (INetCfgComponent *pncc) +{ + LPWSTR lpsz; + DWORD dwChars; + ULONG ulStatus; + HRESULT hr; + hr = pncc->GetDisplayName( &lpsz ); + + if ( hr == S_OK ) { + + TraceMsg( L" \t\tComponent: %s\n", + lpsz ); + + CoTaskMemFree( lpsz ); + } + else { + + TraceMsg( L" GetDisplay failed(HRESULT %x).\n", + hr ); + } + + hr = pncc->GetCharacteristics( &dwChars ); + + if ( hr == S_OK ) { + + TraceMsg( L" \t\tCharacteristics:" ); + + if ( dwChars & NCF_HIDDEN ) { + TraceMsg( L" NCF_HIDDEN" ); + } + + if ( dwChars & NCF_NO_SERVICE ) { + TraceMsg( L" NCF_NO_SERVICE" ); + } + + if ( dwChars & NCF_VIRTUAL ) { + TraceMsg( L" NCF_VIRTUAL" ); + } + + if ( dwChars & NCF_PHYSICAL ) { + TraceMsg( L" NCF_PHYSICAL" ); + } + + if ( dwChars & NCF_FILTER ) { + TraceMsg( L" NCF_FILTER" ); + } + + if ( dwChars & NCF_NOT_USER_REMOVABLE ) { + TraceMsg( L" NCF_NOT_USER_REMOVABLE" ); + } + + if ( dwChars & NCF_HAS_UI ) { + TraceMsg( L" NCF_HAS_UI" ); + } + + if ( dwChars & NCF_SOFTWARE_ENUMERATED ) { + TraceMsg( L" NCF_SOFTWARE_ENUMERATED" ); + } + + if ( dwChars & NCF_MULTIPORT_INSTANCED_ADAPTER ) { + TraceMsg( L" NCF_MULTIPORT_INSTANCED_ADAPTER" ); + } + + TraceMsg( L"\n" ); + } + else { + + TraceMsg( L" GetCharacteristics failed(HRESULT %x).\n", + hr ); + } + + hr = pncc->GetId( &lpsz ); + + if ( hr == S_OK ) { + + TraceMsg( L" \t\tHardware Id: %s\n", + lpsz ); + + CoTaskMemFree( lpsz ); + } + else { + + TraceMsg( L" GetId failed(HRESULT %x).\n", + hr ); + } + + hr = pncc->GetBindName( &lpsz ); + + if ( hr == S_OK ) { + + TraceMsg( L" \t\tBindName: %s\n", + lpsz ); + + CoTaskMemFree( lpsz ); + } + else { + + TraceMsg( L" GetBindName failed(HRESULT %x).\n", + hr ); + } + + if ( dwChars & NCF_PHYSICAL ) { + hr = pncc->GetDeviceStatus( &ulStatus ); + + if ( hr == S_OK ) { + + TraceMsg( L" \t\tDeviceStatus: %#x\n", + ulStatus ); + } + else { + + TraceMsg( L" GetDeviceStatus failed(HRESULT %x).\n", + hr ); + } + } + + return; +} + +#endif + +HRESULT HrFindInstance (INetCfg *pnc, + GUID &guidInstance, + INetCfgComponent **ppnccMiniport) +{ + IEnumNetCfgComponent *pencc; + INetCfgComponent *pncc; + GUID guid; + WCHAR szGuid[MAX_PATH+1]; + ULONG ulCount; + BOOL found; + HRESULT hr; + + TraceMsg( L"-->HrFindInstance.\n" ); + + hr = pnc->EnumComponents( &GUID_DEVCLASS_NET, + &pencc ); + + if ( hr == S_OK ) { + + StringFromGUID2( guidInstance, + szGuid, + MAX_PATH+1 ); + + TraceMsg( L" Looking for component with InstanceGuid %s\n", + szGuid ); + + hr = pencc->Next( 1, + &pncc, + &ulCount ); + + for ( found=FALSE; (hr == S_OK) && (found == FALSE); ) { + + hr = pncc->GetInstanceGuid( &guid ); + + if ( hr == S_OK ) { + + StringFromGUID2( guid, + szGuid, + MAX_PATH+1 ); + + TraceMsg( L" Found component with InstanceGuid %s\n", + szGuid ); + + found = IsEqualGUID( guid, + guidInstance ); + + if ( found == FALSE ) { + + ReleaseObj( pncc ); + + hr = pencc->Next( 1, + &pncc, + &ulCount ); + } + else { + *ppnccMiniport = pncc; + } + } + } + + ReleaseObj( pencc ); + } + else { + + TraceMsg( L" EnumComponents failed(HRESULT = %x).\n", + hr ); + } + + TraceMsg( L"<--HrFindInstance(HRESULT = %x).\n", + hr ); + + return hr; +} + +LONG +AddToMultiSzValue ( + HKEY hkeyAdapterGuid, + _In_ LPWSTR szMiniportGuid) +{ + LPWSTR lpCurrentValue=NULL; + LPWSTR lpNewValue=NULL; + DWORD dwLen; + DWORD dwNewLen; + LONG lResult; + + dwLen = 0; + lResult = RegQueryValueExW( + hkeyAdapterGuid, + c_szUpperBindings, + NULL, + NULL, + NULL, + &dwLen ); + if( lResult != ERROR_SUCCESS ){ + //you may do something + } + + if ( dwLen != 0 ) { + lpCurrentValue = (LPWSTR)calloc( dwLen, 1 ); + + if ( lpCurrentValue ) { + + lResult = RegQueryValueExW( hkeyAdapterGuid, + c_szUpperBindings, + NULL, + NULL, + (LPBYTE)lpCurrentValue, + &dwLen ); + + } + else { + + lResult = ERROR_NOT_ENOUGH_MEMORY; + } + } + else { + dwLen = sizeof(WCHAR); + lpCurrentValue = (LPWSTR)calloc( dwLen, 1 ); + + if ( !lpCurrentValue ) { + lResult = ERROR_NOT_ENOUGH_MEMORY; + } + else { + lResult = ERROR_SUCCESS; + } + } + + if ( lResult == ERROR_SUCCESS ) { + + dwNewLen =(DWORD) (dwLen + ((wcslen(szMiniportGuid) + 1) * sizeof(WCHAR))); + + lpNewValue = (LPWSTR)malloc( dwNewLen ); + + if ( lpNewValue ) { + + StringCbCopyW ( lpNewValue, + dwNewLen, + szMiniportGuid ); + + CopyMemory( lpNewValue+wcslen(szMiniportGuid)+1, + lpCurrentValue, + dwLen ); + + lResult = RegSetValueExW( hkeyAdapterGuid, + c_szUpperBindings, + 0, + REG_MULTI_SZ, + (LPBYTE)lpNewValue, + dwNewLen ); + } + else { + lResult = ERROR_NOT_ENOUGH_MEMORY; + } + } + + if ( lpCurrentValue ) { + +#ifndef DISABLE_PREFAST_PRAGMA +#pragma prefast(suppress:__WARNING_MISSING_ZERO_TERMINATION, "lpCurrentValue is used as a counted string.") +#endif + + free( lpCurrentValue ); + } + + if ( lpNewValue ) { + free( lpNewValue ); + } + + return lResult; +} + +LONG +DeleteFromMultiSzValue ( + HKEY hkeyAdapterGuid, + _In_ LPWSTR szMiniportGuid) +{ + LPWSTR lpCurrentValue=NULL; + LPWSTR lpNewValue=NULL; + LPWSTR lpCurrentValueTemp; + LPWSTR lpNewValueTemp; + DWORD dwLen; + DWORD dwNewLen; + LONG lResult; + + dwLen = 0; + lResult = RegQueryValueExW( + hkeyAdapterGuid, + c_szUpperBindings, + NULL, + NULL, + NULL, + &dwLen ); + + if ( lResult == ERROR_SUCCESS ) { + lpCurrentValue = (LPWSTR)calloc( dwLen, 1 ); + lpNewValue = (LPWSTR)calloc( dwLen, 1 ); + + if ( (lpCurrentValue != NULL) && (lpNewValue != NULL) ) { + + lResult = RegQueryValueExW( + hkeyAdapterGuid, + c_szUpperBindings, + NULL, + NULL, + (LPBYTE)lpCurrentValue, + &dwLen ); + + if ( lResult == ERROR_SUCCESS ) { + + lpCurrentValueTemp = lpCurrentValue; + lpNewValueTemp = lpNewValue; + + dwNewLen = 0; + + lpCurrentValueTemp[dwLen-1]='\0'; + while( wcslen(lpCurrentValueTemp) > 0) { + + //if a register in the existing register sequence do not match szMiniportGuid, copy to new register sequence + if ( _wcsicmp(lpCurrentValueTemp, szMiniportGuid) != 0 ) { + StringCchCopyW ( lpNewValueTemp, + wcslen(lpCurrentValueTemp), //size of the register + lpCurrentValueTemp ); + + *(lpNewValueTemp+=wcslen(lpCurrentValueTemp)+1)='\0'; + + lpNewValueTemp += wcslen(lpNewValueTemp) + 1; + dwNewLen += (DWORD)wcslen(lpNewValueTemp) + 1; + } + + lpCurrentValueTemp += wcslen(lpCurrentValueTemp) + 1; + } //end of while + + //the minimum length of a register sequence is 3 (start with '\\' and end with '\0') eg: '\\','a','\0' + if ( dwNewLen > 2 ) { + lResult = RegSetValueExW( hkeyAdapterGuid, + c_szUpperBindings, + 0, + REG_MULTI_SZ, + (LPBYTE)lpNewValue, + dwNewLen ); + } + else { + lResult = RegDeleteValueW( hkeyAdapterGuid, + c_szUpperBindings ); + } + } + } + else { + lResult = ERROR_NOT_ENOUGH_MEMORY; + } + } + + if ( lpCurrentValue ) { + free( lpCurrentValue ); + } + + if ( lpNewValue ) { + free( lpNewValue ); + } + + return lResult; +} + +LPWSTR +AddDevicePrefix ( + _In_ LPWSTR lpStr) +{ + LPWSTR lpNewStr; + + size_t cchNewStr = wcslen(lpStr) + wcslen(c_szDevicePrefix) + 1; + lpNewStr = (LPWSTR)malloc( cchNewStr * sizeof(WCHAR) ); + if ( lpNewStr ) + { + StringCchCopyW (lpNewStr, + cchNewStr, + c_szDevicePrefix ); + lpNewStr[cchNewStr-1]='\0'; + StringCchCatW ( lpNewStr, + cchNewStr, + lpStr ); + } + + return lpNewStr; +} + +LPWSTR +RemoveDevicePrefix ( + _In_ LPWSTR lpStr) +{ + LPWSTR lpNewStr; + LPWSTR lpTemp; + + lpTemp = wcsrchr( lpStr, '\\' ); + + if ( lpTemp != NULL ) { + + lpNewStr = _wcsdup( lpTemp+1 ); + } + else { + lpNewStr = NULL; + } + + return lpNewStr; +} + diff --git a/network/ndis/mux/notifyob/common.h b/network/ndis/mux/notifyob/common.h new file mode 100644 index 00000000..517641f7 --- /dev/null +++ b/network/ndis/mux/notifyob/common.h @@ -0,0 +1,103 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: C O M M O N. H +// +// Contents: Common macros and declarations for the sample notify object. +// +// Notes: +// +// Author: Alok Sinha +// +//---------------------------------------------------------------------------- + + +#ifndef COMMON_H_INCLUDED + +#define COMMON_H_INCLUDED + +#include +#include + +#define celems(_x) (sizeof(_x) / sizeof(_x[0])) + +#define MAX_VIRTUAL_MP_PER_ADAPTER 64 + +enum ConfigAction { + + eActUnknown, + eActInstall, + eActAdd, + eActRemove, + eActUpdate, + eActPropertyUIAdd, + eActPropertyUIRemove +}; + +// +// PnP ID, also referred to as Hardware ID, of the protocol interface. +// + +const WCHAR c_szMuxProtocol[] = L"ms_muxp"; + +// +// PnP ID, also referred to as Hardware ID, of the Miniport interface. +// + +const WCHAR c_szMuxMiniport[] = L"ms_muxmp"; + +// +// Name of the service as specified in the inf file in AddService directive. +// + +const WCHAR c_szMuxService[] = L"muxp"; + +// +// Path to the config string where the virtual miniport instance names +// are stored. +// + +const WCHAR c_szAdapterList[] = + L"System\\CurrentControlSet\\Services\\muxp\\Parameters\\Adapters"; + +// +// Value name in the registry where miniport device id is stored. +// + +const WCHAR c_szUpperBindings[] = L"UpperBindings"; + + +const WCHAR c_szDevicePrefix[] = L"\\Device\\"; + +#define ReleaseObj( x ) if ( x ) \ + ((IUnknown*)(x))->Release(); + + +#if DBG +void TraceMsg ( _In_ LPWSTR szFormat, ...); +void DumpChangeFlag (DWORD dwChangeFlag); +void DumpBindingPath (INetCfgBindingPath* pncbp); +void DumpComponent (INetCfgComponent *pncc); +#else +#define TraceMsg +#define DumpChangeFlag( x ) +#define DumpBindingPath( x ) +#define DumpComponent( x ) +#endif + +HRESULT HrFindInstance (INetCfg *pnc, + GUID &guidInstance, + INetCfgComponent **ppnccMiniport); + +LONG AddToMultiSzValue( HKEY hkeyAdapterGuid, + _In_ LPWSTR szMiniportGuid); + +LONG DeleteFromMultiSzValue( HKEY hkeyAdapterGuid, + _In_ LPWSTR szMiniportGuid); + +LPWSTR AddDevicePrefix ( _In_ LPWSTR lpStr); +LPWSTR RemoveDevicePrefix ( _In_ LPWSTR lpStr); + +#endif // COMMON_H_INCLUDED diff --git a/network/ndis/mux/notifyob/dllmain.cpp b/network/ndis/mux/notifyob/dllmain.cpp new file mode 100644 index 00000000..d9785cd6 --- /dev/null +++ b/network/ndis/mux/notifyob/dllmain.cpp @@ -0,0 +1,117 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: DLLMAIN . C P P +// +// Contents: Main entry points into the DLL +// +// Notes: +// +//---------------------------------------------------------------------------- + + +#include +#include +#include + +#include +extern CComModule _Module; // required by atlcom.h +#include +#include + +#include "notify.h" +#include "notifyn_i.c" + + +CComModule _Module; + +BEGIN_OBJECT_MAP(ObjectMap) + OBJECT_ENTRY(CLSID_CMuxNotify, CMuxNotify) +END_OBJECT_MAP() + + + + + +///////////////////////////////////////////////////////////////////////////// +// DLL Entry Point + +extern "C" +BOOL WINAPI DllMain (HINSTANCE hInstance, + DWORD dwReason, + LPVOID /*lpReserved*/) +{ + TraceMsg( L"-->DllMain.\n"); + + if (dwReason == DLL_PROCESS_ATTACH) { + + TraceMsg( L" Reason: Attach.\n"); + + _Module.Init(ObjectMap, hInstance); + + DisableThreadLibraryCalls(hInstance); + } + else if (dwReason == DLL_PROCESS_DETACH) { + + TraceMsg( L" Reason: Detach.\n"); + + _Module.Term(); + } + + TraceMsg( L"<--DllMain.\n"); + + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// Used to determine whether the DLL can be unloaded by OLE + +STDAPI DllCanUnloadNow(void) +{ + HRESULT hr; + + TraceMsg( L"-->DllCanUnloadNow.\n"); + + hr = (_Module.GetLockCount() == 0) ? S_OK : S_FALSE; + + TraceMsg( L"-->DllCanUnloadNow(HRESULT = %x).\n", + hr ); + + return hr; +} + +///////////////////////////////////////////////////////////////////////////// +// Returns a class factory to create an object of the requested type + +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ + TraceMsg( L"-->DllGetClassObject.\n"); + + return _Module.GetClassObject(rclsid, riid, ppv); +} + +///////////////////////////////////////////////////////////////////////////// +// DllRegisterServer - Adds entries to the system registry + +STDAPI DllRegisterServer(void) +{ + // Registers object, typelib and all interfaces in typelib + + TraceMsg( L"-->DllRegisterServer.\n"); + + return _Module.RegisterServer(TRUE); +} + +///////////////////////////////////////////////////////////////////////////// +// DllUnregisterServer - Removes entries from the system registry + +STDAPI DllUnregisterServer(void) +{ + TraceMsg( L"-->DllUnregisterServer.\n"); + + _Module.UnregisterServer(); + + return S_OK; +} diff --git a/network/ndis/mux/notifyob/implinc.cpp b/network/ndis/mux/notifyob/implinc.cpp new file mode 100644 index 00000000..31e34f27 --- /dev/null +++ b/network/ndis/mux/notifyob/implinc.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +#include +extern CComModule _Module; // required by atlcom.h +#include +#include +#include + +#ifdef SubclassWindow + +#undef SubclassWindow + +#endif +#pragma prefast(push) +#pragma prefast(disable:28196 28197, "supress warnings caused by atl headers") +#include + +#ifdef _ATL_STATIC_REGISTRY + +#include + +#endif + + +#pragma prefast(pop) + + +//EXTERN_C const IID IID_INetLanConnectionUiInfo = {0xC08956A6,0x1CD3,0x11D1,{0xB1,0xC5,0x00,0x80,0x5F,0xC1,0x27,0x0E}}; + diff --git a/network/ndis/mux/notifyob/list.h b/network/ndis/mux/notifyob/list.h new file mode 100644 index 00000000..b52b75e7 --- /dev/null +++ b/network/ndis/mux/notifyob/list.h @@ -0,0 +1,241 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: LIST . H +// +// Contents: +// +// Notes: List manipulation functions. +// +//---------------------------------------------------------------------------- + +#ifndef LIST_H_INCLUDED + +#define LIST_H_INCLUDED + +#include +#include +#include + + +template class List { + struct Node { + X item; + Y key; + Node *next; + }; + + Node *m_head; + DWORD m_dwNodeCount; + + public: + + List (); + + virtual ~List(); + + HRESULT Insert (X item, + Y key); + + HRESULT Remove (X *item); + + HRESULT RemoveThis (X item); + + HRESULT RemoveByKey (Y key, + X *item); + + VOID RemoveAll(VOID); + + HRESULT Find (DWORD dwIndex, + X *item); + + HRESULT FindByKey (Y key, + X *item); + + DWORD ListCount (VOID); +}; + +template List::List () +{ + m_head = NULL; + m_dwNodeCount = 0; +} + +template List::~List () +{ + RemoveAll(); +} + +template HRESULT List::Insert (X item, + Y key) +{ + Node *pNewNode; + + pNewNode = new Node; + + if ( pNewNode ) { + pNewNode->item = item; + pNewNode->key = key; + pNewNode->next = NULL; + + if ( m_dwNodeCount ) { + pNewNode->next = m_head; + } + + m_head = pNewNode; + m_dwNodeCount++; + } + + return ( pNewNode ) ? S_OK : HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); +} + +template HRESULT List::Remove (X *item) +{ + Node *temp; + + if ( m_dwNodeCount == 0 ) { + return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + } + + *item = m_head->item; + temp = m_head; + m_head = m_head->next; + delete temp; + m_dwNodeCount--; + + return S_OK; +} + +template HRESULT List::RemoveThis (X item) +{ + Node *temp; + Node *nodeToFind; + + if ( m_dwNodeCount == 0 ) { + return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + } + + if ( m_head->item == item ) { + + nodeToFind = m_head; + m_head = m_head->next; + } + else { + for (temp = m_head; temp->next && (temp->next->item != item); ) { + temp = temp->next; + } + + if ( temp->next ) { + nodeToFind = temp->next; + temp->next = temp->next->next; + } + else + nodeToFind = NULL; + } + + if ( nodeToFind ) { + delete nodeToFind; + m_dwNodeCount--; + return S_OK; + } + else + return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); +} + +template HRESULT List::RemoveByKey (Y key, + X *item) +{ + Node *temp; + Node *nodeToFind; + + if ( m_dwNodeCount == 0 ) { + return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + } + + if ( m_head->key == key ) { + + nodeToFind = m_head; + m_head = m_head->next; + } + else { + for (temp = m_head; temp->next && (temp->next->key != key); ) { + temp = temp->next; + } + + if ( temp->next ) { + nodeToFind = temp->next; + temp->next = temp->next->next; + } + else + nodeToFind = NULL; + } + + if ( nodeToFind ) { + *item = nodeToFind->item; + + delete nodeToFind; + m_dwNodeCount--; + + return S_OK; + } + else + return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); +} + +template VOID List::RemoveAll (VOID) +{ + Node *temp; + + for (; m_dwNodeCount; --m_dwNodeCount) { + temp = m_head; + m_head = m_head->next; + delete temp; + } + + return; +} + +template HRESULT List::Find (DWORD dwIndex, + X *item) +{ + Node *temp; + DWORD i; + + if ( (m_dwNodeCount == 0) || (dwIndex > m_dwNodeCount) ) { + + return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + } + + for (i=0, temp = m_head; i < dwIndex; ++i) { + temp = temp->next; + } + + *item = temp->item; + return S_OK; +} + +template HRESULT List::FindByKey (Y key, + X *item) +{ + Node *temp; + + for (temp = m_head; temp && (temp->key != key); ) + temp = temp->next; + + if ( temp ) { + *item = temp->item; + return S_OK; + } + else { + return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + } +} + +template DWORD List::ListCount (VOID) +{ + return m_dwNodeCount; +} + +#endif diff --git a/network/ndis/mux/notifyob/mux.def b/network/ndis/mux/notifyob/mux.def new file mode 100644 index 00000000..7dc2d972 --- /dev/null +++ b/network/ndis/mux/notifyob/mux.def @@ -0,0 +1,6 @@ +LIBRARY MUX +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/network/ndis/mux/notifyob/mux.vcxproj b/network/ndis/mux/notifyob/mux.vcxproj new file mode 100644 index 00000000..eb3db887 --- /dev/null +++ b/network/ndis/mux/notifyob/mux.vcxproj @@ -0,0 +1,274 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CE2FDBC5-FB0E-4B75-9D1F-60BE72C639A6} + $(MSBuildProjectName) + Debug + Win32 + {B6C3DAD5-A1FA-45F1-98F8-66CD4FEDD014} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + mux + + + mux + + + mux + + + mux + + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + true + Level4 + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + true + Level4 + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + true + Level4 + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + true + Level4 + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + + + %(PreprocessorDefinitions);WIN32;DISABLE_PROTOCOLS_TO_PHYSICAL + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + Static + + + Static + + + Static + + + Static + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;setupapi.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib + mux.def + + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;setupapi.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib + mux.def + + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;setupapi.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib + mux.def + + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;setupapi.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib + mux.def + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/mux/notifyob/mux.vcxproj.Filters b/network/ndis/mux/notifyob/mux.vcxproj.Filters new file mode 100644 index 00000000..ae8436c6 --- /dev/null +++ b/network/ndis/mux/notifyob/mux.vcxproj.Filters @@ -0,0 +1,48 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {7CBE03EB-893C-48D0-9792-100241030FDE} + + + h;hpp;hxx;hm;inl;inc;xsd + {819C3038-4DC6-4786-98BA-ADC2B360C2BB} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {416958BA-8A0D-44E3-8B1E-F089723BC01A} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/mux/notifyob/notify.RC b/network/ndis/mux/notifyob/notify.RC new file mode 100644 index 00000000..4888e305 --- /dev/null +++ b/network/ndis/mux/notifyob/notify.RC @@ -0,0 +1,47 @@ +//Microsoft Developer Studio generated resource script. +// + +#include "resource.h" + +#include "windows.h" + +1 TYPELIB "notifyn.tlb" +IDR_REG_SAMPLE_NOTIFY REGISTRY "notify.rgs" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// +IDD_NOTIFY_GENERAL DIALOG DISCARDABLE 0, 0, 232, 151 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "General" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Remove a Miniport",IDC_REMOVE,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,39,73,75,10 + CONTROL "Add a Miniport",IDC_ADD,"Button",BS_AUTORADIOBUTTON | + WS_TABSTOP,39,36,61,10 + LTEXT "Removal of miniport happens in FIFO order",IDC_STATIC, + 39,93,135,8 + CTEXT "What do you want to do today?",IDC_STATIC,0,12,231,14 +END + + +#include +#include + + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Sample MUX Notify Object" +#define VER_INTERNALNAME_STR "notify.dll" + +#include + + + + + + + + diff --git a/network/ndis/mux/notifyob/notify.RGS b/network/ndis/mux/notifyob/notify.RGS new file mode 100644 index 00000000..51e234e2 --- /dev/null +++ b/network/ndis/mux/notifyob/notify.RGS @@ -0,0 +1,14 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {ee6cd6fc-a32b-4a26-8a1c-dbc97988ad7a} = s 'Sample Notify Object' + { + InProcServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + } + } +} + diff --git a/network/ndis/mux/notifyob/notify.cpp b/network/ndis/mux/notifyob/notify.cpp new file mode 100644 index 00000000..be239fcd --- /dev/null +++ b/network/ndis/mux/notifyob/notify.cpp @@ -0,0 +1,2139 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: N O T I F Y . C P P +// +// Contents: Sample notify object code +// +// Notes: +// +// Author: Alok Sinha + +//---------------------------------------------------------------------------- + +#include "notify.h" + +//---------------------------------------------------------------------------- +// +// Function: CMuxNotify::CMuxNotify +// +// Purpose: Constructor for CMuxNotify +// +// Arguments: None +// +// Returns: None +// +// Notes: +// + +CMuxNotify::CMuxNotify (VOID) : m_pncc (NULL), + m_pnc(NULL), + m_eApplyAction(eActUnknown), + m_pUnkContext(NULL) +{ + TraceMsg( L"-->CMuxNotify::CMuxNotify(Constructor).\n" ); + + TraceMsg( L"<--CMuxNotify::CMuxNotify(Constructor).\n" ); +} + + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::~CMuxNotify +// +// Purpose: Destructor for class CMuxNotify +// +// Arguments: None +// +// Returns: None +// +// Notes: +// +CMuxNotify::~CMuxNotify (VOID) +{ + CMuxPhysicalAdapter *pAdapter; + DWORD dwAdapterCount; + DWORD i; + + TraceMsg( L"-->CMuxNotify::~CMuxNotify(Destructor).\n" ); + + // release interfaces if acquired + + ReleaseObj( m_pncc ); + ReleaseObj( m_pnc ); + ReleaseObj( m_pUnkContext ); + + dwAdapterCount = m_AdaptersList.ListCount(); + + for (i=0; i < dwAdapterCount; ++i) { + + pAdapter = NULL; + m_AdaptersList.Remove( &pAdapter ); + + delete pAdapter; + } + + dwAdapterCount = m_AdaptersToRemove.ListCount(); + + for (i=0; i < dwAdapterCount; ++i) { + + pAdapter = NULL; + m_AdaptersToRemove.Remove( &pAdapter ); + + delete pAdapter; + } + + dwAdapterCount = m_AdaptersToAdd.ListCount(); + + for (i=0; i < dwAdapterCount; ++i) { + + pAdapter = NULL; + m_AdaptersToAdd.Remove( &pAdapter ); + + delete pAdapter; + } + + TraceMsg( L"<--CMuxNotify::~CMuxNotify(Destructor).\n" ); +} + +// +//---------------------- NOTIFY OBJECT FUNCTIONS ----------------------------- +// + +//---------------------------------------------------------------------------- +// INetCfgComponentControl +// +// The following functions provide the INetCfgComponentControl interface. +// +//---------------------------------------------------------------------------- + +// +// Function: CMuxNotify::Initialize +// +// Purpose: Initialize the notify object +// +// Arguments: +// IN pnccItem : Pointer to INetCfgComponent object +// IN pnc : Pointer to INetCfg object +// IN fInstalling: TRUE if we are being installed +// +// Returns: +// +// Notes: +// + +STDMETHODIMP CMuxNotify::Initialize (INetCfgComponent* pncc, + INetCfg* pnc, + BOOL fInstalling) +{ + HRESULT hr = S_OK; + + TraceMsg( L"-->CMuxNotify INetCfgControl::Initialize.\n" ); + + + // Save INetCfg & INetCfgComponent and add a refcount + + m_pncc = pncc; + m_pnc = pnc; + + if (m_pncc) { + + m_pncc->AddRef(); + } + + if (m_pnc) { + + m_pnc->AddRef(); + } + + + // + // If this not an installation, then we need to + // initialize all of our data and classes + // + + if ( !fInstalling ) { + + hr = HrLoadAdapterConfiguration(); + } + + TraceMsg( L"<--CMuxNotify INetCfgControl::Initialize(HRESULT = %x).\n", + hr ); + + return hr; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::CancelChanges +// +// Purpose: Cancel any changes made to internal data +// +// Arguments: None +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: +// + +STDMETHODIMP CMuxNotify::CancelChanges (VOID) +{ + TraceMsg( L"-->CMuxNotify INetCfgControl::CancelChanges.\n" ); + + + TraceMsg( L"<--CMuxNotify INetCfgControl::CancelChanges(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::ApplyRegistryChanges +// +// Purpose: Apply changes. +// +// Arguments: None +// +// Returns: S_OK. +// +// Notes: We can make changes to registry etc. here. + +STDMETHODIMP CMuxNotify::ApplyRegistryChanges(VOID) +{ + CMuxPhysicalAdapter *pAdapter = NULL; + DWORD dwAdapterCount; + DWORD i; + + TraceMsg( L"-->CMuxNotify INetCfgControl::ApplyRegistryChanges.\n" ); + + // + // Make registry changes for the adapters added. + // + + dwAdapterCount = m_AdaptersToAdd.ListCount(); + + TraceMsg( L" Adding %d new adapters.\n", + dwAdapterCount ); + + for (i=0; i < dwAdapterCount; ++i) { + + m_AdaptersToAdd.Find( i, + &pAdapter ); + + pAdapter->ApplyRegistryChanges( eActAdd ); + + } + + // + // Make registry changes for the adapters uninstalled. + // + + dwAdapterCount = m_AdaptersToRemove.ListCount(); + + TraceMsg( L" Removing %d adapters.\n", + dwAdapterCount ); + + for (i=0; i < dwAdapterCount; ++i) { + + m_AdaptersToRemove.Find( i, + &pAdapter ); + + pAdapter->ApplyRegistryChanges( eActRemove ); + } + + // + // Make registry changes for the miniports added/removed + // through the property pages. + // + + dwAdapterCount = m_AdaptersList.ListCount(); + + for (i=0; i < dwAdapterCount; ++i) { + + m_AdaptersList.Find( i, + &pAdapter ); + + pAdapter->ApplyRegistryChanges( eActUpdate ); + } + + TraceMsg( L"<--CMuxNotify INetCfgControl::ApplyRegistryChanges(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::ApplyPnpChanges +// +// Purpose: Apply changes. +// +// Arguments: +// IN pfCallback: PnPConfigCallback interface. +// +// Returns: S_OK. +// +// Notes: + +STDMETHODIMP CMuxNotify::ApplyPnpChanges ( + INetCfgPnpReconfigCallback* pfCallback) +{ + CMuxPhysicalAdapter *pAdapter = NULL; + GUID guidAdapter; + DWORD dwAdapterCount; + DWORD i; + + TraceMsg( L"-->CMuxNotify INetCfgControl::ApplyPnpChanges.\n" ); + + // + // Apply PnP changes for the adapters added. + // + + dwAdapterCount = m_AdaptersToAdd.ListCount(); + + TraceMsg( L" Applying PnP changes when %d adapters added.\n", + dwAdapterCount ); + + for (i=0; i < dwAdapterCount; ++i) { + + m_AdaptersToAdd.Remove( &pAdapter ); + + pAdapter->ApplyPnpChanges( pfCallback, + eActAdd ); + + pAdapter->GetAdapterGUID( &guidAdapter ); + + m_AdaptersList.Insert( pAdapter, + guidAdapter ); + } + + // + // Apply PnP changes for the adapters uninstalled. + // + + dwAdapterCount = m_AdaptersToRemove.ListCount(); + + TraceMsg( L" Applying PnP changes when %d adapters removed.\n", + dwAdapterCount ); + + for (i=0; i < dwAdapterCount; ++i) { + + m_AdaptersToRemove.Remove( &pAdapter ); + + pAdapter->ApplyPnpChanges( pfCallback, + eActRemove ); + + delete pAdapter; + } + + // + // Apply PnP changes for the miniports added/removed through + // the property pages. + // + + dwAdapterCount = m_AdaptersList.ListCount(); + + for (i=0; i < dwAdapterCount; ++i) { + + m_AdaptersList.Find( i, + &pAdapter ); + + pAdapter->ApplyPnpChanges( pfCallback, + eActUpdate ); + } + + TraceMsg( L"<--CMuxNotify INetCfgControl::ApplyPnpChanges(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + + +//---------------------------------------------------------------------------- +// INetCfgComponentSetup +// +// The following functions provide the INetCfgComponentSetup interface. +// +//---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::Install +// +// Purpose: Do operations necessary during the installation. +// +// Arguments: +// IN dwSetupFlags: Setup flags +// +// Returns: S_OK +// +// Notes: Don't do anything irreversible (like modifying registry) yet +// since the config. actually completes only when Apply is called! +// + +STDMETHODIMP CMuxNotify::Install (DWORD dwSetupFlags) +{ + + UNREFERENCED_PARAMETER(dwSetupFlags); + TraceMsg( L"-->CMuxNotify INetCfgSetup::Install.\n" ); + + // Start up the install process + + m_eApplyAction = eActInstall; + + TraceMsg( L"<--CMuxNotify INetCfgSetup::Install(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::Upgrade +// +// Purpose: Do operations necessary during the upgrade. +// +// Arguments: +// IN dwSetupFlags: Setup flags +// +// Returns: S_OK +// +// Notes: Don't do anything irreversible (like modifying registry) yet +// since the config. actually completes only when Apply is called! +// + +STDMETHODIMP CMuxNotify::Upgrade (IN DWORD dwSetupFlags, + IN DWORD dwUpgradeFromBuildNo) +{ + + TraceMsg( L"-->CMuxNotify INetCfgSetup::Upgrade.\n" ); + + TraceMsg( L" DwSetupFlags = %x, dwUpgradeFromBuildNo = %x\n", + dwSetupFlags, + dwUpgradeFromBuildNo ); + + TraceMsg( L"<--CMuxNotify INetCfgSetup::Upgrade(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::ReadAnswerFile +// +// Purpose: Read settings from answerfile and configure CMuxNotify +// +// Arguments: +// IN pszAnswerFile : Name of AnswerFile +// IN pszAnswerSection: Name of parameters section +// +// Returns: +// +// Notes: Don't do anything irreversible (like modifying registry) yet +// since the config. actually completes only when Apply is called! +// + +STDMETHODIMP CMuxNotify::ReadAnswerFile (PCWSTR pszAnswerFile, + PCWSTR pszAnswerSection) +{ +// PCWSTR pszParamReadFromAnswerFile = L"ParamFromAnswerFile"; + + UNREFERENCED_PARAMETER(pszAnswerFile); + UNREFERENCED_PARAMETER(pszAnswerSection); + + TraceMsg( L"-->CMuxNotify INetCfgSetup::ReadAnswerFile.\n" ); + + // We will pretend here that szParamReadFromAnswerFile was actually + // read from the AnswerFile using the following steps + // + // - Open file pszAnswerFile using SetupAPI + // - locate section pszAnswerSection + // - locate the required key and get its value + // - store its value in pszParamReadFromAnswerFile + // - close HINF for pszAnswerFile + + // Now that we have read pszParamReadFromAnswerFile from the + // AnswerFile, store it in our memory structure. + // Remember we should not be writing it to the registry till + // our Apply is called!! + // + + TraceMsg( L"<--CMuxNotify INetCfgSetup::ReadAnswerFile(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::Removing +// +// Purpose: Do necessary cleanup when being removed +// +// Arguments: None +// +// Returns: S_OK +// +// Notes: Don't do anything irreversible (like modifying registry) yet +// since the removal is actually complete only when Apply is called! +// + +STDMETHODIMP CMuxNotify::Removing (VOID) +{ + + TraceMsg( L"-->CMuxNotify INetCfgSetup::Removing.\n" ); + + TraceMsg( L"<--CMuxNotify INetCfgSetup::Removing(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + + + +//---------------------------------------------------------------------------- +// INetCfgComponentNotifyBinding +// +// The following functions provide the INetCfgComponentNotifyBinding interface. +// +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +// +// Function: CMuxNotify::QueryBindingPath +// +// Purpose: This is specific to the component being installed. This will +// ask us if we want to bind to the Item being passed into +// this routine. We can disable the binding by returning +// NETCFG_S_DISABLE_QUERY +// +// +// Arguments: +// IN dwChangeFlag: Type of binding change +// IN pncbpItem : Pointer to INetCfgBindingPath object +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// +STDMETHODIMP CMuxNotify::QueryBindingPath (IN DWORD dwChangeFlag, + IN INetCfgBindingPath *pncbp) +{ + UNREFERENCED_PARAMETER(pncbp); + UNREFERENCED_PARAMETER(dwChangeFlag); + + TraceMsg( L"-->CMuxNotify INetCfgNotifyBinding::QueryBindingPath.\n" ); + + DumpChangeFlag( dwChangeFlag ); + DumpBindingPath( pncbp ); + + TraceMsg( L"<--CMuxNotify INetCfgNotifyBinding::QueryBindingPath(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::NotifyBindingPath +// +// Purpose: We are now being told to bind to the component passed to us. +// +// +// Arguments: +// IN dwChangeFlag: Type of system change +// IN pncc : Pointer to INetCfgComponent object +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: +// + + + +STDMETHODIMP CMuxNotify::NotifyBindingPath (IN DWORD dwChangeFlag, + IN INetCfgBindingPath *pncbp) +{ + INetCfgComponent *pnccLower; + INetCfgComponent *pnccUpper; + LPWSTR pszwInfIdLower; + LPWSTR pszwInfIdUpper; + DWORD dwCharcteristics; + HRESULT hr = S_OK; + + TraceMsg( L"-->CMuxNotify INetCfgNotifyBinding::NotifyBindingPath.\n" ); + + DumpChangeFlag( dwChangeFlag ); + DumpBindingPath( pncbp ); + + // + // We are only interested to know 1) when a component is installed + // and we are binding to it i.e. dwChangeFlag = NCN_ADD | NCN_ENABLE + // and 2) when a component is removed to which we are bound i.e. + // dwChangeFlag = NCN_REMOVE | NCN_ENABLE. dwChangeFlag is never + // set to NCN_ADD or NCN_REMOVE only. So, checking for NCN_ENABLE + // covers the case of NCN_ADD | NCN_ENABLE and checking for NCN_REMOVE + // covers the case of NCN_REMOVE | NCN_ENABLE. We don't care about + // NCN_ADD | NCN_DISABLE (case 1) and NCN_REMOVE | NCN_DISABLE (case 2). + // + + if ( dwChangeFlag & (NCN_ENABLE | NCN_REMOVE) ) { + + // + // Get the upper and lower components. + // + + hr = HrGetUpperAndLower( pncbp, + &pnccUpper, + &pnccLower ); + + if ( hr == S_OK ) { + + hr = pnccLower->GetCharacteristics( &dwCharcteristics ); + + if ( hr == S_OK ) { + hr = pnccLower->GetId( &pszwInfIdLower ); + + if ( hr == S_OK ) { + hr = pnccUpper->GetId( &pszwInfIdUpper ); + + if ( hr == S_OK ) { + + // + // We are interested only in binding to a + // physical ethernet adapters. + // + + if ( dwCharcteristics & NCF_PHYSICAL ) { + + if ( !_wcsicmp( pszwInfIdUpper, c_szMuxProtocol ) ) { + + if ( dwChangeFlag & NCN_ADD ) { + + hr = HrAddAdapter( pnccLower ); + if( hr != S_OK ){ + // you may do something + } + m_eApplyAction = eActAdd; + + } else if ( dwChangeFlag & NCN_REMOVE ) { + + hr = HrRemoveAdapter( pnccLower ); + if( hr != S_OK ){ + // you may do something + } + m_eApplyAction = eActRemove; + } + } + } // Physical Adapters. + else if (dwCharcteristics & NCF_VIRTUAL) { + + } + + CoTaskMemFree( pszwInfIdUpper ); + + } // Got the upper component id. + + CoTaskMemFree( pszwInfIdLower ); + + } // Got the lower component id. + + } // Got NIC's characteristics + + ReleaseObj(pnccLower); + ReleaseObj(pnccUpper); + + } // Got the upper and lower components. + + } + + TraceMsg( L"<--CMuxNotify INetCfgNotifyBinding::NotifyBindingPath(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + + + + +//---------------------------------------------------------------------------- +// INetCfgComponentNotifyGlobal +// +// The following functions provide the INetCfgComponentNotifyGlobal interface. +// +//---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::GetSupportedNotifications +// +// Purpose: Tell the system which notifications we are interested in +// +// Arguments: +// OUT pdwNotificationFlag: Pointer to NotificationFlag +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: +// +STDMETHODIMP CMuxNotify::GetSupportedNotifications ( + OUT DWORD* pdwNotificationFlag) +{ + TraceMsg( L"-->CMuxNotify INetCfgNotifyGlobal::GetSupportedNotifications.\n" ); + + *pdwNotificationFlag = NCN_NET | NCN_NETTRANS | NCN_ADD | NCN_REMOVE | + NCN_BINDING_PATH | NCN_ENABLE | NCN_DISABLE; + + TraceMsg( L"<--CMuxNotify INetCfgNotifyGlobal::GetSupportedNotifications(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::SysQueryBindingPath +// +// Purpose: Enable or Disable a binding path. +// +// Arguments: +// IN dwChangeFlag: Type of binding change +// IN pncbp : Pointer to INetCfgBindingPath object +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: +// + +STDMETHODIMP CMuxNotify::SysQueryBindingPath (DWORD dwChangeFlag, + INetCfgBindingPath* pncbp) +{ + INetCfgComponent *pnccLower; + INetCfgComponent *pnccUpper; + LPWSTR pszwInfIdLower; + LPWSTR pszwInfIdUpper; + DWORD dwCharcteristics; + HRESULT hr = S_OK; + + + TraceMsg( L"-->CMuxNotify INetCfgNotifyGlobal::SysQueryBindingPath.\n" ); + + DumpChangeFlag( dwChangeFlag ); + DumpBindingPath( pncbp ); + + if ( dwChangeFlag & NCN_ENABLE ) { + + // + // Get the upper and lower components. + // + + hr = HrGetUpperAndLower( pncbp, + &pnccUpper, + &pnccLower ); + + if ( hr == S_OK ) { + hr = pnccLower->GetCharacteristics( &dwCharcteristics ); + + if ( hr == S_OK ) { + hr = pnccLower->GetId( &pszwInfIdLower ); + + if ( hr == S_OK ) { + hr = pnccUpper->GetId( &pszwInfIdUpper ); + + if ( hr == S_OK ) { + + // + // We are interested only in bindings to physical + // ethernet adapters. + // + + if ( dwCharcteristics & NCF_PHYSICAL ) { + +#ifdef DISABLE_PROTOCOLS_TO_PHYSICAL + + // + // If it not our protocol binding to the + // physical adapter then, disable the + // binding. + // + + if (_wcsicmp( pszwInfIdUpper, c_szMuxProtocol ) ) { + + TraceMsg( L" Disabling the binding between %s " + L"and %s.\n", + pszwInfIdUpper, + pszwInfIdLower ); + + hr = NETCFG_S_DISABLE_QUERY; + } +#endif + + } // Physical Adapters. + else { + if (dwCharcteristics & NCF_VIRTUAL) { + + // If the lower component is our miniport + // and the upper component is our protocol + // then also, disable the binding. + + if ( !_wcsicmp(pszwInfIdLower, c_szMuxMiniport) && + !_wcsicmp(pszwInfIdUpper, c_szMuxProtocol) ) { + + TraceMsg( L" Disabling the binding between %s " + L"and %s.\n", + pszwInfIdUpper, + pszwInfIdLower ); + + hr = NETCFG_S_DISABLE_QUERY; + } + + } // Virtual Adapters + + } + + CoTaskMemFree( pszwInfIdUpper ); + + } // Got the upper component id. + + CoTaskMemFree( pszwInfIdLower ); + + } // Got the lower component id. + + } // Got NIC's characteristics + + ReleaseObj(pnccLower); + ReleaseObj(pnccUpper); + + } + + } + + TraceMsg( L"<--CMuxNotify INetCfgNotifyGlobal::SysQueryBindingPath(HRESULT = %x).\n", + hr ); + + return hr; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::SysNotifyBindingPath +// +// Purpose: System tells us by calling this function which +// binding path has just been formed. +// +// Arguments: +// IN dwChangeFlag: Type of binding change +// IN pncbpItem : Pointer to INetCfgBindingPath object +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: +// +STDMETHODIMP CMuxNotify::SysNotifyBindingPath (DWORD dwChangeFlag, + INetCfgBindingPath* pncbp) +{ + UNREFERENCED_PARAMETER(dwChangeFlag); + UNREFERENCED_PARAMETER(pncbp); + + TraceMsg( L"-->CMuxNotify INetCfgNotifyGlobal::SysNotifyBindingPath.\n" ); + + DumpChangeFlag( dwChangeFlag ); + DumpBindingPath( pncbp ); + + TraceMsg( L"<--CMuxNotify INetCfgNotifyGlobal::SysNotifyBindingPath(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::SysNotifyComponent +// +// Purpose: System tells us by calling this function which +// component has undergone a change (installed/removed) +// +// Arguments: +// IN dwChangeFlag: Type of system change +// IN pncc : Pointer to INetCfgComponent object +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: +// +STDMETHODIMP CMuxNotify::SysNotifyComponent (DWORD dwChangeFlag, + INetCfgComponent* pncc) +{ + UNREFERENCED_PARAMETER(dwChangeFlag); + UNREFERENCED_PARAMETER(pncc); + + TraceMsg( L"-->CMuxNotify INetCfgNotifyGlobal::SysNotifyComponent.\n" ); + + DumpChangeFlag( dwChangeFlag ); + DumpComponent( pncc ); + + TraceMsg( L"<--CMuxNotify INetCfgNotifyGlobal::SysNotifyComponent(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + + +//---------------------------------------------------------------------------- +// INetCfgComponentPropertyUi +// +// The following functions provide the INetCfgComponentPropertyUi interface. +// +//---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::MergePropPages +// +// Purpose: Supply our property page to system. +// +// Arguments: +// OUT pdwDefPages : Pointer to num default pages +// OUT pahpspPrivate: Pointer to array of pages +// OUT pcPages : Pointer to num pages +// IN hwndParent : Handle of parent window +// IN szStartPage : Pointer to +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: +// +STDMETHODIMP CMuxNotify::MergePropPages (IN OUT DWORD* pdwDefPages, + OUT LPBYTE* pahpspPrivate, + OUT UINT* pcPages, + IN HWND hwndParent, + OUT PCWSTR* szStartPage) +{ + HRESULT hr = S_OK; + HPROPSHEETPAGE *ahpsp;; + INetLanConnectionUiInfo *pLanConnUiInfo; + + UNREFERENCED_PARAMETER(szStartPage); + UNREFERENCED_PARAMETER(hwndParent); + + TraceMsg(L"-->CMuxNotify INetCfgPropertyUi::MergePropPages\n"); + + // + // We don't want any default pages to be shown + // + + *pdwDefPages = 0; + *pcPages = 0; + *pahpspPrivate = NULL; + + if ( !m_pUnkContext ) { + return E_UNEXPECTED; + } + + hr = m_pUnkContext->QueryInterface( + IID_INetLanConnectionUiInfo, + reinterpret_cast(&pLanConnUiInfo)); + + if ( hr == S_OK ) { + + ReleaseObj( pLanConnUiInfo ); + + ahpsp = (HPROPSHEETPAGE*)CoTaskMemAlloc( sizeof(HPROPSHEETPAGE) ); + + if (ahpsp) { + + PROPSHEETPAGE psp = {0}; + + psp.dwSize = sizeof(PROPSHEETPAGE); + psp.dwFlags = PSP_DEFAULT; + psp.hInstance = _Module.GetModuleInstance(); + psp.pszTemplate = MAKEINTRESOURCE(IDD_NOTIFY_GENERAL); + psp.pfnDlgProc = NotifyDialogProc; + psp.pfnCallback = NULL; (LPFNPSPCALLBACK)NotifyPropSheetPageProc; + psp.lParam = (LPARAM) this; + psp.pszHeaderTitle = NULL; + psp.pszHeaderSubTitle = NULL; + + ahpsp[0] = ::CreatePropertySheetPage(&psp); + *pcPages = 1; + *pahpspPrivate = (LPBYTE)ahpsp; + } + else { + hr = E_OUTOFMEMORY; + } + } + TraceMsg(L"<--CMuxNotify INetCfgPropertyUi::MergePropPages(HRESULT = %x).\n", + hr ); + + return hr; +} + + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::ValidateProperties +// +// Purpose: Validate changes to property page. +// +// Arguments: +// IN hwndSheet: Window handle of property sheet +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: +// + +STDMETHODIMP CMuxNotify::ValidateProperties (HWND hwndSheet) +{ + + UNREFERENCED_PARAMETER(hwndSheet); + + TraceMsg( L"-->CMuxNotify INetCfgPropertyUi::ValidateProperties\n" ); + + TraceMsg(L"<--CMuxNotify INetCfgPropertyUi::ValidateProperties(HRESULT = %x).\n", + S_OK ); + return S_OK; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::CancelProperties +// +// Purpose: Cancel changes to property page +// +// Arguments: None +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: +// +STDMETHODIMP CMuxNotify::CancelProperties (VOID) +{ + TraceMsg(L"-->CMuxNotify INetCfgPropertyUi::CancelProperties\n"); + + TraceMsg(L"<--CMuxNotify INetCfgPropertyUi::CancelProperties(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::ApplyProperties +// +// Purpose: Apply value of controls on property page +// to internal memory structure +// +// Arguments: None +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: +// +STDMETHODIMP CMuxNotify::ApplyProperties (VOID) +{ + INetLanConnectionUiInfo *pLanConnUiInfo; + CMuxPhysicalAdapter *pAdapter; + GUID guidAdapter; + INetCfgComponent *pncc; + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pncc); + + TraceMsg(L"-->CMuxNotify INetCfgPropertyUi::ApplyProperties\n"); + + if ( m_pUnkContext ) { + + hr = m_pUnkContext->QueryInterface( + IID_INetLanConnectionUiInfo, + reinterpret_cast(&pLanConnUiInfo)); + + if ( hr == S_OK ) { + + hr = pLanConnUiInfo->GetDeviceGuid( &guidAdapter ); + + if ( hr == S_OK ) { + + hr = m_AdaptersList.FindByKey( guidAdapter, + &pAdapter ); + if ( hr == S_OK ) { + + switch( m_eApplyAction ) { + + case eActPropertyUIAdd: + + hr = HrAddMiniport( pAdapter, + &guidAdapter ); + break; + + case eActPropertyUIRemove: + + hr = HrRemoveMiniport( pAdapter, + &guidAdapter ); + break; + } + } + } + + ReleaseObj( pLanConnUiInfo ); + } + } + + TraceMsg(L"<--CMuxNotify INetCfgPropertyUi::ApplyProperties(HRESULT = %x).\n", + hr ); + return hr; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::QueryPropertyUi +// +// Purpose: System is asking if we support property pages. +// +// Arguments: +// IN pUnk: Pointer to IUnknown. +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: We display property pages only in the context of +// a LAN connection. +// + +STDMETHODIMP CMuxNotify::QueryPropertyUi (IUnknown * pUnk) +{ + INetLanConnectionUiInfo *pLanConnUiInfo; + HRESULT hr=S_FALSE; + + TraceMsg(L"-->CMuxNotify INetCfgPropertyUi::QueryPropertyUi\n"); + +#ifndef PASSTHRU_NOTIFY + + if ( pUnk ) { + + hr = pUnk->QueryInterface( + IID_INetLanConnectionUiInfo, + reinterpret_cast(&pLanConnUiInfo)); + + ReleaseObj( pLanConnUiInfo ); + } +#endif + + TraceMsg(L"<--CMuxNotify INetCfgPropertyUi::QueryPropertyUi(HRESULT = %x).\n", + hr ); + + return hr; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::SetContext +// +// Purpose: Save the LAN connection context. +// +// Arguments: +// IN pUnk: Pointer to IUnknown. +// +// Returns: S_OK on success, otherwise an error code +// +// Notes: It is also called to release the current LAN connection context. +// + +STDMETHODIMP CMuxNotify::SetContext (IUnknown * pUnk) +{ + TraceMsg(L"-->CMuxNotify INetCfgPropertyUi::SetContext\n"); + + // + // Release previous context, if any + // + + ReleaseObj( m_pUnkContext ); + + m_pUnkContext = NULL; + + if ( pUnk ) { + + m_pUnkContext = pUnk; + m_pUnkContext->AddRef(); + } + + TraceMsg(L"<--CMuxNotify INetCfgPropertyUi::SetContext(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +//---------------------------------------------------------------------------- +// +// Function: CMuxNotify::HrLoadAdapterConfiguration +// +// Purpose: This loads the Miniport and adapters that have already been +// installed into our own data structures +// +// Arguments: None. +// +// Returns: S_OK, or an error. +// +// +// Notes: +// + + +HRESULT CMuxNotify::HrLoadAdapterConfiguration (VOID) +{ + HKEY hkeyAdapterList; + WCHAR szAdapterGuid[MAX_PATH+1]; + DWORD dwDisp; + CMuxPhysicalAdapter *pAdapter; + GUID guidAdapter; + DWORD dwIndex; + LONG lResult; + + TraceMsg( L"-->CMuxNotify::HrLoadAdapterConfiguration.\n" ); + + lResult = RegCreateKeyExW( HKEY_LOCAL_MACHINE, + c_szAdapterList, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hkeyAdapterList, + &dwDisp); + + + if ( lResult == ERROR_SUCCESS ) { + + // + // If dwDisp indicates that a new key is created then, we know there + // is no adapter currently listed underneath and we simply + // return, otherwise, we enumerate the subkeys, each one representing an + // adapter. + // + + if ( dwDisp != REG_CREATED_NEW_KEY ) { + + lResult = RegEnumKeyW( hkeyAdapterList, + 0, + szAdapterGuid, + MAX_PATH+1 ); + + for (dwIndex=1; lResult == ERROR_SUCCESS; ++dwIndex) { + + TraceMsg( L" Loading configuration for adapter %s...\n", + szAdapterGuid ); + + // + // Subkeys are actually a guid/bindname of the adapters. + // + szAdapterGuid[MAX_PATH]='\0'; + CLSIDFromString( szAdapterGuid, + &guidAdapter ); + + // + // Create an instance representing the adapter. + // + + #pragma prefast(suppress:8197, "The instance is freed in the destructor") + pAdapter = new CMuxPhysicalAdapter( m_pnc, + &guidAdapter ); + + if ( pAdapter ) { + + // + // Load any adapter specific configuration. + // + + pAdapter->LoadConfiguration(); + + // + // Save the adapter instance in a list. + // + + m_AdaptersList.Insert( pAdapter, + guidAdapter ); + + // + // Get next subkey. + // + + lResult = RegEnumKeyW( hkeyAdapterList, + dwIndex, + szAdapterGuid, + MAX_PATH+1 ); + } + else { + + lResult = ERROR_NOT_ENOUGH_MEMORY; + } + } + + // + // RegEnumKeyW may have returned error when there are no more + // subkeys to read. + // + + lResult = ERROR_SUCCESS; + } + + RegCloseKey( hkeyAdapterList ); + } + + TraceMsg( L"<--CMuxNotify::HrLoadAdapterConfiguration(HRESULT = %x).\n", + HRESULT_FROM_WIN32(lResult) ); + + return HRESULT_FROM_WIN32(lResult); +} + +//---------------------------------------------------------------------------- +// +// Function: CMuxNotify::HrGetUpperAndLower +// +// Purpose: Get the upper and lower component of the first interface +// of a binding path. +// +// Arguments: +// IN pncbp : Binding path. +// OUT ppnccUpper: Upper component. +// OUT ppnccLower: Lower component. +// +// Returns: S_OK, or an error. +// +// +// Notes: +// + +HRESULT CMuxNotify::HrGetUpperAndLower (INetCfgBindingPath* pncbp, + INetCfgComponent **ppnccUpper, + INetCfgComponent **ppnccLower) +{ + IEnumNetCfgBindingInterface* pencbi; + INetCfgBindingInterface* pncbi; + ULONG ulCount; + HRESULT hr; + + TraceMsg( L"-->CMuxNotify::HrGetUpperAndLowerComponent.\n" ); + + *ppnccUpper = NULL; + *ppnccLower = NULL; + + hr = pncbp->EnumBindingInterfaces(&pencbi); + + if (S_OK == hr) { + + // + // get the first binding interface + // + + hr = pencbi->Next(1, &pncbi, &ulCount); + + if ( hr == S_OK ) { + + hr = pncbi->GetUpperComponent( ppnccUpper ); + + if ( hr == S_OK ) { + + hr = pncbi->GetLowerComponent ( ppnccLower ); + } + else { + if( ppnccUpper != NULL ) + { + ReleaseObj( *ppnccUpper ); + } + } + + ReleaseObj( pncbi ); + } + + ReleaseObj( pencbi ); + } + + TraceMsg( L"<--CMuxNotify::HrGetUpperAndLowerComponent(HRESULT = %x).\n", + hr ); + + return hr; +} + +//---------------------------------------------------------------------------- +// +// Function: CMuxNotify::HrAddAdapter +// +// Purpose: Create an instance representing the physical adapter and install +// a virtual miniport. +// +// Arguments: +// IN pnccAdapter: Pointer to the physical adapter. +// +// Returns: S_OK, or an error. +// +// +// Notes: +// + +HRESULT CMuxNotify::HrAddAdapter (INetCfgComponent *pnccAdapter) +{ + GUID guidAdapter; + CMuxPhysicalAdapter *pAdapter; + HRESULT hr; + + TraceMsg( L"-->CMuxNotify::HrAddAdapter.\n" ); + + hr = pnccAdapter->GetInstanceGuid( &guidAdapter ); + + if ( hr == S_OK ) { + + #pragma prefast(suppress:8197, "The instance is freed in the destructor") + pAdapter = new CMuxPhysicalAdapter( m_pnc, + &guidAdapter ); + + if ( pAdapter ) { + + hr = HrAddMiniport( pAdapter, + &guidAdapter ); + + if ( hr == S_OK ) { + + m_AdaptersToAdd.Insert( pAdapter, + guidAdapter ); + } + else { + + delete pAdapter; + } + } + else { + hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY ); + } + } + + TraceMsg( L"<--CMuxNotify::HrAddAdapter(HRESULT = %x).\n", + hr ); + + return hr; +} + +//---------------------------------------------------------------------------- +// +// Function: CMuxNotify::HrRemoveAdapter +// +// Purpose: Deletes the instance representing the physical adapter +// and uninstalls all the virtual miniports. +// +// Arguments: +// IN pnccAdapter: Pointer to the physical adapter. +// +// Returns: S_OK, or an error. +// +// +// Notes: This function is called when the adapter or the protocol +// is being uninstalled. +// + +HRESULT CMuxNotify::HrRemoveAdapter (INetCfgComponent *pnccAdapter) +{ + GUID guidAdapter; + CMuxPhysicalAdapter *pAdapter; + HRESULT hr; + + TraceMsg( L"-->CMuxNotify::HrRemoveAdapter.\n" ); + + hr = pnccAdapter->GetInstanceGuid( &guidAdapter ); + + if ( hr == S_OK ) { + + hr = m_AdaptersList.RemoveByKey( guidAdapter, + &pAdapter ); + + if ( hr == S_OK ) { + + m_AdaptersToRemove.Insert( pAdapter, + guidAdapter ); + hr = pAdapter->Remove(); + +#ifdef DISABLE_PROTOCOLS_TO_PHYSICAL + + // + // Restore the bindings of other protocols to the physical + // adapter. + // + + EnableBindings( pnccAdapter, + TRUE ); +#endif + } + } + + TraceMsg( L"<--CMuxNotify::HrRemoveAdapter(HRESULT = %x).\n", + hr ); + + return hr; +} + +//---------------------------------------------------------------------------- +// +// Function: CMuxNotify::HrAddMiniport +// +// Purpose: Installs a virtual miniport. +// +// Arguments: +// IN pAdapter : Pointer to the physical adapter class instance. +// IN pguidAdapter: Pointer to the GUID of the adapter. +// +// Returns: S_OK, or an error. +// +// +// Notes: +// + +HRESULT CMuxNotify::HrAddMiniport (CMuxPhysicalAdapter *pAdapter, + GUID *pguidAdapter) +{ + CMuxVirtualMiniport *pMiniport; + INetCfgComponent *pnccAdapter; + HRESULT hr = S_OK; + + TraceMsg( L"-->CMuxNotify::HrAddMiniport.\n" ); + + // + // Limit the number of virtual miniports + // + if ((pAdapter->MiniportCount() + pAdapter->MiniportAddCount()) >= MAX_VIRTUAL_MP_PER_ADAPTER) + { + hr = HRESULT_FROM_WIN32( ERROR_NO_SYSTEM_RESOURCES ); + TraceMsg( L" Virtual miniport limit reached\n" ); + } + + if ( hr == S_OK ) + { + #pragma prefast(suppress:8197, "The instance is freed in the destructor") + pMiniport = new CMuxVirtualMiniport( m_pnc, + NULL, + pguidAdapter ); + if ( pMiniport ) { + + hr = pMiniport->Install(); + + if ( hr == S_OK ) { + + hr = pAdapter->AddMiniport( pMiniport ); + + if ( hr != S_OK ) { + + pMiniport->DeInstall(); + + delete pMiniport; + } + } + else + { + delete pMiniport; + } + } + else { + + hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY ); + } + } + +#ifdef DISABLE_PROTOCOLS_TO_PHYSICAL + + if ( hr == S_OK ) { + + // + // If this is the first virtual miniport then, disable the bindings + // of other protocols to the physical adapter. + // + + if ( pAdapter->MiniportCount() == 0 ) { + + hr = HrFindInstance( m_pnc, + *pguidAdapter, + &pnccAdapter ); + + if ( hr == S_OK ) { + EnableBindings( pnccAdapter, + FALSE ); + + ReleaseObj( pnccAdapter ); + } + } + } +#endif + + TraceMsg( L"<--CMuxNotify::HrAddMiniport(HRESULT = %x).\n", + hr ); + return hr; +} + +//---------------------------------------------------------------------------- +// +// Function: CMuxNotify::HrRemoveMiniport +// +// Purpose: Uninstalls a virtual miniport. +// +// Arguments: +// IN pAdapter : Pointer to the physical adapter class instance. +// IN pguidAdapter: Pointer to the GUID of the adapter. +// +// Returns: S_OK, or an error. +// +// +// Notes: +// + +HRESULT CMuxNotify::HrRemoveMiniport (CMuxPhysicalAdapter *pAdapter, + GUID *pguidAdapter) +{ + INetCfgComponent *pnccAdapter; + HRESULT hr; + + TraceMsg( L"-->CMuxNotify::HrRemoveMiniport.\n" ); + + hr = pAdapter->RemoveMiniport( NULL ); + +#ifdef DISABLE_PROTOCOLS_TO_PHYSICAL + + if ( hr == S_OK ) { + + // + // If this was the last miniport that was removed then, restore the + // bindings of other protocols to the physical adapter. + // + + if ( pAdapter->AllMiniportsRemoved() ) { + + hr = HrFindInstance( m_pnc, + *pguidAdapter, + &pnccAdapter ); + + if ( hr == S_OK ) { + EnableBindings( pnccAdapter, + TRUE ); + + ReleaseObj( pnccAdapter ); + } + } + } +#endif + + TraceMsg( L"<--CMuxNotify::HrRemoveMiniport(HRESULT = %x).\n", + hr ); + + return hr; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::OnInitDialog +// +// Purpose: Initialize controls +// +// Arguments: +// IN hWnd: Window handle to the property page. +// +// Returns: TRUE. +// +// Notes: +// + +LRESULT CMuxNotify::OnInitDialog (IN HWND hWndPage) +{ + m_eApplyAction = eActUnknown; + + ::SendMessage(GetDlgItem(hWndPage, IDC_ADD), BM_SETCHECK, BST_CHECKED, 0); + ::SendMessage(GetDlgItem(hWndPage, IDC_REMOVE), BM_SETCHECK, BST_UNCHECKED, 0); + + return TRUE; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::OnOk +// +// Purpose: Do actions when OK is pressed +// +// Arguments: +// IN hWnd: Window handle to the property page. +// +// Returns: PSNRET_NOERROR +// +// Notes: +// + +LRESULT CMuxNotify::OnOk (IN HWND hWndPage) +{ + TraceMsg(L"-->CMuxNotify::OnOk\n"); + + if ( ::SendMessage(GetDlgItem(hWndPage, IDC_ADD), + BM_GETCHECK, 0, 0) == BST_CHECKED ) { + + m_eApplyAction = eActPropertyUIAdd; + } + else { + m_eApplyAction = eActPropertyUIRemove; + } + + // + // Set the property sheet changed flag if any of our controls + // get changed. This is important so that we get called to + // apply our property changes. + // + + PropSheet_Changed( GetParent(hWndPage), hWndPage); + + TraceMsg(L"<--CMuxNotify::OnOk(Action = %s).\n", + (m_eApplyAction == eActPropertyUIAdd) ? L"Add" : L"Remove" ); + + return PSNRET_NOERROR; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::OnCancel +// +// Purpose: Do actions when CANCEL is pressed +// +// Arguments: +// IN hWnd: Window handle to the property page. +// +// Returns: FALSE +// +// Notes: +// +LRESULT CMuxNotify::OnCancel (IN HWND hWndPage) +{ + UNREFERENCED_PARAMETER(hWndPage); + + TraceMsg(L"-->CMuxNotify::OnCancel\n"); + + m_eApplyAction = eActUnknown; + + TraceMsg(L"<--CMuxNotify::OnCancel\n"); + + return FALSE; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotifyDialogProc +// +// Purpose: Dialog proc +// +// Arguments: +// IN hWnd : See win32 documentation. +// IN uMsg : See win32 documentation. +// IN wParam: See win32 documentation. +// IN lParam: See win32 documentation. +// +// Returns: See win32 documentation. +// +// Notes: +// +INT_PTR CALLBACK NotifyDialogProc (HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + CMuxNotify *psf; + LRESULT lRes=FALSE; + + UNREFERENCED_PARAMETER(wParam); + + if ( uMsg != WM_INITDIALOG ) { + + psf = (CMuxNotify *)::GetWindowLongPtr( hWnd, + DWLP_USER ); + + // Until we get WM_INITDIALOG, just return FALSE + + if ( !psf ) { + + return lRes; + } + } + + switch( uMsg ) { + + case WM_INITDIALOG: + { + PROPSHEETPAGE* ppsp; + ppsp = (PROPSHEETPAGE *)lParam; + + psf = (CMuxNotify *)ppsp->lParam; + + SetWindowLongPtr( hWnd, + DWLP_USER, + (LONG_PTR)psf); + + lRes = psf->OnInitDialog( hWnd ); + } + break; + + case WM_COMMAND: + + break; + + case WM_NOTIFY: + { + LPNMHDR pnmh = (LPNMHDR)lParam; + + switch (pnmh->code) { + + case PSN_KILLACTIVE: + + // + // ok to loose focus. + // + + SetWindowLongPtr( hWnd, DWLP_MSGRESULT, FALSE); + + lRes = TRUE; + break; + + case PSN_APPLY: + + psf = (CMuxNotify *)::GetWindowLongPtr( hWnd, DWLP_USER); + lRes = psf->OnOk( hWnd ); + + SetWindowLongPtr( hWnd, DWLP_MSGRESULT, lRes); + lRes = TRUE; + break; + + case PSN_RESET: + + psf = (CMuxNotify *)::GetWindowLongPtr( hWnd, DWLP_USER); + psf->OnCancel( hWnd ); + } + } + } + + return lRes; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotifyPropSheetPageProc +// +// Purpose: Prop sheet proc +// +// Arguments: +// IN hWnd: See win32 documentation +// IN uMsg: See win32 documentation +// IN ppsp: See win32 documentation +// +// Returns: See win32 documentation +// +// Notes: +// + +UINT CALLBACK NotifyPropSheetPageProc(HWND hWnd, + UINT uMsg, + LPPROPSHEETPAGE ppsp) +{ + UNREFERENCED_PARAMETER(hWnd); + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(ppsp); + + return TRUE; +} + + +#ifdef DISABLE_PROTOCOLS_TO_PHYSICAL + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::EnableBindings +// +// Purpose: Enable/Disable the bindings of other protocols to +// the physical adapter. +// +// Arguments: +// IN pnccAdapter: Pointer to the physical adapter. +// IN bEnable: TRUE/FALSE to enable/disable respectively. +// +// Returns: None. +// +// Notes: +// + +VOID CMuxNotify::EnableBindings (INetCfgComponent *pnccAdapter, + BOOL bEnable) +{ + IEnumNetCfgBindingPath *pencbp; + INetCfgBindingPath *pncbp; + HRESULT hr; + + TraceMsg( L"-->CMuxNotify::EnableBindings.\n" ); + + + // + // Get the binding path enumerator. + // + + hr = HrGetBindingPathEnum( pnccAdapter, + EBP_ABOVE, + &pencbp ); + if ( hr == S_OK ) { + + hr = HrGetBindingPath( pencbp, + &pncbp ); + + // + // Traverse each binding path. + // + + while( hr == S_OK ) { + + // + // If our protocol does exist in the binding path then, + // disable it. + // + + if ( !IfExistMux(pncbp) ) { + + pncbp->Enable( bEnable ); + } + + ReleaseObj( pncbp ); + + hr = HrGetBindingPath( pencbp, + &pncbp ); + } + + ReleaseObj( pencbp ); + } + else { + TraceMsg( L" Couldn't get the binding path enumerator, " + L"bindings will not be %s.\n", + bEnable ? L"enabled" : L"disabled" ); + } + + TraceMsg( L"<--CMuxNotify::EnableBindings.\n" ); + + return; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::IfExistMux +// +// Purpose: Determine if a given binding path contains our protocol. +// +// Arguments: +// IN pncbp: Pointer to the binding path. +// +// Returns: TRUE if our protocol exists, otherwise FALSE. +// +// Notes: +// + +BOOL CMuxNotify::IfExistMux (INetCfgBindingPath *pncbp) +{ + IEnumNetCfgBindingInterface *pencbi; + INetCfgBindingInterface *pncbi; + INetCfgComponent *pnccUpper; + LPWSTR lpszIdUpper; + HRESULT hr; + BOOL bExist = FALSE; + + TraceMsg( L"-->CMuxNotify::IfExistMux.\n" ); + + // + // Get the binding interface enumerator. + // + + hr = HrGetBindingInterfaceEnum( pncbp, + &pencbi ); + + if ( hr == S_OK ) { + + // + // Traverse each binding interface. + // + + hr = HrGetBindingInterface( pencbi, + &pncbi ); + + while( !bExist && (hr == S_OK) ) { + + // + // Is the upper component our protocol? + // + + hr = pncbi->GetUpperComponent( &pnccUpper ); + + if ( hr == S_OK ) { + + hr = pnccUpper->GetId( &lpszIdUpper ); + + if ( hr == S_OK ) { + + bExist = !_wcsicmp( lpszIdUpper, c_szMuxProtocol ); + + CoTaskMemFree( lpszIdUpper ); + } + else { + TraceMsg( L" Failed to get the upper component of the interface.\n" ); + } + + ReleaseObj( pnccUpper ); + } + else { + TraceMsg( L" Failed to get the upper component of the interface.\n" ); + } + + ReleaseObj( pncbi ); + + if ( !bExist ) { + hr = HrGetBindingInterface( pencbi, + &pncbi ); + } + } + + ReleaseObj( pencbi ); + } + else { + TraceMsg( L" Couldn't get the binding interface enumerator.\n" ); + } + + TraceMsg( L"<--CMuxNotify::IfExistMux(BOOL = %x).\n", + bExist ); + + return bExist; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::HrGetBindingPathEnum +// +// Purpose: Returns the binding path enumerator. +// +// Arguments: +// IN pnccAdapter : Pointer to the physical adapter. +// IN dwBindingType: Type of binding path enumerator. +// OUT ppencbp : Pointer to the binding path enumerator. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT CMuxNotify::HrGetBindingPathEnum ( + INetCfgComponent *pnccAdapter, + DWORD dwBindingType, + IEnumNetCfgBindingPath **ppencbp) +{ + INetCfgComponentBindings *pnccb = NULL; + HRESULT hr; + + *ppencbp = NULL; + + hr = pnccAdapter->QueryInterface( IID_INetCfgComponentBindings, + (PVOID *)&pnccb ); + + if ( hr == S_OK ) { + hr = pnccb->EnumBindingPaths( dwBindingType, + ppencbp ); + + ReleaseObj( pnccb ); + } + + return hr; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::HrGetBindingPath +// +// Purpose: Returns a binding path. +// +// Arguments: +// IN pencbp : Pointer to the binding path enumerator. +// OUT ppncbp : Pointer to the binding path. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT CMuxNotify::HrGetBindingPath (IEnumNetCfgBindingPath *pencbp, + INetCfgBindingPath **ppncbp) +{ + ULONG ulCount; + HRESULT hr; + + *ppncbp = NULL; + + hr = pencbp->Next( 1, + ppncbp, + &ulCount ); + + return hr; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::HrGetBindingInterfaceEnum +// +// Purpose: Returns the binding interface enumerator. +// +// Arguments: +// IN pncbp : Pointer to the binding path. +// OUT ppencbi: Pointer to the binding path enumerator. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT CMuxNotify::HrGetBindingInterfaceEnum ( + INetCfgBindingPath *pncbp, + IEnumNetCfgBindingInterface **ppencbi) +{ + HRESULT hr; + + *ppencbi = NULL; + + hr = pncbp->EnumBindingInterfaces( ppencbi ); + + return hr; +} + +// ---------------------------------------------------------------------- +// +// Function: CMuxNotify::HrGetBindingInterface +// +// Purpose: Returns a binding interface. +// +// Arguments: +// IN pencbi : Pointer to the binding interface enumerator. +// OUT ppncbi : Pointer to the binding interface. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT CMuxNotify::HrGetBindingInterface ( + IEnumNetCfgBindingInterface *pencbi, + INetCfgBindingInterface **ppncbi) +{ + ULONG ulCount; + HRESULT hr; + + *ppncbi = NULL; + + hr = pencbi->Next( 1, + ppncbi, + &ulCount ); + + return hr; +} + +#endif + diff --git a/network/ndis/mux/notifyob/notify.h b/network/ndis/mux/notifyob/notify.h new file mode 100644 index 00000000..08f16e00 --- /dev/null +++ b/network/ndis/mux/notifyob/notify.h @@ -0,0 +1,282 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: N O T I F Y . H +// +// Contents: Header file for sample notify object. +// +// Notes: +// +// Author: Alok Sinha +// +//---------------------------------------------------------------------------- + +#ifndef NOTIFY_H_INCLUDE + +#define NOTIFY_H_INCLUDE + +#include + +#include +extern CComModule _Module; // required by atlcom.h +#include +#include +#include + +#include +#include "list.h" +#include "adapter.h" +#include "resource.h" +#include "common.h" + + +// +// CMuxNotify Object - Base class for the entire notify object +// + + +class CMuxNotify : + + // + // Must inherit from CComObjectRoot(Ex) for reference count + // management and default threading model. + // + + public CComObjectRoot, + + // + // Define the default class factory and aggregation model. + // + + public CComCoClass, + + // + // Notify Object's interfaces. + // + + public INetCfgComponentControl, + public INetCfgComponentSetup, + public INetCfgComponentPropertyUi, + public INetCfgComponentNotifyBinding, + public INetCfgComponentNotifyGlobal +{ + + // + // Public members. + // + + public: + + // + // Constructor + // + + CMuxNotify(VOID); + + // + // Destructors. + // + + ~CMuxNotify(VOID); + + // + // Notify Object's interfaces. + // + + BEGIN_COM_MAP(CMuxNotify) + COM_INTERFACE_ENTRY(INetCfgComponentControl) + COM_INTERFACE_ENTRY(INetCfgComponentSetup) + COM_INTERFACE_ENTRY(INetCfgComponentPropertyUi) + COM_INTERFACE_ENTRY(INetCfgComponentNotifyBinding) + COM_INTERFACE_ENTRY(INetCfgComponentNotifyGlobal) + END_COM_MAP() + + // + // Uncomment the the line below if you don't want your object to + // support aggregation. The default is to support it + // + // DECLARE_NOT_AGGREGATABLE(CMuxNotify) + // + + DECLARE_REGISTRY_RESOURCEID(IDR_REG_SAMPLE_NOTIFY) + + // + // INetCfgComponentControl + // + + STDMETHOD (Initialize) ( + IN INetCfgComponent *pIComp, + IN INetCfg *pINetCfg, + IN BOOL fInstalling); + + STDMETHOD (CancelChanges) (); + + STDMETHOD (ApplyRegistryChanges) (); + + STDMETHOD (ApplyPnpChanges) ( + IN INetCfgPnpReconfigCallback* pICallback); + + // + // INetCfgComponentSetup + // + + STDMETHOD (Install) ( + IN DWORD dwSetupFlags); + + STDMETHOD (Upgrade) ( + IN DWORD dwSetupFlags, + IN DWORD dwUpgradeFromBuildNo); + + STDMETHOD (ReadAnswerFile) ( + IN PCWSTR szAnswerFile, + IN PCWSTR szAnswerSections); + + STDMETHOD (Removing) (); + + // + // INetCfgComponentPropertyUi + // + + STDMETHOD (QueryPropertyUi) ( + IN IUnknown* pUnk); + + STDMETHOD (SetContext) ( + IN IUnknown* pUnk); + + STDMETHOD (MergePropPages) ( + IN OUT DWORD* pdwDefPages, + OUT LPBYTE* pahpspPrivate, + OUT UINT* pcPrivate, + IN HWND hwndParent, + OUT PCWSTR* pszStartPage); + + STDMETHOD (ValidateProperties) ( + HWND hwndSheet); + + STDMETHOD (CancelProperties) (); + + STDMETHOD (ApplyProperties) (); + + // + // INetCfgNotifyBinding + // + + STDMETHOD (QueryBindingPath) ( + IN DWORD dwChangeFlag, + IN INetCfgBindingPath* pncbp); + + STDMETHOD (NotifyBindingPath) ( + IN DWORD dwChangeFlag, + IN INetCfgBindingPath* pncbp); + + // + // INetCfgNotifyGlobal + // + + STDMETHOD (GetSupportedNotifications) ( + OUT DWORD* pdwNotificationFlag ); + + STDMETHOD (SysQueryBindingPath) ( + IN DWORD dwChangeFlag, + IN INetCfgBindingPath* pncbp); + + STDMETHOD (SysNotifyBindingPath) ( + IN DWORD dwChangeFlag, + IN INetCfgBindingPath* pncbp); + + STDMETHOD (SysNotifyComponent) ( + IN DWORD dwChangeFlag, + IN INetCfgComponent* pncc); + + // + // Private members. + // + + private: + + // + // Private member variables. + // + + INetCfgComponent *m_pncc; // Protocol's Net Config component + INetCfg *m_pnc; + ConfigAction m_eApplyAction; + IUnknown* m_pUnkContext; + + // + // List of physical adapters currently installed. + // + + List m_AdaptersList; + + // + // List of physical adapters to be removed. + // + + List m_AdaptersToRemove; + + // + // List of physical adapters to be added. + // + + List m_AdaptersToAdd; + + // + // Private member functions. + // + + HRESULT HrLoadAdapterConfiguration (VOID); + + HRESULT HrGetUpperAndLower (INetCfgBindingPath* pncbp, + INetCfgComponent **ppnccUpper, + INetCfgComponent **ppnccLower); + + HRESULT HrAddAdapter (INetCfgComponent *pnccAdapter); + + HRESULT HrRemoveAdapter (INetCfgComponent *pnccAdapter); + + HRESULT HrAddMiniport (CMuxPhysicalAdapter *pAdapter, + GUID *guidAdapter); + + HRESULT HrRemoveMiniport (CMuxPhysicalAdapter *pAdapter, + GUID *guidAdapter); + +#ifdef DISABLE_PROTOCOLS_TO_PHYSICAL + + VOID EnableBindings (INetCfgComponent *pnccAdapter, + BOOL bEnable); + + BOOL IfExistMux (INetCfgBindingPath *pncbp); + + HRESULT HrGetBindingPathEnum (INetCfgComponent *pnccAdapter, + DWORD dwBindingType, + IEnumNetCfgBindingPath **ppencbp); + + HRESULT HrGetBindingPath (IEnumNetCfgBindingPath *pencbp, + INetCfgBindingPath **ppncbp); + + HRESULT HrGetBindingInterfaceEnum (INetCfgBindingPath *pncbp, + IEnumNetCfgBindingInterface **ppencbi); + + HRESULT HrGetBindingInterface (IEnumNetCfgBindingInterface *pencbi, + INetCfgBindingInterface **ppncbi); +#endif + + public: + + LRESULT OnInitDialog (IN HWND hWnd); + LRESULT OnOk (IN HWND hWnd); + LRESULT OnCancel (IN HWND hWnd); +}; + + +INT_PTR CALLBACK NotifyDialogProc (HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); +UINT CALLBACK NotifyPropSheetPageProc (HWND hWnd, + UINT uMsg, + LPPROPSHEETPAGE ppsp); +#endif // NOTIFY_H_INCLUDE diff --git a/network/ndis/mux/notifyob/notify.htm b/network/ndis/mux/notifyob/notify.htm new file mode 100644 index 00000000..6fe84b68 --- /dev/null +++ b/network/ndis/mux/notifyob/notify.htm @@ -0,0 +1,552 @@ + + + + + + + + +Mux.dll - Sample Notify Object + + + + + + + + + + +
+ +

MUX.DLL – Sample Notify Object

+ +

SUMMARY

+ +

MUX Notify Object

+ +

This sample demonstrates +how to write a notify object for installing and configuring an NDIS MUX intermediate +miniport (IM) driver that implements an N:1 relationship between upper and +lower bindings, i.e. it creates multiple virtual network devices on top of a +single lower adapter. Protocols bind to these virtual adapters as if they are +real adapters. Examples of Intermediate + Miniport drivers that can +use this type of notify object are Virtual LAN (VLAN) +drivers.

+ +

Operation

+ +

During installation, the +notify object performs the following operations.

+ +

·         +It +creates one virtual adapter for each physical adapter the MUX protocol edge +binds to.

+ +

·         +It +disables the bindings of other protocols such as TCP/IP to physical adapters if +it has been compiled with DISABLE_PROTOCOLS_TO_PHYSICAL defined in the Sources +file. This is the most commonly desired behavior for N:1 MUX drivers.

+ +

·         +It +disables the bindings of the protocol edge of the MUX IM driver with all its +virtual adapters.

+ +

The notify object +provides a custom property page for the MUX IM driver. The custom property page +allows the user to add one or more virtual adapters on top of a physical +adapter or delete an existing virtual adapter.

+ +

When the MUX IM driver is +uninstalled, or binding is disabled, or the user deletes all the virtual +adapters on top of a physical adapter, the notify object restores the bindings +of other protocols to the physical adapter if it has been compiled with the +preprocessor flag DISABLE_PROTOCOLS_TO_PHYSICAL defined in the Sources file.

+ +

Windows 2000 Specific

+ +

During the installation, +the notify object copies the INF file for the virtual adapter from the +installation media to the system INF directory by using the SetupCopyOEMInf +function. This is done because Windows 2000 does not support the CopyINF directive to copy INF files from another INF file.

+ +

BUILDING THE SAMPLE

+ +

To +build the sample, type build -ceZ. This command produces the binary +mux.dll, which is the sample notify object.

+ +

The sample compiles +properly for 32-bit and 64-bit systems, and builds properly with Microsoft® +Visual C® 6.0.

+ +

Preprocessor Flags:

+ +

DISABLE_PROTOCOLS_TO_PHYSICAL

+ +

When this flag is defined +in the Sources file, the notify object disables the bindings of other protocols +such as TCP/IP to the physical adapters during the installation. When all the +virtual adapters are removed either through the custom property page or as a +result of uninstalling the MUX driver, the notify object re-enables those +bindings.

+ +

PASSTHRU_NOTIFY

+ +

This flag is defined to +allow the MUX driver to be used in a passthru mode. When this flag is defined, +the notify object:

+ +

1)     +Creates +only one virtual miniport for every physical adapter the MUX protocol edge +binds to.

+ +

2)     +Disables +the property page to prevent adding of additional virtual miniports.

+ +

3)     +Stores +the device name of the virtual adapter in REG_SZ registry value under +HKLM\System\CurrentControlSet\Services\muxp\Parameters\Adapters\{PhysicalAdaptersInstanceGuid}\UpperBindings, +because there is one to one binding. In the MUX mode (when this flag is not +defined), the notify object stores the device name in a REG_MULTI_SZ registry +value as there could be more than one virtual miniports.

+ +

You +can also use this notify object with the Passthru driver by doing the +following:

+ +

1)     +Change +the protocol name in file src\network\ndis\passthru\passthru.c +from PASSTHRU to MUXP.

+ +

2)     +Change +the driver name from Passthru to MUX in the sources file.

+ +

3)     +Rebuild +the driver to obtain a mux.sys driver binary.

+ +

4)     +Build +the MUX notify object with PASSTHRU_NOTIFY defined.

+ +

5)     +Use +the MUX inf files, muxp.inf and mux_mp.inf, to +install the driver and dll.

+ +

The benefit of using +techniques in the MUX notify object for a 1:1 intermediate driver (e.g. +Passthru) is to be able to exercise higher level of control over the bindings +of MUX with other components in the system, which is not possible with the IM +filter driver.

+ +

CUSTOM_EVENTS

+ +

When this macro is +defined, the notify object shows how to send custom events to the MUX IM driver +when a virtual miniport is added or removed.

+ +

CODE TOUR

+ +

File Manifest

+ +
File           Description
Dllmain.cpp    File containing implementations of DLL entry point and exports. 
Implinc.cpp    File including source code for ATL utilities.
mux.htm        The documentation for this sample.
Pch.h          Pre-compiled header file.
Resource.h     The resource header.
notify.cpp     Notify object implementation.
virtual.cpp    Virtual adapter class implementation.
adapter.cpp    Physical adapter class implementation.
common.cpp     Global functions, variables and macro definitions.
mux.def        The sample's linker definition file.
mux.h          The header file for mux.cpp.
virtual.h      Virtual adapter class declaration.
Adapter.h      Physical adapter class declaration.
notify.rc      The resource file.
notify.rgs     Script-based registry.
notifyn.idl    The notify object's .idl file.
setupdi.cpp    Functions to copy the miniport INF file to the system.
 
+ +

Top of page

+ + + + + +
+

 

+
+ +

© 2004 Microsoft +Corporation

+ +
+ + + + + \ No newline at end of file diff --git a/network/ndis/mux/notifyob/notifyn.idl b/network/ndis/mux/notifyob/notifyn.idl new file mode 100644 index 00000000..319ddbfd --- /dev/null +++ b/network/ndis/mux/notifyob/notifyn.idl @@ -0,0 +1,34 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: NOTIFYN.IDL +// +// Contents: +// +// Notes: +// +//---------------------------------------------------------------------------- +#include + +[ + uuid(58dbe723-da1a-484c-9f5b-113ba31869f2), + version(1.0), + helpstring("Sample Notify Object 1.0 Type Library") +] +library SampleNotifyLib +{ + [ + uuid(ee6cd6fc-a32b-4a26-8a1c-dbc97988ad7a), + helpstring("Sample MUX Notify Class") + ] + coclass CMuxNotify + { + [restricted] interface INetCfgComponentControl; + [restricted] interface INetCfgComponentSetup; + [restricted] interface INetCfgComponentNotifyBinding; + [restricted] interface INetCfgComponentNotifyGlobal; + [restricted] interface INetCfgComponentPropertyUi; + }; +}; diff --git a/network/ndis/mux/notifyob/public.h b/network/ndis/mux/notifyob/public.h new file mode 100644 index 00000000..3d6147fc --- /dev/null +++ b/network/ndis/mux/notifyob/public.h @@ -0,0 +1,47 @@ +/*++ +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + public.h + +Abstract: + + This module contains the common declarations shared by driver + and user applications. + +Author: + + +Environment: + + user and kernel +Notes: + + +Revision History: + + +--*/ +#ifndef __PUBLIC_H +#define __PUBLIC_H + +// +// To support ioctls from user-mode: +// + +#define LINKNAME_STRING L"\\DosDevices\\Mux" +#define NTDEVICE_STRING L"\\Device\\Mux" + +#define MUX_CUSTOM_EVENT 1 + +#define NOTIFY_SIGNATURE 0xAFCDABAB + +typedef struct _NOTIFY_CUSTOM_EVENT { + ULONG uSignature; + ULONG uEvent; + WCHAR szMiniport[1]; +} NOTIFY_CUSTOM_EVENT, *PNOTIFY_CUSTOM_EVENT; +#endif + + diff --git a/network/ndis/mux/notifyob/resource.h b/network/ndis/mux/notifyob/resource.h new file mode 100644 index 00000000..498c95d8 --- /dev/null +++ b/network/ndis/mux/notifyob/resource.h @@ -0,0 +1,12 @@ +#ifndef RESOURCE_H_INCLUDE + +#define RESOURCE_H_INCLUDE + +#define IDR_REG_SAMPLE_NOTIFY 40001 + +#define IDD_NOTIFY_GENERAL 1850 +#define IDC_STATIC -1 +#define IDC_ADD 100 +#define IDC_REMOVE 200 + +#endif diff --git a/network/ndis/mux/notifyob/virtual.cpp b/network/ndis/mux/notifyob/virtual.cpp new file mode 100644 index 00000000..5b6cde80 --- /dev/null +++ b/network/ndis/mux/notifyob/virtual.cpp @@ -0,0 +1,512 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: V I R T U A L . C P P +// +// Contents: Virtual miniport class definition. +// +// Notes: +// +// Author: Alok Sinha +//---------------------------------------------------------------------------- + +#include "virtual.h" +#include "common.h" + +//+--------------------------------------------------------------------------- +// +// Function: CMuxVirtualMiniport::CMuxVirtualMiniport +// +// Purpose: Constructor for class CMuxVirtualMiniport +// +// Arguments: None +// +// Returns: +// +// Notes: +// + +CMuxVirtualMiniport::CMuxVirtualMiniport(INetCfg *pnc, + GUID *pguidMiniport, + GUID *pguidAdapter) +{ + TraceMsg( L"-->CMuxVirtualMiniport::CMuxVirtualMiniport(Constructor).\n" ); + + m_pnc = pnc; + m_pnc->AddRef(); + + CopyMemory( &m_guidAdapter, + pguidAdapter, + sizeof(GUID) ); + + if ( pguidMiniport ) { + + CopyMemory( &m_guidMiniport, + pguidMiniport, + sizeof(GUID) ); + + } + else { + + ZeroMemory( &m_guidMiniport, + sizeof(GUID) ); + } + + TraceMsg( L"<--CMuxVirtualMiniport::CMuxVirtualMiniport(Constructor).\n" ); +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxVirtualMiniport::~CMuxVirtualMiniport +// +// Purpose: Destructor for class CMuxVirtualMiniport +// +// Arguments: None +// +// Returns: +// +// Notes: +// + +CMuxVirtualMiniport::~CMuxVirtualMiniport(VOID) +{ + TraceMsg( L"-->CMuxVirtualMiniport::~CMuxVirtualMiniport(Destructor).\n" ); + + ReleaseObj( m_pnc ); + + TraceMsg( L"<--CMuxVirtualMiniport::~CMuxVirtualMiniport(Destructor).\n" ); + +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxVirtualMiniport::LoadConfiguration +// +// Purpose: Load miniport configuration from the registry. +// +// Arguments: None +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT CMuxVirtualMiniport::LoadConfiguration(VOID) +{ + TraceMsg( L"-->CMuxVirtualMiniport::LoadConfiguration.\n" ); + + TraceMsg( L"<--CMuxVirtualMiniport::LoadConfiguration(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxVirtualMiniport::GetAdapterGUID +// +// Purpose: Returns the adapter GUID. +// +// Arguments: +// OUT pguidAdapter: GUID of the adapter returned. +// +// Returns: None. +// +// Notes: +// + +VOID CMuxVirtualMiniport::GetAdapterGUID (GUID *pguidAdapter) +{ + TraceMsg( L"-->CMuxVirtualMiniport::GetAdapterGUID.\n" ); + + CopyMemory( pguidAdapter, + &m_guidAdapter, + sizeof(GUID) ); + + TraceMsg( L"<--CMuxVirtualMiniport::GetAdapterGUID.\n" ); +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxVirtualMiniport::GetMiniportGUID +// +// Purpose: Returns the miniport GUID. +// +// Arguments: +// OUT pguidMiniport: GUID of the miniport returned. +// +// Returns: None. +// +// Notes: +// + +VOID CMuxVirtualMiniport::GetMiniportGUID (GUID *pguidMiniport) +{ + TraceMsg( L"-->CMuxVirtualMiniport::GetMiniportGUID.\n" ); + + CopyMemory( pguidMiniport, + &m_guidMiniport, + sizeof(GUID) ); + + TraceMsg( L"<--CMuxVirtualMiniport::GetMiniportGUID.\n" ); +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxVirtualMiniport::Install +// +// Purpose: Installs a virtual miniport. +// +// Arguments: None +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT CMuxVirtualMiniport::Install (VOID) +{ + INetCfgClass *pncClass; + INetCfgClassSetup *pncClassSetup; + INetCfgComponent *pnccMiniport; + HRESULT hr; + LPWSTR *pmszwRefs=NULL; + OBO_TOKEN *pOboToken=NULL; + DWORD dwSetupFlags=0; + LPCWSTR pszwAnswerFile=NULL; + LPCWSTR pszwAnswerSections=NULL; + + + TraceMsg( L"-->CMuxVirtualMiniport::Install.\n" ); + + hr = m_pnc->QueryNetCfgClass( &GUID_DEVCLASS_NET, + IID_INetCfgClass, + (void **)&pncClass ); + if ( hr == S_OK ) { + + hr = pncClass->QueryInterface( IID_INetCfgClassSetup, + (void **)&pncClassSetup ); + if ( hr == S_OK ) { + + hr = pncClassSetup->Install( c_szMuxMiniport, + pOboToken, + dwSetupFlags, + 0, + pszwAnswerFile, + pszwAnswerSections, + &pnccMiniport ); + if ( hr == S_OK ) { + + hr = pnccMiniport->GetInstanceGuid( &m_guidMiniport ); + + if ( hr != S_OK ) { + + TraceMsg( L" Failed to get the instance guid, uninstalling " + L" the miniport.\n" ); + + pncClassSetup->DeInstall( pnccMiniport, + pOboToken, + pmszwRefs ); + } + + ReleaseObj( pnccMiniport ); + } + else { + + TraceMsg( L" Failed to install the miniport.\n" ); + } + + ReleaseObj( pncClassSetup ); + } + else { + + TraceMsg( L" QueryInterface failed.\n" ); + } + + ReleaseObj( pncClass ); + } + else { + + TraceMsg( L" QueryNetCfgClass failed.\n" ); + } + + TraceMsg( L"<--CMuxVirtualMiniport::Install(HRESULT = %x).\n", + hr ); + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxVirtualMiniport::DeInstall +// +// Purpose: Uninstalls the virtual miniport. +// +// Arguments: None +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT CMuxVirtualMiniport::DeInstall (VOID) +{ + INetCfgClass *pncClass; + INetCfgClassSetup *pncClassSetup; + INetCfgComponent *pnccMiniport; + HRESULT hr; + LPWSTR *pmszwRefs=NULL; + OBO_TOKEN *pOboToken=NULL; + + TraceMsg( L"-->CMuxVirtualMiniport::DeInstall.\n" ); + + hr = m_pnc->QueryNetCfgClass( &GUID_DEVCLASS_NET, + IID_INetCfgClass, + (void **)&pncClass ); + if ( hr == S_OK ) { + + hr = pncClass->QueryInterface( IID_INetCfgClassSetup, + (void **)&pncClassSetup ); + if ( hr == S_OK ) { + + hr = HrFindInstance( m_pnc, + m_guidMiniport, + &pnccMiniport ); + + if ( hr == S_OK ) { + + TraceMsg( L" Found the miniport instance to uninstall.\n" ); + + hr = pncClassSetup->DeInstall( pnccMiniport, + pOboToken, + pmszwRefs ); + ReleaseObj( pnccMiniport ); + } + else { + TraceMsg( L" Didn't find the miniport instance to uninstall.\n" ); + } + + ReleaseObj( pncClassSetup ); + } + else { + + TraceMsg( L" QueryInterface failed.\n" ); + } + + ReleaseObj( pncClass ); + } + else { + + TraceMsg( L" QueryNetCfgClass failed.\n" ); + } + + TraceMsg( L"<--CMuxVirtualMiniport::DeInstall(HRESULT = %x).\n", + hr ); + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxVirtualMiniport::ApplyRegistryChanges +// +// Purpose: Store the changes in the registry. +// +// Arguments: +// IN eApplyAction: Action performed. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT CMuxVirtualMiniport::ApplyRegistryChanges(ConfigAction eApplyAction) +{ + HKEY hkeyAdapterGuid; + WCHAR szAdapterGuid[MAX_PATH+1]; + WCHAR szAdapterGuidKey[MAX_PATH+1]; + WCHAR szMiniportGuid[MAX_PATH+1]; + LPWSTR lpDevice; + LONG lResult = 0; + + TraceMsg( L"-->CMuxVirtualMiniport::ApplyRegistryChanges.\n" ); + + switch( eApplyAction ) { + + case eActAdd: // Virtual miniport added. + + StringFromGUID2( m_guidAdapter, + szAdapterGuid, + MAX_PATH+1 ); + + StringCchPrintfW ( szAdapterGuidKey, + celems(szAdapterGuidKey), + L"%s\\%s", + c_szAdapterList, + szAdapterGuid ); + + szAdapterGuidKey[MAX_PATH]='\0'; + lResult = RegCreateKeyExW( HKEY_LOCAL_MACHINE, + szAdapterGuidKey, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hkeyAdapterGuid, + NULL); + + + if ( lResult == ERROR_SUCCESS ) { + + StringFromGUID2( m_guidMiniport, + szMiniportGuid, + MAX_PATH+1 ); + + lpDevice = AddDevicePrefix( szMiniportGuid ); + + if ( lpDevice ) { + +#ifndef PASSTHRU_NOTIFY + + lResult = AddToMultiSzValue( hkeyAdapterGuid, + lpDevice ); +#else + + lResult = RegSetValueExW( hkeyAdapterGuid, + c_szUpperBindings, + 0, + REG_SZ, + (LPBYTE)lpDevice, + (wcslen(lpDevice) + 1) * + sizeof(WCHAR) ); + + +#endif + + if ( lResult != ERROR_SUCCESS ) { + + TraceMsg( L" Failed to save %s at %s\\%s.\n", + lpDevice, + szAdapterGuidKey, + c_szUpperBindings ); + + } + + free( lpDevice ); + } + else { + lResult = ERROR_NOT_ENOUGH_MEMORY; + } + + RegCloseKey( hkeyAdapterGuid ); + } + else { + TraceMsg( L" Failed to open the registry key: %s.\n", + szAdapterGuidKey ); + } + break; + + case eActRemove: // Virtual miniport removed. + + StringFromGUID2( m_guidAdapter, + szAdapterGuid, + MAX_PATH+1 ); + + StringCchPrintfW( szAdapterGuidKey, + celems(szAdapterGuidKey), + L"%s\\%s", + c_szAdapterList, + szAdapterGuid ); + szAdapterGuidKey[MAX_PATH]='\0'; + lResult = RegCreateKeyExW( HKEY_LOCAL_MACHINE, + szAdapterGuidKey, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hkeyAdapterGuid, + NULL); + + + if ( lResult == ERROR_SUCCESS ) { + + StringFromGUID2( m_guidMiniport, + szMiniportGuid, + MAX_PATH+1 ); + + lpDevice = AddDevicePrefix( szMiniportGuid ); + TraceMsg( L" Deleting %s at %s.\n", + lpDevice, + szAdapterGuidKey ); + + if ( lpDevice ) { + +#ifndef PASSTHRU_NOTIFY + + lResult = DeleteFromMultiSzValue( hkeyAdapterGuid, + lpDevice ); +#else + + lResult = RegDeleteValueW( hkeyAdapterGuid, + c_szUpperBindings ); +#endif + + if ( lResult != ERROR_SUCCESS ) { + + TraceMsg( L" Failed to delete %s at %s\\%s.\n", + lpDevice, + szAdapterGuidKey, + c_szUpperBindings ); + + } + + free( lpDevice ); + } + + RegCloseKey( hkeyAdapterGuid ); + } + else { + TraceMsg( L" Failed to open the registry key: %s.\n", + szAdapterGuidKey ); + } + } + + TraceMsg( L"<--CMuxVirtualMiniport::ApplyRegistryChanges(HRESULT = %x).\n", + HRESULT_FROM_WIN32(lResult) ); + + return HRESULT_FROM_WIN32(lResult); +} + +//+--------------------------------------------------------------------------- +// +// Function: CMuxVirtualMiniport::ApplyPnpChanges +// +// Purpose: +// +// Arguments: +// IN eApplyAction: Action performed. +// +// Returns: S_OK on success, otherwise an error code. +// +// Notes: +// + +HRESULT CMuxVirtualMiniport::ApplyPnpChanges + (INetCfgPnpReconfigCallback *pfCallback, + ConfigAction eApplyAction) +{ + UNREFERENCED_PARAMETER(pfCallback); + UNREFERENCED_PARAMETER(eApplyAction); + + TraceMsg( L"-->CMuxVirtualMiniport::ApplyPnpChanges.\n" ); + + TraceMsg( L"<--CMuxVirtualMiniport::ApplyPnpChanges(HRESULT = %x).\n", + S_OK ); + + return S_OK; +} + diff --git a/network/ndis/mux/notifyob/virtual.h b/network/ndis/mux/notifyob/virtual.h new file mode 100644 index 00000000..5cb8f050 --- /dev/null +++ b/network/ndis/mux/notifyob/virtual.h @@ -0,0 +1,68 @@ +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992-2001. +// +// File: V I R T U A L . H +// +// Contents: Header file for virtual miniport class. +// +// Notes: +// +// Author: Alok Sinha +// +//---------------------------------------------------------------------------- + +#ifndef VIRTUAL_H_INCLUDED + +#define VIRTUAL_H_INCLUDE + +#include +#include +#include "netcfgn.h" + +#include "common.h" + +// +// Class to represent a virtual miniport created by IM driver. +// + +class CMuxVirtualMiniport +{ + // + // Private member variables. + // + + INetCfg *m_pnc; + GUID m_guidAdapter; + GUID m_guidMiniport; + + // + // Public members. + // + + public: + + CMuxVirtualMiniport(INetCfg *m_pnc, + GUID *pguidMiniport, + GUID *guidAdapter); + + virtual ~CMuxVirtualMiniport(VOID); + + HRESULT LoadConfiguration(VOID); + + VOID GetAdapterGUID (GUID *); + + VOID GetMiniportGUID (GUID *); + + HRESULT Install (VOID); + + HRESULT DeInstall (VOID); + + HRESULT ApplyRegistryChanges (ConfigAction eApplyAction); + + HRESULT ApplyPnpChanges (INetCfgPnpReconfigCallback *pfCallback, + ConfigAction eApplyAction); +}; + +#endif // VIRTUAL_H_INCLUDED \ No newline at end of file diff --git a/network/ndis/ndisprot/6x/ReadMe.md b/network/ndis/ndisprot/6x/ReadMe.md new file mode 100644 index 00000000..63e3a5ec --- /dev/null +++ b/network/ndis/ndisprot/6x/ReadMe.md @@ -0,0 +1,84 @@ +NDIS Connection-less Protocol Driver Sample +=========================================== + +This sample demonstrates a connection-less NDIS 6.0 protocol.The driver supports sending and receiving raw Ethernet frames using `ReadFile`/`WriteFile` calls from user-mode. It only receives frames with a specific EtherType field. As an NDIS protocol, it illustrates how to establish and tear down bindings to Ethernet adapters, i.e. those that export medium type **NdisMedium802\_3**. It shows how to set a packet filter, send and receive data, and handle plug-and-play events. + + +INSTALLATION +------------ + +The driver is installed using the INF file ndisprot.inf, which is provided in the driver directory. In Network Connections UI, select an adapter and open **Properties.** + +Click **Install**, then **Protocol**, then **Add**, and then **Have disk**. Then point to the location of the .inf and driver. + +Select **Sample NDIS Protocol Driver** and click **OK**. After installing the protocol, copy over the test application prottest.exe to a convenient location. Please note that the driver service has been set to manual start in the INF file. As a result, it doesn't get loaded automatically when you install. + +Usage +----- + +To start the driver, type **Net start ndisprot**. + +To stop the driver, type **Net stop ndisprot**. + +To test the driver, run **prottest**. For help on usage, run **prottest -?** + +**usage: PROTTEST [options] \** + +**options:** + + ++++ + + + + + + +
-e +Enumerate devices-r +Read
+ +Prottest exercises the IOCTLs supported by NDISPROT, and sends and/or receives data on the selected device. In order to use prottest, the user must have administrative privilege. Users should pass down a big enough buffer in order to receive the entire received data. If the length of the buffer passed down is smaller than the length of the received data, NDISPROT will only copy part of the data and discard the rest when the given buffer is full. + +Use the **–e** option to enumerate all devices to which NDISPROT is bound: + +**C:\\prot\>prottest -n 2 \\DEVICE\\{9273DA7D-5275-4B9A-AC56-68A49D121F1F}** + +**DoWriteProc: finished sending 2 packets of 100 bytes each** + +**DoReadProc finished: read 2 packets** + +**Note**   + +With a checked version of ndisprot.sys, you can control the volume of debug information generated by changing the variable `ndisprotDebugLevel`. Refer to debug.h for more information. + +For more information, see [NDIS Protocol Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff566821) in the network devices design guide. + +File Manifest +------------- + + ++++ + + + + + + + + + + + +
File +Description
prottest.c +User-mode test applicationdebug.c +Routines to aid debugging
+ + diff --git a/network/ndis/ndisprot/6x/ndisprot60.sln b/network/ndis/ndisprot/6x/ndisprot60.sln new file mode 100644 index 00000000..cc13f976 --- /dev/null +++ b/network/ndis/ndisprot/6x/ndisprot60.sln @@ -0,0 +1,63 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "60", "60", "{39A2B1A3-5281-47DF-A0A5-B08E1F979C9E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{C09D8AF9-55C0-4192-A787-B6CA08D83AD0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "630", "630", "{7BA6C25F-BBE2-4331-B598-C5F03E11B45D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{A41A7FEE-D4C4-4D9E-B7B3-679200586B93}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ndisprot60", "sys\60\ndisprot60.vcxproj", "{74918B99-A7A2-4462-9330-D61ED648B991}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ndisprot630", "sys\630\ndisprot630.vcxproj", "{DB252AE8-E32C-4E60-B0EB-33A2CB80861C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "prottest", "test\prottest.vcxproj", "{0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {74918B99-A7A2-4462-9330-D61ED648B991}.Debug|Win32.ActiveCfg = Debug|Win32 + {74918B99-A7A2-4462-9330-D61ED648B991}.Debug|Win32.Build.0 = Debug|Win32 + {74918B99-A7A2-4462-9330-D61ED648B991}.Release|Win32.ActiveCfg = Release|Win32 + {74918B99-A7A2-4462-9330-D61ED648B991}.Release|Win32.Build.0 = Release|Win32 + {74918B99-A7A2-4462-9330-D61ED648B991}.Debug|x64.ActiveCfg = Debug|x64 + {74918B99-A7A2-4462-9330-D61ED648B991}.Debug|x64.Build.0 = Debug|x64 + {74918B99-A7A2-4462-9330-D61ED648B991}.Release|x64.ActiveCfg = Release|x64 + {74918B99-A7A2-4462-9330-D61ED648B991}.Release|x64.Build.0 = Release|x64 + {DB252AE8-E32C-4E60-B0EB-33A2CB80861C}.Debug|Win32.ActiveCfg = Debug|Win32 + {DB252AE8-E32C-4E60-B0EB-33A2CB80861C}.Debug|Win32.Build.0 = Debug|Win32 + {DB252AE8-E32C-4E60-B0EB-33A2CB80861C}.Release|Win32.ActiveCfg = Release|Win32 + {DB252AE8-E32C-4E60-B0EB-33A2CB80861C}.Release|Win32.Build.0 = Release|Win32 + {DB252AE8-E32C-4E60-B0EB-33A2CB80861C}.Debug|x64.ActiveCfg = Debug|x64 + {DB252AE8-E32C-4E60-B0EB-33A2CB80861C}.Debug|x64.Build.0 = Debug|x64 + {DB252AE8-E32C-4E60-B0EB-33A2CB80861C}.Release|x64.ActiveCfg = Release|x64 + {DB252AE8-E32C-4E60-B0EB-33A2CB80861C}.Release|x64.Build.0 = Release|x64 + {0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0}.Debug|Win32.ActiveCfg = Debug|Win32 + {0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0}.Debug|Win32.Build.0 = Debug|Win32 + {0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0}.Release|Win32.ActiveCfg = Release|Win32 + {0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0}.Release|Win32.Build.0 = Release|Win32 + {0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0}.Debug|x64.ActiveCfg = Debug|x64 + {0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0}.Debug|x64.Build.0 = Debug|x64 + {0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0}.Release|x64.ActiveCfg = Release|x64 + {0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {74918B99-A7A2-4462-9330-D61ED648B991} = {39A2B1A3-5281-47DF-A0A5-B08E1F979C9E} + {DB252AE8-E32C-4E60-B0EB-33A2CB80861C} = {7BA6C25F-BBE2-4331-B598-C5F03E11B45D} + {0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0} = {A41A7FEE-D4C4-4D9E-B7B3-679200586B93} + {39A2B1A3-5281-47DF-A0A5-B08E1F979C9E} = {C09D8AF9-55C0-4192-A787-B6CA08D83AD0} + {7BA6C25F-BBE2-4331-B598-C5F03E11B45D} = {C09D8AF9-55C0-4192-A787-B6CA08D83AD0} + EndGlobalSection +EndGlobal diff --git a/network/ndis/ndisprot/6x/sys/60/ndisprot60.inf b/network/ndis/ndisprot/6x/sys/60/ndisprot60.inf new file mode 100644 index 00000000..5618bfcf --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/60/ndisprot60.inf @@ -0,0 +1,90 @@ +;------------------------------------------------------------------------- +; NDISPROT60.INF -- Sample NDIS Protocol Driver +; +; Copyright (c) 2003, Microsoft Corporation +;------------------------------------------------------------------------- +[version] +Signature = "$Windows NT$" +Class = NetTrans +ClassGUID = {4d36e975-e325-11ce-bfc1-08002be10318} +Provider = %Msft% +DriverVer = 10/01/2002,4.2 +CatalogFile = ndisprot60.cat + + +[Manufacturer] +%Msft%=MSFT,NTx86,NTia64,NTamd64 + +[MSFT.NTx86] +%NDISPROT_Desc%=Install, MS_NDISPROT + +[MSFT.NTia64] +%NDISPROT_Desc%=Install, MS_NDISPROT + +[MSFT.NTamd64] +%NDISPROT_Desc%=Install, MS_NDISPROT + +;------------------------------------------------------------------------- +; Installation Section +;------------------------------------------------------------------------- +[Install] +AddReg=Inst_Ndi +Characteristics=0x0 ; +CopyFiles=CpyFiles_Sys + +;------------------------------------------------------------------------- +; Ndi installation support +;------------------------------------------------------------------------- +[Inst_Ndi] +HKR,Ndi,Service,,"Ndisprot" +HKR,Ndi,HelpText,,%NDISPROT_HelpText% +HKR,Ndi\Interfaces, UpperRange,, noupper +HKR,"Ndi\Interfaces","LowerRange",,"ndis5,ndis4,ndis5_prot" + +;------------------------------------------------------------------------- +; Service installation support +;------------------------------------------------------------------------- +[Install.Services] +AddService=Ndisprot,,NDISPROT_Service_Inst + +[NDISPROT_Service_Inst] +DisplayName = %NDISPROT_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\ndisprot60.sys +Description = %NDISPROT_Desc% + +[Install.Remove.Services] +DelService=Ndisprot,0x200 + +;------------------------------------------------------------------------- +; Declare Destination Directories for file copy/deletion +;------------------------------------------------------------------------- +[SourceDisksNames] +1=%DiskDescription%,"",, + +[SourceDisksFiles] +ndisprot60.sys=1 + + +[DestinationDirs] +CpyFiles_Sys = 12 ; DIRID_DRIVERS + + +;------------------------------------------------------------------------- +; Files to Copy/Delete - Referenced by Install and Remove sections above +;------------------------------------------------------------------------- +[CpyFiles_Sys] +Ndisprot60.sys,,,2 + +[Strings] +Msft = "Your Company" +DiskDescription = "Microsoft Ndisprot Sample Protocol Driver Disk" +NDISPROT_Desc = "Sample NDIS Protocol Driver" +NDISPROT_HelpText = "A driver to support user-mode I/O on NDIS devices" + + + + + diff --git a/network/ndis/ndisprot/6x/sys/60/ndisprot60.rc b/network/ndis/ndisprot/6x/sys/60/ndisprot60.rc new file mode 100644 index 00000000..59a37c53 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/60/ndisprot60.rc @@ -0,0 +1,49 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + ndisprot.rc + +Abstract: + + Internal resource file for driver. + +--*/ + +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK + +#define VER_FILEDESCRIPTION_STR "NDIS User mode I/O Driver" +#define VER_INTERNALNAME_STR "NDISPROT60.SYS" +#define VER_ORIGINALFILENAME_STR "NDISPROT60.SYS" +#define VER_LANGNEUTRAL + +#define VER_FILEVERSION 4,20,00,0000 +#define VER_FILEVERSION_STR "4.20.00.0000" + +#undef VER_PRODUCTVERSION +#define VER_PRODUCTVERSION VER_FILEVERSION + +#undef VER_PRODUCTVERSION_STR +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) 2010 Microsoft Corporation" + +#undef VER_COMPANYNAME_STR +#define VER_COMPANYNAME_STR "Microsoft Corporation" + +#undef VER_PRODUCTNAME_STR +#define VER_PRODUCTNAME_STR "NDIS 6.0 Sample Protocol Driver" + +#include "common.ver" + diff --git a/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj new file mode 100644 index 00000000..068f19e0 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj @@ -0,0 +1,265 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {74918B99-A7A2-4462-9330-D61ED648B991} + $(MSBuildProjectName) + Debug + Win32 + {F57A9742-AE91-4E5C-AAF8-582BEC93A8CA} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + ndisprot60 + + + ndisprot60 + + + ndisprot60 + + + ndisprot60 + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters new file mode 100644 index 00000000..e971259b --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters @@ -0,0 +1,49 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {FC988553-FCE1-455C-A2B8-4253B2E40339} + + + h;hpp;hxx;hm;inl;inc;xsd + {CA13EBCF-7249-4085-AEED-A9AB9756BBDF} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {B5901951-2387-48ED-83C6-A5D259BF7E0D} + + + inf;inv;inx;mof;mc; + {FCDA9B5C-B135-477F-BC65-BE9761005881} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/ndisprot/6x/sys/60/precompsrc.c b/network/ndis/ndisprot/6x/sys/60/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/60/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/network/ndis/ndisprot/6x/sys/630/ndisprot630.inf b/network/ndis/ndisprot/6x/sys/630/ndisprot630.inf new file mode 100644 index 00000000..0fc1eb67 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/630/ndisprot630.inf @@ -0,0 +1,109 @@ +;------------------------------------------------------------------------- +; NDISPROT630.INF -- Sample NDIS Protocol Driver +; +; Copyright (c) 2003, Microsoft Corporation +;------------------------------------------------------------------------- +[version] +Signature = "$Windows NT$" +Class = NetTrans +ClassGUID = {4d36e975-e325-11ce-bfc1-08002be10318} +Provider = %Msft% +DriverVer = 07/16/2010,4.2 +CatalogFile = ndisprot630.cat + + +[Manufacturer] +%Msft%=MSFT,NTx86,NTia64,NTamd64,NTarm,NTarm64 + +[MSFT.NTx86] +%NDISPROT_Desc%=Install, MS_NDISPROT + +[MSFT.NTia64] +%NDISPROT_Desc%=Install, MS_NDISPROT + +[MSFT.NTamd64] +%NDISPROT_Desc%=Install, MS_NDISPROT + +[MSFT.NTarm] +%NDISPROT_Desc%=Install, MS_NDISPROT + +[MSFT.NTarm64] +%NDISPROT_Desc%=Install, MS_NDISPROT + +;------------------------------------------------------------------------- +; Installation Section +;------------------------------------------------------------------------- +[Install] +AddReg=Inst_Ndi +Characteristics=0x0 ; +CopyFiles=CpyFiles_Sys + +;------------------------------------------------------------------------- +; Ndi installation support +;------------------------------------------------------------------------- +[Inst_Ndi] +HKR,Ndi,Service,,"Ndisprot" +HKR,Ndi,HelpText,,%NDISPROT_HelpText% +HKR,Ndi\Interfaces, UpperRange,, noupper +HKR,"Ndi\Interfaces","LowerRange",,"ndis5,ndis4,ndis5_prot" + +;------------------------------------------------------------------------- +; Service installation support +;------------------------------------------------------------------------- +[Install.Services] +AddService=Ndisprot,,NDISPROT_Service_Inst + +[NDISPROT_Service_Inst] +DisplayName = %NDISPROT_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\ndisprot630.sys +Description = %NDISPROT_Desc% +AddReg = NdisImPlatformBinding_Reg + +[NdisImPlatformBinding_Reg] +; By default, when an LBFO team or Bridge is created, all protocols will be +; unbound from the underlying members and bound to the TNic(s). This keyword +; allows a component to opt out of the default behavior +; To prevent binding this protocol to the TNic(s): +; HKR, Parameters, NdisImPlatformBindingOptions,0x00010001,1 ; Do not bind to TNic +; To prevent unbinding this protocol from underlying members: +; HKR, Parameters, NdisImPlatformBindingOptions,0x00010001,2 ; Do not unbind from Members +; To prevent both binding to TNic and unbinding from members: +; HKR, Parameters, NdisImPlatformBindingOptions,0x00010001,3 ; Do not bind to TNic or unbind from Members +HKR, Parameters, NdisImPlatformBindingOptions,0x00010001,0 ; Subscribe to default behavior + +[Install.Remove.Services] +DelService=Ndisprot,0x200 + +;------------------------------------------------------------------------- +; Declare Destination Directories for file copy/deletion +;------------------------------------------------------------------------- +[SourceDisksNames] +1=%DiskDescription%,"",, + +[SourceDisksFiles] +ndisprot630.sys=1 + + +[DestinationDirs] +CpyFiles_Sys = 12 ; DIRID_DRIVERS + + +;------------------------------------------------------------------------- +; Files to Copy/Delete - Referenced by Install and Remove sections above +;------------------------------------------------------------------------- +[CpyFiles_Sys] +Ndisprot630.sys,,,2 + +[Strings] +Msft = "Your Company" +DiskDescription = "Microsoft Ndisprot Sample Protocol Driver Disk" +NDISPROT_Desc = "Sample NDIS Protocol Driver" +NDISPROT_HelpText = "A driver to support user-mode I/O on NDIS devices" + + + + + diff --git a/network/ndis/ndisprot/6x/sys/630/ndisprot630.rc b/network/ndis/ndisprot/6x/sys/630/ndisprot630.rc new file mode 100644 index 00000000..f50de3f9 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/630/ndisprot630.rc @@ -0,0 +1,49 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + ndisprot.rc + +Abstract: + + Internal resource file for driver. + +--*/ + +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK + +#define VER_FILEDESCRIPTION_STR "NDIS User mode I/O Driver" +#define VER_INTERNALNAME_STR "NDISPROT630.SYS" +#define VER_ORIGINALFILENAME_STR "NDISPROT630.SYS" +#define VER_LANGNEUTRAL + +#define VER_FILEVERSION 4,20,00,0000 +#define VER_FILEVERSION_STR "4.20.00.0000" + +#undef VER_PRODUCTVERSION +#define VER_PRODUCTVERSION VER_FILEVERSION + +#undef VER_PRODUCTVERSION_STR +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) 2010 Microsoft Corporation" + +#undef VER_COMPANYNAME_STR +#define VER_COMPANYNAME_STR "Microsoft Corporation" + +#undef VER_PRODUCTNAME_STR +#define VER_PRODUCTNAME_STR "NDIS 6.30 Sample Protocol Driver" + +#include "common.ver" + diff --git a/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj b/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj new file mode 100644 index 00000000..5a913947 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj @@ -0,0 +1,265 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {DB252AE8-E32C-4E60-B0EB-33A2CB80861C} + $(MSBuildProjectName) + Debug + Win32 + {FA9D6F91-6B1C-4EFF-BAEA-E67F4CFE9D3E} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + ndisprot630 + + + ndisprot630 + + + ndisprot630 + + + ndisprot630 + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + true + Level4 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 + + + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalIncludeDirectories);.. + + + + + %(AdditionalIncludeDirectories);.. + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ..;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj.Filters b/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj.Filters new file mode 100644 index 00000000..bbdb53e8 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj.Filters @@ -0,0 +1,49 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {088C6D92-3084-464D-B6CD-B47E67D278B5} + + + h;hpp;hxx;hm;inl;inc;xsd + {EDCAD0DA-E747-487E-8458-BAC6BA5E5744} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9F1E323E-8118-4CAC-8124-DA41F78075BE} + + + inf;inv;inx;mof;mc; + {5EA1AAE7-3C58-475C-84F0-40DCDF8823E9} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/ndisprot/6x/sys/630/precompsrc.c b/network/ndis/ndisprot/6x/sys/630/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/630/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/network/ndis/ndisprot/6x/sys/debug.c b/network/ndis/ndisprot/6x/sys/debug.c new file mode 100644 index 00000000..40e90984 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/debug.c @@ -0,0 +1,425 @@ +/*++ + +Copyright (c) 1997 Microsoft Corporation + +Module Name: + + debug.c + +Abstract: + + This module contains all debug-related code. + +Revision History: + +Notes: + +--*/ + +#include + +#define __FILENUMBER 'GBED' + +#if DBG + +INT ndisprotDebugLevel=DL_WARN; + +NDIS_SPIN_LOCK ndisprotDbgLogLock; + +PNPROTD_ALLOCATION ndisprotdMemoryHead = (PNPROTD_ALLOCATION)NULL; +PNPROTD_ALLOCATION ndisprotdMemoryTail = (PNPROTD_ALLOCATION)NULL; +ULONG ndisprotdAllocCount = 0; // how many allocated so far (unfreed) + +NDIS_SPIN_LOCK ndisprotdMemoryLock; +BOOLEAN ndisprotdInitDone = FALSE; + + +PVOID +ndisprotAuditAllocMem( + PVOID pPointer, + ULONG Size, + ULONG FileNumber, + ULONG LineNumber +) +{ + PVOID pBuffer; + PNPROTD_ALLOCATION pAllocInfo; + + if (!ndisprotdInitDone) + { + NdisAllocateSpinLock(&(ndisprotdMemoryLock)); + ndisprotdInitDone = TRUE; + } + + NdisAllocateMemoryWithTag( + (PVOID *)&pAllocInfo, + Size+sizeof(NPROTD_ALLOCATION), + (ULONG)'oiuN' + ); + + if (pAllocInfo == (PNPROTD_ALLOCATION)NULL) + { + DEBUGP(DL_VERY_LOUD+50, + ("ndisprotAuditAllocMem: file %d, line %d, Size %d failed!\n", + FileNumber, LineNumber, Size)); + pBuffer = NULL; + } + else + { + pBuffer = (PVOID)&(pAllocInfo->UserData); + NPROT_SET_MEM(pBuffer, 0xaf, Size); + pAllocInfo->Signature = NPROTD_MEMORY_SIGNATURE; + pAllocInfo->FileNumber = FileNumber; + pAllocInfo->LineNumber = LineNumber; + pAllocInfo->Size = Size; + pAllocInfo->Location = (ULONG_PTR)pPointer; + pAllocInfo->Next = (PNPROTD_ALLOCATION)NULL; + + NdisAcquireSpinLock(&(ndisprotdMemoryLock)); + + pAllocInfo->Prev = ndisprotdMemoryTail; + if (ndisprotdMemoryTail == (PNPROTD_ALLOCATION)NULL) + { + // empty list + ndisprotdMemoryHead = ndisprotdMemoryTail = pAllocInfo; + } + else + { + ndisprotdMemoryTail->Next = pAllocInfo; + } + ndisprotdMemoryTail = pAllocInfo; + + ndisprotdAllocCount++; + NdisReleaseSpinLock(&(ndisprotdMemoryLock)); + } + + DEBUGP(DL_VERY_LOUD+100, + ("ndisprotAuditAllocMem: file %c%c%c%c, line %d, %d bytes, [0x%p] <- 0x%p\n", + (CHAR)(FileNumber & 0xff), + (CHAR)((FileNumber >> 8) & 0xff), + (CHAR)((FileNumber >> 16) & 0xff), + (CHAR)((FileNumber >> 24) & 0xff), + LineNumber, Size, pPointer, pBuffer)); + + return (pBuffer); + +} + + +VOID +ndisprotAuditFreeMem( + PVOID Pointer +) +{ + PNPROTD_ALLOCATION pAllocInfo; + + NdisAcquireSpinLock(&(ndisprotdMemoryLock)); + + pAllocInfo = CONTAINING_RECORD(Pointer, NPROTD_ALLOCATION, UserData); + + if (pAllocInfo->Signature != NPROTD_MEMORY_SIGNATURE) + { + DEBUGP(DL_ERROR, + ("ndisprotAuditFreeMem: unknown buffer 0x%p!\n", Pointer)); + NdisReleaseSpinLock(&(ndisprotdMemoryLock)); +#if DBG + DbgBreakPoint(); +#endif + return; + } + + pAllocInfo->Signature = (ULONG)'DEAD'; + if (pAllocInfo->Prev != (PNPROTD_ALLOCATION)NULL) + { + pAllocInfo->Prev->Next = pAllocInfo->Next; + } + else + { + ndisprotdMemoryHead = pAllocInfo->Next; + } + if (pAllocInfo->Next != (PNPROTD_ALLOCATION)NULL) + { + pAllocInfo->Next->Prev = pAllocInfo->Prev; + } + else + { + ndisprotdMemoryTail = pAllocInfo->Prev; + } + ndisprotdAllocCount--; + NdisReleaseSpinLock(&(ndisprotdMemoryLock)); + + NdisFreeMemory(pAllocInfo, 0, 0); +} + + +VOID +ndisprotAuditShutdown( + VOID +) +{ + if (ndisprotdInitDone) + { + if (ndisprotdAllocCount != 0) + { + DEBUGP(DL_ERROR, ("AuditShutdown: unfreed memory, %d blocks!\n", + ndisprotdAllocCount)); + DEBUGP(DL_ERROR, ("MemoryHead: 0x%p, MemoryTail: 0x%p\n", + ndisprotdMemoryHead, ndisprotdMemoryTail)); + DbgBreakPoint(); + { + PNPROTD_ALLOCATION pAllocInfo; + + while (ndisprotdMemoryHead != (PNPROTD_ALLOCATION)NULL) + { + pAllocInfo = ndisprotdMemoryHead; + DEBUGP(DL_INFO, ("AuditShutdown: will free 0x%p\n", pAllocInfo)); + ndisprotAuditFreeMem(&(pAllocInfo->UserData)); + } + } + } + ndisprotdInitDone = FALSE; + } +} + +#define MAX_HD_LENGTH 128 + +VOID +DbgPrintHexDump( + IN PUCHAR pBuffer, + IN ULONG Length +) +/*++ + +Routine Description: + + Print a hex dump of the given contiguous buffer. If the length + is too long, we truncate it. + +Arguments: + + pBuffer - Points to start of data to be dumped + Length - Length of above. + +Return Value: + + None + +--*/ +{ + ULONG i; + + if (Length > MAX_HD_LENGTH) + { + Length = MAX_HD_LENGTH; + } + + for (i = 0; i < Length; i++) + { + // + // Check if we are at the end of a line + // + if ((i > 0) && ((i & 0xf) == 0)) + { + DbgPrint("\n"); + } + + // + // Print addr if we are at start of a new line + // + if ((i & 0xf) == 0) + { + DbgPrint("%08p ", pBuffer); + } + + DbgPrint(" %02x", *pBuffer++); + } + + // + // Terminate the last line. + // + if (Length > 0) + { + DbgPrint("\n"); + } +} +#endif // DBG + + +#if DBG_SPIN_LOCK +ULONG ndisprotdSpinLockInitDone = 0; +NDIS_SPIN_LOCK ndisprotdLockLock; + +VOID +ndisprotAllocateSpinLock( + IN PNPROT_LOCK pLock, + IN ULONG FileNumber, + IN ULONG LineNumber +) +{ + if (ndisprotdSpinLockInitDone == 0) + { + ndisprotdSpinLockInitDone = 1; + NdisAllocateSpinLock(&(ndisprotdLockLock)); + } + + NdisAcquireSpinLock(&(ndisprotdLockLock)); + pLock->Signature = NPROTL_SIG; + pLock->TouchedByFileNumber = FileNumber; + pLock->TouchedInLineNumber = LineNumber; + pLock->IsAcquired = 0; + pLock->OwnerThread = 0; + NdisAllocateSpinLock(&(pLock->NdisLock)); + NdisReleaseSpinLock(&(ndisprotdLockLock)); +} + +VOID +ndisprotFreeSpinLock( + IN PNPROT_LOCK pLock, + IN ULONG FileNumber, + IN ULONG LineNumber +) +{ + + NdisAcquireSpinLock(&(ndisprotdLockLock)); + pLock->Signature = NUIOL_SIG; + pLock->TouchedByFileNumber = FileNumber; + pLock->TouchedInLineNumber = LineNumber; + pLock->IsAcquired = 0; + pLock->OwnerThread = 0; + NdisFreeSpinLock(&(pLock->NdisLock)); + NdisReleaseSpinLock(&(ndisprotdLockLock)); +} + +VOID +ndisprotFreeDbgLock( + VOID + ) +{ + + ASSERT(ndisprotdSpinLockInitDone == 1); + + ndisprotdSpinLockInitDone = 0; + NdisFreeSpinLock(&(ndisprotdLockLock)); +} + +VOID +ndisprotAcquireSpinLock( + IN PNPROT_LOCK pLock, + IN BOOLEAN DispatchLevel, + IN ULONG FileNumber, + IN ULONG LineNumber +) +{ + PKTHREAD pThread; + + pThread = KeGetCurrentThread(); + if (DispatchLevel == TRUE) + { + NdisDprAcquireSpinLock(&(ndisprotdLockLock)); + } + else + { + NdisAcquireSpinLock(&(ndisprotdLockLock)); + } + if (pLock->Signature != NPROTL_SIG) + { + DbgPrint("Trying to acquire uninited lock 0x%x, File %c%c%c%c, Line %d\n", + pLock, + (CHAR)(FileNumber & 0xff), + (CHAR)((FileNumber >> 8) & 0xff), + (CHAR)((FileNumber >> 16) & 0xff), + (CHAR)((FileNumber >> 24) & 0xff), + LineNumber); + DbgBreakPoint(); + } + + if (pLock->IsAcquired != 0) + { + if (pLock->OwnerThread == pThread) + { + DbgPrint("Detected multiple locking!: pLock 0x%x, File %c%c%c%c, Line %d\n", + pLock, + (CHAR)(FileNumber & 0xff), + (CHAR)((FileNumber >> 8) & 0xff), + (CHAR)((FileNumber >> 16) & 0xff), + (CHAR)((FileNumber >> 24) & 0xff), + LineNumber); + DbgPrint("pLock 0x%x already acquired in File %c%c%c%c, Line %d\n", + pLock, + (CHAR)(pLock->TouchedByFileNumber & 0xff), + (CHAR)((pLock->TouchedByFileNumber >> 8) & 0xff), + (CHAR)((pLock->TouchedByFileNumber >> 16) & 0xff), + (CHAR)((pLock->TouchedByFileNumber >> 24) & 0xff), + pLock->TouchedInLineNumber); + DbgBreakPoint(); + } + } + + pLock->IsAcquired++; + if (DispatchLevel == TRUE) + { + NdisDprReleaseSpinLock(&(ndisprotdLockLock)); + NdisDprAcquireSpinLock(&(pLock->NdisLock)); + } + else + { + NdisReleaseSpinLock(&(ndisprotdLockLock)); + NdisAcquireSpinLock(&(pLock->NdisLock)); + } + + // + // Mark this lock. + // + pLock->OwnerThread = pThread; + pLock->TouchedByFileNumber = FileNumber; + pLock->TouchedInLineNumber = LineNumber; +} + + +VOID +ndisprotReleaseSpinLock( + IN PNPROT_LOCK pLock, + IN BOOLEAN DispatchLevel, + IN ULONG FileNumber, + IN ULONG LineNumber +) +{ + NdisDprAcquireSpinLock(&(ndisprotdLockLock)); + if (pLock->Signature != NPROTL_SIG) + { + DbgPrint("Trying to release uninited lock 0x%x, File %c%c%c%c, Line %d\n", + pLock, + (CHAR)(FileNumber & 0xff), + (CHAR)((FileNumber >> 8) & 0xff), + (CHAR)((FileNumber >> 16) & 0xff), + (CHAR)((FileNumber >> 24) & 0xff), + LineNumber); + DbgBreakPoint(); + } + + if (pLock->IsAcquired == 0) + { + DbgPrint("Detected release of unacquired lock 0x%x, File %c%c%c%c, Line %d\n", + pLock, + (CHAR)(FileNumber & 0xff), + (CHAR)((FileNumber >> 8) & 0xff), + (CHAR)((FileNumber >> 16) & 0xff), + (CHAR)((FileNumber >> 24) & 0xff), + LineNumber); + DbgBreakPoint(); + } + pLock->TouchedByFileNumber = FileNumber; + pLock->TouchedInLineNumber = LineNumber; + pLock->IsAcquired--; + pLock->OwnerThread = 0; + NdisDprReleaseSpinLock(&(ndisprotdLockLock)); + if (DispatchLevel == TRUE) + { + NdisDprReleaseSpinLock(&(pLock->NdisLock)); + } + else + { + NdisReleaseSpinLock(&(pLock->NdisLock)); + } +} +#endif // DBG_SPIN_LOCK diff --git a/network/ndis/ndisprot/6x/sys/debug.h b/network/ndis/ndisprot/6x/sys/debug.h new file mode 100644 index 00000000..64de3453 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/debug.h @@ -0,0 +1,220 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + debug.h + +Abstract: + + Debug macros for NDISPROT + +Revision History: + +Notes: + +--*/ + +#ifndef _NPROTDEBUG__H +#define _NPROTDEBUG__H + +// +// Message verbosity: lower values indicate higher urgency +// +#define DL_EXTRA_LOUD 20 +#define DL_VERY_LOUD 10 +#define DL_LOUD 8 +#define DL_INFO 6 +#define DL_WARN 4 +#define DL_ERROR 2 +#define DL_FATAL 0 + +#if DBG_SPIN_LOCK + +typedef struct _NPROT_LOCK +{ + ULONG Signature; + ULONG IsAcquired; + PKTHREAD OwnerThread; + ULONG TouchedByFileNumber; + ULONG TouchedInLineNumber; + NDIS_SPIN_LOCK NdisLock; +} NPROT_LOCK, *PNPROT_LOCK; + +#define NPROTL_SIG 'KCOL' + +extern NDIS_SPIN_LOCK ndisprotDbgLogLock; + +extern +VOID +ndisprotAllocateSpinLock( + IN PNPROT_LOCK pLock, + IN ULONG FileNumber, + IN ULONG LineNumber +); + +extern +VOID +ndisprotFreeSpinLock( + IN PNPROT_LOCK pLock, + IN ULONG FileNumber, + IN ULONG LineNumber +); + +extern +VOID +ndisprotAcquireSpinLock( + IN PNPROT_LOCK pLock, + IN BOOLEAN DispatchLevel, + IN ULONG FileNumber, + IN ULONG LineNumber +); + +extern +VOID +ndisprotReleaseSpinLock( + IN PNPROT_LOCK pLock, + IN BOOLEAN DispatchLevel, + IN ULONG FileNumber, + IN ULONG LineNumber +); + +extern +VOID +ndisprotFreeDbgLock( + VOID +); + +#define CHECK_LOCK_COUNT(Count) \ + { \ + if ((INT)(Count) < 0) \ + { \ + DbgPrint("Lock Count %d is < 0! File %s, Line %d\n",\ + Count, __FILE__, __LINE__); \ + DbgBreakPoint(); \ + } \ + } +#else + +#define CHECK_LOCK_COUNT(Count) + +typedef NDIS_SPIN_LOCK NPROT_LOCK; +typedef PNDIS_SPIN_LOCK PNPROT_LOCK; + +#endif // DBG_SPIN_LOCK + +#if DBG + +extern INT ndisprotDebugLevel; + + +#define DEBUGP(lev, stmt) \ + { \ + if ((lev) <= ndisprotDebugLevel) \ + { \ + DbgPrint("Ndisprot: "); DbgPrint stmt; \ + } \ + } + +#define DEBUGPDUMP(lev, pBuf, Len) \ + { \ + if ((lev) <= ndisprotDebugLevel) \ + { \ + DbgPrintHexDump((PUCHAR)(pBuf), (ULONG)(Len)); \ + } \ + } + +#define NPROT_ASSERT(exp) \ + { \ + if (!(exp)) \ + { \ + DbgPrint("Ndisprot: assert " #exp " failed in" \ + " file %s, line %d\n", __FILE__, __LINE__); \ + DbgBreakPoint(); \ + } \ + } + +#define NPROT_SET_SIGNATURE(s, t)\ + (s)->t##_sig = t##_signature; + +#define NPROT_STRUCT_ASSERT(s, t) \ + if ((s)->t##_sig != t##_signature) \ + { \ + DbgPrint("ndisprot: assertion failure" \ + " for type " #t " at 0x%p in file %s, line %d\n", \ + s, __FILE__, __LINE__); \ + DbgBreakPoint(); \ + } + + +// +// Memory Allocation/Freeing Audit: +// + +// +// The NPROTD_ALLOCATION structure stores all info about one allocation +// +typedef struct _NPROTD_ALLOCATION { + + ULONG Signature; + struct _NPROTD_ALLOCATION *Next; + struct _NPROTD_ALLOCATION *Prev; + ULONG FileNumber; + ULONG LineNumber; + ULONG Size; + ULONG_PTR Location; // where the returned ptr was stored + union + { + ULONGLONG Alignment; + UCHAR UserData; + }; + +} NPROTD_ALLOCATION, *PNPROTD_ALLOCATION; + +#define NPROTD_MEMORY_SIGNATURE (ULONG)'CSII' + +extern +PVOID +ndisprotAuditAllocMem ( + PVOID pPointer, + ULONG Size, + ULONG FileNumber, + ULONG LineNumber +); + +extern +VOID +ndisprotAuditFreeMem( + PVOID Pointer +); + +extern +VOID +ndisprotAuditShutdown( + VOID +); + +extern +VOID +DbgPrintHexDump( + PUCHAR pBuffer, + ULONG Length +); + +#else + +// +// No debug +// +#define DEBUGP(lev, stmt) +#define DEBUGPDUMP(lev, pBuf, Len) + +#define NPROT_ASSERT(exp) +#define NPROT_SET_SIGNATURE(s, t) UNREFERENCED_PARAMETER(s) +#define NPROT_STRUCT_ASSERT(s, t) UNREFERENCED_PARAMETER(s) + +#endif // DBG + + +#endif // _NPROTDEBUG__H diff --git a/network/ndis/ndisprot/6x/sys/excallbk.c b/network/ndis/ndisprot/6x/sys/excallbk.c new file mode 100644 index 00000000..baa2298c --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/excallbk.c @@ -0,0 +1,195 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + ExCallbk.c + +Abstract: The routines in this module helps to solve driver load order + dependency between this sample and NDISWDM sample. These + routines are not required in a typical protocol driver. By default + this module is not included in the sample. You include these routines + by adding EX_CALLBACK defines to the 'sources' file. Read the + NDISWDM samples readme file for more information on how ExCallback + kernel interfaces are used to solve driver load order issue. + + +Environment: + + Kernel mode + + +Revision History: + +--*/ + +#include "precomp.h" + +#ifdef EX_CALLBACK + +#define __FILENUMBER 'LCxE' + +#define NDISPROT_CALLBACK_NAME L"\\Callback\\NdisProtCallbackObject" + +#define CALLBACK_SOURCE_NDISPROT 0 +#define CALLBACK_SOURCE_NDISWDM 1 + +PCALLBACK_OBJECT CallbackObject = NULL; +PVOID CallbackRegisterationHandle = NULL; + +typedef VOID (* NOTIFY_PRESENCE_CALLBACK)(OUT PVOID Source); + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, ndisprotRegisterExCallBack) +#pragma alloc_text(PAGE, ndisprotUnregisterExCallBack) + +#endif // ALLOC_PRAGMA + +BOOLEAN +ndisprotRegisterExCallBack() +{ + OBJECT_ATTRIBUTES ObjectAttr; + UNICODE_STRING CallBackObjectName; + NTSTATUS Status; + BOOLEAN bResult = TRUE; + + DEBUGP(DL_LOUD, ("--> ndisprotRegisterExCallBack\n")); + + PAGED_CODE(); + + do { + + RtlInitUnicodeString(&CallBackObjectName, NDISPROT_CALLBACK_NAME); + + InitializeObjectAttributes(&ObjectAttr, + &CallBackObjectName, + OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, + NULL, + NULL); + + Status = ExCreateCallback(&CallbackObject, + &ObjectAttr, + TRUE, + TRUE); + + + if (!NT_SUCCESS(Status)) + { + + DEBUGP(DL_ERROR, ("RegisterExCallBack: failed to create callback %lx\n", Status)); + bResult = FALSE; + break; + } + + CallbackRegisterationHandle = ExRegisterCallback(CallbackObject, + ndisprotCallback, + (PVOID)NULL); + if (CallbackRegisterationHandle == NULL) + { + DEBUGP(DL_ERROR,("RegisterExCallBack: failed to register a Callback routine%lx\n", Status)); + bResult = FALSE; + break; + } + + ExNotifyCallback(CallbackObject, + (PVOID)CALLBACK_SOURCE_NDISPROT, + (PVOID)NULL); + + + }while(FALSE); + + if(!bResult) { + if (CallbackRegisterationHandle) + { + ExUnregisterCallback(CallbackRegisterationHandle); + CallbackRegisterationHandle = NULL; + } + + if (CallbackObject) + { + ObDereferenceObject(CallbackObject); + CallbackObject = NULL; + } + } + + DEBUGP(DL_LOUD, ("<-- ndisprotRegisterExCallBack\n")); + + return bResult; + +} + +VOID +ndisprotUnregisterExCallBack() +{ + DEBUGP(DL_LOUD, ("--> ndisprotUnregisterExCallBack\n")); + + PAGED_CODE(); + + if (CallbackRegisterationHandle) + { + ExUnregisterCallback(CallbackRegisterationHandle); + CallbackRegisterationHandle = NULL; + } + + if (CallbackObject) + { + ObDereferenceObject(CallbackObject); + CallbackObject = NULL; + } + + DEBUGP(DL_LOUD, ("<-- ndisprotUnregisterExCallBack\n")); + +} + +VOID +ndisprotCallback( + PVOID CallBackContext, + PVOID Source, + PVOID CallbackAddr + ) +{ + NOTIFY_PRESENCE_CALLBACK func; + + DEBUGP(DL_LOUD, ("==>ndisprotoCallback: Source %lx, CallbackAddr %p\n", + Source, CallbackAddr)); + + // + // if we are the one issuing this notification, just return + // + if (Source == CALLBACK_SOURCE_NDISPROT) { + return; + } + + // + // Notification is coming from NDISWDM + // let it know that you are here + // + ASSERT(Source == (PVOID)CALLBACK_SOURCE_NDISWDM); + + if(Source == (PVOID)CALLBACK_SOURCE_NDISWDM) { + + ASSERT(CallbackAddr); + + if (CallbackAddr == NULL) + { + DEBUGP(DL_ERROR, ("Callback called with invalid address %p\n", CallbackAddr)); + return; + } + + func = CallbackAddr; + + func(CALLBACK_SOURCE_NDISPROT); + } + + DEBUGP(DL_LOUD, ("<==ndisprotoCallback: Source, %lx\n", Source)); + +} + +#endif diff --git a/network/ndis/ndisprot/6x/sys/macros.h b/network/ndis/ndisprot/6x/sys/macros.h new file mode 100644 index 00000000..d750129c --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/macros.h @@ -0,0 +1,234 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + macros.h + +Abstract: + + Some macros for NDISPROT. + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + + +#ifndef MIN +#define MIN(_a, _b) ((_a) < (_b)? (_a): (_b)) +#endif + +#if DBG +#define NPROT_REF_OPEN(_pOpen) ndisprotDbgRefOpen(_pOpen, __FILENUMBER, __LINE__) +#define NPROT_DEREF_OPEN(_pOpen) ndisprotDbgDerefOpen(_pOpen, __FILENUMBER, __LINE__) +#else +#define NPROT_REF_OPEN(_pOpen) ndisprotRefOpen(_pOpen) +#define NPROT_DEREF_OPEN(_pOpen) ndisprotDerefOpen(_pOpen) +#endif + + +// +// Spinlock macros +// +#if DBG_SPIN_LOCK + +#define NPROT_INIT_LOCK(_pLock) \ + ndisprotAllocateSpinLock(_pLock, __FILENUMBER, __LINE__) + +#define NPROT_ACQUIRE_LOCK(_pLock, DispatchLevel) \ + ndisprotAcquireSpinLock(_pLock, DispatchLevel,__FILENUMBER, __LINE__) + +#define NPROT_RELEASE_LOCK(_pLock,DispatchLevel) \ + ndisprotReleaseSpinLock(_pLock, DispatchLevel,__FILENUMBER, __LINE__) + +#define NPROT_FREE_LOCK(_pLock) \ + ndisprotFreeSpinLock(_pLock, __FILENUMBER, __LINE__) + +#define NPROT_FREE_DBG_LOCK() \ + ndisprotFreeDbgLock() +#else + +#define NPROT_INIT_LOCK(_pLock) NdisAllocateSpinLock(_pLock) +#define NPROT_ACQUIRE_LOCK(_pLock, DispatchLevel) \ + { \ + if (DispatchLevel == TRUE) \ + { \ + NdisDprAcquireSpinLock(_pLock); \ + } \ + else \ + { \ + NdisAcquireSpinLock(_pLock); \ + } \ + } +#define NPROT_RELEASE_LOCK(_pLock, DispatchLevel) \ + { \ + if (DispatchLevel == TRUE) \ + { \ + NdisDprReleaseSpinLock(_pLock); \ + } \ + else \ + { \ + NdisReleaseSpinLock(_pLock); \ + } \ + } + +#define NPROT_FREE_LOCK(_pLock) NdisFreeSpinLock(_pLock) + +#define NPROT_FREE_DBG_LOCK() + +#endif // DBG + +// +// List manipulation. +// +#define NPROT_INIT_LIST_HEAD(_pList) InitializeListHead(_pList) +#define NPROT_IS_LIST_EMPTY(_pList) IsListEmpty(_pList) +#define NPROT_INSERT_HEAD_LIST(_pList, _pEnt) InsertHeadList(_pList, _pEnt) +#define NPROT_INSERT_TAIL_LIST(_pList, _pEnt) InsertTailList(_pList, _pEnt) +#define NPROT_REMOVE_ENTRY_LIST(_pEnt) RemoveEntryList(_pEnt) +#define NPROT_REMOVE_HEAD_LIST(_pList) RemoveHeadList(_pList) + + + +#define NPROT_RCV_NBL_TO_LIST_ENTRY(_pNbl) \ + (&((PNPROT_RECV_NBL_RSVD)(NET_BUFFER_LIST_PROTOCOL_RESERVED(_pNbl)))->Link) + +#define NPROT_RCV_NBL_FROM_LIST_ENTRY(_pEnt) \ + (((PNPROT_RECV_NBL_RSVD)(CONTAINING_RECORD(_pEnt, NPROT_RECV_NBL_RSVD, Link)))->pNetBufferList) + + +// +// Send net buffer list context +// +#define NPROT_IRP_FROM_SEND_NBL(_pNbl) \ + (((PNPROT_SEND_NETBUFLIST_RSVD)((_pNbl)->Context->ContextData + (_pNbl)->Context->Offset))->pIrp) + +#define NPROT_SEND_NBL_RSVD(_pNbl) \ + ((PNPROT_SEND_NETBUFLIST_RSVD)((_pNbl)->Context->ContextData + (_pNbl)->Context->Offset)) + + +#define NPROT_REF_SEND_NBL(_pNbl) \ + (VOID)NdisInterlockedIncrement((PLONG)&NPROT_SEND_NBL_RSVD(_pNbl)->RefCount) + + +#define NPROT_DEREF_SEND_NBL(_pNbl, DispatchLevel) \ + { \ + if (NdisInterlockedDecrement((PLONG)&NPROT_SEND_NBL_RSVD(_pNbl)->RefCount) == 0) \ + { \ + NdisFreeNetBufferList(_pNbl); \ + } \ + } + +// +// Cancel IDs are generated by using the partial cancel ID we got from +// NDIS ORed with a monotonically increasing locally generated ID. +// +#define NPROT_CANCEL_ID_LOW_MASK (((ULONG_PTR)-1) >> 8) + +#define NPROT_GET_NEXT_CANCEL_ID() \ + (PVOID)(Globals.PartialCancelId | \ + ((NdisInterlockedIncrement((PLONG)&Globals.LocalCancelId)) & NPROT_CANCEL_ID_LOW_MASK)) + + +// +// Memory allocation +// +#if DBG +#define NPROT_ALLOC_MEM(_pVar, _Size) \ + (_pVar) = ndisprotAuditAllocMem( \ + (PVOID)&(_pVar), \ + _Size, \ + __FILENUMBER, \ + __LINE__); + +#define NPROT_FREE_MEM(_pMem) \ + ndisprotAuditFreeMem(_pMem); + +#else + +#define NPROT_ALLOC_MEM(_pVar, _Size) \ + NdisAllocateMemoryWithTag((PVOID *)(&_pVar), (_Size), NPROT_ALLOC_TAG) + +#define NPROT_FREE_MEM(_pMem) \ + NdisFreeMemory(_pMem, 0, 0) + +#endif // DBG + + +#define NPROT_ZERO_MEM(_pMem, _ByteCount) \ + NdisZeroMemory(_pMem, _ByteCount) + +#define NPROT_COPY_MEM(_pDst, _pSrc, _ByteCount) \ + NdisMoveMemory(_pDst, _pSrc, _ByteCount) + +#define NPROT_MEM_CMP(_p1, _p2, _ByteCount) \ + NdisEqualMemory(_p1, _p2, _ByteCount) + +#define NPROT_SET_MEM(_pMem, _ByteVal, _ByteCount) \ + NdisFillMemory(_pMem, _ByteCount, _ByteVal) + +// +// Events. +// +#define NPROT_INIT_EVENT(_pEvent) NdisInitializeEvent(_pEvent) +#define NPROT_SIGNAL_EVENT(_pEvent) NdisSetEvent(_pEvent) +#define NPROT_WAIT_EVENT(_pEvent, _MsToWait) NdisWaitEvent(_pEvent, _MsToWait) + + +// +// Flags +// +#define NPROT_SET_FLAGS(_FlagsVar, _Mask, _BitsToSet) \ + (_FlagsVar) = ((_FlagsVar) & ~(_Mask)) | (_BitsToSet) + +#define NPROT_TEST_FLAGS(_FlagsVar, _Mask, _BitsToCheck) \ + (((_FlagsVar) & (_Mask)) == (_BitsToCheck)) + + +#define NDIS_STATUS_TO_NT_STATUS(_NdisStatus, _pNtStatus) \ +{ \ + /* \ + * The following NDIS status codes map directly to NT status codes. \ + */ \ + if (((NDIS_STATUS_SUCCESS == (_NdisStatus)) || \ + (NDIS_STATUS_PENDING == (_NdisStatus)) || \ + (NDIS_STATUS_BUFFER_OVERFLOW == (_NdisStatus)) || \ + (NDIS_STATUS_FAILURE == (_NdisStatus)) || \ + (NDIS_STATUS_RESOURCES == (_NdisStatus)) || \ + (NDIS_STATUS_NOT_SUPPORTED == (_NdisStatus)))) \ + { \ + *(_pNtStatus) = (NTSTATUS)(_NdisStatus); \ + } \ + else if (NDIS_STATUS_BUFFER_TOO_SHORT == (_NdisStatus)) \ + { \ + /* \ + * The above NDIS status codes require a little special casing. \ + */ \ + *(_pNtStatus) = STATUS_BUFFER_TOO_SMALL; \ + } \ + else if (NDIS_STATUS_INVALID_LENGTH == (_NdisStatus)) \ + { \ + *(_pNtStatus) = STATUS_INVALID_BUFFER_SIZE; \ + } \ + else if (NDIS_STATUS_INVALID_DATA == (_NdisStatus)) \ + { \ + *(_pNtStatus) = STATUS_INVALID_PARAMETER; \ + } \ + else if (NDIS_STATUS_ADAPTER_NOT_FOUND == (_NdisStatus)) \ + { \ + *(_pNtStatus) = STATUS_NO_MORE_ENTRIES; \ + } \ + else if (NDIS_STATUS_ADAPTER_NOT_READY == (_NdisStatus)) \ + { \ + *(_pNtStatus) = STATUS_DEVICE_NOT_READY; \ + } \ + else \ + { \ + *(_pNtStatus) = STATUS_UNSUCCESSFUL; \ + } \ +} diff --git a/network/ndis/ndisprot/6x/sys/ndisbind.c b/network/ndis/ndisprot/6x/sys/ndisbind.c new file mode 100644 index 00000000..5257d3a2 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/ndisbind.c @@ -0,0 +1,2061 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + ndisbind.c + +Abstract: + + NDIS protocol entry points and utility routines to handle binding + and unbinding from adapters. + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + + +#include "precomp.h" + +#define __FILENUMBER 'DNIB' + +NDIS_OID ndisprotSupportedSetOids[] = +{ + OID_802_11_INFRASTRUCTURE_MODE, + OID_802_11_AUTHENTICATION_MODE, + OID_802_11_RELOAD_DEFAULTS, + OID_802_11_REMOVE_WEP, + OID_802_11_WEP_STATUS, + OID_802_11_BSSID_LIST_SCAN, + OID_802_11_ADD_WEP, + OID_802_11_SSID, + OID_802_11_BSSID, + OID_802_11_BSSID_LIST, + OID_802_11_DISASSOCIATE, + OID_802_11_STATISTICS, // Later used by power management + OID_802_11_POWER_MODE, // Later used by power management + OID_802_11_NETWORK_TYPE_IN_USE, + OID_802_11_RSSI, + OID_802_11_SUPPORTED_RATES, + OID_802_11_CONFIGURATION, + OID_802_3_MULTICAST_LIST, +}; + +NDIS_STATUS +NdisprotBindAdapter( + IN NDIS_HANDLE ProtocolDriverContext, + IN NDIS_HANDLE BindContext, + IN PNDIS_BIND_PARAMETERS BindParameters + ) +/*++ + +Routine Description: + + Protocol Bind Handler entry point called when NDIS wants us + to bind to an adapter. We go ahead and set up a binding. + An OPEN_CONTEXT structure is allocated to keep state about + this binding. + +Arguments: + + +Return Value: + + None + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + NDIS_STATUS Status; + + UNREFERENCED_PARAMETER(ProtocolDriverContext); + + do + { + // + // Allocate our context for this open. + // + NPROT_ALLOC_MEM(pOpenContext, sizeof(NDISPROT_OPEN_CONTEXT)); + if (pOpenContext == NULL) + { + Status = NDIS_STATUS_RESOURCES; + break; + } + + // + // Initialize it. + // + NPROT_ZERO_MEM(pOpenContext, sizeof(NDISPROT_OPEN_CONTEXT)); + NPROT_SET_SIGNATURE(pOpenContext, oc); + + NPROT_INIT_LOCK(&pOpenContext->Lock); + NPROT_INIT_LIST_HEAD(&pOpenContext->PendedReads); + NPROT_INIT_LIST_HEAD(&pOpenContext->PendedWrites); + NPROT_INIT_LIST_HEAD(&pOpenContext->RecvNetBufListQueue); + NPROT_INIT_EVENT(&pOpenContext->PoweredUpEvent); + + + // + // Start off by assuming that the device below is powered up. + // + NPROT_SIGNAL_EVENT(&pOpenContext->PoweredUpEvent); + + NPROT_REF_OPEN(pOpenContext); // Bind + + // + // Add it to the global list. + // + NPROT_ACQUIRE_LOCK(&Globals.GlobalLock, FALSE); + + NPROT_INSERT_TAIL_LIST(&Globals.OpenList, + &pOpenContext->Link); + + NPROT_RELEASE_LOCK(&Globals.GlobalLock, FALSE); + + pOpenContext->State = NdisprotInitializing; + + // + // Here we reference the open context to make sure that even if + // ndisprotCreateBinding failed, open context is still valid + // + NPROT_REF_OPEN(pOpenContext); + // + // Set up the NDIS binding, ndisprotCreateBinding does the cleanup for the + // binding if somehow it fails to create the binding, the + // + Status = ndisprotCreateBinding( + pOpenContext, + BindParameters, + BindContext, + (PUCHAR)BindParameters->AdapterName->Buffer, + BindParameters->AdapterName->Length); + + + if (Status != NDIS_STATUS_SUCCESS) + { + // + // Dereference the open context because we referenced it before we call + // ndisprotCreateBinding + // + NPROT_DEREF_OPEN(pOpenContext); + break; + } + // + // Dereference the open context because we referenced it before we call + // ndisprotCreateBinding + // + NPROT_DEREF_OPEN(pOpenContext); + } + while (FALSE); + + return Status; + +} + +VOID +NdisprotOpenAdapterComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN NDIS_STATUS Status + ) +/*++ + +Routine Description: + + Completion routine called by NDIS if our call to NdisOpenAdapterEx + pends. Wake up the thread that called NdisOpenAdapterEx. + +Arguments: + + ProtocolBindingContext - pointer to open context structure + Status - status of the open + +Return Value: + + None + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + + pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + pOpenContext->BindStatus = Status; + + NPROT_SIGNAL_EVENT(&pOpenContext->BindEvent); +} + + +NDIS_STATUS +NdisprotUnbindAdapter( + IN NDIS_HANDLE UnbindContext, + IN NDIS_HANDLE ProtocolBindingContext + ) +/*++ + +Routine Description: + + NDIS calls this when it wants us to close the binding to an adapter. + +Arguments: + + ProtocolBindingContext - pointer to open context structure + UnbindContext - to use in NdisCompleteUnbindAdapter if we return pending + +Return Value: + + pending or success + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + + UNREFERENCED_PARAMETER(UnbindContext); + + pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + // + // Mark this open as having seen an Unbind. + // + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_UNBIND_FLAGS, NPROTO_UNBIND_RECEIVED); + + // + // In case we had threads blocked for the device below to be powered + // up, wake them up. + // + NPROT_SIGNAL_EVENT(&pOpenContext->PoweredUpEvent); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + pOpenContext->State = NdisprotClosing; + + ndisprotShutdownBinding(pOpenContext); + + return NDIS_STATUS_SUCCESS; +} + + + +VOID +NdisprotCloseAdapterComplete( + IN NDIS_HANDLE ProtocolBindingContext + ) +/*++ + +Routine Description: + + Called by NDIS to complete a pended call to NdisCloseAdapter. + We wake up the thread waiting for this completion. + +Arguments: + + ProtocolBindingContext - pointer to open context structure + +Return Value: + + None + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + + pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + NPROT_SIGNAL_EVENT(&pOpenContext->BindEvent); +} + +NDIS_STATUS +NdisprotPnPEventHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNET_PNP_EVENT_NOTIFICATION pNetPnPEventNotification + ) +/*++ + +Routine Description: + + Called by NDIS to notify us of a PNP event. The most significant + one for us is power state change. + +Arguments: + + ProtocolBindingContext - pointer to open context structure + this is NULL for global reconfig events. + + pNetPnPEventNotification - pointer to the PNP event notification + +Return Value: + + Our processing status for the PNP event. + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PUCHAR Buffer = NULL; + ULONG BufferLength = 0; + PNDIS_PROTOCOL_RESTART_PARAMETERS RestartParameters = NULL; + + pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; + + switch (pNetPnPEventNotification->NetPnPEvent.NetEvent) + { + case NetEventSetPower: + NPROT_STRUCT_ASSERT(pOpenContext, oc); + pOpenContext->PowerState = *(PNET_DEVICE_POWER_STATE)pNetPnPEventNotification->NetPnPEvent.Buffer; + + if (pOpenContext->PowerState > NetDeviceStateD0) + { + // + // The device below is transitioning to a low power state. + // Block any threads attempting to query the device while + // in this state. + // + NPROT_INIT_EVENT(&pOpenContext->PoweredUpEvent); + + // + // There is no need to wait for pending I/O here. + // We wait for it in the NetEventPause handler. + // + + // + // Return any receives that we had queued up. + // + ndisprotFlushReceiveQueue(pOpenContext); + DEBUGP(DL_INFO, ("PnPEvent: Open %p, SetPower to %d\n", + pOpenContext, pOpenContext->PowerState)); + } + else + { + // + // The device below is powered up. + // + DEBUGP(DL_INFO, ("PnPEvent: Open %p, SetPower ON: %d\n", + pOpenContext, pOpenContext->PowerState)); + NPROT_SIGNAL_EVENT(&pOpenContext->PoweredUpEvent); + } + + Status = NDIS_STATUS_SUCCESS; + break; + + case NetEventQueryPower: + Status = NDIS_STATUS_SUCCESS; + break; + + case NetEventBindsComplete: + NPROT_SIGNAL_EVENT(&Globals.BindsComplete); + Status = NDIS_STATUS_SUCCESS; + break; + + case NetEventPause: + // + // Wait all sends to be complete. + // + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + pOpenContext->State = NdisprotPausing; + + // + // we could also complete the PnP Event asynchrously. + // + while (TRUE) + { + if (pOpenContext->PendedSendCount == 0) + break; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + DEBUGP(DL_INFO, ("PnPEvent: Open %p, outstanding count is %d\n", pOpenContext, + pOpenContext->PendedSendCount)); + + NdisMSleep(100000); // 100 ms. + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + // + // Return all queued receives. + // + ndisprotFlushReceiveQueue(pOpenContext); + pOpenContext->State = NdisprotPaused; + + break; + + case NetEventRestart: + + + ASSERT(pOpenContext->State == NdisprotPaused); + // + // Get the updated attributes + // + Buffer = pNetPnPEventNotification->NetPnPEvent.Buffer; + if (Buffer == NULL) + { + pOpenContext->State = NdisprotRunning; + break; + } + BufferLength = pNetPnPEventNotification->NetPnPEvent.BufferLength; + + ASSERT(BufferLength == sizeof(NDIS_PROTOCOL_RESTART_PARAMETERS)); + + RestartParameters = (PNDIS_PROTOCOL_RESTART_PARAMETERS)Buffer; + ndisprotRestart(pOpenContext,RestartParameters); + + + pOpenContext->State = NdisprotRunning; + break; + + case NetEventQueryRemoveDevice: + case NetEventCancelRemoveDevice: + case NetEventReconfigure: + case NetEventBindList: + case NetEventPnPCapabilities: + Status = NDIS_STATUS_SUCCESS; + break; + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + DEBUGP(DL_INFO, ("PnPEvent: Open %p, Event %d, Status %x\n", + pOpenContext, pNetPnPEventNotification->NetPnPEvent.NetEvent, Status)); + + return (Status); +} + +VOID +NdisprotProtocolUnloadHandler( + VOID + ) +/*++ + +Routine Description: + + NDIS calls this on a usermode request to uninstall us. + +Arguments: + + None + +Return Value: + + None + +--*/ +{ + ndisprotDoProtocolUnload(); +} + +NDIS_STATUS +ndisprotCreateBinding( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNDIS_BIND_PARAMETERS BindParameters, + IN NDIS_HANDLE BindContext, + _In_reads_bytes_(BindingInfoLength) IN PUCHAR pBindingInfo, + IN ULONG BindingInfoLength + ) +/*++ + +Routine Description: + + Utility function to create an NDIS binding to the indicated device, + if no such binding exists. + + Here is where we also allocate additional resources (e.g. packet pool) + for the binding. + + NOTE: this function blocks and finishes synchronously. + +Arguments: + + pOpenContext - pointer to open context block + BindParameters - pointer to NDIS_BIND_PARAMETERS + BindConext - pointer to NDIS bind context + pBindingInfo - pointer to unicode device name string + BindingInfoLength - length in bytes of the above. + +Return Value: + + NDIS_STATUS_SUCCESS if a binding was successfully set up. + NDIS_STATUS_XXX error code on any failure. + +--*/ +{ + NDIS_STATUS Status; + NDIS_MEDIUM MediumArray[1] = {NdisMedium802_3}; + NDIS_OPEN_PARAMETERS OpenParameters; + NET_BUFFER_LIST_POOL_PARAMETERS PoolParameters; + UINT SelectedMediumIndex; + BOOLEAN fOpenComplete = FALSE; + ULONG GenericUlong = 0; + NET_FRAME_TYPE FrameTypeArray[2] = {NDIS_ETH_TYPE_802_1X, NDIS_ETH_TYPE_802_1Q}; +#if DBG + PNDISPROT_OPEN_CONTEXT pTmpOpenContext; +#endif + + DEBUGP(DL_LOUD, ("CreateBinding: open %p/%x, device [%s]\n", + pOpenContext, pOpenContext->Flags, pBindingInfo)); + + Status = NDIS_STATUS_SUCCESS; + + do + { + // + // Check if we already have a binding to this device. + // +#if DBG + pTmpOpenContext = ndisprotLookupDevice(pBindingInfo, BindingInfoLength); + + ASSERT(pTmpOpenContext == NULL); + + if (pTmpOpenContext != NULL) + { + DEBUGP(DL_WARN, + ("CreateBinding: Binding to device %ws already exists on open %p\n", + pTmpOpenContext->DeviceName.Buffer, pTmpOpenContext)); + + NPROT_DEREF_OPEN(pTmpOpenContext); // temp ref added by Lookup + Status = NDIS_STATUS_FAILURE; + break; + } +#endif + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_OPENING); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Copy in the device name. Add room for a NULL terminator. + // + NPROT_ALLOC_MEM(pOpenContext->DeviceName.Buffer, BindingInfoLength + sizeof(WCHAR)); + if (pOpenContext->DeviceName.Buffer == NULL) + { + DEBUGP(DL_WARN, ("CreateBinding: failed to alloc device name buf (%d bytes)\n", + BindingInfoLength + sizeof(WCHAR))); + Status = NDIS_STATUS_RESOURCES; + break; + } + + NPROT_COPY_MEM(pOpenContext->DeviceName.Buffer, pBindingInfo, BindingInfoLength); +#pragma prefast(suppress: 12009, "DeviceName length will not cause overflow") + *(PWCHAR)((PUCHAR)pOpenContext->DeviceName.Buffer + BindingInfoLength) = L'\0'; + NdisInitUnicodeString(&pOpenContext->DeviceName, pOpenContext->DeviceName.Buffer); + + NdisZeroMemory(&PoolParameters, sizeof(NET_BUFFER_LIST_POOL_PARAMETERS)); + + PoolParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + PoolParameters.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + PoolParameters.Header.Size = sizeof(PoolParameters); + PoolParameters.ProtocolId = NDIS_PROTOCOL_ID_IPX ; + PoolParameters.ContextSize = sizeof(NPROT_SEND_NETBUFLIST_RSVD); + PoolParameters.fAllocateNetBuffer = TRUE; + PoolParameters.PoolTag = NPROT_ALLOC_TAG; + + pOpenContext->SendNetBufferListPool = NdisAllocateNetBufferListPool( + Globals.NdisProtocolHandle, + &PoolParameters); + if (pOpenContext->SendNetBufferListPool == NULL) + { + DEBUGP(DL_WARN, ("CreateBinding: failed to alloc" + " send net buffer list pool\n")); + + Status = NDIS_STATUS_RESOURCES; + break; + } + + PoolParameters.ContextSize = 0; + + pOpenContext->RecvNetBufferListPool = NdisAllocateNetBufferListPool( + Globals.NdisProtocolHandle, + &PoolParameters); + + if (pOpenContext->RecvNetBufferListPool == NULL) + { + DEBUGP(DL_WARN, ("CreateBinding: failed to alloc" + " recv net buffer list pool.\n")); + + Status = NDIS_STATUS_RESOURCES; + break; + } + + // + // Assume that the device is powered up. + // + pOpenContext->PowerState = NetDeviceStateD0; + + // + // Open the adapter. + // + NPROT_INIT_EVENT(&pOpenContext->BindEvent); + + NPROT_ZERO_MEM(&OpenParameters, sizeof(NDIS_OPEN_PARAMETERS)); + OpenParameters.Header.Revision = NDIS_OPEN_PARAMETERS_REVISION_1; + OpenParameters.Header.Size = sizeof(NDIS_OPEN_PARAMETERS); + OpenParameters.Header.Type = NDIS_OBJECT_TYPE_OPEN_PARAMETERS; + OpenParameters.AdapterName = BindParameters->AdapterName; + OpenParameters.MediumArray = &MediumArray[0]; + OpenParameters.MediumArraySize = sizeof(MediumArray) / sizeof(NDIS_MEDIUM); + OpenParameters.SelectedMediumIndex = &SelectedMediumIndex; + OpenParameters.FrameTypeArray = &FrameTypeArray[0]; + OpenParameters.FrameTypeArraySize = sizeof(FrameTypeArray) / sizeof(NET_FRAME_TYPE); + + + NDIS_DECLARE_PROTOCOL_OPEN_CONTEXT(NDISPROT_OPEN_CONTEXT); + Status = NdisOpenAdapterEx(Globals.NdisProtocolHandle, + (NDIS_HANDLE)pOpenContext, + &OpenParameters, + BindContext, + &pOpenContext->BindingHandle); + + if (Status == NDIS_STATUS_PENDING) + { + NPROT_WAIT_EVENT(&pOpenContext->BindEvent, 0); + Status = pOpenContext->BindStatus; + } + + if (Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_WARN, ("CreateBinding: NdisOpenAdapter (%ws) failed: %x\n", + pOpenContext->DeviceName.Buffer, Status)); + break; + } + + pOpenContext->State = NdisprotPaused; + + fOpenComplete = TRUE; + + // + // Get the friendly name for the adapter. It is not fatal for this + // to fail. + // + (VOID)NdisQueryAdapterInstanceName( + &pOpenContext->DeviceDescr, + pOpenContext->BindingHandle + ); + + NdisMoveMemory(&pOpenContext->CurrentAddress[0], + BindParameters->CurrentMacAddress, + NPROT_MAC_ADDR_LEN); + // + // Get MAC options. + // + pOpenContext->MacOptions = BindParameters->MacOptions; + + + // + // Get the max frame size. + // + pOpenContext->MaxFrameSize = BindParameters->MtuSize; + + // + // Get the media connect status. + // + GenericUlong = BindParameters->MediaConnectState; + + if (GenericUlong == NdisMediaStateConnected) + { + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_MEDIA_FLAGS, NPROTO_MEDIA_CONNECTED); + } + else + { + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_MEDIA_FLAGS, NPROTO_MEDIA_DISCONNECTED); + } + // + // Get the back fill size + // + pOpenContext->DataBackFillSize = BindParameters->DataBackFillSize; + pOpenContext->ContextBackFillSize = BindParameters->ContextBackFillSize; + + // + // Mark this open. Also check if we received an Unbind while + // we were setting this up. + // + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE); + + ASSERT(!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_UNBIND_FLAGS, NPROTO_UNBIND_RECEIVED)); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + } + while (FALSE); + + if (Status != NDIS_STATUS_SUCCESS) + { + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Check if we had actually finished opening the adapter. + // + if (fOpenComplete) + { + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE); + } + else if (NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_OPENING)) + { + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_FAILED); + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + ndisprotShutdownBinding(pOpenContext); + } + + DEBUGP(DL_INFO, ("CreateBinding: OpenContext %p, Status %x\n", + pOpenContext, Status)); + + return (Status); +} + + + +VOID +ndisprotShutdownBinding( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ) +/*++ + +Routine Description: + + Utility function to shut down the NDIS binding, if one exists, on + the specified open. This is written to be called from: + + ndisprotCreateBinding - on failure + NdisprotUnbindAdapter + + We handle the case where a binding is in the process of being set up. + This precaution is not needed if this routine is only called from + the context of our UnbindAdapter handler, but they are here in case + we initiate unbinding from elsewhere (e.g. on processing a user command). + + NOTE: this blocks and finishes synchronously. + +Arguments: + + pOpenContext - pointer to open context block + +Return Value: + + None + +--*/ +{ + NDIS_STATUS Status; + BOOLEAN DoCloseBinding = FALSE; + NPROT_EVENT ClosingEvent; + + do + { + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + if (NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_OPENING)) + { + // + // We are still in the process of setting up this binding. + // + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + break; + } + + if (NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)) + { + ASSERT(pOpenContext->ClosingEvent == NULL); + pOpenContext->ClosingEvent = NULL; + + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_CLOSING); + + if (pOpenContext->PendedSendCount != 0) + { + pOpenContext->ClosingEvent = &ClosingEvent; + NPROT_INIT_EVENT(&ClosingEvent); + } + + DoCloseBinding = TRUE; + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + if (DoCloseBinding) + { + ULONG PacketFilter = 0; + ULONG BytesRead = 0; + + // + // Set Packet filter to 0 before closing the binding + // + Status = ndisprotDoRequest( + pOpenContext, + NDIS_DEFAULT_PORT_NUMBER, + NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, + &PacketFilter, + sizeof(PacketFilter), + &BytesRead); + + if (Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_WARN, ("ShutDownBinding: set packet filter failed: %x\n", Status)); + } + + // + // Set multicast list to null before closing the binding + // + Status = ndisprotDoRequest( + pOpenContext, + NDIS_DEFAULT_PORT_NUMBER, + NdisRequestSetInformation, + OID_802_3_MULTICAST_LIST, + NULL, + 0, + &BytesRead); + + if (Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_WARN, ("ShutDownBinding: set multicast list failed: %x\n", Status)); + } + + // + // Wait for any pending sends or requests on + // the binding to complete. + // + ndisprotWaitForPendingIO(pOpenContext, TRUE); + + // + // Discard any queued receives. + // + ndisprotFlushReceiveQueue(pOpenContext); + + // + // Close the binding now. + // + NPROT_INIT_EVENT(&pOpenContext->BindEvent); + + DEBUGP(DL_INFO, ("ShutdownBinding: Closing OpenContext %p," + " BindingHandle %p\n", + pOpenContext, pOpenContext->BindingHandle)); + + Status = NdisCloseAdapterEx(pOpenContext->BindingHandle); + + if (Status == NDIS_STATUS_PENDING) + { + NPROT_WAIT_EVENT(&pOpenContext->BindEvent, 0); + Status = pOpenContext->BindStatus; + } + + NPROT_ASSERT(Status == NDIS_STATUS_SUCCESS); + + pOpenContext->BindingHandle = NULL; + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_IDLE); + + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_UNBIND_FLAGS, 0); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + } + } while (FALSE); + + + // + // Remove it from the global list. + // + NPROT_ACQUIRE_LOCK(&Globals.GlobalLock, FALSE); + + NPROT_REMOVE_ENTRY_LIST(&pOpenContext->Link); + + NPROT_RELEASE_LOCK(&Globals.GlobalLock, FALSE); + + // + // Free any other resources allocated for this bind. + // + ndisprotFreeBindResources(pOpenContext); + + NPROT_DEREF_OPEN(pOpenContext); // Shutdown binding + +} + + +VOID +ndisprotFreeBindResources( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ) +/*++ + +Routine Description: + + Free any resources set up for an NDIS binding. + +Arguments: + + pOpenContext - pointer to open context block + +Return Value: + + None + +--*/ +{ + if (pOpenContext->SendNetBufferListPool != NULL) + { + NdisFreeNetBufferListPool(pOpenContext->SendNetBufferListPool); + pOpenContext->SendNetBufferListPool = NULL; + } + + if (pOpenContext->RecvNetBufferListPool != NULL) + { + NdisFreeNetBufferListPool(pOpenContext->RecvNetBufferListPool); + pOpenContext->RecvNetBufferListPool = NULL; + } + + if (pOpenContext->DeviceName.Buffer != NULL) + { + NPROT_FREE_MEM(pOpenContext->DeviceName.Buffer); + pOpenContext->DeviceName.Buffer = NULL; + pOpenContext->DeviceName.Length = + pOpenContext->DeviceName.MaximumLength = 0; + } + + if (pOpenContext->DeviceDescr.Buffer != NULL) + { + // + // this would have been allocated by NdisQueryAdpaterInstanceName. + // + NdisFreeMemory(pOpenContext->DeviceDescr.Buffer, 0, 0); + pOpenContext->DeviceDescr.Buffer = NULL; + } +} + + +VOID +ndisprotWaitForPendingIO( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN BOOLEAN DoCancelReads + ) +/*++ + +Routine Description: + + Utility function to wait for all outstanding I/O to complete + on an open context. It is assumed that the open context + won't go away while we are in this routine. + +Arguments: + + pOpenContext - pointer to open context structure + DoCancelReads - do we wait for pending reads to go away (and cancel them)? + +Return Value: + + None + +--*/ +{ + // + // Wait for any pending sends or requests on the binding to complete. + // + if (pOpenContext->PendedSendCount == 0) + { + ASSERT(pOpenContext->ClosingEvent == NULL); + } + else + { + ASSERT(pOpenContext->ClosingEvent != NULL); + DEBUGP(DL_WARN, ("WaitForPendingIO: Open %p, %d pended sends\n", + pOpenContext, pOpenContext->PendedSendCount)); + + NPROT_WAIT_EVENT(pOpenContext->ClosingEvent, 0); + + } + + if (DoCancelReads) + { + // + // Wait for any pended reads to complete/cancel. + // + while (pOpenContext->PendedReadCount != 0) + { + DEBUGP(DL_INFO, ("WaitForPendingIO: Open %p, %d pended reads\n", + pOpenContext, pOpenContext->PendedReadCount)); + + // + // Cancel any pending reads. + // + ndisprotCancelPendingReads(pOpenContext); + + NdisMSleep(100000); // 100 ms. + } + } + +} + + +VOID +ndisprotDoProtocolUnload( + VOID + ) +/*++ + +Routine Description: + + Utility routine to handle unload from the NDIS protocol side. + +Arguments: + + None + +Return Value: + + None + +--*/ +{ + NDIS_HANDLE ProtocolHandle; + + DEBUGP(DL_INFO, ("ProtocolUnload: ProtocolHandle %lp\n", + Globals.NdisProtocolHandle)); + + if (Globals.NdisProtocolHandle != NULL) + { + ProtocolHandle = Globals.NdisProtocolHandle; + Globals.NdisProtocolHandle = NULL; + + NdisDeregisterProtocolDriver(ProtocolHandle); + + } +} + + +NDIS_STATUS +ndisprotDoRequest( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN NDIS_PORT_NUMBER PortNumber, + IN NDIS_REQUEST_TYPE RequestType, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + OUT PULONG pBytesProcessed + ) +/*++ + +Routine Description: + + Utility routine that forms and sends an NDIS_REQUEST to the + miniport, waits for it to complete, and returns status + to the caller. + + NOTE: this assumes that the calling routine ensures validity + of the binding handle until this returns. + +Arguments: + + pOpenContext - pointer to our open context + PortNumber - the port to issue the request + RequestType - NdisRequest[Set|Query|Method]Information + Oid - the object being set/queried + InformationBuffer - data for the request + InformationBufferLength - length of the above + pBytesProcessed - place to return bytes read/written + +Return Value: + + Status of the set/query/method request + +--*/ +{ + NDISPROT_REQUEST ReqContext; + PNDIS_OID_REQUEST pNdisRequest = &ReqContext.Request; + NDIS_STATUS Status; + + + NdisZeroMemory(&ReqContext, sizeof(ReqContext)); + + NPROT_INIT_EVENT(&ReqContext.ReqEvent); + pNdisRequest->Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST; + pNdisRequest->Header.Revision = NDIS_OID_REQUEST_REVISION_1; + pNdisRequest->Header.Size = sizeof(NDIS_OID_REQUEST); + pNdisRequest->RequestType = RequestType; + pNdisRequest->PortNumber = PortNumber; + + switch (RequestType) + { + case NdisRequestQueryInformation: + pNdisRequest->DATA.QUERY_INFORMATION.Oid = Oid; + pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer = + InformationBuffer; + pNdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength = + InformationBufferLength; + break; + + case NdisRequestSetInformation: + pNdisRequest->DATA.SET_INFORMATION.Oid = Oid; + pNdisRequest->DATA.SET_INFORMATION.InformationBuffer = + InformationBuffer; + pNdisRequest->DATA.SET_INFORMATION.InformationBufferLength = + InformationBufferLength; + break; + + default: + NPROT_ASSERT(FALSE); + break; + } + + pNdisRequest->RequestId = NPROT_GET_NEXT_CANCEL_ID(); + Status = NdisOidRequest(pOpenContext->BindingHandle, + pNdisRequest); + + + if (Status == NDIS_STATUS_PENDING) + { + + NPROT_WAIT_EVENT(&ReqContext.ReqEvent, 0); + Status = ReqContext.Status; + } + + if (Status == NDIS_STATUS_SUCCESS) + { + *pBytesProcessed = (RequestType == NdisRequestQueryInformation)? + pNdisRequest->DATA.QUERY_INFORMATION.BytesWritten: + pNdisRequest->DATA.SET_INFORMATION.BytesRead; + + // + // The driver below should set the correct value to BytesWritten + // or BytesRead. But now, we just truncate the value to InformationBufferLength + // + if (*pBytesProcessed > InformationBufferLength) + { + *pBytesProcessed = InformationBufferLength; + } + } + + return (Status); +} + + +NDIS_STATUS +ndisprotValidateOpenAndDoRequest( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN NDIS_REQUEST_TYPE RequestType, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + OUT PULONG pBytesProcessed, + IN BOOLEAN bWaitForPowerOn + ) +/*++ + +Routine Description: + + Utility routine to prevalidate and reference an open context + before calling ndisprotDoRequest. This routine makes sure + we have a valid binding. + +Arguments: + + pOpenContext - pointer to our open context + RequestType - NdisRequest[Set|Query]Information + Oid - the object being set/queried + InformationBuffer - data for the request + InformationBufferLength - length of the above + pBytesProcessed - place to return bytes read/written + bWaitForPowerOn - Wait for the device to be powered on if it isn't already. + +Return Value: + + Status of the set/query request + +--*/ +{ + NDIS_STATUS Status; + + do + { + if (pOpenContext == NULL) + { + DEBUGP(DL_WARN, ("ValidateOpenAndDoRequest: request on unassociated file object!\n")); + Status = NDIS_STATUS_INVALID_DATA; + break; + } + + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Proceed only if we have a binding. + // + if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + Status = NDIS_STATUS_INVALID_DATA; + break; + } + + NPROT_ASSERT(pOpenContext->BindingHandle != NULL); + + // + // Make sure that the binding does not go away until we + // are finished with the request. + // + pOpenContext->PendedSendCount++; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + if (bWaitForPowerOn) + { + // + // Wait for the device below to be powered up. + // We don't wait indefinitely here - this is to avoid + // a PROCESS_HAS_LOCKED_PAGES bugcheck that could happen + // if the calling process terminates, and this IRP doesn't + // complete within a reasonable time. An alternative would + // be to explicitly handle cancellation of this IRP. + // + //NPROT_WAIT_EVENT(&pOpenContext->PoweredUpEvent, 4500); + //the following replace NPROT_WAIT_EVENT(&pOpenContext->PoweredUpEvent, 4500); to supress prefast warning 28193 + NdisWaitEvent(&pOpenContext->PoweredUpEvent, 4500); + } + + if (pOpenContext->PowerState == NetDeviceStateD0) + { + + Status = ndisprotDoRequest( + pOpenContext, + NDIS_DEFAULT_PORT_NUMBER, + RequestType, + Oid, + InformationBuffer, + InformationBufferLength, + pBytesProcessed); + } + else + { + Status = NDIS_STATUS_ADAPTER_NOT_READY; + } + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + // + // Let go of the binding. + // + pOpenContext->PendedSendCount --; + if ((NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_CLOSING)) + && (pOpenContext->PendedSendCount == 0)) + { + ASSERT(pOpenContext->ClosingEvent != NULL); + NPROT_SIGNAL_EVENT(pOpenContext->ClosingEvent); + pOpenContext->ClosingEvent = NULL; + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + } + while (FALSE); + + DEBUGP(DL_LOUD, ("ValidateOpenAndDoReq: Open %p/%x, OID %x, Status %x\n", + pOpenContext, pOpenContext->Flags, Oid, Status)); + + return (Status); +} + + +VOID +NdisprotRequestComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_OID_REQUEST pNdisRequest, + IN NDIS_STATUS Status + ) +/*++ + +Routine Description: + + NDIS entry point indicating completion of a pended NDIS_REQUEST. + +Arguments: + + ProtocolBindingContext - pointer to open context + pNdisRequest - pointer to NDIS request + Status - status of reset completion + +Return Value: + + None + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + PNDISPROT_REQUEST pReqContext; + + pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + // + // Get at the request context. + // + pReqContext = CONTAINING_RECORD(pNdisRequest, NDISPROT_REQUEST, Request); + + // + // Save away the completion status. + // + pReqContext->Status = Status; + + // + // Wake up the thread blocked for this request to complete. + // + NPROT_SIGNAL_EVENT(&pReqContext->ReqEvent); +} + + +VOID +NdisprotStatus( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_STATUS_INDICATION StatusIndication + ) +/*++ + +Routine Description: + + Protocol entry point called by NDIS to indicate a change + in status at the miniport. + + We make note of reset and media connect status indications. + +Arguments: + + ProtocolBindingContext - pointer to open context + StatusIndication - pointer to NDIS_STATUS_INDICATION + +Return Value: + + None + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + NDIS_STATUS GeneralStatus; + PNDIS_LINK_STATE LinkState; + + + pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + + if ((StatusIndication->Header.Type != NDIS_OBJECT_TYPE_STATUS_INDICATION) + || (StatusIndication->Header.Size != sizeof(NDIS_STATUS_INDICATION))) + { + DEBUGP(DL_INFO, ("Status: Received an invalid status indication: Open %p, StatusIndication %p\n", + pOpenContext, StatusIndication)); + return; + } + + GeneralStatus = StatusIndication->StatusCode; + + DEBUGP(DL_INFO, ("Status: Open %p, Status %x\n", + pOpenContext, GeneralStatus)); + + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + do + { + if (pOpenContext->PowerState != NetDeviceStateD0) + { + // + // + // The device is in a low power state. + + // + // We continue and make note of status indications + // + + // + // NOTE that any actions we take based on these + // status indications should take into account + // the current device power state. + // + } + + switch(GeneralStatus) + { + case NDIS_STATUS_RESET_START: + + NPROT_ASSERT(!NPROT_TEST_FLAGS(pOpenContext->Flags, + NPROTO_RESET_FLAGS, + NPROTO_RESET_IN_PROGRESS)); + + NPROT_SET_FLAGS(pOpenContext->Flags, + NPROTO_RESET_FLAGS, + NPROTO_RESET_IN_PROGRESS); + + break; + + case NDIS_STATUS_RESET_END: + + NPROT_ASSERT(NPROT_TEST_FLAGS(pOpenContext->Flags, + NPROTO_RESET_FLAGS, + NPROTO_RESET_IN_PROGRESS)); + + NPROT_SET_FLAGS(pOpenContext->Flags, + NPROTO_RESET_FLAGS, + NPROTO_NOT_RESETTING); + + break; + + case NDIS_STATUS_LINK_STATE: + + ASSERT(StatusIndication->StatusBufferSize >= sizeof(NDIS_LINK_STATE)); + + LinkState = (PNDIS_LINK_STATE)StatusIndication->StatusBuffer; + + if (LinkState->MediaConnectState == MediaConnectStateConnected) + { + NPROT_SET_FLAGS(pOpenContext->Flags, + NPROTO_MEDIA_FLAGS, + NPROTO_MEDIA_CONNECTED); + } + else + { + NPROT_SET_FLAGS(pOpenContext->Flags, + NPROTO_MEDIA_FLAGS, + NPROTO_MEDIA_DISCONNECTED); + } + + break; + + default: + break; + } + } + while (FALSE); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); +} + +NDIS_STATUS +ndisprotWriteString( + _Out_writes_bytes_to_(OutputLength, *pBytesWritten) + PUCHAR pBuffer, + _In_ ULONG OutputLength, + _Out_ PULONG pBytesWritten, + _In_ PNDIS_STRING String + ) +/*++ + +Routine Description: + + Writes an NDIS_STRING to an output buffer as a NULL-terminated string + +Arguments: + + pBuffer - output buffer + OutputLength - size of pBuffer + pBytesWritten - receives the number of bytes written to pBuffer + String - NDIS_STRING to write + +Return Value: + + NDIS_STATUS_SUCCESS if successful + NDIS_STATUS_BUFFER_OVERFLOW if OutputLength is insufficient + +--*/ +{ + ULONG BytesNeeded = String->Length + sizeof(UNICODE_NULL); + + if (OutputLength < BytesNeeded) + { + return NDIS_STATUS_BUFFER_OVERFLOW; + } + + NPROT_COPY_MEM(pBuffer, String->Buffer, String->Length); + *(PWCHAR)(pBuffer + String->Length) = UNICODE_NULL; + + *pBytesWritten = BytesNeeded; + return NDIS_STATUS_SUCCESS; +} + +NDIS_STATUS +ndisprotQueryBinding( + _Inout_updates_bytes_to_(OutputLength, *pBytesReturned) + PUCHAR pBuffer, + _In_ ULONG InputLength, + _In_ ULONG OutputLength, + _Out_ PULONG pBytesReturned + ) +/*++ + +Routine Description: + + Return information about the specified binding. + +Arguments: + + pBuffer - pointer to NDISPROT_QUERY_BINDING + InputLength - input buffer size + OutputLength - output buffer size + pBytesReturned - place to return copied byte count. + +Return Value: + + NDIS_STATUS_SUCCESS if successful, failure code otherwise. + +--*/ +{ + PNDISPROT_QUERY_BINDING pQueryBinding; + PNDISPROT_OPEN_CONTEXT pOpenContext; + PLIST_ENTRY pEnt; + ULONG BindingIndex; + NDIS_STATUS Status; + ULONG DeviceNameOffset; + ULONG DeviceDescrOffset; + ULONG StringBytesWritten; + + do + { + *pBytesReturned = 0; + + if (InputLength < sizeof(NDISPROT_QUERY_BINDING)) + { + Status = NDIS_STATUS_RESOURCES; + break; + } + + if (OutputLength < sizeof(NDISPROT_QUERY_BINDING)) + { + Status = NDIS_STATUS_BUFFER_OVERFLOW; + break; + } + + pQueryBinding = (PNDISPROT_QUERY_BINDING)pBuffer; + BindingIndex = pQueryBinding->BindingIndex; + + Status = NDIS_STATUS_ADAPTER_NOT_FOUND; + + pOpenContext = NULL; + + NPROT_ACQUIRE_LOCK(&Globals.GlobalLock, FALSE); + + for (pEnt = Globals.OpenList.Flink; + pEnt != &Globals.OpenList; + pEnt = pEnt->Flink) + { + pOpenContext = CONTAINING_RECORD(pEnt, NDISPROT_OPEN_CONTEXT, Link); + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Skip if not bound. + // + if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + continue; + } + + if (BindingIndex == 0) + { + // + // Got the binding we are looking for. Copy the device + // name and description strings to the output buffer. + // + DEBUGP(DL_INFO, + ("QueryBinding: found open %p\n", pOpenContext)); + + pQueryBinding->DeviceNameOffset = 0; + pQueryBinding->DeviceNameLength = pOpenContext->DeviceName.Length; + pQueryBinding->DeviceDescrOffset = 0; + pQueryBinding->DeviceDescrLength = pOpenContext->DeviceDescr.Length; + + *pBytesReturned = sizeof(NDISPROT_QUERY_BINDING); + + // + // Copy the DeviceName string + // + + DeviceNameOffset = sizeof(NDISPROT_QUERY_BINDING); + + Status = ndisprotWriteString(pBuffer + DeviceNameOffset, + OutputLength - DeviceNameOffset, + &StringBytesWritten, + &pOpenContext->DeviceName); + + if (NDIS_STATUS_SUCCESS != Status) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + break; + } + + pQueryBinding->DeviceNameOffset = DeviceNameOffset; + *pBytesReturned += StringBytesWritten; + + // + // Copy the DeviceDescr string + // + + DeviceDescrOffset = DeviceNameOffset + StringBytesWritten; + + Status = ndisprotWriteString(pBuffer + DeviceDescrOffset, + OutputLength - DeviceDescrOffset, + &StringBytesWritten, + &pOpenContext->DeviceDescr); + + if (NDIS_STATUS_SUCCESS != Status) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + break; + } + + pQueryBinding->DeviceDescrOffset = DeviceDescrOffset; + *pBytesReturned += StringBytesWritten; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + Status = NDIS_STATUS_SUCCESS; + break; + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + BindingIndex--; + } + + NPROT_RELEASE_LOCK(&Globals.GlobalLock, FALSE); + + } + while (FALSE); + + return (Status); +} + +PNDISPROT_OPEN_CONTEXT +ndisprotLookupDevice( + _In_reads_bytes_(BindingInfoLength) IN PUCHAR pBindingInfo, + IN ULONG BindingInfoLength + ) +/*++ + +Routine Description: + + Search our global list for an open context structure that + has a binding to the specified device, and return a pointer + to it. + + NOTE: we reference the open that we return. + +Arguments: + + pBindingInfo - pointer to unicode device name string + BindingInfoLength - length in bytes of the above. + +Return Value: + + Pointer to the matching open context if found, else NULL + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + PLIST_ENTRY pEnt; + + pOpenContext = NULL; + + NPROT_ACQUIRE_LOCK(&Globals.GlobalLock, FALSE); + + for (pEnt = Globals.OpenList.Flink; + pEnt != &Globals.OpenList; + pEnt = pEnt->Flink) + { + pOpenContext = CONTAINING_RECORD(pEnt, NDISPROT_OPEN_CONTEXT, Link); + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + // + // Check if this has the name we are looking for. + // + if ((pOpenContext->DeviceName.Length == BindingInfoLength) && + NPROT_MEM_CMP(pOpenContext->DeviceName.Buffer, pBindingInfo, BindingInfoLength)) + { + NPROT_REF_OPEN(pOpenContext); // ref added by LookupDevice + break; + } + + pOpenContext = NULL; + } + + NPROT_RELEASE_LOCK(&Globals.GlobalLock, FALSE); + + return (pOpenContext); +} + + +NDIS_STATUS +ndisprotQueryOidValue( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + OUT PVOID pDataBuffer, + IN ULONG BufferLength, + OUT PULONG pBytesWritten + ) +/*++ + +Routine Description: + + Query an arbitrary OID value from the miniport. + +Arguments: + + pOpenContext - pointer to open context representing our binding to the miniport + pDataBuffer - place to store the returned value + BufferLength - length of the above + pBytesWritten - place to return length returned + +Return Value: + + NDIS_STATUS_SUCCESS if we successfully queried the OID. + NDIS_STATUS_XXX error code otherwise. + +--*/ +{ + NDIS_STATUS Status; + PNDISPROT_QUERY_OID pQuery; + NDIS_OID Oid; + + Oid = 0; + + do + { + if (BufferLength < sizeof(NDISPROT_QUERY_OID)) + { + Status = NDIS_STATUS_BUFFER_TOO_SHORT; + break; + } + + pQuery = (PNDISPROT_QUERY_OID)pDataBuffer; + Oid = pQuery->Oid; + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)) + { + DEBUGP(DL_WARN, + ("QueryOid: Open %p/%x is in invalid state\n", + pOpenContext, pOpenContext->Flags)); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + Status = NDIS_STATUS_FAILURE; + break; + } + + // + // Make sure the binding doesn't go away. + // + pOpenContext->PendedSendCount++; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + Status = ndisprotDoRequest( + pOpenContext, + pQuery->PortNumber, + NdisRequestQueryInformation, + Oid, + &pQuery->Data[0], + BufferLength - FIELD_OFFSET(NDISPROT_QUERY_OID, Data), + pBytesWritten); + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Let go of the binding. + // + pOpenContext->PendedSendCount --; + if ((NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_CLOSING)) + && (pOpenContext->PendedSendCount == 0)) + { + ASSERT(pOpenContext->ClosingEvent != NULL); + NPROT_SIGNAL_EVENT(pOpenContext->ClosingEvent); + pOpenContext->ClosingEvent = NULL; + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + if (Status == NDIS_STATUS_SUCCESS) + { + *pBytesWritten += FIELD_OFFSET(NDISPROT_QUERY_OID, Data); + } + + } + while (FALSE); + + DEBUGP(DL_LOUD, ("QueryOid: Open %p/%x, OID %x, Status %x\n", + pOpenContext, pOpenContext->Flags, Oid, Status)); + + return (Status); + +} + +NDIS_STATUS +ndisprotSetOidValue( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + OUT PVOID pDataBuffer, + IN ULONG BufferLength + ) +/*++ + +Routine Description: + + Set an arbitrary OID value to the miniport. + +Arguments: + + pOpenContext - pointer to open context representing our binding to the miniport + pDataBuffer - buffer that contains the value to be set + BufferLength - length of the above + +Return Value: + + NDIS_STATUS_SUCCESS if we successfully set the OID + NDIS_STATUS_XXX error code otherwise. + +--*/ +{ + NDIS_STATUS Status; + PNDISPROT_SET_OID pSet; + NDIS_OID Oid; + ULONG BytesWritten; + + Oid = 0; + + do + { + if (BufferLength < sizeof(NDISPROT_SET_OID)) + { + Status = NDIS_STATUS_BUFFER_TOO_SHORT; + break; + } + + pSet = (PNDISPROT_SET_OID)pDataBuffer; + Oid = pSet->Oid; + + // + // We should check the OID is settable by the user mode apps + // + if (!ndisprotValidOid(Oid)) + { + DEBUGP(DL_WARN, ("SetOid: Oid %x cannot be set\n", Oid)); + + Status = NDIS_STATUS_INVALID_DATA; + break; + } + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)) + { + DEBUGP(DL_WARN, + ("SetOid: Open %p/%x is in invalid state\n", + pOpenContext, pOpenContext->Flags)); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + Status = NDIS_STATUS_FAILURE; + break; + } + + // + // Make sure the binding doesn't go away. + // + pOpenContext->PendedSendCount++; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + Status = ndisprotDoRequest( + pOpenContext, + pSet->PortNumber, + NdisRequestSetInformation, + Oid, + &pSet->Data[0], + BufferLength - FIELD_OFFSET(NDISPROT_SET_OID, Data), + &BytesWritten); + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Let go of the binding. + // + pOpenContext->PendedSendCount --; + if ((NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_CLOSING)) + && (pOpenContext->PendedSendCount == 0)) + { + ASSERT(pOpenContext->ClosingEvent != NULL); + NPROT_SIGNAL_EVENT(pOpenContext->ClosingEvent); + pOpenContext->ClosingEvent = NULL; + } + + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + } + while (FALSE); + + DEBUGP(DL_LOUD, ("SetOid: Open %p/%x, OID %x, Status %x\n", + pOpenContext, pOpenContext->Flags, Oid, Status)); + + return (Status); +} + +BOOLEAN +ndisprotValidOid( + IN NDIS_OID Oid + ) +/*++ + +Routine Description: + + Validate whether the given set OID is settable or not. + +Arguments: + + Oid - The OID which the user tries to set. + +Return Value: + + TRUE if the OID is allowed to set + FALSE otherwise. + +--*/ +{ + UINT i; + UINT NumOids; + + NumOids = sizeof(ndisprotSupportedSetOids) / sizeof(NDIS_OID); + + for (i = 0; i < NumOids; i++) + { + if (ndisprotSupportedSetOids[i] == Oid) + { + break; + } + } + + return (i < NumOids); +} + + + +VOID +#pragma warning(suppress:6262) // Function is only 22 bytes over the 1k guideline, reasonable enough. +ndisprotRestart( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNDIS_PROTOCOL_RESTART_PARAMETERS RestartParameters + ) +/*++ + +Routine Description: + + Handle restart attributes changes. + +Arguments: + + pOpenContext - pointer to open context + RestartParameters - pointer to ndis restart parameters + +Return Value: + + None + +NOTE: Protocols should query any attribute: + 1. the attribute is not included in the RestartAttributes + and 2. The protocol cares about whether the attributes is changed by underlying driver. + +--*/ + +{ + ULONG Length; + ULONG TotalLength = 0; + PUCHAR Buffer; + ULONG BufferLength; +#define NPROT_MAX_FILTER_NAME_LENGTH 512 + + WCHAR FilterNameBuffer[NPROT_MAX_FILTER_NAME_LENGTH]; + PNDIS_RESTART_ATTRIBUTES NdisRestartAttributes; + PNDIS_RESTART_GENERAL_ATTRIBUTES NdisGeneralAttributes; + + DEBUGP(DL_LOUD, ("ndisprotRestart: Open %p", pOpenContext)); + // + // Check the filter stack changes + // + if (RestartParameters->FilterModuleNameBuffer != NULL) + { + + Buffer = RestartParameters->FilterModuleNameBuffer; + + while (RestartParameters->FilterModuleNameBufferLength > TotalLength) + { + + BufferLength = *(PUSHORT)Buffer; + + TotalLength += BufferLength + sizeof(USHORT); + Length = BufferLength + sizeof(USHORT); + + if (BufferLength >= (NPROT_MAX_FILTER_NAME_LENGTH * sizeof(WCHAR))) + { + BufferLength = (NPROT_MAX_FILTER_NAME_LENGTH - 1) * sizeof(WCHAR); + } + NdisMoveMemory(FilterNameBuffer, Buffer + sizeof(USHORT), BufferLength); + + BufferLength /= sizeof(WCHAR); + + // + // BufferLength is bounded by the check above. Check again to suppress + // prefast warning + // + if (BufferLength < NPROT_MAX_FILTER_NAME_LENGTH) + { + FilterNameBuffer[BufferLength] = 0; + } + + DEBUGP(DL_INFO, ("Filter: %ws\n", FilterNameBuffer)); + + Buffer += Length; + } + } + // + // Checked for updated attributes + // + NdisRestartAttributes = RestartParameters->RestartAttributes; + + // + // NdisProt is only interested in the generic attributes. + // + while (NdisRestartAttributes != NULL) + { + if (NdisRestartAttributes->Oid == OID_GEN_MINIPORT_RESTART_ATTRIBUTES) + { + break; + } + NdisRestartAttributes = NdisRestartAttributes->Next; + } + + // + // Pick up the new attributes of interest + // + if (NdisRestartAttributes != NULL) + { + NdisGeneralAttributes = (PNDIS_RESTART_GENERAL_ATTRIBUTES)NdisRestartAttributes->Data; + + pOpenContext->MacOptions = NdisGeneralAttributes->MacOptions; + pOpenContext->MaxFrameSize = NdisGeneralAttributes->MtuSize; + } + + DEBUGP(DL_LOUD, ("ndisprotRestart: Open %p", pOpenContext)); + +} + + diff --git a/network/ndis/ndisprot/6x/sys/ndisprot.h b/network/ndis/ndisprot/6x/sys/ndisprot.h new file mode 100644 index 00000000..60ebb2af --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/ndisprot.h @@ -0,0 +1,567 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + ndisprot.h + +Abstract: + + Data structures, defines and function prototypes for NDISPROT. + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#ifndef __NDISPROT__H +#define __NDISPROT__H + +#pragma warning(disable:28930) // Unused assignment of pointer, by design in samples +#pragma warning(disable:28931) // Unused assignment of variable, by design in samples + +// +// Update the driver version number every time you release a new driver +// The high word is the major version. The low word is the minor version. +// Also make sure that VER_FILEVERSION specified in the .RC file also +// matches with the driver version. +// +// Let's say we're version 4.2. +// +#define MAJOR_DRIVER_VERSION 0x04 +#define MINOR_DRIVER_VERISON 0x02 + + +// +// Define the NDIS protocol interface version that this driver targets. +// +#if defined(NDIS60) +# define NDIS_PROT_MAJOR_VERSION 6 +# define NDIS_PROT_MINOR_VERSION 0 +#elif defined(NDIS630) +# define NDIS_PROT_MAJOR_VERSION 6 +# define NDIS_PROT_MINOR_VERSION 30 +#else +# error Unsupported NDIS version +#endif + + +#define NT_DEVICE_NAME L"\\Device\\Ndisprot" +#define DOS_DEVICE_NAME L"\\Global??\\Ndisprot" + + +// +// Abstract types +// +typedef NDIS_EVENT NPROT_EVENT, *PNPROT_EVENT; + +#define MAX_MULTICAST_ADDRESS 0x20 + +#define NPROT_MAC_ADDR_LEN 6 + + +typedef enum _NDISPROT_OPEN_STATE{ + NdisprotInitializing, + NdisprotRunning, + NdisprotPausing, + NdisprotPaused, + NdisprotRestarting, + NdisprotClosing +} NDISPROT_OPEN_STATE; +// +// The Open Context represents an open of our device object. +// We allocate this on processing a BindAdapter from NDIS, +// and free it when all references (see below) to it are gone. +// +// Binding/unbinding to an NDIS device: +// +// On processing a BindAdapter call from NDIS, we set up a binding +// to the specified NDIS device (miniport). This binding is +// torn down when NDIS asks us to Unbind by calling +// our UnbindAdapter handler. +// +// Receiving data: +// +// While an NDIS binding exists, read IRPs are queued on this +// structure, to be processed when packets are received. +// If data arrives in the absense of a pended read IRP, we +// queue it, to the extent of one packet, i.e. we save the +// contents of the latest packet received. We fail read IRPs +// received when no NDIS binding exists (or is in the process +// of being torn down). +// +// Sending data: +// +// Write IRPs are used to send data. Each write IRP maps to +// a single NDIS packet. Packet send-completion is mapped to +// write IRP completion. We use NDIS 5.1 CancelSend to support +// write IRP cancellation. Write IRPs that arrive when we don't +// have an active NDIS binding are failed. +// +// Reference count: +// +// The following are long-lived references: +// OPEN_DEVICE ioctl (goes away on processing a Close IRP) +// Pended read IRPs +// Queued received packets +// Uncompleted write IRPs (outstanding sends) +// Existence of NDIS binding +// +typedef struct _NDISPROT_OPEN_CONTEXT +{ + LIST_ENTRY Link; // Link into global list + ULONG Flags; // State information + ULONG RefCount; + NPROT_LOCK Lock; + + PFILE_OBJECT pFileObject; // Set on OPEN_DEVICE + + NDIS_HANDLE BindingHandle; + NDIS_HANDLE SendNetBufferListPool; + // let every net buffer list contain one net buffer(don't know how many net buffers can be include in one list. + NDIS_HANDLE RecvNetBufferListPool; + + ULONG MacOptions; + ULONG MaxFrameSize; + ULONG DataBackFillSize; + ULONG ContextBackFillSize; + + LIST_ENTRY PendedWrites; // pended Write IRPs + ULONG PendedSendCount; + + LIST_ENTRY PendedReads; // pended Read IRPs + ULONG PendedReadCount; + LIST_ENTRY RecvNetBufListQueue; + ULONG RecvNetBufListCount; + + NET_DEVICE_POWER_STATE PowerState; + NDIS_EVENT PoweredUpEvent; // signalled iff PowerState is D0 + NDIS_STRING DeviceName; // used in NdisOpenAdapter + NDIS_STRING DeviceDescr; // friendly name + + NDIS_STATUS BindStatus; // for Open/CloseAdapter + NPROT_EVENT BindEvent; // for Open/CloseAdapter + + ULONG oc_sig; // Signature for sanity + NDISPROT_OPEN_STATE State; + PNPROT_EVENT ClosingEvent; + UCHAR CurrentAddress[NPROT_MAC_ADDR_LEN]; + UCHAR MCastAddress[MAX_MULTICAST_ADDRESS][NPROT_MAC_ADDR_LEN]; +} NDISPROT_OPEN_CONTEXT, *PNDISPROT_OPEN_CONTEXT; + + +#define oc_signature 'OiuN' + +// +// Definitions for Flags above. +// +#define NPROTO_BIND_IDLE 0x00000000 +#define NPROTO_BIND_OPENING 0x00000001 +#define NPROTO_BIND_FAILED 0x00000002 +#define NPROTO_BIND_ACTIVE 0x00000004 +#define NPROTO_BIND_CLOSING 0x00000008 +#define NPROTO_BIND_FLAGS 0x0000000F // State of the binding + +#define NPROTO_OPEN_IDLE 0x00000000 +#define NPROTO_OPEN_ACTIVE 0x00000010 +#define NPROTO_OPEN_FLAGS 0x000000F0 // State of the I/O open + +#define NPROTO_RESET_IN_PROGRESS 0x00000100 +#define NPROTO_NOT_RESETTING 0x00000000 +#define NPROTO_RESET_FLAGS 0x00000100 + +#define NPROTO_MEDIA_CONNECTED 0x00000000 +#define NPROTO_MEDIA_DISCONNECTED 0x00000200 +#define NPROTO_MEDIA_FLAGS 0x00000200 + +#define NPROTO_READ_SERVICING 0x00100000 // Is the read service + // routine running? +#define NPROTO_READ_FLAGS 0x00100000 + +#define NPROTO_UNBIND_RECEIVED 0x10000000 // Seen NDIS Unbind? +#define NPROTO_UNBIND_FLAGS 0x10000000 + + +#define NPROT_ALLOCATED_NBL 0x10000000 +#define NPROT_NBL_RETREAT_RECV_RSVD 0x20000000 + +// +// Globals: +// +typedef struct _NDISPROT_GLOBALS +{ + PDRIVER_OBJECT pDriverObject; + PDEVICE_OBJECT ControlDeviceObject; + NDIS_HANDLE NdisProtocolHandle; + USHORT EthType; // frame type we are interested in + UCHAR PartialCancelId; // for cancelling sends + ULONG LocalCancelId; + LIST_ENTRY OpenList; // of OPEN_CONTEXT structures + NPROT_LOCK GlobalLock; // to protect the above + NPROT_EVENT BindsComplete; // have we seen NetEventBindsComplete? +} NDISPROT_GLOBALS, *PNDISPROT_GLOBALS; + + +// +// The following are arranged in the way a little-endian processor +// would read 2 bytes off the wire. +// +#define NPROT_ETH_TYPE 0x8e88 +#define NPROT_8021P_TAG_TYPE 0x0081 + +// +// NDIS Request context structure +// +typedef struct _NDISPROT_REQUEST +{ + NDIS_OID_REQUEST Request; + NPROT_EVENT ReqEvent; + ULONG Status; + +} NDISPROT_REQUEST, *PNDISPROT_REQUEST; + + +#define NPROTO_PACKET_FILTER (NDIS_PACKET_TYPE_DIRECTED| \ + NDIS_PACKET_TYPE_MULTICAST| \ + NDIS_PACKET_TYPE_BROADCAST) + +// +// Send packet pool bounds +// +/* +#define MIN_SEND_PACKET_POOL_SIZE 20 +*/ +#define MAX_SEND_PACKET_POOL_SIZE 400 + + +// +// ProtocolReserved in sent packets. We save a pointer to the IRP +// that generated the send. +// +// The RefCount is used to determine when to free the packet back +// to its pool. It is used to synchronize between a thread completing +// a send and a thread attempting to cancel a send. +// +typedef struct _NPROT_SEND_NETBUFLIST_RSVD +{ + PIRP pIrp; + ULONG RefCount; + +} NPROT_SEND_NETBUFLIST_RSVD, *PNPROT_SEND_NETBUFLIST_RSVD; +// +// Receive packet pool bounds +// +#define MIN_RECV_PACKET_POOL_SIZE 4 +#define MAX_RECV_PACKET_POOL_SIZE 20 + +// +// Max receive packets we allow to be queued up +// +#define MAX_RECV_QUEUE_SIZE 4 + +// +// ProtocolReserved in received packets: we link these +// packets up in a queue waiting for Read IRPs. +// +typedef struct _NPROT_RECV_NBL_RSVD +{ + LIST_ENTRY Link; + PNET_BUFFER_LIST pNetBufferList; // used if we had to partial-map + +} NPROT_RECV_NBL_RSVD, *PNPROT_RECV_NBL_RSVD; + + +#include + +typedef struct _NDISPROT_ETH_HEADER +{ + UCHAR DstAddr[NPROT_MAC_ADDR_LEN]; + UCHAR SrcAddr[NPROT_MAC_ADDR_LEN]; + USHORT EthType; + +} NDISPROT_ETH_HEADER; + +typedef struct _NDISPROT_ETH_HEADER UNALIGNED * PNDISPROT_ETH_HEADER; + +#include + + +extern NDISPROT_GLOBALS Globals; + + +#define NPROT_ALLOC_TAG 'oiuN' + + +// +// Prototypes. +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT pDriverObject, + IN PUNICODE_STRING pRegistryPath + ); + +DRIVER_UNLOAD NdisprotUnload; +VOID +NdisprotUnload( + IN PDRIVER_OBJECT pDriverObject + ); + +_Dispatch_type_(IRP_MJ_CREATE) DRIVER_DISPATCH NdisprotOpen; + +NTSTATUS +NdisprotOpen( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); + +_Dispatch_type_(IRP_MJ_CLOSE) DRIVER_DISPATCH NdisprotClose; +NTSTATUS +NdisprotClose( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); + +_Dispatch_type_(IRP_MJ_CLEANUP) DRIVER_DISPATCH NdisprotCleanup; +NTSTATUS +NdisprotCleanup( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); + +_Dispatch_type_(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH NdisprotIoControl; +_Function_class_(DRIVER_DISPATCH) +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +NTSTATUS +NdisprotIoControl( + _In_ IN PDEVICE_OBJECT pDeviceObject, + _Inout_ IN PIRP pIrp + ); + +NTSTATUS +ndisprotOpenDevice( + _In_reads_bytes_(DeviceNameLength) IN PUCHAR pDeviceName, + IN ULONG DeviceNameLength, + IN PFILE_OBJECT pFileObject, + OUT PNDISPROT_OPEN_CONTEXT * ppOpenContext + ); + +VOID +ndisprotRefOpen( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ); + +VOID +ndisprotDerefOpen( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ); + +#if DBG +VOID +ndisprotDbgRefOpen( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN ULONG FileNumber, + IN ULONG LineNumber + ); + +VOID +ndisprotDbgDerefOpen( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN ULONG FileNumber, + IN ULONG LineNumber + ); +#endif // DBG + +PROTOCOL_BIND_ADAPTER_EX NdisprotBindAdapter; + +PROTOCOL_OPEN_ADAPTER_COMPLETE_EX NdisprotOpenAdapterComplete; + +PROTOCOL_UNBIND_ADAPTER_EX NdisprotUnbindAdapter; + +PROTOCOL_CLOSE_ADAPTER_COMPLETE_EX NdisprotCloseAdapterComplete; + + +PROTOCOL_NET_PNP_EVENT NdisprotPnPEventHandler; + +VOID +NdisprotProtocolUnloadHandler( + VOID + ); + +NDIS_STATUS +ndisprotCreateBinding( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNDIS_BIND_PARAMETERS BindParameters, + IN NDIS_HANDLE BindContext, + _In_reads_bytes_(BindingInfoLength) IN PUCHAR pBindingInfo, + IN ULONG BindingInfoLength + ); + +VOID +ndisprotShutdownBinding( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ); + +VOID +ndisprotFreeBindResources( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ); + +VOID +ndisprotWaitForPendingIO( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN BOOLEAN DoCancelReads + ); + +VOID +ndisprotDoProtocolUnload( + VOID + ); + +NDIS_STATUS +ndisprotDoRequest( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN NDIS_PORT_NUMBER PortNumber, + IN NDIS_REQUEST_TYPE RequestType, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + OUT PULONG pBytesProcessed + ); + +NDIS_STATUS +ndisprotValidateOpenAndDoRequest( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN NDIS_REQUEST_TYPE RequestType, + IN NDIS_OID Oid, + IN PVOID InformationBuffer, + IN ULONG InformationBufferLength, + OUT PULONG pBytesProcessed, + IN BOOLEAN bWaitForPowerOn + ); + +PROTOCOL_OID_REQUEST_COMPLETE NdisprotRequestComplete; + +PROTOCOL_STATUS_EX NdisprotStatus; + +NDIS_STATUS +ndisprotQueryBinding( + _Inout_updates_bytes_to_(OutputLength, *pBytesReturned) + PUCHAR pBuffer, + _In_ ULONG InputLength, + _In_ ULONG OutputLength, + _Out_ PULONG pBytesReturned + ); + +PNDISPROT_OPEN_CONTEXT +ndisprotLookupDevice( + _In_reads_bytes_(BindingInfoLength) IN PUCHAR pBindingInfo, + IN ULONG BindingInfoLength + ); + +NDIS_STATUS +ndisprotQueryOidValue( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + OUT PVOID pDataBuffer, + IN ULONG BufferLength, + OUT PULONG pBytesWritten + ); + +NDIS_STATUS +ndisprotSetOidValue( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + OUT PVOID pDataBuffer, + IN ULONG BufferLength + ); + +BOOLEAN +ndisprotValidOid( + IN NDIS_OID Oid + ); + +_Dispatch_type_(IRP_MJ_READ) DRIVER_DISPATCH NdisprotRead; +NTSTATUS +NdisprotRead( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); + +DRIVER_CANCEL NdisprotCancelRead; +VOID +NdisprotCancelRead( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); + +VOID +ndisprotServiceReads( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ); + +PROTOCOL_RECEIVE_NET_BUFFER_LISTS NdisprotReceiveNetBufferLists; + +VOID +ndisprotQueueReceiveNetBufferList( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNET_BUFFER_LIST pRcvNetBufList, + BOOLEAN DispatchLevel + ); + +_Success_(return != 0) +PNET_BUFFER_LIST +ndisprotAllocateReceiveNetBufferList( + _In_ PNDISPROT_OPEN_CONTEXT pOpenContext, + _In_ UINT DataLength, + _Outptr_result_bytebuffer_(DataLength) + PUCHAR * ppDataBuffer + ); + +VOID +ndisprotFreeReceiveNetBufferList( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNET_BUFFER_LIST pNetBufferList, + IN BOOLEAN DispatchLevel + ); + +VOID +ndisprotCancelPendingReads( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ); + +VOID +ndisprotFlushReceiveQueue( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ); + +_Dispatch_type_(IRP_MJ_WRITE) DRIVER_DISPATCH NdisprotWrite; +NTSTATUS +NdisprotWrite( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); + +DRIVER_CANCEL NdisprotCancelWrite; +VOID +NdisprotCancelWrite( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); + +PROTOCOL_SEND_NET_BUFFER_LISTS_COMPLETE NdisprotSendComplete; + +VOID +ndisprotRestart( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNDIS_PROTOCOL_RESTART_PARAMETERS RestartParameters + ); + +#endif // __NDISPROT__H + + diff --git a/network/ndis/ndisprot/6x/sys/ntdisp.c b/network/ndis/ndisprot/6x/sys/ntdisp.c new file mode 100644 index 00000000..21ceeaeb --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/ntdisp.c @@ -0,0 +1,916 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + ntdisp.c + +Abstract: + + NT Entry points and dispatch routines for NDISPROT. + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#include "precomp.h" + +#define __FILENUMBER 'PSID' + + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, NdisprotUnload) +#pragma alloc_text(PAGE, NdisprotOpen) +#pragma alloc_text(PAGE, NdisprotClose) +#pragma alloc_text(PAGE, NdisprotIoControl) + +#endif // ALLOC_PRAGMA + + +// +// Globals: +// +NDISPROT_GLOBALS Globals = {0}; + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT pDriverObject, + IN PUNICODE_STRING pRegistryPath + ) +/*++ + +Routine Description: + + Called on loading. We create a device object to handle user-mode requests + on, and register ourselves as a protocol with NDIS. + +Arguments: + + pDriverObject - Pointer to driver object created by system. + + pRegistryPath - Pointer to the Unicode name of the registry path + for this driver. + +Return Value: + + NT Status code + +--*/ +{ + NDIS_PROTOCOL_DRIVER_CHARACTERISTICS protocolChar = {0}; + NTSTATUS status = STATUS_SUCCESS; + NDIS_STRING protoName = NDIS_STRING_CONST("NDISPROT"); + UNICODE_STRING ntDeviceName; + UNICODE_STRING win32DeviceName; + BOOLEAN fSymbolicLink = FALSE; + PDEVICE_OBJECT deviceObject = NULL; + NDIS_HANDLE ProtocolDriverContext={0}; + + UNREFERENCED_PARAMETER(pRegistryPath); + + DEBUGP(DL_LOUD, ("DriverEntry\n")); + + Globals.pDriverObject = pDriverObject; + Globals.EthType = NPROT_ETH_TYPE; + NPROT_INIT_EVENT(&Globals.BindsComplete); + + do + { + // + // Create our device object using which an application can + // access NDIS devices. + // + RtlInitUnicodeString(&ntDeviceName, NT_DEVICE_NAME); + + status = IoCreateDevice (pDriverObject, + 0, + &ntDeviceName, + FILE_DEVICE_NETWORK, + FILE_DEVICE_SECURE_OPEN, + FALSE, + &deviceObject); + + if (!NT_SUCCESS (status)) + { + // + // Either not enough memory to create a deviceobject or another + // deviceobject with the same name exits. This could happen + // if you install another instance of this device. + // + break; + } + + RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME); + + status = IoCreateSymbolicLink(&win32DeviceName, &ntDeviceName); + + if (!NT_SUCCESS(status)) + { + break; + } + + fSymbolicLink = TRUE; + + deviceObject->Flags |= DO_DIRECT_IO; + Globals.ControlDeviceObject = deviceObject; + + NPROT_INIT_LIST_HEAD(&Globals.OpenList); + NPROT_INIT_LOCK(&Globals.GlobalLock); + + // + // Initialize the protocol characterstic structure + // +#if (NDIS_SUPPORT_NDIS630) + {C_ASSERT(sizeof(protocolChar) >= NDIS_SIZEOF_PROTOCOL_DRIVER_CHARACTERISTICS_REVISION_2);} + protocolChar.Header.Type = NDIS_OBJECT_TYPE_PROTOCOL_DRIVER_CHARACTERISTICS, + protocolChar.Header.Size = NDIS_SIZEOF_PROTOCOL_DRIVER_CHARACTERISTICS_REVISION_2; + protocolChar.Header.Revision = NDIS_PROTOCOL_DRIVER_CHARACTERISTICS_REVISION_2; +#elif (NDIS_SUPPORT_NDIS6) + {C_ASSERT(sizeof(protocolChar) >= NDIS_SIZEOF_PROTOCOL_DRIVER_CHARACTERISTICS_REVISION_1);} + protocolChar.Header.Type = NDIS_OBJECT_TYPE_PROTOCOL_DRIVER_CHARACTERISTICS, + protocolChar.Header.Size = NDIS_SIZEOF_PROTOCOL_DRIVER_CHARACTERISTICS_REVISION_1; + protocolChar.Header.Revision = NDIS_PROTOCOL_DRIVER_CHARACTERISTICS_REVISION_1; +#endif // NDIS MINIPORT VERSION + + protocolChar.MajorNdisVersion = NDIS_PROT_MAJOR_VERSION; + protocolChar.MinorNdisVersion = NDIS_PROT_MINOR_VERSION; + protocolChar.MajorDriverVersion = MAJOR_DRIVER_VERSION; + protocolChar.MinorDriverVersion = MINOR_DRIVER_VERISON; + protocolChar.Name = protoName; + protocolChar.SetOptionsHandler = NULL; + protocolChar.OpenAdapterCompleteHandlerEx = NdisprotOpenAdapterComplete; + protocolChar.CloseAdapterCompleteHandlerEx = NdisprotCloseAdapterComplete; + protocolChar.SendNetBufferListsCompleteHandler = NdisprotSendComplete; + protocolChar.OidRequestCompleteHandler = NdisprotRequestComplete; + protocolChar.StatusHandlerEx = NdisprotStatus; + protocolChar.UninstallHandler = NULL; + protocolChar.ReceiveNetBufferListsHandler = NdisprotReceiveNetBufferLists; + protocolChar.NetPnPEventHandler = NdisprotPnPEventHandler; + protocolChar.BindAdapterHandlerEx = NdisprotBindAdapter; + protocolChar.UnbindAdapterHandlerEx = NdisprotUnbindAdapter; + + // + // Register as a protocol driver + // + + status = NdisRegisterProtocolDriver(ProtocolDriverContext, // driver context + &protocolChar, + &Globals.NdisProtocolHandle); + + if (status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_WARN, ("Failed to register protocol with NDIS\n")); + status = STATUS_UNSUCCESSFUL; + break; + } + + Globals.PartialCancelId = NdisGeneratePartialCancelId(); + Globals.PartialCancelId <<= ((sizeof(PVOID) - 1) * 8); + DEBUGP(DL_LOUD, ("DriverEntry: CancelId %lx\n", Globals.PartialCancelId)); + + // + // Now set only the dispatch points we would like to handle. + // + + pDriverObject->MajorFunction[IRP_MJ_CREATE] = NdisprotOpen; + + pDriverObject->MajorFunction[IRP_MJ_CLOSE] = NdisprotClose; + + pDriverObject->MajorFunction[IRP_MJ_READ] = NdisprotRead; + + pDriverObject->MajorFunction[IRP_MJ_WRITE] = NdisprotWrite; + + pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = NdisprotCleanup; + + pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = NdisprotIoControl; + + + pDriverObject->DriverUnload = NdisprotUnload; + + status = STATUS_SUCCESS; + + + } + while (FALSE); + + + if (!NT_SUCCESS(status)) + { + if (deviceObject) + { + KeEnterCriticalRegion(); + IoDeleteDevice(deviceObject); + KeLeaveCriticalRegion(); + Globals.ControlDeviceObject = NULL; + } + + if (fSymbolicLink) + { + IoDeleteSymbolicLink(&win32DeviceName); + fSymbolicLink = FALSE; + } + + if (Globals.NdisProtocolHandle) + { + NdisDeregisterProtocolDriver(Globals.NdisProtocolHandle); + Globals.NdisProtocolHandle = NULL; + } + } + + return status; +} + + +VOID +NdisprotUnload( + IN PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + Free all the allocated resources, etc. + +Arguments: + + DriverObject - pointer to a driver object. + +Return Value: + + VOID. + +--*/ +{ + + UNICODE_STRING win32DeviceName; + PAGED_CODE(); + UNREFERENCED_PARAMETER(DriverObject); + + DEBUGP(DL_LOUD, ("Unload Enter\n")); + + // + // First delete the Control deviceobject and the corresponding + // symbolicLink + // + RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME); + + IoDeleteSymbolicLink(&win32DeviceName); + + + if (Globals.ControlDeviceObject) + { + IoDeleteDevice(Globals.ControlDeviceObject); + Globals.ControlDeviceObject = NULL; + } + + + ndisprotDoProtocolUnload(); + +#if DBG + ndisprotAuditShutdown(); +#endif + + DEBUGP(DL_LOUD, ("Unload Exit\n")); +} + + + +NTSTATUS +NdisprotOpen( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This is the dispatch routine for handling IRP_MJ_CREATE. + We simply succeed this. + +Arguments: + + pDeviceObject - Pointer to the device object. + + pIrp - Pointer to the request packet. + +Return Value: + + Status is returned. + +--*/ +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS NtStatus = STATUS_SUCCESS; + PAGED_CODE(); + UNREFERENCED_PARAMETER(pDeviceObject); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pIrpSp->FileObject->FsContext = NULL; + + DEBUGP(DL_INFO, ("Open: FileObject %p\n", pIrpSp->FileObject)); + + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = NtStatus; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return NtStatus; +} + +NTSTATUS +NdisprotClose( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This is the dispatch routine for handling IRP_MJ_CLOSE. + We simply succeed this. + +Arguments: + + pDeviceObject - Pointer to the device object. + + pIrp - Pointer to the request packet. + +Return Value: + + Status is returned. + +--*/ +{ + NTSTATUS NtStatus; + PIO_STACK_LOCATION pIrpSp; + PNDISPROT_OPEN_CONTEXT pOpenContext; + PAGED_CODE(); + UNREFERENCED_PARAMETER(pDeviceObject); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pOpenContext = pIrpSp->FileObject->FsContext; + + DEBUGP(DL_INFO, ("Close: FileObject %p\n", + IoGetCurrentIrpStackLocation(pIrp)->FileObject)); + + if (pOpenContext != NULL) + { + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + // + // Deref the endpoint + // + NPROT_DEREF_OPEN(pOpenContext); // Close + } + + pIrpSp->FileObject->FsContext = NULL; + NtStatus = STATUS_SUCCESS; + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = NtStatus; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + return NtStatus; +} + + + +NTSTATUS +NdisprotCleanup( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This is the dispatch routine for handling IRP_MJ_CLEANUP. + +Arguments: + + pDeviceObject - Pointer to the device object. + + pIrp - Pointer to the request packet. + +Return Value: + + Status is returned. + +--*/ +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS NtStatus; + NDIS_STATUS NdisStatus; + PNDISPROT_OPEN_CONTEXT pOpenContext; + ULONG PacketFilter; + ULONG BytesProcessed; + + + UNREFERENCED_PARAMETER(pDeviceObject); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pOpenContext = pIrpSp->FileObject->FsContext; + + DEBUGP(DL_VERY_LOUD, ("Cleanup: FileObject %p, Open %p\n", + pIrpSp->FileObject, pOpenContext)); + + if (pOpenContext != NULL) + { + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + // + // Set the packet filter to 0, telling NDIS that we aren't + // interested in any more receives. + // + PacketFilter = 0; + NdisStatus = ndisprotValidateOpenAndDoRequest( + pOpenContext, + NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, + &PacketFilter, + sizeof(PacketFilter), + &BytesProcessed, + FALSE // Don't wait for device to be powered on + ); + + if (NdisStatus != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_INFO, ("Cleanup: Open %p, set packet filter (%x) failed: %x\n", + pOpenContext, PacketFilter, NdisStatus)); + // + // Ignore the result. If this failed, we may continue + // to get indicated receives, which will be handled + // appropriately. + // + NdisStatus = NDIS_STATUS_SUCCESS; + } + + // + // Mark this endpoint. + // + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_OPEN_FLAGS, NPROTO_OPEN_IDLE); + pOpenContext->pFileObject = NULL; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Cancel any pending reads. + // + ndisprotCancelPendingReads(pOpenContext); + // + // Clean up the receive packet queue + // + ndisprotFlushReceiveQueue(pOpenContext); + } + + NtStatus = STATUS_SUCCESS; + + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = NtStatus; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + DEBUGP(DL_INFO, ("Cleanup: OpenContext %p\n", pOpenContext)); + + return (NtStatus); +} + +NTSTATUS +NdisprotIoControl( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + This is the dispatch routine for handling device ioctl requests. + +Arguments: + + pDeviceObject - Pointer to the device object. + + pIrp - Pointer to the request packet. + +Return Value: + + Status is returned. + +--*/ +{ + PIO_STACK_LOCATION pIrpSp; + ULONG FunctionCode; + NTSTATUS NtStatus; + NDIS_STATUS Status; + PNDISPROT_OPEN_CONTEXT pOpenContext; + ULONG BytesReturned; +#if !DBG + UNREFERENCED_PARAMETER(pDeviceObject); +#endif + PAGED_CODE(); + DEBUGP(DL_LOUD, ("IoControl: DevObj %p, Irp %p\n", pDeviceObject, pIrp)); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + + FunctionCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode; + pOpenContext = (PNDISPROT_OPEN_CONTEXT)pIrpSp->FileObject->FsContext; + BytesReturned = 0; + + switch (FunctionCode) + { + case IOCTL_NDISPROT_BIND_WAIT: + // + // Block until we have seen a NetEventBindsComplete event, + // meaning that we have finished binding to all running + // adapters that we are supposed to bind to. + // + // If we don't get this event in 5 seconds, time out. + // + NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); + + if (NPROT_WAIT_EVENT(&Globals.BindsComplete, 5000)) + { + NtStatus = STATUS_SUCCESS; + } + else + { + NtStatus = STATUS_TIMEOUT; + } + DEBUGP(DL_INFO, ("IoControl: BindWait returning %x\n", NtStatus)); + break; + + case IOCTL_NDISPROT_QUERY_BINDING: + + NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); + + Status = ndisprotQueryBinding( + pIrp->AssociatedIrp.SystemBuffer, + pIrpSp->Parameters.DeviceIoControl.InputBufferLength, + pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, + &BytesReturned + ); + + NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); + + DEBUGP(DL_LOUD, ("IoControl: QueryBinding returning %x\n", NtStatus)); + + break; + + case IOCTL_NDISPROT_OPEN_DEVICE: + + NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); + if (pOpenContext != NULL) + { + NPROT_STRUCT_ASSERT(pOpenContext, oc); + DEBUGP(DL_WARN, ("IoControl: OPEN_DEVICE: FileObj %p already" + " associated with open %p\n", pIrpSp->FileObject, pOpenContext)); + + NtStatus = STATUS_DEVICE_BUSY; + break; + } + + NtStatus = ndisprotOpenDevice( + pIrp->AssociatedIrp.SystemBuffer, + pIrpSp->Parameters.DeviceIoControl.InputBufferLength, + pIrpSp->FileObject, + &pOpenContext + ); + + if (NT_SUCCESS(NtStatus)) + { + + DEBUGP(DL_VERY_LOUD, ("IoControl OPEN_DEVICE: Open %p <-> FileObject %p\n", + pOpenContext, pIrpSp->FileObject)); + + } + else + { + DEBUGP(DL_INFO, ("NdisprotIoControl: IOCTL_NDISPROT_OPEN_DEVICE, ndisprotOpenDevice failed %lx\n", NtStatus)); + } + + break; + + case IOCTL_NDISPROT_QUERY_OID_VALUE: + + NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); + if (pOpenContext != NULL) + { + Status = ndisprotQueryOidValue( + pOpenContext, + pIrp->AssociatedIrp.SystemBuffer, + pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, + &BytesReturned + ); + + NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); + } + else + { + NtStatus = STATUS_DEVICE_NOT_CONNECTED; + } + break; + + case IOCTL_NDISPROT_SET_OID_VALUE: + + NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); + if (pOpenContext != NULL) + { + Status = ndisprotSetOidValue( + pOpenContext, + pIrp->AssociatedIrp.SystemBuffer, + pIrpSp->Parameters.DeviceIoControl.InputBufferLength + ); + + BytesReturned = 0; + + NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); + } + else + { + NtStatus = STATUS_DEVICE_NOT_CONNECTED; + } + break; + + default: + + NtStatus = STATUS_NOT_SUPPORTED; + break; + } + + if (NtStatus != STATUS_PENDING) + { + pIrp->IoStatus.Information = BytesReturned; + pIrp->IoStatus.Status = NtStatus; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + return NtStatus; +} + + + +NTSTATUS +ndisprotOpenDevice( + _In_reads_bytes_(DeviceNameLength) IN PUCHAR pDeviceName, + IN ULONG DeviceNameLength, + IN PFILE_OBJECT pFileObject, + OUT PNDISPROT_OPEN_CONTEXT * ppOpenContext + ) +/*++ + +Routine Description: + + Helper routine called to process IOCTL_NDISPROT_OPEN_DEVICE. Check if + there is a binding to the specified device, and is not associated with + a file object already. If so, make an association between the binding + and this file object. + +Arguments: + + pDeviceName - pointer to device name string + DeviceNameLength - length of above + pFileObject - pointer to file object being associated with the device binding + +Return Value: + + Status is returned. +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + NTSTATUS NtStatus; + ULONG PacketFilter; + NDIS_STATUS NdisStatus; + ULONG BytesProcessed; + PNDISPROT_OPEN_CONTEXT pCurrentOpenContext = NULL; + + pOpenContext = NULL; + + do + { + pOpenContext = ndisprotLookupDevice( + pDeviceName, + DeviceNameLength + ); + + if (pOpenContext == NULL) + { + DEBUGP(DL_WARN, ("ndisprotOpenDevice: couldn't find device\n")); + NtStatus = STATUS_OBJECT_NAME_NOT_FOUND; + break; + } + + // + // else ndisprotLookupDevice would have addref'ed the open. + // + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_OPEN_FLAGS, NPROTO_OPEN_IDLE)) + { + NPROT_ASSERT(pOpenContext->pFileObject != NULL); + + DEBUGP(DL_WARN, ("ndisprotOpenDevice: Open %p/%x already associated" + " with another FileObject %p\n", + pOpenContext, pOpenContext->Flags, pOpenContext->pFileObject)); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_DEREF_OPEN(pOpenContext); // ndisprotOpenDevice failure + NtStatus = STATUS_DEVICE_BUSY; + break; + } + // + // This InterlockedXXX function performs an atomic operation: First it compare + // pFileObject->FsContext with NULL, if they are equal, the function puts pOpenContext + // into FsContext, and return NULL. Otherwise, it return pFileObject->FsContext without + // changing anything. + // + + if ((pCurrentOpenContext = InterlockedCompareExchangePointer (& (pFileObject->FsContext), pOpenContext, NULL)) != NULL) + { + // + // pFileObject->FsContext already is used by other open + // + DEBUGP(DL_WARN, ("ndisprotOpenDevice: FileObject %p already associated" + " with another Open %p/%x\n", + pFileObject, pCurrentOpenContext, pCurrentOpenContext->Flags)); //BUG + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_DEREF_OPEN(pOpenContext); // ndisprotOpenDevice failure + NtStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + pOpenContext->pFileObject = pFileObject; + + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_OPEN_FLAGS, NPROTO_OPEN_ACTIVE); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Set the packet filter now. + // + PacketFilter = NPROTO_PACKET_FILTER; + NdisStatus = ndisprotValidateOpenAndDoRequest( + pOpenContext, + NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, + &PacketFilter, + sizeof(PacketFilter), + &BytesProcessed, + TRUE // Do wait for power on + ); + + if (NdisStatus != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_WARN, ("openDevice: Open %p: set packet filter (%x) failed: %x\n", + pOpenContext, PacketFilter, NdisStatus)); + + // + // Undo all that we did above. + // + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + // + // Need to set pFileObject->FsContext to NULL again, so others can open a device + // for this file object later + // + pCurrentOpenContext = InterlockedCompareExchangePointer (& (pFileObject->FsContext), NULL, pOpenContext); + + + NPROT_ASSERT(pCurrentOpenContext == pOpenContext); + + NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_OPEN_FLAGS, NPROTO_OPEN_IDLE); + pOpenContext->pFileObject = NULL; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_DEREF_OPEN(pOpenContext); // ndisprotOpenDevice failure + + NDIS_STATUS_TO_NT_STATUS(NdisStatus, &NtStatus); + break; + } + + *ppOpenContext = pOpenContext; + + NtStatus = STATUS_SUCCESS; + } + while (FALSE); + + return (NtStatus); +} + + +VOID +ndisprotRefOpen( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ) +/*++ + +Routine Description: + + Reference the given open context. + + NOTE: Can be called with or without holding the opencontext lock. + +Arguments: + + pOpenContext - pointer to open context + +Return Value: + + None + +--*/ +{ + NdisInterlockedIncrement((PLONG)&pOpenContext->RefCount); +} + + +VOID +ndisprotDerefOpen( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ) +/*++ + +Routine Description: + + Dereference the given open context. If the ref count goes to zero, + free it. + + NOTE: called without holding the opencontext lock + +Arguments: + + pOpenContext - pointer to open context + +Return Value: + + None + +--*/ +{ + if (NdisInterlockedDecrement((PLONG)&pOpenContext->RefCount) == 0) + { + DEBUGP(DL_INFO, ("DerefOpen: Open %p, Flags %x, ref count is zero!\n", + pOpenContext, pOpenContext->Flags)); + + NPROT_ASSERT(pOpenContext->BindingHandle == NULL); + NPROT_ASSERT(pOpenContext->RefCount == 0); + NPROT_ASSERT(pOpenContext->pFileObject == NULL); + + pOpenContext->oc_sig++; + + // + // Free it. + // + NPROT_FREE_MEM(pOpenContext); + } +} + +#if DBG +VOID +ndisprotDbgRefOpen( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN ULONG FileNumber, + IN ULONG LineNumber + ) +{ + DEBUGP(DL_VERY_LOUD, (" RefOpen: Open %p, old ref %d, File %c%c%c%c, line %d\n", + pOpenContext, + pOpenContext->RefCount, + (CHAR)(FileNumber), + (CHAR)(FileNumber >> 8), + (CHAR)(FileNumber >> 16), + (CHAR)(FileNumber >> 24), + LineNumber)); + + ndisprotRefOpen(pOpenContext); +} + +VOID +ndisprotDbgDerefOpen( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN ULONG FileNumber, + IN ULONG LineNumber + ) +{ + DEBUGP(DL_VERY_LOUD, ("DerefOpen: Open %p, old ref %d, File %c%c%c%c, line %d\n", + pOpenContext, + pOpenContext->RefCount, + (CHAR)(FileNumber), + (CHAR)(FileNumber >> 8), + (CHAR)(FileNumber >> 16), + (CHAR)(FileNumber >> 24), + LineNumber)); + + ndisprotDerefOpen(pOpenContext); +} + +#endif // DBG + diff --git a/network/ndis/ndisprot/6x/sys/precomp.h b/network/ndis/ndisprot/6x/sys/precomp.h new file mode 100644 index 00000000..1e9c0b78 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/precomp.h @@ -0,0 +1,18 @@ +#pragma warning(disable:4214) // bit field types other than int + +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4115) // named type definition in parentheses +#pragma warning(disable:4127) // conditional expression is constant +#pragma warning(disable:4054) // cast of function pointer to PVOID +#pragma warning(disable:4244) // conversion from 'int' to 'BOOLEAN', possible loss of data +#pragma warning(disable:4206) // nonstandard extension used : translation unit is empty + +#include "ndis.h" +#include "ntddk.h" +#include +#include +#include +#include "debug.h" +#include "ndisprot.h" +#include "macros.h" +#include "protuser.h" diff --git a/network/ndis/ndisprot/6x/sys/protuser.h b/network/ndis/ndisprot/6x/sys/protuser.h new file mode 100644 index 00000000..823d1ef5 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/protuser.h @@ -0,0 +1,92 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + nuiouser.h + +Abstract: + + Constants and types to access the NDISPROT driver. + Users must also include ntddndis.h + +Environment: + + User/Kernel mode. + +Revision History: + +--*/ + +#ifndef __NPROTUSER__H +#define __NPROTUSER__H + + +#define FSCTL_NDISPROT_BASE FILE_DEVICE_NETWORK + +#define _NDISPROT_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(FSCTL_NDISPROT_BASE, _Function, _Method, _Access) + +#define IOCTL_NDISPROT_OPEN_DEVICE \ + _NDISPROT_CTL_CODE(0x200, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISPROT_QUERY_OID_VALUE \ + _NDISPROT_CTL_CODE(0x201, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISPROT_SET_OID_VALUE \ + _NDISPROT_CTL_CODE(0x205, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISPROT_QUERY_BINDING \ + _NDISPROT_CTL_CODE(0x203, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISPROT_BIND_WAIT \ + _NDISPROT_CTL_CODE(0x204, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + + + + +// +// Structure to go with IOCTL_NDISPROT_QUERY_OID_VALUE. +// The Data part is of variable length, determined by +// the input buffer length passed to DeviceIoControl. +// +typedef struct _NDISPROT_QUERY_OID +{ + NDIS_OID Oid; + NDIS_PORT_NUMBER PortNumber; + UCHAR Data[sizeof(ULONG)]; +} NDISPROT_QUERY_OID, *PNDISPROT_QUERY_OID; + +// +// Structure to go with IOCTL_NDISPROT_SET_OID_VALUE. +// The Data part is of variable length, determined +// by the input buffer length passed to DeviceIoControl. +// +typedef struct _NDISPROT_SET_OID +{ + NDIS_OID Oid; + NDIS_PORT_NUMBER PortNumber; + UCHAR Data[sizeof(ULONG)]; +} NDISPROT_SET_OID, *PNDISPROT_SET_OID; + + +// +// Structure to go with IOCTL_NDISPROT_QUERY_BINDING. +// The input parameter is BindingIndex, which is the +// index into the list of bindings active at the driver. +// On successful completion, we get back a device name +// and a device descriptor (friendly name). +// +typedef struct _NDISPROT_QUERY_BINDING +{ + ULONG BindingIndex; // 0-based binding number + ULONG DeviceNameOffset; // from start of this struct + ULONG DeviceNameLength; // in bytes + ULONG DeviceDescrOffset; // from start of this struct + ULONG DeviceDescrLength; // in bytes + +} NDISPROT_QUERY_BINDING, *PNDISPROT_QUERY_BINDING; + +#endif // __NPROTUSER__H + diff --git a/network/ndis/ndisprot/6x/sys/recv.c b/network/ndis/ndisprot/6x/sys/recv.c new file mode 100644 index 00000000..71c867f1 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/recv.c @@ -0,0 +1,1119 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + recv.c + +Abstract: + + NDIS protocol entry points and utility routines to handle receiving + data. + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#include "precomp.h" + +#define __FILENUMBER 'VCER' + + + +NTSTATUS +NdisprotRead( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + Dispatch routine to handle IRP_MJ_READ. + +Arguments: + + pDeviceObject - pointer to our device object + pIrp - Pointer to request packet + +Return Value: + + NT status code. + +--*/ +{ + PIO_STACK_LOCATION pIrpSp; + NTSTATUS NtStatus; + PNDISPROT_OPEN_CONTEXT pOpenContext; + + UNREFERENCED_PARAMETER(pDeviceObject); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pOpenContext = pIrpSp->FileObject->FsContext; + + do + { + // + // Validate! + // + if (pOpenContext == NULL) + { + DEBUGP(DL_FATAL, ("Read: NULL FsContext on FileObject %p\n", + pIrpSp->FileObject)); + NtStatus = STATUS_INVALID_HANDLE; + break; + } + + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + if (pIrp->MdlAddress == NULL) + { + DEBUGP(DL_FATAL, ("Read: NULL MDL address on IRP %p\n", pIrp)); + NtStatus = STATUS_INVALID_PARAMETER; + break; + } + + // + // Try to get a virtual address for the MDL. + // + if (MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority) == NULL) + { + DEBUGP(DL_FATAL, ("Read: MmGetSystemAddr failed for IRP %p, MDL %p\n", + pIrp, pIrp->MdlAddress)); + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + NtStatus = STATUS_INVALID_HANDLE; + break; + } + + IoSetCancelRoutine(pIrp, NdisprotCancelRead); + + if (pIrp->Cancel && + IoSetCancelRoutine(pIrp, NULL)) + { + + // + // IRP has been canceled but the I/O manager did not manage to call our cancel routine. This + // code is safe referencing the Irp->Cancel field without locks because of the memory barriers + // in the interlocked exchange sequences used by IoSetCancelRoutine. + // + + NtStatus = STATUS_CANCELLED; + + // IRP should be completed after releasing the lock + + } + else + { + + // + // Add this IRP to the list of pended Read IRPs + // + NPROT_INSERT_TAIL_LIST(&pOpenContext->PendedReads, &pIrp->Tail.Overlay.ListEntry); + pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pOpenContext; + + NPROT_REF_OPEN(pOpenContext); // pended read IRP + pOpenContext->PendedReadCount++; + IoMarkIrpPending(pIrp); + + + NtStatus = STATUS_PENDING; + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + // + // Run the service routine for reads. + // + ndisprotServiceReads(pOpenContext); + + } + while (FALSE); + + if (NtStatus != STATUS_PENDING) + { + NPROT_ASSERT(NtStatus != STATUS_SUCCESS); + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = NtStatus; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + return (NtStatus); +} + + +VOID +NdisprotCancelRead( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + Cancel a pending read IRP. We unlink the IRP from the open context + queue and complete it. + +Arguments: + + pDeviceObject - pointer to our device object + pIrp - IRP to be cancelled + +Return Value: + + None + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + + UNREFERENCED_PARAMETER(pDeviceObject); + + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + pOpenContext = (PNDISPROT_OPEN_CONTEXT) pIrp->Tail.Overlay.DriverContext[0]; + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry); + pOpenContext->PendedReadCount--; + + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + DEBUGP(DL_INFO, ("CancelRead: Open %p, IRP %p\n", pOpenContext, pIrp)); + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + NPROT_DEREF_OPEN(pOpenContext); // Cancel removed pended Read + +} + + + +VOID +ndisprotServiceReads( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ) +/*++ + +Routine Description: + + Utility routine to copy received data into user buffers and + complete READ IRPs. + +Arguments: + + pOpenContext - pointer to open context + +Return Value: + + None + +--*/ +{ + PIRP pIrp = NULL; + PLIST_ENTRY pIrpEntry; + PNET_BUFFER_LIST pRcvNetBufList; + PLIST_ENTRY pRcvNetBufListEntry; + PUCHAR pSrc, pDst; + ULONG BytesRemaining; // at pDst + PMDL pMdl; + ULONG BytesAvailable; + BOOLEAN FoundPendingIrp = FALSE; + ULONG SrcTotalLength = 0; // Source NetBuffer DataLenght + ULONG Offset = 0; // CurrentMdlOffset + ULONG BytesToCopy = 0; + + DEBUGP(DL_VERY_LOUD, ("ServiceReads: open %p/%x\n", + pOpenContext, pOpenContext->Flags)); + + NPROT_REF_OPEN(pOpenContext); // temp ref - service reads + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + while (!NPROT_IS_LIST_EMPTY(&pOpenContext->PendedReads) && + !NPROT_IS_LIST_EMPTY(&pOpenContext->RecvNetBufListQueue)) + { + FoundPendingIrp = FALSE; + + // + // Get the first pended Read IRP + // + pIrpEntry = pOpenContext->PendedReads.Flink; + while (pIrpEntry != &pOpenContext->PendedReads) + { + pIrp = CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry); + + // + // Check to see if it is being cancelled. + // + if (IoSetCancelRoutine(pIrp, NULL)) + { + // + // It isn't being cancelled, and can't be cancelled henceforth. + // + NPROT_REMOVE_ENTRY_LIST(pIrpEntry); + FoundPendingIrp = TRUE; + break; + + // + // NOTE: we decrement PendedReadCount way below in the + // while loop, to avoid letting through a thread trying + // to unbind. + // + } + else + { + // + // The IRP is being cancelled; let the cancel routine handle it. + // + DEBUGP(DL_INFO, ("ServiceReads: open %p, skipping cancelled IRP %p\n", + pOpenContext, pIrp)); + + pIrpEntry = pIrpEntry->Flink; + } + } + + if (FoundPendingIrp == FALSE) + { + break; + } + // + // Get the first queued receive packet + // + pRcvNetBufListEntry = pOpenContext->RecvNetBufListQueue.Flink; + NPROT_REMOVE_ENTRY_LIST(pRcvNetBufListEntry); + + pOpenContext->RecvNetBufListCount --; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_DEREF_OPEN(pOpenContext); // Service: dequeue rcv packet + + pRcvNetBufList = NPROT_RCV_NBL_FROM_LIST_ENTRY(pRcvNetBufListEntry); + NPROT_ASSERT(pRcvNetBufList != NULL); + _Analysis_assume_(pRcvNetBufList != NULL); + NPROT_RCV_NBL_FROM_LIST_ENTRY(pRcvNetBufListEntry) = NULL; + + // + // Copy as much data as possible from the receive packet to + // the IRP MDL. + // + + pDst = NULL; + NdisQueryMdl(pIrp->MdlAddress, &pDst, &BytesRemaining, NormalPagePriority); + NPROT_ASSERT(pDst != NULL); // since it was already mapped + _Analysis_assume_(pDst != NULL); + + pMdl = NET_BUFFER_CURRENT_MDL(NET_BUFFER_LIST_FIRST_NB(pRcvNetBufList)); + + // + // Copy the data in the received packet into the buffer provided by the client. + // If the length of the receive packet is greater than length of the given buffer, + // we just copy as many bytes as we can. Once the buffer is full, we just discard + // the rest of the data, and complete the IRP sucessfully even we only did a partial copy. + // + + SrcTotalLength = NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(pRcvNetBufList)); + Offset = NET_BUFFER_CURRENT_MDL_OFFSET(NET_BUFFER_LIST_FIRST_NB(pRcvNetBufList)); + + while (BytesRemaining && (pMdl != NULL) && SrcTotalLength) + { + pSrc = NULL; + NdisQueryMdl(pMdl, &pSrc, &BytesAvailable, NormalPagePriority); + + if (pSrc == NULL) + { + DEBUGP(DL_FATAL, + ("ServiceReads: Open %p, NdisQueryMdl failed for MDL %p\n", + pOpenContext, pMdl)); + break; + } + + NPROT_ASSERT(BytesAvailable > Offset); + + BytesToCopy = MIN(BytesAvailable - Offset, BytesRemaining); + BytesToCopy = MIN(BytesToCopy, SrcTotalLength); + + NPROT_COPY_MEM(pDst, pSrc + Offset, BytesToCopy); + BytesRemaining -= BytesToCopy; + pDst += BytesToCopy; + SrcTotalLength -= BytesToCopy; + + // + // CurrentMdlOffset is used only for the first Mdl processed. For the remaining Mdls, it is 0. + // + Offset = 0; + + NdisGetNextMdl(pMdl, &pMdl); + } + + // + // Complete the IRP. + // + pIrp->IoStatus.Status = STATUS_SUCCESS; + pIrp->IoStatus.Information = MmGetMdlByteCount(pIrp->MdlAddress) - BytesRemaining; + + DEBUGP(DL_INFO, ("ServiceReads: Open %p, IRP %p completed with %d bytes\n", + pOpenContext, pIrp, (ULONG)pIrp->IoStatus.Information)); + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + ndisprotFreeReceiveNetBufferList(pOpenContext, pRcvNetBufList,FALSE); + + + NPROT_DEREF_OPEN(pOpenContext); // took out pended Read + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + pOpenContext->PendedReadCount--; + + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_DEREF_OPEN(pOpenContext); // temp ref - service reads +} + + +VOID +NdisprotReceiveNetBufferLists( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNET_BUFFER_LIST pNetBufferLists, + IN NDIS_PORT_NUMBER PortNumber, + IN ULONG NumberOfNetBufferLists, + IN ULONG ReceiveFlags + ) +/*++ + +Routine Description: + + Protocol entry point called by NDIS if the driver below + uses NDIS 6 net buffer list indications. + + If the miniport allows us to hold on to this net buffer list, we + use it as is, otherwise we make a copy. + +Arguments: + + ProtocolBindingContext - pointer to open context + pNetBufferLists - a list of the Net Buffer lists being indicated up. + PortNumber - Port on which the Net Bufer list was received + NumberOfNetBufferLists - the number of NetBufferLists in this indication + ReceiveFlags - indicates whether the NetBufferLists can be pended in + the protocol driver. + +Return Value: + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + PMDL pMdl = NULL; + UINT BufferLength; + PNDISPROT_ETH_HEADER pEthHeader = NULL; + PNET_BUFFER_LIST pCopyNetBufList; + PUCHAR pCopyBuf; + ULONG TotalLength; + ULONG BytesCopied; + PNET_BUFFER_LIST pNetBufList; + PNET_BUFFER_LIST pNetBufListOrig = NULL; + PNET_BUFFER_LIST pNextNetBufList; + PNET_BUFFER_LIST pReturnNetBufList = NULL; + PNET_BUFFER_LIST pLastReturnNetBufList = NULL; + NTSTATUS NtStatus; + BOOLEAN bAcceptedReceive; + ULONG Offset; + ULONG ReturnFlags = 0; + BOOLEAN DispatchLevel; + BOOLEAN NoReadIRP = FALSE; + + UNREFERENCED_PARAMETER(PortNumber); + UNREFERENCED_PARAMETER(NumberOfNetBufferLists); + + pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; + + if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags)) + { + NDIS_SET_RETURN_FLAG(ReturnFlags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL); + } + + NPROT_STRUCT_ASSERT(pOpenContext, oc); + if ((pOpenContext->State == NdisprotPausing) + || (pOpenContext->State == NdisprotPaused)) + { + if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags) == TRUE) + { + + NdisReturnNetBufferLists(pOpenContext->BindingHandle, + pNetBufferLists, + ReturnFlags); + } + return; + } + + pNetBufList = pNetBufferLists; + + while (pNetBufList != NULL) + { + pNextNetBufList = NET_BUFFER_LIST_NEXT_NBL (pNetBufList); + + NBL_CLEAR_PROT_RSVD_FLAG(pNetBufList, NBL_PROT_RSVD_FLAGS); + bAcceptedReceive = FALSE; + + // + // Get first MDL and data length in the list + // + pMdl = NET_BUFFER_CURRENT_MDL(NET_BUFFER_LIST_FIRST_NB(pNetBufList)); + TotalLength = NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(pNetBufList)); + Offset = NET_BUFFER_CURRENT_MDL_OFFSET(NET_BUFFER_LIST_FIRST_NB(pNetBufList)); + BufferLength = 0; + + do + { + ASSERT(pMdl != NULL); + if (pMdl) + { + NdisQueryMdl( + pMdl, + &pEthHeader, + &BufferLength, + NormalPagePriority); + } + + if (pEthHeader == NULL) + { + // + // The system is low on resources. Set up to handle failure + // below. + // + BufferLength = 0; + break; + } + + if (BufferLength == 0) + { + break; + } + + ASSERT(BufferLength > Offset); + + BufferLength -= Offset; + pEthHeader = (PNDISPROT_ETH_HEADER)((PUCHAR)pEthHeader + Offset); + + if (BufferLength < sizeof(NDISPROT_ETH_HEADER)) + { + DEBUGP(DL_WARN, + ("ReceiveNetBufferList: Open %p, runt nbl %p, first buffer length %d\n", + pOpenContext, pNetBufList, BufferLength)); + + break; + } + + // + // Check the EtherType. If the Ether type indicates presence of + // a tag, then the "real" Ether type is 4 bytes further down. + // + if (pEthHeader->EthType == NPROT_8021P_TAG_TYPE) + { + USHORT UNALIGNED *pEthType; + + if (BufferLength < (sizeof(NDISPROT_ETH_HEADER) + 4)) + { + break; + } + + pEthType = (USHORT UNALIGNED *)((PUCHAR)&pEthHeader->EthType + 4); + + if (*pEthType != Globals.EthType) + { + break; + } + } + else if (pEthHeader->EthType != Globals.EthType) + { + break; + } + + bAcceptedReceive = TRUE; + DEBUGP(DL_LOUD, ("ReceiveNetBufferList: Open %p, interesting nbl %p\n", + pOpenContext, pNetBufList)); + + // + // If the miniport is out of resources, we can't queue + // this list of net buffer list - make a copy if this is so. + // + DispatchLevel = NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags); + + NoReadIRP = NPROT_IS_LIST_EMPTY(&pOpenContext->PendedReads); + + if (NoReadIRP || NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags)) + { + bAcceptedReceive = FALSE; + pCopyNetBufList = ndisprotAllocateReceiveNetBufferList( + pOpenContext, + TotalLength, + &pCopyBuf); + + if (pCopyNetBufList == NULL) + { + DEBUGP(DL_FATAL, ("ReceiveNetBufferList: Open %p, failed to" + " alloc copy, %d bytes\n", pOpenContext, TotalLength)); + break; + } + NBL_SET_PROT_RSVD_FLAG(pCopyNetBufList, NPROT_ALLOCATED_NBL); + // + // Copy the data to the new allocated NetBufferList + // + NtStatus = NdisCopyFromNetBufferToNetBuffer(NET_BUFFER_LIST_FIRST_NB(pCopyNetBufList), + 0, + TotalLength, + NET_BUFFER_LIST_FIRST_NB(pNetBufList), + 0, + &BytesCopied); + + + if (NtStatus != STATUS_SUCCESS) + { + DEBUGP(DL_FATAL, ("ReceiveNetBufferList: Open %p, failed to" + " copy the data, %d bytes\n", pOpenContext, TotalLength)); + // + // Free the NetBufferList and memory allocate before + // + ndisprotFreeReceiveNetBufferList(pOpenContext, + pCopyNetBufList, + DispatchLevel); + break; + } + + NPROT_ASSERT(BytesCopied == TotalLength); + + + // + // The other members of NET_BUFFER_DATA structure are already initialized properly during allocation. + // + NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(pCopyNetBufList)) = BytesCopied; + + // + //save a copy for no Read IRP case + // + if(NoReadIRP) + { + pNetBufListOrig = pNetBufList; + } + pNetBufList = pCopyNetBufList; + + } + // + // Queue this up and service any pending Read IRPs. + // + ndisprotQueueReceiveNetBufferList(pOpenContext, pNetBufList, DispatchLevel); + + } + while (FALSE); + + // + // Ndisprot is not interested this NetBufferList, return the + // NetBufferList back to the miniport if the miniport gave us + // ownership of it + // + if ((bAcceptedReceive == FALSE) && + (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags) == TRUE)) + { + // Restore pNetBufList if it was overwritten earlier + if (pNetBufListOrig != NULL) + { + pNetBufList = pNetBufListOrig; + + pNetBufListOrig = NULL; + } + if (pReturnNetBufList == NULL) + { + pReturnNetBufList = pNetBufList; + } + else + { + NET_BUFFER_LIST_NEXT_NBL(pLastReturnNetBufList) = pNetBufList; + } + pLastReturnNetBufList = pNetBufList; + NET_BUFFER_LIST_NEXT_NBL(pNetBufList) = NULL; + + } + + pNetBufList = pNextNetBufList; + } // end of the for loop + + if (pReturnNetBufList != NULL) + { + NdisReturnNetBufferLists(pOpenContext->BindingHandle, + pReturnNetBufList, + ReturnFlags); + } + +} + + +VOID +ndisprotQueueReceiveNetBufferList( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNET_BUFFER_LIST pRcvNetBufList, + IN BOOLEAN DispatchLevel + ) +/*++ + +Routine Description: + + Queue up a received net buffer list on the open context structure. + If the queue size goes beyond a water mark, discard a Net Buffer list + at the head of the queue. + + Finally, run the queue service routine. + +Arguments: + + pOpenContext - pointer to open context + pRcvPacket - the received packet + DipatchLevel - the irql level + +Return Value: + + None + +--*/ +{ + PLIST_ENTRY pEnt; + PLIST_ENTRY pDiscardEnt; + PNET_BUFFER_LIST pDiscardNetBufList; + + do + { + + NPROT_REF_OPEN(pOpenContext); // queued rcv net buffer list + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, DispatchLevel); + + if ((pOpenContext->State == NdisprotPaused) + || (pOpenContext->State == NdisprotPausing)) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); + + ndisprotFreeReceiveNetBufferList(pOpenContext, pRcvNetBufList, DispatchLevel); + break; + } + + // + // Check if the binding is in the proper state to receive + // this net buffer list. + // + if (NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE) && + (pOpenContext->PowerState == NetDeviceStateD0)) + { + + // + // Queue the net buffer list + // + pEnt = NPROT_RCV_NBL_TO_LIST_ENTRY(pRcvNetBufList); + NPROT_INSERT_TAIL_LIST(&pOpenContext->RecvNetBufListQueue, pEnt); + NPROT_RCV_NBL_FROM_LIST_ENTRY(pEnt) = pRcvNetBufList; + pOpenContext->RecvNetBufListCount++; + + DEBUGP(DL_VERY_LOUD, ("QueueReceiveNetBufferList: open %p," + " queued nbl %p, queue size %d\n", + pOpenContext, pRcvNetBufList, pOpenContext->RecvNetBufListCount)); + + } + else + { + // + // Received this net buffer list when the binding is going away. + // Drop this. + // + NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); + + ndisprotFreeReceiveNetBufferList(pOpenContext, pRcvNetBufList, DispatchLevel); + + NPROT_DEREF_OPEN(pOpenContext); // dropped rcv packet - bad state + break; + } + + + // + // Trim the queue if it has grown too big. + // + if (pOpenContext->RecvNetBufListCount > MAX_RECV_QUEUE_SIZE) + { + // + // Remove the head of the queue. + // + pDiscardEnt = pOpenContext->RecvNetBufListQueue.Flink; + NPROT_REMOVE_ENTRY_LIST(pDiscardEnt); + + pOpenContext->RecvNetBufListCount --; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); + + pDiscardNetBufList = NPROT_RCV_NBL_FROM_LIST_ENTRY(pDiscardEnt); + + NPROT_RCV_NBL_FROM_LIST_ENTRY(pDiscardEnt) = NULL; + + ndisprotFreeReceiveNetBufferList(pOpenContext, pDiscardNetBufList, DispatchLevel); + + NPROT_DEREF_OPEN(pOpenContext); // dropped rcv packet - queue too long + + DEBUGP(DL_INFO, ("QueueReceiveNetBufferList: open %p queue" + " too long, discarded %p\n", + pOpenContext, pDiscardNetBufList)); + } + else + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); + } + + // + // Run the receive queue service routine now. + // + ndisprotServiceReads(pOpenContext); + } + while (FALSE); +} + +_Success_(return != 0) +PNET_BUFFER_LIST +ndisprotAllocateReceiveNetBufferList( + _In_ PNDISPROT_OPEN_CONTEXT pOpenContext, + _In_ UINT DataLength, + _Outptr_result_bytebuffer_(DataLength) + PUCHAR * ppDataBuffer + ) +/*++ + +Routine Description: + + Allocate resources to copy and queue a received net buffer list + +Arguments: + + pOpenContext - pointer to open context for received packet + DataLength - total length in bytes of the net buffer list's first net buffer + ppDataBuffer - place to return pointer to allocated buffer + +Return Value: + + Pointer to NDIS packet if successful, else NULL. + +--*/ +{ + PNET_BUFFER_LIST pNetBufList; + PMDL pMdl; + PUCHAR pDataBuffer; + + pNetBufList = NULL; + pMdl = NULL; + pDataBuffer = NULL; + *ppDataBuffer = NULL; + + do + { + NPROT_ALLOC_MEM(pDataBuffer, DataLength); + + if (pDataBuffer == NULL) + { + DEBUGP(DL_FATAL, ("AllocRcvNbl: open %p, failed to alloc" + " data buffer %d bytes\n", pOpenContext, DataLength)); + break; + } + + // + // Make this an NDIS buffer. + // + pMdl = NdisAllocateMdl(pOpenContext->BindingHandle, pDataBuffer, DataLength); + + if (pMdl == NULL) + { + DEBUGP(DL_FATAL, ("AllocateRcvNbl: open %p, failed to alloc" + " MDL, %d bytes\n", pOpenContext, DataLength)); + break; + } + + pNetBufList = NdisAllocateNetBufferAndNetBufferList( + pOpenContext->RecvNetBufferListPool, + 0, // ContextSize + 0, // ContextBackfill + pMdl, // MdlChain + 0, // DataOffset + DataLength); // DataLength + + if (pNetBufList == NULL) + { + DEBUGP(DL_FATAL, ("AllocateRcvNbl: open %p, failed to alloc" + " Net Buffer List, %d bytes\n", pOpenContext, DataLength)); + break; + } + + + *ppDataBuffer = pDataBuffer; + + } + while (FALSE); + + if (pNetBufList == NULL) + { + // + // Clean up + // + if (pMdl != NULL) + { + NdisFreeMdl(pMdl); + } + + if (pDataBuffer != NULL) + { + NPROT_FREE_MEM(pDataBuffer); + } + } + + return (pNetBufList); +} + + + +VOID +ndisprotFreeReceiveNetBufferList( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNET_BUFFER_LIST pNetBufferList, + IN BOOLEAN DispatchLevel + ) +/*++ + +Routine Description: + + Free up all resources associated with a received net buffer list. If this + is a local copy, free the net buffer list to our receive pool, else return + this to the miniport. + +Arguments: + + pOpenContext - pointer to open context + pNetBufferList - pointer to net buffer list to be freed. + DipatchLevel - the irql level + +Return Value: + + None + +--*/ +{ + PMDL pMdl; + UINT TotalLength; + UINT BufferLength; + PUCHAR pCopyData = NULL; + ULONG ReturnFlags = 0; + + + do + { + if (NBL_TEST_PROT_RSVD_FLAG(pNetBufferList, NPROT_ALLOCATED_NBL)) + { + // + // This is a local copy. + // + + pMdl = NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pNetBufferList)); + TotalLength = NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(pNetBufferList)); + + NPROT_ASSERT(pMdl != NULL); + + NdisQueryMdl( + pMdl, + (PVOID *)&pCopyData, + &BufferLength, + NormalPagePriority); + + NPROT_ASSERT(BufferLength == TotalLength); + + + NPROT_ASSERT(pCopyData != NULL); // we would have allocated non-paged pool + + + NdisFreeNetBufferList(pNetBufferList); + + NdisFreeMdl(pMdl); + + NPROT_FREE_MEM(pCopyData); + break; + } + // + // The NetBufferList should be returned + + NET_BUFFER_LIST_NEXT_NBL(pNetBufferList) = NULL; + + if (DispatchLevel) + { + NDIS_SET_RETURN_FLAG(ReturnFlags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL); + } + + NdisReturnNetBufferLists(pOpenContext->BindingHandle, + pNetBufferList, + ReturnFlags); + } + while (FALSE); + +} + + +VOID +ndisprotCancelPendingReads( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ) +/*++ + +Routine Description: + + Cancel any pending read IRPs queued on the given open. + +Arguments: + + pOpenContext - pointer to open context + +Return Value: + + None + +--*/ +{ + PIRP pIrp; + PLIST_ENTRY pIrpEntry; + + NPROT_REF_OPEN(pOpenContext); // temp ref - cancel reads + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + while (!NPROT_IS_LIST_EMPTY(&pOpenContext->PendedReads)) + { + // + // Get the first pended Read IRP + // + pIrpEntry = pOpenContext->PendedReads.Flink; + pIrp = CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry); + + // + // Check to see if it is being cancelled. + // + if (IoSetCancelRoutine(pIrp, NULL)) + { + // + // It isn't being cancelled, and can't be cancelled henceforth. + // + NPROT_REMOVE_ENTRY_LIST(pIrpEntry); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Complete the IRP. + // + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + + DEBUGP(DL_INFO, ("CancelPendingReads: Open %p, IRP %p cancelled\n", + pOpenContext, pIrp)); + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + NPROT_DEREF_OPEN(pOpenContext); // took out pended Read for cancelling + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + pOpenContext->PendedReadCount--; + } + else + { + // + // It is being cancelled, let the cancel routine handle it. + // + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Give the cancel routine some breathing space, otherwise + // we might end up examining the same (cancelled) IRP over + // and over again. + // + NdisMSleep(100000); // 100 ms. + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + } + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_DEREF_OPEN(pOpenContext); // temp ref - cancel reads +} + + +VOID +ndisprotFlushReceiveQueue( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ) +/*++ + +Routine Description: + + Free any receive packets queued up on the specified open + +Arguments: + + pOpenContext - pointer to open context + +Return Value: + + None + +--*/ +{ + PLIST_ENTRY pRcvNetBufListEntry; + PNET_BUFFER_LIST pRcvNetBufList; + + NPROT_REF_OPEN(pOpenContext); // temp ref - flushRcvQueue + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + while (!NPROT_IS_LIST_EMPTY(&pOpenContext->RecvNetBufListQueue)) + { + // + // Get the first queued receive packet + // + pRcvNetBufListEntry = pOpenContext->RecvNetBufListQueue.Flink; + NPROT_REMOVE_ENTRY_LIST(pRcvNetBufListEntry); + + pOpenContext->RecvNetBufListCount--; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + pRcvNetBufList = NPROT_RCV_NBL_FROM_LIST_ENTRY(pRcvNetBufListEntry); + NPROT_RCV_NBL_FROM_LIST_ENTRY(pRcvNetBufListEntry) = NULL; + + DEBUGP(DL_LOUD, ("FlushReceiveQueue: open %p, nbl %p\n", + pOpenContext, pRcvNetBufList)); + + ndisprotFreeReceiveNetBufferList(pOpenContext, pRcvNetBufList, FALSE); + + NPROT_DEREF_OPEN(pOpenContext); // took out pended Read + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_DEREF_OPEN(pOpenContext); // temp ref - flushRcvQueue +} + + diff --git a/network/ndis/ndisprot/6x/sys/send.c b/network/ndis/ndisprot/6x/sys/send.c new file mode 100644 index 00000000..b2be7300 --- /dev/null +++ b/network/ndis/ndisprot/6x/sys/send.c @@ -0,0 +1,463 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + NDIS protocol entry points and utility routines to handle sending + data. + +Environment: + + Kernel mode only. + +Revision History: + +--*/ + +#include "precomp.h" + +#define __FILENUMBER 'DNES' + + + + +NTSTATUS +NdisprotWrite( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + Dispatch routine to handle IRP_MJ_WRITE. + +Arguments: + + pDeviceObject - pointer to our device object + pIrp - Pointer to request packet + +Return Value: + + NT status code. + +--*/ +{ + PIO_STACK_LOCATION pIrpSp; + ULONG DataLength; + NTSTATUS NtStatus; + PNDISPROT_OPEN_CONTEXT pOpenContext; + PNET_BUFFER_LIST pNetBufferList; + PMDL pMdl; + NDISPROT_ETH_HEADER UNALIGNED *pEthHeader; + PVOID CancelId; + ULONG SendFlags = 0; + + UNREFERENCED_PARAMETER(pDeviceObject); + + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pOpenContext = pIrpSp->FileObject->FsContext; + + + do + { + if (pOpenContext == NULL) + { + DEBUGP(DL_WARN, ("Write: FileObject %p not yet associated with a device\n", + pIrpSp->FileObject)); + NtStatus = STATUS_INVALID_HANDLE; + break; + } + + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + if (pIrp->MdlAddress == NULL) + { + DEBUGP(DL_FATAL, ("Write: NULL MDL address on IRP %p\n", pIrp)); + NtStatus = STATUS_INVALID_PARAMETER; + break; + } + + // + // Try to get a virtual address for the MDL. + // + + pEthHeader = NULL; + NdisQueryMdl(pIrp->MdlAddress, &pEthHeader, &DataLength, NormalPagePriority); + + if (pEthHeader == NULL) + { + DEBUGP(DL_FATAL, ("Write: MmGetSystemAddr failed for" + " IRP %p, MDL %p\n", + pIrp, pIrp->MdlAddress)); + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // Sanity-check the length. + // + if (DataLength < sizeof(NDISPROT_ETH_HEADER)) + { + DEBUGP(DL_WARN, ("Write: too small to be a valid packet (%d bytes)\n", + DataLength)); + NtStatus = STATUS_BUFFER_TOO_SMALL; + break; + } + + if (DataLength > (pOpenContext->MaxFrameSize + sizeof(NDISPROT_ETH_HEADER))) + { + DEBUGP(DL_WARN, ("Write: Open %p: data length (%d)" + " larger than max frame size (%d)\n", + pOpenContext, DataLength, pOpenContext->MaxFrameSize)); + + NtStatus = STATUS_INVALID_BUFFER_SIZE; + break; + } + + if (pEthHeader->EthType != Globals.EthType) + { + DEBUGP(DL_WARN, ("Write: Failing send with EthType %x\n", + pEthHeader->EthType)); + NtStatus = STATUS_INVALID_PARAMETER; + break; + } + + if (!NPROT_MEM_CMP(pEthHeader->SrcAddr, pOpenContext->CurrentAddress, NPROT_MAC_ADDR_LEN)) + { + DEBUGP(DL_WARN, ("Write: Failing with invalid Source address")); + NtStatus = STATUS_INVALID_PARAMETER; + break; + } + + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + DEBUGP(DL_FATAL, ("Write: Open %p is not bound" + " or in low power state\n", pOpenContext)); + + NtStatus = STATUS_INVALID_HANDLE; + break; + } + + if (pOpenContext->State != NdisprotRunning || + pOpenContext->PowerState != NetDeviceStateD0) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + DEBUGP(DL_INFO, ("Device is not ready.\n")); + NtStatus = STATUS_UNSUCCESSFUL; + break; + } + + + pMdl = pIrp->MdlAddress; + NPROT_ASSERT(pOpenContext->SendNetBufferListPool != NULL); + pNetBufferList = NdisAllocateNetBufferAndNetBufferList( + pOpenContext->SendNetBufferListPool, + sizeof(NPROT_SEND_NETBUFLIST_RSVD), //Request control offset delta + 0, // back fill size + pMdl, + 0, // Data offset + DataLength); + + if (pNetBufferList == NULL) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + DEBUGP(DL_FATAL, ("Write: open %p, failed to alloc send net buffer list\n", + pOpenContext)); + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + pOpenContext->PendedSendCount++; + + NPROT_REF_OPEN(pOpenContext); // pended send + + IoMarkIrpPending(pIrp); + + // + // Initialize the NetBufferList ref count. This NetBufferList will be freed + // when this count goes to zero. + // + NPROT_SEND_NBL_RSVD(pNetBufferList)->RefCount = 1; + + // + // We set up a cancel ID on each send NetBufferList (which maps to a Write IRP), + // and save the NetBufferList pointer in the IRP. If the IRP gets cancelled, we use + // NdisCancelSendNetBufferLists() to cancel the NetBufferList. + // + + CancelId = NPROT_GET_NEXT_CANCEL_ID(); + NDIS_SET_NET_BUFFER_LIST_CANCEL_ID(pNetBufferList, CancelId); + pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pOpenContext; + pIrp->Tail.Overlay.DriverContext[1] = (PVOID)pNetBufferList; + pIrp->Tail.Overlay.DriverContext[2] = CancelId; + + NPROT_INSERT_TAIL_LIST(&pOpenContext->PendedWrites, &pIrp->Tail.Overlay.ListEntry); + + IoSetCancelRoutine(pIrp, NdisprotCancelWrite); + + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Set a back pointer from the packet to the IRP. + // + NPROT_IRP_FROM_SEND_NBL(pNetBufferList) = pIrp; + + NtStatus = STATUS_PENDING; + + +#if SEND_DBG + { + PUCHAR pData; + + pData = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority); + NPROT_ASSERT(pEthHeader == pData); + + DEBUGP(DL_VERY_LOUD, + ("Write: MDL %p, MdlFlags %x, SystemAddr %p, %d bytes\n", + pIrp->MdlAddress, pIrp->MdlAddress->MdlFlags, pData, DataLength)); + + DEBUGPDUMP(DL_VERY_LOUD, pData, MIN(DataLength, 48)); + } +#endif // SEND_DBG + + pNetBufferList->SourceHandle = pOpenContext->BindingHandle; + ASSERT (NDIS_MDL_LINKAGE(pMdl) == NULL); + + SendFlags |= NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK; + + NdisSendNetBufferLists( + pOpenContext->BindingHandle, + pNetBufferList, + NDIS_DEFAULT_PORT_NUMBER, + SendFlags); + + } + while (FALSE); + + if (NtStatus != STATUS_PENDING) + { + pIrp->IoStatus.Status = NtStatus; + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + } + + return (NtStatus); +} + + + +VOID +NdisprotCancelWrite( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) +/*++ + +Routine Description: + + Cancel a pending write IRP. This routine attempt to cancel the NDIS send. + +Arguments: + + pDeviceObject - pointer to our device object + pIrp - IRP to be cancelled + +Return Value: + + None + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + PLIST_ENTRY pIrpEntry; + BOOLEAN FoundIrp = FALSE; + + UNREFERENCED_PARAMETER(pDeviceObject); + + pOpenContext = (PNDISPROT_OPEN_CONTEXT) pIrp->Tail.Overlay.DriverContext[0]; + if (pOpenContext == NULL) + { + // + // The IRP is going to be completed from send complete function + // + IoReleaseCancelSpinLock(pIrp->CancelIrql); + return; + } + + NPROT_REF_OPEN(pOpenContext); + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + // + // Try to locate the IRP in the pended write queue. The send completion + // routine may be running and might have removed it from there. + // + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + for (pIrpEntry = pOpenContext->PendedWrites.Flink; + pIrpEntry != &pOpenContext->PendedWrites; + pIrpEntry = pIrpEntry->Flink) + { + if (pIrp == CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry)) + { + FoundIrp = TRUE; + break; + } + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + if (FoundIrp) + { + PVOID CancelId; + + CancelId = pIrp->Tail.Overlay.DriverContext[2]; + // + // Either the send completion routine hasn't run, or we got a peak + // at the IRP/netbufferlist before it had a chance to take it out of the + // pending IRP queue. + // + // We do not complete the IRP here - note that we didn't dequeue it + // above. This is because we always want the send complete routine to + // complete the IRP. And this in turn is because the NetBufferlist that was + // prepared from the IRP has a buffer chain pointing to data associated + // with this IRP. Therefore we cannot complete the IRP before the driver + // below us is done with the data it pointed to. + // + + // + // Request NDIS to cancel this send. The result of this call is that + // our SendComplete handler will be called (if not already called). + // + DEBUGP(DL_INFO, ("CancelWrite: cancelling nbl %p on Open %p\n", + pIrp->Tail.Overlay.DriverContext[1], pOpenContext)); + + NdisCancelSendNetBufferLists( + pOpenContext->BindingHandle, + CancelId); + + } + // + // else the send completion routine has already picked up this IRP. + // + NPROT_DEREF_OPEN(pOpenContext); +} + + +VOID +NdisprotSendComplete( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNET_BUFFER_LIST pNetBufferList, + IN ULONG SendCompleteFlags + ) +/*++ + +Routine Description: + + NDIS entry point called to signify completion of a packet send. + We pick up and complete the Write IRP corresponding to this packet. + +Arguments: + + ProtocolBindingContext - pointer to open context + pNetBufferList - NetBufferList that completed send + SendCompleteFlags - Specifies if the caller is at DISPATCH level + +Return Value: + + None + +--*/ +{ + PIRP pIrp; + PIO_STACK_LOCATION pIrpSp; + PNDISPROT_OPEN_CONTEXT pOpenContext; + PNET_BUFFER_LIST CurrNetBufferList = NULL; + PNET_BUFFER_LIST NextNetBufferList; + NDIS_STATUS CompletionStatus; + BOOLEAN DispatchLevel; + + pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; + NPROT_STRUCT_ASSERT(pOpenContext, oc); + DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendCompleteFlags); + + for (CurrNetBufferList = pNetBufferList; + CurrNetBufferList != NULL; + CurrNetBufferList = NextNetBufferList) + { + NextNetBufferList = NET_BUFFER_LIST_NEXT_NBL(CurrNetBufferList); + + pIrp = NPROT_IRP_FROM_SEND_NBL(CurrNetBufferList); + + IoAcquireCancelSpinLock(&pIrp->CancelIrql); + IoSetCancelRoutine(pIrp, NULL); + pIrp->Tail.Overlay.DriverContext[0] = NULL; + pIrp->Tail.Overlay.DriverContext[1] = NULL; + IoReleaseCancelSpinLock(pIrp->CancelIrql); + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, DispatchLevel); + + NPROT_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); + + CompletionStatus = NET_BUFFER_LIST_STATUS(CurrNetBufferList); + + + // + // We are done with the NDIS_PACKET: + // + NPROT_DEREF_SEND_NBL(CurrNetBufferList, DispatchLevel); + + // + // Complete the Write IRP with the right status. + // + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + if (CompletionStatus == NDIS_STATUS_SUCCESS) + { + pIrp->IoStatus.Information = pIrpSp->Parameters.Write.Length; + pIrp->IoStatus.Status = STATUS_SUCCESS; + } + else + { + pIrp->IoStatus.Information = 0; + pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; + } + + DEBUGP(DL_INFO, ("SendComplete: NetBufferList %p/IRP %p/Length %d " + "completed with status %x\n", + CurrNetBufferList, pIrp, pIrp->IoStatus.Information, pIrp->IoStatus.Status)); + + IoCompleteRequest(pIrp, IO_NO_INCREMENT); + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, DispatchLevel); + pOpenContext->PendedSendCount--; + + if ((NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_CLOSING)) + && (pOpenContext->PendedSendCount == 0)) + { + ASSERT(pOpenContext->ClosingEvent != NULL); + NPROT_SIGNAL_EVENT(pOpenContext->ClosingEvent); + pOpenContext->ClosingEvent = NULL; + } + NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); + + NPROT_DEREF_OPEN(pOpenContext); // send complete - dequeued send IRP + } + +} + + diff --git a/network/ndis/ndisprot/6x/test/prottest.c b/network/ndis/ndisprot/6x/test/prottest.c new file mode 100644 index 00000000..54af3ddc --- /dev/null +++ b/network/ndis/ndisprot/6x/test/prottest.c @@ -0,0 +1,665 @@ +// +// UIOTEST.C +// +// Test program for ndisprot.sys +// +// usage: UIOTEST [options] +// +// options: +// -e: Enumerate devices +// -r: Read +// -w: Write (default) +// -l : length of each packet (default: %d)\n", PacketLength +// -n : number of packets (defaults to infinity) +// -m (defaults to local MAC) +// + +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4127) // conditional expression is constant + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "protuser.h" + +// this is needed to prevent comppiler from complaining about +// pragma prefast statements below +#ifndef _PREFAST_ + #pragma warning(disable:4068) +#endif + +#ifndef NDIS_STATUS +#define NDIS_STATUS ULONG +#endif + +#if DBG +#define DEBUGP(stmt) printf stmt +#else +#define DEBUGP(stmt) +#endif + +#define PRINTF(stmt) printf stmt + +#ifndef MAC_ADDR_LEN +#define MAC_ADDR_LEN 6 +#endif + +#define MAX_NDIS_DEVICE_NAME_LEN 256 + +CHAR NdisProtDevice[] = "\\\\.\\\\NdisProt"; +CHAR * pNdisProtDevice = &NdisProtDevice[0]; + +BOOLEAN DoEnumerate = FALSE; +BOOLEAN DoReads = FALSE; +INT NumberOfPackets = -1; +ULONG PacketLength = 100; +UCHAR SrcMacAddr[MAC_ADDR_LEN]; +UCHAR DstMacAddr[MAC_ADDR_LEN]; +BOOLEAN bDstMacSpecified = FALSE; +CHAR * pNdisDeviceName = "JUNK"; +USHORT EthType = 0x8e88; +BOOLEAN bUseFakeAddress = FALSE; +UCHAR FakeSrcMacAddr[MAC_ADDR_LEN] = {0}; + + +#include + +typedef struct _ETH_HEADER +{ + UCHAR DstAddr[MAC_ADDR_LEN]; + UCHAR SrcAddr[MAC_ADDR_LEN]; + USHORT EthType; +} ETH_HEADER, *PETH_HEADER; + +#include + + +VOID +PrintUsage() +{ + PRINTF(("usage: PROTTEST [options] \n")); + PRINTF(("options:\n")); + PRINTF((" -e: Enumerate devices\n")); + PRINTF((" -r: Read\n")); + PRINTF((" -w: Write (default)\n")); + PRINTF((" -l : length of each packet (default: %d)\n", PacketLength)); + PRINTF((" -n : number of packets (defaults to infinity)\n")); + PRINTF((" -m (defaults to local MAC)\n")); + PRINTF((" -f Use a fake address to send out the packets.\n")); + + +} + +BOOL +GetOptions( + INT argc, + _In_reads_(argc+1) CHAR *argv[] +) +{ + INT i, j; + CHAR *pOption; + ULONG DstMacAddrUlong[MAC_ADDR_LEN]; + INT RetVal; + PCSTR Parameter; + + do + { + if (argc < 2) + { + PRINTF(("Missing argument\n")); + return FALSE; + } + + for (i = 1; i < argc; i++) + { + pOption = argv[i]; + + if ((*pOption == '-') || (*pOption == '/')) + { + pOption++; + if (*pOption == '\0') + { + DEBUGP(("Badly formed option\n")); + return (FALSE); + } + } + else + { + break; + } + + if (i < argc - 2) + { + Parameter = argv[i+1]; + } + else + { + Parameter = NULL; + } + + switch (*pOption) + { + case 'e': + DoEnumerate = TRUE; + break; + + case 'f': + bUseFakeAddress = TRUE; + break; + + case 'r': + DoReads = TRUE; + break; + + case 'w': + DoReads = FALSE; + break; + + case 'l': + + if (Parameter != NULL) + { + RetVal = atoi(Parameter); + if (RetVal != 0) + { + PacketLength = RetVal; + DEBUGP((" Option: PacketLength = %d\n", PacketLength)); + i++; + break; + } + } + PRINTF(("Option l needs PacketLength parameter\n")); + return (FALSE); + + case 'n': + + if (Parameter != NULL) + { + RetVal = atoi(Parameter); + if (RetVal != 0) + { + NumberOfPackets = RetVal; + DEBUGP((" Option: NumberOfPackets = %d\n", NumberOfPackets)); + i++; + break; + } + } + PRINTF(("Option n needs NumberOfPackets parameter\n")); + return (FALSE); + + case 'm': + + if (Parameter != NULL) + { + RetVal = sscanf_s(Parameter, "%2x:%2x:%2x:%2x:%2x:%2x", + &DstMacAddrUlong[0], + &DstMacAddrUlong[1], + &DstMacAddrUlong[2], + &DstMacAddrUlong[3], + &DstMacAddrUlong[4], + &DstMacAddrUlong[5]); + + if (RetVal == 6) + { + for (j = 0; j < MAC_ADDR_LEN; j++) + { + DstMacAddr[j] = (UCHAR)DstMacAddrUlong[j]; + } + + DEBUGP((" Option: Dest MAC Addr: %02x:%02x:%02x:%02x:%02x:%02x\n", + DstMacAddr[0], + DstMacAddr[1], + DstMacAddr[2], + DstMacAddr[3], + DstMacAddr[4], + DstMacAddr[5])); + bDstMacSpecified = TRUE; + + i++; + break; + } + } + + PRINTF(("Option m needs MAC address parameter\n")); + return (FALSE); + + case '?': + return (FALSE); + + default: + PRINTF(("Unknown option %c\n", *pOption)); + return (FALSE); + } + } + + pNdisDeviceName = argv[i]; + + } + while (FALSE); + + return TRUE; +} + + +HANDLE +OpenHandle( + _In_ PSTR pDeviceName +) +{ + DWORD DesiredAccess; + DWORD ShareMode; + LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL; + + DWORD CreationDistribution; + DWORD FlagsAndAttributes; + HANDLE Handle; + DWORD BytesReturned; + + DesiredAccess = GENERIC_READ|GENERIC_WRITE; + ShareMode = 0; + CreationDistribution = OPEN_EXISTING; + FlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; + + Handle = CreateFileA( + pDeviceName, + DesiredAccess, + ShareMode, + lpSecurityAttributes, + CreationDistribution, + FlagsAndAttributes, + NULL + ); + if (Handle == INVALID_HANDLE_VALUE) + { + DEBUGP(("Creating file failed, error %x\n", GetLastError())); + return Handle; + } + // + // Wait for the driver to finish binding. + // + if (!DeviceIoControl( + Handle, + IOCTL_NDISPROT_BIND_WAIT, + NULL, + 0, + NULL, + 0, + &BytesReturned, + NULL)) + { + DEBUGP(("IOCTL_NDISIO_BIND_WAIT failed, error %x\n", GetLastError())); + CloseHandle(Handle); + Handle = INVALID_HANDLE_VALUE; + } + + return (Handle); +} + + +BOOL +OpenNdisDevice( + _In_ HANDLE Handle, + _In_ PCSTR pDeviceName +) +{ + WCHAR wNdisDeviceName[MAX_NDIS_DEVICE_NAME_LEN]; + INT wNameLength; + SIZE_T NameLength = strlen(pDeviceName); + DWORD BytesReturned; + SIZE_T i; + + + // + // Convert to unicode string - non-localized... + // + wNameLength = 0; + for (i = 0; i < NameLength && i < MAX_NDIS_DEVICE_NAME_LEN-1; i++) + { + wNdisDeviceName[i] = (WCHAR)pDeviceName[i]; + wNameLength++; + } + wNdisDeviceName[i] = L'\0'; + + DEBUGP(("Trying to access NDIS Device: %ws\n", wNdisDeviceName)); + + return (DeviceIoControl( + Handle, + IOCTL_NDISPROT_OPEN_DEVICE, + (LPVOID)&wNdisDeviceName[0], + wNameLength*sizeof(WCHAR), + NULL, + 0, + &BytesReturned, + NULL)); + +} + +_Success_(return) +BOOL +GetSrcMac( + _In_ HANDLE Handle, + _Out_writes_bytes_(MAC_ADDR_LEN) PUCHAR pSrcMacAddr + ) +{ + DWORD BytesReturned; + BOOLEAN bSuccess; + UCHAR QueryBuffer[sizeof(NDISPROT_QUERY_OID) + MAC_ADDR_LEN]; + PNDISPROT_QUERY_OID pQueryOid; + + + DEBUGP(("Trying to get src mac address\n")); + + pQueryOid = (PNDISPROT_QUERY_OID)&QueryBuffer[0]; + pQueryOid->Oid = OID_802_3_CURRENT_ADDRESS; + pQueryOid->PortNumber = 0; + + bSuccess = (BOOLEAN)DeviceIoControl( + Handle, + IOCTL_NDISPROT_QUERY_OID_VALUE, + (LPVOID)&QueryBuffer[0], + sizeof(QueryBuffer), + (LPVOID)&QueryBuffer[0], + sizeof(QueryBuffer), + &BytesReturned, + NULL); + + if (bSuccess) + { + DEBUGP(("GetSrcMac: IoControl success, BytesReturned = %d\n", + BytesReturned)); + +#pragma warning(suppress:6202) // buffer overrun warning - enough space allocated in QueryBuffer + memcpy(pSrcMacAddr, pQueryOid->Data, MAC_ADDR_LEN); + } + else + { + DEBUGP(("GetSrcMac: IoControl failed: %d\n", GetLastError())); + } + + return (bSuccess); +} + + + +VOID +DoReadProc( + HANDLE Handle + ) +{ + PUCHAR pReadBuf = NULL; + INT ReadCount = 0; + BOOLEAN bSuccess; + ULONG BytesRead; + + DEBUGP(("DoReadProc\n")); + + do + { + pReadBuf = malloc(PacketLength); + + if (pReadBuf == NULL) + { + PRINTF(("DoReadProc: failed to alloc %d bytes\n", PacketLength)); + break; + } + + ReadCount = 0; + while (TRUE) + { + bSuccess = (BOOLEAN)ReadFile( + Handle, + (LPVOID)pReadBuf, + PacketLength, + &BytesRead, + NULL); + + if (!bSuccess) + { + PRINTF(("DoReadProc: ReadFile failed on Handle %p, error %x\n", + Handle, GetLastError())); + break; + } + ReadCount++; + + DEBUGP(("DoReadProc: read pkt # %d, %d bytes\n", ReadCount, BytesRead)); + + if ((NumberOfPackets != -1) && (ReadCount == NumberOfPackets)) + { + break; + } + } + } + while (FALSE); + + if (pReadBuf) + { + free(pReadBuf); + } + + PRINTF(("DoReadProc finished: read %d packets\n", ReadCount)); + +} + + +VOID +DoWriteProc( + HANDLE Handle + ) +{ + PUCHAR pWriteBuf = NULL; + PUCHAR pData; + UINT i; + INT SendCount; + PETH_HEADER pEthHeader; + DWORD BytesWritten; + BOOLEAN bSuccess; + + DEBUGP(("DoWriteProc\n")); + SendCount = 0; + + do + { + pWriteBuf = malloc(PacketLength); + + if (pWriteBuf == NULL) + { + DEBUGP(("DoWriteProc: Failed to malloc %d bytes\n", PacketLength)); + break; + } + pEthHeader = (PETH_HEADER)pWriteBuf; + pEthHeader->EthType = EthType; + + if (bUseFakeAddress) + { + memcpy(pEthHeader->SrcAddr, FakeSrcMacAddr, MAC_ADDR_LEN); + } + else + { + memcpy(pEthHeader->SrcAddr, SrcMacAddr, MAC_ADDR_LEN); + } + + memcpy(pEthHeader->DstAddr, DstMacAddr, MAC_ADDR_LEN); + + pData = (PUCHAR)(pEthHeader + 1); + for (i = 0; i < PacketLength - sizeof(ETH_HEADER); i++) + { + *pData++ = (UCHAR)i; + } + + SendCount = 0; + + while (TRUE) + { + + bSuccess = (BOOLEAN)WriteFile( + Handle, + pWriteBuf, + PacketLength, + &BytesWritten, + NULL); + if (!bSuccess) + { + PRINTF(("DoWriteProc: WriteFile failed on Handle %p\n", Handle)); + break; + } + SendCount++; + + DEBUGP(("DoWriteProc: sent %d bytes\n", BytesWritten)); + + if ((NumberOfPackets != -1) && (SendCount == NumberOfPackets)) + { + break; + } + } + + } + while (FALSE); + + if (pWriteBuf) + { + free(pWriteBuf); + } + + PRINTF(("DoWriteProc: finished sending %d packets of %d bytes each\n", + SendCount, PacketLength)); +} + +VOID +EnumerateDevices( + HANDLE Handle + ) +{ + typedef __declspec(align(MEMORY_ALLOCATION_ALIGNMENT)) QueryBindingCharBuf; + QueryBindingCharBuf Buf[1024]; + DWORD BufLength = sizeof(Buf); + DWORD BytesWritten; + DWORD i; + PNDISPROT_QUERY_BINDING pQueryBinding; + + pQueryBinding = (PNDISPROT_QUERY_BINDING)Buf; + + i = 0; + for (pQueryBinding->BindingIndex = i; + /* NOTHING */; + pQueryBinding->BindingIndex = ++i) + { + if (DeviceIoControl( + Handle, + IOCTL_NDISPROT_QUERY_BINDING, + pQueryBinding, + sizeof(NDISPROT_QUERY_BINDING), + Buf, + BufLength, + &BytesWritten, + NULL)) + { + PRINTF(("%2d. %ws\n - %ws\n", + pQueryBinding->BindingIndex, + (WCHAR *)((PUCHAR)pQueryBinding + pQueryBinding->DeviceNameOffset), + (WCHAR *)((PUCHAR )pQueryBinding + pQueryBinding->DeviceDescrOffset))); + + memset(Buf, 0, BufLength); + } + else + { + ULONG rc = GetLastError(); + if (rc != ERROR_NO_MORE_ITEMS) + { + PRINTF(("EnumerateDevices: terminated abnormally, error %d\n", rc)); + } + break; + } + } +} + + + + +VOID __cdecl +main( + INT argc, + _In_reads_(argc+1) LPSTR *argv +) +{ + HANDLE DeviceHandle; + + DeviceHandle = INVALID_HANDLE_VALUE; + + do + { + if (!GetOptions(argc, argv)) + { + PrintUsage(); + break; + } + + DeviceHandle = OpenHandle(pNdisProtDevice); + + if (DeviceHandle == INVALID_HANDLE_VALUE) + { + PRINTF(("Failed to open %s\n", pNdisProtDevice)); + break; + } + + if (DoEnumerate) + { + EnumerateDevices(DeviceHandle); + break; + } + + if (!OpenNdisDevice(DeviceHandle, pNdisDeviceName)) + { + PRINTF(("Failed to access %s\n", pNdisDeviceName)); + break; + } + + DEBUGP(("Opened device %s successfully!\n", pNdisDeviceName)); + + if (!GetSrcMac(DeviceHandle, SrcMacAddr)) + { + PRINTF(("Failed to obtain local MAC address\n")); + break; + } + + + DEBUGP(("Got local MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + SrcMacAddr[0], + SrcMacAddr[1], + SrcMacAddr[2], + SrcMacAddr[3], + SrcMacAddr[4], + SrcMacAddr[5])); + + if (!bDstMacSpecified) + { + memcpy(DstMacAddr, SrcMacAddr, MAC_ADDR_LEN); + } + + if (DoReads) + { + DoReadProc(DeviceHandle); + } + else + { + DoWriteProc(DeviceHandle); + DoReadProc(DeviceHandle); + } + + } + while (FALSE); + + if (DeviceHandle != INVALID_HANDLE_VALUE) + { + CloseHandle(DeviceHandle); + } +} diff --git a/network/ndis/ndisprot/6x/test/prottest.vcxproj b/network/ndis/ndisprot/6x/test/prottest.vcxproj new file mode 100644 index 00000000..7706c8e3 --- /dev/null +++ b/network/ndis/ndisprot/6x/test/prottest.vcxproj @@ -0,0 +1,243 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {0AD0B8E0-5A3E-43FE-8E27-E5BB3DB184B0} + $(MSBuildProjectName) + Debug + Win32 + {B8B7CF7B-163A-4705-B726-3B7251D8BD10} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + prottest + + + prottest + + + prottest + + + prottest + + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + Level4 + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + + + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + Level4 + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + + + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + Level4 + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + + + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + Level4 + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + + + %(PreprocessorDefinitions);UNICODE=1 + %(PreprocessorDefinitions);_UNICODE=1 + %(PreprocessorDefinitions);_WIN32WIN_ + + + + + %(AdditionalDependencies);user32.lib + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + + + %(AdditionalIncludeDirectories);..\sys + + + + + %(AdditionalDependencies);user32.lib + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + + + %(AdditionalIncludeDirectories);..\sys + + + + + %(AdditionalDependencies);user32.lib + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + + + %(AdditionalIncludeDirectories);..\sys + + + + + %(AdditionalDependencies);user32.lib + + + %(AdditionalIncludeDirectories);..\sys + + + %(AdditionalIncludeDirectories);..\sys + + + + + %(AdditionalIncludeDirectories);..\sys + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/ndisprot/6x/test/prottest.vcxproj.Filters b/network/ndis/ndisprot/6x/test/prottest.vcxproj.Filters new file mode 100644 index 00000000..8b87abfa --- /dev/null +++ b/network/ndis/ndisprot/6x/test/prottest.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {5DB43F2D-E248-4063-B2E6-D0F81FCF5D84} + + + h;hpp;hxx;hm;inl;inc;xsd + {3B8958F0-D888-4CE4-9473-3300FDF5A2B3} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {35E32097-01E2-4113-877C-7E88FDCD0FAA} + + + + + Source Files + + + \ No newline at end of file diff --git a/network/ndis/netvmini/6x/60/netvmini60.inf b/network/ndis/netvmini/6x/60/netvmini60.inf new file mode 100644 index 00000000..043aff2e Binary files /dev/null and b/network/ndis/netvmini/6x/60/netvmini60.inf differ diff --git a/network/ndis/netvmini/6x/60/netvmini60.rc b/network/ndis/netvmini/6x/60/netvmini60.rc new file mode 100644 index 00000000..ac470534 --- /dev/null +++ b/network/ndis/netvmini/6x/60/netvmini60.rc @@ -0,0 +1,51 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + netvmini.rc + +Abstract: + + Internal resource file for driver. + +--*/ + +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Microsoft Virtual Miniport Driver" +#define VER_INTERNALNAME_STR "netvmin6.sys" +#define VER_ORIGINALFILENAME_STR "netvmin6.sys" + + + +#define VER_FILEVERSION 4,02,00,0000 +#define VER_FILEVERSION_STR "4.02.00.0000" + +#undef VER_PRODUCTVERSION +#define VER_PRODUCTVERSION VER_FILEVERSION + +#undef VER_PRODUCTVERSION_STR +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) 2009 Microsoft Corporation" +#ifdef VER_COMPANYNAME_STR +#undef VER_COMPANYNAME_STR +#define VER_COMPANYNAME_STR "Microsoft Corporation" +#endif + +#undef VER_PRODUCTNAME_STR +#define VER_PRODUCTNAME_STR "Microsoft Virtual Network Adapter (NDIS 6.0 Miniport)" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/network/ndis/netvmini/6x/60/netvmini60.vcxproj b/network/ndis/netvmini/6x/60/netvmini60.vcxproj new file mode 100644 index 00000000..34fbc15f --- /dev/null +++ b/network/ndis/netvmini/6x/60/netvmini60.vcxproj @@ -0,0 +1,218 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B} + $(MSBuildProjectName) + Debug + Win32 + {FB69D6D4-CF9F-4C63-9FEA-D7D1B1F73BFF} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + DEBUGP(LEVEL,MSG,...) + + + true + true + DEBUGP(LEVEL,MSG,...) + + + + netvmini60 + + + netvmini60 + + + netvmini60 + + + netvmini60 + + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS60_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/netvmini/6x/60/netvmini60.vcxproj.Filters b/network/ndis/netvmini/6x/60/netvmini60.vcxproj.Filters new file mode 100644 index 00000000..36412ff4 --- /dev/null +++ b/network/ndis/netvmini/6x/60/netvmini60.vcxproj.Filters @@ -0,0 +1,46 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {60D1BC26-8429-4B71-B0A4-A09C81AA9006} + + + h;hpp;hxx;hm;inl;inc;xsd + {C5B761C0-3390-4C0D-92B3-296CBE739D12} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {2837E1B4-FB49-43C4-B0DA-0459E959F944} + + + inf;inv;inx;mof;mc; + {78CC6DBE-CEE8-4DB1-BD8D-4C3C3551030A} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/netvmini/6x/620/netvmini620.inf b/network/ndis/netvmini/6x/620/netvmini620.inf new file mode 100644 index 00000000..307f43a0 Binary files /dev/null and b/network/ndis/netvmini/6x/620/netvmini620.inf differ diff --git a/network/ndis/netvmini/6x/620/netvmini620.rc b/network/ndis/netvmini/6x/620/netvmini620.rc new file mode 100644 index 00000000..72dba137 --- /dev/null +++ b/network/ndis/netvmini/6x/620/netvmini620.rc @@ -0,0 +1,51 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + netvmini.rc + +Abstract: + + Internal resource file for driver. + +--*/ + +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Microsoft Virtual Miniport Driver" +#define VER_INTERNALNAME_STR "netvmini62.sys" +#define VER_ORIGINALFILENAME_STR "netvmini62.sys" + + + +#define VER_FILEVERSION 4,02,00,0000 +#define VER_FILEVERSION_STR "4.02.00.0000" + +#undef VER_PRODUCTVERSION +#define VER_PRODUCTVERSION VER_FILEVERSION + +#undef VER_PRODUCTVERSION_STR +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) 2009 Microsoft Corporation" +#ifdef VER_COMPANYNAME_STR +#undef VER_COMPANYNAME_STR +#define VER_COMPANYNAME_STR "Microsoft Corporation" +#endif + +#undef VER_PRODUCTNAME_STR +#define VER_PRODUCTNAME_STR "Microsoft Virtual Network Adapter (NDIS 6.20 Miniport)" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/network/ndis/netvmini/6x/620/netvmini620.vcxproj b/network/ndis/netvmini/6x/620/netvmini620.vcxproj new file mode 100644 index 00000000..dda1e977 --- /dev/null +++ b/network/ndis/netvmini/6x/620/netvmini620.vcxproj @@ -0,0 +1,218 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D} + $(MSBuildProjectName) + Debug + Win32 + {7039332E-F9EF-43DD-8AEC-4478C735DF1D} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + DEBUGP(LEVEL,MSG,...) + + + true + true + DEBUGP(LEVEL,MSG,...) + + + + netvmini620 + + + netvmini620 + + + netvmini620 + + + netvmini620 + + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS620_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/netvmini/6x/620/netvmini620.vcxproj.Filters b/network/ndis/netvmini/6x/620/netvmini620.vcxproj.Filters new file mode 100644 index 00000000..3237b662 --- /dev/null +++ b/network/ndis/netvmini/6x/620/netvmini620.vcxproj.Filters @@ -0,0 +1,49 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {703913F3-8635-4541-B263-C02BD6486AA1} + + + h;hpp;hxx;hm;inl;inc;xsd + {A1EB0C9F-4422-445A-8E6C-268C9DF3497F} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {175D5117-B719-462D-AD06-09F8EDFF3688} + + + inf;inv;inx;mof;mc; + {C815125B-DF87-4E45-8C33-58380D5859E2} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/netvmini/6x/630/netvmini630.inf b/network/ndis/netvmini/6x/630/netvmini630.inf new file mode 100644 index 00000000..dc6bb49a Binary files /dev/null and b/network/ndis/netvmini/6x/630/netvmini630.inf differ diff --git a/network/ndis/netvmini/6x/630/netvmini630.rc b/network/ndis/netvmini/6x/630/netvmini630.rc new file mode 100644 index 00000000..4cd2306b --- /dev/null +++ b/network/ndis/netvmini/6x/630/netvmini630.rc @@ -0,0 +1,50 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + netvmini630.rc + +Abstract: + + Internal resource file for driver. + +--*/ + +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Microsoft Virtual Miniport Driver" +#define VER_INTERNALNAME_STR "netvmini630.sys" +#define VER_ORIGINALFILENAME_STR "netvmini630.sys" + + +#define VER_FILEVERSION 4,20,00,0000 +#define VER_FILEVERSION_STR "4.20.00.0000" + +#undef VER_PRODUCTVERSION +#define VER_PRODUCTVERSION VER_FILEVERSION + +#undef VER_PRODUCTVERSION_STR +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) 2009 Microsoft Corporation" +#ifdef VER_COMPANYNAME_STR +#undef VER_COMPANYNAME_STR +#define VER_COMPANYNAME_STR "Microsoft Corporation" +#endif + +#undef VER_PRODUCTNAME_STR +#define VER_PRODUCTNAME_STR "Microsoft Virtual Network Adapter (NDIS 6.30 Miniport)" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/network/ndis/netvmini/6x/630/netvmini630.vcxproj b/network/ndis/netvmini/6x/630/netvmini630.vcxproj new file mode 100644 index 00000000..b881fbd6 --- /dev/null +++ b/network/ndis/netvmini/6x/630/netvmini630.vcxproj @@ -0,0 +1,218 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {20227FE5-F4F3-4980-98C2-B860C8B2F62C} + $(MSBuildProjectName) + Debug + Win32 + {2C8923A5-C60B-45DE-AF84-B27B1BECA400} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + DEBUGP(LEVEL,MSG,...) + + + true + true + DEBUGP(LEVEL,MSG,...) + + + + netvmini630 + + + netvmini630 + + + netvmini630 + + + netvmini630 + + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + true + Level4 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + %(DisableSpecificWarnings);4201;4214;4127 + + + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(PreprocessorDefinitions);NDIS630_MINIPORT=1 + %(PreprocessorDefinitions);NDIS_MINIPORT_DRIVER=1 + %(PreprocessorDefinitions);NDIS_WDM=1 + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/netvmini/6x/630/netvmini630.vcxproj.Filters b/network/ndis/netvmini/6x/630/netvmini630.vcxproj.Filters new file mode 100644 index 00000000..93779b42 --- /dev/null +++ b/network/ndis/netvmini/6x/630/netvmini630.vcxproj.Filters @@ -0,0 +1,52 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {9D259E56-FF93-4ED4-B79E-BE9390D6F96E} + + + h;hpp;hxx;hm;inl;inc;xsd + {2D6DC915-1942-425E-87CD-DC9F26064AED} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {2DBFC871-0321-474E-ACAA-EF309426BBED} + + + inf;inv;inx;mof;mc; + {8493E657-4501-41D4-8F6A-B7527F30AA2D} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/netvmini/6x/ReadMe.md b/network/ndis/netvmini/6x/ReadMe.md new file mode 100644 index 00000000..a056a846 --- /dev/null +++ b/network/ndis/netvmini/6x/ReadMe.md @@ -0,0 +1,15 @@ +NDIS Virtual Miniport Driver +============================ + +The NDIS Virtual Miniport Driver sample illustrates the functionality of an NDIS miniport driver without requiring a physical network adapter. + +Because the driver does not interact with any hardware, it makes it easier to understand the miniport interface and the usage of various NDIS functions without the clutter of hardware-specific code that is normally found in a fully functional driver. The driver can be installed either manually using the Add Hardware wizard as a root enumerated virtual miniport driver or on a virtual bus (like toaster bus). + +This sample driver demonstrates an NDIS virtual miniport driver. If a single instance of the virtual miniport exists, it simply drops the send packets and completes the send operation successfully. If there are multiple virtual miniport instances, the instances behave as if they were multiple network interface cards (NICs) plugged into a single Ethernet hub. This "hub" indicates the incoming send packets to all of the virtual miniport instances. + +To test the miniport driver, install more than one miniport driver instance. You can repeat the installation to install more than one instance of the miniport. + +**Note**  This sample provides an example of minimal driver intended for education purposes. The driver and its sample test programs are not intended for use in a production environment. + +For more information on creating NDIS Miniport Drivers, see [NDIS Miniport Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff565949). + diff --git a/network/ndis/netvmini/6x/adapter.c b/network/ndis/netvmini/6x/adapter.c new file mode 100644 index 00000000..0cea21d4 --- /dev/null +++ b/network/ndis/netvmini/6x/adapter.c @@ -0,0 +1,2318 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Adapter.C + +Abstract: + + The purpose of this sample is to illustrate functionality of a deserialized + NDIS miniport driver without requiring a physical network adapter. This + sample is based on E100BEX sample present in the DDK. It is basically a + simplified version of E100bex driver. The driver can be installed either + manually using Add Hardware wizard as a root enumerated virtual miniport + driver or on a virtual bus (like toaster bus). Since the driver does not + interact with any hardware, it makes it very easy to understand the miniport + interface and the usage of various NDIS functions without the clutter of + hardware specific code normally found in a fully functional driver. + + This sample provides an example of minimal driver intended for education + purposes. Neither the driver or its sample test programs are intended + for use in a production environment. + +Revision History: + +Notes: + +--*/ + +#include "netvmin6.h" +#include "adapter.tmh" + +static +NDIS_STATUS +NICAllocAdapter( + _In_ NDIS_HANDLE MiniportAdapterHandle, + _Outptr_ PMP_ADAPTER *Adapter); + +void +NICFreeAdapter( + _In_ PMP_ADAPTER Adapter); + +VOID +NICFreeReceiveDpc( + _In_ PMP_ADAPTER_RECEIVE_DPC AdapterDpc); + +static +NDIS_STATUS +NICReadRegParameters( + _In_ PMP_ADAPTER Adapter); + +static +VOID +NICSetMacAddress( + _In_ PMP_ADAPTER Adapter, + _In_ NDIS_HANDLE ConfigurationHandle); + + +static +VOID +NICScheduleTheResetOrPauseDpc( + _In_ PMP_ADAPTER Adapter, + _In_ BOOLEAN fReschedule); + +NDIS_TIMER_FUNCTION NICAsyncResetOrPauseDpc; + +#pragma NDIS_PAGEABLE_FUNCTION(MPInitializeEx) +#pragma NDIS_PAGEABLE_FUNCTION(MPPause) +#pragma NDIS_PAGEABLE_FUNCTION(MPRestart) +#pragma NDIS_PAGEABLE_FUNCTION(MPHaltEx) +#pragma NDIS_PAGEABLE_FUNCTION(MPDevicePnpEventNotify) +#pragma NDIS_PAGEABLE_FUNCTION(NICAllocAdapter) +#pragma NDIS_PAGEABLE_FUNCTION(NICReadRegParameters) +#pragma NDIS_PAGEABLE_FUNCTION(NICSetMacAddress) + + + +NDIS_OID NICSupportedOids[] = +{ + OID_GEN_HARDWARE_STATUS, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_DRIVER_VERSION, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_STATISTICS, + OID_GEN_TRANSMIT_QUEUE_LENGTH, // Optional + OID_GEN_LINK_PARAMETERS, + OID_GEN_INTERRUPT_MODERATION, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, + OID_802_3_XMIT_DEFERRED, // Optional + OID_802_3_XMIT_MAX_COLLISIONS, // Optional + OID_802_3_RCV_OVERRUN, // Optional + OID_802_3_XMIT_UNDERRUN, // Optional + OID_802_3_XMIT_HEARTBEAT_FAILURE, // Optional + OID_802_3_XMIT_TIMES_CRS_LOST, // Optional + OID_802_3_XMIT_LATE_COLLISIONS, // Optional + OID_PNP_CAPABILITIES, // Optional + #if (NDIS_SUPPORT_NDIS620) + OID_RECEIVE_FILTER_ALLOCATE_QUEUE, + OID_RECEIVE_FILTER_QUEUE_ALLOCATION_COMPLETE, + OID_RECEIVE_FILTER_FREE_QUEUE, + OID_RECEIVE_FILTER_CLEAR_FILTER, + OID_RECEIVE_FILTER_SET_FILTER, +#endif +}; + + +NDIS_STATUS +MPInitializeEx( + _In_ NDIS_HANDLE MiniportAdapterHandle, + _In_ NDIS_HANDLE MiniportDriverContext, + _In_ PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters) +/*++ +Routine Description: + + The MiniportInitialize function is a required function that sets up a + NIC (or virtual NIC) for network I/O operations, claims all hardware + resources necessary to the NIC in the registry, and allocates resources + the driver needs to carry out network I/O operations. + + MiniportInitialize runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Return Value: + + NDIS_STATUS_xxx code + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PMP_ADAPTER Adapter = NULL; + + + DEBUGP(MP_TRACE, "---> MPInitializeEx\n"); + UNREFERENCED_PARAMETER(MiniportDriverContext); + PAGED_CODE(); + + + do + { + NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES AdapterRegistration = {0}; + NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES AdapterGeneral = {0}; + +#if (NDIS_SUPPORT_NDIS620) + NDIS_PM_CAPABILITIES PmCapabilities; +#elif (NDIS_SUPPORT_NDIS6) + NDIS_PNP_CAPABILITIES PnpCapabilities; +#endif // NDIS MINIPORT VERSION + + // + // Allocate adapter context structure and initialize all the + // memory resources for sending and receiving packets. + // + Status = NICAllocAdapter(MiniportAdapterHandle, &Adapter); + if(Status != NDIS_STATUS_SUCCESS) + { + break; + } + + DEBUGP(MP_TRACE, "[%p] MPInitializeEx Adapter allocated.\n", Adapter); + + // + // First, set the registration attributes. + // +#if (NDIS_SUPPORT_NDIS630) + {C_ASSERT(sizeof(AdapterRegistration) >= NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_2);} + AdapterRegistration.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES; + AdapterRegistration.Header.Size = sizeof(AdapterRegistration); + AdapterRegistration.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_2; +#else + {C_ASSERT(sizeof(AdapterRegistration) >= NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1);} + AdapterRegistration.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES; + AdapterRegistration.Header.Size = sizeof(AdapterRegistration); + AdapterRegistration.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1; +#endif // NDIS MINIPORT VERSION + + AdapterRegistration.MiniportAdapterContext = Adapter; + AdapterRegistration.AttributeFlags = NIC_ADAPTER_ATTRIBUTES_FLAGS; + +#if (NDIS_SUPPORT_NDIS630) + AdapterRegistration.AttributeFlags |= NDIS_MINIPORT_ATTRIBUTES_NO_PAUSE_ON_SUSPEND; +#endif + + AdapterRegistration.CheckForHangTimeInSeconds = NIC_ADAPTER_CHECK_FOR_HANG_TIME_IN_SECONDS; + AdapterRegistration.InterfaceType = NIC_INTERFACE_TYPE; + + NDIS_DECLARE_MINIPORT_ADAPTER_CONTEXT(MP_ADAPTER); + Status = NdisMSetMiniportAttributes( + MiniportAdapterHandle, + (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&AdapterRegistration); + if (NDIS_STATUS_SUCCESS != Status) + { + DEBUGP(MP_ERROR, "[%p] NdisSetOptionalHandlers Status 0x%08x\n", Adapter, Status); + break; + } + + // + // Read Advanced configuration information from the registry + // + Status = NICReadRegParameters(Adapter); + if(Status != NDIS_STATUS_SUCCESS) + { + break; + } + + // + // NdisMGetDeviceProperty function enables us to get the: + // PDO - created by the bus driver to represent our device. + // FDO - created by NDIS to represent our miniport as a function driver. + // NextDeviceObject - deviceobject of another driver (filter) + // attached to us at the bottom. + // In a pure NDIS miniport driver, there is no use for this + // information, but a NDISWDM driver would need to know this so that it + // can transfer packets to the lower WDM stack using IRPs. + // + NdisMGetDeviceProperty( + MiniportAdapterHandle, + &Adapter->Pdo, + &Adapter->Fdo, + &Adapter->NextDeviceObject, + NULL, + NULL); + + // + // Allocate default DPC, using current processor information (this will be + // added to the DPC list by the allocation function). The default DPC + // is used for all receives when VMQ is not enabled, and for the default queue + // when VMQ is enabled. + // + Adapter->DefaultRecvDpc = NICAllocReceiveDpc(Adapter, 0, 0, 0); + if(!Adapter->DefaultRecvDpc) + { + DEBUGP(MP_ERROR, "[%p] Failed while allocating the default DPC\n", Adapter); + Status = NDIS_STATUS_RESOURCES; + break; + } + + // + // If VMQ is enabled, allocate the default receive queue. Otherwise, initialize the default receive + // block for use in non-VMQ receives + // + if(VMQ_ENABLED(Adapter)) + { + Status = AllocateDefaultRxQueue(Adapter); + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] AllocateDefaultRxQueue Status 0x%08x\n", Adapter, Status); + break; + } + } + else + { + // + // Initialize the default receive block + // + Status = NICInitializeReceiveBlock(Adapter, 0); + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] NICInitializeReceiveBlock Status 0x%08x\n", Adapter, Status); + break; + } + + // + // Initialize list and lock of the free Rcb list + // + NdisInitializeListHead(&Adapter->FreeRcbList); + NdisAllocateSpinLock(&Adapter->FreeRcbListLock); + + // + // Allocate the adapter's non-VMQ RCB & receive NBL data + // + Status = NICAllocRCBData( + Adapter, + NIC_MAX_BUSY_RECVS, + &Adapter->RcbMemoryBlock, + &Adapter->FreeRcbList, + &Adapter->FreeRcbListLock, + &Adapter->RecvNblPoolHandle + ); + if(Status != NDIS_STATUS_SUCCESS) + { + break; + } + } + + // + // Next, set the general attributes. + // + +#if (NDIS_SUPPORT_NDIS620) + {C_ASSERT(sizeof(AdapterGeneral) >= NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2);} + AdapterGeneral.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES; + AdapterGeneral.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2; + AdapterGeneral.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2; +#elif (NDIS_SUPPORT_NDIS6) + {C_ASSERT(sizeof(AdapterGeneral) >= NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1);} + AdapterGeneral.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES; + AdapterGeneral.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1; + AdapterGeneral.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1; +#endif // NDIS MINIPORT VERSION + + // + // Specify the medium type that the NIC can support but not + // necessarily the medium type that the NIC currently uses. + // + AdapterGeneral.MediaType = NIC_MEDIUM_TYPE; + + // + // Specifiy medium type that the NIC currently uses. + // + AdapterGeneral.PhysicalMediumType = NIC_PHYSICAL_MEDIUM; + + // + // Specifiy the maximum network frame size, in bytes, that the NIC + // supports excluding the header. A NIC driver that emulates another + // medium type for binding to a transport must ensure that the maximum + // frame size for a protocol-supplied net buffer does not exceed the + // size limitations for the true network medium. + // + AdapterGeneral.MtuSize = HW_FRAME_MAX_DATA_SIZE; + AdapterGeneral.MaxXmitLinkSpeed = Adapter->ulLinkSendSpeed; + AdapterGeneral.XmitLinkSpeed = Adapter->ulLinkSendSpeed; + AdapterGeneral.MaxRcvLinkSpeed = Adapter->ulLinkRecvSpeed; + AdapterGeneral.RcvLinkSpeed = Adapter->ulLinkRecvSpeed; + AdapterGeneral.MediaConnectState = HWGetMediaConnectStatus(Adapter); + AdapterGeneral.MediaDuplexState = MediaDuplexStateFull; + + // + // The maximum number of bytes the NIC can provide as lookahead data. + // If that value is different from the size of the lookahead buffer + // supported by bound protocols, NDIS will call MiniportOidRequest to + // set the size of the lookahead buffer provided by the miniport driver + // to the minimum of the miniport driver and protocol(s) values. If the + // driver always indicates up full packets with + // NdisMIndicateReceiveNetBufferLists, it should set this value to the + // maximum total frame size, which excludes the header. + // + // Upper-layer drivers examine lookahead data to determine whether a + // packet that is associated with the lookahead data is intended for + // one or more of their clients. If the underlying driver supports + // multipacket receive indications, bound protocols are given full net + // packets on every indication. Consequently, this value is identical + // to that returned for OID_GEN_RECEIVE_BLOCK_SIZE. + // + AdapterGeneral.LookaheadSize = Adapter->ulLookahead; + AdapterGeneral.MacOptions = NIC_MAC_OPTIONS; + AdapterGeneral.SupportedPacketFilters = NIC_SUPPORTED_FILTERS; + + + // + // The maximum number of multicast addresses the NIC driver can manage. + // This list is global for all protocols bound to (or above) the NIC. + // Consequently, a protocol can receive NDIS_STATUS_MULTICAST_FULL from + // the NIC driver when attempting to set the multicast address list, + // even if the number of elements in the given list is less than the + // number originally returned for this query. + // + AdapterGeneral.MaxMulticastListSize = NIC_MAX_MCAST_LIST; + AdapterGeneral.MacAddressLength = NIC_MACADDR_SIZE; + + + // + // Return the MAC address of the NIC burnt in the hardware. + // + NIC_COPY_ADDRESS(AdapterGeneral.PermanentMacAddress, Adapter->PermanentAddress); + + // + // Return the MAC address the NIC is currently programmed to use. Note + // that this address could be different from the permananent address as + // the user can override using registry. Read NdisReadNetworkAddress + // doc for more info. + // + NIC_COPY_ADDRESS(AdapterGeneral.CurrentMacAddress, Adapter->CurrentAddress); + AdapterGeneral.RecvScaleCapabilities = NULL; + AdapterGeneral.AccessType = NIC_ACCESS_TYPE; + AdapterGeneral.DirectionType = NIC_DIRECTION_TYPE; + AdapterGeneral.ConnectionType = NIC_CONNECTION_TYPE; + AdapterGeneral.IfType = NIC_IFTYPE; + AdapterGeneral.IfConnectorPresent = NIC_HAS_PHYSICAL_CONNECTOR; + AdapterGeneral.SupportedStatistics = NIC_SUPPORTED_STATISTICS; + AdapterGeneral.SupportedPauseFunctions = NdisPauseFunctionsUnsupported; + AdapterGeneral.DataBackFillSize = 0; + AdapterGeneral.ContextBackFillSize = 0; + + + // + // The SupportedOidList is an array of OIDs for objects that the + // underlying driver or its NIC supports. Objects include general, + // media-specific, and implementation-specific objects. NDIS forwards a + // subset of the returned list to protocols that make this query. That + // is, NDIS filters any supported statistics OIDs out of the list + // because protocols never make statistics queries. + // + AdapterGeneral.SupportedOidList = NICSupportedOids; + AdapterGeneral.SupportedOidListLength = sizeof(NICSupportedOids); + AdapterGeneral.AutoNegotiationFlags = NDIS_LINK_STATE_DUPLEX_AUTO_NEGOTIATED; + + // + // Set the power management capabilities. The format used is NDIS + // version-specific. + // +#if (NDIS_SUPPORT_NDIS620) + + NdisZeroMemory(&PmCapabilities, sizeof(PmCapabilities)); + + {C_ASSERT(sizeof(PmCapabilities) >= NDIS_SIZEOF_NDIS_PM_CAPABILITIES_REVISION_1);} + PmCapabilities.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + PmCapabilities.Header.Size = NDIS_SIZEOF_NDIS_PM_CAPABILITIES_REVISION_1; + PmCapabilities.Header.Revision = NDIS_PM_CAPABILITIES_REVISION_1; + + PmCapabilities.MinMagicPacketWakeUp = NIC_MAGIC_PACKET_WAKEUP; + PmCapabilities.MinPatternWakeUp = NIC_PATTERN_WAKEUP; + PmCapabilities.MinLinkChangeWakeUp = NIC_LINK_CHANGE_WAKEUP; + + AdapterGeneral.PowerManagementCapabilitiesEx = &PmCapabilities; + +#elif (NDIS_SUPPORT_NDIS6) + + NdisZeroMemory(&PnpCapabilities, sizeof(PnpCapabilities)); + PnpCapabilities.WakeUpCapabilities.MinMagicPacketWakeUp = NIC_MAGIC_PACKET_WAKEUP; + PnpCapabilities.WakeUpCapabilities.MinPatternWakeUp = NIC_PATTERN_WAKEUP; + AdapterGeneral.PowerManagementCapabilities = &PnpCapabilities; // Optional + +#endif // NDIS MINIPORT VERSION + + Status = NdisMSetMiniportAttributes( + MiniportAdapterHandle, + (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&AdapterGeneral); + if (NDIS_STATUS_SUCCESS != Status) + { + DEBUGP(MP_ERROR, "[%p] NdisSetOptionalHandlers Status 0x%08x\n", Adapter, Status); + break; + } + + // + // Set Miniport attributes for supported and enabled VMQ features. + // + Status = InitializeRxQueueMPConfig(Adapter); + if (NDIS_STATUS_SUCCESS != Status) + { + DEBUGP(MP_ERROR, "[%p] InitializeRxQueueMPConfig Status 0x%08x\n", Adapter, Status); + break; + } + + // + // Set miniport attributes for supported and enabled NDIS QOS features. + // + Status = InitializeQOSConfig(Adapter); + if (NDIS_STATUS_SUCCESS != Status) + { + DEBUGP(MP_ERROR, "[%p] InitializeQOSConfig Status 0x%08x\n", Adapter, Status); + break; + } + + // + // For hardware devices, you should register your interrupt handlers + // here, using NdisMRegisterInterruptEx. + // + + // + // Get the Adapter Resources & Initialize the hardware. + // + + Status = HWInitialize(Adapter, MiniportInitParameters); + if(Status != NDIS_STATUS_SUCCESS) + { + Status = NDIS_STATUS_FAILURE; + break; + } + } while(FALSE); + + + if (Status != NDIS_STATUS_SUCCESS) + { + if (Adapter) + { + NICFreeAdapter(Adapter); + } + Adapter = NULL; + } + + + DEBUGP(MP_TRACE, "[%p] <--- MPInitializeEx Status = 0x%08x%\n", Adapter, Status); + return Status; +} + + +NDIS_STATUS +MPPause( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNDIS_MINIPORT_PAUSE_PARAMETERS MiniportPauseParameters) +/*++ + +Routine Description: + + When a miniport receives a pause request, it enters into a Pausing state. + The miniport should not indicate up any more network data. Any pending + send requests must be completed, and new requests must be rejected with + NDIS_STATUS_PAUSED. + + Once all sends have been completed and all recieve NBLs have returned to + the miniport, the miniport enters the Paused state. + + While paused, the miniport can still service interrupts from the hardware + (to, for example, continue to indicate NDIS_STATUS_MEDIA_CONNECT + notifications). + + The miniport must continue to be able to handle status indications and OID + requests. MiniportPause is different from MiniportHalt because, in + general, the MiniportPause operation won't release any resources. + MiniportPause must not attempt to acquire any resources where allocation + can fail, since MiniportPause itself must not fail. + + + MiniportPause runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + MiniportAdapterContext Pointer to the Adapter + MiniportPauseParameters Additional information about the pause operation + +Return Value: + + If the miniport is able to immediately enter the Paused state, it should + return NDIS_STATUS_SUCCESS. + + If the miniport must wait for send completions or pending receive NBLs, it + should return NDIS_STATUS_PENDING now, and call NDISMPauseComplete when the + miniport has entered the Paused state. + + No other return value is permitted. The pause operation must not fail. + +--*/ +{ + NDIS_STATUS Status; + + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + + DEBUGP(MP_TRACE, "[%p] ---> MPPause\n", Adapter); + UNREFERENCED_PARAMETER(MiniportPauseParameters); + PAGED_CODE(); + + MP_SET_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS); + + NICStopTheDatapath(Adapter); + + + if (NICIsBusy(Adapter)) + { + // + // The adapter is busy sending or receiving data, so the pause must + // be completed asynchronously later. + // + NICScheduleTheResetOrPauseDpc(Adapter, FALSE); + Status = NDIS_STATUS_PENDING; + } + else // !NICIsBusy + { + // + // The pause operation has completed synchronously. + // + MP_SET_FLAG(Adapter, fMP_ADAPTER_PAUSED); + MP_CLEAR_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS); + Status = NDIS_STATUS_SUCCESS; + } + + DEBUGP(MP_TRACE, "[%p] <--- MPPause\n", Adapter); + return Status; +} + + +NDIS_STATUS +MPRestart( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNDIS_MINIPORT_RESTART_PARAMETERS RestartParameters) +/*++ + +Routine Description: + + When a miniport receives a restart request, it enters into a Restarting + state. The miniport may begin indicating received data (e.g., using + NdisMIndicateReceiveNetBufferLists), handling status indications, and + processing OID requests in the Restarting state. However, no sends will be + requested while the miniport is in the Restarting state. + + Once the miniport is ready to send data, it has entered the Running state. + The miniport informs NDIS that it is in the Running state by returning + NDIS_STATUS_SUCCESS from this MiniportRestart function; or if this function + has already returned NDIS_STATUS_PENDING, by calling NdisMRestartComplete. + + + MiniportRestart runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + MiniportAdapterContext Pointer to the Adapter + RestartParameters Additional information about the restart operation + +Return Value: + + If the miniport is able to immediately enter the Running state, it should + return NDIS_STATUS_SUCCESS. + + If the miniport is still in the Restarting state, it should return + NDIS_STATUS_PENDING now, and call NdisMRestartComplete when the miniport + has entered the Running state. + + Other NDIS_STATUS codes indicate errors. If an error is encountered, the + miniport must return to the Paused state (i.e., stop indicating receives). + +--*/ + +{ + NDIS_STATUS Status = NDIS_STATUS_PENDING; + + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + + DEBUGP(MP_TRACE, "[%p] ---> MPRestart\n", Adapter); + UNREFERENCED_PARAMETER(Adapter); + UNREFERENCED_PARAMETER(RestartParameters); + PAGED_CODE(); + + + MP_CLEAR_FLAG(Adapter, (fMP_ADAPTER_PAUSE_IN_PROGRESS|fMP_ADAPTER_PAUSED)); + + NICStartTheDatapath(Adapter); + + + // + // The simulated hardware is immediately ready to send data again, so in + // this sample code, MiniportRestart always returns success. If we had to + // wait for hardware to reinitialize, we'd return NDIS_STATUS_PENDING now, + // and call NdisMRestartComplete later. + // + Status = NDIS_STATUS_SUCCESS; + + + DEBUGP(MP_TRACE, "[%p] <--- MPRestart\n", Adapter); + return Status; +} + + +VOID +MPHaltEx( + IN NDIS_HANDLE MiniportAdapterContext, + IN NDIS_HALT_ACTION HaltAction + ) +/*++ + +Routine Description: + + Halt handler is called when NDIS receives IRP_MN_STOP_DEVICE, + IRP_MN_SUPRISE_REMOVE or IRP_MN_REMOVE_DEVICE requests from the PNP + manager. Here, the driver should free all the resources acquired in + MiniportInitialize and stop access to the hardware. NDIS will not submit + any further request once this handler is invoked. + + 1) Free and unmap all I/O resources. + 2) Disable interrupt and deregister interrupt handler. + 3) Deregister shutdown handler regsitered by + NdisMRegisterAdapterShutdownHandler . + 4) Cancel all queued up timer callbacks. + 5) Finally wait indefinitely for all the outstanding receive + packets indicated to the protocol to return. + + MiniportHalt runs at IRQL = PASSIVE_LEVEL. + + +Arguments: + + MiniportAdapterContext Pointer to the Adapter + HaltAction The reason for halting the adapter + +Return Value: + + None. + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + LONG nHaltCount = 0; + + + PAGED_CODE(); + DEBUGP(MP_TRACE, "[%p] ---> MPHaltEx\n", Adapter); + UNREFERENCED_PARAMETER(HaltAction); + + MP_SET_FLAG(Adapter, fMP_ADAPTER_HALT_IN_PROGRESS); + + // + // Call Shutdown handler to disable interrupt and turn the hardware off by + // issuing a full reset + // + // On XP and later, NDIS notifies our PNP event handler the reason for + // calling Halt. So before accessing the device, check to see if the device + // is surprise removed, if so don't bother calling the shutdown handler to + // stop the hardware because it doesn't exist. + // + if(!MP_TEST_FLAG(Adapter, fMP_ADAPTER_SURPRISE_REMOVED)) { + MPShutdownEx(MiniportAdapterContext, NdisShutdownPowerOff); + } + + + NICStopTheDatapath(Adapter); + + + while (NICIsBusy(Adapter) || + Adapter->SendCompleteWorkItemQueued || + Adapter->SendCompleteWorkItemRunning) + { + if (++nHaltCount % 100 == 0) + { + DEBUGP(MP_ERROR, "[%p] Halt timed out!\n", Adapter); + ASSERT(FALSE); + } + + DEBUGP(MP_INFO, "[%p] MPHaltEx - waiting ...\n", Adapter); + NdisMSleep(1000); + } + + + NICFreeAdapter(Adapter); + + DEBUGP(MP_TRACE, "[%p] <--- MPHaltEx\n", Adapter); +} + + +NDIS_STATUS +MPResetEx( + _In_ NDIS_HANDLE MiniportAdapterContext, + _Out_ PBOOLEAN AddressingReset) +/*++ + +Routine Description: + + MiniportResetEx is a required to issue a hardware reset to the NIC + and/or to reset the driver's software state. + + 1) The miniport driver can optionally complete any pending + OID requests. NDIS will submit no further OID requests + to the miniport driver for the NIC being reset until + the reset operation has finished. After the reset, + NDIS will resubmit to the miniport driver any OID requests + that were pending but not completed by the miniport driver + before the reset. + + 2) A deserialized miniport driver must complete any pending send + operations. NDIS will not requeue pending send packets for + a deserialized driver since NDIS does not maintain the send + queue for such a driver. + + 3) If MiniportReset returns NDIS_STATUS_PENDING, the driver must + complete the original request subsequently with a call to + NdisMResetComplete. + + MiniportReset runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + +AddressingReset - If multicast or functional addressing information + or the lookahead size, is changed by a reset, + MiniportReset must set the variable at AddressingReset + to TRUE before it returns control. This causes NDIS to + call the MiniportSetInformation function to restore + the information. + +MiniportAdapterContext - Pointer to our adapter + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status; + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + + DEBUGP(MP_TRACE, "[%p] ---> MPResetEx\n", Adapter); + + *AddressingReset = FALSE; + + do + { + ASSERT(!MP_TEST_FLAG(Adapter, fMP_ADAPTER_HALT_IN_PROGRESS)); + + if (MP_TEST_FLAG(Adapter, fMP_RESET_IN_PROGRESS)) + { + Status = NDIS_STATUS_RESET_IN_PROGRESS; + break; + } + + MP_SET_FLAG(Adapter, fMP_RESET_IN_PROGRESS); + + // + // Complete all the queued up send packets + // + TXFlushSendQueue(Adapter, NDIS_STATUS_RESET_IN_PROGRESS); + + if (NICIsBusy(Adapter)) + { + NICScheduleTheResetOrPauseDpc(Adapter, FALSE); + + // + // By returning NDIS_STATUS_PENDING, we are promising NDIS that + // we will complete the reset request by calling NdisMResetComplete. + // + Status = NDIS_STATUS_PENDING; + break; + } + + MP_CLEAR_FLAG(Adapter, fMP_RESET_IN_PROGRESS); + + Status = NDIS_STATUS_SUCCESS; + + } while(FALSE); + + + DEBUGP(MP_TRACE, "[%p] <--- MPResetEx Status = 0x%08x\n", Adapter, Status); + return Status; +} + + +VOID +NICScheduleTheResetOrPauseDpc( + _In_ PMP_ADAPTER Adapter, + _In_ BOOLEAN fReschedule) +/*++ + +Routine Description: + + Schedules the timer callback function for asynchronous pause/reset. + +Arguments: + + Adapter - + fReschedule - TRUE if this is rescheduling an existing async pause/reset + operation, or FALSE if this is a new pause/reset operation. + +Return Value: + + None. + +--*/ +{ + LARGE_INTEGER liRetryTime; + + DEBUGP(MP_TRACE, "[%p] ---> NICScheduleTheResetOrPauseDpc\n", Adapter); + + if (!fReschedule) + { + Adapter->AsyncBusyCheckCount = 0; + } + + + liRetryTime.QuadPart = -1000000LL; // 100ms in 100ns increments + NdisSetTimerObject(Adapter->AsyncBusyCheckTimer, liRetryTime, 0, NULL); + + DEBUGP(MP_TRACE, "[%p] <--- NICScheduleTheResetOrPauseDpc\n", Adapter); +} + + +_Use_decl_annotations_ +VOID +NICAsyncResetOrPauseDpc( + PVOID SystemSpecific1, + PVOID FunctionContext, + PVOID SystemSpecific2, + PVOID SystemSpecific3) +/*++ + +Routine Description: + + Timer callback function for Reset operation. + +Arguments: + +FunctionContext - Pointer to our adapter + +Return Value: + + None. + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(FunctionContext); + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(SystemSpecific1); + UNREFERENCED_PARAMETER(SystemSpecific2); + UNREFERENCED_PARAMETER(SystemSpecific3); + + + DEBUGP(MP_TRACE, "[%p] ---> NICAsyncResetOrPauseDpc\n", Adapter); + + if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS) + || MP_TEST_FLAG(Adapter, fMP_RESET_IN_PROGRESS)) + { + BOOLEAN fBusy = NICIsBusy(Adapter); + + if (fBusy && ++Adapter->AsyncBusyCheckCount <= 20) + { + // + // Still busy -- let's try another time. + // + NICScheduleTheResetOrPauseDpc(Adapter, TRUE); + } + else + { + if (fBusy) + { + + // + // We have tried enough. Something is wrong. Let us + // just complete the reset request with failure. + // + DEBUGP(MP_ERROR, "[%p] Reset timed out!!!\n", Adapter); + + ASSERT(FALSE); + + Status = NDIS_STATUS_FAILURE; + } + + + if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS)) + { + DEBUGP(MP_INFO, "[%p] Done - NdisMPauseComplete\n", Adapter); + + MP_SET_FLAG(Adapter, fMP_ADAPTER_PAUSED); + MP_CLEAR_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS); + + NdisMPauseComplete(Adapter->AdapterHandle); + } + else if (MP_TEST_FLAG(Adapter, fMP_RESET_IN_PROGRESS)) + { + DEBUGP(MP_INFO, "[%p] Done - NdisMResetComplete\n", Adapter); + + MP_CLEAR_FLAG(Adapter, fMP_RESET_IN_PROGRESS); + + NdisMResetComplete(Adapter->AdapterHandle, Status, FALSE); + } + } + } + + + DEBUGP(MP_TRACE, "[%p] <--- NICAsyncResetOrPauseDpc Status = 0x%08x\n", Adapter, Status); +} + +VOID +MPShutdownEx( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ NDIS_SHUTDOWN_ACTION ShutdownAction) +/*++ + +Routine Description: + + The MiniportShutdownEx handler restores hardware to its initial state when + the system is shut down, whether by the user or because an unrecoverable + system error occurred. This is to ensure that the NIC is in a known + state and ready to be reinitialized when the machine is rebooted after + a system shutdown occurs for any reason, including a crash dump. + + Here just disable the interrupt and stop the DMA engine. Do not free + memory resources or wait for any packet transfers to complete. Do not call + into NDIS at this time. + + This can be called at aribitrary IRQL, including in the context of a + bugcheck. + +Arguments: + + MiniportAdapterContext Pointer to our adapter + ShutdownAction The reason why NDIS called the shutdown function + +Return Value: + + None. + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + UNREFERENCED_PARAMETER(ShutdownAction); + UNREFERENCED_PARAMETER(Adapter); + + DEBUGP(MP_TRACE, "[%p] ---> MPShutdownEx\n", Adapter); + + // + // We don't have any hardware to reset. + // + + DEBUGP(MP_TRACE, "[%p] <--- MPShutdownEx\n", Adapter); +} + + +BOOLEAN +MPCheckForHangEx( + _In_ NDIS_HANDLE MiniportAdapterContext) +/*++ + +Routine Description: + + The MiniportCheckForHangEx handler is called to report the state of the + NIC, or to monitor the responsiveness of an underlying device driver. + This is an optional function. If this handler is not specified, NDIS + judges the driver unresponsive when the driver holds + MiniportQueryInformation or MiniportSetInformation requests for a + time-out interval (deafult 4 sec), and then calls the driver's + MiniportReset function. A NIC driver's MiniportInitialize function can + extend NDIS's time-out interval by calling NdisMSetAttributesEx to + avoid unnecessary resets. + + MiniportCheckForHangEx runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + MiniportAdapterContext Pointer to our adapter + +Return Value: + + TRUE NDIS calls the driver's MiniportReset function. + FALSE Everything is fine + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + + DEBUGP(MP_TRACE, "[%p] ---> MPCheckForHangEx\n", Adapter); + DEBUGP(MP_TRACE, "[%p] <--- MPCheckForHangEx. FALSE\n", Adapter); + return FALSE; +} + + +VOID +MPDevicePnpEventNotify( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNET_DEVICE_PNP_EVENT NetDevicePnPEvent) +/*++ + +Routine Description: + + + + Runs at IRQL = PASSIVE_LEVEL in the context of system thread. + +Arguments: + + MiniportAdapterContext Pointer to our adapter + NetDevicePnPEvent Self-explanatory + +Return Value: + + None. + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + + DEBUGP(MP_TRACE, "[%p] ---> MPDevicePnpEventNotify\n", Adapter); + + PAGED_CODE(); + + + switch (NetDevicePnPEvent->DevicePnPEvent) + { + case NdisDevicePnPEventSurpriseRemoved: + // + // Called when NDIS receives IRP_MN_SUPRISE_REMOVAL. + // NDIS calls MiniportHalt function after this call returns. + // + MP_SET_FLAG(Adapter, fMP_ADAPTER_SURPRISE_REMOVED); + DEBUGP(MP_INFO, "[%p] MPDevicePnpEventNotify: NdisDevicePnPEventSurpriseRemoved\n", Adapter); + break; + + case NdisDevicePnPEventPowerProfileChanged: + // + // After initializing a miniport driver and after miniport driver + // receives an OID_PNP_SET_POWER notification that specifies + // a device power state of NdisDeviceStateD0 (the powered-on state), + // NDIS calls the miniport's MiniportPnPEventNotify function with + // PnPEvent set to NdisDevicePnPEventPowerProfileChanged. + // + DEBUGP(MP_INFO, "[%p] MPDevicePnpEventNotify: NdisDevicePnPEventPowerProfileChanged\n", Adapter); + + if (NetDevicePnPEvent->InformationBufferLength == sizeof(ULONG)) + { + ULONG NdisPowerProfile = *((PULONG)NetDevicePnPEvent->InformationBuffer); + + if (NdisPowerProfile == NdisPowerProfileBattery) + { + DEBUGP(MP_INFO, "[%p] The host system is running on battery power\n", Adapter); + } + if (NdisPowerProfile == NdisPowerProfileAcOnLine) + { + DEBUGP(MP_INFO, "[%p] The host system is running on AC power\n", Adapter); + } + } + break; + + default: + DEBUGP(MP_ERROR, "[%p] MPDevicePnpEventNotify: unknown PnP event 0x%x\n", Adapter, NetDevicePnPEvent->DevicePnPEvent); + } + + + DEBUGP(MP_TRACE, "[%p] <--- MPDevicePnpEventNotify\n", Adapter); +} + +NDIS_STATUS +NICAllocAdapter( + _In_ NDIS_HANDLE MiniportAdapterHandle, + _Outptr_ PMP_ADAPTER *pAdapter) +/*++ +Routine Description: + + The NICAllocAdapter function allocates and initializes the memory used to track a miniport instance. + + NICAllocAdapter runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + MiniportAdapterHandle NDIS handle for the adapter. + pAdapter Receives the allocated and initialized adapter memory. + + Return Value: + + NDIS_STATUS_xxx code + +--*/ +{ + PMP_ADAPTER Adapter = NULL; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + LONG index; + + DEBUGP(MP_TRACE, "---> NICAllocAdapter\n"); + + PAGED_CODE(); + + *pAdapter = NULL; + + do + { + // + // Allocate extra space for the MP_ADAPTER memory (one cache line's worth) so that we can + // reference the memory from a cache-aligned starting point. This way we guarantee that + // members with cache-aligned directives are actually cache aligned. + // + PVOID UnalignedAdapterBuffer = NULL; + ULONG UnalignedAdapterBufferSize = sizeof(MP_ADAPTER)+ NdisGetSharedDataAlignment(); + NDIS_TIMER_CHARACTERISTICS Timer; + + // + // Allocate memory for adapter context (unaligned) + // + UnalignedAdapterBuffer = NdisAllocateMemoryWithTagPriority( + NdisDriverHandle, + UnalignedAdapterBufferSize, + NIC_TAG, + NormalPoolPriority); + if (!UnalignedAdapterBuffer) + { + Status = NDIS_STATUS_RESOURCES; + DEBUGP(MP_ERROR, "Failed to allocate memory for adapter context\n"); + break; + } + + // + // Zero the memory block + // + NdisZeroMemory(UnalignedAdapterBuffer, UnalignedAdapterBufferSize); + + // + // Start the Adapter pointer at a cache-aligned boundary + // + Adapter = ALIGN_UP_POINTER_BY(UnalignedAdapterBuffer, NdisGetSharedDataAlignment()); + + // + // Store the unaligned information so that we can free it later + // + Adapter->UnalignedAdapterBuffer = UnalignedAdapterBuffer; + Adapter->UnalignedAdapterBufferSize = UnalignedAdapterBufferSize; + + // + // Set the adapter handle + // + Adapter->AdapterHandle = MiniportAdapterHandle; + + NdisInitializeListHead(&Adapter->List); + + // + // Initialize Send & Recv listheads and corresponding + // spinlocks. + // + NdisInitializeListHead(&Adapter->FreeTcbList); + NdisAllocateSpinLock(&Adapter->FreeTcbListLock); + + NdisInitializeListHead(&Adapter->SendWaitList); + NdisAllocateSpinLock(&Adapter->SendWaitListLock); + + NdisInitializeListHead(&Adapter->BusyTcbList); + NdisAllocateSpinLock(&Adapter->BusyTcbListLock); + + KeInitializeSpinLock(&Adapter->SendPathSpinLock); + + // + // Set the default lookahead buffer size. + // + Adapter->ulLookahead = NIC_MAX_LOOKAHEAD; + + // + // Allocate data for Send and Receive Control blocks. + // + + Adapter->TcbMemoryBlock = NdisAllocateMemoryWithTagPriority( + MiniportAdapterHandle, + sizeof(TCB) * NIC_MAX_BUSY_SENDS, + NIC_TAG_TCB, + NormalPoolPriority); + + if (!Adapter->TcbMemoryBlock) + { + Status = NDIS_STATUS_RESOURCES; + DEBUGP(MP_ERROR, "[%p] NdisAllocateMemoryWithTagPriority failed\n", Adapter); + break; + } + + for (index = 0; index < NIC_MAX_BUSY_SENDS; index++) + { + NdisInterlockedInsertTailList( + &Adapter->FreeTcbList, + &((PTCB)Adapter->TcbMemoryBlock)[index].TcbLink, + &Adapter->FreeTcbListLock); + } + + // + // Set up timers to simulate hardware interrupts (SendComplete and Recv) + // + + NdisZeroMemory(&Timer, sizeof(Timer)); + + {C_ASSERT(NDIS_SIZEOF_TIMER_CHARACTERISTICS_REVISION_1 <= sizeof(Timer));} + Timer.Header.Type = NDIS_OBJECT_TYPE_TIMER_CHARACTERISTICS; + Timer.Header.Size = NDIS_SIZEOF_TIMER_CHARACTERISTICS_REVISION_1; + Timer.Header.Revision = NDIS_TIMER_CHARACTERISTICS_REVISION_1; + + Timer.TimerFunction = TXSendCompleteDpc; + Timer.FunctionContext = Adapter; + Timer.AllocationTag = NIC_TAG_TIMER; + + Status = NdisAllocateTimerObject( + NdisDriverHandle, + &Timer, + &Adapter->SendCompleteTimer); + if (Status != NDIS_STATUS_SUCCESS) + { + Status = NDIS_STATUS_FAILURE; + break; + } + + Adapter->SendCompleteWorkItem = NdisAllocateIoWorkItem(MiniportAdapterHandle); + if (Adapter->SendCompleteWorkItem == NULL) + { + Status = NDIS_STATUS_RESOURCES; + break; + } + + // + // Initialize DPC list data + // + NdisInitializeListHead(&Adapter->RecvDpcList); + NdisAllocateSpinLock(&Adapter->RecvDpcListLock); + + // + // Set up a timer function for use with our MPReset routine. + // + Timer.TimerFunction = NICAsyncResetOrPauseDpc; + + Status = NdisAllocateTimerObject( + NdisDriverHandle, + &Timer, + &Adapter->AsyncBusyCheckTimer); + if (Status != NDIS_STATUS_SUCCESS) + { + Status = NDIS_STATUS_FAILURE; + break; + } + + // + // The miniport adapter is powered up + // + Adapter->CurrentPowerState = NdisDeviceStateD0; + + // + // Initialize and allocate the basic VMQ data for this adapter if supported. Queues, filters, shared memory, + // and other data is allocated at a later time, when the relavant OID's are called to eable/configure them. + // + Status = AllocateVMQData(Adapter); + if (Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] AllocateVMQData Status 0x%08x\n", Adapter, Status); + Status = NDIS_STATUS_FAILURE; + break; + } + + } while(FALSE); + + + *pAdapter = Adapter; + + // + // In the failure case, the caller of this routine will end up + // calling NICFreeAdapter to free all the successfully allocated + // resources. + // + DEBUGP(MP_TRACE, "[%p] <--- NICAllocAdapter\n", Adapter); + return Status; +} + + +void NICFreeAdapter( + _In_ PMP_ADAPTER Adapter) +/*++ +Routine Description: + + The NICFreeAdapter function frees memory used to track a miniport instance. Should only be called from MPHaltEx. + + NICFreeAdapter runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Adapter Adapter memory to free. + + Return Value: + + NDIS_STATUS_xxx code + +--*/ +{ + PLIST_ENTRY pEntry; + + DEBUGP(MP_TRACE, "[%p] ---> NICFreeAdapter\n", Adapter); + + ASSERT(Adapter); + + // + // Free all the resources we allocated in NICAllocAdapter. + // + + if (Adapter->AsyncBusyCheckTimer) + { + NdisFreeTimerObject(Adapter->AsyncBusyCheckTimer); + Adapter->AsyncBusyCheckTimer = NULL; + } + + if(Adapter->SendCompleteTimer) + { + NdisFreeTimerObject(Adapter->SendCompleteTimer); + Adapter->SendCompleteTimer = NULL; + } + + if (Adapter->SendCompleteWorkItem) + { + ASSERT(!Adapter->SendCompleteWorkItemQueued); + NdisFreeIoWorkItem(Adapter->SendCompleteWorkItem); + Adapter->SendCompleteWorkItem = NULL; + } + + // + // If VMQ is enabled, then the global adapter RCB list will not have been allocated + // + if(!VMQ_ENABLED(Adapter)) + { + if(Adapter->FreeRcbList.Flink) + { + while (NULL != (pEntry = NdisInterlockedRemoveHeadList( + &Adapter->FreeRcbList, + &Adapter->FreeRcbListLock))) + { + PRCB Rcb = CONTAINING_RECORD(pEntry, RCB, RcbLink); + NdisFreeNetBufferList(Rcb->Nbl); + } + } + + if (Adapter->RecvNblPoolHandle) + { + NdisFreeNetBufferListPool(Adapter->RecvNblPoolHandle); + Adapter->RecvNblPoolHandle = NULL; + } + + if (Adapter->RcbMemoryBlock) + { + NdisFreeMemory( + Adapter->RcbMemoryBlock, + sizeof(RCB)*NIC_MAX_BUSY_RECVS, + 0); + Adapter->RcbMemoryBlock = NULL; + } + + if(Adapter->DefaultRecvDpc) + { + // + // For non VMQ receives, we've referenced the default queue DPC so we should reduce that count + // + NICReceiveDpcRemoveOwnership(Adapter->DefaultRecvDpc, 0); + Adapter->DefaultRecvDpc = NULL; + } + + } + + if (Adapter->TcbMemoryBlock) + { + NdisFreeMemory( + Adapter->TcbMemoryBlock, + sizeof(TCB)*NIC_MAX_BUSY_SENDS, + 0); + Adapter->TcbMemoryBlock = NULL; + } + + ASSERT(Adapter->SendWaitList.Flink && IsListEmpty(&Adapter->SendWaitList)); + ASSERT(Adapter->BusyTcbList.Flink && IsListEmpty(&Adapter->BusyTcbList)); + + NdisFreeSpinLock(&Adapter->FreeTcbListLock); + NdisFreeSpinLock(&Adapter->SendWaitListLock); + NdisFreeSpinLock(&Adapter->BusyTcbListLock); + NdisFreeSpinLock(&Adapter->FreeRcbListLock); + + // + // Free any remaining VMQ related data + // + FreeVMQData(Adapter); + + // + // Free receive DPCs + // + if(Adapter->RecvDpcList.Flink) + { + while (!IsListEmpty(&Adapter->RecvDpcList)) + { + pEntry = RemoveHeadList(&Adapter->RecvDpcList); + NICFreeReceiveDpc(CONTAINING_RECORD(pEntry, MP_ADAPTER_RECEIVE_DPC, Entry)); + } + } + NdisFreeSpinLock(&Adapter->RecvDpcListLock); + + // + // Finally free the memory for adapter context. + // + NdisFreeMemory(Adapter->UnalignedAdapterBuffer, Adapter->UnalignedAdapterBufferSize, 0); + + DEBUGP(MP_TRACE, "[%p] <--- NICFreeAdapter\n", Adapter); +} + +VOID +NICUpdateDPCMaxIndicateCount( + _In_ PMP_ADAPTER_RECEIVE_DPC ReceiveDpc) +/*++ +Routine Description: + + The NICUpdateDPCMaxIndicateCount function updates the maximum amount of NBLs to be indicated per block, based + on the number of owned receive blocks. + +Arguments: + + ReceiveDpc The receive DPC being modified + BlockId ID of the receive block to add ownership + + Return Value: + + None +--*/ +{ + if(ReceiveDpc->RecvBlockCount) + { + // + // Update MaxNblCountPerIndicate. Scale back the amount of NBL indications we're allowed to do per + // consumed receive block so that we don't sepnd too much time in the DPC as the number of queues grows large. + // + ReceiveDpc->MaxNblCountPerIndicate = NIC_MAX_RECVS_PER_DPC/ReceiveDpc->RecvBlockCount; + } + +} + +VOID +NICReceiveDpcSetOwnership( + _In_ PMP_ADAPTER_RECEIVE_DPC ReceiveDpc, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId) +/*++ +Routine Description: + + The NICReceiveDpcSetOwnership function updates the Receive DPC passed in so that it consumes the receive queue of the + passed in receive block ID. This involves updating the boolean array, block count, and updating the maximum + amount of NBLs to be indicated per block. + +Arguments: + + ReceiveDpc The receive DPC being modified + BlockId ID of the receive block to add ownership + + Return Value: + + None +--*/ +{ + ASSERT(BlockId < NIC_SUPPORTED_NUM_QUEUES); + + // + // If the DPC is not already set to consume receives for this QueueId, increase + // the reference count and set it to be consumed. + // + if(!ReceiveDpc->RecvBlock[BlockId]) + { + InterlockedIncrement(&ReceiveDpc->RecvBlockCount); + ReceiveDpc->RecvBlock[BlockId]=TRUE; + NICUpdateDPCMaxIndicateCount(ReceiveDpc); + } +} + +VOID +NICReceiveDpcRemoveOwnership( + _In_ PMP_ADAPTER_RECEIVE_DPC ReceiveDpc, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId) +/*++ +Routine Description: + + The NICReceiveDpcRemoveOwnership function updates the Receive DPC passed in so that it stops consuming the receive + queue of the passed in receive block ID. This involves updating the boolean array, block count, and updating the maximum + amount of NBLs to be indicated per block. + +Arguments: + + ReceiveDpc The receive DPC being modified + ReceiveBlockId ID of the receive block to remove ownership + + Return Value: + + None +--*/ +{ + ASSERT(BlockId < NIC_SUPPORTED_NUM_QUEUES); + + // + // If the DPC is set to consume receives for this QueueId, decrease + // the reference count and set it to not be consumed. + // + if(ReceiveDpc->RecvBlock[BlockId]) + { + InterlockedDecrement(&ReceiveDpc->RecvBlockCount); + ASSERT(ReceiveDpc->RecvBlockCount>=0); + ReceiveDpc->RecvBlock[BlockId]=FALSE; + NICUpdateDPCMaxIndicateCount(ReceiveDpc); + } +} + +PMP_ADAPTER_RECEIVE_DPC +NICAllocReceiveDpc( + _In_ PMP_ADAPTER Adapter, + ULONG ProcessorNumber, + USHORT ProcessorGroup, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId) +/*++ +Routine Description: + + The NICAllocReceiveDpc function allocates a receive DPC with the specified processor characteristics + for the target QueueId. If the adapter has already allocated a DPC that matches the processor requirements, + the existing DPC is reused and given ownership of consuming the QueueId's receives. + + NICAllocReceiveDpc runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Adapter Pointer to the adapter that will own the DPC. + ProcessorNumber Target processor for DPC + ProcessorGroup Target processor group for the DPC (if not Win7, set to 0) + QueueId Queue whose receives the DPC should consume (0 for non-VMQ scenarios) + + Return Value: + + PMP_ADAPTER_RECEIVE_DPC structure on success. + NULL on failure. + +--*/ +{ + PMP_ADAPTER_RECEIVE_DPC ReceiveDpc=NULL, ExistingDpc=NULL; + PLIST_ENTRY ReceiveListEntry; + NTSTATUS Status = STATUS_SUCCESS; + + ASSERT(BlockId < NIC_SUPPORTED_NUM_QUEUES); + + do + { + ReceiveDpc = NdisAllocateMemoryWithTagPriority( + Adapter->AdapterHandle, + sizeof(MP_ADAPTER_RECEIVE_DPC), + NIC_TAG_DPC, + NormalPoolPriority); + if(!ReceiveDpc) + { + DEBUGP(MP_ERROR, "[%p] Could not allocate memory for receive DPC.\n", Adapter); + break; + } + NdisZeroMemory(ReceiveDpc, sizeof(MP_ADAPTER_RECEIVE_DPC)); + + ReceiveDpc->Adapter = Adapter; + + // + // Initialize DPC data (entry, DPC function, target processor) + // + NdisInitializeListHead(&ReceiveDpc->Entry); + KeInitializeDpc(&ReceiveDpc->Dpc, RXReceiveIndicateDpc, Adapter); + + // + // Allocate the work item that's used if we're close to the DPC watchdog timer limit + // + ReceiveDpc->WorkItem = NdisAllocateIoWorkItem(Adapter->AdapterHandle); + if(!ReceiveDpc->WorkItem) + { + DEBUGP(MP_ERROR, "[%p] Could not allocate work item for receive DPC.\n", Adapter); + Status = NDIS_STATUS_RESOURCES; + break; + } + + // + // Make sure the target DPC list starts getting processed as soon as it's queued even if was queued from + // another processor. + // + KeSetImportanceDpc(&ReceiveDpc->Dpc, MediumHighImportance); + +#if (NDIS_SUPPORT_NDIS620) + { + // + // Use Win7 APIs to set the target processor + // + PROCESSOR_NUMBER DpcProcessor = {0}; + DpcProcessor.Number = (UCHAR)ProcessorNumber; + DpcProcessor.Group = ProcessorGroup; + Status = KeSetTargetProcessorDpcEx(&ReceiveDpc->Dpc, &DpcProcessor); + } +#else + // + // Use Vista APIs to set target processor + // + KeSetTargetProcessorDpc(&ReceiveDpc->Dpc, (CCHAR)ProcessorNumber); +#endif + if(!NT_SUCCESS(Status)) + { + DEBUGP(MP_ERROR, "[%p] Failed setting KeSetTargetProcessorDpcEx. Status: 0x%08x\n", Adapter, Status); + break; + } + ReceiveDpc->ProcessorGroup = ProcessorGroup; + ReceiveDpc->ProcessorNumber = ProcessorNumber; + + // + // Check if it's already on the list, if it is, return existing Dpc and free this one + // otherwise, add to the adapter's DPC list. + // + NdisAcquireSpinLock(&Adapter->RecvDpcListLock); + + for(ReceiveListEntry = Adapter->RecvDpcList.Flink; + ReceiveListEntry != &Adapter->RecvDpcList; + ExistingDpc = NULL, ReceiveListEntry = ReceiveListEntry->Flink) + { + ExistingDpc = CONTAINING_RECORD(ReceiveListEntry, MP_ADAPTER_RECEIVE_DPC, Entry); + if(ExistingDpc->ProcessorNumber == ProcessorNumber && ExistingDpc->ProcessorGroup == ProcessorGroup) + { + // + // A DPC exists with matching affinity information. Break so that we don't NULL ExistingDpc + // + break; + } + } + + if(ExistingDpc) + { + // + // Use existing DPC + // + DEBUGP(MP_TRACE, "[%p] Reusing existing DPC which matches affinity.\n", Adapter); + NICFreeReceiveDpc(ReceiveDpc); + ReceiveDpc = ExistingDpc; + + } + else + { + // + // Add new DPC to adapter's list + // + InsertTailList(&Adapter->RecvDpcList, &ReceiveDpc->Entry); + } + + NdisReleaseSpinLock(&Adapter->RecvDpcListLock); + + NICReceiveDpcSetOwnership(ReceiveDpc,BlockId); + + }while(FALSE); + + if(!NT_SUCCESS(Status) && ReceiveDpc) + { + NICFreeReceiveDpc(ReceiveDpc); + ReceiveDpc = NULL; + } + + return ReceiveDpc; +} + +VOID +NICFreeReceiveDpc( + _In_ PMP_ADAPTER_RECEIVE_DPC AdapterDpc) +/*++ +Routine Description: + + The NICFreeReceiveDpc function frees the passed in DPC. Should only be called when the DPC is not active (not queued, flushed). + +Arguments: + + AdapterDpc DPC to free + + Return Value: + + None + +--*/ +{ + ASSERT(AdapterDpc->RecvBlockCount==0); + // + // Free DPC dymainc fields and memory + // + if(AdapterDpc->WorkItem) + { + NdisFreeIoWorkItem(AdapterDpc->WorkItem); + } + NdisFreeMemory(AdapterDpc, sizeof(MP_ADAPTER_RECEIVE_DPC), 0); +} + +PMP_ADAPTER_RECEIVE_DPC +NICGetDefaultReceiveDpc( + _In_ PMP_ADAPTER Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId) +/*++ +Routine Description: + + The NICGetDefaultReceiveDpc function returns the default receive DPC for the adapter, and sets it to consume + receives from the passed in QueueId. + + +Arguments: + + Adapter Pointer to the adapter that will own the DPC. + QueueId Queue whose receives the DPC should consume (0 for non-VMQ scenarios) + + Return Value: + + PMP_ADAPTER_RECEIVE_DPC structure. + +--*/ +{ + ASSERT(BlockId < NIC_SUPPORTED_NUM_QUEUES); + + if(VMQ_ENABLED(Adapter)) + { + NdisAcquireSpinLock(&Adapter->RecvDpcListLock); + NICReceiveDpcSetOwnership(Adapter->DefaultRecvDpc,BlockId); + NdisReleaseSpinLock(&Adapter->RecvDpcListLock); + } + else + { + UNREFERENCED_PARAMETER(BlockId); + } + + return Adapter->DefaultRecvDpc; +} + +NDIS_STATUS + NICAllocRCBData( + _In_ PMP_ADAPTER Adapter, + ULONG NumberOfRcbs, + _Outptr_result_bytebuffer_(NumberOfRcbs * sizeof(RCB)) PVOID *RcbMemoryBlock, + _Inout_ PLIST_ENTRY FreeRcbList, + _Inout_ PNDIS_SPIN_LOCK FreeRcbListLock, + _Inout_ PNDIS_HANDLE RecvNblPoolHandle + ) +/*++ +Routine Description: + + The NICAllocRCBData function allocated NumberOfRcbs worth of RCB and NBL memory for use in receive indication, + and populates the passed in FreeRcbList with this data. + + IRQL = PASSIVE_LEVEL + +Arguments: + + Adapter Pointer to our adapter + NumberOfRcbs Number of RCB structures to allocate (and NBLs as a result) + RcbMemoryBlock Receives the allocated memory block that is split up into each individual RCB + FreeRcbList Initialized blank list to be populated with each individual RCB. + FreeRcbListLock Initialized lock used when updating the RCB list (and subsequent consumers should use lock). + RcbNblPoolHandle Receives the Ndis NBL pool handle for the allocated NBLs (to be used on free). + + Return Value: + + NDIS_STATUS_xxx code + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NET_BUFFER_LIST_POOL_PARAMETERS NblParameters; + ULONG index; + + do + { + // + // Allocate an NBL pool for receive indications. + // + + {C_ASSERT(sizeof(NblParameters) >= NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1);} + NblParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + NblParameters.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + NblParameters.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + + NblParameters.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT; // always use DEFAULT for miniport drivers + NblParameters.fAllocateNetBuffer = TRUE; + NblParameters.ContextSize = 0; + NblParameters.PoolTag = NIC_TAG_RECV_NBL; + NblParameters.DataSize = 0; + + *RecvNblPoolHandle = NdisAllocateNetBufferListPool( + NdisDriverHandle, + &NblParameters); + if (!*RecvNblPoolHandle) + { + Status = NDIS_STATUS_RESOURCES; + DEBUGP(MP_ERROR, "[%p] NdisAllocateNetBufferListPool failed\n", Adapter); + break; + } + + // + // Allocate receive memory block + // + *RcbMemoryBlock = NdisAllocateMemoryWithTagPriority( + Adapter->AdapterHandle, + sizeof(RCB) * NumberOfRcbs, + NIC_TAG_RCB, + NormalPoolPriority); + + if (!*RcbMemoryBlock) + { + Status = NDIS_STATUS_RESOURCES; + DEBUGP(MP_ERROR, "[%p] NdisAllocateMemoryWithTagPriority failed\n", Adapter); + break; + } + NdisZeroMemory(*RcbMemoryBlock, sizeof(RCB) * NumberOfRcbs); + + // + // Split into individual RCBs, allocate NBL, and add to free list + // + for (index = 0; index < NumberOfRcbs; index++) + { + PRCB Rcb = &((PRCB)*RcbMemoryBlock)[index]; + + // + // Allocate an NBL with its single NET_BUFFER from the preallocated + // pool. + // + Rcb->Nbl = NdisAllocateNetBufferAndNetBufferList( + *RecvNblPoolHandle, + 0, // ContextSize + 0, // ContextBackfill + NULL, // MdlChain + 0, // DataOffset + 0); // DataLength + if (Rcb->Nbl == NULL) + { + Status = NDIS_STATUS_RESOURCES; + DEBUGP(MP_ERROR, "[%p] NdisAllocateNetBufferAndNetBufferList failed\n", Adapter); + break; + } + + // + // Add RCB pointer to miniport reserved portion of NBL + // + RCB_FROM_NBL(Rcb->Nbl) = Rcb; + + NdisInterlockedInsertTailList( + FreeRcbList, + &Rcb->RcbLink, + FreeRcbListLock); + } + + }while(FALSE); + + return Status; +} + +NDIS_STATUS +NICReadRegParameters( + _In_ PMP_ADAPTER Adapter) +/*++ +Routine Description: + + Read device configuration parameters from the registry + +Arguments: + + Adapter Pointer to our adapter + WrapperConfigurationContext For use by NdisOpenConfiguration + + Should be called at IRQL = PASSIVE_LEVEL. + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_FAILURE + NDIS_STATUS_RESOURCES + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + NDIS_CONFIGURATION_OBJECT ConfigurationParameters; + NDIS_HANDLE ConfigurationHandle; + + DEBUGP(MP_TRACE, "[%p] ---> NICReadRegParameters\n", Adapter); + + PAGED_CODE(); + + // + // Open the registry for this adapter to read advanced + // configuration parameters stored by the INF file. + // + + NdisZeroMemory(&ConfigurationParameters, sizeof(ConfigurationParameters)); + + {C_ASSERT(sizeof(ConfigurationParameters) >= NDIS_SIZEOF_CONFIGURATION_OBJECT_REVISION_1);} + ConfigurationParameters.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT; + ConfigurationParameters.Header.Size = NDIS_SIZEOF_CONFIGURATION_OBJECT_REVISION_1; + ConfigurationParameters.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1; + + ConfigurationParameters.NdisHandle = Adapter->AdapterHandle; + ConfigurationParameters.Flags = 0; + + Status = NdisOpenConfigurationEx( + &ConfigurationParameters, + &ConfigurationHandle); + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] NdisOpenConfigurationEx Status = 0x%08x\n", Adapter, Status); + return NDIS_STATUS_FAILURE; + } + + + // + // Read all of our configuration parameters using NdisReadConfiguration + // and parse the value. + // + NICSetMacAddress(Adapter, ConfigurationHandle); + + Adapter->ulLinkSendSpeed = NIC_XMIT_SPEED; + Adapter->ulLinkRecvSpeed = NIC_RECV_SPEED; + + // + // Read VMQ related configuration parameters + // + Status = ReadRxQueueConfig(ConfigurationHandle, Adapter); + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] ReadRxQueueConfig Status = 0x%08x\n", Adapter, Status); + Status = NDIS_STATUS_FAILURE; + goto Exit; + } + + // + // Read NDIS QOS related configuration parameters + // + Status = ReadQOSConfig(ConfigurationHandle, Adapter); + if (Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] ReadQOSConfig Status = 0x%08x\n", Adapter, Status); + Status = NDIS_STATUS_FAILURE; + goto Exit; + } + +Exit: + // + // Close the configuration registry + // + NdisCloseConfiguration(ConfigurationHandle); + + DEBUGP(MP_TRACE, "[%p] <--- NICReadRegParameters Status = 0x%08x\n", Adapter, Status); + return Status; +} + + +VOID +NICSetMacAddress( + _In_ PMP_ADAPTER Adapter, + _In_ NDIS_HANDLE ConfigurationHandle) +/*++ +Routine Description: + + Configures the NIC with the correct permanent and current MAC addresses. + If no permanent address is saved, generate a new one. + + IRQL = PASSIVE_LEVEL + +Arguments: + + Adapter Pointer to our adapter + ConfigurationHandle NIC configuration from NdisOpenConfigurationEx + +Return Value: + + None. + +--*/ +{ + NDIS_STATUS Status; + PUCHAR NetworkAddress; + UINT Length = 0; + + + PAGED_CODE(); + + + HWReadPermanentMacAddress( + Adapter, + ConfigurationHandle, + + Adapter->PermanentAddress); + + + // + // Now seed the current MAC address with the permanent address. + // + NIC_COPY_ADDRESS(Adapter->CurrentAddress, Adapter->PermanentAddress); + + + // + // Read NetworkAddress registry value and use it as the current address + // if there is a software configurable NetworkAddress specified in + // the registry. + // + NdisReadNetworkAddress( + &Status, + &NetworkAddress, + &Length, + ConfigurationHandle); + + if ((Status == NDIS_STATUS_SUCCESS) && (Length == NIC_MACADDR_SIZE)) + { + if ((NIC_ADDR_IS_MULTICAST(NetworkAddress) + || NIC_ADDR_IS_BROADCAST(NetworkAddress)) + || !NIC_ADDR_IS_LOCALLY_ADMINISTERED(NetworkAddress)) + { + DEBUGP(MP_ERROR, "[%p] Overriding NetworkAddress is invalid: ", Adapter); + DbgPrintAddress(NetworkAddress); + } + else + { + NIC_COPY_ADDRESS(Adapter->CurrentAddress, NetworkAddress); + } + } + + DEBUGP(MP_LOUD, "[%p] Permanent Address = ", Adapter); + DbgPrintAddress(Adapter->PermanentAddress); + + DEBUGP(MP_LOUD, "[%p] Current Address = ", Adapter); + DbgPrintAddress(Adapter->CurrentAddress); +} + + +BOOLEAN +NICIsBusy( + _In_ PMP_ADAPTER Adapter) +/*++ +Routine Description: + + The NICIsBusy function returns whether the NIC has pending receives or sends. Before calling this + function, the NIC should be in a state where it will not receive new sends/receives (either datapath + is stopped, or !MP_IS_READY). Otherwise, the NIC may create new pending receives and sends after the + NICIsBusy call. + +Arguments: + + Adapter Pointer to our adapter + +Return Value: + + TRUE: Send or Receive path is not idle. + FALSE: Send & Receive paths are idle. + +--*/ +{ + BOOLEAN fBusy = FALSE; + DEBUGP(MP_TRACE, "[%p] ---> NICIsBusy\n", Adapter); + +#if DBG + // + // Check whether we might get new sends/receives + // + if(MPIsAdapterAttached(Adapter)) + { + // + // Adapter is still attached to the datapath, so it should not be ready (otherwise receives could get queued after we've read the counters) + // + ASSERT(!MP_IS_READY(Adapter)); + } +#endif + + // + // Check if all the NBLs that the protocol sent down for transmission have + // been completed yet. + // + if (Adapter->nBusySend) + { + DEBUGP(MP_INFO, "[%p] Send path is not idle, nBusySend = %d", Adapter, Adapter->nBusySend); + fBusy = TRUE; + } + else + { + // + // Check if all of our NBLs that we indicated up to the protocol have + // returned to us yet. + // + USHORT ReceiveBlockId = 0; + for(;ReceiveBlockIdReceiveBlock[ReceiveBlockId].PendingReceives); + fBusy = TRUE; + break; + } + } + } + + DEBUGP(MP_TRACE, "[%p] <--- NICIsBusy fBusy = %u\n", Adapter, (UINT)fBusy); + return fBusy; +} + + +NDIS_STATUS +NICInitializeReceiveBlock( + _In_ PMP_ADAPTER Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId) +/*++ +Routine Description: + + The NICInitializeReceiveBlock function initializes the fields of an adapter receive block. This block + is consumed by the owning receive DPC. + + +Arguments: + + Adapter - Pointer to our Adapter + BlockIndex - Receive block to initialize + +Return Value: + + NDIS_STATUS_xxx code. + +--*/ +{ + DEBUGP(MP_TRACE, "[%p] ---> NICInitializeReceiveBlock. Index: %i\n", Adapter, BlockId); + ASSERT(BlockId < NIC_SUPPORTED_NUM_QUEUES); + + NdisZeroMemory(&Adapter->ReceiveBlock[BlockId], sizeof(MP_ADAPTER_RECEIVE_BLOCK)); + NdisInitializeListHead(&Adapter->ReceiveBlock[BlockId].ReceiveList); + NdisAllocateSpinLock(&Adapter->ReceiveBlock[BlockId].ReceiveListLock); + + DEBUGP(MP_TRACE, "[%p] <--- NICInitializeReceiveBlock\n", Adapter); + + return NDIS_STATUS_SUCCESS; + +} + +VOID +NICFlushReceiveBlock( + _In_ PMP_ADAPTER Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId) +/*++ +Routine Description: + + This routine flushes all pending receives for a specific adapter receive block. + +Arguments: + + Adapter - Pointer to our adapter + BlockIndex - Receive block to flush +Return Value: + + None + +--*/ +{ + PLIST_ENTRY Entry; + PMP_ADAPTER_RECEIVE_BLOCK ReceiveBlock; + + DEBUGP(MP_TRACE, "[%p] ---> NICFlushReceiveBlock. QueueId: %i\n", Adapter, BlockId); + ASSERT(BlockId < NIC_SUPPORTED_NUM_QUEUES); + ReceiveBlock = &Adapter->ReceiveBlock[BlockId]; + + // + // Return any pending receives on the block + // + for(Entry = NdisInterlockedRemoveHeadList(&ReceiveBlock->ReceiveList, &ReceiveBlock->ReceiveListLock); + Entry; + Entry = NdisInterlockedRemoveHeadList(&ReceiveBlock->ReceiveList, &ReceiveBlock->ReceiveListLock)) + { + ReturnRCB(Adapter, CONTAINING_RECORD(Entry, RCB, RcbLink)); + } + + DEBUGP(MP_TRACE, "[%p] <-- NICFlushReceiveBlock\n", Adapter); + +} + +NDIS_STATUS +NICReferenceReceiveBlock( + _In_ PMP_ADAPTER Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId) +/*++ +Routine Description: + + This routine increments the pending receive count on a adapter receive block. This count is used + throughout the code to determine whether there are pending operations that will use the adapter's resources such as + receive buffers (which would pend operations such as adapter reset, VMQ queue free, etc...). + +Arguments: + + Adapter - Pointer to our adapter + BlockIndex - Receive block to flush + +Return Value: + + NDIS_STATUS_SUCCESS if reference was acquired succesfully. + NDIS_STATUS_ADAPTER_NOT_READY if the adapter state is such that we should not acquire new references to resources + +--*/ +{ + // + // Increment the reference count before checking NIC state avoid race conditions with code that checks reference count. The + // reference will be undone if the adapter is not ready. + // + ULONG RefCount = InterlockedIncrement(&Adapter->ReceiveBlock[BlockId].PendingReceives); + + // + // Make sure the increment happens before ready state check + // + KeMemoryBarrier(); + + // + // If the adapter is not ready, undo the reference and fail the call + // + if(!MP_IS_READY(Adapter)) + { + InterlockedDecrement(&Adapter->ReceiveBlock[BlockId].PendingReceives); + DEBUGP(MP_LOUD, "[%p] Could not acquire reference for ReceiveBlock %i. The adapter is not ready.\n", Adapter, BlockId); + return NDIS_STATUS_ADAPTER_NOT_READY; + } + + DEBUGP(MP_LOUD, "[%p] Acquired reference for ReceiveBlock %i. RefCount: %i.\n", Adapter, BlockId, RefCount); + + return NDIS_STATUS_SUCCESS; +} + +VOID +NICDereferenceReceiveBlock( + _In_ PMP_ADAPTER Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId, + _Out_opt_ ULONG *RefCount) +/*++ +Routine Description: + + This routine decrements the pending receive count on a adapter receive block. This count is used + throughout the code to determine whether there are pending operations that will use the adapter's resources such as + receive buffers (which would pend operations such as adapter reset, VMQ queue free, etc...). + +Arguments: + + Adapter - Pointer to our adapter + BlockIndex - Receive block to flush + +Return Value: + + None + +--*/ +{ + ULONG Count = InterlockedDecrement(&Adapter->ReceiveBlock[BlockId].PendingReceives); + DEBUGP(MP_LOUD, "[%p] Released reference for ReceiveBlock %i. RefCount: %i.\n", Adapter, BlockId, Count); + if(RefCount) + { + *RefCount = Count; + } +} diff --git a/network/ndis/netvmini/6x/adapter.h b/network/ndis/netvmini/6x/adapter.h new file mode 100644 index 00000000..7162fc02 --- /dev/null +++ b/network/ndis/netvmini/6x/adapter.h @@ -0,0 +1,376 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Adapter.H + +Abstract: + + This module contains structure definitons and function prototypes. + +Revision History: + +Notes: + +--*/ + + + + +// +// Utility macros +// ----------------------------------------------------------------------------- +// + +#define MP_SET_FLAG(_M, _F) ((_M)->Flags |= (_F)) +#define MP_CLEAR_FLAG(_M, _F) ((_M)->Flags &= ~(_F)) +#define MP_TEST_FLAG(_M, _F) (((_M)->Flags & (_F)) != 0) +#define MP_TEST_FLAGS(_M, _F) (((_M)->Flags & (_F)) == (_F)) + +#define MP_IS_READY(_M) (((_M)->Flags & \ + (fMP_DISCONNECTED \ + | fMP_RESET_IN_PROGRESS \ + | fMP_ADAPTER_HALT_IN_PROGRESS \ + | fMP_ADAPTER_PAUSE_IN_PROGRESS \ + | fMP_ADAPTER_PAUSED \ + | fMP_ADAPTER_LOW_POWER \ + )) == 0) + +// +// Each receive DPC is tracked by this structure, kept in a list in the MP_ADAPTER structure. +// It tracks the DPC object, the processor affinity of the DPC, and which MP_ADAPTER_RECEIVE_BLOCK +// indexes the DPC should consume for receive NBLs. +// +typedef struct _MP_ADAPTER_RECEIVE_DPC +{ + LIST_ENTRY Entry; + // + // Kernel DPC used for recieve + // + KDPC Dpc; + USHORT ProcessorGroup; + ULONG ProcessorNumber; + + // + // Tracks which receive blocks need to be recieved on this DPC. + // + BOOLEAN RecvBlock[NIC_SUPPORTED_NUM_QUEUES]; + volatile LONG RecvBlockCount; + + // + // Sets up the maximum amount of NBLs that can be indicated by a single + // receive block. This is initially NIC_MAX_RECVS_PER_INDICATE. + // + ULONG MaxNblCountPerIndicate; + + // + // Work item used if we need to avoid DPC timeout + // + NDIS_HANDLE WorkItem; + volatile LONG WorkItemQueued; + + // + // Pointer back to owner Adapter structure (accesed within work item) + // + struct _MP_ADAPTER *Adapter; + +} MP_ADAPTER_RECEIVE_DPC, * PMP_ADAPTER_RECEIVE_DPC; + +// +// This structure is used to track pending receives on the adpater (consumed by receive DPCs). +// One receive block maintained for each VMQ queue (if enabled), otherwise +// a single structure is used to track receives on the adapter. +// +typedef struct DECLSPEC_CACHEALIGN _MP_ADAPTER_RECEIVE_BLOCK +{ + // + // List of pending RCB blocks that need to be indicated up to NDIS + // + LIST_ENTRY ReceiveList; + NDIS_SPIN_LOCK ReceiveListLock; + volatile LONG PendingReceives; +} MP_ADAPTER_RECEIVE_BLOCK, * PMP_ADAPTER_RECEIVE_BLOCK; + +// +// Each adapter managed by this driver has a MP_ADAPTER struct. +// +typedef struct _MP_ADAPTER +{ + LIST_ENTRY List; + + // + // Keep track of various device objects. + // + PDEVICE_OBJECT Pdo; + PDEVICE_OBJECT Fdo; + PDEVICE_OBJECT NextDeviceObject; + + NDIS_HANDLE AdapterHandle; + + + // + // Status flags + // + +#define fMP_RESET_IN_PROGRESS 0x00000001 +#define fMP_DISCONNECTED 0x00000002 +#define fMP_ADAPTER_HALT_IN_PROGRESS 0x00000004 +#define fMP_ADAPTER_PAUSE_IN_PROGRESS 0x00000010 +#define fMP_ADAPTER_PAUSED 0x00000020 +#define fMP_ADAPTER_SURPRISE_REMOVED 0x00000100 +#define fMP_ADAPTER_LOW_POWER 0x00000200 + + ULONG Flags; + + + UCHAR PermanentAddress[NIC_MACADDR_SIZE]; + UCHAR CurrentAddress[NIC_MACADDR_SIZE]; + + // + // Send tracking + // ------------------------------------------------------------------------- + // + + // Pool of unused TCBs + PVOID TcbMemoryBlock; + + // List of unused TCBs (sliced out of TcbMemoryBlock) + LIST_ENTRY FreeTcbList; + NDIS_SPIN_LOCK FreeTcbListLock; + + // List of net buffers to send that are waiting for a free TCB + LIST_ENTRY SendWaitList; + NDIS_SPIN_LOCK SendWaitListLock; + + // List of TCBs that are being read by the NIC hardware + LIST_ENTRY BusyTcbList; + NDIS_SPIN_LOCK BusyTcbListLock; + + // A DPC that simulates interrupt processing for send completes + NDIS_HANDLE SendCompleteTimer; + + // + // Work item used if we need to avoid DPC timeout + // + NDIS_HANDLE SendCompleteWorkItem; + volatile LONG SendCompleteWorkItemQueued; + volatile BOOLEAN SendCompleteWorkItemRunning; + + + // Number of transmit NBLs from the protocol that we still have + volatile LONG nBusySend; + + // Spin lock to ensure only one CPU is sending at a time + KSPIN_LOCK SendPathSpinLock; + + + // + // Receive tracking + // ------------------------------------------------------------------------- + // + + // Pool of unused RCBs + PVOID RcbMemoryBlock; + + // List of unused RCBs (sliced out of RcbMemoryBlock) + LIST_ENTRY FreeRcbList; + NDIS_SPIN_LOCK FreeRcbListLock; + + NDIS_HANDLE RecvNblPoolHandle; + + // + // List of receive DPCs allocated for various ProcessorAffinity values (if only + // one needed, then only default is present in the list + // + LIST_ENTRY RecvDpcList; + NDIS_SPIN_LOCK RecvDpcListLock; + PMP_ADAPTER_RECEIVE_DPC DefaultRecvDpc; + + // + // Async pause and reset tracking + // ------------------------------------------------------------------------- + // + NDIS_HANDLE AsyncBusyCheckTimer; + LONG AsyncBusyCheckCount; + + + // + // NIC configuration + // ------------------------------------------------------------------------- + // + ULONG PacketFilter; + ULONG ulLookahead; + ULONG64 ulLinkSendSpeed; + ULONG64 ulLinkRecvSpeed; + ULONG ulMaxBusySends; + ULONG ulMaxBusyRecvs; + + // multicast list + ULONG ulMCListSize; + UCHAR MCList[NIC_MAX_MCAST_LIST][NIC_MACADDR_SIZE]; + + + // + // Statistics + // ------------------------------------------------------------------------- + // + + // Packet counts + ULONG64 FramesRxDirected; + ULONG64 FramesRxMulticast; + ULONG64 FramesRxBroadcast; + ULONG64 FramesTxDirected; + ULONG64 FramesTxMulticast; + ULONG64 FramesTxBroadcast; + + // Byte counts + ULONG64 BytesRxDirected; + ULONG64 BytesRxMulticast; + ULONG64 BytesRxBroadcast; + ULONG64 BytesTxDirected; + ULONG64 BytesTxMulticast; + ULONG64 BytesTxBroadcast; + + // Count of transmit errors + ULONG TxAbortExcessCollisions; + ULONG TxLateCollisions; + ULONG TxDmaUnderrun; + ULONG TxLostCRS; + ULONG TxOKButDeferred; + ULONG OneRetry; + ULONG MoreThanOneRetry; + ULONG TotalRetries; + ULONG TransmitFailuresOther; + + // Count of receive errors + ULONG RxCrcErrors; + ULONG RxAlignmentErrors; + ULONG RxResourceErrors; + ULONG RxDmaOverrunErrors; + ULONG RxCdtFrames; + ULONG RxRuntErrors; + + // + // Reference to the allocated root of MP_ADAPTER memory, which may not be cache aligned. + // When allocating, the pointer returned will be UnalignedBuffer + an offset that will make + // the base pointer cache aligned. + // + PVOID UnalignedAdapterBuffer; + ULONG UnalignedAdapterBufferSize; + + // + // Tracks any pending NBLs for the particular receiver (either + // 0 for non-VMQ scenarios, or the corresponding VMQ queue). These + // are consumed by the receive DPCs. + // + MP_ADAPTER_RECEIVE_BLOCK ReceiveBlock[NIC_SUPPORTED_NUM_QUEUES]; + + // + // An OID request that could not be fulfulled at the time of the call. These OIDs are serialized + // so we will not receive new queue management OID's until this one is complete. + // Currently this is used only for freeing a Queue (which may still have outstanding references) + // + PNDIS_OID_REQUEST PendingRequest; + + NDIS_DEVICE_POWER_STATE CurrentPowerState; + +#if (NDIS_SUPPORT_NDIS620) + + // + // VMQ related data + // + MP_ADAPTER_VMQ_DATA VMQData; + +#endif + +#if (NDIS_SUPPORT_NDIS630) + + // + // NDIS QoS related data + // + MP_ADAPTER_QOS_DATA QOSData; + +#endif + +} MP_ADAPTER, *PMP_ADAPTER; + +#define MP_ADAPTER_FROM_CONTEXT(_ctx_) ((PMP_ADAPTER)(_ctx_)) + +PMP_ADAPTER_RECEIVE_DPC +NICAllocReceiveDpc( + _In_ PMP_ADAPTER Adapter, + ULONG ProcessorNumber, + USHORT ProcessorGroup, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId); + +VOID +NICReceiveDpcRemoveOwnership( + _In_ PMP_ADAPTER_RECEIVE_DPC ReceiveDpc, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId); + +PMP_ADAPTER_RECEIVE_DPC +NICGetDefaultReceiveDpc( + _In_ PMP_ADAPTER Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId); + +NDIS_STATUS +NICAllocRCBData( + _In_ PMP_ADAPTER Adapter, + ULONG NumberOfRcbs, + _Outptr_result_bytebuffer_(NumberOfRcbs * sizeof(RCB)) PVOID *RcbMemoryBlock, + _Inout_ PLIST_ENTRY FreeRcbList, + _Inout_ PNDIS_SPIN_LOCK FreeRcbListLock, + _Inout_ PNDIS_HANDLE RecvNblPoolHandle); + +NDIS_STATUS +NICInitializeReceiveBlock( + _In_ PMP_ADAPTER Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId); + +VOID +NICFlushReceiveBlock( + _In_ PMP_ADAPTER Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId); + +NDIS_STATUS +NICReferenceReceiveBlock( + _In_ PMP_ADAPTER Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId); + +VOID +NICDereferenceReceiveBlock( + _In_ PMP_ADAPTER Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId, + _Out_opt_ ULONG *RefCount); + + +BOOLEAN +NICIsBusy( + _In_ PMP_ADAPTER Adapter); + + +#define RECEIVE_BLOCK_REFERENCE_COUNT(Adapter, BlockIndex) Adapter->ReceiveBlock[BlockIndex].PendingReceives +#define RECEIVE_BLOCK_IS_BUSY(Adapter, BlockIndex) Adapter->ReceiveBlock[BlockIndex].PendingReceives!=0 + +// Prototypes for standard NDIS miniport entry points +MINIPORT_INITIALIZE MPInitializeEx; +MINIPORT_HALT MPHaltEx; +MINIPORT_UNLOAD DriverUnload; +MINIPORT_PAUSE MPPause; +MINIPORT_RESTART MPRestart; +MINIPORT_SEND_NET_BUFFER_LISTS MPSendNetBufferLists; +MINIPORT_RETURN_NET_BUFFER_LISTS MPReturnNetBufferLists; +MINIPORT_CANCEL_SEND MPCancelSend; +MINIPORT_CHECK_FOR_HANG MPCheckForHangEx; +MINIPORT_RESET MPResetEx; +MINIPORT_DEVICE_PNP_EVENT_NOTIFY MPDevicePnpEventNotify; +MINIPORT_SHUTDOWN MPShutdownEx; +MINIPORT_CANCEL_OID_REQUEST MPCancelOidRequest; + diff --git a/network/ndis/netvmini/6x/ctrlpath.c b/network/ndis/netvmini/6x/ctrlpath.c new file mode 100644 index 00000000..01969705 --- /dev/null +++ b/network/ndis/netvmini/6x/ctrlpath.c @@ -0,0 +1,1755 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + CtrlPath.c + +Abstract: + + This module implements the miniport's control path. It contains the main + miniport entrypoint for OID handling. + +--*/ + + +#include "netvmin6.h" +#include "ctrlpath.tmh" + + +static +NDIS_STATUS +MPMethodRequest( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisRequest); + +static +NDIS_STATUS +MPSetInformation( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest); + +static +NDIS_STATUS +MPQueryInformation( + _In_ PMP_ADAPTER Adapter, + _Inout_ PNDIS_OID_REQUEST NdisQueryRequest); + +static +NDIS_STATUS +NICSetMulticastList( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest); + +static +NDIS_STATUS +NICSetPacketFilter( + _In_ PMP_ADAPTER Adapter, + _In_ ULONG PacketFilter); + +#if (NDIS_SUPPORT_NDIS620) + +static +NDIS_STATUS +NICAllocateRxQueue( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisMethodRequest); + +static +NDIS_STATUS +NICCompleteAllocationRxQueue( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisMethodRequest); + +static +NDIS_STATUS +NICFreeRxQueue( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest); + +static +NDIS_STATUS +NICSetRxFilter( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisMethodRequest); + +static +NDIS_STATUS +NICClearRxFilter( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest); + +static +NDIS_STATUS +NICUpdateRxQueue( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest); + +_IRQL_requires_(PASSIVE_LEVEL) +static +NDIS_STATUS +NICSetQOSParameters( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisMethodRequest); + +#pragma NDIS_PAGEABLE_FUNCTION(NICFreeRxQueue) +#pragma NDIS_PAGEABLE_FUNCTION(NICClearRxFilter) +#pragma NDIS_PAGEABLE_FUNCTION(NICUpdateRxQueue) +#pragma NDIS_PAGEABLE_FUNCTION(NICAllocateRxQueue) +#pragma NDIS_PAGEABLE_FUNCTION(NICCompleteAllocationRxQueue) +#pragma NDIS_PAGEABLE_FUNCTION(NICSetRxFilter) +#pragma NDIS_PAGEABLE_FUNCTION(NICSetQOSParameters) + +#endif + +static +NDIS_STATUS +MPSetPower( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest); + +static +NDIS_STATUS +MPSetPowerD0( + _In_ PMP_ADAPTER Adapter); + +static +NDIS_STATUS +MPSetPowerLow( + _In_ PMP_ADAPTER Adapter, + _In_ NDIS_DEVICE_POWER_STATE PowerState); + + +#pragma NDIS_PAGEABLE_FUNCTION(MPOidRequest) +#pragma NDIS_PAGEABLE_FUNCTION(MPQueryInformation) +#pragma NDIS_PAGEABLE_FUNCTION(MPSetInformation) +#pragma NDIS_PAGEABLE_FUNCTION(MPMethodRequest) +#pragma NDIS_PAGEABLE_FUNCTION(MPSetPower) +#pragma NDIS_PAGEABLE_FUNCTION(MPSetPowerD0) +#pragma NDIS_PAGEABLE_FUNCTION(MPSetPowerLow) +#pragma NDIS_PAGEABLE_FUNCTION(NICSetMulticastList) +#pragma NDIS_PAGEABLE_FUNCTION(NICSetPacketFilter) + + +NDIS_STATUS +MPOidRequest( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNDIS_OID_REQUEST NdisRequest) +/*++ + +Routine Description: + + Entry point called by NDIS to get or set the value of a specified OID. + +Arguments: + + MiniportAdapterContext - Our adapter handle + NdisRequest - The OID request to handle + +Return Value: + + Return code from the NdisRequest below. + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + + PAGED_CODE(); + + DEBUGP(MP_LOUD, "[%p] ---> MPOidRequest\n", Adapter); + + + switch (NdisRequest->RequestType) + { + case NdisRequestMethod: + Status = MPMethodRequest(Adapter, NdisRequest); + break; + + case NdisRequestSetInformation: + Status = MPSetInformation(Adapter, NdisRequest); + break; + + case NdisRequestQueryInformation: + case NdisRequestQueryStatistics: + Status = MPQueryInformation(Adapter, NdisRequest); + break; + + default: + // + // The entry point may by used by other requests + // + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + + DEBUGP(MP_LOUD, "[%p] <--- MPOidRequest Status = 0x%08x\n", Adapter, Status); + return Status; +} + + +VOID +MPCancelOidRequest( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PVOID RequestId) +/*++ + +Routine Description: + + Entry point called by NDIS to abort an asynchronous OID request. + +Arguments: + + MiniportAdapterContext - Our adapter handle + RequestId - An identifier that corresponds to the RequestId + field of the NDIS_OID_REQUEST + +Return Value: + + None. + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + + DEBUGP(MP_LOUD, "[%p] ---> MPCancelOidRequest", Adapter); + UNREFERENCED_PARAMETER(Adapter); + UNREFERENCED_PARAMETER(RequestId); + + + // + // This miniport sample does not pend any OID requests, so we don't have + // to worry about cancelling them. + // + + DEBUGP(MP_LOUD, "[%p] <--- MPCancelOidRequest", Adapter); +} + + + +NDIS_STATUS +MPQueryInformation( + _In_ PMP_ADAPTER Adapter, + _Inout_ PNDIS_OID_REQUEST NdisQueryRequest) +/*++ + +Routine Description: + + Helper function to perform a query OID request + +Arguments: + + Adapter - + NdisQueryRequest - The OID that is being queried + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _QUERY *Query = &NdisQueryRequest->DATA.QUERY_INFORMATION; + + NDIS_HARDWARE_STATUS HardwareStatus = NdisHardwareStatusReady; + UCHAR VendorDesc[] = NIC_VENDOR_DESC; + NDIS_MEDIUM Medium = NIC_MEDIUM_TYPE; + ULONG ulInfo; + USHORT usInfo; + ULONG64 ulInfo64; + + // Default to returning the ULONG value + PVOID pInfo=NULL; + ULONG ulInfoLen = sizeof(ulInfo); + + PAGED_CODE(); + + DEBUGP(MP_LOUD, "[%p] ---> MPQueryInformation ", Adapter); + DbgPrintOidName(Query->Oid); + + + switch(Query->Oid) + { + case OID_GEN_HARDWARE_STATUS: + // + // Specify the current hardware status of the underlying NIC as + // one of the following NDIS_HARDWARE_STATUS-type values. + // + pInfo = (PVOID) &HardwareStatus; + ulInfoLen = sizeof(NDIS_HARDWARE_STATUS); + break; + + case OID_GEN_MAXIMUM_TOTAL_SIZE: + // + // Specify the maximum total packet length, in bytes, the NIC + // supports including the header. A protocol driver might use + // this returned length as a gauge to determine the maximum + // size packet that a NIC driver could forward to the + // protocol driver. The miniport driver must never indicate + // up to the bound protocol driver packets received over the + // network that are longer than the packet size specified by + // OID_GEN_MAXIMUM_TOTAL_SIZE. + // + + __fallthrough; + + case OID_GEN_TRANSMIT_BLOCK_SIZE: + // + // The OID_GEN_TRANSMIT_BLOCK_SIZE OID specifies the minimum + // number of bytes that a single net packet occupies in the + // transmit buffer space of the NIC. For example, a NIC that + // has a transmit space divided into 256-byte pieces would have + // a transmit block size of 256 bytes. To calculate the total + // transmit buffer space on such a NIC, its driver multiplies + // the number of transmit buffers on the NIC by its transmit + // block size. In our case, the transmit block size is + // identical to its maximum packet size. + + __fallthrough; + + case OID_GEN_RECEIVE_BLOCK_SIZE: + // + // The OID_GEN_RECEIVE_BLOCK_SIZE OID specifies the amount of + // storage, in bytes, that a single packet occupies in the receive + // buffer space of the NIC. + // + + ulInfo = (ULONG) HW_MAX_FRAME_SIZE; + pInfo = &ulInfo; + break; + + case OID_GEN_TRANSMIT_BUFFER_SPACE: + // + // Specify the amount of memory, in bytes, on the NIC that + // is available for buffering transmit data. A protocol can + // use this OID as a guide for sizing the amount of transmit + // data per send. + // + + ulInfo = HW_MAX_FRAME_SIZE * Adapter->ulMaxBusySends; + pInfo = &ulInfo; + break; + + case OID_GEN_RECEIVE_BUFFER_SPACE: + // + // Specify the amount of memory on the NIC that is available + // for buffering receive data. A protocol driver can use this + // OID as a guide for advertising its receive window after it + // establishes sessions with remote nodes. + // + + ulInfo = HW_MAX_FRAME_SIZE * Adapter->ulMaxBusyRecvs; + pInfo = &ulInfo; + break; + + case OID_GEN_MEDIA_SUPPORTED: + // + // Return an array of media that are supported by the miniport. + // This miniport only supports one medium (Ethernet), so the OID + // returns identical results to OID_GEN_MEDIA_IN_USE. + // + + __fallthrough; + + case OID_GEN_MEDIA_IN_USE: + // + // Return an array of media that are currently in use by the + // miniport. This array should be a subset of the array returned + // by OID_GEN_MEDIA_SUPPORTED. + // + pInfo = &Medium; + ulInfoLen = sizeof(Medium); + break; + + case OID_GEN_MAXIMUM_SEND_PACKETS: + ulInfo = NIC_MAX_BUSY_SENDS; + pInfo = &ulInfo; + break; + + case OID_GEN_XMIT_ERROR: + ulInfo = (ULONG) + (Adapter->TxAbortExcessCollisions + + Adapter->TxDmaUnderrun + + Adapter->TxLostCRS + + Adapter->TxLateCollisions+ + Adapter->TransmitFailuresOther); + pInfo = &ulInfo; + break; + + case OID_GEN_RCV_ERROR: + ulInfo = (ULONG) + (Adapter->RxCrcErrors + + Adapter->RxAlignmentErrors + + Adapter->RxDmaOverrunErrors + + Adapter->RxRuntErrors); + pInfo = &ulInfo; + break; + + case OID_GEN_RCV_DISCARDS: + ulInfo = (ULONG)Adapter->RxResourceErrors; + pInfo = &ulInfo; + break; + + case OID_GEN_RCV_NO_BUFFER: + ulInfo = (ULONG) + Adapter->RxResourceErrors; + pInfo = &ulInfo; + break; + + case OID_GEN_VENDOR_ID: + // + // Specify a three-byte IEEE-registered vendor code, followed + // by a single byte that the vendor assigns to identify a + // particular NIC. The IEEE code uniquely identifies the vendor + // and is the same as the three bytes appearing at the beginning + // of the NIC hardware address. Vendors without an IEEE-registered + // code should use the value 0xFFFFFF. + // + + ulInfo = NIC_VENDOR_ID; + pInfo = &ulInfo; + break; + + case OID_GEN_VENDOR_DESCRIPTION: + // + // Specify a zero-terminated string describing the NIC vendor. + // + pInfo = VendorDesc; + ulInfoLen = sizeof(VendorDesc); + break; + + case OID_GEN_VENDOR_DRIVER_VERSION: + // + // Specify the vendor-assigned version number of the NIC driver. + // The low-order half of the return value specifies the minor + // version; the high-order half specifies the major version. + // + + ulInfo = NIC_VENDOR_DRIVER_VERSION; + pInfo = &ulInfo; + break; + + case OID_GEN_DRIVER_VERSION: + // + // Specify the NDIS version in use by the NIC driver. The high + // byte is the major version number; the low byte is the minor + // version number. + // + usInfo = (USHORT) (MP_NDIS_MAJOR_VERSION<<8) + MP_NDIS_MINOR_VERSION; + pInfo = (PVOID) &usInfo; + ulInfoLen = sizeof(USHORT); + break; + +#if (NDIS_SUPPORT_NDIS61 && !NDIS_SUPPORT_NDIS620) + case OID_PNP_CAPABILITIES: + // + // This OID is obsolete for NDIS 6.20 drivers + // + // Return the wake-up capabilities of its NIC. If you return + // NDIS_STATUS_NOT_SUPPORTED, NDIS considers the miniport driver + // to be not Power management aware and doesn't send any power + // or wake-up related queries such as + // OID_PNP_SET_POWER, OID_PNP_QUERY_POWER, + // OID_PNP_ADD_WAKE_UP_PATTERN, OID_PNP_REMOVE_WAKE_UP_PATTERN, + // OID_PNP_ENABLE_WAKE_UP. + // + Status = NDIS_STATUS_NOT_SUPPORTED; + break; +#endif + // + // Following 4 OIDs are for querying Ethernet Operational + // Characteristics. + // + case OID_802_3_PERMANENT_ADDRESS: + // + // Return the MAC address of the NIC burnt in the hardware. + // + pInfo = Adapter->PermanentAddress; + ulInfoLen = NIC_MACADDR_SIZE; + break; + + case OID_802_3_CURRENT_ADDRESS: + // + // Return the MAC address the NIC is currently programmed to + // use. Note that this address could be different from the + // permananent address as the user can override using + // registry. Read NdisReadNetworkAddress doc for more info. + // + pInfo = Adapter->CurrentAddress; + ulInfoLen = NIC_MACADDR_SIZE; + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + // + // The maximum number of multicast addresses the NIC driver + // can manage. This list is global for all protocols bound + // to (or above) the NIC. Consequently, a protocol can receive + // NDIS_STATUS_MULTICAST_FULL from the NIC driver when + // attempting to set the multicast address list, even if + // the number of elements in the given list is less than + // the number originally returned for this query. + // + + ulInfo = NIC_MAX_MCAST_LIST; + pInfo = &ulInfo; + break; + + // + // Following list consists of both general and Ethernet + // specific statistical OIDs. + // + + case OID_GEN_XMIT_OK: + ulInfo64 = Adapter->FramesTxBroadcast + + Adapter->FramesTxMulticast + + Adapter->FramesTxDirected; + pInfo = &ulInfo64; + if (Query->InformationBufferLength >= sizeof(ULONG64) || + Query->InformationBufferLength == 0) + { + ulInfoLen = sizeof(ULONG64); + } + else + { + ulInfoLen = sizeof(ULONG); + } + // We should always report that only 8 bytes are required to keep ndistest happy + Query->BytesNeeded = sizeof(ULONG64); + break; + + case OID_GEN_RCV_OK: + ulInfo64 = Adapter->FramesRxBroadcast + + Adapter->FramesRxMulticast + + Adapter->FramesRxDirected; + pInfo = &ulInfo64; + if (Query->InformationBufferLength >= sizeof(ULONG64) || + Query->InformationBufferLength == 0) + { + ulInfoLen = sizeof(ULONG64); + } + else + { + ulInfoLen = sizeof(ULONG); + } + // We should always report that only 8 bytes are required to keep ndistest happy + Query->BytesNeeded = sizeof(ULONG64); + break; + + case OID_GEN_STATISTICS: + + if (Query->InformationBufferLength < sizeof(NDIS_STATISTICS_INFO)) + { + Status = NDIS_STATUS_INVALID_LENGTH; + Query->BytesNeeded = sizeof(NDIS_STATISTICS_INFO); + break; + } + else + { + PNDIS_STATISTICS_INFO Statistics = (PNDIS_STATISTICS_INFO)Query->InformationBuffer; + + {C_ASSERT(sizeof(NDIS_STATISTICS_INFO) >= NDIS_SIZEOF_STATISTICS_INFO_REVISION_1);} + Statistics->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + Statistics->Header.Size = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1; + Statistics->Header.Revision = NDIS_STATISTICS_INFO_REVISION_1; + + Statistics->SupportedStatistics = NIC_SUPPORTED_STATISTICS; + + /* Bytes in */ + Statistics->ifHCInOctets = + Adapter->BytesRxDirected + + Adapter->BytesRxMulticast + + Adapter->BytesRxBroadcast; + + Statistics->ifHCInUcastOctets = + Adapter->BytesRxDirected; + + Statistics->ifHCInMulticastOctets = + Adapter->BytesRxMulticast; + + Statistics->ifHCInBroadcastOctets = + Adapter->BytesRxBroadcast; + + /* Packets in */ + Statistics->ifHCInUcastPkts = + Adapter->FramesRxDirected; + + Statistics->ifHCInMulticastPkts = + Adapter->FramesRxMulticast; + + Statistics->ifHCInBroadcastPkts = + Adapter->FramesRxBroadcast; + + /* Errors in */ + Statistics->ifInErrors = + Adapter->RxCrcErrors + + Adapter->RxAlignmentErrors + + Adapter->RxDmaOverrunErrors + + Adapter->RxRuntErrors; + + Statistics->ifInDiscards = + Adapter->RxResourceErrors; + + + /* Bytes out */ + Statistics->ifHCOutOctets = + Adapter->BytesTxDirected + + Adapter->BytesTxMulticast + + Adapter->BytesTxBroadcast; + + Statistics->ifHCOutUcastOctets = + Adapter->BytesTxDirected; + + Statistics->ifHCOutMulticastOctets = + Adapter->BytesTxMulticast; + + Statistics->ifHCOutBroadcastOctets = + Adapter->BytesTxBroadcast; + + /* Packets out */ + Statistics->ifHCOutUcastPkts = + Adapter->FramesTxDirected; + + Statistics->ifHCOutMulticastPkts = + Adapter->FramesTxMulticast; + + Statistics->ifHCOutBroadcastPkts = + Adapter->FramesTxBroadcast; + + /* Errors out */ + Statistics->ifOutErrors = + Adapter->TxAbortExcessCollisions + + Adapter->TxDmaUnderrun + + Adapter->TxLostCRS + + Adapter->TxLateCollisions+ + Adapter->TransmitFailuresOther; + + Statistics->ifOutDiscards = + 0ULL; + + ulInfoLen = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1; + } + + break; + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + + ulInfo = Adapter->nBusySend; + pInfo = &ulInfo; + break; + + case OID_802_3_RCV_ERROR_ALIGNMENT: + + ulInfo = Adapter->RxAlignmentErrors; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_ONE_COLLISION: + + ulInfo = Adapter->OneRetry; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_MORE_COLLISIONS: + + ulInfo = Adapter->MoreThanOneRetry; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_DEFERRED: + + ulInfo = Adapter->TxOKButDeferred; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_MAX_COLLISIONS: + + ulInfo = Adapter->TxAbortExcessCollisions; + pInfo = &ulInfo; + break; + + case OID_802_3_RCV_OVERRUN: + + ulInfo = Adapter->RxDmaOverrunErrors; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_UNDERRUN: + + ulInfo = Adapter->TxDmaUnderrun; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + + ulInfo = Adapter->TxLostCRS; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_TIMES_CRS_LOST: + + ulInfo = Adapter->TxLostCRS; + pInfo = &ulInfo; + break; + + case OID_802_3_XMIT_LATE_COLLISIONS: + + ulInfo = Adapter->TxLateCollisions; + pInfo = &ulInfo; + break; + + case OID_GEN_INTERRUPT_MODERATION: + { + PNDIS_INTERRUPT_MODERATION_PARAMETERS Moderation = (PNDIS_INTERRUPT_MODERATION_PARAMETERS)Query->InformationBuffer; + Moderation->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + Moderation->Header.Revision = NDIS_INTERRUPT_MODERATION_PARAMETERS_REVISION_1; + Moderation->Header.Size = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1; + Moderation->Flags = 0; + Moderation->InterruptModeration = NdisInterruptModerationNotSupported; + ulInfoLen = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1; + } + break; + + case OID_PNP_QUERY_POWER: + // simply succeed this. + break; + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + if (Status == NDIS_STATUS_SUCCESS) + { + ASSERT(ulInfoLen > 0); + + if (ulInfoLen <= Query->InformationBufferLength) + { + if(pInfo) + { + // Copy result into InformationBuffer + NdisMoveMemory(Query->InformationBuffer, pInfo, ulInfoLen); + } + Query->BytesWritten = ulInfoLen; + } + else + { + // too short + Query->BytesNeeded = ulInfoLen; + Status = NDIS_STATUS_BUFFER_TOO_SHORT; + } + } + + + DEBUGP(MP_LOUD, "[%p] <--- MPQueryInformation Status = 0x%08x\n", Adapter, Status); + return Status; +} + + +NDIS_STATUS +MPSetInformation( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest) +/*++ + +Routine Description: + + Helper function to perform a set OID request + +Arguments: + + Adapter - + NdisSetRequest - The OID to set + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _SET *Set = &NdisSetRequest->DATA.SET_INFORMATION; + + PAGED_CODE(); + + DEBUGP(MP_LOUD, "[%p] ---> MPSetInformation ", Adapter); + DbgPrintOidName(Set->Oid); + + + switch(Set->Oid) + { + case OID_802_3_MULTICAST_LIST: + // + // Set the multicast address list on the NIC for packet reception. + // The NIC driver can set a limit on the number of multicast + // addresses bound protocol drivers can enable simultaneously. + // NDIS returns NDIS_STATUS_MULTICAST_FULL if a protocol driver + // exceeds this limit or if it specifies an invalid multicast + // address. + // + Status = NICSetMulticastList(Adapter, NdisSetRequest); + + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + // + // Program the hardware to indicate the packets + // of certain filter types. + // + if(Set->InformationBufferLength != sizeof(ULONG)) + { + Set->BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + Set->BytesRead = Set->InformationBufferLength; + + Status = NICSetPacketFilter( + Adapter, + *((PULONG)Set->InformationBuffer)); + + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + // + // A protocol driver can set a suggested value for the number + // of bytes to be used in its binding; however, the underlying + // NIC driver is never required to limit its indications to + // the value set. + // + if (Set->InformationBufferLength != sizeof(ULONG)) + { + Set->BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + Adapter->ulLookahead = *(PULONG)Set->InformationBuffer; + + Set->BytesRead = sizeof(ULONG); + Status = NDIS_STATUS_SUCCESS; + break; + +#if (NDIS_SUPPORT_NDIS620) + + case OID_RECEIVE_FILTER_FREE_QUEUE: + // + // Free the requested receive queue. + // + Status = NICFreeRxQueue( + Adapter, + NdisSetRequest); + break; + + case OID_RECEIVE_FILTER_CLEAR_FILTER: + // + // Remove the requested filter on the requested receive queue. + // + Status = NICClearRxFilter( + Adapter, + NdisSetRequest); + break; + + case OID_RECEIVE_FILTER_QUEUE_PARAMETERS: + // + // Update the queue information. + // + Status = NICUpdateRxQueue( + Adapter, + NdisSetRequest); + break; +#endif + + case OID_PNP_SET_POWER: + // + // Update power state + // + Status = MPSetPower( + Adapter, + NdisSetRequest); + break; + +#if (NDIS_SUPPORT_NDIS620) + case OID_PM_ADD_WOL_PATTERN: + case OID_PM_REMOVE_WOL_PATTERN: + case OID_PM_ADD_PROTOCOL_OFFLOAD: + case OID_PM_REMOVE_PROTOCOL_OFFLOAD: + case OID_PM_PARAMETERS: + +#else if (NDIS_SUPPORT_NDIS61) + case OID_PNP_ADD_WAKE_UP_PATTERN: + case OID_PNP_REMOVE_WAKE_UP_PATTERN: + case OID_PNP_ENABLE_WAKE_UP: +#endif + ASSERT(!"NIC does not support wake on LAN OIDs"); + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + if(Status == NDIS_STATUS_SUCCESS) + { + Set->BytesRead = Set->InformationBufferLength; + } + + + DEBUGP(MP_LOUD, "[%p] <--- MPSetInformation Status = 0x%08x\n", Adapter, Status); + + return Status; +} + + +NDIS_STATUS +MPMethodRequest( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisRequest) +/*++ +Routine Description: + + Helper function to perform a WMI OID request + +Arguments: + + Adapter - + NdisRequest - THe WMI OID request + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_NOT_SUPPORTED + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NDIS_OID Oid = NdisRequest->DATA.METHOD_INFORMATION.Oid; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(Adapter); + UNREFERENCED_PARAMETER(NdisRequest); + + DEBUGP(MP_LOUD, "[%p] ---> MPMethodRequest ", Adapter); + DbgPrintOidName(Oid); + + + switch (Oid) + { + +#if (NDIS_SUPPORT_NDIS620) + case OID_RECEIVE_FILTER_ALLOCATE_QUEUE: + // + // Allocate the requested receive queue. + // + Status = NICAllocateRxQueue( + Adapter, + NdisRequest); + + break; + + case OID_RECEIVE_FILTER_QUEUE_ALLOCATION_COMPLETE: + // + // Complete any remaining allocation for receive queues. + // + Status = NICCompleteAllocationRxQueue( + Adapter, + NdisRequest); + + break; + + case OID_RECEIVE_FILTER_SET_FILTER: + // + // Add a filter to the requested queue. + // + Status = NICSetRxFilter( + Adapter, + NdisRequest); + break; + +#endif + +#if (NDIS_SUPPORT_NDIS630) + case OID_QOS_PARAMETERS: + // + // Set NDIS QoS configuration parameters. + // + Status = NICSetQOSParameters( + Adapter, + NdisRequest); + break; + +#endif + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + + DEBUGP(MP_LOUD, "[%p] <--- MPMethodRequest Status = 0x%08x\n", Adapter, Status); + return Status; +} + + + + +NDIS_STATUS +NICSetPacketFilter( + _In_ PMP_ADAPTER Adapter, + _In_ ULONG PacketFilter) +/*++ +Routine Description: + + This routine will set up the adapter so that it accepts packets + that match the specified packet filter. + +Arguments: + + Adapter - pointer to adapter block + PacketFilter - the new packet filter + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_NOT_SUPPORTED + +--*/ + +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + PAGED_CODE(); + + DEBUGP(MP_TRACE, "[%p] ---> NICSetPacketFilter\n", Adapter); + + + // any bits not supported? + if (PacketFilter & ~(NIC_SUPPORTED_FILTERS)) + { + DEBUGP(MP_WARNING, "[%p] Unsupported packet filter: 0x%08x\n", Adapter, PacketFilter); + return NDIS_STATUS_NOT_SUPPORTED; + } + + // any filtering changes? + if (PacketFilter != Adapter->PacketFilter) + { + // + // Change the filtering modes on hardware + // + + + // Save the new packet filter value + Adapter->PacketFilter = PacketFilter; + } + + + DEBUGP(MP_TRACE, "[%p] <--- NICSetPacketFilter Status = 0x%08x\n", Adapter, Status); + + return Status; +} + + +NDIS_STATUS +NICSetMulticastList( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest) +/*++ +Routine Description: + + This routine will set up the adapter for a specified multicast + address list. + +Arguments: + + Adapter - Pointer to adapter block + NdisSetRequest - The OID request with the new multicast list + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _SET *Set = &NdisSetRequest->DATA.SET_INFORMATION; + +#if DBG + ULONG index; +#endif + + PAGED_CODE(); + + DEBUGP(MP_TRACE, "[%p] ---> NICSetMulticastList\n", Adapter); + + + // + // Initialize. + // + Set->BytesNeeded = NIC_MACADDR_SIZE; + Set->BytesRead = Set->InformationBufferLength; + + do + { + if (Set->InformationBufferLength % NIC_MACADDR_SIZE) + { + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (Set->InformationBufferLength > (NIC_MAX_MCAST_LIST * NIC_MACADDR_SIZE)) + { + Status = NDIS_STATUS_MULTICAST_FULL; + Set->BytesNeeded = NIC_MAX_MCAST_LIST * NIC_MACADDR_SIZE; + break; + } + + // + // Protect the list update with a lock if it can be updated by + // another thread simultaneously. + // + + NdisZeroMemory(Adapter->MCList, + NIC_MAX_MCAST_LIST * NIC_MACADDR_SIZE); + + NdisMoveMemory(Adapter->MCList, + Set->InformationBuffer, + Set->InformationBufferLength); + + Adapter->ulMCListSize = Set->InformationBufferLength / NIC_MACADDR_SIZE; + +#if DBG + // display the multicast list + for(index = 0; index < Adapter->ulMCListSize; index++) + { + DEBUGP(MP_LOUD, "[%p] MC(%d) = ", Adapter, index); + DbgPrintAddress(Adapter->MCList[index]); + } +#endif + } + while (FALSE); + + + // + // Program the hardware to add suport for these muticast addresses + // + + + DEBUGP(MP_TRACE, "[%p] <--- NICSetMulticastList Status 0x%08x\n", Adapter, Status); + + return Status; +} + +#if (NDIS_SUPPORT_NDIS620) + +#define VERIFY_OID_SET(_Request, _MinRevision, _MinLength)\ + _Request->DATA.SET_INFORMATION.BytesNeeded = _MinLength;\ + if(_Request->Header.Revision < _MinRevision)\ + { \ + Status = NDIS_STATUS_NOT_SUPPORTED;\ + break;\ + }\ + if(_Request->DATA.SET_INFORMATION.InformationBufferLength < _MinLength)\ + {\ + Status = NDIS_STATUS_INVALID_LENGTH;\ + break;\ + }\ + +#define VERIFY_OID_METHOD(_Request, _MinRevision, _MinLength)\ + _Request->DATA.METHOD_INFORMATION.BytesNeeded = _MinLength;\ + if(_Request->Header.Revision < _MinRevision)\ + { \ + Status = NDIS_STATUS_NOT_SUPPORTED;\ + break;\ + }\ + if(_Request->DATA.METHOD_INFORMATION.InputBufferLength < _MinLength)\ + {\ + Status = NDIS_STATUS_INVALID_LENGTH;\ + break;\ + }\ + + +NDIS_STATUS +NICAllocateRxQueue( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisMethodRequest) +/*++ +Routine Description: + + This routine will allocate a receive queue according to the passed in allocation request. It verifies that the request + is well formed, then passes the request to underlying queue management code. + +Arguments: + + Adapter - Pointer to adapter block + NdisSetRequest - The OID data for the request + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _METHOD *Method = &NdisMethodRequest->DATA.METHOD_INFORMATION; + PNDIS_RECEIVE_QUEUE_PARAMETERS QueueParams = (PNDIS_RECEIVE_QUEUE_PARAMETERS)Method->InformationBuffer; + + PAGED_CODE(); + + DEBUGP(MP_TRACE, "[%p] ---> NICAllocateRxQueue\n", Adapter); + + do + { + // + // Verify that the request matches our requirements + // + VERIFY_OID_METHOD(NdisMethodRequest, + NDIS_RECEIVE_QUEUE_PARAMETERS_REVISION_1, + NDIS_SIZEOF_RECEIVE_QUEUE_PARAMETERS_REVISION_1); + + // + // Request is well formed, set bytes read + // + Method->BytesRead = NDIS_SIZEOF_RECEIVE_QUEUE_PARAMETERS_REVISION_1; + + // + // Should not ask to allocate default queue, or non VMQ type + // + if(QueueParams->QueueId==NDIS_DEFAULT_RECEIVE_QUEUE_ID + || + QueueParams->QueueType != NdisReceiveQueueTypeVMQueue) + { + DEBUGP(MP_ERROR, "[%p] Unsupported QueueId (%i) or QueueType (%i) for allocation.\n", Adapter, QueueParams->QueueId, QueueParams->QueueType); + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + // + // Ready to allocate + // + Status = AllocateRxQueue(Adapter, QueueParams); + + } while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- NICAllocateRxQueuee Status 0x%08x\n", Adapter, Status); + + return Status; +} + + +NDIS_STATUS +NICCompleteAllocationRxQueue( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisMethodRequest) +/*++ +Routine Description: + + This routine will complete any remaining queue allocation, including shared memory. It verifies that the request + is well formed, then passes the request to underlying queue management code. + +Arguments: + + Adapter - Pointer to adapter block + NdisSetRequest - The OID data for the request + +Return Value: + + NDIS_STATUS + +--*/ +{ + + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _METHOD *Method = &NdisMethodRequest->DATA.METHOD_INFORMATION; + PNDIS_RECEIVE_QUEUE_ALLOCATION_COMPLETE_ARRAY CompleteArray = (PNDIS_RECEIVE_QUEUE_ALLOCATION_COMPLETE_ARRAY)Method->InformationBuffer; + + PAGED_CODE(); + + DEBUGP(MP_TRACE, "[%p] ---> NICCompleteAllocationRxQueue\n", Adapter); + + do + { + + // + // Verify that the request matches our requirements + // + VERIFY_OID_METHOD(NdisMethodRequest, + NDIS_RECEIVE_QUEUE_ALLOCATION_COMPLETE_ARRAY_REVISION_1, + NDIS_SIZEOF_RECEIVE_QUEUE_ALLOCATION_COMPLETE_ARRAY_REVISION_1); + + // + // Request is well formed, set bytes read + // + Method->BytesRead = NDIS_SIZEOF_RECEIVE_QUEUE_ALLOCATION_COMPLETE_ARRAY_REVISION_1+ + (CompleteArray->NumElements * CompleteArray->ElementSize); + + // + // Ready to complete allocation + // + Status = CompleteAllocationRxQueue(Adapter, CompleteArray); + + }while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- NICCompleteAllocationRxQueue Status 0x%08x\n", Adapter, Status); + + return Status; + +} + +NDIS_STATUS +NICFreeRxQueue( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest) +/*++ +Routine Description: + + This routine will handle the passed in queue free request. It verifies that the request + is well formed, then passes the request to underlying queue management code. + +Arguments: + + Adapter - Pointer to adapter block + NdisSetRequest - The OID data for the request + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _SET *Set = &NdisSetRequest->DATA.SET_INFORMATION; + PNDIS_RECEIVE_QUEUE_FREE_PARAMETERS QueueFreeParams = (PNDIS_RECEIVE_QUEUE_FREE_PARAMETERS)Set->InformationBuffer; + + PAGED_CODE(); + + DEBUGP(MP_TRACE, "[%p] ---> NICFreeRxQueue\n", Adapter); + + do + { + // + // Verify that the request matches our requirements + // + VERIFY_OID_SET(NdisSetRequest, + NDIS_RECEIVE_QUEUE_FREE_PARAMETERS_REVISION_1, + NDIS_SIZEOF_RECEIVE_QUEUE_FREE_PARAMETERS_REVISION_1); + + // + // Request is well formed, set bytes read + // + Set->BytesRead = NDIS_SIZEOF_RECEIVE_QUEUE_FREE_PARAMETERS_REVISION_1; + + // + // Default queue cannot be freed + // + if(QueueFreeParams->QueueId==NDIS_DEFAULT_RECEIVE_QUEUE_ID) + { + DEBUGP(MP_ERROR, "[%p] Received request to free default queue.\n", Adapter); + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + // + // Ready to attempt a free. + // + Status = FreeRxQueue(Adapter, QueueFreeParams, NdisSetRequest); + + }while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- NICFreeRxQueuee Status 0x%08x\n", Adapter, Status); + + return Status; +} + +static +NDIS_STATUS +NICSetRxFilter( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisMethodRequest) +/*++ +Routine Description: + + This routine will handle the passed filter set request. It verifies that the request + is well formed, then passes the request to underlying filter management code. + +Arguments: + + Adapter - Pointer to adapter block + NdisSetRequest - The OID data for the request + +Return Value: + + NDIS_STATUS + +--*/ +{ + + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _METHOD *Method = &NdisMethodRequest->DATA.METHOD_INFORMATION; + PNDIS_RECEIVE_FILTER_PARAMETERS FilterParams = (PNDIS_RECEIVE_FILTER_PARAMETERS)Method->InformationBuffer; + + PAGED_CODE(); + + do + { + // + // Verify that the request matches our requirements + // + VERIFY_OID_METHOD(NdisMethodRequest, + NDIS_RECEIVE_FILTER_PARAMETERS_REVISION_1, + NDIS_SIZEOF_RECEIVE_FILTER_PARAMETERS_REVISION_1); + + // + // Request is well formed, set bytes read + // + Method->BytesRead = NDIS_SIZEOF_RECEIVE_FILTER_PARAMETERS_REVISION_1; + + // + // Ready to set Filter + // + Status = SetRxFilter(Adapter, FilterParams); + + }while(FALSE); + + return Status; +} + +static +NDIS_STATUS +NICClearRxFilter( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest) +/*++ +Routine Description: + + This routine will handle the passed filter clear request. It verifies that the request + is well formed, then passes the request to underlying filter management code. + +Arguments: + + Adapter - Pointer to adapter block + NdisSetRequest - The OID data for the request + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _SET *Set = &NdisSetRequest->DATA.SET_INFORMATION; + PNDIS_RECEIVE_FILTER_CLEAR_PARAMETERS FilterParams = (PNDIS_RECEIVE_FILTER_CLEAR_PARAMETERS)Set->InformationBuffer; + + PAGED_CODE(); + + do + { + + // + // Verify that the request matches our requirements + // + VERIFY_OID_SET(NdisSetRequest, + NDIS_RECEIVE_FILTER_CLEAR_PARAMETERS_REVISION_1, + NDIS_SIZEOF_RECEIVE_FILTER_CLEAR_PARAMETERS_REVISION_1); + + // + // Request is well formed, set bytes read + // + Set->BytesRead = NDIS_SIZEOF_RECEIVE_FILTER_CLEAR_PARAMETERS_REVISION_1; + + // + // Ready to clear the filter + // + Status = ClearRxFilter(Adapter, FilterParams); + + }while(FALSE); + + return Status; +} + +static +NDIS_STATUS +NICUpdateRxQueue( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _SET *Set = &NdisSetRequest->DATA.SET_INFORMATION; + PNDIS_RECEIVE_QUEUE_PARAMETERS FilterParams = (PNDIS_RECEIVE_QUEUE_PARAMETERS)Set->InformationBuffer; + + PAGED_CODE(); + + do + { + + // + // Verify that the request matches our requirements + // + VERIFY_OID_SET(NdisSetRequest, + NDIS_RECEIVE_QUEUE_PARAMETERS_REVISION_1, + NDIS_SIZEOF_RECEIVE_QUEUE_PARAMETERS_REVISION_1); + + // + // Request is well formed, set bytes read + // + Set->BytesRead = NDIS_SIZEOF_RECEIVE_FILTER_CLEAR_PARAMETERS_REVISION_1; + + // + // Ready to clear the filter + // + Status = UpdateRxQueue(Adapter, FilterParams); + + } while(FALSE); + + return Status; + +} + +#endif + +#if (NDIS_SUPPORT_NDIS630) + +_IRQL_requires_(PASSIVE_LEVEL) +NDIS_STATUS +NICSetQOSParameters( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisMethodRequest) +/*++ +Routine Description: + + This routine will build a classification table according to the request. It verifies that + the request is well formed, then passes the request to the underlying classification + management code. On the other hand, hardware-oriented parameters such as PFC and ETS are + validated but not enforceable in a software implementation. + +Arguments: + + Adapter - Pointer to adapter block + NdisSetRequest - The OID data for the request + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _METHOD *Method = &NdisMethodRequest->DATA.METHOD_INFORMATION; + PNDIS_QOS_PARAMETERS Params = (PNDIS_QOS_PARAMETERS)Method->InformationBuffer; + + PAGED_CODE(); + + DEBUGP(MP_TRACE, "[%p] ---> NICSetQOSParameters\n", Adapter); + + do + { + // + // Verify that the request matches our requirements. + // + VERIFY_OID_METHOD(NdisMethodRequest, + NDIS_QOS_PARAMETERS_REVISION_1, + NDIS_SIZEOF_QOS_PARAMETERS_REVISION_1); + + // + // Request is well formed, set bytes read. + // + Method->BytesRead = NDIS_SIZEOF_QOS_PARAMETERS_REVISION_1 + + Params->NumClassificationElements * Params->ClassificationElementSize; + + Status = SetQOSParameters(Adapter, Params); + if (Status != NDIS_STATUS_SUCCESS) + { + break; + } + + // + // Set bytes written for in-place write, basically same as bytes read. + // + Method->BytesWritten = Method->BytesRead; + } while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- NICSetQOSParameters Status 0x%08x\n", Adapter, Status); + + return Status; +} + +#endif + +static +NDIS_STATUS +MPSetPower( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest) +/*++ +Routine Description: + + This routine handles OID_PNP_SET_POWER request. + +Arguments: + + Adapter - Pointer to adapter block + NdisSetRequest - The OID data for the request + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + struct _SET *Set = &NdisSetRequest->DATA.SET_INFORMATION; + NDIS_DEVICE_POWER_STATE PowerState; + + PAGED_CODE(); + + if (Set->InformationBufferLength < sizeof(NDIS_DEVICE_POWER_STATE)) + { + return NDIS_STATUS_INVALID_LENGTH; + } + + PowerState = *(PNDIS_DEVICE_POWER_STATE UNALIGNED)Set->InformationBuffer; + Set->BytesRead = sizeof(NDIS_DEVICE_POWER_STATE); + + if(PowerState < NdisDeviceStateD0 || + PowerState > NdisDeviceStateD3) + { + return NDIS_STATUS_INVALID_DATA; + } + + + if (PowerState == NdisDeviceStateD0) + { + Status = MPSetPowerD0(Adapter); + } + else + { + Status = MPSetPowerLow(Adapter, PowerState); + } + + return Status; +} + +static +NDIS_STATUS +MPSetPowerD0( + _In_ PMP_ADAPTER Adapter) +/*++ +Routine Description: + + NIC power has been restored to the working power state (D0). + Prepare the NIC for normal operation: + - Restore hardware context (packet filters, multicast addresses, MAC address, etc.) + - Enable interrupts and the NIC's DMA engine. + +Arguments: + + Adapter - Pointer to adapter block + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + PAGED_CODE(); + + Adapter->CurrentPowerState = NdisDeviceStateD0; + MP_CLEAR_FLAG(Adapter, fMP_ADAPTER_LOW_POWER); + + NICStartTheDatapath(Adapter); + + return Status; +} + +static +NDIS_STATUS +MPSetPowerLow( + _In_ PMP_ADAPTER Adapter, + _In_ NDIS_DEVICE_POWER_STATE PowerState) +/*++ +Routine Description: + + The NIC is about to be transitioned to a low power state. + Prepare the NIC for the sleeping state: + - Disable interrupts and the NIC's DMA engine, cancel timers. + - Save any hardware context that the NIC cannot preserve in + a sleeping state (packet filters, multicast addresses, + the current MAC address, etc.) + A miniport driver cannot access the NIC hardware after + the NIC has been set to the D3 state by the bus driver. + + Miniport drivers NDIS v6.30 and above + Do NOT wait for NDIS to return the ownership of all + NBLs from outstanding receive indications + Retain ownership of all the receive descriptors and + packet buffers previously owned by the hardware. + +Arguments: + + Adapter - Pointer to adapter block + PowerState - New power state + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + LONG nSendWaitCount = 0; + + PAGED_CODE(); + + MP_SET_FLAG(Adapter, fMP_ADAPTER_LOW_POWER); + Adapter->CurrentPowerState = PowerState; + + +#if (NDIS_SUPPORT_NDIS630) + + // + // Miniport drivers NDIS v6.30 and above are not + // necessarily paused prior the low power transition + // + + // + // Prevent future sends and receives on the data path + // + NICStopTheDatapath(Adapter); + + // + // Wait for outstanding sends + // Do NOT wait for outstanding receives + // + while(Adapter->nBusySend) + { + if (++nSendWaitCount % 100) + { + DEBUGP(MP_ERROR, "[%p] MPSetPowerLow timed out!\n", Adapter); + ASSERT(FALSE); + } + + DEBUGP(MP_INFO, "[%p] MPSetPowerLow - waiting ...\n", Adapter); + NdisMSleep(1000); + } + +#else + + UNREFERENCED_PARAMETER(nSendWaitCount); + + // + // Miniport drivers NDIS v6.20 and below are + // paused prior the low power transition + // + ASSERT(MP_TEST_FLAG(Adapter, fMP_ADAPTER_PAUSED)); + ASSERT(!NICIsBusy(Adapter)); + +#endif + + return Status; +} diff --git a/network/ndis/netvmini/6x/ctrlpath.h b/network/ndis/netvmini/6x/ctrlpath.h new file mode 100644 index 00000000..99fd791e --- /dev/null +++ b/network/ndis/netvmini/6x/ctrlpath.h @@ -0,0 +1,28 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + CtrlPath.H + +Abstract: + + This module declares functions for the miniport's control path. + +--*/ + +#ifndef _CTRLPATH_H +#define _CTRLPATH_H + + +MINIPORT_OID_REQUEST MPOidRequest; + + +#endif // _CTRLPATH_H + diff --git a/network/ndis/netvmini/6x/datapath.c b/network/ndis/netvmini/6x/datapath.c new file mode 100644 index 00000000..1c18094c --- /dev/null +++ b/network/ndis/netvmini/6x/datapath.c @@ -0,0 +1,1740 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + DataPath.C + +Abstract: + + This module implements the data path of the netvmini miniport. + + In order to excercise the data path of this driver, + you should install more than one instance of the miniport. If there + is only one instance installed, the driver throws the send packet on + the floor and completes the send successfully. If there are more + instances present, it indicates the incoming send packet to the other + instances. For example, if there 3 instances: A, B, & C installed. + Frames sent on instance A would be received on B & C; frames + sent on B would be received on C, & A; and frames sent on C + would be received on A & B. + + This sample miniport goes to some extra lengths so that the data path's + design resembles the design of a real hardware miniport's data path. For + example, this sample has both send and receive queues, even though all the + miniports on the simulated network are on the same computer (and thus could + take some shortcuts when passing data buffers back and forth). + + + +--*/ + +#include "netvmin6.h" +#include "datapath.tmh" + +static +VOID +TXQueueNetBufferForSend( + _In_ PMP_ADAPTER Adapter, + _In_ PNET_BUFFER NetBuffer); + +static +VOID +TXTransmitQueuedSends( + _In_ PMP_ADAPTER Adapter, + _In_ BOOLEAN fAtDispatch); + +static +VOID +TXScheduleTheSendComplete( + _In_ PMP_ADAPTER Adapter); + +static +VOID +RXQueueFrameOnAdapter( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _In_ PFRAME Frame); + +static +VOID +RXScheduleTheReceiveIndication( + _In_ PMP_ADAPTER Adapter, + _In_ PRCB Rcb); + +_Must_inspect_result_ +static +PTCB +TXGetNextTcbToSend( + _In_ PMP_ADAPTER Adapter); + + +VOID +RXRequeueRcbToReceive( + _In_ PMP_ADAPTER Adapter, + _In_ PRCB Rcb); + +VOID +RXReceiveIndicate( + _In_ PMP_ADAPTER Adapter, + _In_ PMP_ADAPTER_RECEIVE_DPC AdapterDpc, + BOOLEAN AtDpc); + +NDIS_IO_WORKITEM_FUNCTION RXReceiveIndicateWorkItem; +NDIS_IO_WORKITEM_FUNCTION TXSendCompleteWorkItem; + +#pragma NDIS_PAGEABLE_FUNCTION(NICStartTheDatapath) +#pragma NDIS_PAGEABLE_FUNCTION(NICStopTheDatapath) + +VOID +MPSendNetBufferLists( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ NDIS_PORT_NUMBER PortNumber, + _In_ ULONG SendFlags) +/*++ + +Routine Description: + + Send Packet Array handler. Called by NDIS whenever a protocol + bound to our miniport sends one or more packets. + + The input packet descriptor pointers have been ordered according + to the order in which the packets should be sent over the network + by the protocol driver that set up the packet array. The NDIS + library preserves the protocol-determined ordering when it submits + each packet array to MiniportSendPackets + + As a deserialized driver, we are responsible for holding incoming send + packets in our internal queue until they can be transmitted over the + network and for preserving the protocol-determined ordering of packet + descriptors incoming to its MiniportSendPackets function. + A deserialized miniport driver must complete each incoming send packet + with NdisMSendComplete, and it cannot call NdisMSendResourcesAvailable. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + MiniportAdapterContext Pointer to our adapter + NetBufferLists Head of a list of NBLs to send + PortNumber A miniport adapter port. Default is 0. + SendFlags Additional flags for the send operation + +Return Value: + + None. Write status directly into each NBL with the NET_BUFFER_LIST_STATUS + macro. + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + PNET_BUFFER_LIST Nbl; + PNET_BUFFER_LIST NextNbl = NULL; + BOOLEAN fAtDispatch = (SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL) ? TRUE:FALSE; + NDIS_STATUS Status; + ULONG NumNbls=0; + + DEBUGP(MP_TRACE, "[%p] ---> MPSendNetBufferLists\n", Adapter); + + UNREFERENCED_PARAMETER(PortNumber); + UNREFERENCED_PARAMETER(SendFlags); + ASSERT(PortNumber == 0); // Only the default port is supported + + + // + // Each NET_BUFFER_LIST has a list of NET_BUFFERs. + // Loop over all the NET_BUFFER_LISTs, sending each NET_BUFFER. + // + for ( + Nbl = NetBufferLists; + Nbl!= NULL; + Nbl = NextNbl, ++NumNbls) + { + PNET_BUFFER NetBuffer; + + NextNbl = NET_BUFFER_LIST_NEXT_NBL(Nbl); + + // + // Unlink the NBL and prepare our bookkeeping. + // + // We use a reference count to make sure that we don't send complete + // the NBL until we're done reading each NB on the NBL. + // + NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL; + SEND_REF_FROM_NBL(Nbl) = 0; + + Status = TXNblReference(Adapter, Nbl); + + if(Status == NDIS_STATUS_SUCCESS) + { + NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_SUCCESS; + + // + // Queue each NB for transmission. + // + for ( + NetBuffer = NET_BUFFER_LIST_FIRST_NB(Nbl); + NetBuffer != NULL; + NetBuffer = NET_BUFFER_NEXT_NB(NetBuffer)) + { + NBL_FROM_SEND_NB(NetBuffer) = Nbl; + TXQueueNetBufferForSend(Adapter, NetBuffer); + } + + TXNblRelease(Adapter, Nbl, fAtDispatch); + } + else + { + // + // We can't send this NBL now. Indicate failure. + // + if (MP_TEST_FLAG(Adapter, fMP_RESET_IN_PROGRESS)) + { + NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_RESET_IN_PROGRESS; + } + else if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS|fMP_ADAPTER_PAUSED)) + { + NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_PAUSED; + } + else if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_LOW_POWER)) + { + NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_LOW_POWER_STATE; + } + else + { + NET_BUFFER_LIST_STATUS(Nbl) = Status; + } + + NdisMSendNetBufferListsComplete( + Adapter->AdapterHandle, + Nbl, + fAtDispatch ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL:0); + + continue; + } + } + + DEBUGP(MP_TRACE, "[%p] %i NBLs processed.\n", Adapter, NumNbls); + + // + // Now actually go send each of the queued NBs. + // + TXTransmitQueuedSends(Adapter, fAtDispatch); + + DEBUGP(MP_TRACE, "[%p] <--- MPSendNetBufferLists\n", Adapter); +} + +VOID +TXQueueNetBufferForSend( + _In_ PMP_ADAPTER Adapter, + _In_ PNET_BUFFER NetBuffer) +/*++ + +Routine Description: + + This routine inserts the NET_BUFFER into the SendWaitList, then calls + TXTransmitQueuedSends to start sending data from the list. + + We use this indirect queue to send data because the miniport should try to + send frames in the order in which the protocol gave them. If we just sent + the NET_BUFFER immediately, then it would be out-of-order with any data on + the SendWaitList. + + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Adapter Adapter that is transmitting this NB + NetBuffer NB to be transfered + +Return Value: + + None. + +--*/ +{ + NDIS_STATUS Status; + UCHAR DestAddress[NIC_MACADDR_SIZE]; + + DEBUGP(MP_TRACE, "[%p] ---> TXQueueNetBufferForSend, NB= 0x%p\n", Adapter, NetBuffer); + + do + { + // + // First, do a sanity check on the frame data. + // + Status = HWGetDestinationAddress(NetBuffer, DestAddress); + if (Status != NDIS_STATUS_SUCCESS) + { + NET_BUFFER_LIST_STATUS(NBL_FROM_SEND_NB(NetBuffer)) = NDIS_STATUS_INVALID_DATA; + break; + } + + // + // Stash away the frame type. We'll use that later, when updating + // our send statistics (since we don't have NIC hardware to compute the + // send statistics for us). + // + FRAME_TYPE_FROM_SEND_NB(NetBuffer) = NICGetFrameTypeFromDestination(DestAddress); + + // + // Pin the original NBL with a reference, so it isn't completed until + // we're done with its NB. + // + Status = TXNblReference(Adapter, NBL_FROM_SEND_NB(NetBuffer)); + if(Status == NDIS_STATUS_SUCCESS) + { + // + // Insert the NB into the queue. The caller will flush the queue when + // it's done adding items to the queue. + // + NdisInterlockedInsertTailList( + &Adapter->SendWaitList, + SEND_WAIT_LIST_FROM_NB(NetBuffer), + &Adapter->SendWaitListLock); + } + + } while (FALSE); + + + DEBUGP(MP_TRACE, "[%p] <--- TXQueueNetBufferForSend\n", Adapter); +} + + +VOID +#pragma prefast(suppress: 28167, "PREfast does not recognize IRQL is conditionally raised and lowered") +TXTransmitQueuedSends( + _In_ PMP_ADAPTER Adapter, + _In_ BOOLEAN fAtDispatch) +/*++ + +Routine Description: + + This routine sends as many frames from the SendWaitList as it can. + + If there are not enough resources to send immediately, this function stops + and leaves the remaining frames on the SendWaitList, to be sent once there + are enough resources. + + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Adapter Our adapter + fAtDispatch TRUE if the current IRQL is DISPATCH_LEVEL + +Return Value: + + None. + +--*/ +{ + BOOLEAN fScheduleTheSendCompleteDpc = FALSE; + ULONG NumFramesSent = 0; + KIRQL OldIrql = PASSIVE_LEVEL; + + DEBUGP(MP_TRACE, + "[%p] ---> TXTransmitQueuedSends\n", + Adapter); + + // + // This guard ensures that only one CPU is running this function at a time. + // We check this so that items from the SendWaitList get sent to the + // receiving adapters in the same order that they were queued. + // + // You could remove this guard and everything will still work ok, but some + // frames might be delivered out-of-order. + // + // Generally, this mechanism wouldn't be applicable to real hardware, since + // the hardware would have its own mechanism to ensure sends are transmitted + // in the correct order. + // + if (!fAtDispatch) + { + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + } + + if (KeTryToAcquireSpinLockAtDpcLevel(&Adapter->SendPathSpinLock)) + { + for (NumFramesSent = 0; NumFramesSent < NIC_MAX_SENDS_PER_DPC; NumFramesSent++) + { + PLIST_ENTRY pTcbEntry = NULL; + PTCB Tcb = NULL; + PLIST_ENTRY pQueuedSend = NULL; + PNET_BUFFER NetBuffer; + + // + // Get the next available TCB. + // + pTcbEntry = NdisInterlockedRemoveHeadList( + &Adapter->FreeTcbList, + &Adapter->FreeTcbListLock); + if (!pTcbEntry) + { + // + // The adapter can't handle any more simultaneous transmit + // operations. Keep any remaining sends in the SendWaitList and + // we'll come back later when there are TCBs available. + // + break; + } + + Tcb = CONTAINING_RECORD(pTcbEntry, TCB, TcbLink); + + // + // Get the next NB that needs sending. + // + pQueuedSend = NdisInterlockedRemoveHeadList( + &Adapter->SendWaitList, + &Adapter->SendWaitListLock); + if (!pQueuedSend) + { + // + // There's nothing left that needs sending. We're all done. + // + NdisInterlockedInsertTailList( + &Adapter->FreeTcbList, + &Tcb->TcbLink, + &Adapter->FreeTcbListLock); + break; + } + + NetBuffer = NB_FROM_SEND_WAIT_LIST(pQueuedSend); + + + // + // We already packed the frame type into the net buffer before accepting + // it for send. Now that we have a TCB to keep track of the data, let's + // pull it out and keep it in a proper variable. + // + Tcb->FrameType = FRAME_TYPE_FROM_SEND_NB(NetBuffer); + + HWProgramDmaForSend(Adapter, Tcb, NetBuffer, fAtDispatch); + + NdisInterlockedInsertTailList( + &Adapter->BusyTcbList, + &Tcb->TcbLink, + &Adapter->BusyTcbListLock); + + fScheduleTheSendCompleteDpc = TRUE; + } + + KeReleaseSpinLock(&Adapter->SendPathSpinLock, DISPATCH_LEVEL); + } + + if (!fAtDispatch) + { + KeLowerIrql(OldIrql); + } + + DEBUGP(MP_TRACE, "[%p] %i Frames transmitted.\n", Adapter, NumFramesSent); + + if (fScheduleTheSendCompleteDpc) + { + TXScheduleTheSendComplete(Adapter); + } + + DEBUGP(MP_TRACE, "[%p] <-- TXTransmitQueuedSends\n", Adapter); +} + + +VOID +TXScheduleTheSendComplete( + _In_ PMP_ADAPTER Adapter) +/*++ + +Routine Description: + + This function schedules the transmit DPC on the sending miniport. + +Arguments: + + FunctionContext Pointer to the adapter that is sending frames + +Return Value: + + None. + +--*/ +{ + LARGE_INTEGER liDelay; + + if (!Adapter->SendCompleteWorkItemQueued) + { + liDelay.QuadPart = -(NIC_SIMULATED_LATENCY); + NdisSetTimerObject(Adapter->SendCompleteTimer, liDelay, 0, NULL); + } + + DEBUGP(MP_TRACE, "[%p] Scheduled Send Complete DPC [Delay: %i].\n", Adapter, NIC_SIMULATED_LATENCY); + +} + + +_IRQL_requires_(DISPATCH_LEVEL) +BOOLEAN +WorkItemQueuedForWatchdogAvoidance( + _In_ NDIS_HANDLE WorkItem, + _In_ volatile LONG* WorkItemQueued, + _In_ NDIS_IO_WORKITEM_ROUTINE WIRoutine, + _In_ PVOID WIContext + ) +/*++ + +Routine Description: + + This function should be called from the receive or send-complete DPCs. It queues a work item to do the receives or send-completes + if the DPC watchdog timer is within 25% of the limit. This allows the processor to reach PASSIVE_LEVEL and reset the watchdog. + + Runs at IRQL = DISPATCH_LEVEL. + +Arguments: + + WorkItem The work item to queue + WorkItemQueued Variable that stores whether a work item is currently queued + WIRoutine The work routine + WIContext The context passed to the work routine + +Return Value: + + TRUE - Work item queued due to watchdog timer, caller should exit DPC + FALSE - Ok to continue in DPC + +--*/ +{ + KDPC_WATCHDOG_INFORMATION WatchdogInfo; + NTSTATUS Status; + + if(*WorkItemQueued) + { + // + // We've already queued up the work item, no need to check watchdog information + // + return TRUE; + } + + Status = KeQueryDpcWatchdogInformation(&WatchdogInfo); + if (NT_SUCCESS(Status) + // + // Verify the watchdog is enabled + // + && WatchdogInfo.DpcWatchdogLimit != 0 + // + // Once we go below 25% of the watchdog limit we fall back on the work item to allow the watchdog to reset + // + && WatchdogInfo.DpcWatchdogCount < WatchdogInfo.DpcWatchdogLimit / 4) + { + // + // Make sure we don't queue the work item if it's already been queued for this DPC + // + LONG AlreadyQueued = InterlockedCompareExchange( + WorkItemQueued, + TRUE, + FALSE); + if(!AlreadyQueued) + { + // + // We've crossed our threshold for consecutive DPCs, schedule work item to complete this receive + // + DEBUGP(MP_TRACE, "Processor has spent too much time in DPC. Queueing work item to handle next receives/send-completes.\n"); + NdisQueueIoWorkItem(WorkItem, WIRoutine, WIContext); + } + return TRUE; + } + + // + // We're still within acceptable time limits + // + return FALSE; +} + + +_IRQL_requires_(DISPATCH_LEVEL) +VOID +TXSendComplete( + _In_ PMP_ADAPTER Adapter) +/*++ + +Routine Description: + + This routine completes pending sends for the given adapter. + + Each busy TCB is popped from the BusyTcbList and its corresponding NB is + released. If there was an error sending the frame, the NB's NBL's status + is updated. + +--*/ +{ + BOOLEAN fRescheduleThisDpcAgain = TRUE; + ULONG NumFramesSent = 0; + + DEBUGP(MP_TRACE, "[%p] ---> TXSendComplete.\n", Adapter); + + for (NumFramesSent = 0; NumFramesSent < NIC_MAX_SENDS_PER_DPC; NumFramesSent++) + { + ULONG BytesSent; + + PTCB Tcb = TXGetNextTcbToSend(Adapter); + if (!Tcb) + { + // + // There are no more TCBs remaining to send. We're all done. + // + fRescheduleThisDpcAgain = FALSE; + break; + } + + + // + // Finish the transmit operation. For our hardware, that means the + // frame is pushed onto the RecvWaitLists of each other adapter. + // + + BytesSent = HWGetBytesSent(Adapter, Tcb); + + if (BytesSent == 0) + { + // + // Failed to send the frame. + // + + Adapter->TransmitFailuresOther++; + NET_BUFFER_LIST_STATUS(NBL_FROM_SEND_NB(Tcb->NetBuffer)) = NDIS_STATUS_RESOURCES; + } + else + { + // + // We've finished sending this NB successfully; update the stats. + // + switch (Tcb->FrameType) + { + case NDIS_PACKET_TYPE_BROADCAST: + Adapter->FramesTxBroadcast++; + Adapter->BytesTxBroadcast += BytesSent; + break; + + case NDIS_PACKET_TYPE_MULTICAST: + Adapter->FramesTxMulticast++; + Adapter->BytesTxMulticast += BytesSent; + break; + + case NDIS_PACKET_TYPE_DIRECTED: + default: + Adapter->FramesTxDirected++; + Adapter->BytesTxDirected += BytesSent; + } + } + + + // + // Now that we've finished using the TCB and its associated NET_BUFFER, + // we can release the NET_BUFFER back to the protocol and the TCB back + // to the free list. + // + ReturnTCB(Adapter, Tcb); + } + + TXTransmitQueuedSends(Adapter, TRUE); + + if (fRescheduleThisDpcAgain) + { + TXScheduleTheSendComplete(Adapter); + } + + DEBUGP(MP_TRACE, "[%p] <--- TXSendComplete.\n", Adapter); +} + +_Use_decl_annotations_ +VOID +TXSendCompleteWorkItem( + PVOID FunctionContext, + NDIS_HANDLE WorkItem) +/*++ + +Routine Description: + + This work item handler is used to do send completions in the case when we are trying + to avoid a DPC watchdog timeout + +Arguments: + + FunctionContext - The Adapter object for which send-completions are to be done + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(FunctionContext); + KIRQL OldIrql; + + UNREFERENCED_PARAMETER(WorkItem); + + ASSERT(Adapter != NULL); + _Analysis_assume_(Adapter != NULL); + DEBUGP(MP_TRACE, "[%p] ---> TXSendCompleteWorkItem.\n", Adapter); + + Adapter->SendCompleteWorkItemRunning = TRUE; + KeMemoryBarrier(); + + Adapter->SendCompleteWorkItemQueued = FALSE; + KeMemoryBarrier(); + + NDIS_RAISE_IRQL_TO_DISPATCH(&OldIrql); + TXSendComplete(Adapter); + NDIS_LOWER_IRQL(OldIrql,DISPATCH_LEVEL); + + KeMemoryBarrier(); + Adapter->SendCompleteWorkItemRunning = FALSE; + + DEBUGP(MP_TRACE, "[%p] <--- TXSendCompleteWorkItem.\n", Adapter); +} + +_Use_decl_annotations_ +VOID +TXSendCompleteDpc( + PVOID UnusedParameter1, + PVOID FunctionContext, + PVOID UnusedParameter2, + PVOID UnusedParameter3) +/*++ + +Routine Description: + + This routine simulates the DPC handler of a send complete hardware + interrupt. + +Arguments: + + FunctionContext - The Adapter object for which send-completions are to be done + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(FunctionContext); + + UNREFERENCED_PARAMETER(UnusedParameter1); + UNREFERENCED_PARAMETER(UnusedParameter2); + UNREFERENCED_PARAMETER(UnusedParameter3); + + DEBUGP(MP_TRACE, "[%p] ---> TXSendCompleteDpc\n", Adapter); + + if (!WorkItemQueuedForWatchdogAvoidance(Adapter->SendCompleteWorkItem, + &Adapter->SendCompleteWorkItemQueued, + TXSendCompleteWorkItem, + Adapter)) + { + TXSendComplete(Adapter); + } + + DEBUGP(MP_TRACE, "[%p] <--- TXSendCompleteDpc\n", Adapter); +} + +NDIS_STATUS +TXNblReference( + _In_ PMP_ADAPTER Adapter, + _In_ PNET_BUFFER_LIST NetBufferList) +/*++ + +Routine Description: + + Adds a reference on a NBL that is being transmitted. + The NBL won't be returned to the protocol until the last reference is + released. + + Runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + Adapter Pointer to our adapter + NetBufferList The NBL to reference + +Return Value: + + NDIS_STATUS_SUCCESS if reference was acquired succesfully. + NDIS_STATUS_ADAPTER_NOT_READY if the adapter state is such that we should not acquire new references to resources + +--*/ +{ + NdisInterlockedIncrement(&Adapter->nBusySend); + + // + // Make sure the increment happens before ready state check + // + KeMemoryBarrier(); + + // + // If the adapter is not ready, undo the reference and fail the call + // + if(!MP_IS_READY(Adapter)) + { + InterlockedDecrement(&Adapter->nBusySend); + DEBUGP(MP_LOUD, "[%p] Could not acquire transmit reference, the adapter is not ready.\n", Adapter); + return NDIS_STATUS_ADAPTER_NOT_READY; + } + + NdisInterlockedIncrement(&SEND_REF_FROM_NBL(NetBufferList)); + return NDIS_STATUS_SUCCESS; + +} + + +VOID +TXNblRelease( + _In_ PMP_ADAPTER Adapter, + _In_ PNET_BUFFER_LIST NetBufferList, + _In_ BOOLEAN fAtDispatch) +/*++ + +Routine Description: + + Releases a reference on a NBL that is being transmitted. + If the last reference is released, the NBL is returned to the protocol. + + Runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + Adapter Pointer to our adapter + NetBufferList The NBL to release + fAtDispatch TRUE if the current IRQL is DISPATCH_LEVEL + +Return Value: + + None. + +--*/ +{ + + if (0 == NdisInterlockedDecrement(&SEND_REF_FROM_NBL(NetBufferList))) + { + DEBUGP(MP_TRACE, "[%p] Send NBL %p complete.\n", Adapter, NetBufferList); + + NET_BUFFER_LIST_NEXT_NBL(NetBufferList) = NULL; + + NdisMSendNetBufferListsComplete( + Adapter->AdapterHandle, + NetBufferList, + fAtDispatch ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL:0); + } + else + { + DEBUGP(MP_TRACE, "[%p] Send NBL %p not complete. RefCount: %i.\n", Adapter, NetBufferList, SEND_REF_FROM_NBL(NetBufferList)); + } + + NdisInterlockedDecrement(&Adapter->nBusySend); +} + + +_Must_inspect_result_ +PTCB +TXGetNextTcbToSend( + _In_ PMP_ADAPTER Adapter) +/*++ + +Routine Description: + + Returns the next TCB queued on the send list, or NULL if the list was empty. + + Runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + Adapter Pointer to our adapter + +Return Value: + + NULL if there was no TCB queued. + Else, a pointer to the TCB that was popped off the top of the BusyTcbList. + +--*/ +{ + PTCB Tcb; + PLIST_ENTRY pTcbEntry = NdisInterlockedRemoveHeadList( + &Adapter->BusyTcbList, + &Adapter->BusyTcbListLock); + + if (! pTcbEntry) + { + // End of list -- no more items to receive. + return NULL; + } + + Tcb = CONTAINING_RECORD(pTcbEntry, TCB, TcbLink); + + ASSERT(Tcb); + ASSERT(Tcb->NetBuffer); + + return Tcb; +} + + +VOID +TXFlushSendQueue( + _In_ PMP_ADAPTER Adapter, + _In_ NDIS_STATUS CompleteStatus) +/*++ + +Routine Description: + + This routine is called by the Halt or Reset handler to fail all + the queued up Send NBLs because the device is either gone, being + stopped for resource rebalance, or reset. + +Arguments: + + Adapter Pointer to our adapter + CompleteStatus The status code with which to complete each NBL + +Return Value: + + None. + +--*/ +{ + PTCB Tcb; + + DEBUGP(MP_TRACE, "[%p] ---> TXFlushSendQueue Status = 0x%08x\n", Adapter, CompleteStatus); + + + // + // First, free anything queued in the driver. + // + + while (TRUE) + { + PLIST_ENTRY pEntry; + PNET_BUFFER NetBuffer; + PNET_BUFFER_LIST NetBufferList; + + pEntry = NdisInterlockedRemoveHeadList( + &Adapter->SendWaitList, + &Adapter->SendWaitListLock); + + if (!pEntry) + { + // End of list -- nothing left to free. + break; + } + + NetBuffer = NB_FROM_SEND_WAIT_LIST(pEntry); + NetBufferList = NBL_FROM_SEND_NB(NetBuffer); + + DEBUGP(MP_TRACE, "[%p] Dropping Send NB: 0x%p.\n", Adapter, NetBuffer); + + NET_BUFFER_LIST_STATUS(NetBufferList) = CompleteStatus; + TXNblRelease(Adapter, NetBufferList, FALSE); + } + + + // + // Next, cancel anything queued in the hardware. + // + + while (NULL != (Tcb = TXGetNextTcbToSend(Adapter))) + { + NET_BUFFER_LIST_STATUS(NBL_FROM_SEND_NB(Tcb->NetBuffer)) = CompleteStatus; + ReturnTCB(Adapter, Tcb); + } + + + DEBUGP(MP_TRACE, "[%p] <--- TXFlushSendQueue\n", Adapter); +} + + +VOID +RXDeliverFrameToEveryAdapter( + _In_ PMP_ADAPTER SendAdapter, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _In_ PFRAME Frame, + _In_ BOOLEAN fAtDispatch) +/*++ + +Routine Description: + + This routine sends a TCB to each netvmini 6.x adapter (besides the sending + adapter itself) + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + SendAdapter Our adapter that is doing the sending + Nbl1QInfo 8021Q Tag information for the FRAME to be sent + Frame The FRAME to be sent + fAtDispatch TRUE if the current IRQL is DISPATCH_LEVEL + +Return Value: + + None. + +--*/ +{ + MP_LOCK_STATE LockState; + PLIST_ENTRY AdapterLink; + + + DEBUGP(MP_TRACE, "[%p] ---> RXDeliverFrameToEveryAdapter. Frame=0x%p\n", SendAdapter, Frame); + + LOCK_ADAPTER_LIST_FOR_READ(&LockState, fAtDispatch ? NDIS_RWL_AT_DISPATCH_LEVEL:0); + UNREFERENCED_PARAMETER(fAtDispatch); + + // + // Go through the adapter list and queue packet for + // indication on them if there are any. Otherwise + // just drop the packet on the floor and tell NDIS that + // you have completed send. + // + + for ( + AdapterLink = GlobalData.AdapterList.Flink; + AdapterLink != &GlobalData.AdapterList; + AdapterLink = AdapterLink->Flink + ) + { + PMP_ADAPTER DestAdapter = CONTAINING_RECORD(AdapterLink, MP_ADAPTER, List); + + if (DestAdapter == SendAdapter) + { + // Don't loopback packets to the sending adapter. + continue; + } + + RXQueueFrameOnAdapter(DestAdapter, Nbl1QInfo, Frame); + } + + UNLOCK_ADAPTER_LIST(&LockState); + + DEBUGP(MP_TRACE, "[%p] <-- RXDeliverFrameToEveryAdapter\n", SendAdapter); + +} + +VOID +RXQueueFrameOnAdapter( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _In_ PFRAME Frame) +/*++ + +Routine Description: + + This routine queues the send packet in to the destination + adapters RecvWaitList and fires a timer DPC so that it + can be indicated as soon as possible. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Adapter Pointer to the destination adapter + Nbl1QInfo 8021Q Tag information for the FRAME to be sent + Frame Pointer to FRAME that contains the data payload + + +Return Value: + + None. + +--*/ +{ + DEBUGP(MP_TRACE, "[%p] ---> RXQueueFrameOnAdapter\n", Adapter); + + do + { + PRCB Rcb; + UCHAR DestAddress[NIC_MACADDR_SIZE]; + ULONG FrameType; + + + if (!MP_IS_READY(Adapter)) + { + // + // The NIC is not receiving any data. + // + break; + } + + if (Frame->ulSize < HW_MIN_FRAME_SIZE) + { + // + // This frame is malformed. Drop it. + // + Adapter->RxRuntErrors++; + break; + } + + GET_DESTINATION_OF_FRAME(DestAddress, Frame->Data); + FrameType = NICGetFrameTypeFromDestination(DestAddress); + + if(VMQ_ENABLED(Adapter) && FrameType == NDIS_PACKET_TYPE_DIRECTED) + { + // + // Defer decision whether to drop until we check for VMQ matches + // + } + else if (!HWIsFrameAcceptedByPacketFilter(Adapter, DestAddress, FrameType)) + { + // + // Our NIC "hardware" has a packet filter that eliminates frames + // that weren't sent to us. This frame didn't match the filter, + // so pretend we never saw this frame. + // + break; + } + + // + // Allocate memory for RCB. + // + Rcb = GetRCB(Adapter, Nbl1QInfo, Frame); + if (!Rcb) + { + DEBUGP(MP_TRACE, "[%p] GetRCB did not return an RCB.\n", Adapter); + break; + } + + + switch (FrameType) + { + case NDIS_PACKET_TYPE_BROADCAST: + Adapter->FramesRxBroadcast++; + Adapter->BytesRxBroadcast += Frame->ulSize; + break; + + case NDIS_PACKET_TYPE_MULTICAST: + Adapter->FramesRxMulticast++; + Adapter->BytesRxMulticast += Frame->ulSize; + break; + + case NDIS_PACKET_TYPE_DIRECTED: + default: + Adapter->FramesRxDirected++; + Adapter->BytesRxDirected += Frame->ulSize; + } + + + // + // If VMQ is enabled, queue Rcb on the owner VMQ, otherwise + // use global receive wait list + // + if(VMQ_ENABLED(Adapter)) + { + // + // Queue on owner VMQ receive block + // + AddPendingRcbToRxQueue(Adapter, Rcb); + } + else + { + // + // Queue on global receive block + // + NdisInterlockedInsertTailList(&Adapter->ReceiveBlock[0].ReceiveList, &Rcb->RcbLink, &Adapter->ReceiveBlock[0].ReceiveListLock); + } + + RXScheduleTheReceiveIndication(Adapter, Rcb); + + + } while (FALSE); + + + DEBUGP(MP_TRACE, "[%p] <--- RXQueueFrameOnAdapter\n", Adapter); +} + +VOID +RXScheduleTheReceiveIndication( + _In_ PMP_ADAPTER Adapter, + _In_ PRCB Rcb) +/*++ + +Routine Description: + + This function schedules the receive DPC on the receiving miniport. + +Arguments: + + FunctionContext Pointer to the adapter that is receiving frames + +Return Value: + + None. + +--*/ +{ + + // + // Use default DPC unless VMQ is enabled, in which case you use the Queue's DPC + // + PMP_ADAPTER_RECEIVE_DPC AdapterDpc = Adapter->DefaultRecvDpc; + + if(VMQ_ENABLED(Adapter)) + { + // + // Add Rcb to owner Queue's pending List + // + AdapterDpc = GetRxQueueDpc(Adapter, NET_BUFFER_LIST_RECEIVE_QUEUE_ID(Rcb->Nbl)); + } + else + { + UNREFERENCED_PARAMETER(Rcb); + } + + // + // Schedule DPC + // + if(AdapterDpc->WorkItemQueued) + { + // + // We've queued up receive work item to avoid DPC watchdog timeout. Let's wait for it to start rather + // than queue up the DPC. + // + DEBUGP(MP_TRACE, "[%p] Receive DPC not scheduled, receive work item is pending. Processor: %i\n", Adapter, AdapterDpc->ProcessorNumber); + } + else + { + KeInsertQueueDpc(&AdapterDpc->Dpc, AdapterDpc, NULL); + DEBUGP(MP_TRACE, "[%p] Scheduled Receive DPC. Processor: %i\n", Adapter, AdapterDpc->ProcessorNumber); + } + +} + +VOID +RXReceiveIndicateDpc( + _In_ struct _KDPC *Dpc, + _In_opt_ PVOID DeferredContext, + _In_opt_ PVOID SystemArgument1, + _In_opt_ PVOID SystemArgument2) +/*++ + +Routine Description: + + DPC function for Receive Indication. Please note that receive + timer DPC is not required when you are talking to a real device. In real + miniports, this DPC is usually provided by NDIS as MPHandleInterrupt + callback whenever the device interrupts for receive indication. + +Arguments: + + DeferredContext Pointer to our adapter + SystemArgument1 PMP_ADAPTER_RECEIVE_DPC structure for this DPC + +Return Value: + + None. + +--*/ +{ + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument2); + + ASSERT(DeferredContext != NULL); + ASSERT(SystemArgument1 != NULL); + _Analysis_assume_(DeferredContext != NULL); + _Analysis_assume_(SystemArgument1 != NULL); + + RXReceiveIndicate((PMP_ADAPTER)DeferredContext, (PMP_ADAPTER_RECEIVE_DPC)SystemArgument1, TRUE); +} + +_Use_decl_annotations_ +VOID +RXReceiveIndicateWorkItem( + PVOID WorkItemContext, + NDIS_HANDLE NdisIoWorkItemHandle) +/*++ + +Routine Description: + + Work Item function for Receive Indication. The work item is invoked if the corresponding receive + DPC has run enough times on the processor without a transition to PASSIVE to risk hitting the DPC + watchdog timer. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + WorkItemContext PMP_ADAPTER_RECEIVE_DPC structure for the corresponding receive DPC + NdisIoWorkItemHandle Workitem handle, unused + +Return Value: + + None. + +--*/ +{ + PMP_ADAPTER_RECEIVE_DPC AdapterDpc = (PMP_ADAPTER_RECEIVE_DPC)WorkItemContext; + UNREFERENCED_PARAMETER(NdisIoWorkItemHandle); + ASSERT(AdapterDpc != NULL); + _Analysis_assume_(AdapterDpc != NULL); + RXReceiveIndicate(AdapterDpc->Adapter, AdapterDpc, FALSE); +} + +VOID +RXReceiveIndicate( + _In_ PMP_ADAPTER Adapter, + _In_ PMP_ADAPTER_RECEIVE_DPC AdapterDpc, + BOOLEAN AtDpc) +/*++ + +Routine Description: + + This function performs the receive indications for the specified RECEIVE_DPC structure. + + Runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + Adapter Pointer to our adapter + AdapterDpc PMP_ADAPTER_RECEIVE_DPC structure for this receive + AtDpc TRUE if the function was called from the context of the DPC, FALSE if called from work item (to avoid watchdog) + +Return Value: + + None. + +--*/ + +{ + + ULONG NumNblsReceived = 0; + PNET_BUFFER_LIST FirstNbl = NULL, LastNbl = NULL; + USHORT CurrentQueue; + + DEBUGP(MP_TRACE, "[%p] ---> RXReceiveIndicate. Processor: %i, AtDpc: %i\n", Adapter, AdapterDpc->ProcessorNumber, AtDpc); + + // + // Exit DPC if we've queued a work item to avoid DPC watchdog timer expiration + // + if(AtDpc && WorkItemQueuedForWatchdogAvoidance(AdapterDpc->WorkItem, + &AdapterDpc->WorkItemQueued, + RXReceiveIndicateWorkItem, + AdapterDpc)) + { + DEBUGP(MP_TRACE, "[%p] <--- RXReceiveIndicate. Processor: %i\n", Adapter, AdapterDpc->ProcessorNumber); + return; + } + + for(CurrentQueue = 0; CurrentQueue RecvBlock[CurrentQueue]) + { + PMP_ADAPTER_RECEIVE_BLOCK ReceiveBlock = &Adapter->ReceiveBlock[CurrentQueue]; + FirstNbl = LastNbl = NULL; + + // + // Collect pending NBLs, indicate up to MaxNblCountPerIndicate per receive block + // + for(NumNblsReceived=0; NumNblsReceived < AdapterDpc->MaxNblCountPerIndicate; ++NumNblsReceived) + { + PLIST_ENTRY Entry; + PRCB Rcb = NULL; + + Entry = NdisInterlockedRemoveHeadList(&ReceiveBlock->ReceiveList, &ReceiveBlock->ReceiveListLock); + if(Entry) + { + Rcb = CONTAINING_RECORD(Entry, RCB, RcbLink); + } + + if(!Rcb) + { + break; + } + + ASSERT(Rcb->Data); + + // + // The recv NBL's data was filled out by the hardware. Now just update + // its bookkeeping. + // + NET_BUFFER_LIST_STATUS(Rcb->Nbl) = NDIS_STATUS_SUCCESS; + Rcb->Nbl->SourceHandle = Adapter->AdapterHandle; + + // + // Add this NBL to the chain of NBLs to indicate up. + // + if (!FirstNbl) + { + LastNbl = FirstNbl = Rcb->Nbl; + } + else + { + NET_BUFFER_LIST_NEXT_NBL(LastNbl) = Rcb->Nbl; + LastNbl = Rcb->Nbl; + } + } + + // + // Indicate NBLs + // + if (FirstNbl) + { + DEBUGP(MP_TRACE, "[%p] Receive Block %i: %i frames indicated.\n", Adapter, CurrentQueue, NumNblsReceived); + + NET_BUFFER_LIST_NEXT_NBL(LastNbl) = NULL; + + // + // Indicate up the NBLs. + // + // The NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL allows a perf optimization: + // NDIS doesn't have to check and raise the current IRQL, since we + // promise that the current IRQL is exactly DISPATCH_LEVEL already. + // + NdisMIndicateReceiveNetBufferLists( + Adapter->AdapterHandle, + FirstNbl, + 0, // default port + NumNblsReceived, + (AtDpc?NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL:0) + | NDIS_RECEIVE_FLAGS_PERFECT_FILTERED +#if (NDIS_SUPPORT_NDIS620) + | NDIS_RECEIVE_FLAGS_SINGLE_QUEUE + | (CurrentQueue?NDIS_RECEIVE_FLAGS_SHARED_MEMORY_INFO_VALID:0) //non-default queues use shared memory +#endif + ); + } + + if(!AtDpc) + { + // + // Clear work item flag to allow DPCs to be queued + // + InterlockedExchange(&AdapterDpc->WorkItemQueued, FALSE); + } + + if (!IsListEmpty(&ReceiveBlock->ReceiveList)) + { + // + // More left to indicate for this receive block, queue this DPC again + // + DEBUGP(MP_TRACE, "[%p] Receive Block %i: Requeued DPC.\n", Adapter, CurrentQueue); + KeInsertQueueDpc(&AdapterDpc->Dpc, AdapterDpc, NULL); + } + + } + + } + + DEBUGP(MP_TRACE, "[%p] <--- RXReceiveIndicate. Processor: %i\n", Adapter, AdapterDpc->ProcessorNumber); +} + + +VOID +MPReturnNetBufferLists( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ ULONG ReturnFlags) +/*++ + +Routine Description: + + NDIS Miniport entry point called whenever protocols are done with one or + NBLs that we indicated up with NdisMIndicateReceiveNetBufferLists. + + Note that the list of NBLs may be chained together from multiple separate + lists that were indicated up individually. + +Arguments: + + MiniportAdapterContext Pointer to our adapter + NetBufferLists NBLs being returned + ReturnFlags May contain the NDIS_RETURN_FLAGS_DISPATCH_LEVEL + flag, which if is set, indicates we can get a + small perf win by not checking or raising the + IRQL + +Return Value: + + None. + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + UNREFERENCED_PARAMETER(ReturnFlags); + + DEBUGP(MP_TRACE, "[%p] ---> MPReturnNetBufferLists\n", Adapter); + + while (NetBufferLists) + { + PRCB Rcb = RCB_FROM_NBL(NetBufferLists); + ReturnRCB(Adapter, Rcb); + NetBufferLists = NET_BUFFER_LIST_NEXT_NBL(NetBufferLists); + } + + DEBUGP(MP_TRACE, "[%p] <--- MPReturnNetBufferLists\n", Adapter); +} + +VOID +RXFlushReceiveQueue( + _In_ PMP_ADAPTER Adapter, + _In_ PMP_ADAPTER_RECEIVE_DPC AdapterDpc) +/*++ + +Routine Description: + + This routine is called by the Halt handler to fail all + the queued up RecvNbls if it succeeds in cancelling + the RecvIndicate timer DPC. + +Arguments: + + Adapter Our adapter + AdapterDpc DPC to be flushed + +Return Value: + + None. + +--*/ +{ + DEBUGP(MP_TRACE, "[%p] ---> RXFlushReceiveQueue\n", Adapter); + + // + // If VMQ enabled, then flush the receive queues for this DPC + // + if(VMQ_ENABLED(Adapter)) + { + USHORT index; + for(index =0; index < NIC_SUPPORTED_NUM_QUEUES; index++) + { + if(AdapterDpc->RecvBlock[index]) + { + NICFlushReceiveBlock(Adapter, index); + } + } + } + else + { + NICFlushReceiveBlock(Adapter, 0); + UNREFERENCED_PARAMETER(AdapterDpc); + } + + DEBUGP(MP_TRACE, "[%p] <--- RXFlushReceiveQueue\n", Adapter); + +} + + +VOID +MPCancelSend( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PVOID CancelId) +/*++ + +Routine Description: + + MiniportCancelSend cancels the transmission of all NET_BUFFER_LISTs that + are marked with a specified cancellation identifier. Miniport drivers + that queue send packets for more than one second should export this + handler. When a protocol driver or intermediate driver calls the + NdisCancelSendNetBufferLists function, NDIS calls the MiniportCancelSend + function of the appropriate lower-level driver (miniport driver or + intermediate driver) on the binding. + + Runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + MiniportAdapterContext Pointer to our adapter + CancelId All the packets with this Id should be cancelled + +Return Value: + + None. + +--*/ +{ + PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); + UNREFERENCED_PARAMETER(Adapter); + UNREFERENCED_PARAMETER(CancelId); + + + DEBUGP(MP_TRACE, "[%p] ---> MPCancelSend\n", Adapter); + + // + // This miniport completes its sends quickly, so it isn't strictly + // neccessary to implement MiniportCancelSend. + // + // If we did implement it, we'd have to walk the Adapter->SendWaitList + // and look for any NB that points to a NBL where the CancelId matches + // NDIS_GET_NET_BUFFER_LIST_CANCEL_ID(Nbl). For any NB that so matches, + // we'd remove the NB from the SendWaitList and set the NBL's status to + // NDIS_STATUS_SEND_ABORTED, then complete the NBL. + // + + DEBUGP(MP_TRACE, "[%p] <--- MPCancelSend\n", Adapter); +} + + +VOID +NICStartTheDatapath( + _In_ PMP_ADAPTER Adapter) +/*++ + +Routine Description: + + This function enables sends and receives on the data path. It is the + reciprocal of NICStopTheDatapath. + + Runs at IRQL == PASSIVE_LEVEL. + +Arguments: + + Adapter Pointer to our adapter + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + MPAttachAdapter(Adapter); +} + + +VOID +NICStopTheDatapath( + _In_ PMP_ADAPTER Adapter) +/*++ + +Routine Description: + + This function prevents future sends and receives on the data path, then + prepares the adapter to reach an idle state. + + Although the adapter is entering an idle state, there may still be + outstanding NBLs that haven't been returned by a protocol. Call NICIsBusy + to check if NBLs are still outstanding. + + Runs at IRQL == PASSIVE_LEVEL. + +Arguments: + + Adapter Pointer to our adapter + +Return Value: + + None. + +--*/ +{ + BOOLEAN fResetCancelled, fSendCancelled; + PLIST_ENTRY ReceiveListEntry; + + DEBUGP(MP_TRACE, "[%p] ---> NICStopTheDatapath.\n", Adapter); + + PAGED_CODE(); + + // + // Remove this adapter from consideration for future receives. + // + MPDetachAdapter(Adapter); + + // + // Free any queued send operations + // + TXFlushSendQueue(Adapter, NDIS_STATUS_FAILURE); + + // + // Prevent new calls to NICAsyncResetOrPauseDpc + // + fResetCancelled = NdisCancelTimerObject(Adapter->AsyncBusyCheckTimer); + + // + // Prevent new calls to RXReceiveIndicateDpc. + // + + for(ReceiveListEntry = Adapter->RecvDpcList.Flink; + ReceiveListEntry != &Adapter->RecvDpcList; + ReceiveListEntry = ReceiveListEntry->Flink) + { + PMP_ADAPTER_RECEIVE_DPC ReceiveDpc = CONTAINING_RECORD(ReceiveListEntry, MP_ADAPTER_RECEIVE_DPC, Entry); + KeRemoveQueueDpc(&ReceiveDpc->Dpc); + } + + // + // Prevent new calls to TXSendCompleteDpc. + // + fSendCancelled = NdisCancelTimerObject(Adapter->SendCompleteTimer); + + // + // Wait for any DPCs (like our reset and recv timers) that were in-progress + // to run to completion. This is slightly expensive to call, but we don't + // mind calling it during MiniportHaltEx, since it's not a performance- + // sensitive path. + // + KeFlushQueuedDpcs(); + + if (fSendCancelled) + { + // Free resources associated with a pending (but cancelled) send + } + + if (fResetCancelled) + { + // Free resources associated with a pending (but cancelled) reset + } + + // + // Double-check that there are still no queued receive operations + // + for(ReceiveListEntry = Adapter->RecvDpcList.Flink; + ReceiveListEntry != &Adapter->RecvDpcList; + ReceiveListEntry = ReceiveListEntry->Flink) + { + RXFlushReceiveQueue(Adapter, CONTAINING_RECORD(ReceiveListEntry, MP_ADAPTER_RECEIVE_DPC, Entry)); + } + + // + // Double-check that there are still no queued send operations + // + TXFlushSendQueue(Adapter, NDIS_STATUS_FAILURE); + + + DEBUGP(MP_TRACE, "[%p] <--- NICStopTheDatapath.\n", Adapter); +} + +ULONG +NICGetFrameTypeFromDestination( + _In_reads_bytes_(NIC_MACADDR_SIZE) PUCHAR DestAddress) +/*++ + +Routine Description: + + Reads the network frame's destination address to determine the type + (broadcast, multicast, etc) + + Runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + DestAddress The frame's destination address + +Return Value: + + NDIS_PACKET_TYPE_BROADCAST + NDIS_PACKET_TYPE_MULTICAST + NDIS_PACKET_TYPE_DIRECTED + +--*/ +{ + if (NIC_ADDR_IS_BROADCAST(DestAddress)) + { + return NDIS_PACKET_TYPE_BROADCAST; + } + else if(NIC_ADDR_IS_MULTICAST(DestAddress)) + { + return NDIS_PACKET_TYPE_MULTICAST; + } + else + { + return NDIS_PACKET_TYPE_DIRECTED; + } +} + diff --git a/network/ndis/netvmini/6x/datapath.h b/network/ndis/netvmini/6x/datapath.h new file mode 100644 index 00000000..2926c7ca --- /dev/null +++ b/network/ndis/netvmini/6x/datapath.h @@ -0,0 +1,77 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + DataPath.H + +Abstract: + + This module declares the TCB and RCB structures, and the functions to + manipulate them. + + See the comments in TcbRcb.c. + +--*/ + + + + +#ifndef _DATAPATH_H +#define _DATAPATH_H + + + + +NDIS_STATUS +TXNblReference( + _In_ PMP_ADAPTER Adapter, + _In_ PNET_BUFFER_LIST NetBufferList); + +VOID +TXNblRelease( + _In_ PMP_ADAPTER Adapter, + _In_ PNET_BUFFER_LIST NetBufferList, + _In_ BOOLEAN fAtDispatch); + +NDIS_TIMER_FUNCTION TXSendCompleteDpc; + +KDEFERRED_ROUTINE RXReceiveIndicateDpc; + +VOID +RXDeliverFrameToEveryAdapter( + _In_ PMP_ADAPTER SendAdapter, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _In_ PFRAME Frame, + _In_ BOOLEAN fAtDispatch); + +VOID +RXFlushReceiveQueue( + _In_ PMP_ADAPTER Adapter, + _In_ PMP_ADAPTER_RECEIVE_DPC AdapterDpc); + +VOID +TXFlushSendQueue( + _In_ PMP_ADAPTER Adapter, + _In_ NDIS_STATUS CompleteStatus); + +VOID +NICStartTheDatapath( + _In_ PMP_ADAPTER Adapter); + +VOID +NICStopTheDatapath( + _In_ PMP_ADAPTER Adapter); + +ULONG +NICGetFrameTypeFromDestination( + _In_reads_bytes_(NIC_MACADDR_SIZE) PUCHAR DestAddress); + +#endif // _DATAPATH_H + diff --git a/network/ndis/netvmini/6x/hardware.h b/network/ndis/netvmini/6x/hardware.h new file mode 100644 index 00000000..c3076429 --- /dev/null +++ b/network/ndis/netvmini/6x/hardware.h @@ -0,0 +1,321 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Hardware.H + +Abstract: + + This module defines constants that describe physical characteristics and + limits of the underlying hardware. While this miniport is virtual -- it + doesn't have any "real" hardware -- it emulates a fast Ethernet (802.3 at + 100 Mbit/s) link. Constants (like the 1514 byte frame size) are based on + typical Ethernet parameters. + + TODO: + 1. Change the vendor name and ID from Microsoft. + 2. Update the hardware limits and physical adapter properties to match + those of your hardware. + 3. If your hardware does not use a 100 Mbit/s Ethernet medium, also + update the addressing, framing, and medium sections. + +--*/ + + +#ifndef _HARDWARE_H +#define _HARDWARE_H + + +// +// Link layer addressing +// ----------------------------------------------------------------------------- +// + +// Number of bytes in a hardware address. Ethernet uses 6 byte addresses. +#define NIC_MACADDR_SIZE ETH_LENGTH_OF_ADDRESS + +// True iff the given address is a multicast address +#define NIC_ADDR_IS_MULTICAST(_addr) ETH_IS_MULTICAST(_addr) + +// True iff the given address is a broadcast address +#define NIC_ADDR_IS_BROADCAST(_addr) ETH_IS_BROADCAST(_addr) + +// Copies a hardware address from _src to _dest +#define NIC_COPY_ADDRESS(_dest,_src) ETH_COPY_NETWORK_ADDRESS(_dest,_src) + +// True iff the given address was assigned by the local administrator +#define NIC_ADDR_IS_LOCALLY_ADMINISTERED(_addr) \ + (BOOLEAN)(((PUCHAR)(_addr))[0] & ((UCHAR)0x02)) + +// True iff two addresses are equal +#define NIC_ADDR_EQUAL(_a,_b) \ + ((*(ULONG UNALIGNED *)&(_a)[2] == *(ULONG UNALIGNED *)&(_b)[2]) \ + && (*(USHORT UNALIGNED *)(_a) == *(USHORT UNALIGNED *)(_b))) + + + +// +// Frames +// ----------------------------------------------------------------------------- +// + +#define HW_FRAME_HEADER_SIZE 14 +#define HW_FRAME_MAX_DATA_SIZE 1500 +#define HW_MAX_FRAME_SIZE (HW_FRAME_HEADER_SIZE + HW_FRAME_MAX_DATA_SIZE) +#define HW_MIN_FRAME_SIZE 60 + +typedef struct tagNIC_FRAME_HEADER +{ + UCHAR DestAddress[NIC_MACADDR_SIZE]; + UCHAR SrcAddress[NIC_MACADDR_SIZE]; + UCHAR EtherType[2]; +} NIC_FRAME_HEADER, *PNIC_FRAME_HEADER; + +C_ASSERT(sizeof(NIC_FRAME_HEADER) == HW_FRAME_HEADER_SIZE); + +#define GET_DESTINATION_OF_FRAME(_dest, _frame) NdisMoveMemory(_dest, ((PNIC_FRAME_HEADER)(_frame))->DestAddress, NIC_MACADDR_SIZE) + +// +// Medium properties +// ----------------------------------------------------------------------------- +// + +#define NIC_MEDIUM_TYPE NdisMedium802_3 + +// If you have physical hardware on 802.3, use NdisPhysicalMedium802_3. +#define NIC_PHYSICAL_MEDIUM NdisPhysicalMediumUnspecified + + +// Set this value to TRUE if there is a physical adapter. +#define NIC_HAS_PHYSICAL_CONNECTOR FALSE +#define NIC_ACCESS_TYPE NET_IF_ACCESS_BROADCAST +#define NIC_DIRECTION_TYPE NET_IF_DIRECTION_SENDRECEIVE +#define NIC_CONNECTION_TYPE NET_IF_CONNECTION_DEDICATED + +// This value must match the *IfType in the driver .inf file +#define NIC_IFTYPE IF_TYPE_ETHERNET_CSMACD + +// Claim to be 100mbps duplex +#define MEGABITS_PER_SECOND 1000000ULL +#define NIC_XMIT_SPEED (100ULL*MEGABITS_PER_SECOND) +#define NIC_RECV_SPEED (100ULL*MEGABITS_PER_SECOND) + + + +// +// Hardware limits +// ----------------------------------------------------------------------------- +// + +// Max number of multicast addresses supported in hardware +#define NIC_MAX_MCAST_LIST 32 + +// Maximum number of uncompleted sends that a single adapter will permit +#define NIC_MAX_BUSY_SENDS 256 + +// Maximum number of unreturned receives that a single adapter will permit +#define NIC_MAX_BUSY_RECVS 256 + + + +// Maximum number of send completes that will be processed per DPC. +#define NIC_MAX_SENDS_PER_DPC 64 + +// +// Maximum number of receives that will be processed per DPC. +// This constraints the amount of time spent for a single receive DPC. +// +#define NIC_MAX_RECVS_PER_DPC 64 + +#define NIC_MAX_LOOKAHEAD HW_FRAME_MAX_DATA_SIZE +#define NIC_BUFFER_SIZE HW_MAX_FRAME_SIZE + + +// Simulated latency across the link. If this is set to zero, the driver +// will saturate the link. Unfortunately, when the "link" is simulated in CPU, +// that means that the CPU is saturated, and you hit DPC timeouts. +// (This throttling isn't needed with a physical NIC) +#define NIC_SIMULATED_LATENCY 0 // in 100ns units + +// +// Physical adapter properties +// ----------------------------------------------------------------------------- +// + +// The bus that connects the adapter to the PC. +// (Example: PCI adapters should use NdisInterfacePci). +#define NIC_INTERFACE_TYPE NdisInterfaceInternal + +// Change to your company name instead of using Microsoft +#define NIC_VENDOR_DESC "Microsoft" + +// Highest byte is the NIC byte plus three vendor bytes. This is normally +// obtained from the NIC. +#define NIC_VENDOR_ID 0x00FFFFFF + + +// Wakeup capabilities, as in OID_PNP_CAPABILITIES +#define NIC_MAGIC_PACKET_WAKEUP NdisDeviceStateUnspecified +#define NIC_PATTERN_WAKEUP NdisDeviceStateUnspecified +#define NIC_LINK_CHANGE_WAKEUP NdisDeviceStateUnspecified + + +#define NIC_SUPPORTED_FILTERS ( \ + NDIS_PACKET_TYPE_DIRECTED | \ + NDIS_PACKET_TYPE_MULTICAST | \ + NDIS_PACKET_TYPE_BROADCAST | \ + NDIS_PACKET_TYPE_PROMISCUOUS | \ + NDIS_PACKET_TYPE_ALL_MULTICAST) + +// +// This sample is a virtual device, so it can tolerate surprise removal +// and suspend. Ensure the correct flags are set for your hardware. +// +// If your hardware supports busmaster DMA, you must specify +// NDIS_MINIPORT_ATTRIBUTES_BUS_MASTER. Our virtual miniport will +// not be allocating hardware resources such as interrupts, so we set the +// WDM attribute. +// +#define NIC_ADAPTER_ATTRIBUTES_FLAGS (\ + NDIS_MINIPORT_ATTRIBUTES_SURPRISE_REMOVE_OK | NDIS_MINIPORT_ATTRIBUTES_NDIS_WDM) + + +// +// Specify a bitmask that defines optional properties of the NIC. +// This miniport indicates receive with NdisMIndicateReceiveNetBufferLists +// function. Such a driver should set this NDIS_MAC_OPTION_TRANSFERS_NOT_PEND +// flag. +// +// NDIS_MAC_OPTION_NO_LOOPBACK tells NDIS that NIC has no internal +// loopback support so NDIS will manage loopbacks on behalf of +// this driver. +// +// NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA tells the protocol that +// our receive buffer is not on a device-specific card. If +// NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA is not set, multi-buffer +// indications are copied to a single flat buffer. +// +#define NIC_MAC_OPTIONS (\ + NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | \ + NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | \ + NDIS_MAC_OPTION_NO_LOOPBACK | \ + NDIS_MAC_OPTION_8021P_PRIORITY | \ + NDIS_MAC_OPTION_8021Q_VLAN) + +// NDIS 6.x miniports must support all counters in OID_GEN_STATISTICS. +#define NIC_SUPPORTED_STATISTICS (\ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS | \ + NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR | \ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR | \ + NDIS_STATISTICS_FLAGS_VALID_XMIT_DISCARDS | \ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_RCV | \ + NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_XMIT | \ + NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_XMIT) + +#if (NDIS_SUPPORT_NDIS620) + +// +// VMQ hardware information +// + +// +// The NIC must reserve at least one filter available HW queue. More filters allows +// the VMQ queues to be assigned asymmetrically. For this sample we chose to allow +// twice as many filters as queues. +// +#define NIC_SUPPORTED_NUM_QUEUES 8 +#define NIC_MAX_HEADER_FILTERS (NIC_SUPPORTED_NUM_QUEUES*2) + +// +// Determines the minimum and maximum amount of lookahead split that we can do. Real hardware +// might have tighter constraints on the range depending on the HW design. +// +#define NIC_MIN_LOOKAHEAD_SPLIT 64 +#define NIC_MAX_LOOKAHEAD_SPLIT 128 + +// +// Determines the minimum amount of receive blocks we will attempt to allocate if the full allocations fail and +// we retry with a reduced count. If we fail with this amount we fail the allocation. +// +#define NIC_MIN_BUSY_RECVS 64 + +#else + +// +// No VMQ, define the queue count to 1 so that we only allocate the global receive block +// +#define NIC_SUPPORTED_NUM_QUEUES 1 + +#endif + +#if (NDIS_SUPPORT_NDIS630) + +// +// NDIS QoS hardware information +// + +// +// Total number of general Traffic Classes (TCs) that the hardware supports, each of which is capable of but +// not just limited to ETS functionality. +// +#define NIC_SUPPORTED_NUM_TCS 8 + +// +// Total number of TCs that the hardware supports that is capable of only ETS functionality. +// +#define NIC_SUPPORTED_NUM_ETS_CAPABLE_TCS NIC_SUPPORTED_NUM_TCS +C_ASSERT(NIC_SUPPORTED_NUM_ETS_CAPABLE_TCS <= NIC_SUPPORTED_NUM_TCS); + +// +// Number of simultaneous PFC-enabled TCs that the hardware can support. +// +#define NIC_SUPPORTED_NUM_PFC_ENABLED_TCS NIC_SUPPORTED_NUM_TCS +C_ASSERT(NIC_SUPPORTED_NUM_PFC_ENABLED_TCS <= NIC_SUPPORTED_NUM_TCS); + +// +// The Ethertype value for FCoE traffic and the default 802.1p priority assignment. +// +#define NIC_FCOE_ETHERTYPE 0x8906 +#define NIC_FCOE_PRIORITY 1 + +// +// The TCP port number for iSCSI traffic and the default 802.1p priority assignment. +// +#define NIC_ISCSI_TCP_PORT 3260 +#define NIC_ISCSI_PRIORITY 2 + +// +// Maximum number of classification entries that the miniport can support. +// +// In most cases, a standard NDIS QoS-compliant miniport will look for one or two very specific classification entries +// and not implement a general classification mechanism as is done here. +// +// For example, a DCB-capable NIC with its own FCoE implementation would, in its NDIS QoS-compliant miniport, look for +// a QoS classification entry specifying Ethertype 0x8906. The associated priority value would then be used in +// all FCoE packets sent by that NIC. +// +#define NIC_SUPPORTED_NUM_CLASSIFICATIONS 2 + + +#endif + +#endif // _HARDWARE_H + diff --git a/network/ndis/netvmini/6x/miniport.c b/network/ndis/netvmini/6x/miniport.c new file mode 100644 index 00000000..08d8cc3b --- /dev/null +++ b/network/ndis/netvmini/6x/miniport.c @@ -0,0 +1,622 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Miniport.C + +Abstract: + + The purpose of this sample is to illustrate functionality of a deserialized + NDIS miniport driver without requiring a physical network adapter. This + sample is based on E100BEX sample present in the DDK. It is basically a + simplified version of E100bex driver. The driver can be installed either + manually using Add Hardware wizard as a root enumerated virtual miniport + driver or on a virtual bus (like toaster bus). Since the driver does not + interact with any hardware, it makes it very easy to understand the miniport + interface and the usage of various NDIS functions without the clutter of + hardware specific code normally found in a fully functional driver. + + This sample provides an example of minimal driver intended for education + purposes. Neither the driver or its sample test programs are intended + for use in a production environment. + +Revision History: + +Notes: + +--*/ +#include "netvmin6.h" +#include "miniport.tmh" + +NDIS_STATUS +DriverEntry( + _In_ PVOID DriverObject, + _In_ PVOID RegistryPath); + + +#pragma NDIS_INIT_FUNCTION(DriverEntry) +#pragma NDIS_PAGEABLE_FUNCTION(DriverUnload) + + +MP_GLOBAL GlobalData; +NDIS_HANDLE NdisDriverHandle; + +static +NDIS_STATUS +InitializeAdapterListLock(); + +static +VOID +FreeAdapterListLock(); + +NDIS_STATUS +DriverEntry( + _In_ PVOID DriverObject, + _In_ PVOID RegistryPath) +/*++ +Routine Description: + + In the context of its DriverEntry function, a miniport driver associates + itself with NDIS, specifies the NDIS version that it is using, and + registers its entry points. + + +Arguments: + PVOID DriverObject - pointer to the driver object. + PVOID RegistryPath - pointer to the driver registry path. + + Return Value: + + NTSTATUS code + +--*/ +{ + NDIS_STATUS Status; + NDIS_MINIPORT_DRIVER_CHARACTERISTICS MPChar; + + WPP_INIT_TRACING(DriverObject,RegistryPath); + + DEBUGP(MP_TRACE, "---> DriverEntry built on "__DATE__" at "__TIME__ "\n"); + PAGED_CODE(); + + do + { + // + // Initialize any driver-global variables here. + // + + NdisZeroMemory(&GlobalData, sizeof(GlobalData)); + + // + // The ApaterList in the GlobalData structure is used to track multiple + // adapters controlled by this miniport. + // + NdisInitializeListHead(&GlobalData.AdapterList); + + + // + // The FrameDataLookaside list is used to help emulate an Ethernet hub. + // + NdisInitializeNPagedLookasideList( + &GlobalData.FrameDataLookaside, + HWFrameAllocate, + HWFrameFree, + 0, // Reserved for system use + sizeof(FRAME), + NIC_TAG_FRAME, + 0); // Reserved for system use + GlobalData.Flags |= fGLOBAL_LOOKASIDE_INITIALIZED; + + // + // Fill in the Miniport characteristics structure with the version numbers + // and the entry points for driver-supplied MiniportXxx + // + + NdisZeroMemory(&MPChar, sizeof(MPChar)); + + +#if (NDIS_SUPPORT_NDIS620) + {C_ASSERT(sizeof(MPChar) >= NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2);} + MPChar.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS; + MPChar.Header.Size = NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2; + MPChar.Header.Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2; +#elif (NDIS_SUPPORT_NDIS6) + {C_ASSERT(sizeof(MPChar) >= NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_1);} + MPChar.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS; + MPChar.Header.Size = NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_1; + MPChar.Header.Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_1; +#endif // NDIS MINIPORT VERSION + + MPChar.MajorNdisVersion = MP_NDIS_MAJOR_VERSION; + MPChar.MinorNdisVersion = MP_NDIS_MINOR_VERSION; + + MPChar.MajorDriverVersion = NIC_MAJOR_DRIVER_VERSION; + MPChar.MinorDriverVersion = NIC_MINOR_DRIVER_VERISON; + + MPChar.Flags = 0; + + MPChar.SetOptionsHandler = MPSetOptions; // Optional + MPChar.InitializeHandlerEx = MPInitializeEx; + MPChar.HaltHandlerEx = MPHaltEx; + MPChar.UnloadHandler = DriverUnload; + MPChar.PauseHandler = MPPause; + MPChar.RestartHandler = MPRestart; + MPChar.OidRequestHandler = MPOidRequest; + MPChar.SendNetBufferListsHandler = MPSendNetBufferLists; + MPChar.ReturnNetBufferListsHandler = MPReturnNetBufferLists; + MPChar.CancelSendHandler = MPCancelSend; + MPChar.CheckForHangHandlerEx = MPCheckForHangEx; + MPChar.ResetHandlerEx = MPResetEx; + MPChar.DevicePnPEventNotifyHandler = MPDevicePnpEventNotify; + MPChar.ShutdownHandlerEx = MPShutdownEx; + MPChar.CancelOidRequestHandler = MPCancelOidRequest; + + // + // Associate the miniport driver with NDIS by calling the + // NdisMRegisterMiniportDriver. This function returns an NdisDriverHandle. + // The miniport driver must retain this handle but it should never attempt + // to access or interpret this handle. + // + // By calling NdisMRegisterMiniportDriver, the driver indicates that it + // is ready for NDIS to call the driver's MiniportSetOptions and + // MiniportInitializeEx handlers. + // + DEBUGP(MP_LOUD, "Calling NdisMRegisterMiniportDriver...\n"); + NDIS_DECLARE_MINIPORT_DRIVER_CONTEXT(MP_GLOBAL); + Status = NdisMRegisterMiniportDriver( + DriverObject, + RegistryPath, + &GlobalData, + &MPChar, + &NdisDriverHandle); + if (NDIS_STATUS_SUCCESS != Status) + { + DEBUGP(MP_ERROR, "NdisMRegisterMiniportDriver failed: %d\n", Status); + DriverUnload(DriverObject); + Status = NDIS_STATUS_FAILURE; + break; + } + + GlobalData.Flags |= fGLOBAL_MINIPORT_REGISTERED; + + // + // The AdapterListLock protects the AdapterList + // + Status = InitializeAdapterListLock(); + if (Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "InitializeAdapterListLock failed 0x%08x", Status); + DriverUnload(DriverObject); + Status = NDIS_STATUS_FAILURE; + break; + } + GlobalData.Flags |= fGLOBAL_LOCK_ALLOCATED; + }while(FALSE); + + DEBUGP(MP_TRACE, "<--- DriverEntry Status 0x%08x\n", Status); + return Status; +} + + +VOID +DriverUnload( + _In_ PDRIVER_OBJECT DriverObject) +/*++ + +Routine Description: + + The unload handler is called during driver unload to free up resources + acquired in DriverEntry. This handler is registered in DriverEntry through + NdisMRegisterMiniportDriver. Note that an unload handler differs from + a MiniportHalt function in that this unload handler releases resources that + are global to the driver, while the halt handler releases resource for a + particular adapter. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + DriverObject Not used + +Return Value: + + None. + +--*/ +{ + UNREFERENCED_PARAMETER(DriverObject); + + DEBUGP(MP_TRACE, "---> DriverUnload\n"); + PAGED_CODE(); + + + // + // Clean up all globals that were allocated in DriverEntry + // + + + ASSERT(IsListEmpty(&GlobalData.AdapterList)); + + + if (GlobalData.Flags & fGLOBAL_MINIPORT_REGISTERED) + { + // + // Since DriverEntry has successfully called NdisMRegisterMiniportDriver, + // NdisMDeregisterMiniportDriver must be called to release NDIS's per-driver + // resources. + // + DEBUGP(MP_LOUD, "Calling NdisMDeregisterMiniportDriver...\n"); + NdisMDeregisterMiniportDriver(NdisDriverHandle); + } + + if (GlobalData.Flags & fGLOBAL_LOOKASIDE_INITIALIZED) + { + NdisDeleteNPagedLookasideList(&GlobalData.FrameDataLookaside); + } + + if (GlobalData.Flags & fGLOBAL_LOCK_ALLOCATED) + { + FreeAdapterListLock(); + } + + WPP_CLEANUP(DriverObject->DeviceObject); + + DEBUGP(MP_TRACE, "<--- DriverUnload\n"); +} + + +NDIS_STATUS +MPSetOptions( + _In_ NDIS_HANDLE DriverHandle, + _In_ NDIS_HANDLE DriverContext) +/*++ +Routine Description: + + The MiniportSetOptions function registers optional handlers. For each + optional handler that should be registered, this function makes a call + to NdisSetOptionalHandlers. + + MiniportSetOptions runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + DriverContext The context handle + +Return Value: + + NDIS_STATUS_xxx code + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PMP_GLOBAL Global = (PMP_GLOBAL)DriverContext; + + DEBUGP(MP_TRACE, "---> MPSetOptions\n"); + UNREFERENCED_PARAMETER(DriverHandle); + UNREFERENCED_PARAMETER(Global); + + + // + // Set any optional handlers by filling out the appropriate struct and + // calling NdisSetOptionalHandlers here. + // + + + DEBUGP(MP_TRACE, "<--- MPSetOptions Status = 0x%08x\n", Status); + return Status; +} + +BOOLEAN +MPIsAdapterAttached( + _In_ PMP_ADAPTER Adapter) +{ + PLIST_ENTRY CurrentEntry = NULL; + for ( + CurrentEntry = GlobalData.AdapterList.Flink; + CurrentEntry != &GlobalData.AdapterList; + CurrentEntry = CurrentEntry->Flink) + { + if(CurrentEntry == &Adapter->List) + { + return TRUE; + } + } + + return FALSE; +} + +VOID +MPAttachAdapter( + _In_ PMP_ADAPTER Adapter) +{ + MP_LOCK_STATE LockState; + + DEBUGP(MP_TRACE, "[%p] ---> MPAttachAdapter\n", Adapter); + + LOCK_ADAPTER_LIST_FOR_WRITE(&LockState, 0); + + if(!MPIsAdapterAttached(Adapter)) + { + InsertTailList(&GlobalData.AdapterList, &Adapter->List); + } + + UNLOCK_ADAPTER_LIST(&LockState); + + DEBUGP(MP_TRACE, "[%p] <--- MPAttachAdapter\n", Adapter); +} + +VOID +MPDetachAdapter( + _In_ PMP_ADAPTER Adapter) +{ + MP_LOCK_STATE LockState; + DEBUGP(MP_TRACE, "[%p] ---> MPDetachAdapter\n", Adapter); + + LOCK_ADAPTER_LIST_FOR_WRITE(&LockState, 0); + + if(MPIsAdapterAttached(Adapter)) + { + RemoveEntryList(&Adapter->List); + } + + UNLOCK_ADAPTER_LIST(&LockState); + + DEBUGP(MP_TRACE, "[%p] <--- MPDetachAdapter\n", Adapter); +} + + +#if (NDIS_SUPPORT_NDIS620) + +NDIS_STATUS +InitializeAdapterListLock() +{ + GlobalData.Lock = NdisAllocateRWLock(NdisDriverHandle); + if (!GlobalData.Lock) + { + return NDIS_STATUS_RESOURCES; + } + + return NDIS_STATUS_SUCCESS; +} + +VOID +FreeAdapterListLock() +{ + ASSERT(GlobalData.Lock); + NdisFreeRWLock(GlobalData.Lock); + GlobalData.Lock = NULL; +} + +#elif (NDIS_SUPPORT_NDIS6) + +NDIS_STATUS +InitializeAdapterListLock() +{ + NdisInitializeReadWriteLock(&GlobalData.Lock); + return NDIS_STATUS_SUCCESS; +} + +VOID +FreeAdapterListLock() +{ + // No action needed to clean up a NDIS 6.0 RW lock. +} + +#endif + + + +#ifndef DBG + +VOID +DbgPrintOidName( + _In_ NDIS_OID Oid) +{ + UNREFERENCED_PARAMETER(Oid); +} + +VOID +DbgPrintAddress( + _In_reads_bytes_(NIC_MACADDR_SIZE) PUCHAR Address) +{ + UNREFERENCED_PARAMETER(Address); +} + + +#else // DBG + +VOID +DbgPrintOidName( + _In_ NDIS_OID Oid) +{ + PCHAR oidName = NULL; + + switch (Oid){ + + #undef MAKECASE + #define MAKECASE(oidx) case oidx: oidName = #oidx "\n"; break; + + /* Operational OIDs */ + MAKECASE(OID_GEN_SUPPORTED_LIST) + MAKECASE(OID_GEN_HARDWARE_STATUS) + MAKECASE(OID_GEN_MEDIA_SUPPORTED) + MAKECASE(OID_GEN_MEDIA_IN_USE) + MAKECASE(OID_GEN_MAXIMUM_LOOKAHEAD) + MAKECASE(OID_GEN_MAXIMUM_FRAME_SIZE) + MAKECASE(OID_GEN_LINK_SPEED) + MAKECASE(OID_GEN_TRANSMIT_BUFFER_SPACE) + MAKECASE(OID_GEN_RECEIVE_BUFFER_SPACE) + MAKECASE(OID_GEN_TRANSMIT_BLOCK_SIZE) + MAKECASE(OID_GEN_RECEIVE_BLOCK_SIZE) + MAKECASE(OID_GEN_VENDOR_ID) + MAKECASE(OID_GEN_VENDOR_DESCRIPTION) + MAKECASE(OID_GEN_VENDOR_DRIVER_VERSION) + MAKECASE(OID_GEN_CURRENT_PACKET_FILTER) + MAKECASE(OID_GEN_CURRENT_LOOKAHEAD) + MAKECASE(OID_GEN_DRIVER_VERSION) + MAKECASE(OID_GEN_MAXIMUM_TOTAL_SIZE) + MAKECASE(OID_GEN_PROTOCOL_OPTIONS) + MAKECASE(OID_GEN_MAC_OPTIONS) + MAKECASE(OID_GEN_MEDIA_CONNECT_STATUS) + MAKECASE(OID_GEN_MAXIMUM_SEND_PACKETS) + MAKECASE(OID_GEN_SUPPORTED_GUIDS) + MAKECASE(OID_GEN_NETWORK_LAYER_ADDRESSES) + MAKECASE(OID_GEN_TRANSPORT_HEADER_OFFSET) + MAKECASE(OID_GEN_MEDIA_CAPABILITIES) + MAKECASE(OID_GEN_PHYSICAL_MEDIUM) + MAKECASE(OID_GEN_MACHINE_NAME) + MAKECASE(OID_GEN_VLAN_ID) + MAKECASE(OID_GEN_RNDIS_CONFIG_PARAMETER) + + /* Operational OIDs for NDIS 6.0 */ + MAKECASE(OID_GEN_MAX_LINK_SPEED) + MAKECASE(OID_GEN_LINK_STATE) + MAKECASE(OID_GEN_LINK_PARAMETERS) + MAKECASE(OID_GEN_MINIPORT_RESTART_ATTRIBUTES) + MAKECASE(OID_GEN_ENUMERATE_PORTS) + MAKECASE(OID_GEN_PORT_STATE) + MAKECASE(OID_GEN_PORT_AUTHENTICATION_PARAMETERS) + MAKECASE(OID_GEN_INTERRUPT_MODERATION) + MAKECASE(OID_GEN_PHYSICAL_MEDIUM_EX) + + /* Statistical OIDs */ + MAKECASE(OID_GEN_XMIT_OK) + MAKECASE(OID_GEN_RCV_OK) + MAKECASE(OID_GEN_XMIT_ERROR) + MAKECASE(OID_GEN_RCV_ERROR) + MAKECASE(OID_GEN_RCV_NO_BUFFER) + MAKECASE(OID_GEN_DIRECTED_BYTES_XMIT) + MAKECASE(OID_GEN_DIRECTED_FRAMES_XMIT) + MAKECASE(OID_GEN_MULTICAST_BYTES_XMIT) + MAKECASE(OID_GEN_MULTICAST_FRAMES_XMIT) + MAKECASE(OID_GEN_BROADCAST_BYTES_XMIT) + MAKECASE(OID_GEN_BROADCAST_FRAMES_XMIT) + MAKECASE(OID_GEN_DIRECTED_BYTES_RCV) + MAKECASE(OID_GEN_DIRECTED_FRAMES_RCV) + MAKECASE(OID_GEN_MULTICAST_BYTES_RCV) + MAKECASE(OID_GEN_MULTICAST_FRAMES_RCV) + MAKECASE(OID_GEN_BROADCAST_BYTES_RCV) + MAKECASE(OID_GEN_BROADCAST_FRAMES_RCV) + MAKECASE(OID_GEN_RCV_CRC_ERROR) + MAKECASE(OID_GEN_TRANSMIT_QUEUE_LENGTH) + + /* Statistical OIDs for NDIS 6.0 */ + MAKECASE(OID_GEN_STATISTICS) + MAKECASE(OID_GEN_BYTES_RCV) + MAKECASE(OID_GEN_BYTES_XMIT) + MAKECASE(OID_GEN_RCV_DISCARDS) + MAKECASE(OID_GEN_XMIT_DISCARDS) + + /* Misc OIDs */ + MAKECASE(OID_GEN_GET_TIME_CAPS) + MAKECASE(OID_GEN_GET_NETCARD_TIME) + MAKECASE(OID_GEN_NETCARD_LOAD) + MAKECASE(OID_GEN_DEVICE_PROFILE) + MAKECASE(OID_GEN_INIT_TIME_MS) + MAKECASE(OID_GEN_RESET_COUNTS) + MAKECASE(OID_GEN_MEDIA_SENSE_COUNTS) + + /* PnP power management operational OIDs */ + MAKECASE(OID_PNP_CAPABILITIES) + MAKECASE(OID_PNP_SET_POWER) + MAKECASE(OID_PNP_QUERY_POWER) + MAKECASE(OID_PNP_ADD_WAKE_UP_PATTERN) + MAKECASE(OID_PNP_REMOVE_WAKE_UP_PATTERN) + MAKECASE(OID_PNP_ENABLE_WAKE_UP) + MAKECASE(OID_PNP_WAKE_UP_PATTERN_LIST) + + /* PnP power management statistical OIDs */ + MAKECASE(OID_PNP_WAKE_UP_ERROR) + MAKECASE(OID_PNP_WAKE_UP_OK) + + /* Ethernet operational OIDs */ + MAKECASE(OID_802_3_PERMANENT_ADDRESS) + MAKECASE(OID_802_3_CURRENT_ADDRESS) + MAKECASE(OID_802_3_MULTICAST_LIST) + MAKECASE(OID_802_3_MAXIMUM_LIST_SIZE) + MAKECASE(OID_802_3_MAC_OPTIONS) + + /* Ethernet operational OIDs for NDIS 6.0 */ + MAKECASE(OID_802_3_ADD_MULTICAST_ADDRESS) + MAKECASE(OID_802_3_DELETE_MULTICAST_ADDRESS) + + /* Ethernet statistical OIDs */ + MAKECASE(OID_802_3_RCV_ERROR_ALIGNMENT) + MAKECASE(OID_802_3_XMIT_ONE_COLLISION) + MAKECASE(OID_802_3_XMIT_MORE_COLLISIONS) + MAKECASE(OID_802_3_XMIT_DEFERRED) + MAKECASE(OID_802_3_XMIT_MAX_COLLISIONS) + MAKECASE(OID_802_3_RCV_OVERRUN) + MAKECASE(OID_802_3_XMIT_UNDERRUN) + MAKECASE(OID_802_3_XMIT_HEARTBEAT_FAILURE) + MAKECASE(OID_802_3_XMIT_TIMES_CRS_LOST) + MAKECASE(OID_802_3_XMIT_LATE_COLLISIONS) + + /* TCP/IP OIDs */ + MAKECASE(OID_TCP_TASK_OFFLOAD) + MAKECASE(OID_TCP_TASK_IPSEC_ADD_SA) + MAKECASE(OID_TCP_TASK_IPSEC_DELETE_SA) + MAKECASE(OID_TCP_SAN_SUPPORT) + MAKECASE(OID_TCP_TASK_IPSEC_ADD_UDPESP_SA) + MAKECASE(OID_TCP_TASK_IPSEC_DELETE_UDPESP_SA) + MAKECASE(OID_TCP4_OFFLOAD_STATS) + MAKECASE(OID_TCP6_OFFLOAD_STATS) + MAKECASE(OID_IP4_OFFLOAD_STATS) + MAKECASE(OID_IP6_OFFLOAD_STATS) + + /* TCP offload OIDs for NDIS 6 */ + MAKECASE(OID_TCP_OFFLOAD_CURRENT_CONFIG) + MAKECASE(OID_TCP_OFFLOAD_PARAMETERS) + MAKECASE(OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) + MAKECASE(OID_TCP_CONNECTION_OFFLOAD_CURRENT_CONFIG) + MAKECASE(OID_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES) + MAKECASE(OID_OFFLOAD_ENCAPSULATION) + +#if (NDIS_SUPPORT_NDIS620) + /* VMQ OIDs for NDIS 6.20 */ + MAKECASE(OID_RECEIVE_FILTER_FREE_QUEUE) + MAKECASE(OID_RECEIVE_FILTER_CLEAR_FILTER) + MAKECASE(OID_RECEIVE_FILTER_ALLOCATE_QUEUE) + MAKECASE(OID_RECEIVE_FILTER_QUEUE_ALLOCATION_COMPLETE) + MAKECASE(OID_RECEIVE_FILTER_SET_FILTER) +#endif + +#if (NDIS_SUPPORT_NDIS630) + /* NDIS QoS OIDs for NDIS 6.30 */ + MAKECASE(OID_QOS_PARAMETERS) +#endif + } + + if (oidName) + { + DEBUGP(MP_LOUD, "%s", oidName); + } + else + { + DEBUGP(MP_LOUD, "<** Unknown OID 0x%08x **>\n", Oid); + } +} + +VOID +DbgPrintAddress( + _In_reads_bytes_(NIC_MACADDR_SIZE) PUCHAR Address) +{ + // If your MAC address has a different size, adjust the printf accordingly. + {C_ASSERT(NIC_MACADDR_SIZE == 6);} + + DEBUGP(MP_LOUD, "%02x-%02x-%02x-%02x-%02x-%02x\n", + Address[0], Address[1], Address[2], + Address[3], Address[4], Address[5]); +} + + + +#endif //DBG + diff --git a/network/ndis/netvmini/6x/miniport.h b/network/ndis/netvmini/6x/miniport.h new file mode 100644 index 00000000..9b922b1a --- /dev/null +++ b/network/ndis/netvmini/6x/miniport.h @@ -0,0 +1,255 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Miniport.H + +Abstract: + + This module contains structure definitons and function prototypes. + + TODO: + 1. Set the correct driver version number for your versioning scheme. + 2. Create unique memory allocation tags. + + --*/ + + +#ifndef _MINIPORT_H +#define _MINIPORT_H + + + +// +// Update the driver version number every time you release a new driver +// The high word is the major version. The low word is the minor version. +// Also make sure that VER_FILEVERSION specified in the .RC file also +// matches with the driver version because NDISTESTER checks for that. +// +// Let's say we're version 4.2. +// +#define NIC_MAJOR_DRIVER_VERSION 0x04 +#define NIC_MINOR_DRIVER_VERISON 0x02 +#define NIC_VENDOR_DRIVER_VERSION ((NIC_MAJOR_DRIVER_VERSION << 16) | NIC_MINOR_DRIVER_VERISON) + + + +// +// Define the NDIS miniport interface version that this driver targets. +// +#if defined(NDIS60_MINIPORT) +# define MP_NDIS_MAJOR_VERSION 6 +# define MP_NDIS_MINOR_VERSION 0 +#elif defined(NDIS620_MINIPORT) +# define MP_NDIS_MAJOR_VERSION 6 +# define MP_NDIS_MINOR_VERSION 20 +#elif defined(NDIS630_MINIPORT) +# define MP_NDIS_MAJOR_VERSION 6 +# define MP_NDIS_MINOR_VERSION 30 +#else +# error Unsupported NDIS version +#endif + + +// +// Memory allocation tags to help track and debug memory usage. Change these +// for your miniport. You can add or remove tags as needed. +// +#define NIC_TAG ((ULONG)'_MVN') // NVM_ +#define NIC_TAG_TCB ((ULONG)'TMVN') // NVMT +#define NIC_TAG_RCB ((ULONG)'RMVN') // NVMR +#define NIC_TAG_RECV_NBL ((ULONG)'rMVN') // NVMr +#define NIC_TAG_FRAME ((ULONG)'FMVN') // NVMF +#define NIC_TAG_DPC ((ULONG)'DMVN') // NVMD +#define NIC_TAG_TIMER ((ULONG)'tMVN') // NVMt + +#if (NDIS_SUPPORT_NDIS620) + +#define NIC_TAG_QUEUE_INFO ((ULONG)'QMVN') // NVMQ +#define NIC_TAG_QUEUE_SHARED_MEM ((ULONG)'MMVN') // NVMM +#define NIC_TAG_QUEUE_SHARED_MEM_BLOCK ((ULONG)'IMVN') // NVMB +#define NIC_TAG_QUEUE_WORK_ITEM ((ULONG)'WMVN') // NVMW +#define NIC_TAG_QUEUE_SG_LIST ((ULONG)'SMVN') // NVMS + +#endif + +#if (NDIS_SUPPORT_NDIS630) + +#define NIC_TAG_QOS_PARAMS ((ULONG)'PMVN') // NVMP + +#endif + +#define NIC_ADAPTER_CHECK_FOR_HANG_TIME_IN_SECONDS 4 + + + +// +// Buffer size passed in NdisMQueryAdapterResources +// We should only need three adapter resources (IO, interrupt and memory), +// Some devices get extra resources, so have room for 10 resources +// +#define NIC_RESOURCE_BUF_SIZE \ + (sizeof(NDIS_RESOURCE_LIST) + \ + (10*sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR))) + + +// +// Utility macros +// ----------------------------------------------------------------------------- +// + +#ifndef min +#define min(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif + +#ifndef max +#define max(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif + + + +#define LIST_ENTRY_FROM_NBL(_NBL) ((PLIST_ENTRY)&(_NBL)->MiniportReserved[0]) +#define NBL_FROM_LIST_ENTRY(_ENTRY) (CONTAINING_RECORD(_ENTRY, NET_BUFFER_LIST, MiniportReserved[0])) + +// Get a pointer to a LIST_ENTRY for the receive free list, from an NBL pointer +#define RECV_FREE_LIST_FROM_NBL(_NBL) LIST_ENTRY_FROM_NBL(_NBL) + +// Get a pointer to a NBL, from a pointer to a LIST_ENTRY on the receive free list +#define NBL_FROM_RECV_FREE_LIST(_ENTRY) NBL_FROM_LIST_ENTRY(_ENTRY) + + +// Get a pointer to a LIST_ENTRY for the cancel list, from an NBL pointer +#define CANCEL_LIST_FROM_NBL(_NBL) ((PSINGLE_LIST_ENTRY)&(_NBL)->MiniportReserved[0]) + +// Get a pointer to a NBL, from a pointer to a LIST_ENTRY on the cancel list +#define NBL_FROM_CANCEL_LIST(_ENTRY) NBL_FROM_LIST_ENTRY(_ENTRY) + + + +// Get a pointer to a LIST_ENTRY for the send wait list, from an NB pointer +#define SEND_WAIT_LIST_FROM_NB(_NB) ((PLIST_ENTRY)&(_NB)->MiniportReserved[0]) + +// Get a pointer to a NB, from a pointer to a LIST_ENTRY on the send wait list +#define NB_FROM_SEND_WAIT_LIST(_ENTRY) (CONTAINING_RECORD(_ENTRY, NET_BUFFER, MiniportReserved[0])) + + +// Get a pointer to an RCB from an NBL +#define RCB_FROM_NBL(_NBL) (*((PRCB*)&(_NBL)->MiniportReserved[0])) + +// Gets the number of outstanding TCBs/NET_BUFFERs associated with +// this NET_BUFFER_LIST +#define SEND_REF_FROM_NBL(_NBL) (*((PLONG)&(_NBL)->MiniportReserved[1])) + + +#define NBL_FROM_SEND_NB(_NB) (*((PNET_BUFFER_LIST*)&(_NB)->MiniportReserved[2])) + +#define FRAME_TYPE_FROM_SEND_NB(_NB) (*((PULONG)&(_NB)->MiniportReserved[3])) + + +// +// The NDIS_RW_LOCK_EX lock is more efficient (especially in the presence of +// more than 64 CPUs), but it is only available on Windows 7 and later. +// +// These macros let us use the better lock if we are a 6.20 or better miniport, +// or fallback to compiling against the 6.0 lock otherwise. +// + +#if (NDIS_SUPPORT_NDIS620) +# define MP_RW_LOCK_TYPE PNDIS_RW_LOCK_EX +# define MP_LOCK_STATE LOCK_STATE_EX +# define LOCK_ADAPTER_LIST_FOR_READ(STATE, FLAGS) NdisAcquireRWLockRead (GlobalData.Lock, STATE, FLAGS) +# define LOCK_ADAPTER_LIST_FOR_WRITE(STATE, FLAGS) NdisAcquireRWLockWrite(GlobalData.Lock, STATE, FLAGS) +# define UNLOCK_ADAPTER_LIST(STATE) NdisReleaseRWLock(GlobalData.Lock, STATE) +#else +# define MP_RW_LOCK_TYPE NDIS_RW_LOCK +# define MP_LOCK_STATE LOCK_STATE +# define LOCK_ADAPTER_LIST_FOR_READ(STATE, FLAGS) NdisAcquireReadWriteLock(&GlobalData.Lock, FALSE, STATE) +# define LOCK_ADAPTER_LIST_FOR_WRITE(STATE, FLAGS) NdisAcquireReadWriteLock(&GlobalData.Lock, TRUE, STATE) +# define UNLOCK_ADAPTER_LIST(STATE) NdisReleaseReadWriteLock(&GlobalData.Lock, STATE) +#endif + +#define ACQUIRE_NDIS_SPINLOCK(_AtDpc, _SpinLock)\ + if(_AtDpc)\ + {\ + NdisDprAcquireSpinLock(_SpinLock);\ + }\ + else\ + {\ + NdisAcquireSpinLock(_SpinLock);\ + } + +#define RELEASE_NDIS_SPINLOCK(_AtDpc, _SpinLock)\ + if(_AtDpc)\ + {\ + NdisDprReleaseSpinLock(_SpinLock);\ + }\ + else\ + {\ + NdisReleaseSpinLock(_SpinLock);\ + } + +// +// The driver has exactly one instance of the MP_GLOBAL structure. NDIS keeps +// an opaque handle to this data, (it doesn't attempt to read or interpret this +// data), and it passes the handle back to the miniport in MiniportSetOptions +// and MiniportInitializeEx. +// +typedef struct _MP_GLOBAL +{ + LIST_ENTRY AdapterList; + + MP_RW_LOCK_TYPE Lock; + + NPAGED_LOOKASIDE_LIST FrameDataLookaside; + +#define fGLOBAL_LOCK_ALLOCATED 0x0001 +#define fGLOBAL_LOOKASIDE_INITIALIZED 0x0002 +#define fGLOBAL_MINIPORT_REGISTERED 0x0004 + ULONG Flags; +} MP_GLOBAL, *PMP_GLOBAL; + +struct _MP_ADAPTER; + + +// Global data +extern NDIS_HANDLE NdisDriverHandle; +extern MP_GLOBAL GlobalData; + + + +// Miniport routines +SET_OPTIONS MPSetOptions; + + +void +MPAttachAdapter( + _In_ struct _MP_ADAPTER *Adapter); + +void +MPDetachAdapter( + _In_ struct _MP_ADAPTER *Adapter); + +BOOLEAN +MPIsAdapterAttached( + _In_ struct _MP_ADAPTER *Adapter); + + +VOID +DbgPrintOidName( + _In_ NDIS_OID OidReqQuery); + +VOID +DbgPrintAddress( + _In_reads_bytes_(NIC_MACADDR_SIZE) PUCHAR Address); + + +#endif // _MINIPORT_H + + diff --git a/network/ndis/netvmini/6x/mphal.c b/network/ndis/netvmini/6x/mphal.c new file mode 100644 index 00000000..c7747319 --- /dev/null +++ b/network/ndis/netvmini/6x/mphal.c @@ -0,0 +1,1002 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + MpHAL.C + +Abstract: + + This module implements the adapter's hardware. + +--*/ + + +#include "netvmin6.h" +#include "mphal.tmh" + +// +// This registry value saves the permanent MAC address of a netvmini NIC. It is +// only needed because there's no hardware that keeps track of the permanent +// address across reboots. +// +// It is saved as a NdisParameterBinary configuration value (REG_BINARY) with +// length NIC_MACADDR_LEN (6 bytes). +// +#define NETVMINI_MAC_ADDRESS_KEY L"NetvminiMacAddress" + + +static +NDIS_STATUS +HWCopyBytesFromNetBuffer( + _In_ PNET_BUFFER NetBuffer, + _Inout_ PULONG cbDest, + _Out_writes_bytes_to_(*cbDest, *cbDest) PVOID Dest); + + +#pragma NDIS_PAGEABLE_FUNCTION(HWInitialize) +#pragma NDIS_PAGEABLE_FUNCTION(HWReadPermanentMacAddress) + + + + +NDIS_STATUS +HWInitialize( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_MINIPORT_INIT_PARAMETERS InitParameters) +/*++ +Routine Description: + + Query assigned resources and initialize the adapter. + +Arguments: + + Adapter Pointer to our adapter + InitParameters Parameters to MiniportInitializeEx + +Return Value: + + NDIS_STATUS_SUCCESS + NDIS_STATUS_ADAPTER_NOT_FOUND + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_ADAPTER_NOT_FOUND; + PCM_PARTIAL_RESOURCE_DESCRIPTOR pResDesc; + ULONG index; + + UNREFERENCED_PARAMETER(Adapter); + + DEBUGP(MP_TRACE, "[%p] ---> HWInitialize\n", Adapter); + PAGED_CODE(); + + + do + { + if (InitParameters->AllocatedResources) + { + for (index=0; index < InitParameters->AllocatedResources->Count; index++) + { + pResDesc = &InitParameters->AllocatedResources->PartialDescriptors[index]; + + switch (pResDesc->Type) + { + case CmResourceTypePort: + DEBUGP(MP_INFO, "[%p] IoBaseAddress = 0x%x\n", Adapter, + NdisGetPhysicalAddressLow(pResDesc->u.Port.Start)); + DEBUGP(MP_INFO, "[%p] IoRange = x%x\n", Adapter, + pResDesc->u.Port.Length); + break; + + case CmResourceTypeInterrupt: + DEBUGP(MP_INFO, "[%p] InterruptLevel = x%x\n", Adapter, + pResDesc->u.Interrupt.Level); + break; + + case CmResourceTypeMemory: + DEBUGP(MP_INFO, "[%p] MemPhysAddress(Low) = 0x%0x\n", Adapter, + NdisGetPhysicalAddressLow(pResDesc->u.Memory.Start)); + DEBUGP(MP_INFO, "[%p] MemPhysAddress(High) = 0x%0x\n", Adapter, + NdisGetPhysicalAddressHigh(pResDesc->u.Memory.Start)); + break; + } + } + } + + Status = NDIS_STATUS_SUCCESS; + + // + // Map bus-relative IO range to system IO space using + // NdisMRegisterIoPortRange + // + + // + // Map bus-relative registers to virtual system-space + // using NdisMMapIoSpace + // + + + // + // Disable interrupts here as soon as possible + // + + // + // Register the interrupt using NdisMRegisterInterruptEx + // + + // + // Initialize the hardware with mapped resources + // + + // + // Enable the interrupt + // + + } while (FALSE); + + + DEBUGP(MP_TRACE, "[%p] <--- HWInitialize Status = 0x%x\n", Adapter, Status); + return Status; +} + +VOID +HWReadPermanentMacAddress( + _In_ PMP_ADAPTER Adapter, + _In_ NDIS_HANDLE ConfigurationHandle, + _Out_writes_bytes_(NIC_MACADDR_SIZE) PUCHAR PermanentMacAddress) +/*++ + +Routine Description: + + Loads the permanent MAC address that is burnt into the NIC. + + IRQL = PASSIVE_LEVEL + +Arguments: + + Adapter Pointer to our adapter + ConfigurationHandle NIC configuration from NdisOpenConfigurationEx + PermanentMacAddress On return, receives the NIC's MAC address + +Return Value: + + None. + +--*/ +{ + NDIS_STATUS Status; + PNDIS_CONFIGURATION_PARAMETER Parameter = NULL; + NDIS_STRING PermanentAddressKey = RTL_CONSTANT_STRING(NETVMINI_MAC_ADDRESS_KEY); + + UNREFERENCED_PARAMETER(Adapter); + PAGED_CODE(); + + // + // We want to figure out what the NIC's physical address is. + // If we had a hardware NIC, we would query the physical address from it. + // Instead, for the purposes of this sample, we'll read it from the + // registry. This will help us keep the permanent address constant, even + // if the adapter is disabled/enabled. + // + // Note that the registry value that saves our permanent MAC address isn't + // the one that end-users can configure through the NIC management GUI, + // nor is it expected that other miniports would need to use a parameter + // like this. We only have it to work around the lack of physical hardware. + // + NdisReadConfiguration( + &Status, + &Parameter, + ConfigurationHandle, + &PermanentAddressKey, + NdisParameterBinary); + if (Status == NDIS_STATUS_SUCCESS + && Parameter->ParameterType == NdisParameterBinary + && Parameter->ParameterData.BinaryData.Length == NIC_MACADDR_SIZE) + { + // + // There is a permanent address stashed in the special netvmini + // parameter. + // + NIC_COPY_ADDRESS(PermanentMacAddress, Parameter->ParameterData.BinaryData.Buffer); + } + else + { + NDIS_CONFIGURATION_PARAMETER NewPhysicalAddress; + LARGE_INTEGER TickCountValue; + UCHAR CurrentMacIndex = 3; + + // + // There is no (valid) address stashed in the netvmini parameter, so + // this is probably the first time we've loaded this adapter before. + // + // Just for testing purposes, let us make up a dummy mac address. + // In order to avoid conflicts with MAC addresses, it is usually a good + // idea to check the IEEE OUI list (e.g. at + // http://standards.ieee.org/regauth/oui/oui.txt). According to that + // list 00-50-F2 is owned by Microsoft. + // + // An important rule to "generating" MAC addresses is to have the + // "locally administered bit" set in the address, which is bit 0x02 for + // LSB-type networks like Ethernet. Also make sure to never set the + // multicast bit in any MAC address: bit 0x01 in LSB networks. + // + + {C_ASSERT(NIC_MACADDR_SIZE > 3);} + NdisZeroMemory(PermanentMacAddress, NIC_MACADDR_SIZE); + PermanentMacAddress[0] = 0x02; + PermanentMacAddress[1] = 0x50; + PermanentMacAddress[2] = 0xF2; + + // + // Generated value based on the current tick count value. + // + KeQueryTickCount(&TickCountValue); + do + { + // + // Pick up the value in groups of 8 bits to populate the rest of the MAC address. + // + PermanentMacAddress[CurrentMacIndex] = (UCHAR)(TickCountValue.LowPart>>((CurrentMacIndex-3)*8)); + } while(++CurrentMacIndex < NIC_MACADDR_SIZE); + + // + // Finally, we should make a best-effort attempt to save this address + // to our configuration, so the NIC will always come up with this + // permanent address. + // + + NewPhysicalAddress.ParameterType = NdisParameterBinary; + NewPhysicalAddress.ParameterData.BinaryData.Length = NIC_MACADDR_SIZE; + NewPhysicalAddress.ParameterData.BinaryData.Buffer = PermanentMacAddress; + + NdisWriteConfiguration( + &Status, + ConfigurationHandle, + &PermanentAddressKey, + &NewPhysicalAddress); + if (NDIS_STATUS_SUCCESS != Status) + { + DEBUGP(MP_WARNING, "[%p] NdisWriteConfiguration failed to save the permanent MAC address", Adapter); + // No other handling -- this isn't a fatal error + } + } +} + + + +_IRQL_requires_same_ +_Function_class_(ALLOCATE_FUNCTION) +PVOID +HWFrameAllocate ( + _In_ POOL_TYPE PoolType, + _In_ SIZE_T NumberOfBytes, + _In_ ULONG Tag) +/*++ + +Routine Description: + + This routine allocates memory for a FRAME. It is an ALLOCATE_FUNCTION, so + its parameters and usage are the same as ExAllocatePoolWithTag. + + This allocator is meant to be used with the send NPAGED_LOOKASIDE_LIST. + While you do not normally need to provide your own allocator (and in fact + there is a performance penalty for doing so), we use the allocator to + initialize the FRAME's MDL. This saves us the effort of (re)initializing + the same MDL every time we reuse the FRAME. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + PoolType Must be NonPagedPool, or NonPagedPoolNx for Win8 + and later + NumberOfBytes Must be sizeof(FRAME) + Tag The pool allocation tag + +Return Value: + + NULL if there are insufficient resources to allocate a FRAME. + Else, a pointer to a newly-allocated FRAME. Free it with HWFrameFree. + +--*/ +{ + PFRAME Frame = NULL; + + DEBUGP(MP_TRACE, "---> HWFrameAllocate\n"); + + UNREFERENCED_PARAMETER(PoolType); + ASSERT(NumberOfBytes == sizeof(FRAME)); + + Frame = (PFRAME) NdisAllocateMemoryWithTagPriority( + NdisDriverHandle, + (UINT)NumberOfBytes, + Tag, + NormalPoolPriority); + if (!Frame) + { + DEBUGP(MP_ERROR, "NdisAllocateMemoryWithTagPriority failed"); + return NULL; + } + NdisZeroMemory(Frame, NumberOfBytes); + + Frame->Mdl = NdisAllocateMdl( + NdisDriverHandle, + (PVOID)&Frame->Data[0], + sizeof(Frame->Data)); + if (Frame->Mdl == NULL) + { + DEBUGP(MP_ERROR, "NdisAllocateMdl failed\n"); + NdisFreeMemory(Frame, (UINT)NumberOfBytes, 0); + return NULL; + } + + DEBUGP(MP_TRACE, "<--- HWFrameAllocate. Frame: %p\n", Frame); + + return Frame; +} + + +_Use_decl_annotations_ +VOID +HWFrameFree ( + PVOID Memory) +/*++ + +Routine Description: + + This routine frees memory for a FRAME. It is a FREE_FUNCTION, the + reciprocal of HWFrameAllocate. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Memory A buffer allocated with HWFrameAllocate + +Return Value: + + None. + +--*/ +{ + PFRAME Frame = (PFRAME) Memory; + + DEBUGP(MP_TRACE, "---> HWFrameFree. Frame: %p\n", Frame); + + ASSERT(Frame && Frame->Mdl); + + NdisFreeMdl(Frame->Mdl); + NdisFreeMemory(Frame, sizeof(FRAME), 0); + + DEBUGP(MP_TRACE, "<--- HWFrameFree\n"); + +} + + +VOID +HWFrameReference( + _In_ PFRAME Frame) +/*++ + +Routine Description: + + This routine increments the reference count of a FRAME. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Frame Frame to reference + +Return Value: + + None. + +--*/ +{ + DEBUGP(MP_TRACE, "---> HWFrameReference. Frame: %p\n", Frame); + + NdisInterlockedIncrement(&Frame->Ref); + + DEBUGP(MP_TRACE, "<--- HWFrameReference. Frame: %p\n", Frame); + +} + + +VOID +HWFrameRelease( + _In_ PFRAME Frame) +/*++ + +Routine Description: + + This routine decrements the reference count of a FRAME. If the last + refernce was released, the FRAME is freed back to the unused pool. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Frame Frame to release + +Return Value: + + None. + +--*/ +{ + DEBUGP(MP_TRACE, "---> HWFrameRelease. Frame: %p\n", Frame); + + if (0 == NdisInterlockedDecrement(&Frame->Ref)) + { + DEBUGP(MP_TRACE, "---> Freeing Frame: %p\n", Frame); + + NdisFreeToNPagedLookasideList(&GlobalData.FrameDataLookaside, Frame); + Frame = NULL; + } + + DEBUGP(MP_TRACE, "<--- HWFrameRelease. Frame: %p\n", Frame); + +} + + +NDIS_STATUS +HWCopyBytesFromNetBuffer( + _In_ PNET_BUFFER NetBuffer, + _Inout_ PULONG cbDest, + _Out_writes_bytes_to_(*cbDest, *cbDest) PVOID Dest) +/*++ + +Routine Description: + + Copies the first cbDest bytes from a NET_BUFFER. In order to show how the various data structures fit together, this + implementation copies the data by iterating through the MDLs for the NET_BUFFER. The NdisGetDataBuffer API also allows you + to copy a contiguous block of data from a NET_BUFFER. + + Runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + NetBuffer The NB to read + cbDest On input, the number of bytes in the buffer Dest + On return, the number of bytes actually copied + Dest On return, receives the first cbDest bytes of + the network frame in NetBuffer + +Return Value: + + None. + +Notes: + + If the output buffer is larger than the NB's frame size, *cbDest will + contain the number of bytes in the frame size. + + If the output buffer is smaller than the NB's frame size, only the first + *cbDest bytes will be copied (the buffer will receive a truncated copy of + the frame). + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + // + // Start copy from current MDL + // + PMDL CurrentMdl = NET_BUFFER_CURRENT_MDL(NetBuffer); + // + // Data on current MDL may be offset from start of MDL + // + ULONG DestOffset = 0; + while (DestOffset < *cbDest && CurrentMdl) + { + // + // Map MDL memory to System Address Space. LowPagePriority means mapping may fail if + // system is low on memory resources. + // + PUCHAR SrcMemory = MmGetSystemAddressForMdlSafe(CurrentMdl, LowPagePriority); + ULONG Length = MmGetMdlByteCount(CurrentMdl); + if (!SrcMemory) + { + Status = NDIS_STATUS_RESOURCES; + break; + } + + if(DestOffset==0) + { + // + // The first MDL segment should be accessed from the current MDL offset + // + ULONG MdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer); + SrcMemory += MdlOffset; + Length -= MdlOffset; + } + + Length = min(Length, *cbDest-DestOffset); + + // + // Copy Memory + // + NdisMoveMemory((PUCHAR)Dest+DestOffset, SrcMemory, Length); + DestOffset += Length; + + // + // Get next MDL (if any available) + // + CurrentMdl = NDIS_MDL_LINKAGE(CurrentMdl); + } + + if(Status == NDIS_STATUS_SUCCESS) + { + *cbDest = DestOffset; + } + + return Status; +} + + +NDIS_STATUS +HWGetDestinationAddress( + _In_ PNET_BUFFER NetBuffer, + _Out_writes_bytes_(NIC_MACADDR_SIZE) PUCHAR DestAddress) +/*++ + +Routine Description: + + Returns the destination address of a NET_BUFFER that is to be sent. + + Runs at IRQL <= DISPATCH_LEVEL. + +Arguments: + + NetBuffer The NB containing the frame that is being sent + DestAddress On return, receives the frame's destination + +Return Value: + + NDIS_STATUS_FAILURE The frame is too short + NDIS_STATUS_SUCCESS Else + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NIC_FRAME_HEADER Header; + ULONG cbHeader = sizeof(Header); + + Status = HWCopyBytesFromNetBuffer(NetBuffer, &cbHeader, &Header); + if(Status == NDIS_STATUS_SUCCESS) + { + if (cbHeader < sizeof(Header)) + { + NdisZeroMemory(DestAddress, NIC_MACADDR_SIZE); + Status = NDIS_STATUS_FAILURE; + } + else + { + GET_DESTINATION_OF_FRAME(DestAddress, &Header); + } + } + else + { + NdisZeroMemory(DestAddress, NIC_MACADDR_SIZE); + } + + return Status; +} + +BOOLEAN +HWIsFrameAcceptedByPacketFilter( + _In_ PMP_ADAPTER Adapter, + _In_reads_bytes_(NIC_MACADDR_SIZE) PUCHAR DestAddress, + _In_ ULONG FrameType) +/*++ + +Routine Description: + + This routines checks to see whether the packet can be accepted + for transmission based on the currently programmed filter type + of the NIC and the mac address of the packet. + + With real adapter, this routine would be implemented in hardware. However, + since we don't have any hardware to do the matching for us, we'll do it in + the driver. + +Arguments: + + Adapter Our adapter that is receiving a frame + FrameData The raw frame, starting at the frame header + cbFrameData Number of bytes in the FrameData buffer + +Return Value: + + TRUE if the frame is accepted by the packet filter, and should be indicated + up to the higher levels of the stack. + + FALSE if the frame doesn't match the filter, and should just be dropped. + +--*/ +{ + BOOLEAN result = FALSE; + + DEBUGP(MP_LOUD, "[%p] ---> HWIsFrameAcceptedByPacketFilter PacketFilter = 0x%08x, FrameType = 0x%08x\n", + Adapter, + Adapter->PacketFilter, + FrameType); + + do + { + // + // If the NIC is in promiscuous mode, we will accept anything + // and everything. + // + if (Adapter->PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) + { + result = TRUE; + break; + } + + + switch (FrameType) + { + case NDIS_PACKET_TYPE_BROADCAST: + if (Adapter->PacketFilter & NDIS_PACKET_TYPE_BROADCAST) + { + // + // If it's a broadcast packet and broadcast is enabled, + // we can accept that. + // + result = TRUE; + } + break; + + + case NDIS_PACKET_TYPE_MULTICAST: + // + // If it's a multicast packet and multicast is enabled, + // we can accept that. + // + if (Adapter->PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) + { + result = TRUE; + break; + } + else if (Adapter->PacketFilter & NDIS_PACKET_TYPE_MULTICAST) + { + ULONG index; + + // + // Check to see if the multicast address is in our list + // + ASSERT(Adapter->ulMCListSize <= NIC_MAX_MCAST_LIST); + for (index=0; index < Adapter->ulMCListSize && index < NIC_MAX_MCAST_LIST; index++) + { + if (NIC_ADDR_EQUAL(DestAddress, Adapter->MCList[index])) + { + result = TRUE; + break; + } + } + } + break; + + + case NDIS_PACKET_TYPE_DIRECTED: + + if (Adapter->PacketFilter & NDIS_PACKET_TYPE_DIRECTED) + { + // + // This has to be a directed packet. If so, does packet dest + // address match with the mac address of the NIC. + // + if (NIC_ADDR_EQUAL(DestAddress, Adapter->CurrentAddress)) + { + result = TRUE; + break; + } + } + + break; + } + + } while(FALSE); + + + DEBUGP(MP_LOUD, "[%p] <--- HWIsFrameAcceptedByPacketFilter Result = %u\n", Adapter, result); + return result; +} + + +NDIS_MEDIA_CONNECT_STATE +HWGetMediaConnectStatus( + _In_ PMP_ADAPTER Adapter) +/*++ + +Routine Description: + + This routine will query the hardware and return + the media status. + +Arguments: + + Adapter Our Adapter + +Return Value: + + NdisMediaStateDisconnected or + NdisMediaStateConnected + +--*/ +{ + if (MP_TEST_FLAG(Adapter, fMP_DISCONNECTED)) + { + return MediaConnectStateDisconnected; + } + else + { + return MediaConnectStateConnected; + } +} + +VOID +HWProgramDmaForSend( + _In_ PMP_ADAPTER Adapter, + _In_ PTCB Tcb, + _In_ PNET_BUFFER NetBuffer, + _In_ BOOLEAN fAtDispatch) +/*++ + +Routine Description: + + Program the hardware to read the data payload from the NET_BUFFER's MDL + and queue it for transmission. When the hardware has finished reading the + MDL, it will fire an interrupt to indicate that it no longer needs the MDL + anymore. + + Our hardware, of course, doesn't have any DMA, so it just copies the data + to a FRAME structure and transmits that. + + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Adapter Our adapter that will send a frame + Tcb The TCB that tracks the transmit status + NetBuffer Contains the data to send + fAtDispatch TRUE if the current IRQL is DISPATCH_LEVEL + +Return Value: + + None. + +--*/ +{ + PFRAME Frame; + NDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo = {0}; + PNET_BUFFER_LIST Nbl = NULL; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + DEBUGP(MP_TRACE, "[%p] ---> HWProgramDmaForSend. NB: 0x%p\n", Adapter, NetBuffer); + + do + { + // + // Program the hardware to begin reading the data from NetBuffer's MDL and + // queue it for transmission. + // + + Tcb->NetBuffer = NetBuffer; + Tcb->BytesActuallySent = 0; + + Frame = (PFRAME)NdisAllocateFromNPagedLookasideList(&GlobalData.FrameDataLookaside); + + if (!Frame) + { + DEBUGP(MP_TRACE, "[%p] ---> No frames available for send.\n", Adapter); + // + // Oops, we couldn't get any more FRAMEs to send. + // + // When the SendComplete fires, we'll tell the driver that zero bytes + // were sent successfully. It will update the bookkeeping and inform + // the protocol that the NBL wasn't completely sent. + // + break; + } + + DEBUGP(MP_TRACE, "[%p] Send Frame: 0x%p\n", Adapter, Frame); + ASSERT(NET_BUFFER_DATA_LENGTH(NetBuffer) <= NIC_BUFFER_SIZE); + + Frame->Ref = 1; + + // + // Copy the data from the NB to the FRAME's data region. This step roughly + // corresponds to a hardware DMA. + // + Frame->ulSize = min(NET_BUFFER_DATA_LENGTH(NetBuffer), NIC_BUFFER_SIZE); + Status = HWCopyBytesFromNetBuffer(NetBuffer, &Frame->ulSize, Frame->Data); + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_TRACE, "[%p] ---> Failed to copy frame buffer. Result = %u\n", Adapter, Status); + break; + } + + if (Frame->ulSize < HW_MIN_FRAME_SIZE) + { + // Don't leak the contents of kernel memory! Zero out padding bytes. + ULONG cbPaddingNeeded = HW_MIN_FRAME_SIZE - Frame->ulSize; + NdisZeroMemory(Frame->Data + Frame->ulSize, cbPaddingNeeded); + Frame->ulSize += cbPaddingNeeded; + } + + ASSERT(Frame->ulSize >= HW_MIN_FRAME_SIZE && Frame->ulSize <= NIC_BUFFER_SIZE); + + + // + // For simplicity in the sample in order to support VLAN we extract the information from the NBL or frame and pass the + // NDIS_NET_BUFFER_LIST_8021Q_INFO structure to the code that simulates the send/receive. The code does nothing to convert + // modify the frame format. + // In real HW, on send the code should extract the information from the NBL and covert it to 802.1Q format for transmission, and + // on receive the adapter should detect if the packet is in 802.1Q format and if so convert it back to 802.3 before indicating it up to NDIS + // (populating the 8021Q info in the NBL being indicated). + // + Nbl = NBL_FROM_SEND_NB(NetBuffer); + Nbl1QInfo.Value = NET_BUFFER_LIST_INFO(Nbl, Ieee8021QNetBufferListInfo); + + if(Nbl1QInfo.Value) + { + DEBUGP(MP_TRACE, "[%p] Send NBL (%p) OOB Vlan ID: %i\n", Adapter, Nbl, Nbl1QInfo.TagHeader.VlanId); + } + else + { + DEBUGP(MP_TRACE, "[%p] Send NBL (%p) has no OOB VLAN tag, checking frame header.\n", Adapter, Nbl); + if(IS_FRAME_8021Q(Frame)) + { + // + // The frame has type of 802.1Q. Retrieve the VLAN information + // + COPY_TAG_INFO_FROM_HEADER_TO_PACKET_INFO(Nbl1QInfo, GET_FRAME_VLAN_TAG_HEADER(Frame)); + DEBUGP(MP_TRACE, "[%p] Send NBL (%p) frame Vlan ID: %i\n", Adapter, Nbl, Nbl1QInfo.TagHeader.VlanId); + } + else + { + DEBUGP(MP_TRACE, "[%p] Send NBL (%p) has no VLAN information in its frame header.\n", Adapter, Nbl); + } + } + + RXDeliverFrameToEveryAdapter(Adapter, &Nbl1QInfo, Frame, fAtDispatch); + + Tcb->BytesActuallySent = Frame->ulSize; + + } while(FALSE); + + if(Frame) + { + HWFrameRelease(Frame); + } + + DEBUGP(MP_TRACE, "[%p] <--- HWProgramDmaForSend\n", Adapter); + +} + + +_IRQL_requires_(DISPATCH_LEVEL) +ULONG +HWGetBytesSent( + _In_ PMP_ADAPTER Adapter, + _In_ PTCB Tcb) +/*++ + +Routine Description: + + When the adapter indicates it has completed the send operation, call this + routine to determine how many bytes were successfully sent (if any). + +Arguments: + + Adapter Our adapter that will send a frame + Tcb The TCB that tracks the transmit status + +Return Value: + + 0 There was an error sending this frame + >0 Number of bytes actually sent + +--*/ +{ + UNREFERENCED_PARAMETER(Adapter); + + return Tcb->BytesActuallySent; +} + +NDIS_STATUS +HWBeginReceiveDma( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _In_ PRCB Rcb, + _In_ PFRAME Frame) +/*++ + +Routine Description: + + Simulate the hardware deciding to receive a FRAME into one of its RCBs. In VMQ enabled scenarios, it will + find the matching queue and if matched retrieve the shared memory for the queue for the NBL. Otherwise, it + uses the existing Frame for the NBL. + +Arguments: + + Adapter Pointer to our adapter + Nbl1QInfo 8021Q Tag information for the FRAME being received + Rcb The RCB that tracks this receive operation + Frame The FRAME that to receive + +Return Value: + + NDIS_STATUS + +--*/ +{ + + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PNET_BUFFER NetBuffer = NET_BUFFER_LIST_FIRST_NB(Rcb->Nbl); + + do + { + DEBUGP(MP_TRACE, "[%p] ---> HWBeginReceiveDma. Frame: 0x%p\n", Adapter, Frame); + + // + // Preserve 802.1Q information, if specified. + // + if(Nbl1QInfo->Value) + { + DEBUGP(MP_TRACE, "[%p] Preserved VLAN Id=%i\n", Adapter, Nbl1QInfo->TagHeader.VlanId); + NET_BUFFER_LIST_INFO(Rcb->Nbl, Ieee8021QNetBufferListInfo) = Nbl1QInfo->Value; + } + else + { + DEBUGP(MP_TRACE, "[%p] No VLAN tag to preserve.\n", Adapter); + NET_BUFFER_LIST_INFO(Rcb->Nbl, Ieee8021QNetBufferListInfo) = 0; + } + + // + // If VMQ is enabled, and we're not using the default queue, + // we need to copy the FRAME to the NBL's shared memory area + // + if(VMQ_ENABLED(Adapter)) + { + BOOLEAN Copied; + Status = CopyFrameToRxQueueRcb(Adapter, Frame, Nbl1QInfo, Rcb, &Copied); + if(Copied || Status != NDIS_STATUS_SUCCESS) + { + break; + } + } + else + { + UNREFERENCED_PARAMETER(Adapter); + } + + // + // Either 6.20 is not supported, VMQ is disabled, or matched default queue, so use existing frame memory for indication + // + HWFrameReference(Frame); + Rcb->Data = Frame; + + NET_BUFFER_FIRST_MDL(NetBuffer) = Frame->Mdl; + NET_BUFFER_DATA_LENGTH(NetBuffer) = Frame->ulSize; + NET_BUFFER_DATA_OFFSET(NetBuffer) = 0; + NET_BUFFER_CURRENT_MDL(NetBuffer) = NET_BUFFER_FIRST_MDL(NetBuffer); + NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer) = 0; + + } + while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- HWBeginReceiveDma Status 0x%08x\n", Adapter, Status); + return Status; + +} + diff --git a/network/ndis/netvmini/6x/mphal.h b/network/ndis/netvmini/6x/mphal.h new file mode 100644 index 00000000..5348545b --- /dev/null +++ b/network/ndis/netvmini/6x/mphal.h @@ -0,0 +1,126 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + MpHAL.H + +Abstract: + + This module declares the structures and functions that abstract the + adapter and medium's (emulated) hardware capabilities. + +--*/ + + +#ifndef _MPHAL_H +#define _MPHAL_H + + +// +// A FRAME represents the physical bits as they are being transmitted on the +// wire. Normally, a miniport wouldn't need to implement any such tracking, +// but we have to simulate our own Ethernet hub. +// +typedef struct _FRAME +{ + volatile LONG Ref; + PMDL Mdl; + ULONG ulSize; + UCHAR Data[NIC_BUFFER_SIZE]; +} FRAME, *PFRAME; + +ALLOCATE_FUNCTION HWFrameAllocate; +FREE_FUNCTION HWFrameFree; + +struct _TCB; +struct _RCB; + +// +// Structures and utility macros to retrieve and set VLAN tag related data +// +#define FRAME_8021Q_ETHER_TYPE 0x81 +#define IS_FRAME_8021Q(_Frame)\ + (((PNIC_FRAME_HEADER)(_Frame)->Data)->EtherType[0] == FRAME_8021Q_ETHER_TYPE) + +typedef struct _VLAN_TAG_HEADER +{ + UCHAR TagInfo[2]; +} VLAN_TAG_HEADER, *PVLAN_TAG_HEADER; + +#define GET_FRAME_VLAN_TAG_HEADER(_Frame)\ + ((VLAN_TAG_HEADER UNALIGNED *)((PNIC_FRAME_HEADER)(_Frame)->Data)->EtherType+1) + +#define USER_PRIORITY_MASK 0xe0 +#define CANONICAL_FORMAT_ID_MASK 0x10 +#define HIGH_VLAN_ID_MASK 0x0F + +#define COPY_TAG_INFO_FROM_HEADER_TO_PACKET_INFO(_Ieee8021qInfo, _pTagHeader) \ +{ \ + (_Ieee8021qInfo).TagHeader.UserPriority = ((_pTagHeader->TagInfo[0] & USER_PRIORITY_MASK) >> 5); \ + (_Ieee8021qInfo).TagHeader.CanonicalFormatId = ((_pTagHeader->TagInfo[0] & CANONICAL_FORMAT_ID_MASK) >> 4); \ + (_Ieee8021qInfo).TagHeader.VlanId = (((USHORT)(_pTagHeader->TagInfo[0] & HIGH_VLAN_ID_MASK) << 8)| (USHORT)(_pTagHeader->TagInfo[1])); \ +} + +VOID +HWFrameReference( + _In_ PFRAME Frame); + +VOID +HWFrameRelease( + _In_ PFRAME Frame); + +NDIS_STATUS +HWInitialize( + _In_ PMP_ADAPTER Adapter, + _In_ NDIS_HANDLE WrapperConfigurationContext); + +VOID +HWReadPermanentMacAddress( + _In_ PMP_ADAPTER Adapter, + _In_ NDIS_HANDLE ConfigurationHandle, + _Out_writes_bytes_(NIC_MACADDR_SIZE) PUCHAR PermanentMacAddress); + +NDIS_STATUS +HWGetDestinationAddress( + _In_ PNET_BUFFER NetBuffer, + _Out_writes_bytes_(NIC_MACADDR_SIZE) PUCHAR DestAddress); + +BOOLEAN +HWIsFrameAcceptedByPacketFilter( + _In_ PMP_ADAPTER Adapter, + _In_reads_bytes_(NIC_MACADDR_SIZE) PUCHAR DestAddress, + _In_ ULONG FrameType); + +NDIS_MEDIA_CONNECT_STATE +HWGetMediaConnectStatus( + _In_ PMP_ADAPTER Adapter); + +VOID +HWProgramDmaForSend( + _In_ PMP_ADAPTER Adapter, + _In_ struct _TCB *Tcb, + _In_ PNET_BUFFER NetBuffer, + _In_ BOOLEAN fAtDispatch); + +_IRQL_requires_(DISPATCH_LEVEL) +ULONG +HWGetBytesSent( + _In_ PMP_ADAPTER Adapter, + _In_ struct _TCB *Tcb); + +NDIS_STATUS +HWBeginReceiveDma( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _In_ struct _RCB *Rcb, + _In_ PFRAME Frame); + +#endif // _MPHAL_H + diff --git a/network/ndis/netvmini/6x/netvmin6.h b/network/ndis/netvmini/6x/netvmin6.h new file mode 100644 index 00000000..33419487 --- /dev/null +++ b/network/ndis/netvmini/6x/netvmin6.h @@ -0,0 +1,45 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Netvmin6.H + +Abstract: + + This module collects all the headers needed to compile the netvmin6 sample. + +--*/ + + +#ifndef _NETVMIN6_H +#define _NETVMIN6_H + + +#include + + +// Tell the analysis tools that this code should be treated as a kernel driver. +_Analysis_mode_(_Analysis_code_type_kernel_driver_); + +#include "trace.h" +#include "hardware.h" +#include "miniport.h" +#include "vmq.h" +#include "qos.h" +#include "adapter.h" +#include "mphal.h" +#include "tcbrcb.h" +#include "datapath.h" +#include "ctrlpath.h" + + + +#endif // _NETVMIN6_H + diff --git a/network/ndis/netvmini/6x/netvmini.sln b/network/ndis/netvmini/6x/netvmini.sln new file mode 100644 index 00000000..0485a5a1 --- /dev/null +++ b/network/ndis/netvmini/6x/netvmini.sln @@ -0,0 +1,59 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "60", "60", "{FB1356F7-2404-4CF8-8FF8-D9D99AB89F12}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "620", "620", "{9164ECBF-12E2-44DA-8038-3BD0F347C637}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "630", "630", "{2212DE82-26A1-46C0-810A-809089EC5623}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvmini60", "60\netvmini60.vcxproj", "{E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvmini620", "620\netvmini620.vcxproj", "{9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvmini630", "630\netvmini630.vcxproj", "{20227FE5-F4F3-4980-98C2-B860C8B2F62C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B}.Debug|Win32.ActiveCfg = Debug|Win32 + {E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B}.Debug|Win32.Build.0 = Debug|Win32 + {E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B}.Release|Win32.ActiveCfg = Release|Win32 + {E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B}.Release|Win32.Build.0 = Release|Win32 + {E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B}.Debug|x64.ActiveCfg = Debug|x64 + {E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B}.Debug|x64.Build.0 = Debug|x64 + {E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B}.Release|x64.ActiveCfg = Release|x64 + {E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B}.Release|x64.Build.0 = Release|x64 + {9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D}.Debug|Win32.ActiveCfg = Debug|Win32 + {9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D}.Debug|Win32.Build.0 = Debug|Win32 + {9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D}.Release|Win32.ActiveCfg = Release|Win32 + {9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D}.Release|Win32.Build.0 = Release|Win32 + {9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D}.Debug|x64.ActiveCfg = Debug|x64 + {9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D}.Debug|x64.Build.0 = Debug|x64 + {9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D}.Release|x64.ActiveCfg = Release|x64 + {9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D}.Release|x64.Build.0 = Release|x64 + {20227FE5-F4F3-4980-98C2-B860C8B2F62C}.Debug|Win32.ActiveCfg = Debug|Win32 + {20227FE5-F4F3-4980-98C2-B860C8B2F62C}.Debug|Win32.Build.0 = Debug|Win32 + {20227FE5-F4F3-4980-98C2-B860C8B2F62C}.Release|Win32.ActiveCfg = Release|Win32 + {20227FE5-F4F3-4980-98C2-B860C8B2F62C}.Release|Win32.Build.0 = Release|Win32 + {20227FE5-F4F3-4980-98C2-B860C8B2F62C}.Debug|x64.ActiveCfg = Debug|x64 + {20227FE5-F4F3-4980-98C2-B860C8B2F62C}.Debug|x64.Build.0 = Debug|x64 + {20227FE5-F4F3-4980-98C2-B860C8B2F62C}.Release|x64.ActiveCfg = Release|x64 + {20227FE5-F4F3-4980-98C2-B860C8B2F62C}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {E1613ED0-DFEF-4C4D-8E06-6DB68964EE8B} = {FB1356F7-2404-4CF8-8FF8-D9D99AB89F12} + {9DA1838D-2D4D-4357-9E4A-49BF1A8E6B7D} = {9164ECBF-12E2-44DA-8038-3BD0F347C637} + {20227FE5-F4F3-4980-98C2-B860C8B2F62C} = {2212DE82-26A1-46C0-810A-809089EC5623} + EndGlobalSection +EndGlobal diff --git a/network/ndis/netvmini/6x/qos.c b/network/ndis/netvmini/6x/qos.c new file mode 100644 index 00000000..a7b2e530 --- /dev/null +++ b/network/ndis/netvmini/6x/qos.c @@ -0,0 +1,653 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Qos.c + +Abstract: + + This module implements the NDIS QOS related functionality for the adapter. + +--*/ + +#include "netvmin6.h" +#include "qos.tmh" + + +#pragma NDIS_PAGEABLE_FUNCTION(ReadQOSConfig) +#pragma NDIS_PAGEABLE_FUNCTION(SetQOSParameters) +#pragma NDIS_PAGEABLE_FUNCTION(InitializeQOSConfig) + + +// +// The priority-to-TC assignment table below replicates the recommended mapping in +// Table 8-4 of the IEEE 802.1Q spec. The table assumes the hardware supports 8 +// traffic classes. +// +C_ASSERT(NIC_SUPPORTED_NUM_TCS == 8); +CONST UCHAR DefaultQOSPriorityAssignmentTable[NDIS_QOS_MAXIMUM_PRIORITIES] = { 1, 0, 2, 3, 4, 5, 6, 7 }; + +// +// By default, all TCs use ETS. Split the 100% bandwidth across all 8 TCs. +// +#define _S NDIS_QOS_TSA_STRICT +#define _E NDIS_QOS_TSA_ETS +CONST UCHAR DefaultQOSTsaAssignmentTable[NDIS_QOS_MAXIMUM_TRAFFIC_CLASSES] = { _E, _E, _E, _E, _E, _E, _E, _E }; +#undef _E +#undef _S +CONST UCHAR DefaultQOSTcBandwidthAssignmentTable[NDIS_QOS_MAXIMUM_TRAFFIC_CLASSES] = { 12, 13, 12, 13, 12, 13, 12, 13 }; + + +_IRQL_requires_max_(DISPATCH_LEVEL) +PNDIS_QOS_PARAMETERS +CreateParameters( + _In_ ULONG NumTrafficClasses, + _In_reads_(NDIS_QOS_MAXIMUM_PRIORITIES) CONST UCHAR *PriorityAssignments, + _In_reads_(NDIS_QOS_MAXIMUM_TRAFFIC_CLASSES) CONST UCHAR *TcBandwidthAssignments, + _In_reads_(NDIS_QOS_MAXIMUM_TRAFFIC_CLASSES) CONST UCHAR *TsaAssignments, + _In_ ULONG PfcEnable, + _In_ ULONG NumClassificationElements, + _Post_ + _When_(return != 0, _Deref_out_range_(>=, sizeof(NDIS_QOS_PARAMETERS))) + ULONG *MaxParamsSize + ) +/*++ +Routine Description: + + This routine creates and initializes a new NDIS_QOS_PARAMETERS structure. + +--*/ +{ + ULONG FirstElementOffset; + ULONG ParamSize; + PNDIS_QOS_PARAMETERS Parameters; + + *MaxParamsSize = 0; + + // + // Figure out the actual size of NDIS_QOS_PARAMETERS if it were to accommodate + // NumClassificationElements classification elements. Note that the first + // element must be aligned at the end of the parent NDIS_QOS_PARAMETERS + // structure. + // + ParamSize = sizeof(NDIS_QOS_PARAMETERS); + FirstElementOffset = ALIGN_UP(ParamSize, NDIS_QOS_CLASSIFICATION_ELEMENT); + ParamSize = FirstElementOffset + NumClassificationElements * sizeof(NDIS_QOS_CLASSIFICATION_ELEMENT); + + // + // Allocate the NDIS_QOS_PARAMETERS structure. + // + Parameters = + NdisAllocateMemoryWithTagPriority( + NdisDriverHandle, + ParamSize, + NIC_TAG_QOS_PARAMS, + NormalPoolPriority); + + if (Parameters == NULL) + { + DEBUGP(MP_ERROR, "Failed to allocate NDIS_QOS_PARAMETERS.\n"); + return NULL; + } + + // + // Initialize the structure. + // + NdisZeroMemory(Parameters, ParamSize); + Parameters->Header.Type = NDIS_OBJECT_TYPE_QOS_PARAMETERS; + Parameters->Header.Revision = NDIS_QOS_PARAMETERS_REVISION_1; + Parameters->Header.Size = (USHORT)FirstElementOffset; + Parameters->Flags = NDIS_QOS_PARAMETERS_ETS_CONFIGURED | + NDIS_QOS_PARAMETERS_PFC_CONFIGURED | + NDIS_QOS_PARAMETERS_CLASSIFICATION_CONFIGURED; + Parameters->NumTrafficClasses = NumTrafficClasses; + NdisMoveMemory( + Parameters->PriorityAssignmentTable, + PriorityAssignments, + sizeof(Parameters->PriorityAssignmentTable)); + NdisMoveMemory( + Parameters->TcBandwidthAssignmentTable, + TcBandwidthAssignments, + sizeof(Parameters->TcBandwidthAssignmentTable)); + NdisMoveMemory( + Parameters->TsaAssignmentTable, + TsaAssignments, + sizeof(Parameters->TsaAssignmentTable)); + Parameters->PfcEnable = PfcEnable; + Parameters->ClassificationElementSize = sizeof(NDIS_QOS_CLASSIFICATION_ELEMENT); + Parameters->FirstClassificationElementOffset = FirstElementOffset; + + *MaxParamsSize = ParamSize; + + return Parameters; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +PNDIS_QOS_PARAMETERS +CreateDefaultOperationalParameters( + _In_ ULONG NumClassificationElements, + _Deref_out_range_(>=, sizeof(NDIS_QOS_PARAMETERS)) ULONG *MaxParamsSize + ) +/*++ +Routine Description: + + This routine creates and initializes the default operational parameters, which is often implementation- + dependent. + +Remarks: + + This example shows a simple case where a default set of PFC and ETS parameters are defined. When the + respective NDIS_QOS_PARAMETERS_ETS_CONFIGURED and NDIS_QOS_PARAMETERS_PFC_CONFIGURED flags are not set + in the NDIS_QOS_PARAMETERS structure given during OID_QOS_PARAMETERS, the default parameters will be + used instead. + +--*/ +{ + return + CreateParameters( + NIC_SUPPORTED_NUM_TCS, + DefaultQOSPriorityAssignmentTable, + DefaultQOSTcBandwidthAssignmentTable, + DefaultQOSTsaAssignmentTable, + 0, // Flow control disabled on all priorities by default + NumClassificationElements, + MaxParamsSize); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +AddNewClassification( + _Inout_updates_bytes_to_(MaxParamsSize, Parameters->Header.Size) PNDIS_QOS_PARAMETERS Parameters, + _In_range_(>=, sizeof(NDIS_QOS_PARAMETERS)) ULONG MaxParamsSize, + _In_ USHORT ConditionSelector, + _In_ USHORT ConditionField, + _In_ USHORT ActionSelector, + _In_ USHORT ActionField + ) +/*++ +Routine Description: + + This routine adds a new NDIS_QOS_CLASSIFICATION_ELEMENT to an existing NDIS_QOS_PARAMETERS created + using the CreateParameters routine. + +Remarks: + + The NDIS_QOS_PARAMETERS structure is assumed to have preallocated enough storage to accommodate + all the new classification entries. + +--*/ +{ + PNDIS_QOS_CLASSIFICATION_ELEMENT Element; + + Element = + (PNDIS_QOS_CLASSIFICATION_ELEMENT) + ((PCHAR)Parameters + + Parameters->FirstClassificationElementOffset + + Parameters->ClassificationElementSize * Parameters->NumClassificationElements); + + if ((PCHAR)Element + Parameters->ClassificationElementSize <= (PCHAR)Parameters + MaxParamsSize) + { + Element->Header.Type = NDIS_OBJECT_TYPE_QOS_CLASSIFICATION_ELEMENT; + Element->Header.Revision = NDIS_QOS_CLASSIFICATION_ELEMENT_REVISION_1; + Element->Header.Size = NDIS_SIZEOF_QOS_CLASSIFICATION_ELEMENT_REVISION_1; + Element->Flags = 0; + Element->ConditionSelector = ConditionSelector; + Element->ConditionField = ConditionField; + Element->ActionSelector = ActionSelector; + Element->ActionField = ActionField; + + NT_ASSERT(Parameters->ClassificationElementSize <= sizeof(*Element)); + _Analysis_assume_(Parameters->ClassificationElementSize <= sizeof(*Element)); + + Parameters->Header.Size += sizeof(*Element); + Parameters->NumClassificationElements++; + } + else + { + ASSERTMSG("Parameters buffer too small", 0); + } +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +IndicateParameters( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ NDIS_STATUS StatusCode, + _In_ PNDIS_QOS_PARAMETERS Parameters + ) +/*++ +Routine Description: + + This routine indicates to NDIS a NDIS_QOS_PARAMETERS structure. + +--*/ +{ + NDIS_STATUS_INDICATION Status = { 0 }; + + DEBUGP(MP_TRACE, "[%p] ---> IndicateParameters\n", Adapter); + + Status.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION; + Status.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1; + Status.Header.Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1; + Status.StatusCode = StatusCode; + Status.StatusBuffer = Parameters; + Status.StatusBufferSize = Parameters->Header.Size; + + NdisMIndicateStatusEx(Adapter->AdapterHandle, &Status); + + DEBUGP(MP_TRACE, "[%p] <--- IndicateParameters\n", Adapter); +} + +_IRQL_requires_(PASSIVE_LEVEL) +NDIS_STATUS +ReadQOSConfig( + _In_ NDIS_HANDLE ConfigurationHandle, + _Inout_ struct _MP_ADAPTER *Adapter) +/*++ +Routine Description: + + This routine will read the QOS configuration from the NDIS registry, and set the results to the QOSData flags field. + +Arguments: + + ConfigurationHandle - Adapter configuration handle + Adapter - Pointer to our adapter + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PNDIS_CONFIGURATION_PARAMETER Parameter; + NDIS_STRING QOSKeyword = NDIS_STRING_CONST("*QOS"); + + DEBUGP(MP_TRACE, "[%p] ---> ReadQOSConfig\n", Adapter); + + PAGED_CODE(); + + do + { + // + // Read the *QOS flag (whether QOS is enabled on the adapter). + // + NdisReadConfiguration( + &Status, + &Parameter, + ConfigurationHandle, + &QOSKeyword, + NdisParameterInteger); + if (Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, + "[%p] NdisReadConfiguration for *QOS failed Status 0x%08x, defaulting to disabled.\n", + Adapter, + Status); + Status = NDIS_STATUS_SUCCESS; + break; + } + + if (Parameter->ParameterData.IntegerData != 1) + { + break; + } + + Adapter->QOSData.Flags |= fMP_QOS_ENABLED; + + // + // Set up the current QOS capabilities. If QOS is enabled, the current + // capabilities will normally match the hardware capabilities, i.e. + // they are not configurable. + // + Adapter->QOSData.CurrentMaxNumTCs = NIC_SUPPORTED_NUM_TCS; + Adapter->QOSData.CurrentMaxNumEtsCapableTCs = min(NIC_SUPPORTED_NUM_ETS_CAPABLE_TCS, Adapter->QOSData.CurrentMaxNumTCs); + Adapter->QOSData.CurrentMaxNumPfcEnabledTCs = min(NIC_SUPPORTED_NUM_PFC_ENABLED_TCS, Adapter->QOSData.CurrentMaxNumTCs); + + Status = NDIS_STATUS_SUCCESS; + } while (FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- ReadQOSConfig Status 0x%08x\n", Adapter, Status); + + return Status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NDIS_STATUS +SetQOSParameters( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_QOS_PARAMETERS Params) +/*++ +Routine Description: + + This routine will configure the classification table and validate the ETS and PFC parameters. + +Arguments: + + Adapter - Pointer to our adapter + Params - Pointer to the new parameters + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PNDIS_QOS_PARAMETERS OperationalParams; + ULONG MaxParamsSize; + UCHAR iSCSIPriority = NIC_ISCSI_PRIORITY; + UCHAR FCOEPriority = NIC_FCOE_PRIORITY; + + PAGED_CODE(); + + DEBUGP(MP_TRACE, "[%p] ---> SetQOSParameters\n", Adapter); + + OperationalParams = + CreateDefaultOperationalParameters( + NIC_SUPPORTED_NUM_CLASSIFICATIONS, + &MaxParamsSize); + if (OperationalParams == NULL) + { + // + // Unable to allocate operational parameters structure but this is not a deal breaker. + // Proceed to service the incoming NDIS_QOS_PARAMETERS. + // + DEBUGP(MP_ERROR, "Failed to allocate default NDIS_QOS_PARAMETERS\n"); + } + + if ((Params->Flags & (NDIS_QOS_PARAMETERS_ETS_CONFIGURED | + NDIS_QOS_PARAMETERS_PFC_CONFIGURED | + NDIS_QOS_PARAMETERS_CLASSIFICATION_CONFIGURED)) != 0) + { + if ((Params->Flags & (NDIS_QOS_PARAMETERS_ETS_CONFIGURED | NDIS_QOS_PARAMETERS_PFC_CONFIGURED)) != + (NDIS_QOS_PARAMETERS_ETS_CONFIGURED | NDIS_QOS_PARAMETERS_PFC_CONFIGURED)) + { + // + // ETS and PFC must be configured together. + // + Status = NDIS_STATUS_NOT_SUPPORTED; + goto Exit; + } + + // + // Enable ETS using the parameters in: + // - Params->PriorityAssignmentTable + // - Params->TcBandwidthAssignmentTable + // - Params->TsaAssignmentTable + // + // A real hardware-based implementation would program the ETS hardware here. + // + if (OperationalParams != NULL) + { + OperationalParams->NumTrafficClasses = Params->NumTrafficClasses; + NdisMoveMemory( + OperationalParams->PriorityAssignmentTable, + Params->PriorityAssignmentTable, + sizeof(Params->PriorityAssignmentTable)); + NdisMoveMemory( + OperationalParams->TcBandwidthAssignmentTable, + Params->TcBandwidthAssignmentTable, + sizeof(Params->TcBandwidthAssignmentTable)); + NdisMoveMemory( + OperationalParams->TsaAssignmentTable, + Params->TsaAssignmentTable, + sizeof(Params->TsaAssignmentTable)); + } + + // + // Enable PFC using the bitmap in Params->PfcEnable + // + // A real hardware-based implementation would program the PFC hardware here. + // + if (OperationalParams != NULL) + { + OperationalParams->PfcEnable = Params->PfcEnable; + } + + // + // Process packet classification parameters. + // + iSCSIPriority = 0; + FCOEPriority = 0; + + if (Params->Flags & NDIS_QOS_PARAMETERS_CLASSIFICATION_CONFIGURED) + { + // + // Packet classification enabled. Search for a classification element that matches the + // traffic type that your related hardware device or software driver (e.g. FCoE driver) + // sends. + // + PNDIS_QOS_CLASSIFICATION_ELEMENT Entry; + ULONG i; + + Entry = (PNDIS_QOS_CLASSIFICATION_ELEMENT)((PCHAR)Params + Params->FirstClassificationElementOffset); + + for (i = 0; i < Params->NumClassificationElements; i++) + { + NT_ASSERT((PCHAR)Entry + sizeof(*Entry) <= (PCHAR)Params + Params->Header.Size); + _Analysis_assume_((PCHAR)Entry + sizeof(*Entry) <= (PCHAR)Params + Params->Header.Size); + + if (Entry->Header.Type == NDIS_OBJECT_TYPE_QOS_CLASSIFICATION_ELEMENT && + Entry->Header.Revision == NDIS_QOS_CLASSIFICATION_ELEMENT_REVISION_1 && + Entry->Header.Size >= NDIS_SIZEOF_QOS_CLASSIFICATION_ELEMENT_REVISION_1) + { + NT_ASSERT((PCHAR)Entry + Entry->Header.Size <= (PCHAR)Params + Params->Header.Size); + _Analysis_assume_((PCHAR)Entry + Entry->Header.Size <= (PCHAR)Params + Params->Header.Size); + + if (Entry->ConditionSelector == NDIS_QOS_CONDITION_ETHERTYPE && + Entry->ConditionField == NIC_FCOE_ETHERTYPE && + Entry->ActionSelector == NDIS_QOS_ACTION_PRIORITY) + { + // + // Found a classification entry for FCoE. FCoE packets that directly originate + // from the device or driver are to be tagged with the 802.1p priority value + // stored in Entry->ActionField. + // + Entry->Flags |= NDIS_QOS_CLASSIFICATION_ENFORCED_BY_MINIPORT; // Acknowledge the classification + FCOEPriority = (UCHAR)Entry->ActionField; + } + else if (Entry->ConditionSelector == NDIS_QOS_CONDITION_TCP_PORT && + Entry->ConditionField == NIC_ISCSI_TCP_PORT && + Entry->ActionSelector == NDIS_QOS_ACTION_PRIORITY) + { + // + // Found a classification entry for iSCSI. iSCSI packets that directly originate + // from the device or driver are to be tagged with the 802.1p priority value + // stored in Entry->ActionField. + // + Entry->Flags |= NDIS_QOS_CLASSIFICATION_ENFORCED_BY_MINIPORT; // Acknowledge the classification + iSCSIPriority = (UCHAR)Entry->ActionField; + } + else if (Entry->ConditionSelector == NDIS_QOS_CONDITION_DEFAULT && + Entry->ActionSelector == NDIS_QOS_ACTION_PRIORITY) + { + // + // Found a default classification entry. This entry, if present, is always located + // at the start of the classification array. + // + NT_ASSERT(i == 0); + iSCSIPriority = FCOEPriority = (UCHAR)Entry->ActionField; + } + } + + Entry = (PNDIS_QOS_CLASSIFICATION_ELEMENT)((PCHAR)Entry + Params->ClassificationElementSize); + } + } + } + else + { + // + // ETS, PFC, and Classification settings not available. Use default. + // + } + + if (OperationalParams != NULL) + { + AddNewClassification( + OperationalParams, + MaxParamsSize, + NDIS_QOS_CONDITION_ETHERTYPE, + NIC_FCOE_ETHERTYPE, + NDIS_QOS_ACTION_PRIORITY, + FCOEPriority); + + AddNewClassification( + OperationalParams, + MaxParamsSize, + NDIS_QOS_CONDITION_TCP_PORT, + NIC_ISCSI_TCP_PORT, + NDIS_QOS_ACTION_PRIORITY, + iSCSIPriority); + + // + // 'OperationalParams' contains the new operational parameters. An actual device will + // operate on these settings. + // + IndicateParameters(Adapter, NDIS_STATUS_QOS_OPERATIONAL_PARAMETERS_CHANGE, OperationalParams); + } + +Exit: + if (OperationalParams != NULL) + { + NdisFreeMemoryWithTagPriority(NdisDriverHandle, OperationalParams, NIC_TAG_QOS_PARAMS); + } + + DEBUGP(MP_TRACE, "<--- [%p] SetQOSParameters Status 0x%08x\n", Adapter, Status); + + return Status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NDIS_STATUS +InitializeQOSConfig( + _Inout_ PMP_ADAPTER Adapter) +/*++ +Routine Description: + + This routine will set the supported and actual QOS capabilities for the miniport. + +Arguments: + + Adapter - Pointer to our adapter + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status; + NDIS_MINIPORT_ADAPTER_HARDWARE_ASSIST_ATTRIBUTES HardwareAssistAttributes; + NDIS_QOS_CAPABILITIES HwQosCapabilities; + NDIS_QOS_CAPABILITIES CurrentQosCapabilities; + PNDIS_QOS_PARAMETERS OperationalParams; + ULONG MaxParamsSize; + + DEBUGP(MP_TRACE, "[%p] ---> InitializeQOSConfig\n", Adapter); + + PAGED_CODE(); + + NdisZeroMemory(&HardwareAssistAttributes, sizeof(HardwareAssistAttributes)); + HardwareAssistAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_HARDWARE_ASSIST_ATTRIBUTES; + HardwareAssistAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_HARDWARE_ASSIST_ATTRIBUTES_REVISION_3; + HardwareAssistAttributes.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_HARDWARE_ASSIST_ATTRIBUTES_REVISION_3; + + // + // Set NIC QOS capabilities. + // + NdisZeroMemory(&HwQosCapabilities, sizeof(HwQosCapabilities)); + HwQosCapabilities.Header.Type = NDIS_OBJECT_TYPE_QOS_CAPABILITIES; + HwQosCapabilities.Header.Revision = NDIS_QOS_CAPABILITIES_REVISION_1; + HwQosCapabilities.Header.Size = NDIS_SIZEOF_QOS_CAPABILITIES_REVISION_1; + + HwQosCapabilities.Flags = NDIS_QOS_CAPABILITIES_STRICT_TSA_SUPPORTED; + HwQosCapabilities.MaxNumTrafficClasses = NIC_SUPPORTED_NUM_TCS; + HwQosCapabilities.MaxNumEtsCapableTrafficClasses = NIC_SUPPORTED_NUM_ETS_CAPABLE_TCS; + HwQosCapabilities.MaxNumPfcEnabledTrafficClasses = NIC_SUPPORTED_NUM_PFC_ENABLED_TCS; + + HardwareAssistAttributes.HardwareQosCapabilities = &HwQosCapabilities; + + // + // Set the actual NIC QOS capabilities. + // + NdisZeroMemory(&CurrentQosCapabilities, sizeof(CurrentQosCapabilities)); + CurrentQosCapabilities.Header.Type = NDIS_OBJECT_TYPE_QOS_CAPABILITIES; + CurrentQosCapabilities.Header.Revision = NDIS_QOS_CAPABILITIES_REVISION_1; + CurrentQosCapabilities.Header.Size = NDIS_SIZEOF_QOS_CAPABILITIES_REVISION_1; + + if (QOS_ENABLED(Adapter)) + { + // + // Note: If QOS is disabled, all the following fields are set to 0. + // + CurrentQosCapabilities.Flags = NDIS_QOS_CAPABILITIES_STRICT_TSA_SUPPORTED; + CurrentQosCapabilities.MaxNumTrafficClasses = Adapter->QOSData.CurrentMaxNumTCs; + CurrentQosCapabilities.MaxNumEtsCapableTrafficClasses = Adapter->QOSData.CurrentMaxNumEtsCapableTCs; + CurrentQosCapabilities.MaxNumPfcEnabledTrafficClasses = Adapter->QOSData.CurrentMaxNumPfcEnabledTCs; + } + + HardwareAssistAttributes.CurrentQosCapabilities = &CurrentQosCapabilities; + + Status = NdisMSetMiniportAttributes( + Adapter->AdapterHandle, + (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&HardwareAssistAttributes); + if (NDIS_STATUS_SUCCESS != Status) + { + DEBUGP(MP_ERROR, "[%p] NdisMSetMiniportAttributes Status 0x%08x\n", Adapter, Status); + } + + // + // At this point, we can indicate up the default operational parameters. + // + if (QOS_ENABLED(Adapter)) + { + OperationalParams = CreateDefaultOperationalParameters(NIC_SUPPORTED_NUM_CLASSIFICATIONS, &MaxParamsSize); + + if (OperationalParams != NULL) + { + // + // Add default FCoE classification. + // + AddNewClassification( + OperationalParams, + MaxParamsSize, + NDIS_QOS_CONDITION_ETHERTYPE, + NIC_FCOE_ETHERTYPE, + NDIS_QOS_ACTION_PRIORITY, + NIC_FCOE_PRIORITY); + + // + // Add default iSCSI classification. + // + AddNewClassification( + OperationalParams, + MaxParamsSize, + NDIS_QOS_CONDITION_TCP_PORT, + NIC_ISCSI_TCP_PORT, + NDIS_QOS_ACTION_PRIORITY, + NIC_ISCSI_PRIORITY); + + IndicateParameters(Adapter, NDIS_STATUS_QOS_OPERATIONAL_PARAMETERS_CHANGE, OperationalParams); + + + NdisFreeMemoryWithTagPriority(NdisDriverHandle, OperationalParams, NIC_TAG_QOS_PARAMS); + } + else + { + DEBUGP(MP_ERROR, "Failed to allocate default NDIS_QOS_PARAMETERS\n"); + } + } + + DEBUGP(MP_TRACE, "<--- [%p] InitializeQOSConfig Status 0x%08x\n", Adapter, Status); + + return Status; +} + diff --git a/network/ndis/netvmini/6x/qos.h b/network/ndis/netvmini/6x/qos.h new file mode 100644 index 00000000..66a27483 --- /dev/null +++ b/network/ndis/netvmini/6x/qos.h @@ -0,0 +1,70 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Qos.h + +Abstract: + + This module declares the NDIS QoS related data types, flags, macros, and functions. + +Revision History: + +--*/ + +#pragma once + +#if (NDIS_SUPPORT_NDIS630) + +// +// The MP_ADAPTER_QOS_DATA structure is used to track the global QoS configuration for an adapter +// +typedef struct _MP_ADAPTER_QOS_DATA +{ + // + // Tracks global NDIS QoS state + // +#define fMP_QOS_ENABLED 0x00000001 + ULONG Flags; + + // + // Current QoS capabilities + // + ULONG CurrentMaxNumTCs; + ULONG CurrentMaxNumEtsCapableTCs; + ULONG CurrentMaxNumPfcEnabledTCs; +} MP_ADAPTER_QOS_DATA, *PMP_ADAPTER_QOS_DATA; + +#define QOS_ENABLED(_Adapter) \ + ((_Adapter)->QOSData.Flags & fMP_QOS_ENABLED) + +_IRQL_requires_(PASSIVE_LEVEL) +NDIS_STATUS +ReadQOSConfig( + _In_ NDIS_HANDLE ConfigurationHandle, + _Inout_ struct _MP_ADAPTER *Adapter); + +_IRQL_requires_(PASSIVE_LEVEL) +NDIS_STATUS +SetQOSParameters( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_QOS_PARAMETERS Params); + +_IRQL_requires_(PASSIVE_LEVEL) +NDIS_STATUS +InitializeQOSConfig( + _Inout_ struct _MP_ADAPTER *Adapter); + +#else // NDIS_SUPPORT_NDIS630 + +#define ReadQOSConfig(ConfigurationHandle, Adapter) NDIS_STATUS_SUCCESS +#define InitializeQOSConfig(Adapter) NDIS_STATUS_SUCCESS + +#endif // NDIS_SUPPORT_NDIS630 diff --git a/network/ndis/netvmini/6x/tcbrcb.c b/network/ndis/netvmini/6x/tcbrcb.c new file mode 100644 index 00000000..1d2faf50 --- /dev/null +++ b/network/ndis/netvmini/6x/tcbrcb.c @@ -0,0 +1,221 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + TcbRcb.C + +Abstract: + + This module contains miniport functions for handling Send & Receive + packets and other helper routines called by these miniport functions. + + In order to excercise the send and receive code path of this driver, + you should install more than one instance of the miniport. If there + is only one instance installed, the driver throws the send packet on + the floor and completes the send successfully. If there are more + instances present, it indicates the incoming send packet to the other + instances. For example, if there 3 instances: A, B, & C installed. + Packets coming in for A instance would be indicated to B & C; packets + coming into B would be indicated to C, & A; and packets coming to C + would be indicated to A & B. + +Revision History: + +Notes: + +--*/ + +#include "netvmin6.h" +#include "tcbrcb.tmh" + + +VOID +ReturnTCB( + _In_ PMP_ADAPTER Adapter, + _In_ PTCB Tcb) +{ + TXNblRelease(Adapter, NBL_FROM_SEND_NB(Tcb->NetBuffer), TRUE); + Tcb->NetBuffer = NULL; + + NdisInterlockedInsertTailList( + &Adapter->FreeTcbList, + &Tcb->TcbLink, + &Adapter->FreeTcbListLock); +} + + + +_Must_inspect_result_ +_Success_(return != NULL) +PRCB +GetRCB( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _In_ PFRAME Frame) +/*++ + +Routine Description: + + This routine gets an unused RCB from the pool. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Adapter The receiving adapter + Nbl1QInfo 8021Q Tag information for the FRAME being received + Frame The frame that will be attached to the RCB + +Return Value: + + NULL if an RCB could not be allocated. + Else, a pointer to an initialized RCB. + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_RESOURCES; + PRCB Rcb = NULL; + + DEBUGP(MP_TRACE, "[%p] ---> GetRCB.\n", Adapter); + + if(VMQ_ENABLED(Adapter)) + { + // + // Retrieve the RCB from the target VMQ queue for the frame + // + GetRcbForRxQueue(Adapter, Frame, Nbl1QInfo, &Rcb); + } + else + { + // + // Retrieve the RCB from the global RCB pool + // + PLIST_ENTRY pEntry = NdisInterlockedRemoveHeadList( + &Adapter->FreeRcbList, + &Adapter->FreeRcbListLock); + if (pEntry) + { + Rcb = CONTAINING_RECORD(pEntry, RCB, RcbLink); + // + // Receiving on the default receive queue, increment its pending count + // + Status = NICReferenceReceiveBlock(Adapter, 0); + if(Status != NDIS_STATUS_SUCCESS) + { + // + // The adapter is no longer in a ready state, so we were not able to take a reference on the + // receive block. Add the RCB back to the free list and fail this receive. + // + NdisInterlockedInsertTailList( + &Adapter->FreeRcbList, + &Rcb->RcbLink, + &Adapter->FreeRcbListLock); + Rcb = NULL; + } + } + } + + if (Rcb) + { + // + // Simulate the hardware DMA'ing the received frame into the NB's MDL. + // + Status = HWBeginReceiveDma(Adapter, Nbl1QInfo, Rcb, Frame); + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_TRACE, "[%p] HWBeginReceiveDma failed with error 0x%08x, aborting RCB allocation.\n", Adapter, Status); + // + // Increase failure counters if appropriate + // + if(Status == NDIS_STATUS_RESOURCES) + { + ++Adapter->RxResourceErrors; + } + else if(Status != NDIS_STATUS_INVALID_ADDRESS) + { + ++Adapter->RxRuntErrors; + } + // + // Recover RCB + // + ReturnRCB(Adapter, Rcb); + Rcb = NULL; + } + } + else + { + DEBUGP(MP_LOUD, "[%p] An RCB could not be retrieved. Status: 0x%08x.\n", Adapter, Status); + ++Adapter->RxResourceErrors; + } + + DEBUGP(MP_LOUD, "[%p] Allocated RCB: %p.", Adapter, Rcb); + DEBUGP(MP_TRACE, "[%p] <--- GetRCB.\n", Adapter); + + return Rcb; +} + + +VOID +ReturnRCB( + _In_ PMP_ADAPTER Adapter, + _In_ PRCB Rcb) +/*++ + +Routine Description: + + This routine frees an RCB back to the unused pool, recovers relevant memory used in the RCB. + + Runs at IRQL <= DISPATCH_LEVEL + +Arguments: + + Adapter - The receiving adapter (the one that owns the RCB). + Rcb - The RCB to be freed. + +Return Value: + + None. + +--*/ +{ + PUCHAR Data = Rcb->Data; + ASSERT(Data); + + DEBUGP(MP_TRACE, "[%p] ---> ReturnRCB. RCB: %p\n", Adapter, Rcb); + + if(VMQ_ENABLED(Adapter)) + { + // + // Recover RCB back to owner VMQ queue + // + RecoverRxQueueRcb(Adapter, Rcb); + } + else + { + // + // Recover RCB to global RCB pool + // + NdisInterlockedInsertTailList( + &Adapter->FreeRcbList, + &Rcb->RcbLink, + &Adapter->FreeRcbListLock); + Rcb = NULL; + // + // We receive on the default receive queue, decrement its pending count + // + NICDereferenceReceiveBlock(Adapter, 0, NULL); + HWFrameRelease((PFRAME)Data); + } + + DEBUGP(MP_TRACE, "[%p] <--- ReturnRCB.\n", Adapter); + +} + + diff --git a/network/ndis/netvmini/6x/tcbrcb.h b/network/ndis/netvmini/6x/tcbrcb.h new file mode 100644 index 00000000..fca8f123 --- /dev/null +++ b/network/ndis/netvmini/6x/tcbrcb.h @@ -0,0 +1,81 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + TcbRcb.H + +Abstract: + + This module declares the TCB and RCB structures, and the functions to + manipulate them. + + See the comments in TcbRcb.c. + +--*/ + + +#ifndef _TCBRCB_H +#define _TCBRCB_H + + + +// +// TCB (Transmit Control Block) +// ----------------------------------------------------------------------------- +// + +typedef struct _TCB +{ + LIST_ENTRY TcbLink; + PNET_BUFFER NetBuffer; + ULONG FrameType; + ULONG BytesActuallySent; +} TCB, *PTCB; + + + +VOID +ReturnTCB( + _In_ PMP_ADAPTER Adapter, + _In_ PTCB Tcb); + + +// +// RCB (Receive Control Block) +// ----------------------------------------------------------------------------- +// + +typedef struct _RCB +{ + LIST_ENTRY RcbLink; + PNET_BUFFER_LIST Nbl; + PVOID Data; +#if (NDIS_SUPPORT_NDIS620) + PVOID LookaheadData; +#endif +} RCB, *PRCB; + +_Must_inspect_result_ +_Success_(return != NULL) +PRCB +GetRCB( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _In_ PFRAME Frame); + +VOID +ReturnRCB( + _In_ PMP_ADAPTER Adapter, + _In_ PRCB Rcb); + + + +#endif // _TCBRCB_H + diff --git a/network/ndis/netvmini/6x/trace.h b/network/ndis/netvmini/6x/trace.h new file mode 100644 index 00000000..b42c5275 --- /dev/null +++ b/network/ndis/netvmini/6x/trace.h @@ -0,0 +1,38 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Trace.H + +Abstract: + + + --*/ + + +#ifndef _TRACE_H +#define _TRACE_H + +// +// Debug support macros +// ----------------------------------------------------------------------------- +// + + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(NetVMiniGUID,(AF864470,D828,4526,BF2C,22D3A5C98C4C), \ + WPP_DEFINE_BIT(MP_ERROR) \ + WPP_DEFINE_BIT(MP_WARNING) \ + WPP_DEFINE_BIT(MP_TRACE) \ + WPP_DEFINE_BIT(MP_INFO) \ + WPP_DEFINE_BIT(MP_LOUD) \ + ) + +#endif diff --git a/network/ndis/netvmini/6x/vmq.c b/network/ndis/netvmini/6x/vmq.c new file mode 100644 index 00000000..d4e8fa7c --- /dev/null +++ b/network/ndis/netvmini/6x/vmq.c @@ -0,0 +1,3123 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Vmq.c + +Abstract: + + This module implements the VMQ related functionality for the adapter. + +--*/ + +#include "netvmin6.h" +#include "vmq.tmh" + +NDIS_STATUS +FreeAdapterQueueInfo( + _In_ PMP_ADAPTER Adapter, + USHORT QueueId, + _In_opt_ PNDIS_OID_REQUEST NdisSetRequest); + +NDIS_STATUS +InitializeAdapterQueueInfo( + _In_ PMP_ADAPTER Adapter, + _In_opt_ PNDIS_RECEIVE_QUEUE_PARAMETERS QueueParams, + _Out_ PMP_ADAPTER_QUEUE QueueInfo); + +NDIS_STATUS +AllocateRxQueueReceiveMemory( + _Inout_ struct _MP_ADAPTER *Adapter, + PMP_ADAPTER_QUEUE Queue); + +NDIS_IO_WORKITEM_FUNCTION FreeRxQueuesWorkItem; + +NDIS_STATUS +AllocateVMQData( + _Inout_ struct _MP_ADAPTER *Adapter) +/*++ +Routine Description: + + This routine will initialize the basic fields necessary for a MP_ADAPTER_VMQ_DATA structure. The function + should be called during adapter initialization, before any queues or filter requests are fielded. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Adapter - Pointer to our adapter + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PMP_ADAPTER_VMQ_DATA VMQData = &Adapter->VMQData; + UINT i; + + DEBUGP(MP_TRACE, "[%p] ---> AllocateVMQData\n", Adapter); + + do + { + NdisZeroMemory(VMQData, sizeof(MP_ADAPTER_VMQ_DATA)); + + // + // Initialize the locks for the queues + // + for(i=0; iRxQueues[i].QueueLock = NdisAllocateRWLock(Adapter->AdapterHandle); + if(!VMQData->RxQueues[i].QueueLock) + { + DEBUGP(MP_ERROR, "[%p] NdisAllocateRWLock failed for Queue %i.\n", Adapter, i); + Status = NDIS_STATUS_RESOURCES; + break; + } + } + + if(Status != NDIS_STATUS_SUCCESS) + { + // + // Failed allocating the locks, need to fail this allocation + // + break; + } + + }while(FALSE); + + DEBUGP(MP_TRACE, "<--- [%p] AllocateVMQData Status 0x%08x\n", Adapter, Status); + + return Status; +} + +NDIS_STATUS +AllocateDefaultRxQueue( + _Inout_ struct _MP_ADAPTER *Adapter) +/*++ +Routine Description: + + This routine will allocate the default receive queue (MP_ADAPTER_QUEUE). The function + should be called during adapter initialization, before any queues or filter requests are fielded. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Adapter - Pointer to our adapter + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PMP_ADAPTER_VMQ_DATA VMQData = &Adapter->VMQData; + + do + { + // + // Initialize the default queue. + // No need to take lock since this is done during adapter initialization. We won't be fielding receives until this is complete. + // + Status = InitializeAdapterQueueInfo(Adapter, NULL, &VMQData->RxQueues[0]); + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] Failed to initialize the default queue.\n", Adapter); + break; + } + + // + // Allocate the receive memory (no shared memory is necessary) + // + Status = AllocateRxQueueReceiveMemory(Adapter, &VMQData->RxQueues[0]); + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] Failed to allocate the receive memory for the default queue.\n", Adapter); + break; + } + + // + // Set default as completed. At this point the queue is ready for receives. + // + QUEUE_SET_FLAG(&VMQData->RxQueues[0], fMPAQI_COMPLETION_FINISHED); + } while(FALSE); + + return Status; +} + +NDIS_STATUS +ReadRxQueueConfig( + _In_ NDIS_HANDLE ConfigurationHandle, + _Inout_ struct _MP_ADAPTER *Adapter) +/*++ +Routine Description: + + This routine will read the VMQ configuration from the NDIS registry, and set the results to the VMQData flags field. + +Arguments: + + ConfigurationHandle - Adapter configuration handle + Adapter - Pointer to our adapter + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PNDIS_CONFIGURATION_PARAMETER Parameter = NULL; + NDIS_STRING VMQKeyword = NDIS_STRING_CONST("*VMQ"), + LookaheadKeyword = NDIS_STRING_CONST("*VMQLookaheadSplit"), + FilteringKeyword = NDIS_STRING_CONST("*VMQVlanFiltering"); + + DEBUGP(MP_TRACE, "[%p] ---> ReadRxQueueConfig\n", Adapter); + + do + { + // + // Read the *VMQ flag (whether VMQ is enabled on the adapter). + // Note: RSS and VMQ cannot be active simultaneously. If the miniport supports + // RSS, and the *RSS keyword is set to 1, then the *RssOrVmqPreference keyword + // must be used to determine which technology should be enabled on the adapter + // (0: enabled RSS, disable VMQ, 1: disable RSS, enable VMQ). + // + // + NdisReadConfiguration( + &Status, + &Parameter, + ConfigurationHandle, + &VMQKeyword, + NdisParameterInteger); + + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] NdisReadConfiguration for *VMQ failed Status 0x%08x, defaulting to disabled.\n", Adapter, Status); + Status = NDIS_STATUS_SUCCESS; + break; + } + + if(Parameter->ParameterData.IntegerData==1) + { + VMQ_SET_FLAG(Adapter, fMPVMQD_FILTERING_ENABLED); + } + + // + // Read the *VMQLookaheadSplit flag (whether lookahead split is enabled on the adapter) + // + NdisReadConfiguration( + &Status, + &Parameter, + ConfigurationHandle, + &LookaheadKeyword, + NdisParameterInteger); + + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] NdisReadConfiguration for *VMQLookaheadSplit failed Status 0x%08x, defaulting to disabled.\n", Adapter, Status); + Status = NDIS_STATUS_SUCCESS; + } + else if(Parameter->ParameterData.IntegerData==1) + { + VMQ_SET_FLAG(Adapter, fMPVMQD_LOOKAHEAD_ENABLED); + } + + // + // Read the *VMQVlanFiltering flag (whether lookahead split is enabled on the adapter) + // + NdisReadConfiguration( + &Status, + &Parameter, + ConfigurationHandle, + &FilteringKeyword, + NdisParameterInteger); + + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] NdisReadConfiguration for *VMQVlanFiltering failed Status 0x%08x, defaulting to disabled.\n", Adapter, Status); + Status = NDIS_STATUS_SUCCESS; + } + else if(Parameter->ParameterData.IntegerData==1) + { + VMQ_SET_FLAG(Adapter, fMPVMQD_VLANFILTER_ENABLED); + } + + } while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- ReadRxQueueConfig Status 0x%08x\n", Adapter, Status); + + return Status; +} + +NDIS_STATUS +InitializeRxQueueMPConfig( + _Inout_ struct _MP_ADAPTER *Adapter) +/*++ +Routine Description: + + This routine will set the supported and actual VMQ configuration for the miniport calling NdisMSetMiniportAttributes. + +Arguments: + + Adapter - Pointer to our adapter + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NDIS_MINIPORT_ADAPTER_HARDWARE_ASSIST_ATTRIBUTES HardwareAssistAttributes; + NDIS_RECEIVE_FILTER_CAPABILITIES HwCapabilities, CurrentCapabilities; + NDIS_NIC_SWITCH_CAPABILITIES HwSwitchCapabilities, CurrentSwitchCapabilities; + + DEBUGP(MP_TRACE, "[%p] ---> InitializeRxQueueMPConfig\n", Adapter); + + do + { + // + // Set the VMQ adapter capabilities. + // + NdisZeroMemory(&HardwareAssistAttributes, sizeof(HardwareAssistAttributes)); + + {C_ASSERT(sizeof(HardwareAssistAttributes) >= NDIS_SIZEOF_MINIPORT_ADAPTER_HARDWARE_ASSIST_ATTRIBUTES_REVISION_2);} + HardwareAssistAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_HARDWARE_ASSIST_ATTRIBUTES; + HardwareAssistAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_HARDWARE_ASSIST_ATTRIBUTES_REVISION_2; + HardwareAssistAttributes.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_HARDWARE_ASSIST_ATTRIBUTES_REVISION_2; + + // + // Filter capabilities for VMQ + // + + NdisZeroMemory(&HwCapabilities, sizeof(HwCapabilities)); + + // + // header fields + // + HwCapabilities.Header.Revision = NDIS_RECEIVE_FILTER_CAPABILITIES_REVISION_1; + HwCapabilities.Header.Size = NDIS_SIZEOF_RECEIVE_FILTER_CAPABILITIES_REVISION_1; + HwCapabilities.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + // + // enable VMQ filters + // + HwCapabilities.EnabledFilterTypes = NDIS_RECEIVE_FILTER_VMQ_FILTERS_ENABLED; + // + // enable VMQ queues + // + HwCapabilities.EnabledQueueTypes = NDIS_RECEIVE_FILTER_VM_QUEUES_ENABLED; + // + // queue properties + // + HwCapabilities.NumQueues = NIC_SUPPORTED_NUM_QUEUES - 1;// First queue reserved for default + HwCapabilities.SupportedQueueProperties = NDIS_RECEIVE_FILTER_VM_QUEUE_SUPPORTED | NDIS_RECEIVE_FILTER_LOOKAHEAD_SPLIT_SUPPORTED; + +#if (NDIS_SUPPORT_NDIS630) + HwCapabilities.SupportedQueueProperties |= NDIS_RECEIVE_FILTER_DYNAMIC_PROCESSOR_AFFINITY_CHANGE_SUPPORTED; +#endif + + HwCapabilities.SupportedFilterTests = NDIS_RECEIVE_FILTER_TEST_HEADER_FIELD_EQUAL_SUPPORTED; + HwCapabilities.SupportedHeaders = NDIS_RECEIVE_FILTER_MAC_HEADER_SUPPORTED; + HwCapabilities.SupportedMacHeaderFields = NDIS_RECEIVE_FILTER_MAC_HEADER_DEST_ADDR_SUPPORTED | NDIS_RECEIVE_FILTER_MAC_HEADER_VLAN_ID_SUPPORTED; + HwCapabilities.MaxMacHeaderFilters = NIC_MAX_HEADER_FILTERS; + HwCapabilities.MinLookaheadSplitSize = NIC_MIN_LOOKAHEAD_SPLIT; + HwCapabilities.MaxLookaheadSplitSize = NIC_MAX_LOOKAHEAD_SPLIT; + HardwareAssistAttributes.HardwareReceiveFilterCapabilities = &HwCapabilities; + + // + // Set the actual VMQ adapter configuration. + // + + NdisZeroMemory(&CurrentCapabilities, sizeof(CurrentCapabilities)); + + // + // header fields + // + CurrentCapabilities.Header.Revision = NDIS_RECEIVE_FILTER_CAPABILITIES_REVISION_1; + CurrentCapabilities.Header.Size = NDIS_SIZEOF_RECEIVE_FILTER_CAPABILITIES_REVISION_1; + CurrentCapabilities.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + + if(VMQ_ENABLED(Adapter)) + { + // + //set equal to HW capabilities, which has all the supported options set, and then remove optional disabled capabilties + // + memcpy(&CurrentCapabilities, &HwCapabilities, sizeof(CurrentCapabilities)); + if(!LOOKAHEAD_SPLIT_ENABLED(Adapter)) + { + // + // Remove lookahead split capability flag, as it's disabled in the driver configuration + // + CurrentCapabilities.SupportedQueueProperties &= ~NDIS_RECEIVE_FILTER_LOOKAHEAD_SPLIT_SUPPORTED; + } + if(!VLAN_FILTER_ENABLED(Adapter)) + { + // + // Remove lookahead vlan filtering capability flag, as it's disabled in the driver configuration + // + CurrentCapabilities.SupportedMacHeaderFields &= ~NDIS_RECEIVE_FILTER_MAC_HEADER_VLAN_ID_SUPPORTED; + } + } + + // + // Set NIC switch adapter capabilities. + // + HardwareAssistAttributes.CurrentReceiveFilterCapabilities = &CurrentCapabilities; + + NdisZeroMemory(&HwSwitchCapabilities, sizeof(HwSwitchCapabilities)); + + HwSwitchCapabilities.Header.Revision = NDIS_NIC_SWITCH_CAPABILITIES_REVISION_1; + HwSwitchCapabilities.Header.Size = NDIS_SIZEOF_NIC_SWITCH_CAPABILITIES_REVISION_1; + HwSwitchCapabilities.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + + // + // The NIC's limits corresponds to the maximum number of filters supported. + // + HwSwitchCapabilities.NumTotalMacAddresses = NIC_MAX_HEADER_FILTERS; + HwSwitchCapabilities.NumMacAddressesPerPort = NIC_MAX_HEADER_FILTERS; + HwSwitchCapabilities.NumVlansPerPort = NIC_MAX_HEADER_FILTERS; + + HardwareAssistAttributes.HardwareNicSwitchCapabilities = &HwSwitchCapabilities; + + // + // Set the actual NIC switch adapter configuration. + // + NdisZeroMemory(&CurrentSwitchCapabilities, sizeof(CurrentSwitchCapabilities)); + + CurrentSwitchCapabilities.Header.Revision = NDIS_NIC_SWITCH_CAPABILITIES_REVISION_1; + CurrentSwitchCapabilities.Header.Size = NDIS_SIZEOF_NIC_SWITCH_CAPABILITIES_REVISION_1; + CurrentSwitchCapabilities.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + + if(VMQ_ENABLED(Adapter)) + { + // + // Set equal to HW capabilities, which has all the supported options set. + // + memcpy(&CurrentSwitchCapabilities, &HwSwitchCapabilities, sizeof(CurrentSwitchCapabilities)); + } + + HardwareAssistAttributes.CurrentNicSwitchCapabilities = &CurrentSwitchCapabilities; + + Status = NdisMSetMiniportAttributes( + Adapter->AdapterHandle, + (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&HardwareAssistAttributes); + if (NDIS_STATUS_SUCCESS != Status) + { + DEBUGP(MP_ERROR, "[%p] NdisMSetMiniportAttributes Status 0x%08x\n", Adapter, Status); + break; + } + + }while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- InitializeRxQueueMPConfig Status 0x%08x\n", Adapter, Status); + + return Status; +} + +BOOLEAN +ValidGroupAffinity( +_In_ PGROUP_AFFINITY GroupAffinity) +/*++ +Routine Description: + + This routine will verify that the passed in group affinity contains valid values. + + Runs at any IRQL. + +Arguments: + + GroupAffinity - Affinity to verify + +Return Value: + + TRUE - Affinity is valid + FALSE - Affinity is invalid + +--*/ +{ + // + // GetActiveProcessorCount will validate the group value + // + #pragma warning( suppress: 28193 ) // we check ProcessorNumber result later in code + UCHAR ProcessorNumber = RtlFindLeastSignificantBit((ULONGLONG)GroupAffinity->Mask); + ULONG ActiveProcessors = KeQueryActiveProcessorCountEx(GroupAffinity->Group); + + if(!ActiveProcessors) + { + // + // Group was invalid + // + DEBUGP(MP_TRACE, "ValidGroupAffinity failed (KeQueryActiveProcessorCountEx error). Group: 0x%x, Mask: 0x%I64x\n", + GroupAffinity->Group, GroupAffinity->Mask); + return FALSE; + } + + if(ProcessorNumber == -1) + { + // + // The affinity mask must be non-zero (RtlFindLeastSignificantBit returns -1 if there aren't any non-zero bits) + // + DEBUGP(MP_TRACE, "ValidGroupAffinity failed (zero mask). Group: 0x%x, Mask: 0x%I64x\n", GroupAffinity->Group, GroupAffinity->Mask); + return FALSE; + } + + if(ProcessorNumber >= ActiveProcessors) + { + // + // The processor number is out of range + // + DEBUGP(MP_TRACE, "ValidGroupAffinity failed (processor number out of range). Group: 0x%x, Mask: 0x%I64x, ProcessorNumber: %i, ActiveProcessors: %i\n", + GroupAffinity->Group, GroupAffinity->Mask, ProcessorNumber, ActiveProcessors); + return FALSE; + } + + return TRUE; +} + +NDIS_STATUS +InitializeAdapterQueueInfo( + _In_ PMP_ADAPTER Adapter, + _In_opt_ PNDIS_RECEIVE_QUEUE_PARAMETERS QueueParams, + _Out_ PMP_ADAPTER_QUEUE QueueInfo) +/*++ +Routine Description: + + This routine will allocate the PMP_ADAPTER_QUEUE structure passed in using the QueueParams specified. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Adapter - Pointer to our adapter + QueueParams - NDIS_RECEIVE_QUEUE_PARAMETERS data that define the configuration for the queue + QueueInfo - Pointer to PMP_ADAPTER_QUEUE to derefence and allocate + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NDIS_RECEIVE_QUEUE_ID QueueId = 0; + + DEBUGP(MP_TRACE, "[%p] ---> InitializeAdapterQueueInfo\n", Adapter); + + do + { + if(QueueParams) + { + if(!ValidGroupAffinity(&QueueParams->ProcessorAffinity)) + { + DEBUGP(MP_ERROR, "[%p] Requested group affinity failed validation\n", Adapter); + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + QueueId = QueueParams->QueueId; + // + // Copy relevant data from the request + // + QueueInfo->QueueId = QueueId; + QueueInfo->NumSuggestedReceiveBuffers = QueueParams->NumSuggestedReceiveBuffers; + if(QueueParams->LookaheadSize) + { + if(QueueParams->LookaheadSizeLookaheadSize = NIC_MIN_LOOKAHEAD_SPLIT; + DEBUGP(MP_INFO, "[%p] Requested LA size (%i) for Queue %i is too small, using our minium (%i)\n", Adapter, QueueParams->LookaheadSize, QueueId, NIC_MIN_LOOKAHEAD_SPLIT); + } + else if(QueueParams->LookaheadSize>NIC_MAX_LOOKAHEAD_SPLIT) + { + Status = NDIS_STATUS_NOT_SUPPORTED; + DEBUGP(MP_ERROR, "[%p] Requested LA size (%i) for Queue %i is above our maximum (%i), the request has failed.\n", Adapter, QueueParams->LookaheadSize, QueueId, NIC_MAX_LOOKAHEAD_SPLIT); + break; + } + else + { + QueueInfo->LookaheadSize = QueueParams->LookaheadSize; + } + } + + + QueueInfo->NdisFlags = QueueParams->Flags & (~NDIS_RECEIVE_QUEUE_PARAMETERS_CHANGE_MASK); + QueueInfo->ProcessorAffinity = QueueParams->ProcessorAffinity; + } + + // + // Please note that for PCI hardware, rather than maintaining DPCs with differing affinities for receives as we do in this + // software implementation, the affinity information would instead be applied to the MSI-X configuration. + // + + if(QueueParams) + { + // + // Set processor affininty for receive DPC + // + PROCESSOR_NUMBER ProcNumber = {0}; + ProcNumber.Group = QueueInfo->ProcessorAffinity.Group; + ProcNumber.Number = RtlFindLeastSignificantBit((ULONGLONG)QueueInfo->ProcessorAffinity.Mask); + ASSERT(ProcNumber.Number != -1); + if(ProcNumber.Number == -1) + { + // + // Queue has no affinity bits set. + // + Status = NDIS_STATUS_INVALID_PARAMETER; + DEBUGP(MP_ERROR, + "[%p] Queue %i has no affinity bits set.\n", + Adapter, + QueueId); + + break; + } + QueueInfo->ReceiveDpc = NICAllocReceiveDpc(Adapter, ProcNumber.Number, ProcNumber.Group, QueueId); + if(!QueueInfo->ReceiveDpc) + { + // + // Could not allocate DPC + // + Status = NDIS_STATUS_RESOURCES; + DEBUGP(MP_ERROR, "[%p] Could not allocate receive DPC for Queue %i\n", Adapter, QueueId); + break; + } + } + else + { + // + // No configuration specified, use this is the default queue so use default DPC + // + QueueInfo->ReceiveDpc = NICGetDefaultReceiveDpc(Adapter, QueueId); + } + + // + // Initialize the receive block for the specified queue + // + Status = NICInitializeReceiveBlock(Adapter, QueueId); + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] Could not initialize receive block for Queue %i\n", Adapter, QueueId); + break; + } + + // + // Mark the Queue as initialized. It will not be used for receives until it is marked as completed. + // + QUEUE_SET_FLAG(QueueInfo, fMPAQI_INITIALIZED); + + } while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- InitializeAdapterQueueInfo Status 0x%08x\n", Adapter, Status); + + return Status; + +} + +NDIS_STATUS +AllocateRxQueue( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_QUEUE_PARAMETERS QueueParams +) +/*++ +Routine Description: + + This routine will allocate the corresponding queue in the passed in QueueParams, + and persist any relevant configuration from the request. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Adapter - Pointer to our adapter + QueueParams - Pointer to the queue allocation parameters + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PMP_ADAPTER_VMQ_DATA VMQData = &Adapter->VMQData; + PMP_ADAPTER_QUEUE QueueInfo = &VMQData->RxQueues[QueueParams->QueueId]; + LOCK_STATE_EX LockState; + BOOLEAN ReleaseLock = FALSE; + + DEBUGP(MP_TRACE, "[%p] ---> AllocateRxQueue\n", Adapter); + + do + { + // + // Verify queue is within the range of supported queues, and that it has not already + // been allocated + // + if(QueueParams->QueueId>=NIC_SUPPORTED_NUM_QUEUES) + { + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + // + // Synchronize assigning of allocated memory + // + NdisAcquireRWLockWrite(VMQData->RxQueues[QueueParams->QueueId].QueueLock, &LockState, 0); + ReleaseLock = TRUE; + + if(QUEUE_INITIALIZED(QueueInfo)) + { + // + // For this sample, changes are not supported. To support changes check whether + // Flags&NDIS_RECEIVE_QUEUE_PARAMETERS_CHANGE_MASK != 0. The + // NDIS_RECEIVE_QUEUE_PARAMETERS_*_CHANGED flags can be checked against + // to determine which fields are being updated, and apply those changes. + // + DEBUGP(MP_ERROR, "[%p] Queue %i already allocated, failing request.\n", Adapter, QueueParams->QueueId); + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + else if(QUEUE_FREEING(QueueInfo)) + { + DEBUGP(MP_ERROR, "[%p] Queue %i is in the process of being freed (RefCount of %i).\n", Adapter, QueueParams->QueueId, RECEIVE_BLOCK_REFERENCE_COUNT(Adapter, QueueParams->QueueId)); + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + // + // Initialize the queue information + // + Status = InitializeAdapterQueueInfo(Adapter, QueueParams, QueueInfo); + + } while(FALSE); + + if(ReleaseLock) + { + NdisReleaseRWLock(VMQData->RxQueues[QueueParams->QueueId].QueueLock, &LockState); + } + + DEBUGP(MP_TRACE, "[%p] <--- AllocateRxQueue Status 0x%08x\n", Adapter, Status); + + return Status; +} + +VOID +FreeQueueReceiveSharedMemory( + _In_ PMP_ADAPTER Adapter, + ULONG EntryCount, + _In_reads_bytes_(EntryCount*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK)) PUCHAR MemoryBlock, + PLIST_ENTRY AllocationList) +/*++ +Routine Description: + + This routine frees up the passed in receive shared and tracking memory. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Adapter - Pointer to our adapter + EntryCount - Number of memory block entries + MemoryBlock - MP_ADAPTER_SHARED_MEMORY_BLOCK memory allocated to track shared memory + AllocationList - List of PMP_ADAPTER_SHARED_MEMORY allocations that build up the total amount of shared memory available for receive + +Return Value: + + VOID + +--*/ +{ + ULONG BlockIndex; + PLIST_ENTRY CurrentAllocation; + + DEBUGP(MP_TRACE, "[%p] ---> FreeQueueReceiveSharedMemory.\n", Adapter); + + if(MemoryBlock) + { + // + // Free the MDLs for the shared memory blocks + // + for(BlockIndex = 0; BlockIndex < EntryCount; ++BlockIndex) + { + PMP_ADAPTER_SHARED_MEMORY_BLOCK SMBlock = ((PMP_ADAPTER_SHARED_MEMORY_BLOCK)MemoryBlock)+BlockIndex; +#pragma prefast(suppress: 6001, "PREfast doees not believe the MDL is initialized.") + NdisFreeMdl(SMBlock->Mdl); + } + + // + // Free the tracking buffers + // + NdisFreeMemory(MemoryBlock, EntryCount*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK), 0); + } + + // + // Free the shared memory + // + for(CurrentAllocation = RemoveTailList(AllocationList); + CurrentAllocation != AllocationList; + CurrentAllocation = RemoveTailList(AllocationList)) + { + PMP_ADAPTER_SHARED_MEMORY CurrentSharedMemory = CONTAINING_RECORD(CurrentAllocation, MP_ADAPTER_SHARED_MEMORY, Entry); + NdisFreeSharedMemory(Adapter->AdapterHandle, CurrentSharedMemory->AllocationHandle); + NdisFreeMemory(CurrentSharedMemory, sizeof(MP_ADAPTER_SHARED_MEMORY), 0); + } + + DEBUGP(MP_TRACE, "[%p] <--- FreeQueueReceiveSharedMemory.\n", Adapter); + +} + +VOID + FreeRxQueueReceiveMemory( + _Inout_ PMP_ADAPTER Adapter, + PMP_ADAPTER_QUEUE QueueInfo) +/*++ +Routine Description: + + This routine frees up any receive shared memory and tracking memory allocated for this queue. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Adapter - Pointer to our adapter + QueueInfo - Pointer to shared memory owner queue + +Return Value: + + VOID + +--*/ +{ + + DEBUGP(MP_TRACE, "[%p] ---> FreeRxQueueReceiveMemory. QueueId: %i\n", Adapter, QueueInfo->QueueId); + + // + // Free lookahead memory and clear values + // + FreeQueueReceiveSharedMemory( + Adapter, + QueueInfo->NumPostLookaheadBlocks, + QueueInfo->PostLookaheadBlocks, + &QueueInfo->PostLookaheadSharedMemoryList); + + QueueInfo->PostLookaheadBlocks = NULL; + QueueInfo->NumPostLookaheadBlocks = 0; + + // + // Free postlookahead memory and clear values + // + FreeQueueReceiveSharedMemory( + Adapter, + QueueInfo->NumLookaheadBlocks, + QueueInfo->LookaheadBlocks, + &QueueInfo->LookaheadSharedMemoryList); + + QueueInfo->LookaheadBlocks = NULL; + QueueInfo->NumLookaheadBlocks = 0; + + DEBUGP(MP_TRACE, "[%p] <--- FreeRxQueueReceiveMemory.\n", Adapter); + +} + +NDIS_STATUS + AssociateSharedMemoryAndBlocks( + _Inout_ struct _MP_ADAPTER *Adapter, + ULONG EntryCount, + ULONG EntrySize, + ULONG OffsetSize, + _In_ PLIST_ENTRY AllocationList, + _In_reads_bytes_(EntryCount*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK)) PUCHAR MemoryBlock) +/*++ +Routine Description: + + This routine populates each MP_ADAPTER_SHARED_MEMORY_BLOCK entry with its corresponding shared memory allocation. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Adapter - Pointer to our adapter + EntryCount - Number of memory block entries + EntrySize - Unaligned size of each shared memory entry (the actual size is aligned up to a memory aligned size) + OffsetSize - If this is a postlookahead buffer, this is the offset from the start of the buffer that must be left for lookahead + AllocationList - List of PMP_ADAPTER_SHARED_MEMORY allocations that build up the total amount of shared memory available for receive + MemoryBlock - MP_ADAPTER_SHARED_MEMORY_BLOCK memory allocated to track shared memory + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + DEBUGP(MP_TRACE, "[%p] ---> AssociateSharedMemoryAndBlocks.\n", Adapter); + + do + { + ULONG BlockIndex, AllocationIndex, AlignedEntrySize = ALIGN_UP_BY(EntrySize, NdisGetSharedDataAlignment()); + PLIST_ENTRY CurrentAllocation = AllocationList->Flink; + PMP_ADAPTER_SHARED_MEMORY CurrentSharedMemory; + + // + // We should have at least one allocation + // + ASSERT(CurrentAllocation != AllocationList); + + CurrentSharedMemory = CONTAINING_RECORD(CurrentAllocation, MP_ADAPTER_SHARED_MEMORY, Entry); + + // + // Populate the shared memory tracking blocks with the necessary information (MDL, NET_BUFFER_SHARED_MEM macros) + // + for(BlockIndex = 0, AllocationIndex = 0; BlockIndex < EntryCount; ++BlockIndex, ++AllocationIndex) + { + PMP_ADAPTER_SHARED_MEMORY_BLOCK CurrentBlock = (PMP_ADAPTER_SHARED_MEMORY_BLOCK)((MemoryBlock) + (BlockIndex*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK))); + + // + // Find the next free shared memory buffer + // + if(AllocationIndex >= CurrentSharedMemory->NumberOfEntries) + { + // + // We've exhausted the current shared memory allocation, need to index off of the next one + // + CurrentAllocation = CurrentAllocation->Flink; + ASSERT(CurrentAllocation != AllocationList); + AllocationIndex = 0; + CurrentSharedMemory = CONTAINING_RECORD(CurrentAllocation, MP_ADAPTER_SHARED_MEMORY, Entry); + } + + CurrentBlock->Buffer = CurrentSharedMemory->Buffer+(AllocationIndex*AlignedEntrySize); + // + // Sanity check that the pointer is cache aligned + // + ASSERT(((ULONG_PTR)CurrentBlock->Buffer)%NdisGetSharedDataAlignment() == 0); + + // + // Allocate the MDL + // + CurrentBlock->Mdl = NdisAllocateMdl(Adapter->AdapterHandle, + CurrentBlock->Buffer + OffsetSize, + EntrySize - OffsetSize); + if(!CurrentBlock->Mdl) + { + DEBUGP(MP_ERROR, "[%p] Failed to allocate shared memory MDL.\n", Adapter); + Status = NDIS_STATUS_RESOURCES; + break; + } + + // + // Populate the NET_BUFFER_SHARED_MEMORY information + // + NET_BUFFER_SHARED_MEM_HANDLE(&CurrentBlock->BufferSharedMemoryData) = CurrentSharedMemory->MemoryHandle; + NET_BUFFER_SHARED_MEM_LENGTH(&CurrentBlock->BufferSharedMemoryData) = EntrySize - OffsetSize; + NET_BUFFER_SHARED_MEM_OFFSET(&CurrentBlock->BufferSharedMemoryData) = (ULONG)((CurrentBlock->Buffer + OffsetSize) - CurrentSharedMemory->Buffer); + + // + // For now clear next segment field. If lookahead and postlookahead are allocated, the segment and MDLs are linked once allocation is complete + // + NET_BUFFER_SHARED_MEM_NEXT_SEGMENT(&CurrentBlock->BufferSharedMemoryData) = NULL; + } + } while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- AssociateSharedMemoryAndBlocks. Status=0x%x\n", Adapter, Status); + + return Status; +} + +VOID +ChainMemoryBlocks( + ULONG EntryCount, + _In_reads_bytes_(EntryCount*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK)) PUCHAR LookaheadBlock, + _In_reads_bytes_(EntryCount*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK))PUCHAR PostLookaheadBlock) +/*++ +Routine Description: + + This routine steps through each entry in the lookahead and postlookahead memory blocks and sets up their + relationship by linking pointing the lookahead's MDL and NET_BUFFER_SHARED_MEM next fields to the postlookahead. + +Arguments: + + EntryCount - Number of entris to step through in the memory block + LookaheadBlock - Lookahead tracking memory block + PostLookaheadBlock - Postlookahead tracking memory block + +Return Value: + + VOID + +--*/ +{ + ULONG BlockIndex; + ASSERT(LookaheadBlock); + for(BlockIndex = 0; BlockIndex < EntryCount; ++BlockIndex) + { + PMP_ADAPTER_SHARED_MEMORY_BLOCK CurrentLookaheadBlock = (PMP_ADAPTER_SHARED_MEMORY_BLOCK)((LookaheadBlock) + (BlockIndex*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK))); + PMP_ADAPTER_SHARED_MEMORY_BLOCK CurrentPostLookaheadBlock = (PMP_ADAPTER_SHARED_MEMORY_BLOCK)((PostLookaheadBlock) + (BlockIndex*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK))); + + // + // link MDLs + // + CurrentLookaheadBlock->Mdl->Next = CurrentPostLookaheadBlock->Mdl; + + // + // link shared memory info so that lookahead points to the postlookahead block + // + NET_BUFFER_SHARED_MEM_NEXT_SEGMENT(&CurrentLookaheadBlock->BufferSharedMemoryData) = &CurrentPostLookaheadBlock->BufferSharedMemoryData; + } +} + +_Success_(return == NDIS_STATUS_SUCCESS) +NDIS_STATUS + AllocateSharedMemory( + _In_ PMP_ADAPTER Adapter, + NDIS_RECEIVE_QUEUE_ID QueueId, + NDIS_SHARED_MEMORY_USAGE Usage, + ULONG BlockSize, + _Out_ PNDIS_HANDLE AllocationHandle, + _Out_ PNDIS_HANDLE MemoryHandle, + _Outptr_result_bytebuffer_(BlockSize) PUCHAR *SharedBuffer + ) +/*++ +Routine Description: + + This routine allocates shared memory for the specified QueueId (the default queue is not valid here, + since no shared memory is used). This should be called through AllocateRxSharedMemory, to get failure recovery. + + Runs at IRQL = PASSIVE_LEVEL. + +Arguments: + + Adapter - Pointer to our adapter + QueueId - QueueId owning the shared memory + Usage - Usage of the shared memory (lookahead, postlookahead, receive) + BlockSize - Size of the block to allocate + AllocationHandle - Receives the Allocation handle returned from NdisAllocateSharedMemory + MemoryHandle - Receives the Memory handle returned from NdisAllocateSharedMemory + SharedBuffer - Receives the allocated shared memory + +Return Value: + + NDIS_STATUS + +--*/ +{ + + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NDIS_SHARED_MEMORY_PARAMETERS MemoryParams; + PSCATTER_GATHER_LIST SGListBuffer = NULL; + ULONG SGListSize, SGListNumElements; + + DEBUGP(MP_TRACE, "[%p] ---> AllocateSharedMemory\n", Adapter); + + *MemoryHandle = NULL; + *SharedBuffer = NULL; + + do + { + // + // Build the scatter gather list + // + + // + // Calculate the number of pages that would be necessary for the specified block size. + // + SGListNumElements = BYTES_TO_PAGES(BlockSize); + // + // Allocate gather list elements to hold information for each of these pages. + // + SGListSize = sizeof(SCATTER_GATHER_LIST) + sizeof(SCATTER_GATHER_ELEMENT) * SGListNumElements; + SGListBuffer = NdisAllocateMemoryWithTagPriority(NdisDriverHandle, + SGListSize, + NIC_TAG_QUEUE_SG_LIST, + NormalPoolPriority); + if(SGListBuffer == NULL) + { + DEBUGP(MP_ERROR, "Failed to allocate scatter gather memory.\n"); + Status = NDIS_STATUS_RESOURCES; + break; + } + SGListBuffer->NumberOfElements = SGListNumElements; + + // + // Prepare the shared memory parameters to be passed in to the NDIS allocation function + // + NdisZeroMemory(&MemoryParams, sizeof(NDIS_SHARED_MEMORY_PARAMETERS)); + MemoryParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + MemoryParams.Header.Revision = NDIS_SHARED_MEMORY_PARAMETERS_REVISION_1; + MemoryParams.Header.Size = NDIS_SIZEOF_SHARED_MEMORY_PARAMETERS_REVISION_1; + + // + // Memory characteristics + // + MemoryParams.Length = BlockSize; + MemoryParams.QueueId = QueueId; + MemoryParams.Usage = Usage; + + // + // Scatter gather buffer + // + MemoryParams.SGListBufferLength = SGListSize; + MemoryParams.SGListBuffer = SGListBuffer; + + // + // Allocate the memory + // + Status = NdisAllocateSharedMemory(Adapter->AdapterHandle, + &MemoryParams, + AllocationHandle); + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] NdisAllocateSharedMemory failed Status 0x%x.\n", Adapter, Status); + break; + } + + // + // Set the buffer to return + // + *SharedBuffer = MemoryParams.VirtualAddress; + *MemoryHandle = MemoryParams.SharedMemoryHandle; + + NdisZeroMemory(*SharedBuffer, BlockSize); + + } while(FALSE); + + if(SGListBuffer) + { + // + // This virtual miniport does not actually do DMA, so no need to preserve the SG information + // + NdisFreeMemoryWithTagPriority(NdisDriverHandle, SGListBuffer, NIC_TAG_QUEUE_SG_LIST); + } + + DEBUGP(MP_TRACE, "[%p] <--- AllocateSharedMemory. Status=0x%x\n", Adapter, Status); + + return Status; +} + +NDIS_STATUS + AllocateRxSharedMemory( + _In_ PMP_ADAPTER Adapter, + NDIS_RECEIVE_QUEUE_ID QueueId, + NDIS_SHARED_MEMORY_USAGE Usage, + _Inout_ ULONG *EntryCount, + ULONG EntrySize, + ULONG OffsetSize, + _In_ PLIST_ENTRY AllocationList, + _Outptr_result_bytebuffer_(*EntryCount*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK)) PUCHAR *MemoryBlock + ) +/*++ +Routine Description: + + This routine wraps the call to AllocateSharedMemory providing failover if the allocation fails. If the original + requested number of entries fails to allocate, we try to allocate the same buffer size in smaller chunks. + + Runs at IRQL APC_PASSIVE + +Arguments: + + Adapter - Pointer to our adapter + QueueId - QueueId owning the shared memory + Usage - Usage of the shared memory (lookahead, postlookahead, receive) + EntryCount - Number of Entries worth of shared memory to allocate + EntrySize - Size of each entry + AllocationList - LIST_ENTRY to receive all blocks allocated (MP_ADAPTER_SHARED_MEMORY) + MemoryBlock - Receives allocated and populated MP_ADAPTER_SHARED_MEMORY_BLOCK buffer with bookkeeping data on shared memory + +Return Value: + + NDIS_STATUS + + On failure, caller needs to call FreeRxQueueReceiveMemory to free any allocations made during the function + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + DEBUGP(MP_TRACE, "[%p] ---> AllocateRxSharedMemory.\n", Adapter); + + do + { + ULONG AllocatedEntryCount = 0, CurAllocationTarget = *EntryCount; + // + // Make sure the entry size results in cache aligned segments + // + ULONG AlignedEntrySize = ALIGN_UP_BY(EntrySize, NdisGetSharedDataAlignment()); + PMP_ADAPTER_SHARED_MEMORY CurMemory = NULL; + + while(AllocatedEntryCount < *EntryCount) + { + // + // Entry size will be cache aligned, allocate enough memory so that we can be sure the entries start at + // a cache boundary + // + ULONG TotalAllocationSize = (CurAllocationTarget*AlignedEntrySize) + NdisGetSharedDataAlignment(); + + if(!CurMemory) + { + // + // Allocate MP_ADAPTER_SHARED_MEMORY we use to track this particular allocation + // + CurMemory = NdisAllocateMemoryWithTagPriority(NdisDriverHandle, + sizeof(MP_ADAPTER_SHARED_MEMORY), + NIC_TAG_QUEUE_SHARED_MEM, + NormalPoolPriority); + if(!CurMemory) + { + break; + } + NdisZeroMemory(CurMemory, sizeof(MP_ADAPTER_SHARED_MEMORY)); + } + + // + // Attempt to allocate shared memory block of (EntryCount * EntrySize) size + // + Status = AllocateSharedMemory(Adapter, + QueueId, + Usage, + TotalAllocationSize, + &CurMemory->AllocationHandle, + &CurMemory->MemoryHandle, + &CurMemory->Buffer); + + if(Status != NDIS_STATUS_SUCCESS) + { + // + // Allocation failed, lets try multiple smaller allocation + // + CurAllocationTarget /= 2; + if(CurAllocationTarget >= NIC_MIN_RECV_ENTRY_ALLOCATION_COUNT) + { + DEBUGP(MP_ERROR, "[%p] AllocateSharedMemory failed, will attempt to allocate %i units of memory.\n", Adapter, CurAllocationTarget); + } + else + { + DEBUGP(MP_ERROR, "[%p] AllocateSharedMemory failed, reached minimum allocation size. Failing allocation. \n", Adapter); + NdisFreeMemory(CurMemory, sizeof(MP_ADAPTER_SHARED_MEMORY), 0); + CurMemory = NULL; + break; + } + } + else + { + // + // Move base buffer pointer up to a cache aligned boundary + // + CurMemory->Buffer = ALIGN_UP_POINTER_BY(CurMemory->Buffer, NdisGetSharedDataAlignment()); + + // + // Store away successful allocation + // + CurMemory->EntrySize = AlignedEntrySize; + CurMemory->NumberOfEntries = CurAllocationTarget; + AllocatedEntryCount += CurAllocationTarget; + InsertTailList(AllocationList, &CurMemory->Entry); + CurMemory = NULL; + } + } + + // + // Verify that we were at least able to allocate up to our minimum number of receive blocks. If not, then we fail the + // allocation altogether. + // + if(AllocatedEntryCountNbl); + PMP_ADAPTER_SHARED_MEMORY_BLOCK CurrentPostLookaheadBlock = (PMP_ADAPTER_SHARED_MEMORY_BLOCK)((PostLookaheadBlock) + (BlockIndex*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK))); + Rcb->Data = CurrentPostLookaheadBlock; + if(LookaheadBlock) + { + PMP_ADAPTER_SHARED_MEMORY_BLOCK CurrentLookaheadBlock = (PMP_ADAPTER_SHARED_MEMORY_BLOCK)((LookaheadBlock) + (BlockIndex*sizeof(MP_ADAPTER_SHARED_MEMORY_BLOCK))); + Rcb->LookaheadData = CurrentLookaheadBlock; + // + // Set the shared memory and MDL information + // + NET_BUFFER_FIRST_SHARED_MEM_INFO(NetBuffer) = &CurrentLookaheadBlock->BufferSharedMemoryData; + NET_BUFFER_FIRST_MDL(NetBuffer) = CurrentLookaheadBlock->Mdl; + } + else + { + // + // Set the shared memory and MDL information + // + NET_BUFFER_FIRST_SHARED_MEM_INFO(NetBuffer) = &CurrentPostLookaheadBlock->BufferSharedMemoryData; + NET_BUFFER_FIRST_MDL(NetBuffer) = CurrentPostLookaheadBlock->Mdl; + } + + // + // Set the remaining NBL fields + // + NET_BUFFER_DATA_OFFSET(NetBuffer) = 0; + NET_BUFFER_CURRENT_MDL(NetBuffer) = NET_BUFFER_FIRST_MDL(NetBuffer); + NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer) = 0; + NET_BUFFER_LIST_RECEIVE_QUEUE_ID(Rcb->Nbl) = (USHORT)QueueId; + NET_BUFFER_LIST_RECEIVE_FILTER_ID(Rcb->Nbl) = 0; + } + +} + +NDIS_STATUS + AllocateRxQueueReceiveMemory( + _Inout_ struct _MP_ADAPTER *Adapter, + PMP_ADAPTER_QUEUE QueueInfo) +/*++ +Routine Description: + + This routine allocates the necessary receive memory for the specified Queue. + + Runs at IRQL APC_PASSIVE + +Arguments: + + Adapter - Pointer to our adapter + Queue - Queue to allocate + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + do + { + NDIS_SHARED_MEMORY_USAGE postMemoryUsage = NdisSharedMemoryUsageReceive; + + // + // Queue should already be initialized + // + if(!QUEUE_INITIALIZED(QueueInfo)) + { + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + + if(QueueInfo->NumSuggestedReceiveBuffers) + { + // + // If the number of receive buffers has been passed down for the queue, then we will allocate that suggested amount (as long as it's not below our minimum required amount of receive buffers), + // otherwise we use our minimum. + // + QueueInfo->NumReceiveBuffers = max(QueueInfo->NumSuggestedReceiveBuffers, NIC_MIN_BUSY_RECVS); + } + else + { + // + // No suggested receive buffer count was set for the queue, so we our preferred maximum amount + // + QueueInfo->NumReceiveBuffers = NIC_MAX_BUSY_RECVS; + } + + // + // Initialize the shared memory allocation tracking lists + // + InitializeListHead(&QueueInfo->LookaheadSharedMemoryList); + InitializeListHead(&QueueInfo->PostLookaheadSharedMemoryList); + + // + // Initialize list and lock of the free Rcb list + // + NdisInitializeListHead(&QueueInfo->FreeRcbList); + NdisAllocateSpinLock(&QueueInfo->FreeRcbListLock); + + + // + // In our implementation receive memory is only necessary for non-default queues. For default queue + // we reuse the Frame copy made during send. + // + if(QueueInfo->QueueId) + { + ULONG LookaheadSize=0, PostLookaheadSize=HW_MAX_FRAME_SIZE; + + if(LOOKAHEAD_SPLIT_REQUIRED(QueueInfo)) + { + ASSERT(QueueInfo->LookaheadSize); + LookaheadSize = QueueInfo->LookaheadSize; + } + + // + // Allocate the shared memory that will be used to receive on this non-default queue. + // + if(LookaheadSize) + { + // + // Lookahead memory + // + Status = AllocateRxSharedMemory( + Adapter, + QueueInfo->QueueId, + NdisSharedMemoryUsageReceiveLookahead, + &QueueInfo->NumReceiveBuffers, + LookaheadSize, + 0, + &QueueInfo->LookaheadSharedMemoryList, + &QueueInfo->LookaheadBlocks); + + if(Status != NDIS_STATUS_SUCCESS) + { + break; + } + + QueueInfo->NumLookaheadBlocks = QueueInfo->NumReceiveBuffers; + + // + // We're doing lookahead split, so the usage for the Postlookahead needs to be + // explicitly postlookahead. + // + postMemoryUsage = NdisSharedMemoryUsageReceivePostLookahead; + } + + // + // Allocate postlookahead memory + // + Status = AllocateRxSharedMemory( + Adapter, + QueueInfo->QueueId, + postMemoryUsage, + &QueueInfo->NumReceiveBuffers, + PostLookaheadSize, + LookaheadSize, + &QueueInfo->PostLookaheadSharedMemoryList, + &QueueInfo->PostLookaheadBlocks); + + if(Status != NDIS_STATUS_SUCCESS) + { + break; + } + + QueueInfo->NumPostLookaheadBlocks = QueueInfo->NumReceiveBuffers; + + if(LookaheadSize) + { + // + // Now that we have both lookahead and post-lookahead allocated we need to chain + // a few of their fields together + // + ChainMemoryBlocks( + QueueInfo->NumReceiveBuffers, + QueueInfo->LookaheadBlocks, + QueueInfo->PostLookaheadBlocks); + + } + + } + + // + // To finalize the allocation, we need to allocate the RCB & NBL blocks used for receive, and populated them + // with the appropriate shared memory data (if non-default queue). + // + + // + // Ready to allocate the RCB blocks + // + Status = NICAllocRCBData( + Adapter, + QueueInfo->NumReceiveBuffers, + &QueueInfo->RcbMemoryBlock, + &QueueInfo->FreeRcbList, + &QueueInfo->FreeRcbListLock, + &QueueInfo->RecvNblPoolHandle + ); + + if(QueueInfo->QueueId && Status == NDIS_STATUS_SUCCESS) + { + // + // Populate the RCB NBLs with the queue's shared memory. + // + AssociateSharedMemoryAndRCBs( + QueueInfo->QueueId, + QueueInfo->LookaheadBlocks, + QueueInfo->PostLookaheadBlocks, + QueueInfo->RcbMemoryBlock, + QueueInfo->NumReceiveBuffers); + } + + // + // At this point, all that is left to do on receive is for the shared memory to get populated with real frame data (e.g. DMA), and set the frame length. + // This avoids any expensive allocations on receive. + // + + }while(FALSE); + + if(Status != NDIS_STATUS_SUCCESS && QUEUE_INITIALIZED(QueueInfo)) + { + // + // Cleanup any left over allocated receive resources from this function + // + FreeRxQueueReceiveMemory(Adapter, QueueInfo); + } + + DEBUGP(MP_TRACE, "[%p] <--- AllocateRxQueueReceiveMemory. Status=0x%x\n", Adapter, Status); + + return Status; +} + + +NDIS_STATUS +CompleteAllocationRxQueue( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_QUEUE_ALLOCATION_COMPLETE_ARRAY CompleteArray + ) +/*++ +Routine Description: + + This routine will complete the process of queue allocation, allocating shared mmemory and + flagging queues as complete. Once a Queue is complete, receive indications can begin on it. + + Runs at IRQL APC_PASSIVE + +Arguments: + + Adapter - Pointer to our Adapter + CompleteArray - Pointer to the complete allocation parameters + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PMP_ADAPTER_VMQ_DATA VMQData = &Adapter->VMQData; + PNDIS_RECEIVE_QUEUE_ALLOCATION_COMPLETE_PARAMETERS CompleteParamArray = (PNDIS_RECEIVE_QUEUE_ALLOCATION_COMPLETE_PARAMETERS)((PUCHAR)CompleteArray + CompleteArray->FirstElementOffset); + ULONG index; + LOCK_STATE_EX LockState; + + DEBUGP(MP_TRACE, "[%p] ---> CompleteAllocationRxQueue\n", Adapter); + + // + // Iterate through each complete entry in the parameter array + // + for (index=0; indexNumElements; index++) + { + PMP_ADAPTER_QUEUE CurrentQueue; + + if(CompleteParamArray[index].QueueId >= NIC_SUPPORTED_NUM_QUEUES) + { + // + // Queue out of range + // + CompleteParamArray[index].CompletionStatus = NDIS_STATUS_INVALID_PARAMETER; + continue; + } + + // + // Verify that the queue is initialized, and not already being completed (or complete) + // + + // + // Synchronize access to queue status + // + CurrentQueue = &VMQData->RxQueues[CompleteParamArray[index].QueueId]; + NdisAcquireRWLockWrite(CurrentQueue->QueueLock, &LockState, 0); + if(!QUEUE_INITIALIZED(CurrentQueue) || + QUEUE_COMPLETING(CurrentQueue) || + QUEUE_FREEING(CurrentQueue)) + { + // + // Queue should have already been initialized, and should not be already completed or freed. + // + DEBUGP(MP_ERROR, "[%p] Queue %i does not have the correct status for completion (QueueInfoFlags: 0x%x).\n", Adapter, CompleteParamArray[index].QueueId, CurrentQueue->QueueInfoFlags); + CompleteParamArray[index].CompletionStatus = NDIS_STATUS_INVALID_PARAMETER; + NdisReleaseRWLock(CurrentQueue->QueueLock, &LockState); + continue; + } + // + // Mark that completion has started for the queue + // + QUEUE_SET_FLAG(CurrentQueue, fMPAQI_COMPLETION_STARTED); + NdisReleaseRWLock(CurrentQueue->QueueLock, &LockState); + + Status = AllocateRxQueueReceiveMemory(Adapter, CurrentQueue); + CompleteParamArray[index].CompletionStatus = Status; + if(Status != NDIS_STATUS_SUCCESS) + { + DEBUGP(MP_ERROR, "[%p] Failed while allocating receive memory for Queue %i\n", Adapter, CompleteParamArray[index].QueueId); + QUEUE_CLEAR_FLAG(CurrentQueue,fMPAQI_COMPLETION_STARTED); + continue; + } + + // + // Completion done. Update queue status in a synchronized block. + // + NdisAcquireRWLockWrite(CurrentQueue->QueueLock, &LockState, 0); + QUEUE_SET_FLAG(CurrentQueue,fMPAQI_COMPLETION_FINISHED); + NdisReleaseRWLock(CurrentQueue->QueueLock, &LockState); + } + + DEBUGP(MP_TRACE, "[%p] <--- CompleteAllocationRxQueue Status 0x%08x\n", Adapter, Status); + + return Status; +} + +NDIS_STATUS +UpdateRxQueue( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_QUEUE_PARAMETERS QueueParams +) +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + NDIS_RECEIVE_QUEUE_ID QueueId = QueueParams->QueueId; + PMP_ADAPTER_QUEUE QueueInfo; + + DEBUGP(MP_TRACE, "[%p] ---> UpdateRxQueue\n", Adapter); + + do + { + if(QueueId >= NIC_SUPPORTED_NUM_QUEUES) + { + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + + } + + QueueInfo = &Adapter->VMQData.RxQueues[QueueId]; + if(QueueParams->Flags&NDIS_RECEIVE_QUEUE_PARAMETERS_CHANGE_MASK) + { + if (QueueParams->Flags & NDIS_RECEIVE_QUEUE_PARAMETERS_PROCESSOR_AFFINITY_CHANGED) + { + // + // Change the owner DPC + // + PMP_ADAPTER_RECEIVE_DPC NewDpc; + PROCESSOR_NUMBER ProcNumber = {0}; + + if(!ValidGroupAffinity(&QueueParams->ProcessorAffinity)) + { + DEBUGP(MP_ERROR, "[%p] Requested group affinity failed validation\n", Adapter); + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + ProcNumber.Group = QueueParams->ProcessorAffinity.Group; + ProcNumber.Number = RtlFindLeastSignificantBit((ULONGLONG)QueueParams->ProcessorAffinity.Mask); + ASSERT(ProcNumber.Number != -1); + + DEBUGP(MP_INFO, "[%p] Updating Queue Affinity Group: 0x%x, Number: 0x%x\n", Adapter, ProcNumber.Group, ProcNumber.Number); + + // + // Get the new DPC with different affinity (if one already exists, will reuse it) + // + NewDpc = NICAllocReceiveDpc(Adapter, ProcNumber.Number, ProcNumber.Group, QueueId); + if(!NewDpc) + { + // + // Could not allocate DPC + // + Status = NDIS_STATUS_RESOURCES; + DEBUGP(MP_ERROR, "[%p] Could not allocate receive DPC for Queue %i, will keep original DPC\n", Adapter, QueueId); + break; + } + + if(NewDpc != QueueInfo->ReceiveDpc) + { + // + // This is a new DPC for the queue, update the existing owner DPC and replace + // + NICReceiveDpcRemoveOwnership(QueueInfo->ReceiveDpc, QueueId); + + // + // Store new DPC + // + QueueInfo->ReceiveDpc = NewDpc; + + // + // Check whether there are any pending receives on the queue, if there are, then schedule the + // new DPC to make sure they are not lost + // + if(!IsListEmpty(&Adapter->ReceiveBlock[QueueId].ReceiveList)) + { + DEBUGP(MP_INFO, "[%p] Receive Block %i: Receives were pending, queued new DPC.\n", Adapter, QueueId); + KeInsertQueueDpc(&QueueInfo->ReceiveDpc->Dpc, QueueInfo->ReceiveDpc, NULL); + } + } + + } + + if (QueueParams->Flags & NDIS_RECEIVE_QUEUE_PARAMETERS_FLAGS_CHANGED) + { + ULONG Flags = QueueParams->Flags & ~NDIS_RECEIVE_QUEUE_PARAMETERS_CHANGE_MASK; + DEBUGP(MP_INFO, "[%p] Updating Queue flags: 0x%x\n", Adapter, Flags); + if(Flags&NDIS_RECEIVE_QUEUE_PARAMETERS_LOOKAHEAD_SPLIT_REQUIRED + && !LOOKAHEAD_SPLIT_REQUIRED(QueueInfo) + && QUEUE_COMPLETE(QueueInfo)) + { + // + // Our implementation does not support enabling lookahead after the queue is complete + // since our complete handler would not have allocated the lookahead memory. + // + DEBUGP(MP_ERROR, "[%p] Queue was already completed without lookahead split. Did not update flags. \n", Adapter); + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + else + { + QueueInfo->NdisFlags = Flags; + } + } + + if (QueueParams->Flags & NDIS_RECEIVE_QUEUE_PARAMETERS_SUGGESTED_RECV_BUFFER_NUMBERS_CHANGED) + { + DEBUGP(MP_INFO, "[%p] Updating Queue number of suggested receive buffers: %i\n", Adapter, QueueParams->NumSuggestedReceiveBuffers); + QueueInfo->NumSuggestedReceiveBuffers = QueueParams->NumSuggestedReceiveBuffers; + } + } + }while(FALSE); + + + DEBUGP(MP_TRACE, "[%p] <--- UpdateRxQueue Status 0x%08x\n", Adapter, Status); + return Status; +} + + +NDIS_STATUS +FreeAdapterQueueInfo( + _In_ PMP_ADAPTER Adapter, + USHORT QueueId, + _In_opt_ PNDIS_OID_REQUEST NdisSetRequest) +/*++ +Routine Description: + + This routine will clean up the memory for the specified queue, and reset it to uninitialized. If the + queue still has active references on it (e.g. - still in use), the function does not clean up the queue + and instead returns NDIS_STATUS_PENDING. Callers should handle attempting the free at a later time. + + Runs at IRQL = PASSIVE_LEVEL + +Arguments: + + Adapter - Pointer to our adapter + QueueId - Queue to free + NdisSetRequest - The OID for the free request (NULL if the call is not due to an OID request) + +Return Value: + + Void + +--*/ +{ + LOCK_STATE_EX QueueLockState; + PMP_ADAPTER_QUEUE QueueInfo = &Adapter->VMQData.RxQueues[QueueId]; + LONG ReferenceCount = 0; + BOOLEAN DmaInProgress; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + PLIST_ENTRY Entry; + + do + { + // + // Check (and possibly update) queue status in synchronized block + // + NdisAcquireRWLockWrite(QueueInfo->QueueLock, &QueueLockState, 0); + if(!QueueInfo->QueueInfoFlags) + { + // + // Queue has already been freed, no need to do anything. + // + Status = NDIS_STATUS_SUCCESS; + NdisReleaseRWLock(QueueInfo->QueueLock, &QueueLockState); + break; + } + + // + // Reset the flags to "freeing". This way we will not accumulate any more receives on the queue. + // + QUEUE_SET_FLAG(QueueInfo,fMPAQI_FREEING); + + // + // Make sure the freeing state set operation happens before the DMA in progress check + // + KeMemoryBarrier(); + + // + // Check whether the queue has a DMA in progress. This value check has to occur *after* we've updated + // the queue state to freeing. The receive code depends on this ordering of operations to avoid taking locks + // on its "DMA" path (see CopyFrameToRxQueue). + // + DmaInProgress = QUEUE_DMA_IN_PROGRESS(QueueInfo); + if(!DmaInProgress) + { + // + // There are no DMA's in progress. Update the flags of the queue to DMA stopped to avoid pending receive code + // from indicating DMA stopped state later. The indication to NDIS of this change happens outside of our synchronized + // block to avoid holding the lock for too long. + // + QUEUE_SET_FLAG(QueueInfo,fMPAQI_DMA_STOPPED); + } + + // + // Get the reference count + // + ReferenceCount = RECEIVE_BLOCK_REFERENCE_COUNT(Adapter, QueueId); + if(ReferenceCount != 0) + { + // + // Still have outstanding usage of the queue, so we must pend the free request. + // + if(NdisSetRequest) + { + // + // Store the OID so that we can complete it later + // + SetPendingRxQueueFree(Adapter, NdisSetRequest); + } + else + { + // + // The queue free should only pend on OID request. If this free call came from halt or other operation, + // the queue should have been ready to free already, so check that we have a pending request. + // + ASSERT(Adapter->PendingRequest); + } + + } + + NdisReleaseRWLock(QueueInfo->QueueLock, &QueueLockState); + + if(!DmaInProgress) + { + // + // There were no DMA's in progress when we updated that state to freeing, and no more DMA's will occur, so it's safe to indicate that queue DMA is stopped. + // Any pending receives that have already completed their copies will still be indicated. + // + IndicateRxQueue( + Adapter, + QueueId, + NdisReceiveQueueOperationalStateDmaStopped); + } + + if(ReferenceCount != 0) + { + // + // Bail out of the free path. Once the ref count reaches zero on the queue and there are no in-progress DMA's, the queue will be freed and the OID will complete. + // + DEBUGP(MP_LOUD, "[%p] Queue %i has pending references (RefCount %i), returning NDIS_STATUS_PENDING for the free request.\n", Adapter, QueueId, ReferenceCount); + Status = NDIS_STATUS_PENDING; + break; + } + + DEBUGP(MP_LOUD, "[%p] Queue %i has no outstanding references, queue memory has been freed.\n", Adapter, QueueId); + + NICReceiveDpcRemoveOwnership(QueueInfo->ReceiveDpc, QueueId); + QueueInfo->ReceiveDpc = NULL; + + // + // Free the RCB & NBL data + // + if(QueueInfo->FreeRcbList.Flink) + { + while (NULL != (Entry = NdisInterlockedRemoveHeadList( + &QueueInfo->FreeRcbList, + &QueueInfo->FreeRcbListLock))) + { + PRCB Rcb = CONTAINING_RECORD(Entry, RCB, RcbLink); + NdisFreeNetBufferList(Rcb->Nbl); + } + NdisFreeSpinLock(&QueueInfo->FreeRcbListLock); + } + + if (QueueInfo->RecvNblPoolHandle) + { + NdisFreeNetBufferListPool(QueueInfo->RecvNblPoolHandle); + QueueInfo->RecvNblPoolHandle = NULL; + } + + if (QueueInfo->RcbMemoryBlock) + { + NdisFreeMemory( + QueueInfo->RcbMemoryBlock, + sizeof(RCB)*QueueInfo->NumReceiveBuffers, + 0); + QueueInfo->RcbMemoryBlock = NULL; + } + + if(QUEUE_COMPLETE(QueueInfo)) + { + FreeRxQueueReceiveMemory( + Adapter, + QueueInfo); + } + + // + // Completed free, clear flags altogether + // + QueueInfo->QueueInfoFlags = 0; + + if(Adapter->PendingRequest) + { + PNDIS_OID_REQUEST Request = Adapter->PendingRequest; + Adapter->PendingRequest = NULL; + NdisMOidRequestComplete(Adapter->AdapterHandle, + Request, + Status); + } + + } while(FALSE); + + return Status; +} + +NDIS_STATUS +FreeRxQueue( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_QUEUE_FREE_PARAMETERS QueueFreeParams, + _In_opt_ PNDIS_OID_REQUEST NdisSetRequest + ) +/*++ +Routine Description: + + This routine will clean up the memory for the specified queue. Queue should already been indicated as DMA stopped + using IndicateRxQueue. Callers should handle attempting the free at a later time if NDIS_STATUS_PENDING is returned. + + Runs at IRQL = PASSIVE_LEVEL + +Arguments: + + Adapter - Pointer to our adapter + QueueFreeParams - Pointer to the complete allocation parameters + NdisSetRequest - The OID for the free request (NULL if the call is not due to an OID request) + +Return Value: + + NDIS_STATUS + +--*/ + +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + DEBUGP(MP_TRACE, "[%p] ---> FreeRxQueue\n", Adapter); + + // + // Verify within valid range + // + if(QueueFreeParams->QueueId>=NIC_SUPPORTED_NUM_QUEUES) + { + Status = NDIS_STATUS_INVALID_PARAMETER; + } + else + { + // + // Attempt to free the queue (pending will be returned if queue still in use) + // + Status = FreeAdapterQueueInfo(Adapter, (USHORT)QueueFreeParams->QueueId, NdisSetRequest); + } + + DEBUGP(MP_TRACE, "[%p] <--- FreeRxQueue Status 0x%08x\n", Adapter, Status); + + return Status; +} + +_Use_decl_annotations_ +VOID +FreeRxQueuesWorkItem( + PVOID WorkItemContext, + NDIS_HANDLE NdisIoWorkItemHandle + ) +/*++ +Routine Description: + + This routine retrieves the pending free request (there can only be one at a time), and frees the queue. The work item is used + when we pend a queue free OID (because the reference count is non-zero), and when the reference count is finally reduced to + zero we are not at PASSIVE_LEVEL. + + Runs at IRQL = PASSIVE_LEVEL + +Arguments: + + WorkItemContext - Context for work item (PMP_ADAPTER) + NdisIoWorkItemHandle - Work item handle + +Return Value: + + None + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PMP_ADAPTER Adapter; + PNDIS_OID_REQUEST Request; + + Adapter = MP_ADAPTER_FROM_CONTEXT(WorkItemContext); + ASSERT(Adapter != NULL); + _Analysis_assume_(Adapter != NULL); + + DEBUGP(MP_TRACE, "[%p] ---> FreeRxQueuesWorkItem\n", Adapter); + + Request = Adapter->PendingRequest; + if(Request != NULL) + { + switch(Request->DATA.SET_INFORMATION.Oid) + { + case OID_RECEIVE_FILTER_FREE_QUEUE: + Status = FreeRxQueue( + Adapter, + (PNDIS_RECEIVE_QUEUE_FREE_PARAMETERS)Request->DATA.SET_INFORMATION.InformationBuffer, + NULL //OID should already be held in pending OID member of adapter + ); + // + // Should only be called once reference count has reached zero, it should succeed + // + ASSERT(Status == NDIS_STATUS_SUCCESS); + break; + default: + ASSERT(!"Unsupported Set OID added to pending list"); + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + } + + NdisFreeIoWorkItem(NdisIoWorkItemHandle); + + DEBUGP(MP_TRACE, "[%p] <-- FreeRxQueuesWorkItem. Status=0x%x\n", Adapter, Status); +} + +VOID +FreeVMQData( + _Inout_ struct _MP_ADAPTER *Adapter) +/*++ +Routine Description: + + This routine will clean up the memory for all the queues in the passed in VMQData. It should only be called + when all pending receives have stopped (e.g. - miniport halt). + + Runs at IRQL = PASSIVE_LEVEL + +Arguments: + + Adapter - Pointer to our adapter + +Return Value: + + NDIS_STATUS + +--*/ +{ + USHORT index; + PMP_ADAPTER_VMQ_DATA VMQData = &Adapter->VMQData; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + for(index=0; indexRxQueues[index].QueueLock != NULL) + { + #pragma warning( suppress: 28931 ) // unused assignment of variable Status OK in Fre build + Status = FreeAdapterQueueInfo(Adapter, index, NULL); + ASSERT(Status == NDIS_STATUS_SUCCESS); + NdisFreeRWLock(VMQData->RxQueues[index].QueueLock); + } + } + +} + +VOID +IndicateRxQueue( + _In_ PMP_ADAPTER Adapter, + NDIS_RECEIVE_QUEUE_ID QueueId, + NDIS_RECEIVE_QUEUE_OPERATIONAL_STATE State) +/*++ +Routine Description: + + This routine will indicate to NDIS the state of the queue. + +Arguments: + + Adapter - Pointer to our adapter + QueueId - Queue to indicate + State - Current state of Queue + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS_INDICATION Status; + NDIS_RECEIVE_QUEUE_STATE QueueState; + + DEBUGP(MP_TRACE, "[%p] ---> IndicateRxQueue ID: %i, State: 0x%x\n", Adapter, QueueId, State); + + NdisZeroMemory(&Status, sizeof(Status)); + NdisZeroMemory(&QueueState, sizeof(QueueState)); + + QueueState.Header.Revision = NDIS_RECEIVE_QUEUE_STATE_REVISION_1; + QueueState.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + QueueState.Header.Size = NDIS_SIZEOF_NDIS_RECEIVE_QUEUE_STATE_REVISION_1; + QueueState.QueueId = QueueId; + QueueState.QueueState = State; + + Status.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION; + Status.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1; + Status.Header.Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1; + Status.StatusCode = NDIS_STATUS_RECEIVE_QUEUE_STATE; + Status.StatusBuffer = &QueueState; + Status.StatusBufferSize = QueueState.Header.Size; + + NdisMIndicateStatusEx( + Adapter->AdapterHandle, + &Status); + + DEBUGP(MP_TRACE, "[%p] <--- IndicateRxQueue\n", Adapter); + +} + +BOOLEAN +IsSupportedRxFilter( + _In_ PMP_ADAPTER Adapter, + _In_ PNDIS_RECEIVE_FILTER_PARAMETERS FilterParams, + _In_ PNDIS_RECEIVE_FILTER_FIELD_PARAMETERS FilterCriteria) +/*++ +Routine Description: + + This routine will verify whether the filter is well formed and supported by this adapter. + +Arguments: + + Adapter - Pointer to our adapter + FilterParams - Filter Parameters + FilterCriteria - Filter Field Parameters + +Return Value: + + TRUE - filter is supported + FALSE - filter is not not + +--*/ +{ + + BOOLEAN Supported = TRUE; + ULONG index; + BOOLEAN VlanFilterPresent = FALSE, VlanUntaggedOrZeroFlagPresent = FALSE; + + DEBUGP(MP_TRACE, "[%p] ---> IsSupportedRxFilter. FilterParams: QueueId: %i, FilterId: %i, Flags: 0x%x\n", + Adapter, FilterParams->QueueId, FilterParams->FilterId, FilterParams->Flags); + + // + // Check all criteria for the filter + // + for(index = 0; index < FilterParams->FieldParametersArrayNumElements && Supported; index++) + { + DEBUGP(MP_TRACE, "[%p] FilterCriteris[%i]: FrameHeader: %i, ReceiveFilterTest: %i, MacHeaderField: %i\n", + Adapter, + index, FilterCriteria[index].FrameHeader, FilterCriteria[index].ReceiveFilterTest, + FilterCriteria[index].HeaderField.MacHeaderField); + + // + // Verify frame header + // + if(FilterCriteria[index].FrameHeader != NdisFrameHeaderMac) + { + DEBUGP(MP_ERROR, "[%p] Unknown filter FrameHeader type %x\n", Adapter, FilterCriteria[index].FrameHeader); + Supported = FALSE; + break; + } + + // + // Verify that filter test type is supported + // + switch(FilterCriteria[index].ReceiveFilterTest) + { + case NdisReceiveFilterTestEqual: + Supported = TRUE; + break; + default: + Supported=FALSE; + DEBUGP(MP_ERROR, "[%p] ReceiveFilterTest 0x%x not supported.\n", Adapter, FilterCriteria[index].ReceiveFilterTest); + break; + }; + + if(!Supported) + { + break; + } + + // + // Verify header field + // + switch(FilterCriteria[index].HeaderField.MacHeaderField) + { + case NdisMacHeaderFieldDestinationAddress: + VlanUntaggedOrZeroFlagPresent = FilterCriteria->Flags&NDIS_RECEIVE_FILTER_FIELD_MAC_HEADER_VLAN_UNTAGGED_OR_ZERO; + Supported = TRUE; + break; + case NdisMacHeaderFieldVlanId: + VlanFilterPresent = TRUE; + Supported = TRUE; + break; + default: + Supported = FALSE; + DEBUGP(MP_ERROR, "[%p] MacHeaderField 0x%x not supported.\n", Adapter, FilterCriteria[index].HeaderField.MacHeaderField); + break; + }; + } + + if(Supported) + { + // + // NDIS_RECEIVE_FILTER_FIELD_MAC_HEADER_VLAN_UNTAGGED_OR_ZERO should not be set if VLAN filter is specified, + // and should be set if no VLAN filter is specified. + // + if(VlanFilterPresent == VlanUntaggedOrZeroFlagPresent) + { + // + // Both are specified, this is not correct. + // + DEBUGP(MP_ERROR, "[%p] Invalid VMQ filter configuration. NDIS_RECEIVE_FILTER_FIELD_MAC_HEADER_VLAN_UNTAGGED_OR_ZERO (%s), Vlan ID filter present (%s). Filter not accepted.\n", + Adapter, + VlanFilterPresent?"TRUE":"FALSE", + VlanUntaggedOrZeroFlagPresent?"TRUE":"FALSE"); + Supported = FALSE; + } + else if(!VLAN_FILTER_ENABLED(Adapter) && VlanFilterPresent) + { + // + // VLAN based filtering not configured on the adapter + // + DEBUGP(MP_ERROR, "[%p] Invalid VMQ filter configuration. *VMQVlanFiltering is not set, and Vlan ID filter was specified.\n", Adapter); + Supported = FALSE; + } + } + + DEBUGP(MP_TRACE, "[%p] <--- IsSupportedRxFilter %s\n", Adapter, Supported?"TRUE":"FALSE"); + return Supported; +} + +NDIS_STATUS +SetRxFilter( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_FILTER_PARAMETERS FilterParams + ) +/*++ +Routine Description: + + This routine will assign a filter to the specified queue, after verifying that the filter is valid. + +Arguments: + + Adapter - Pointer to our adapter + FilterParams - Filter data + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PMP_ADAPTER_VMQ_DATA VMQData = &Adapter->VMQData; + PNDIS_RECEIVE_FILTER_FIELD_PARAMETERS FilterCriteria = (PNDIS_RECEIVE_FILTER_FIELD_PARAMETERS)((PUCHAR)FilterParams + FilterParams->FieldParametersArrayOffset); + UINT FilterIndex = MP_ADAPTER_FILTER_INDEX(FilterParams->FilterId); + UINT CriteriaIndex; + + DEBUGP(MP_TRACE, "[%p] ---> SetRxFilter\n", Adapter); + + do + { + // + // Verify the requested queue is within supported range and is initialized + // + if(FilterParams->QueueId>=NIC_SUPPORTED_NUM_QUEUES + || + FilterIndex>=NIC_MAX_HEADER_FILTERS + || + !QUEUE_INITIALIZED(&VMQData->RxQueues[FilterParams->QueueId])) + { + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + // + // Verify that the filter itself is valid, and that it hasn't already been set + // + if(!IsSupportedRxFilter(Adapter, FilterParams, FilterCriteria) + || + VMQData->RxFilters[FilterIndex].Valid) + { + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + // + // Make sure filter data is all zero at the outset + // + NdisZeroMemory(&VMQData->RxFilters[FilterIndex], sizeof(MP_ADAPTER_FILTER)); + + // + // Copy filter information. IsSupportedRxFilter will have already verified that this filter definition + // only contains MAC and VLAN ID definitions, and equals match criteria. + // + for(CriteriaIndex = 0; CriteriaIndex < FilterParams->FieldParametersArrayNumElements; CriteriaIndex++) + { + if(FilterCriteria[CriteriaIndex].HeaderField.MacHeaderField == NdisMacHeaderFieldDestinationAddress) + { + // + // MAC field + // + memcpy(&VMQData->RxFilters[FilterIndex].MacAddress, FilterCriteria[CriteriaIndex].FieldValue.FieldByteArrayValue, NIC_MACADDR_SIZE); + DEBUGP(MP_TRACE, "[%p] Filter Destination: %02x-%02x-%02x-%02x\n", + Adapter, + VMQData->RxFilters[FilterIndex].MacAddress[0], VMQData->RxFilters[FilterIndex].MacAddress[1], + VMQData->RxFilters[FilterIndex].MacAddress[2], VMQData->RxFilters[FilterIndex].MacAddress[3]); + } + else + { + // + // Vlan ID field + // + VMQData->RxFilters[FilterIndex].VlanId = FilterCriteria[CriteriaIndex].FieldValue.FieldShortValue; + DEBUGP(MP_TRACE, "[%p] Filter VlanId: %i\n", Adapter, VMQData->RxFilters[FilterIndex].VlanId); + } + + if(FilterCriteria[CriteriaIndex].Flags&NDIS_RECEIVE_FILTER_FIELD_MAC_HEADER_VLAN_UNTAGGED_OR_ZERO) + { + VMQData->RxFilters[FilterIndex].VlanUntaggedOrZero = TRUE; + DEBUGP(MP_TRACE, "[%p] Filter VlanUntaggedOrZero is TRUE\n", Adapter); + } + } + + // + // Store the QueueId + // + VMQData->RxFilters[FilterIndex].QueueId = (USHORT)FilterParams->QueueId; + + // + // Set to valid + // + VMQData->RxFilters[FilterIndex].Valid = TRUE; + + + } while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- SetRxFilter Status 0x%08x\n", Adapter, Status); + + return Status; +} + +NDIS_STATUS +ClearRxFilter( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_FILTER_CLEAR_PARAMETERS FilterParams +) +/*++ +Routine Description: + + This routine will remove the specified filter from a particular queue. + +Arguments: + + Adapter - Pointer to our adapter + FilterParams - Parameters for freeing filter + +Return Value: + + NDIS_STATUS + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + PMP_ADAPTER_VMQ_DATA VMQData = &Adapter->VMQData; + UINT FilterIndex = MP_ADAPTER_FILTER_INDEX(FilterParams->FilterId); + + DEBUGP(MP_TRACE, "[%p] ---> ClearRxFilter. FilterParams: QueueId: %i, FilterId: %i\n", + Adapter, FilterParams->QueueId, FilterParams->FilterId); + + do + { + + // + // Make sure the filter id is within the valid range + // + if(FilterParams->QueueId>=NIC_SUPPORTED_NUM_QUEUES + || + FilterIndex>=NIC_MAX_HEADER_FILTERS) + { + DEBUGP(MP_ERROR, "[%p] Filter Clear request out of valid range. QueueId: %i, FilterId: %i\n", + Adapter, FilterParams->QueueId, FilterParams->FilterId); + Status = NDIS_STATUS_INVALID_PARAMETER; + break; + } + + if(!VMQData->RxFilters[FilterIndex].Valid) + { + // + // The filter is not valid. + // + DEBUGP(MP_ERROR, "[%p] Filter was not set. QueueId: %i, FilterId: %i\n", + Adapter, FilterParams->QueueId, FilterParams->FilterId); + Status = NDIS_STATUS_INVALID_PARAMETER; + } + else + { + // + // Reset the filter to invalid + // + VMQData->RxFilters[FilterIndex].Valid = FALSE; + } + + } while(FALSE); + + DEBUGP(MP_TRACE, "[%p] <--- ClearRxFilter Status 0x%08x\n", Adapter, Status); + + return Status; +} + +BOOLEAN +MatchRxFilter( + _In_ PMP_ADAPTER Adapter, + _In_reads_bytes_(NIC_MACADDR_SIZE) PUCHAR DestAddress, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _In_ PMP_ADAPTER_FILTER Filter) +/*++ +Routine Description: + + This routine will check whether a particular filter matches the specified destination address + and VLAN ID. + +Arguments: + + DestAddress - Destination MAC address + Nbl1QInfo - VLAN information + Filter - Filter to check against + +Return Value: + + TRUE - Data matches filter + FALSE - Data did not match filter + +--*/ +{ + BOOLEAN MatchResult; + + DEBUGP(MP_TRACE, "[%p] ---> MatchRxFilter\n", Adapter); + + + DEBUGP(MP_TRACE, "[%p] NBL Destination: %02x-%02x-%02x-%02x, Filter Destination: %02x-%02x-%02x-%02x\n", + Adapter, + DestAddress[0],DestAddress[1],DestAddress[2],DestAddress[3], + Filter->MacAddress[0], Filter->MacAddress[1], + Filter->MacAddress[2], Filter->MacAddress[3]); + + // + // Match MAC address + // + MatchResult = NIC_ADDR_EQUAL(Filter->MacAddress, DestAddress); + + + if(MatchResult) + { + if(Filter->VlanUntaggedOrZero) + { + // + // VLAN ID should be zero or untagged for match + // + DEBUGP(MP_TRACE, "[%p] Destination Matched. Making sure VLAN is untagged or zero.\n", Adapter); + if(Nbl1QInfo->Value) + { + MatchResult = (Nbl1QInfo->Value == 0 || Nbl1QInfo->TagHeader.VlanId == 0); + if(Nbl1QInfo->Value) + { + DEBUGP(MP_TRACE, "[%p] NBL Vlan ID: %i\n", Adapter, Nbl1QInfo->TagHeader.VlanId); + } + } + } + else + { + // + // Match VLAN ID + // + if(Nbl1QInfo->Value) + { + MatchResult = (Nbl1QInfo->TagHeader.VlanId == Filter->VlanId); + DEBUGP(MP_TRACE, "[%p] NBL Vlan ID: %i, Filter Vlan ID: %i\n", + Adapter, + Nbl1QInfo->TagHeader.VlanId, Filter->VlanId); + } + else + { + MatchResult = FALSE; + DEBUGP(MP_TRACE, "[%p] NBL without VLAN TAG. Filter Vlan ID: %i\n", + Adapter, + Filter->VlanId); + } + } + } + + DEBUGP(MP_TRACE, "[%p] <--- MatchRxFilter MatchResult %s\n", Adapter, MatchResult?"TRUE":"FALSE"); + + return MatchResult; +} + +BOOLEAN +FindRxQueueRecipient( + _In_ struct _MP_ADAPTER *Adapter, + _In_ struct _FRAME *Frame, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _Out_ USHORT *QueueId) +/*++ +Routine Description: + + This routine will return the matching QueueId and FilterId for a particular Frame and its 802.1Q data. + +Arguments: + + Adapter - Pointer to our adapter + Frame - Receive frame + Nbl1QInfo - Receive VLAN information + QueueId - Is assigned the matching QueueId (NDIS_DEFAULT_RECEIVE_QUEUE_GROUP_ID if no queue matches) + +Return Value: + + TRUE - Matched a specific queue (even default queue, if filters present on it) + FALSE - No queue matched + +--*/ +{ + USHORT index; + BOOLEAN Matched=FALSE; + + DEBUGP(MP_TRACE, "[%p] ---> FindRxQueueRecipient\n", Adapter); + + // + // Iterate through all the filters and check whether a match exists + // + *QueueId = NDIS_DEFAULT_RECEIVE_QUEUE_ID; + for(index = 0; index < NIC_MAX_HEADER_FILTERS; index++) + { + // + // Only attempt to match a filter when it is valid and its queue has been completed + // + if(Adapter->VMQData.RxFilters[index].Valid + && + QUEUE_COMPLETE(&Adapter->VMQData.RxQueues[Adapter->VMQData.RxFilters[index].QueueId]) + && + MatchRxFilter(Adapter, ((PNIC_FRAME_HEADER)Frame->Data)->DestAddress, Nbl1QInfo, &Adapter->VMQData.RxFilters[index])) + { + DEBUGP(MP_TRACE, "[%p] Match Found.\n", Adapter); + Matched = TRUE; + *QueueId = Adapter->VMQData.RxFilters[index].QueueId; + break; + } + } + + if(!Matched) + { + UCHAR DestAddress[NIC_MACADDR_SIZE]; + ULONG FrameType; + // + // No queue matched, if the packet is destinaed specifically for this NIC we indicate it on the default queue + // + GET_DESTINATION_OF_FRAME(DestAddress, Frame->Data); + FrameType = NICGetFrameTypeFromDestination(DestAddress); + Matched = HWIsFrameAcceptedByPacketFilter(Adapter, DestAddress, FrameType); + } + + DEBUGP(MP_TRACE, "[%p] <-- FindRxQueueRecipient QueueId: %i, Matched: %i\n", Adapter, *QueueId, Matched); + + return Matched; + +} + +BOOLEAN +AcquireRxQueueReference( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) USHORT QueueId, + BOOLEAN RequireComplete + ) +/*++ +Routine Description: + + This routine will increase the reference count on a queue, as long as it is still valid. + +Arguments: + + Adapter - Pointer to our adapter + QueueId - The Queue to take reference + RequireComplete - If TRUE, the queue needs to be Completed to be considered valid, otherwise + Initialized is enough + +Return Value: + + TRUE - Queue was valid, ref count increased + FALSE - Queue was not valid, ref count not changed + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_FAILURE; + PMP_ADAPTER_QUEUE QueueInfo = &Adapter->VMQData.RxQueues[QueueId]; + LOCK_STATE_EX LockState; + + DEBUGP(MP_TRACE, "[%p] ---> AcquireRxQueueReference. QueueId: %i\n", Adapter, QueueId); + ASSERT(QueueId < NIC_SUPPORTED_NUM_QUEUES); + + // + // Acquire read lock on queue to verify that it is valid + // + NdisAcquireRWLockRead(QueueInfo->QueueLock, &LockState, 0); + do + { + // + // Verify queue is initialized and optionally completed before taking reference + // + if(!QUEUE_INITIALIZED(QueueInfo) + || + (RequireComplete && !QUEUE_COMPLETE(QueueInfo))) + { + DEBUGP(MP_LOUD, "[%p] Queue not in correct state to take reference. QueueInfoFlags: 0x%x.\n", Adapter, QueueInfo->QueueInfoFlags); + break; + } + + // + // Queue is ready for use. Increment the reference count for the receive block. + // + Status = NICReferenceReceiveBlock(Adapter, QueueInfo->QueueId); + + } while(FALSE); + + NdisReleaseRWLock(QueueInfo->QueueLock, &LockState); + + DEBUGP(MP_TRACE, "[%p] <--- AcquireRxQueueReference.\n", Adapter); + + return Status == NDIS_STATUS_SUCCESS; +} + + +VOID +ReleaseRxQueueReference( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) USHORT QueueId) +/*++ +Routine Description: + + This routine will decrease the reference count on a queue + +Arguments: + + Adapter - Pointer to our adapter + QueueId - The Queue to take reference + +Return Value: + + None + +--*/ +{ + ULONG RefCount = 0; + PMP_ADAPTER_QUEUE QueueInfo; + LOCK_STATE_EX LockState; + BOOLEAN PerformFree; + + DEBUGP(MP_TRACE, "[%p] ---> ReleaseRxQueueReference. QueueId: %i\n", Adapter, QueueId); + ASSERT(QueueId < NIC_SUPPORTED_NUM_QUEUES); + + QueueInfo = &Adapter->VMQData.RxQueues[QueueId]; + + // + // Acquire read lock on queue to synchronize our check whether the queue free was pended because reference count was not at zero + // + NdisAcquireRWLockRead(QueueInfo->QueueLock, &LockState, 0); + + // + // Reduce the reference count + // + NICDereferenceReceiveBlock(Adapter, QueueId, &RefCount); + + // + // If the reference count is zero, and the queue is tagged as freeing (which was pended earlier) + // then execute the free. + // + PerformFree = (RefCount == 0 && QUEUE_FREEING(QueueInfo)); + + // + // Done dereferencing and checking queue state + // + NdisReleaseRWLock(QueueInfo->QueueLock, &LockState); + + if(PerformFree) + { + DEBUGP(MP_LOUD, "[%p] RefCount has reached zero and the queue is marked for free. Freeing Queue %i.\n", Adapter, QueueId); + // + // We've already gotten the call to free this queue, but references were still + // active on it. Call the free function now that we're at zero references. + // + if(NDIS_CURRENT_IRQL() < DISPATCH_LEVEL) + { + NDIS_STATUS Status = FreeAdapterQueueInfo(Adapter, QueueId, NULL); + ASSERT(Status == NDIS_STATUS_SUCCESS); + UNREFERENCED_PARAMETER(Status); + } + else + { + NDIS_HANDLE WorkItemHandle = NdisAllocateIoWorkItem(Adapter->AdapterHandle); + DEBUGP(MP_LOUD, "[%p] Currently at DISPATCH, queueing work item to free Queue %i.\n", Adapter, QueueId); + if(WorkItemHandle) + { + NdisQueueIoWorkItem(WorkItemHandle, FreeRxQueuesWorkItem, Adapter); + } + else + { + DEBUGP(MP_ERROR, "[%p] Could not allocate IoWorkItem to free queue.\n", Adapter); + } + } + } + + DEBUGP(MP_TRACE, "[%p] <--- ReleaseRxQueueReference.\n", Adapter); + +} + +VOID +SetPendingRxQueueFree( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest + ) +/*++ +Routine Description: + + This routine stores the pending queue free OID request, so that it can be completed + at a later time. + +Arguments: + + Adapter - Pointer to our adapter + QueueId - The Queue to take reference + +Return Value: + + None + +--*/ +{ + DEBUGP(MP_TRACE, "[%p] ---> SetPendingRxQueueFree\n", Adapter); + + ASSERT(!Adapter->PendingRequest); + Adapter->PendingRequest = NdisSetRequest; + + DEBUGP(MP_TRACE, "[%p] <--- SetPendingRxQueueFree\n", Adapter); +} + +VOID +GetRcbForRxQueue( + _In_ struct _MP_ADAPTER *Adapter, + _In_ struct _FRAME *Frame, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _Outptr_result_maybenull_ PRCB *Rcb) +/*++ +Routine Description: + + This routine returns an RCB for the passed in Frame & VLAN info. The RCB will originate from the + matching queue. + +Arguments: + + Adapter - Pointer to our adapter + Frame - Data Frame + Nbl1QInfo - VLAN information for Frame + +Return Value: + + RCB pointer if matched + NULL if not matched + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + USHORT QueueId = 0; + + DEBUGP(MP_TRACE, "[%p] ---> GetRcbForRxQueue. Frame: 0x%p.\n", Adapter, Frame); + + *Rcb = NULL; + + // + // Find the matching queue for the Frame + // + if(FindRxQueueRecipient(Adapter, Frame, Nbl1QInfo, &QueueId)) + { + // + // Get a reference to the Queue to make sure it is kept valid throughout the receive + // + if(AcquireRxQueueReference(Adapter, QueueId, TRUE)) + { + PMP_ADAPTER_QUEUE Queue = &Adapter->VMQData.RxQueues[QueueId]; + // + // Get RCB from Queue + // + PLIST_ENTRY pEntry = NdisInterlockedRemoveHeadList( + &Queue->FreeRcbList, + &Queue->FreeRcbListLock); + if (pEntry) + { + *Rcb = CONTAINING_RECORD(pEntry, RCB, RcbLink); + } + else + { + DEBUGP(MP_ERROR, "[%p] Queue %i has run out of available RCBs for receive.\n", Adapter, QueueId); + // + // release the acquired reference on the queue + // + ReleaseRxQueueReference(Adapter, QueueId); + Status = NDIS_STATUS_RESOURCES; + } + } + } + else + { + DEBUGP(MP_LOUD, "[%p] No queue matched Frame: 0x%p.\n", Adapter, Frame); + } + + DEBUGP(MP_TRACE, "[%p] <--- GetRcbForRxQueue. RCB: %p\n", Adapter, Rcb); + + return; +} + +NDIS_STATUS +CopyFrameToRxQueueRcb( + _In_ struct _MP_ADAPTER *Adapter, + _In_ struct _FRAME *Frame, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _Inout_ struct _RCB *Rcb, + _Out_ BOOLEAN *Copied) +/*++ +Routine Description: + + This routine copies the Frame and VLAN information to the passed in RCB. On a real NIC this copy would + occur through a HW DMA. + +Arguments: + + Adapter - Pointer to our adapter + Frame - Data Frame + Nbl1QInfo - VLAN information for Frame + Rcb - RCB to receive the data + Copied - TRUE if the frame was copied to shared memory + +Return Value: + + NDIS_STATUS_SUCCESS if copy succeeded. + NDIS_STATUS_ADAPTER_NOT_READY if we can no longer copy data to the RCB due to the queue being in freeing state (DMA stopped) + +--*/ +{ + USHORT QueueId = NET_BUFFER_LIST_RECEIVE_QUEUE_ID(Rcb->Nbl); + PMP_ADAPTER_QUEUE Queue = &Adapter->VMQData.RxQueues[QueueId]; + PNET_BUFFER NetBuffer = NET_BUFFER_LIST_FIRST_NB(Rcb->Nbl); + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + BOOLEAN DMAEnabled = TRUE; + *Copied = FALSE; + + DEBUGP(MP_TRACE, "[%p] ---> CopyFrameToRxQueueRcb. RCB: %p\n", Adapter, Rcb); + + // + // Mark the queue as having an in progress DMA. This avoids a queue free OID request from causing the queue to + // enter DMA stopped state prematurely. + // + QUEUE_SET_FLAG(Queue,fMPAQI_DMA_IN_PROGRESS); + + // + // Make sure the DMA progress set operation happends before the queue freeing check + // + KeMemoryBarrier(); + + // + // Verify that the queue is in a state where we can modify receive memory. Please note we set the DMA in progress + // first, then check freeing state. The freeing code sets freeing state first, then checks DMA in progress flag. With this + // approach, it's safe to check freeing state without synchronization and perform DMA if it's not yet set. If it gets updated + // to the pending freeing state after the below check, the freeing code (FreeAdapterQueueInfo) will see the DMA in progress flag + // and not set the DMA stopped state. + // + if(QUEUE_FREEING(Queue)) + { + LOCK_STATE_EX LockState; + BOOLEAN InDMAStoppedState; + // + // A free OID was issued on the queue, which is now pending until reference count reaches zero (we must have + // at least one reference to have reached this point). Verify whether we should update the queue to DMA stopped state + // (a DMA in progress may have prevented the free code from updating the state). + // + NdisAcquireRWLockWrite(Queue->QueueLock, &LockState, 0); + InDMAStoppedState = QUEUE_DMA_STOPPED(Queue); + if(!InDMAStoppedState) + { + // Set the DMA stopped sate. The indication to NDIS of this change happens outside of our synchronized + // block to avoid holding the lock for too long. + QUEUE_SET_FLAG(Queue,fMPAQI_DMA_STOPPED); + } + NdisReleaseRWLock(Queue->QueueLock, &LockState); + + if(!InDMAStoppedState) + { + // + // The queue was marked for free, but a pending DMA prevented it from entering DMA stopped. Enter + // DMA stopped state now, as we will not perform any more copies. + // + IndicateRxQueue( + Adapter, + QueueId, + NdisReceiveQueueOperationalStateDmaStopped); + } + + // + // Once we've entered the queue free state, we should not perform any more copies to the receive buffers + // + DMAEnabled = FALSE; + + } + + if(DMAEnabled) + { + if(QueueId) + { + PMP_ADAPTER_SHARED_MEMORY_BLOCK PostLookaheadBlock = Rcb->Data, LookaheadBlock = Rcb->LookaheadData; + ULONG LookaheadSize = 0; + + if(LOOKAHEAD_SPLIT_REQUIRED(Queue)) + { + LookaheadSize = Queue->LookaheadSize; + } + + if(LookaheadSize < Frame->ulSize) + { + + // + // Copy the PostLookahead data + // + memcpy(((PUCHAR)PostLookaheadBlock->Buffer) + LookaheadSize, ((PUCHAR)Frame->Data) + LookaheadSize, Frame->ulSize - LookaheadSize); + // + // Update the MDL to reflect the amount of data present + // + NdisAdjustMdlLength(PostLookaheadBlock->Mdl, Frame->ulSize - LookaheadSize); + } + + if(LookaheadSize) + { + // + // Copy the Lookahead + // + ULONG DataSize = min(LookaheadSize,Frame->ulSize); + memcpy(LookaheadBlock->Buffer, Frame->Data, DataSize); + // + // Update MDL to reflect the amount of data present + // + NdisAdjustMdlLength(LookaheadBlock->Mdl, DataSize); + } + + *Copied = TRUE; + + NET_BUFFER_DATA_LENGTH(NetBuffer) = Frame->ulSize; + + } + + // + // Set the OOB VLAN Information + // + if(Nbl1QInfo->Value) + { + NET_BUFFER_LIST_INFO(Rcb->Nbl, Ieee8021QNetBufferListInfo) = Nbl1QInfo->Value; + } + + } + else + { + // + // We are not in a state where we can copy data to receive buffers + // + Status = NDIS_STATUS_ADAPTER_NOT_READY; + } + + // + // Clear the DMA in progress flag + // + QUEUE_CLEAR_FLAG(Queue,fMPAQI_DMA_IN_PROGRESS); + + + DEBUGP(MP_TRACE, "[%p] <--- CopyFrameToRxQueueRcb. Copied: %i, Status 0x%08x\n", Adapter, *Copied, Status); + + return Status; + +} + +VOID +RecoverRxQueueRcb( + _In_ struct _MP_ADAPTER *Adapter, + _In_ struct _RCB *Rcb) +/*++ +Routine Description: + + This routine returns an RCB to its owner VMQ Queue, releasing any references held. + +Arguments: + + Adapter - Pointer to our adapter + Rcb - RCB to recover + +Return Value: + + None + +--*/ +{ + // + // Return RCB to owner Queue + // + PMP_ADAPTER_QUEUE Queue = NULL; + USHORT QueueId = NET_BUFFER_LIST_RECEIVE_QUEUE_ID(Rcb->Nbl); + + DEBUGP(MP_TRACE, "[%p] ---> RecoverRxQueueRcb. RCB: %p\n", Adapter, Rcb); + + if(QueueId>=NIC_SUPPORTED_NUM_QUEUES) + { + DEBUGP(MP_ERROR, "[%p] RCB could not be recovered. The NBL QueueId was out of range: %i\n", Adapter, QueueId); + ASSERT(FALSE); + return; + } + + Queue = &Adapter->VMQData.RxQueues[QueueId]; + if(!QueueId) + { + // + // Default queue uses the send frame, rather than copying the data. Release + // the frame reference. + // + HWFrameRelease((PFRAME)Rcb->Data); + Rcb->Data = NULL; + } + + // + // Add RCB back to free pool + // + NdisInterlockedInsertTailList( + &Queue->FreeRcbList, + &Rcb->RcbLink, + &Queue->FreeRcbListLock); + + // + // Release reference to queue that we took when indicating the NBL + // + ReleaseRxQueueReference(Adapter, QueueId); + + DEBUGP(MP_TRACE, "[%p] <--- RecoverRxQueueRcb\n", Adapter); + +} + +VOID +AddPendingRcbToRxQueue( + _In_ struct _MP_ADAPTER *Adapter, + _In_ struct _RCB *Rcb) +/*++ +Routine Description: + + This routine adds an RCB to the pending receive list for its owner VMQ queue. + +Arguments: + + Adapter - Pointer to our adapter + Rcb - RCB to queue for recieve + +Return Value: + + None + +--*/ +{ + NDIS_RECEIVE_QUEUE_ID QueueId = NET_BUFFER_LIST_RECEIVE_QUEUE_ID(Rcb->Nbl); + DEBUGP(MP_TRACE, "[%p] ---> AddPendingRcbToRxQueue. RCB: %p\n", Adapter, Rcb); + NdisInterlockedInsertTailList( + &Adapter->ReceiveBlock[QueueId].ReceiveList, + &Rcb->RcbLink, + &Adapter->ReceiveBlock[QueueId].ReceiveListLock); + DEBUGP(MP_TRACE, "[%p] <--- AddPendingRcbToRxQueue\n", Adapter); +} + diff --git a/network/ndis/netvmini/6x/vmq.h b/network/ndis/netvmini/6x/vmq.h new file mode 100644 index 00000000..5b949e2a --- /dev/null +++ b/network/ndis/netvmini/6x/vmq.h @@ -0,0 +1,393 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Vmq.h + +Abstract: + + This module declares the VMQ related data types, flags, macros, and functions. + +Revision History: + +Notes: + +--*/ + + +struct _FRAME; +struct _RCB; + +#if (NDIS_SUPPORT_NDIS620) + + +// +// VMQ queue structures +// + +// +// Tracks the shared memory segment allocated for indications +// +typedef struct _MP_ADAPTER_SHARED_MEMORY_BLOCK +{ + // + // MDL for the shared memory segment + // + PMDL Mdl; + // + // Pointer to shared memory for this block + // + PUCHAR Buffer; + // + // Description of the shared memory block used for the NetBuffer usage + // + NET_BUFFER_SHARED_MEMORY BufferSharedMemoryData; +} MP_ADAPTER_SHARED_MEMORY_BLOCK, *PMP_ADAPTER_SHARED_MEMORY_BLOCK; + +// +// Tracks a shared memory buffer allocation, which is then subdivided into individual blocks for indications +// +typedef struct _MP_ADAPTER_SHARED_MEMORY +{ + LIST_ENTRY Entry; + NDIS_HANDLE AllocationHandle; + NDIS_HANDLE MemoryHandle; + PUCHAR Buffer; + ULONG EntrySize; + ULONG NumberOfEntries; +} MP_ADAPTER_SHARED_MEMORY, *PMP_ADAPTER_SHARED_MEMORY; + +// +// The minimum number of shared memory blocks we require. Used when recovering from +// shared memory allocation failures. +// +#define NIC_MIN_RECV_ENTRY_ALLOCATION_COUNT 32 + +// +// Flags set to individual queues. Any code changing the flag value should acquire the corresponding +// lock. +// +// +// The queue is initialized, but not yet completed. Receives disabled. +// +#define fMPAQI_INITIALIZED 0x1 +// +// Completion of the queue has started. Receives disabled. +// +#define fMPAQI_COMPLETION_STARTED 0x2 +// +// Queue completed. Receives enabled. +// +#define fMPAQI_COMPLETION_FINISHED 0x4 +// +// Queue is being freed (pending RefCount). Receives disabled. +// +#define fMPAQI_FREEING 0x8 +// +// DMA is being performed for queue +// +#define fMPAQI_DMA_IN_PROGRESS 0x10 +// +// The queue has entered DMA stopped state. No copies should occur to receive memory for this queue. +// +#define fMPAQI_DMA_STOPPED 0x20 + +#define QUEUE_SET_FLAG(_QueueInfo, _Flag)\ + ((_QueueInfo)->QueueInfoFlags |= _Flag) + +#define QUEUE_CLEAR_FLAG(_QueueInfo, _Flag)\ + ((_QueueInfo)->QueueInfoFlags &= ~(_Flag)) + +#define QUEUE_INITIALIZED(_QueueInfo)\ + ((_QueueInfo)->QueueInfoFlags & fMPAQI_INITIALIZED) + +#define QUEUE_COMPLETING(_QueueInfo)\ + ((_QueueInfo)->QueueInfoFlags & fMPAQI_COMPLETION_STARTED) + +#define QUEUE_COMPLETE(_QueueInfo)\ + ((_QueueInfo)->QueueInfoFlags & fMPAQI_COMPLETION_FINISHED) + +#define QUEUE_FREEING(_QueueInfo)\ + ((_QueueInfo)->QueueInfoFlags & fMPAQI_FREEING) + +#define QUEUE_DMA_IN_PROGRESS(_QueueInfo)\ + ((_QueueInfo)->QueueInfoFlags & fMPAQI_DMA_IN_PROGRESS) + +#define QUEUE_DMA_STOPPED(_QueueInfo)\ + ((_QueueInfo)->QueueInfoFlags & fMPAQI_DMA_STOPPED) + +// +// The MP_ADAPTER_QUEUE structure is used to track a specific VMQ queue +// +typedef struct DECLSPEC_CACHEALIGN _MP_ADAPTER_QUEUE +{ + // + // Lock for Read/Write access to QueueInfoFlags and RefCount. + // + PNDIS_RW_LOCK_EX QueueLock; + // + // Flags used to track status of Queue (fMPAQI_* flags) + // + ULONG QueueInfoFlags; + + // + // DPC used for receives for this queue + // + struct _MP_ADAPTER_RECEIVE_DPC *ReceiveDpc; + + // + // List of unused RCBs (sliced out of RcbMemoryBlock) + // + LIST_ENTRY FreeRcbList; + NDIS_SPIN_LOCK FreeRcbListLock; + + // + // RCB & NBL memory information + // + PUCHAR RcbMemoryBlock; + NDIS_HANDLE RecvNblPoolHandle; + + // + // Shared memory information (MP_ADAPTER_SHARED_MEMORY) + // + ULONG NumReceiveBuffers; + // + // List of MP_ADAPTER_SHARED_MEMORY, large shared memory buffers subdivided for use when receiving + // + LIST_ENTRY LookaheadSharedMemoryList; + LIST_ENTRY PostLookaheadSharedMemoryList; + // + // MP_ADAPTER_SHARED_MEMORY_BLOCK buffers to hold book-keeping info on subdivided shared memory buffers + // + PUCHAR LookaheadBlocks; + ULONG NumLookaheadBlocks; + PUCHAR PostLookaheadBlocks; + ULONG NumPostLookaheadBlocks; + + // + // Data passed in through the VMQ Queue configuration related OIDs + // + NDIS_RECEIVE_QUEUE_ID QueueId; + ULONG NumSuggestedReceiveBuffers; + ULONG LookaheadSize; + ULONG NdisFlags; + GROUP_AFFINITY ProcessorAffinity; +} MP_ADAPTER_QUEUE, *PMP_ADAPTER_QUEUE; + +// +// Structure used to store receive filter data +// +typedef struct _MP_ADAPTER_FILTER +{ + // + // Whether the receive filter should be used + // + BOOLEAN Valid; + // + // Filter matching fields + // + BOOLEAN VlanUntaggedOrZero; + USHORT QueueId; + USHORT VlanId; + UCHAR MacAddress[NIC_MACADDR_SIZE]; +} MP_ADAPTER_FILTER, *PMP_ADAPTER_FILTER; + +#define MP_ADAPTER_FILTER_INDEX(_FilterId_)\ + ((_FilterId_)-1) + +// +// Global VMQ configuration structures +// + +// +// Flags tracking global VMQ state +// +// +// VMQ is enabled on the adapter. +// +#define fMPVMQD_FILTERING_ENABLED 0x0001 +// +// Lookahead split in VMQ indication is enabled on the adapter. +// +#define fMPVMQD_LOOKAHEAD_ENABLED 0x0002 +// +// VLAN filtering in VMQ is enabled on the adapter. +// +#define fMPVMQD_VLANFILTER_ENABLED 0x0004 + +#define VMQ_SET_FLAG(_Adapter, _Flag) \ + ((_Adapter)->VMQData.Flags |= (_Flag)) + +#define VMQ_ENABLED(_Adapter) \ + ((_Adapter)->VMQData.Flags & fMPVMQD_FILTERING_ENABLED) +#define LOOKAHEAD_SPLIT_ENABLED(_Adapter)\ + ((_Adapter)->VMQData.Flags & fMPVMQD_LOOKAHEAD_ENABLED) +#define VLAN_FILTER_ENABLED(_Adapter)\ + ((_Adapter)->VMQData.Flags & fMPVMQD_VLANFILTER_ENABLED) + +#define LOOKAHEAD_SPLIT_REQUIRED(_QueueInfo)\ + ((_QueueInfo)->NdisFlags & NDIS_RECEIVE_QUEUE_PARAMETERS_LOOKAHEAD_SPLIT_REQUIRED) + +// +// The MP_ADAPTER_VMQ_DATA structure is used to track the global VMQ configuration for an adapter +// +typedef struct _MP_ADAPTER_VMQ_DATA +{ + // + // Tracks global VMQ state (fMPVMQD_* flags) + // + ULONG Flags; + // + // Individual Queues. The MP_ADAPTER_QUEUE array is not dynamically allocated to reduce + // pointer dereferencing during receives, which can affect performance. + // + MP_ADAPTER_QUEUE RxQueues[NIC_SUPPORTED_NUM_QUEUES]; + // + // Filters used to match packets to Queues + // + MP_ADAPTER_FILTER RxFilters[NIC_MAX_HEADER_FILTERS]; +} MP_ADAPTER_VMQ_DATA, *PMP_ADAPTER_VMQ_DATA; + +NDIS_STATUS +AllocateVMQData( + _Inout_ struct _MP_ADAPTER *Adapter); + +VOID +FreeVMQData( + _Inout_ struct _MP_ADAPTER *Adapter); + +NDIS_STATUS +ReadRxQueueConfig( + _In_ NDIS_HANDLE ConfigurationHandle, + _Inout_ struct _MP_ADAPTER *Adapter); + +NDIS_STATUS +InitializeRxQueueMPConfig( + _Inout_ struct _MP_ADAPTER *Adapter); + +NDIS_STATUS +AllocateDefaultRxQueue( + _Inout_ struct _MP_ADAPTER *Adapter); + + +NDIS_STATUS +AllocateRxQueue( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_QUEUE_PARAMETERS QueueParams + ); + +NDIS_STATUS +CompleteAllocationRxQueue( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_QUEUE_ALLOCATION_COMPLETE_ARRAY CompleteArray + ); + +NDIS_STATUS +UpdateRxQueue( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_QUEUE_PARAMETERS QueueParams +); + +NDIS_STATUS +FreeRxQueue( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_QUEUE_FREE_PARAMETERS QueueParams, + _In_opt_ PNDIS_OID_REQUEST NdisSetRequest +); + +VOID +IndicateRxQueue( + _In_ NDIS_HANDLE AdapterHandle, + NDIS_RECEIVE_QUEUE_ID QueueId, + NDIS_RECEIVE_QUEUE_OPERATIONAL_STATE State); + +NDIS_STATUS +SetRxFilter( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_FILTER_PARAMETERS FilterParams + ); + +NDIS_STATUS +ClearRxFilter( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_RECEIVE_FILTER_CLEAR_PARAMETERS FilterParams + ); + +BOOLEAN +AcquireRxQueueReference( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) USHORT QueueId, + BOOLEAN RequireComplete + ); + +VOID +ReleaseRxQueueReference( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) USHORT QueueId + ); + +VOID +SetPendingRxQueueFree( + _Inout_ struct _MP_ADAPTER *Adapter, + _In_ PNDIS_OID_REQUEST NdisSetRequest + ); + +VOID +GetRcbForRxQueue( + _In_ struct _MP_ADAPTER *Adapter, + _In_ struct _FRAME *Frame, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _Outptr_result_maybenull_ struct _RCB **Rcb); + +NDIS_STATUS +CopyFrameToRxQueueRcb( + _In_ struct _MP_ADAPTER *Adapter, + _In_ struct _FRAME *Frame, + _In_ PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo, + _Inout_ struct _RCB *Rcb, + _Out_ BOOLEAN *Copied); + +VOID +RecoverRxQueueRcb( + _In_ struct _MP_ADAPTER *Adapter, + _In_ struct _RCB *Rcb); + +VOID +AddPendingRcbToRxQueue( + _In_ struct _MP_ADAPTER *Adapter, + _In_ struct _RCB *Rcb); + +#define GetRxQueueDpc(Adapter, QueueId) (Adapter)->VMQData.RxQueues[(QueueId)].ReceiveDpc + +#else + +// +// In order to avoid excessible "#if defined(NDIS620_MINIPORT)" statements scattered +// through the miniport implementation, NDIS60 miniports define +// placeholder macros for the VMQ functions which cause the code to always proceed +// as if VMQ were disabled on the adapter. +// + +#define VMQ_ENABLED(_Adapter) FALSE +#define LOOKAHEAD_SPLIT_ENABLED(_Adapter) FALSE +#define VLAN_FILTER_ENABLED(_Adapter) FALSE +#define LOOKAHEAD_SPLIT_REQUIRED(_QueueInfo) FALSE +#define AllocateDefaultRxQueue(Adapter) NDIS_STATUS_NOT_SUPPORTED +#define AddPendingRcbToRxQueue(Adapter, Rcb) +#define GetRxQueueDpc(Adapter, QueueId) NULL +#define AllocateVMQData(Adapter) NDIS_STATUS_SUCCESS +#define FreeVMQData(Adapter) +#define ReadRxQueueConfig(ConfigurationHandle, Adapter) NDIS_STATUS_SUCCESS +#define InitializeRxQueueMPConfig(Adapter) NDIS_STATUS_SUCCESS +#define CopyFrameToRxQueueRcb(Adapter, Frame, Nbl1QInfo, Rcb, Copied) FALSE +#define GetRcbForRxQueue(Adapter, Frame, Nbl1QInfo, Rcb) NDIS_STATUS_NOT_SUPPORTED +#define RecoverRxQueueRcb(Adapter, Rcb) + +#endif diff --git a/network/trans/ddproxy/ReadMe.md b/network/trans/ddproxy/ReadMe.md new file mode 100644 index 00000000..67a6b213 --- /dev/null +++ b/network/trans/ddproxy/ReadMe.md @@ -0,0 +1,66 @@ +Windows Filtering Platform Packet Modification Sample +===================================================== + +The sample driver demonstrates the packet modification capabilities of the Windows Filtering Platform (WFP). + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Run the sample +-------------- + +The computer where you install the driver is called the *target computer* or the *test computer*. Typically this is a separate computer from where you develop and build the driver package. The computer where you develop and build the driver is called the *host computer*. + +The process of moving the driver package to the target computer and installing the driver is called *deploying the driver*. You can deploy the Windows Filtering Platform Packet Modification Sample driver automatically or manually. + +Automatic deployment +-------------------- + +Before you automatically deploy a driver, you must provision the target computer. For instructions, see [Configuring a Computer for Driver Deployment, Testing, and Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/). After you have provisioned the target computer, continue with these steps: + +1. On the host computer, in Visual Studio, in Solution Explorer, right click **package** (lower case), and choose **Properties**. Navigate to **Configuration Properties \> Driver Install \> Deployment**. +2. Check **Enable deployment**, and check **Remove previous driver versions before deployment**. For **Target Computer Name**, select the name of a target computer that you provisioned previously. Select **Do not install**. Click **OK**. +3. On the **Build** menu, choose **Build Solution**. +4. On the target computer, navigate to DriverTest\\Drivers, and locate the file ddproxy.inf. Right click ddproxy.inf, and choose **Install**. + +Manual deployment +----------------- + +Before you manually deploy a driver, you must turn on test signing and install a certificate on the target computer. You also need to copy the [DevCon](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544707) tool to the target computer. For instructions, see [Preparing a Computer for Manual Driver Deployment](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265571). After you have prepared the target computer for manual deployment, continue with these steps: + +1. Copy all of the files in your driver package to a folder on the target computer (for example, c:\\WfpPacketModificationSamplePackage). +2. On the target computer, navigate to your driver package folder. Right click ddproxy.inf, and choose **Install** + +Create Registry values +---------------------- + +1. On the target computer, open Regedit, and navigate to this key: + + **HKLM**\\**System**\\**CurrentControlSet**\\**Services**\\**ddproxy**\\**Parameters** + +2. Create a REG\_SZ entry named **DestinationAddressToIntercept** and set it's value to an IPV4 or IPV6 address (example: 10.0.0.1). + +3. Create a REG\_SZ entry named **NewDestinationAddress**, and set it's value to an IPV4 or IPV6 address (example: 10.0.0.2). + +You can also create and set values for the following registry entries. + +- **InspectUdp** (REG\_DWORD type): 0 for ICMP and 1 for UDP (default) +- **DestinationPortToIntercept** (REG\_DWORD type): UDP port number (applicable if InspectUdp is set to 1) +- **NewDestinationPort** (REG\_DWORD type): UDP port number (applicable if InspectUdp is set to 1) + +Start the ddproxy service +------------------------- + +On the target computer, open a Command Prompt window as Administrator, and enter **net start ddproxy**. (To stop the driver, enter **net stop ddproxy**.) + +Remarks +------- + +This sample driver consists of a kernel-mode Windows Filtering Platform (WFP) callout driver (Ddproxy.sys) that intercepts User Datagram Protocol (UDP) and nonerror Internet Control Message Protocol (ICMP) traffic of interest and acts as a redirector. For outbound traffic, Ddproxy.sys redirects the traffic to a new destination address and, for UDP, a new UDP port. For inbound traffic, Ddproxy.sys redirects the traffic back to the original address and UDP port values. This redirection is transparent to the application. + +Packet modification is done out-of-band by a system worker thread by using the reference-drop-clone-modify-reinject mechanism. Therefore, the sample can serve as a basis for scenarios in which the filtering/modification decision cannot be made within the `classifyFn()` callout, but instead must be made, for example, by a user-mode application. + +Ddproxy.sys acts as a redirector for both Internet Protocol version 4 (IPv4) and Internet Protocol version 6 (IPv6) traffic. + +For more information on creating a Windows Filtering Platform Callout Driver, see [Windows Filtering Platform Callout Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff571068). + diff --git a/network/trans/ddproxy/ddproxy.sln b/network/trans/ddproxy/ddproxy.sln new file mode 100644 index 00000000..9115b369 --- /dev/null +++ b/network/trans/ddproxy/ddproxy.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ddproxy", "sys\ddproxy.vcxproj", "{F5ED1745-0947-474A-924D-CB5D3D2D6C5E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F5ED1745-0947-474A-924D-CB5D3D2D6C5E}.Debug|Win32.ActiveCfg = Debug|Win32 + {F5ED1745-0947-474A-924D-CB5D3D2D6C5E}.Debug|Win32.Build.0 = Debug|Win32 + {F5ED1745-0947-474A-924D-CB5D3D2D6C5E}.Release|Win32.ActiveCfg = Release|Win32 + {F5ED1745-0947-474A-924D-CB5D3D2D6C5E}.Release|Win32.Build.0 = Release|Win32 + {F5ED1745-0947-474A-924D-CB5D3D2D6C5E}.Debug|x64.ActiveCfg = Debug|x64 + {F5ED1745-0947-474A-924D-CB5D3D2D6C5E}.Debug|x64.Build.0 = Debug|x64 + {F5ED1745-0947-474A-924D-CB5D3D2D6C5E}.Release|x64.ActiveCfg = Release|x64 + {F5ED1745-0947-474A-924D-CB5D3D2D6C5E}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/network/trans/ddproxy/sys/DD_drv.c b/network/trans/ddproxy/sys/DD_drv.c new file mode 100644 index 00000000..18509e53 --- /dev/null +++ b/network/trans/ddproxy/sys/DD_drv.c @@ -0,0 +1,1059 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Datagram-Data Transparent Proxy Callout Driver Sample. + + This sample callout driver intercepts UDP and non-error ICMP traffic + of interest and proxies them to a new destination address and/or port + (for UDP); response traffic will be proxied back to have the original + tuple values. The proxying is transparent to the application. + + Inspection parameters and proxy settings are configurable via the + following registry values -- + + HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ddproxy\Parameters + + o InspectUdp (REG_DWORD) : 0 (ICMP); 1 (UDP, default) + o DestinationAddressToIntercept (REG_SZ) : literal IPv4/IPv6 string + (e.g. “10.0.0.1”) + o DestinationPortToIntercept (REG_DWORD) : applicable if InspectUdp is 1 + o NewDestinationAddress(REG_SZ) : literal IPv4/IPv6 string + o NewDestinationPort(REG_DWORD) + + The sample is IP version agnostic. It performs proxying for both IPv4 + and IPv6 traffic. + +Environment: + + Kernel mode + +--*/ + +#include +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + +#include + +#include +#include +#include + +#include "DD_proxy.h" + +#define INITGUID +#include + +// +// Configurable parameters (addresses and ports are in host order) +// + +BOOLEAN configInspectUdp = TRUE; + +UINT16 configInspectDestPort = 5001; +UINT8* configInspectDestAddrV4 = NULL; +UINT8* configInspectDestAddrV6 = NULL; + +UINT16 configNewDestPort = 5001; +UINT8* configNewDestAddrV4 = NULL; +UINT8* configNewDestAddrV6 = NULL; + +SOCKADDR_STORAGE destAddr, newDestAddr; + +// +// Callout and sublayer GUIDs +// + +// b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8 +DEFINE_GUID( + DD_PROXY_CALLOUT_V4, + 0xb16b0a6e, + 0x2b2a, + 0x41a3, + 0x8b, 0x39, 0xbd, 0x3f, 0xfc, 0x85, 0x5f, 0xf8 +); +// 2cebde39-1f59-48d1-a5d9-3e2458351476 +DEFINE_GUID( + DD_PROXY_CALLOUT_V6, + 0x2cebde39, + 0x1f59, + 0x48d1, + 0xa5, 0xd9, 0x3e, 0x24, 0x58, 0x35, 0x14, 0x76 +); +// ee93719d-ad5d-48c9-ae46-7270367d205d +DEFINE_GUID( + DD_PROXY_FLOW_ESTABLISHED_CALLOUT_V4, + 0xee93719d, + 0xad5d, + 0x48c9, + 0xae, 0x46, 0x72, 0x70, 0x36, 0x7d, 0x20, 0x5d +); + +// 1e3d3d13-0588-4167-82a3-14f68c98de86 +DEFINE_GUID( + DD_PROXY_FLOW_ESTABLISHED_CALLOUT_V6, + 0x1e3d3d13, + 0x0588, + 0x4167, + 0x82, 0xa3, 0x14, 0xf6, 0x8c, 0x98, 0xde, 0x86 +); + +// 0104fd7e-c825-414e-94c9-f0d525bbc169 +DEFINE_GUID( + DD_PROXY_SUBLAYER, + 0x0104fd7e, + 0xc825, + 0x414e, + 0x94, 0xc9, 0xf0, 0xd5, 0x25, 0xbb, 0xc1, 0x69 +); + +// +// Callout driver global variables +// + +DEVICE_OBJECT* gWdmDevice; + +HANDLE gEngineHandle; +UINT32 gFlowEstablishedCalloutIdV4, gCalloutIdV4; +UINT32 gFlowEstablishedCalloutIdV6, gCalloutIdV6; + +HANDLE gInjectionHandle; + +LIST_ENTRY gFlowList; +KSPIN_LOCK gFlowListLock; + +LIST_ENTRY gPacketQueue; +KSPIN_LOCK gPacketQueueLock; +KEVENT gPacketQueueEvent; + +BOOLEAN gDriverUnloading = FALSE; +void* gThreadObj; + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_UNLOAD EvtDriverUnload; + +// +// Callout driver implementation +// + +NTSTATUS +DDProxyLoadIPAddress( + _In_ const WDFKEY key, + _In_ const UNICODE_STRING* valueName, + _Out_ SOCKADDR_STORAGE* result + ) +{ + NTSTATUS status; + PWSTR terminator; + DECLARE_UNICODE_STRING_SIZE(value, INET6_ADDRSTRLEN); + IN_ADDR *resultV4 = &((SOCKADDR_IN*)result)->sin_addr; + IN6_ADDR *resultV6 = &((SOCKADDR_IN6*)result)->sin6_addr; + + status = WdfRegistryQueryUnicodeString(key, valueName, NULL, &value); + result->ss_family = AF_UNSPEC; + + if (NT_SUCCESS(status)) + { + // The Registry API does not guarantee that the string will be + // null-terminated. + // Defensively null-terminate the string. + value.Length = min(value.Length, value.MaximumLength - sizeof(WCHAR)); + value.Buffer[value.Length/sizeof(WCHAR)] = UNICODE_NULL; + + status = RtlIpv4StringToAddressW( + value.Buffer, + TRUE, + &terminator, + resultV4 + ); + + if (NT_SUCCESS(status)) + { + resultV4->S_un.S_addr = RtlUlongByteSwap(resultV4->S_un.S_addr); + result->ss_family = AF_INET; + } + else + { + status = RtlIpv6StringToAddressW( + value.Buffer, + &terminator, + resultV6 + ); + + if (NT_SUCCESS(status)) + { + result->ss_family = AF_INET6; + } + } + } + + return status; +} + +NTSTATUS +DDProxyLoadConfig( + _In_ const WDFKEY key + ) +{ + NTSTATUS status = STATUS_SUCCESS; + DECLARE_CONST_UNICODE_STRING(inspectUdpValueName, L"InspectUdp"); + DECLARE_CONST_UNICODE_STRING(destAddrValueName, L"DestinationAddressToIntercept"); + DECLARE_CONST_UNICODE_STRING(destPortValueName, L"DestinationPortToIntercept"); + DECLARE_CONST_UNICODE_STRING(newDestAddrValueName, L"NewDestinationAddress"); + DECLARE_CONST_UNICODE_STRING(newDestPortValueName, L"NewDestinationPort"); + + ULONG ulongValue; + + if (NT_SUCCESS(WdfRegistryQueryULong( + key, + &inspectUdpValueName, + &ulongValue + ))) + { + configInspectUdp = (ulongValue != 0); + } + + + if (NT_SUCCESS(DDProxyLoadIPAddress( + key, + &destAddrValueName, + &destAddr + ))) + { + if (destAddr.ss_family == AF_INET) + { + configInspectDestAddrV4 = &((SOCKADDR_IN*)&destAddr)->sin_addr.S_un.S_un_b.s_b1; + } + else if (destAddr.ss_family == AF_INET6) + { + configInspectDestAddrV6 = (UINT8*)(&((SOCKADDR_IN6*)&destAddr)->sin6_addr.u.Byte[0]); + } + } + + if (NT_SUCCESS(WdfRegistryQueryULong( + key, + &destPortValueName, + &ulongValue + ))) + { + configInspectDestPort = (USHORT) ulongValue; + } + + if (NT_SUCCESS(DDProxyLoadIPAddress( + key, + &newDestAddrValueName, + &newDestAddr + ))) + { + if (destAddr.ss_family == AF_INET) + { + configNewDestAddrV4 = &((SOCKADDR_IN*)&newDestAddr)->sin_addr.S_un.S_un_b.s_b1; + } + else if (destAddr.ss_family == AF_INET6) + { + configNewDestAddrV6 = (UINT8*)(&((SOCKADDR_IN6*)&newDestAddr)->sin6_addr.u.Byte[0]); + } + } + + if (NT_SUCCESS(WdfRegistryQueryULong( + key, + &newDestPortValueName, + &ulongValue + ))) + { + configNewDestPort = (USHORT) ulongValue; + } + + return status; +} + +NTSTATUS +DDProxyAddFilter( + _In_ const PWSTR filterName, + _In_ const PWSTR filterDesc, + _In_reads_(16) const UINT8* remoteAddr, + _In_ USHORT remotePort, + _In_ FWP_DIRECTION direction, + _In_ UINT64 context, + _In_ const GUID* layerKey, + _In_ const GUID* calloutKey + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + FWPM_FILTER filter = {0}; + FWPM_FILTER_CONDITION filterConditions[3] = {0}; + UINT conditionIndex; + + filter.layerKey = *layerKey; + filter.displayData.name = (wchar_t*)filterName; + filter.displayData.description = (wchar_t*)filterDesc; + + filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; + filter.action.calloutKey = *calloutKey; + filter.filterCondition = filterConditions; + filter.subLayerKey = DD_PROXY_SUBLAYER; + filter.weight.type = FWP_EMPTY; // auto-weight. + filter.rawContext = context; + + conditionIndex = 0; + + if (remoteAddr != NULL) + { + filterConditions[conditionIndex].fieldKey = + FWPM_CONDITION_IP_REMOTE_ADDRESS; + filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL; + + if (IsEqualGUID(layerKey, &FWPM_LAYER_DATAGRAM_DATA_V4) || + IsEqualGUID(layerKey, &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4)) + { + filterConditions[conditionIndex].conditionValue.type = FWP_UINT32; + filterConditions[conditionIndex].conditionValue.uint32 = + *(UINT32*)remoteAddr; + } + else + { + filterConditions[conditionIndex].conditionValue.type = + FWP_BYTE_ARRAY16_TYPE; + filterConditions[conditionIndex].conditionValue.byteArray16 = + (FWP_BYTE_ARRAY16*)remoteAddr; + } + + conditionIndex++; + } + + filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_DIRECTION; + filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL; + filterConditions[conditionIndex].conditionValue.type = FWP_UINT32; + filterConditions[conditionIndex].conditionValue.uint32 = direction; + + conditionIndex++; + + if (configInspectUdp) + { + filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT; + filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL; + filterConditions[conditionIndex].conditionValue.type = FWP_UINT16; + filterConditions[conditionIndex].conditionValue.uint16 = remotePort; + + conditionIndex++; + } + + filter.numFilterConditions = conditionIndex; + + status = FwpmFilterAdd( + gEngineHandle, + &filter, + NULL, + NULL); + + return status; +} + +NTSTATUS +DDProxyRegisterFlowEstablishedCallouts( + _In_ const GUID* layerKey, + _In_ const GUID* calloutKey, + _Inout_ void* deviceObject, + _Out_ UINT32* calloutId + ) +/* ++ + + This function registers callouts and filters at the following layers + to intercept flow creations for the original and the proxy flows. + + FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 + FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + FWPS_CALLOUT sCallout = {0}; + FWPM_CALLOUT mCallout = {0}; + + FWPM_DISPLAY_DATA displayData = {0}; + + BOOLEAN calloutRegistered = FALSE; + + sCallout.calloutKey = *calloutKey; + sCallout.classifyFn = DDProxyFlowEstablishedClassify; + sCallout.notifyFn = DDProxyFlowEstablishedNotify; + + status = FwpsCalloutRegister( + deviceObject, + &sCallout, + calloutId + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + calloutRegistered = TRUE; + + displayData.name = L"Datagram-Data Proxy Flow-Established Callout"; + displayData.description = + L"Intercepts flow creations for the original and the proxy flows"; + + mCallout.calloutKey = *calloutKey; + mCallout.displayData = displayData; + mCallout.applicableLayer = *layerKey; + + status = FwpmCalloutAdd( + gEngineHandle, + &mCallout, + NULL, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = DDProxyAddFilter( + L"Datagram-Data Proxy Flow-Established Filter (Original Flow)", + L"Intercepts flow creations for the original flow", + IsEqualGUID(layerKey, &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4) ? + configInspectDestAddrV4 : configInspectDestAddrV6, + configInspectDestPort, + FWP_DIRECTION_OUTBOUND, + DD_PROXY_FLOW_ORIGINAL, + layerKey, + calloutKey + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = DDProxyAddFilter( + L"Datagram-Data Proxy Flow-Established Filter (Proxy Flow)", + L"Intercepts flow creations for the proxy flow", + IsEqualGUID(layerKey, &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4) ? + configNewDestAddrV4 : configNewDestAddrV6, + configNewDestPort, + FWP_DIRECTION_OUTBOUND, + DD_PROXY_FLOW_PROXY, + layerKey, + calloutKey + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + +Exit: + + if (!NT_SUCCESS(status)) + { + if (calloutRegistered) + { + FwpsCalloutUnregisterById(*calloutId); + *calloutId = 0; + } + } + + return status; +} + +NTSTATUS +DDProxyRegisterDatagramDataCallouts( + _In_ const GUID* layerKey, + _In_ const GUID* calloutKey, + _Inout_ void* deviceObject, + _Out_ UINT32* calloutId + ) +/* ++ + + This function registers callouts and filters that intercept TCP traffic at + WFP FWPM_LAYER_DATAGRAM_DATA_V4 or FWPM_LAYER_DATAGRAM_DATA_V6 layer. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + FWPS_CALLOUT sCallout = {0}; + FWPM_CALLOUT mCallout = {0}; + + FWPM_DISPLAY_DATA displayData = {0}; + + BOOLEAN calloutRegistered = FALSE; + + sCallout.calloutKey = *calloutKey; + sCallout.classifyFn = DDProxyClassify; + sCallout.notifyFn = DDProxyNotify; + sCallout.flowDeleteFn = DDProxyFlowDelete; + sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW; + + status = FwpsCalloutRegister( + deviceObject, + &sCallout, + calloutId + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + calloutRegistered = TRUE; + + displayData.name = L"Datagram-Data Proxy Callout"; + displayData.description = L"Proxies destination address/port for UDP/ICMP"; + + mCallout.calloutKey = *calloutKey; + mCallout.displayData = displayData; + mCallout.applicableLayer = *layerKey; + + status = FwpmCalloutAdd( + gEngineHandle, + &mCallout, + NULL, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = DDProxyAddFilter( + L"Datagram-Data Proxy Filter (Outbound)", + L"Proxies destination address/port for UDP/ICMP", + IsEqualGUID(layerKey, &FWPM_LAYER_DATAGRAM_DATA_V4) ? + configInspectDestAddrV4 : configInspectDestAddrV6, + configInspectDestPort, + FWP_DIRECTION_OUTBOUND, + 0, + layerKey, + calloutKey + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = DDProxyAddFilter( + L"Datagram-Data Proxy Filter (Inbound)", + L"Proxies destination address/port for UDP/ICMP", + IsEqualGUID(layerKey, &FWPM_LAYER_DATAGRAM_DATA_V4) ? + configNewDestAddrV4 : configNewDestAddrV6, + configNewDestPort, + FWP_DIRECTION_INBOUND, + 0, + layerKey, + calloutKey + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + +Exit: + + if (!NT_SUCCESS(status)) + { + if (calloutRegistered) + { + FwpsCalloutUnregisterById(*calloutId); + *calloutId = 0; + } + } + + return status; +} + +NTSTATUS +DDProxyRegisterCallouts( + _Inout_ void* deviceObject + ) +/* ++ + + This function registers dynamic callouts and filters that intercept UDP or + non-error ICMP traffic at WFP FWPM_LAYER_DATAGRAM_DATA_V{4|6} and + FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4|6} layers. + + Callouts and filters will be removed during DriverUnload. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + FWPM_SUBLAYER DDProxySubLayer; + + BOOLEAN engineOpened = FALSE; + BOOLEAN inTransaction = FALSE; + + FWPM_SESSION session = {0}; + + session.flags = FWPM_SESSION_FLAG_DYNAMIC; + + status = FwpmEngineOpen( + NULL, + RPC_C_AUTHN_WINNT, + NULL, + &session, + &gEngineHandle + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + engineOpened = TRUE; + + status = FwpmTransactionBegin(gEngineHandle, 0); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + inTransaction = TRUE; + + RtlZeroMemory(&DDProxySubLayer, sizeof(FWPM_SUBLAYER)); + + DDProxySubLayer.subLayerKey = DD_PROXY_SUBLAYER; + DDProxySubLayer.displayData.name = L"Datagram-Data Proxy Sub-Layer"; + DDProxySubLayer.displayData.description = + L"Sub-Layer for use by Datagram-Data Proxy callouts"; + DDProxySubLayer.flags = 0; + DDProxySubLayer.weight = FWP_EMPTY; // auto-weight.; + + status = FwpmSubLayerAdd(gEngineHandle, &DDProxySubLayer, NULL); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = DDProxyRegisterFlowEstablishedCallouts( + &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, + &DD_PROXY_FLOW_ESTABLISHED_CALLOUT_V4, + deviceObject, + &gFlowEstablishedCalloutIdV4 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = DDProxyRegisterFlowEstablishedCallouts( + &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, + &DD_PROXY_FLOW_ESTABLISHED_CALLOUT_V6, + deviceObject, + &gFlowEstablishedCalloutIdV6 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = DDProxyRegisterDatagramDataCallouts( + &FWPM_LAYER_DATAGRAM_DATA_V4, + &DD_PROXY_CALLOUT_V4, + deviceObject, + &gCalloutIdV4 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = DDProxyRegisterDatagramDataCallouts( + &FWPM_LAYER_DATAGRAM_DATA_V6, + &DD_PROXY_CALLOUT_V6, + deviceObject, + &gCalloutIdV6 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = FwpmTransactionCommit(gEngineHandle); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + inTransaction = FALSE; + +Exit: + + if (!NT_SUCCESS(status)) + { + if (inTransaction) + { + FwpmTransactionAbort(gEngineHandle); + _Analysis_assume_lock_not_held_(gEngineHandle); // Potential leak if "FwpmTransactionAbort" fails + } + if (engineOpened) + { + FwpmEngineClose(gEngineHandle); + gEngineHandle = NULL; + } + } + + return status; +} + +void +DDProxyUnregisterCallouts(void) +{ + FwpmEngineClose(gEngineHandle); + gEngineHandle = NULL; + + FwpsCalloutUnregisterById(gCalloutIdV6); + FwpsCalloutUnregisterById(gCalloutIdV4); + + FwpsCalloutUnregisterById(gFlowEstablishedCalloutIdV6); + FwpsCalloutUnregisterById(gFlowEstablishedCalloutIdV4); +} + +void +DDProxyRemoveFlows(void) +{ + while (!IsListEmpty(&gFlowList)) + { + KLOCK_QUEUE_HANDLE flowListLockHandle; + LIST_ENTRY* listEntry = NULL; + DD_PROXY_FLOW_CONTEXT* flowContext; + + KeAcquireInStackQueuedSpinLock( + &gFlowListLock, + &flowListLockHandle + ); + + if (!IsListEmpty(&gFlowList)) + { + listEntry = RemoveHeadList(&gFlowList); + } + + // + // Releasing the lock here since removing the flow context + // will invoke the callout's flowDeleteFn synchronously + // if there are no active classifications in progress. + // + KeReleaseInStackQueuedSpinLock(&flowListLockHandle); + + if (listEntry != NULL) + { + flowContext = CONTAINING_RECORD( + listEntry, + DD_PROXY_FLOW_CONTEXT, + listEntry + ); + + flowContext->deleted = TRUE; + + FwpsFlowRemoveContext( + flowContext->flowId, + flowContext->layerId, + flowContext->calloutId + ); + } + } +} + +_Function_class_(EVT_WDF_DRIVER_UNLOAD) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +void +EvtDriverUnload( + _In_ WDFDRIVER driverObject + ) +{ + KLOCK_QUEUE_HANDLE packetQueueLockHandle; + KLOCK_QUEUE_HANDLE flowListLockHandle; + + UNREFERENCED_PARAMETER(driverObject); + + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + KeAcquireInStackQueuedSpinLock( + &gFlowListLock, + &flowListLockHandle + ); + + gDriverUnloading = TRUE; + + KeReleaseInStackQueuedSpinLock(&flowListLockHandle); + + // + // Any associated flow contexts must be removed before + // a callout can be successfully unregistered. + // + DDProxyRemoveFlows(); + + if (IsListEmpty(&gPacketQueue)) + { + KeSetEvent( + &gPacketQueueEvent, + IO_NO_INCREMENT, + FALSE + ); + } + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + + NT_ASSERT(gThreadObj != NULL); + + KeWaitForSingleObject( + gThreadObj, + Executive, + KernelMode, + FALSE, + NULL + ); + + ObDereferenceObject(gThreadObj); + + DDProxyUnregisterCallouts(); + + FwpsInjectionHandleDestroy(gInjectionHandle); +} + +// +// Create the minimal WDF Driver and Device objects required for a WFP callout +// driver. +// +NTSTATUS +DDProxyInitDriverObjects( + _Inout_ DRIVER_OBJECT* driverObject, + _In_ const UNICODE_STRING* registryPath, + _Out_ WDFDRIVER* pDriver, + _Out_ WDFDEVICE* pDevice + ) +{ + NTSTATUS status; + WDF_DRIVER_CONFIG config; + PWDFDEVICE_INIT pInit = NULL; + + WDF_DRIVER_CONFIG_INIT( + &config, + WDF_NO_EVENT_CALLBACK + ); + + config.DriverInitFlags |= WdfDriverInitNonPnpDriver; + config.EvtDriverUnload = EvtDriverUnload; + + status = WdfDriverCreate( + driverObject, + registryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + pDriver + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + pInit = WdfControlDeviceInitAllocate( + *pDriver, + &SDDL_DEVOBJ_KERNEL_ONLY + ); + + if (!pInit) + { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + WdfDeviceInitSetDeviceType( + pInit, + FILE_DEVICE_NETWORK + ); + + WdfDeviceInitSetCharacteristics( + pInit, + FILE_DEVICE_SECURE_OPEN, + FALSE + ); + + WdfDeviceInitSetCharacteristics( + pInit, + FILE_AUTOGENERATED_DEVICE_NAME, + TRUE + ); + + status = WdfDeviceCreate( + &pInit, + WDF_NO_OBJECT_ATTRIBUTES, + pDevice + ); + + if (!NT_SUCCESS(status)) + { + WdfDeviceInitFree(pInit); + goto Exit; + } + + WdfControlFinishInitializing(*pDevice); + +Exit: + return status; +} + + +NTSTATUS +DriverEntry( + DRIVER_OBJECT* driverObject, + UNICODE_STRING* registryPath + ) +{ + NTSTATUS status; + WDFDRIVER driver; + WDFDEVICE device; + WDFKEY configKey; + HANDLE threadHandle; + + // Request NX Non-Paged Pool when available + ExInitializeDriverRuntime(DrvRtPoolNxOptIn); + + status = DDProxyInitDriverObjects( + driverObject, + registryPath, + &driver, + &device + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = WdfDriverOpenParametersRegistryKey( + driver, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &configKey + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = DDProxyLoadConfig(configKey); + + if (!NT_SUCCESS(status)) + { + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto Exit; + } + + // + // To proxy UDP traffic, a new destination port or a pair of inspect and + // proxy ip address need to be pre-configured. To proxy UDP traffic, a + // pair of inspect and proxy ip addresses must be pre-configured. + // + if (configInspectUdp) + { + if ((configInspectDestPort == configNewDestPort) && + (((configInspectDestAddrV4 == NULL) || + (configNewDestAddrV4 == NULL)) && + ((configInspectDestAddrV6 == NULL) || + (configNewDestAddrV6 == NULL)))) + { + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto Exit; + } + } + else + { + if (((configInspectDestAddrV4 == NULL) || + (configNewDestAddrV4 == NULL)) && + ((configInspectDestAddrV6 == NULL) || + (configNewDestAddrV6 == NULL))) + { + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto Exit; + } + } + + status = FwpsInjectionHandleCreate( + AF_UNSPEC, + FWPS_INJECTION_TYPE_TRANSPORT, + &gInjectionHandle + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + InitializeListHead(&gFlowList); + KeInitializeSpinLock(&gFlowListLock); + + InitializeListHead(&gPacketQueue); + KeInitializeSpinLock(&gPacketQueueLock); + KeInitializeEvent( + &gPacketQueueEvent, + NotificationEvent, + FALSE + ); + + gWdmDevice = WdfDeviceWdmGetDeviceObject(device); + + status = DDProxyRegisterCallouts(gWdmDevice); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = PsCreateSystemThread( + &threadHandle, + THREAD_ALL_ACCESS, + NULL, + NULL, + NULL, + DDProxyWorker, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = ObReferenceObjectByHandle( + threadHandle, + 0, + NULL, + KernelMode, + &gThreadObj, + NULL + ); + NT_ASSERT(NT_SUCCESS(status)); + + ZwClose(threadHandle); + +Exit: + + if (!NT_SUCCESS(status)) + { + if (gEngineHandle != NULL) + { + DDProxyUnregisterCallouts(); + } + if (gInjectionHandle != NULL) + { + FwpsInjectionHandleDestroy(gInjectionHandle); + } + } + + return status; +} diff --git a/network/trans/ddproxy/sys/DD_proxy.c b/network/trans/ddproxy/sys/DD_proxy.c new file mode 100644 index 00000000..a75e2cf9 --- /dev/null +++ b/network/trans/ddproxy/sys/DD_proxy.c @@ -0,0 +1,1039 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + This file implements the classifyFn, notifiFn, and flowDeleteFn callout + functions for the flow-established and datagram-data callouts. In addition + the system worker thread that performs the actual packet modifications + is also implemented here along with the eventing mechanisms shared between + the classify function and the worker thread. + + Packet modification is done out-of-band by a system worker thread using + the reference-drop-clone-modify-reinject mechanism. Therefore the sample + can serve as a base in scenarios where filtering/modification decision + cannot be made within the classifyFn() callout and instead must be made, + for example, by an user-mode application. + +Environment: + + Kernel mode + +--*/ + +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + +#include + +#include "DD_proxy.h" + +__inline +void +DDProxyFreePendedPacket( + _Inout_ __drv_freesMem(Mem) DD_PROXY_PENDED_PACKET* packet, + _Inout_opt_ __drv_freesMem(Mem) WSACMSGHDR* controlData + ) +{ + FwpsDereferenceNetBufferList(packet->netBufferList, FALSE); + DDProxyDereferenceFlowContext(packet->belongingFlow); + if (controlData != NULL) + { + ExFreePoolWithTag(controlData, DD_PROXY_CONTROL_DATA_POOL_TAG); + } + ExFreePoolWithTag(packet, DD_PROXY_PENDED_PACKET_POOL_TAG); +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +DDProxyFlowEstablishedClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#else + +void +DDProxyFlowEstablishedClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +/* ++ + + This is the classifyFn function of the flow-established callout. It + allocates flow context for the original and the proxy flow and associates + them with the indicated flow-id. This function also stores information + common to both flows in the context. The flow context is inserted into the + global flow list. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + BOOLEAN locked = FALSE; + + KLOCK_QUEUE_HANDLE flowListLockHandle; + + DD_PROXY_FLOW_CONTEXT* flowContextLocal = NULL; + + UNREFERENCED_PARAMETER(layerData); +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(classifyContext); +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(flowContext); + + flowContextLocal = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(DD_PROXY_FLOW_CONTEXT), + DD_PROXY_FLOW_CONTEXT_POOL_TAG + ); + + if (flowContextLocal == NULL) + { + status = STATUS_NO_MEMORY; + goto Exit; + } + + RtlZeroMemory(flowContextLocal, sizeof(DD_PROXY_FLOW_CONTEXT)); + + flowContextLocal->refCount = 1; + flowContextLocal->flowType = (DD_PROXY_FLOW_TYPE)(filter->context); + flowContextLocal->addressFamily = + (inFixedValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4) ? + AF_INET : AF_INET6; + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, + FWPS_METADATA_FIELD_FLOW_HANDLE)); + flowContextLocal->flowId = inMetaValues->flowHandle; + + // + // Note that since the consumer of the flow context is the datagram-data + // layer classifyFn, layerId and calloutId are set to those of DD and not + // flow-established. + // + flowContextLocal->layerId = + (flowContextLocal->addressFamily == AF_INET) ? + FWPS_LAYER_DATAGRAM_DATA_V4 : FWPS_LAYER_DATAGRAM_DATA_V6; + flowContextLocal->calloutId = + (flowContextLocal->addressFamily == AF_INET) ? + gCalloutIdV4 : gCalloutIdV6; + + if (flowContextLocal->addressFamily == AF_INET) + { + // Prefast thinks we are ignoring this return value. + // If driver is unloading, we give up and ignore it on purpose. + // Otherwise, we put the pointer onto the list, but we make it opaque + // by casting it as a UINT64, and this tricks Prefast. + flowContextLocal->ipv4LocalAddr = + RtlUlongByteSwap( + inFixedValues->incomingValue\ + [FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS].value.uint32 + ); + flowContextLocal->protocol = + inFixedValues->incomingValue\ + [FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8; + } + else + { + RtlCopyMemory( + (UINT8*)&flowContextLocal->localAddr, + inFixedValues->incomingValue\ + [FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_LOCAL_ADDRESS].value.byteArray16, + sizeof(FWP_BYTE_ARRAY16) + ); + flowContextLocal->protocol = + inFixedValues->incomingValue\ + [FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL].value.uint8; + } + + if (flowContextLocal->flowType == DD_PROXY_FLOW_ORIGINAL) + { + flowContextLocal->toRemoteAddr = + (flowContextLocal->addressFamily == AF_INET) ? + configNewDestAddrV4 : configNewDestAddrV6; + // host-order -> network-order conversion for port. + flowContextLocal->toRemotePort = RtlUshortByteSwap(configNewDestPort); + } + else + { + NT_ASSERT(flowContextLocal->flowType == DD_PROXY_FLOW_PROXY); + flowContextLocal->toRemoteAddr = + (flowContextLocal->addressFamily == AF_INET) ? + configInspectDestAddrV4 : configInspectDestAddrV6; + // host-order -> network-order conversion for port. + // See PREfast comments above. Opaque pointer tricks PREfast. + flowContextLocal->toRemotePort = RtlUshortByteSwap(configInspectDestPort); + } + if ((flowContextLocal->toRemoteAddr != NULL) && + (flowContextLocal->addressFamily == AF_INET)) + { + // host-order -> network-order conversion for Ipv4 address. + // See PREfast comments above. Opaque pointer tricks PREfast. + flowContextLocal->ipv4NetworkOrderStorage = + RtlUlongByteSwap(*(ULONG*)(flowContextLocal->toRemoteAddr)); + flowContextLocal->toRemoteAddr = + (UINT8*)&flowContextLocal->ipv4NetworkOrderStorage; + } + + KeAcquireInStackQueuedSpinLock( + &gFlowListLock, + &flowListLockHandle + ); + + locked = TRUE; + + if (!gDriverUnloading) + { + // + // Associate DD_PROXY_FLOW_CONTEXT with the indicated flow-id to be + // accessible by the Datagram-Data classifyFn. (i.e. when a packet + // belongs to the same flow being classified at Datagram-Data layer, + // DD_PROXY_FLOW_CONTEXT will be passed onto the classifyFn as the + // "flowContext" parameter. + // + status = FwpsFlowAssociateContext( + flowContextLocal->flowId, + flowContextLocal->layerId, + flowContextLocal->calloutId, + (UINT64)flowContextLocal + ); + if(!NT_SUCCESS(status)) + { + goto Exit; + } + + InsertHeadList(&gFlowList, &flowContextLocal->listEntry); + flowContextLocal = NULL; // ownership transferred + } + + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + +Exit: + + if(locked) + { + KeReleaseInStackQueuedSpinLock(&flowListLockHandle); + } + + if (flowContextLocal != NULL) + { + ExFreePoolWithTag(flowContextLocal, DD_PROXY_FLOW_CONTEXT_POOL_TAG); + } + + if(!NT_SUCCESS(status)) + { + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + return; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +DDProxyClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#else + +void +DDProxyClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +/* ++ + + This is the classifyFn function of the datagram-data callout. It + allocates a packet structure to store the classify and meta data and + it references the net buffer list for out-of-band modification and + re-injection. The packet structure will be queued to the global packet + queue. The worker thread will then be signaled, if idle, to process + the queue. + +-- */ +{ + DD_PROXY_PENDED_PACKET* packet = NULL; + DD_PROXY_FLOW_CONTEXT* flowContextLocal = (DD_PROXY_FLOW_CONTEXT*)(DWORD_PTR)flowContext; + + FWPS_PACKET_INJECTION_STATE packetState; + KLOCK_QUEUE_HANDLE packetQueueLockHandle; + BOOLEAN signalWorkerThread; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(classifyContext); +#endif + UNREFERENCED_PARAMETER(filter); + + _Analysis_assume_(layerData != NULL); + + // + // We don't have the necessary right to alter the packet. + // + if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0) + { + goto Exit; + } + + // + // We don't re-inspect packets that we've inspected earlier. + // + packetState = FwpsQueryPacketInjectionState( + gInjectionHandle, + layerData, + NULL + ); + + if ((packetState == FWPS_PACKET_INJECTED_BY_SELF) || + (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)) + { + classifyOut->actionType = FWP_ACTION_PERMIT; + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + goto Exit; + } + + packet = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(DD_PROXY_PENDED_PACKET), + DD_PROXY_PENDED_PACKET_POOL_TAG + ); + + if (packet == NULL) + { + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + goto Exit; + } + + RtlZeroMemory(packet, sizeof(DD_PROXY_PENDED_PACKET)); + + NT_ASSERT(flowContextLocal != NULL); + + packet->belongingFlow = flowContextLocal; + DDProxyReferenceFlowContext(packet->belongingFlow); + if (flowContextLocal->addressFamily == AF_INET) + { + NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4); + packet->direction = + inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].\ + value.uint32; + } + else + { + NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6); + packet->direction = + inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_DIRECTION].\ + value.uint32; + } + packet->netBufferList = layerData; + + // + // Reference the net buffer list to make it accessible outside of + // classifyFn. + // + FwpsReferenceNetBufferList(packet->netBufferList, TRUE); + + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, + FWPS_METADATA_FIELD_COMPARTMENT_ID)); + packet->compartmentId = inMetaValues->compartmentId; + + if (packet->direction == FWP_DIRECTION_OUTBOUND) + { + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT( + inMetaValues, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)); + packet->endpointHandle = inMetaValues->transportEndpointHandle; + + if (flowContextLocal->addressFamily == AF_INET) + { + // See PREfast comments above. Opaque pointer tricks PREfast. + packet->ipv4RemoteAddr = + RtlUlongByteSwap( /* host-order -> network-order conversion */ + inFixedValues->incomingValue\ + [FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_ADDRESS].value.uint32 + ); + } + else + { + RtlCopyMemory( + (UINT8*)&packet->remoteAddr, + inFixedValues->incomingValue\ + [FWPS_FIELD_DATAGRAM_DATA_V6_IP_REMOTE_ADDRESS].value.byteArray16, + sizeof(FWP_BYTE_ARRAY16) + ); + + } + packet->remoteScopeId = inMetaValues->remoteScopeId; + + if (FWPS_IS_METADATA_FIELD_PRESENT( + inMetaValues, + FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA)) + { + NT_ASSERT(inMetaValues->controlDataLength > 0); + + packet->controlData = ExAllocatePoolWithTag( + NonPagedPool, + inMetaValues->controlDataLength, + DD_PROXY_CONTROL_DATA_POOL_TAG + ); + if (packet->controlData == NULL) + { + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + goto Exit; + } + + RtlCopyMemory( + packet->controlData, + inMetaValues->controlData, + inMetaValues->controlDataLength + ); + + packet->controlDataLength = inMetaValues->controlDataLength; + } + } + else + { + NT_ASSERT(packet->direction == FWP_DIRECTION_INBOUND); + + if (flowContextLocal->addressFamily == AF_INET) + { + NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4); + packet->interfaceIndex = + inFixedValues->incomingValue\ + [FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_INDEX].value.uint32; + packet->subInterfaceIndex = + inFixedValues->incomingValue\ + [FWPS_FIELD_DATAGRAM_DATA_V4_SUB_INTERFACE_INDEX].value.uint32; + } + else + { + NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6); + packet->interfaceIndex = + inFixedValues->incomingValue\ + [FWPS_FIELD_DATAGRAM_DATA_V6_INTERFACE_INDEX].value.uint32; + packet->subInterfaceIndex = + inFixedValues->incomingValue\ + [FWPS_FIELD_DATAGRAM_DATA_V6_SUB_INTERFACE_INDEX].value.uint32; + } + + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT( + inMetaValues, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)); + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT( + inMetaValues, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)); + packet->ipHeaderSize = inMetaValues->ipHeaderSize; + packet->transportHeaderSize = inMetaValues->transportHeaderSize; + + packet->nblOffset = + NET_BUFFER_DATA_OFFSET(NET_BUFFER_LIST_FIRST_NB(packet->netBufferList)); + } + + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + if (!gDriverUnloading) + { + signalWorkerThread = IsListEmpty(&gPacketQueue); + + InsertTailList(&gPacketQueue, &packet->listEntry); + packet = NULL; // ownership transferred + + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + } + else + { + // + // Driver is being unloaded, permit any incoming packets. + // + signalWorkerThread = FALSE; + + classifyOut->actionType = FWP_ACTION_PERMIT; + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + } + + if (signalWorkerThread) + { + KeSetEvent( + &gPacketQueueEvent, + 0, + FALSE + ); + } + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + +Exit: + + if (packet != NULL) + { + DDProxyFreePendedPacket(packet, packet->controlData); + } + + return; +} + +NTSTATUS +DDProxyFlowEstablishedNotify( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter + ) +{ + UNREFERENCED_PARAMETER(notifyType); + UNREFERENCED_PARAMETER(filterKey); + UNREFERENCED_PARAMETER(filter); + + return STATUS_SUCCESS; +} + +NTSTATUS +DDProxyNotify( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter + ) +{ + UNREFERENCED_PARAMETER(notifyType); + UNREFERENCED_PARAMETER(filterKey); + UNREFERENCED_PARAMETER(filter); + + return STATUS_SUCCESS; +} + +void +DDProxyFlowDelete( + _In_ UINT16 layerId, + _In_ UINT32 calloutId, + _In_ UINT64 flowContext + ) +/* ++ + + This is the flowDeleteFn function of the datagram-data callout. It + removes the flow context from the global flow list and dereference the + context. + +-- */ +{ + DD_PROXY_FLOW_CONTEXT* flowContextLocal = (DD_PROXY_FLOW_CONTEXT*)(DWORD_PTR)flowContext; + + KLOCK_QUEUE_HANDLE flowListLockHandle; + + UNREFERENCED_PARAMETER(layerId); + UNREFERENCED_PARAMETER(calloutId); + + KeAcquireInStackQueuedSpinLock( + &gFlowListLock, + &flowListLockHandle + ); + + if (!flowContextLocal->deleted) + { + RemoveEntryList(&flowContextLocal->listEntry); + } + + KeReleaseInStackQueuedSpinLock(&flowListLockHandle); + + DDProxyDereferenceFlowContext(flowContextLocal); +} + +typedef struct UDP_HEADER_ { + UINT16 srcPort; + UINT16 destPort; + UINT16 length; + UINT16 checksum; +} UDP_HEADER; + +void DDProxyInjectComplete( + _Inout_ void* context, + _Inout_ NET_BUFFER_LIST* netBufferList, + _In_ BOOLEAN dispatchLevel + ) +{ + DD_PROXY_PENDED_PACKET* packet = context; + UNREFERENCED_PARAMETER(dispatchLevel); + + FwpsFreeCloneNetBufferList(netBufferList, 0); + + DDProxyFreePendedPacket(packet, packet->controlData); +} + +NTSTATUS +DDProxyCloneModifyReinjectOutbound( + _In_ DD_PROXY_PENDED_PACKET* packet + ) +/* ++ + + This function clones the outbound net buffer list and, if needed, + modifies the destination port of all indicated packets (i.e. NET_BUFFER) + and/or send-injects the clone to a new destination address. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + NET_BUFFER_LIST* clonedNetBufferList = NULL; + UDP_HEADER* udpHeader; + FWPS_TRANSPORT_SEND_PARAMS sendArgs = {0}; + + status = FwpsAllocateCloneNetBufferList( + packet->netBufferList, + NULL, + NULL, + 0, + &clonedNetBufferList + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + // + // Check to see if port modification is required. + // + if ((packet->belongingFlow->protocol == IPPROTO_UDP) && + (packet->belongingFlow->toRemotePort != 0)) + { + NET_BUFFER* netBuffer; + + // + // The data offset of outbound transport packets is the beginning of + // transport header (e.g. UDP header). The IP header has not yet been + // constructed at Datagram-Data (or outbound Transport) layer. + // + // Note the packet offset is inherited by the clone. + // + + // + // Outbound net buffer list can contain more than one net buffer (e.g. + // one UDP packet). + // + + for (netBuffer = NET_BUFFER_LIST_FIRST_NB(clonedNetBufferList); + netBuffer != NULL; + netBuffer = NET_BUFFER_NEXT_NB(netBuffer)) + { + udpHeader = NdisGetDataBuffer( + netBuffer, + sizeof(UDP_HEADER), + NULL, + sizeof(UINT16), + 0 + ); + NT_ASSERT(udpHeader != NULL); // We can assume UDP header in a net buffer + // is contiguous and 2-byte aligned. + _Analysis_assume_(udpHeader != NULL); + + udpHeader->destPort = packet->belongingFlow->toRemotePort; + udpHeader->checksum = 0; + } + } + + // + // Determine whehter we need to proxy the destination address. If not, + // we set the remoteAddress to the same address that was initially + // classified. + // + sendArgs.remoteAddress = + (packet->belongingFlow->toRemoteAddr ? packet->belongingFlow->toRemoteAddr + : (UINT8*)&packet->remoteAddr); + sendArgs.remoteScopeId = packet->remoteScopeId; + sendArgs.controlData = packet->controlData; + sendArgs.controlDataLength = packet->controlDataLength; + + // + // Send-inject the modified net buffer list to the new destination address. + // + + status = FwpsInjectTransportSendAsync( + gInjectionHandle, + NULL, + packet->endpointHandle, + 0, + &sendArgs, + packet->belongingFlow->addressFamily, + packet->compartmentId, + clonedNetBufferList, + DDProxyInjectComplete, + packet + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + clonedNetBufferList = NULL; // ownership transferred to the + // completion function. + +Exit: + + if (clonedNetBufferList != NULL) + { + FwpsFreeCloneNetBufferList(clonedNetBufferList, 0); + } + + return status; +} + +NTSTATUS +DDProxyCloneModifyReinjectInbound( + _In_ DD_PROXY_PENDED_PACKET* packet + ) +/* ++ + + This function clones the inbound net buffer list and, if needed, + modifies the source port and/or source address and receive-injects + the clone back to the tcpip stack. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + NET_BUFFER_LIST* clonedNetBufferList = NULL; + NET_BUFFER* netBuffer; + UDP_HEADER* udpHeader; + ULONG nblOffset; + NDIS_STATUS ndisStatus; + + // + // For inbound net buffer list, we can assume it contains only one + // net buffer. + // + netBuffer = NET_BUFFER_LIST_FIRST_NB(packet->netBufferList); + + nblOffset = NET_BUFFER_DATA_OFFSET(netBuffer); + + // + // The TCP/IP stack could have retreated the net buffer list by the + // transportHeaderSize amount; detect the condition here to avoid + // retreating twice. + // + if (nblOffset != packet->nblOffset) + { + NT_ASSERT(packet->nblOffset - nblOffset == packet->transportHeaderSize); + packet->transportHeaderSize = 0; + } + + // + // Adjust the net buffer list offset to the start of the IP header. + // + ndisStatus = NdisRetreatNetBufferDataStart( + netBuffer, + packet->ipHeaderSize + packet->transportHeaderSize, + 0, + NULL + ); + _Analysis_assume_(ndisStatus == NDIS_STATUS_SUCCESS); + + // + // Note that the clone will inherit the original net buffer list's offset. + // + + status = FwpsAllocateCloneNetBufferList( + packet->netBufferList, + NULL, + NULL, + 0, + &clonedNetBufferList + ); + + // + // Undo the adjustment on the original net buffer list. + // + + NdisAdvanceNetBufferDataStart( + netBuffer, + packet->ipHeaderSize + packet->transportHeaderSize, + FALSE, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + // + // Check to see if port modification is required. + // + if ((packet->belongingFlow->protocol == IPPROTO_UDP) && + (packet->belongingFlow->toRemotePort != 0)) + { + netBuffer = NET_BUFFER_LIST_FIRST_NB(clonedNetBufferList); + + // + // Advance to the beginning of the transport header (i.e. UDP header). + // + NdisAdvanceNetBufferDataStart( + netBuffer, + packet->ipHeaderSize, + FALSE, + NULL + ); + + udpHeader = NdisGetDataBuffer( + netBuffer, + sizeof(UDP_HEADER), + NULL, + sizeof(UINT16), + 0 + ); + NT_ASSERT(udpHeader != NULL); // We can assume UDP header in a net buffer + // is contiguous and 2-byte aligned. + _Analysis_assume_(udpHeader != NULL); + + udpHeader->destPort = + packet->belongingFlow->toRemotePort; + // This is our new source port -- or + // the destination port of the original + // outbound traffic. + udpHeader->checksum = 0; + + // + // Undo the advance. Net buffer list needs to be positioned at the + // beginning of IP header for address modification and/or receive- + // injection. + // + ndisStatus = NdisRetreatNetBufferDataStart( + netBuffer, + packet->ipHeaderSize, + 0, + NULL + ); + _Analysis_assume_(ndisStatus == NDIS_STATUS_SUCCESS); + + } + + if (packet->belongingFlow->toRemoteAddr != NULL) + { + status = FwpsConstructIpHeaderForTransportPacket( + clonedNetBufferList, + packet->ipHeaderSize, + packet->belongingFlow->addressFamily, + packet->belongingFlow->toRemoteAddr, + // This is our new source address -- + // or the destination address of the + // original outbound traffic. + (UINT8*)&packet->belongingFlow->localAddr, + // This is the destination address of + // the clone -- or the source of the + // original outbound traffic. + packet->belongingFlow->protocol, + 0, + NULL, + 0, + 0, + NULL, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + } + + status = FwpsInjectTransportReceiveAsync( + gInjectionHandle, + NULL, + NULL, + 0, + packet->belongingFlow->addressFamily, + packet->compartmentId, + packet->interfaceIndex, + packet->subInterfaceIndex, + clonedNetBufferList, + DDProxyInjectComplete, + packet + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + clonedNetBufferList = NULL; // ownership transferred to the + // completion function. + +Exit: + + if (clonedNetBufferList != NULL) + { + FwpsFreeCloneNetBufferList(clonedNetBufferList, 0); + } + + return status; +} + +void +DDProxyWorker( + _In_ void* StartContext + ) +/* ++ + + This worker thread waits for the packet queue event when the queue is + empty; and it will be woken up when there are packets queued needing to + be proxied to or from the new destination address/port. Once awaking, + It will run in a loop to clone-modify-reinject packets until the packet + queue is exhausted (and it will go to sleep waiting for more work). + + The worker thread will end once it detected the driver is unloading. + +-- */ +{ + DD_PROXY_PENDED_PACKET* packet; + LIST_ENTRY* listEntry; + KLOCK_QUEUE_HANDLE packetQueueLockHandle; + + UNREFERENCED_PARAMETER(StartContext); + + for(;;) + { + KeWaitForSingleObject( + &gPacketQueueEvent, + Executive, + KernelMode, + FALSE, + NULL + ); + + if (gDriverUnloading) + { + break; + } + + NT_ASSERT(!IsListEmpty(&gPacketQueue)); + + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + listEntry = RemoveHeadList(&gPacketQueue); + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + + packet = CONTAINING_RECORD( + listEntry, + DD_PROXY_PENDED_PACKET, + listEntry + ); + + if (!packet->belongingFlow->deleted) + { + NTSTATUS status; + + if (packet->direction == FWP_DIRECTION_OUTBOUND) + { + status = DDProxyCloneModifyReinjectOutbound(packet); + } + else + { + status = DDProxyCloneModifyReinjectInbound(packet); + } + + if (NT_SUCCESS(status)) + { + packet = NULL; // ownership transferred. + } + } + + if (packet != NULL) + { + DDProxyFreePendedPacket(packet, packet->controlData); + } + + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + if (IsListEmpty(&gPacketQueue) && !gDriverUnloading) + { + KeClearEvent(&gPacketQueueEvent); + } + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + } + + NT_ASSERT(gDriverUnloading); + + // + // Discard all the pended packets if driver is being unloaded. + // + + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + while (!IsListEmpty(&gPacketQueue)) + { + listEntry = RemoveHeadList(&gPacketQueue); + + packet = CONTAINING_RECORD( + listEntry, + DD_PROXY_PENDED_PACKET, + listEntry + ); + + DDProxyFreePendedPacket(packet, packet->controlData); + } + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + PsTerminateSystemThread(STATUS_SUCCESS); + + +} diff --git a/network/trans/ddproxy/sys/DD_proxy.h b/network/trans/ddproxy/sys/DD_proxy.h new file mode 100644 index 00000000..197b0ff9 --- /dev/null +++ b/network/trans/ddproxy/sys/DD_proxy.h @@ -0,0 +1,251 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + This header files declares common data types and function prototypes used + throughout the Datagram-Data transparent proxy sample. + +Environment: + + Kernel mode + +--*/ + +#ifndef _DD_PROXY_H_ +#define _DD_PROXY_H_ + +typedef enum DD_PROXY_FLOW_TYPE_ +{ + DD_PROXY_FLOW_ORIGINAL, + DD_PROXY_FLOW_PROXY +} DD_PROXY_FLOW_TYPE; + +// +// DD_PROXY_FLOW_CONTEXT is the object type we used to stored information +// specific flow. This callout driver maintains two kind of flow contexts -- +// the original flow and the flow being proxied to. +// + +typedef struct DD_PROXY_FLOW_CONTEXT_ +{ + LIST_ENTRY listEntry; + + BOOLEAN deleted; + + DD_PROXY_FLOW_TYPE flowType; + ADDRESS_FAMILY addressFamily; + + #pragma warning(push) + #pragma warning(disable: 4201) //NAMELESS_STRUCT_UNION + union + { + FWP_BYTE_ARRAY16 localAddr; + UINT32 ipv4LocalAddr; + }; + #pragma warning(pop) + + + UINT8 protocol; + + UINT64 flowId; + UINT16 layerId; + UINT32 calloutId; + + UINT32 ipv4NetworkOrderStorage; + + // + // For DD_PROXY_FLOW_ORIGINAL type, toRemote* is the new address/port + // we are proxing to. For DD_PROXY_FLOW_PROXY type, it is the address/ + // port that we will need to revert to. + // + UINT8* toRemoteAddr; + UINT16 toRemotePort; + + LONG refCount; +} DD_PROXY_FLOW_CONTEXT; + +// +// DD_PROXY_PENDED_PACKET is the object type we used to store all information +// needed for out-of-band packet modification and re-injection. This type +// also points back to the flow context the packet belongs to. + +typedef struct DD_PROXY_PENDED_PACKET_ +{ + LIST_ENTRY listEntry; + + DD_PROXY_FLOW_CONTEXT* belongingFlow; + FWP_DIRECTION direction; + + // + // Common fields for inbound and outbound traffic. + // + NET_BUFFER_LIST* netBufferList; + COMPARTMENT_ID compartmentId; + + // + // Data fields for outbound packet re-injection. + // + UINT64 endpointHandle; + + #pragma warning(push) + #pragma warning(disable: 4201) //NAMELESS_STRUCT_UNION + union + { + FWP_BYTE_ARRAY16 remoteAddr; + UINT32 ipv4RemoteAddr; + }; + #pragma warning(pop) + + SCOPE_ID remoteScopeId; + WSACMSGHDR* controlData; + ULONG controlDataLength; + + // + // Data fields for inbound packet re-injection. + // + ULONG nblOffset; + UINT32 ipHeaderSize; + UINT32 transportHeaderSize; + IF_INDEX interfaceIndex; + IF_INDEX subInterfaceIndex; +} DD_PROXY_PENDED_PACKET; + +// +// Pooltags used by this callout driver. +// +#define DD_PROXY_FLOW_CONTEXT_POOL_TAG 'olfD' +#define DD_PROXY_PENDED_PACKET_POOL_TAG 'kppD' +#define DD_PROXY_CONTROL_DATA_POOL_TAG 'dcdD' + +// +// Shared global data. +// +extern UINT16 configInspectDestPort; +extern UINT8* configInspectDestAddrV4; +extern UINT8* configInspectDestAddrV6; + +extern UINT16 configNewDestPort; +extern UINT8* configNewDestAddrV4; +extern UINT8* configNewDestAddrV6; + +extern HANDLE gInjectionHandle; + +extern LIST_ENTRY gFlowList; +extern KSPIN_LOCK gFlowListLock; + +extern LIST_ENTRY gPacketQueue; +extern KSPIN_LOCK gPacketQueueLock; +extern KEVENT gPacketQueueEvent; + +extern UINT32 gCalloutIdV4; +extern UINT32 gCalloutIdV6; + +extern BOOLEAN gDriverUnloading; + +// +// Utility functions +// + +__inline void +DDProxyReferenceFlowContext( + _Inout_ DD_PROXY_FLOW_CONTEXT* flowContext + ) +{ + NT_ASSERT(flowContext->refCount > 0); + InterlockedIncrement(&flowContext->refCount); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +__inline +void +DDProxyDereferenceFlowContext( + _Inout_ DD_PROXY_FLOW_CONTEXT* flowContext + ) +{ + NT_ASSERT(flowContext->refCount > 0); + InterlockedDecrement(&flowContext->refCount); + if (flowContext->refCount == 0) + { + ExFreePoolWithTag(flowContext, DD_PROXY_FLOW_CONTEXT_POOL_TAG); + } +} + +// +// Shared function prototypes +// + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +DDProxyFlowEstablishedClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +void +DDProxyClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +#else + +void +DDProxyFlowEstablishedClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +void +DDProxyClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +void +DDProxyFlowDelete( + _In_ UINT16 layerId, + _In_ UINT32 calloutId, + _In_ UINT64 flowContext + ); + +NTSTATUS +DDProxyFlowEstablishedNotify( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter + ); + + +NTSTATUS +DDProxyNotify( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter + ); + +KSTART_ROUTINE DDProxyWorker; + +#endif // _DD_PROXY_H_ diff --git a/network/trans/ddproxy/sys/ddproxy.inf b/network/trans/ddproxy/sys/ddproxy.inf new file mode 100644 index 00000000..dd504967 --- /dev/null +++ b/network/trans/ddproxy/sys/ddproxy.inf @@ -0,0 +1,63 @@ +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved +;;; +;;; Abstract: +;;; DatagramData Proxy Callout sample driver install configuration. +;;; + +[Version] + Signature = "$Windows NT$" + Class = WFPCALLOUTS + ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} + Provider = %Contoso% + CatalogFile = DDProxy.cat + DriverVer = 11/24/2014,14.24.55.836 + +[SourceDisksNames] + 1 = %DDProxyDisk%,,,"" + +[SourceDisksFiles] + DDProxy.sys = 1,, + +[DestinationDirs] + DefaultDestDir = 12 ; %WinDir%\System32\Drivers + DDProxy.DriverFiles = 12 ; %WinDir%\System32\Drivers + +[DefaultInstall] + OptionDesc = %DDProxyServiceDesc% + CopyFiles = DDProxy.DriverFiles + +[DefaultInstall.Services] + AddService = %DDProxyServiceName%,,DDProxy.Service + +[DefaultUninstall] + DelFiles = DDProxy.DriverFiles + +[DefaultUninstall.Services] + DelService = %DDProxyServiceName%,0x200 ; SPSVCINST_STOPSERVICE + DelReg = DDProxy.DelRegistry + +[DDProxy.DriverFiles] + DDProxy.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY + +[DDProxy.Service] + DisplayName = %DDProxyServiceName% + Description = %DDProxyServiceDesc% + ServiceType = 1 ; SERVICE_KERNEL_DRIVER + StartType = 3 ; SERVICE_DEMAND_START + ErrorControl = 1 ; SERVICE_ERROR_NORMAL + ServiceBinary = %12%\DDProxy.sys ; %WinDir%\System32\Drivers\DDProxy.sys + AddReg = DDProxy.AddRegistry + +[DDProxy.AddRegistry] + HKR,"Parameters","DestinationAddressToIntercept",0x00000000,"10.0.0.1" ; FLG_ADDREG_TYPE_SZ + HKR,"Parameters","NewDestinationAddress",0x00000000,"10.0.0.2" ; FLG_ADDREG_TYPE_SZ + +[DDProxy.DelRegistry] + HKR,"Parameters",,, + +[Strings] + Contoso = "Contoso Ltd." + DDProxyDisk = "DatagramData Proxy Installation Disk" + DDProxyServiceDesc = "DatagramData Proxy Callout Driver" + DDProxyServiceName = "DDProxy" \ No newline at end of file diff --git a/network/trans/ddproxy/sys/ddproxy.vcxproj b/network/trans/ddproxy/sys/ddproxy.vcxproj new file mode 100644 index 00000000..9fd6e6af --- /dev/null +++ b/network/trans/ddproxy/sys/ddproxy.vcxproj @@ -0,0 +1,185 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {F5ED1745-0947-474A-924D-CB5D3D2D6C5E} + $(MSBuildProjectName) + 1 + Debug + Win32 + {91B8C3EC-EC68-40D0-BDAE-FECDC2A5A69E} + + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + ddproxy + + + ddproxy + + + ddproxy + + + ddproxy + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters b/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters new file mode 100644 index 00000000..9728a6bb --- /dev/null +++ b/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters @@ -0,0 +1,29 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {667070D4-ABD3-4134-83D9-C2E8E3B4D990} + + + h;hpp;hxx;hm;inl;inc;xsd + {55D8461C-9BC4-4CBA-8364-1C9E8628AD2E} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {89C687B6-C04D-48DD-BFC4-36AB091E7E94} + + + inf;inv;inx;mof;mc; + {3E15A537-E520-445D-AFC4-4CFCEF6234E6} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/network/trans/inspect/ReadMe.md b/network/trans/inspect/ReadMe.md new file mode 100644 index 00000000..a288e694 --- /dev/null +++ b/network/trans/inspect/ReadMe.md @@ -0,0 +1,55 @@ +Windows Filtering Platform Traffic Inspection Sample +==================================================== + +This sample driver demonstrates the traffic inspection capabilities of the Windows Filtering Platform (WFP). + +The sample driver consists of a kernel-mode Windows Filtering Platform (WFP) callout driver (Inspect.sys) that intercepts all transport layer traffic (for example, Transmission Control Protocol (TCP), User Datagram Protocol (UDP), and nonerror Internet Control Message Protocol (ICMP)) sent to or received from a configurable remote peer and queues then to a worker thread for out-of-band processing. + +Inspect.sys inspects inbound and outbound connections and all packets that belong to those connections. Additionally, Inspect.sys demonstrates the special considerations that are required to be compatible with Internet Protocol security (IPsec) in Windows Vista and Windows Server 2008. + +Inspect.sys implements the `ClassifyFn` callout functions for the ALE Connect, Recv-Accept, and Transport callouts. In addition, the system worker thread that performs the actual packet inspection is also implemented along with the event mechanisms that are shared between the Classify function and the worker thread. + +Connect/Packet inspection is done out-of-band by a system worker thread by using the reference-drop-clone-reinject mechanism as well as the ALE pend/complete mechanism. Therefore, the sample can serve as a basis for scenarios in which a filtering decision cannot be made within the `classifyFn()` callout and instead must be made, for example, by a user-mode application. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Automatic deployment +-------------------- + +Before you automatically deploy a driver, you must provision the target computer. For instructions, see [Configuring a Computer for Driver Deployment, Testing, and Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/). After you have provisioned the target computer, continue with these steps: + +1. On the host computer, in Visual Studio, in Solution Explorer, right click **package** (lower case), and choose **Properties**. Navigate to **Configuration Properties \> Driver Install \> Deployment**. +2. Check **Enable deployment**, and check **Remove previous driver versions before deployment**. For **Target Computer Name**, select the name of a target computer that you provisioned previously. Select **Do not install**. Click **OK**. +3. On the **Build** menu, choose **Build Solution**. +4. On the target computer, navigate to DriverTest\\Drivers, and locate the file inspect.inf. Right click inspect.inf, and choose **Install**. + +Manual deployment +----------------- + +Before you manually deploy a driver, you must turn on test signing and install a certificate on the target computer. You also need to copy the [DevCon](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544707) tool to the target computer. For instructions, see [Preparing a Computer for Manual Driver Deployment](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265571). After you have prepared the target computer for manual deployment, continue with these steps: + +1. Copy all of the files in your driver package to a folder on the target computer (for example, c:\\WfpTrafficInspectionSamplePackage). +2. On the target computer, navigate to your driver package folder. Right click inspect.inf, and choose **Install** + +Create Registry values +---------------------- + +1. On the target computer, open Regedit, and navigate to this key: + + **HKLM**\\**System**\\**CurrentControlSet**\\**Services**\\**inspect**\\**Parameters** + +2. Create a REG\_DWORD entry named **BlockTraffic** and set it's value to 0 for permit or 1 to block. + +3. Create a REG\_SZ entry named **RemoteAddressToInspect**, and set it's value to an IPV4 or IPV6 address (example: 10.0.0.2). + +Start the inspect service +------------------------- + +On the target computer, open a Command Prompt window as Administrator, and enter **net start inspect**. (To stop the driver, enter **net stop inspect**.) + +Remarks +------- + +For more information on creating a Windows Filtering Platform Callout Driver, see [Windows Filtering Platform Callout Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff571068). + diff --git a/network/trans/inspect/inspect.sln b/network/trans/inspect/inspect.sln new file mode 100644 index 00000000..79901c54 --- /dev/null +++ b/network/trans/inspect/inspect.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inspect", "sys\inspect.vcxproj", "{43AC1739-03F1-4465-8A16-F5FCD06BC2E9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {43AC1739-03F1-4465-8A16-F5FCD06BC2E9}.Debug|Win32.ActiveCfg = Debug|Win32 + {43AC1739-03F1-4465-8A16-F5FCD06BC2E9}.Debug|Win32.Build.0 = Debug|Win32 + {43AC1739-03F1-4465-8A16-F5FCD06BC2E9}.Release|Win32.ActiveCfg = Release|Win32 + {43AC1739-03F1-4465-8A16-F5FCD06BC2E9}.Release|Win32.Build.0 = Release|Win32 + {43AC1739-03F1-4465-8A16-F5FCD06BC2E9}.Debug|x64.ActiveCfg = Debug|x64 + {43AC1739-03F1-4465-8A16-F5FCD06BC2E9}.Debug|x64.Build.0 = Debug|x64 + {43AC1739-03F1-4465-8A16-F5FCD06BC2E9}.Release|x64.ActiveCfg = Release|x64 + {43AC1739-03F1-4465-8A16-F5FCD06BC2E9}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/network/trans/inspect/sys/TL_drv.c b/network/trans/inspect/sys/TL_drv.c new file mode 100644 index 00000000..5186b69c --- /dev/null +++ b/network/trans/inspect/sys/TL_drv.c @@ -0,0 +1,927 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Transport Inspect Proxy Callout Driver Sample. + + This sample callout driver intercepts all transport layer traffic (e.g. + TCP, UDP, and non-error ICMP) sent to or receive from a (configurable) + remote peer and queue them to a worker thread for out-of-band processing. + The sample performs inspection of inbound and outbound connections as + well as all packets belong to those connections. In addition the sample + demonstrates special considerations required to be compatible with Windows + Vista and Windows Server 2008’s IpSec implementation. + + Inspection parameters are configurable via the following registry + values -- + + HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Inspect\Parameters + + o BlockTraffic (REG_DWORD) : 0 (permit, default); 1 (block) + o RemoteAddressToInspect (REG_SZ) : literal IPv4/IPv6 string + (e.g. “10.0.0.1”) + The sample is IP version agnostic. It performs inspection for + both IPv4 and IPv6 traffic. + +Environment: + + Kernel mode + +--*/ + +#include +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + +#include + +#include +#include +#include + +#include "inspect.h" + +#define INITGUID +#include + + +// +// Configurable parameters (addresses and ports are in host order) +// + +BOOLEAN configPermitTraffic = TRUE; + +UINT8* configInspectRemoteAddrV4 = NULL; +UINT8* configInspectRemoteAddrV6 = NULL; + +IN_ADDR remoteAddrStorageV4; +IN6_ADDR remoteAddrStorageV6; + +// +// Callout and sublayer GUIDs +// + +// bb6e405b-19f4-4ff3-b501-1a3dc01aae01 +DEFINE_GUID( + TL_INSPECT_OUTBOUND_TRANSPORT_CALLOUT_V4, + 0xbb6e405b, + 0x19f4, + 0x4ff3, + 0xb5, 0x01, 0x1a, 0x3d, 0xc0, 0x1a, 0xae, 0x01 +); +// cabf7559-7c60-46c8-9d3b-2155ad5cf83f +DEFINE_GUID( + TL_INSPECT_OUTBOUND_TRANSPORT_CALLOUT_V6, + 0xcabf7559, + 0x7c60, + 0x46c8, + 0x9d, 0x3b, 0x21, 0x55, 0xad, 0x5c, 0xf8, 0x3f +); +// 07248379-248b-4e49-bf07-24d99d52f8d0 +DEFINE_GUID( + TL_INSPECT_INBOUND_TRANSPORT_CALLOUT_V4, + 0x07248379, + 0x248b, + 0x4e49, + 0xbf, 0x07, 0x24, 0xd9, 0x9d, 0x52, 0xf8, 0xd0 +); +// 6d126434-ed67-4285-925c-cb29282e0e06 +DEFINE_GUID( + TL_INSPECT_INBOUND_TRANSPORT_CALLOUT_V6, + 0x6d126434, + 0xed67, + 0x4285, + 0x92, 0x5c, 0xcb, 0x29, 0x28, 0x2e, 0x0e, 0x06 +); +// 76b743d4-1249-4614-a632-6f9c4d08d25a +DEFINE_GUID( + TL_INSPECT_ALE_CONNECT_CALLOUT_V4, + 0x76b743d4, + 0x1249, + 0x4614, + 0xa6, 0x32, 0x6f, 0x9c, 0x4d, 0x08, 0xd2, 0x5a +); + +// ac80683a-5b84-43c3-8ae9-eddb5c0d23c2 +DEFINE_GUID( + TL_INSPECT_ALE_CONNECT_CALLOUT_V6, + 0xac80683a, + 0x5b84, + 0x43c3, + 0x8a, 0xe9, 0xed, 0xdb, 0x5c, 0x0d, 0x23, 0xc2 +); + +// 7ec7f7f5-0c55-4121-adc5-5d07d2ac0cef +DEFINE_GUID( + TL_INSPECT_ALE_RECV_ACCEPT_CALLOUT_V4, + 0x7ec7f7f5, + 0x0c55, + 0x4121, + 0xad, 0xc5, 0x5d, 0x07, 0xd2, 0xac, 0x0c, 0xef +); + +// b74ac2ed-4e71-4564-9975-787d5168a151 +DEFINE_GUID( + TL_INSPECT_ALE_RECV_ACCEPT_CALLOUT_V6, + 0xb74ac2ed, + 0x4e71, + 0x4564, + 0x99, 0x75, 0x78, 0x7d, 0x51, 0x68, 0xa1, 0x51 +); + +// 2e207682-d95f-4525-b966-969f26587f03 +DEFINE_GUID( + TL_INSPECT_SUBLAYER, + 0x2e207682, + 0xd95f, + 0x4525, + 0xb9, 0x66, 0x96, 0x9f, 0x26, 0x58, 0x7f, 0x03 +); + +// +// Callout driver global variables +// + +DEVICE_OBJECT* gWdmDevice; +WDFKEY gParametersKey; + +HANDLE gEngineHandle; +UINT32 gAleConnectCalloutIdV4, gOutboundTlCalloutIdV4; +UINT32 gAleRecvAcceptCalloutIdV4, gInboundTlCalloutIdV4; +UINT32 gAleConnectCalloutIdV6, gOutboundTlCalloutIdV6; +UINT32 gAleRecvAcceptCalloutIdV6, gInboundTlCalloutIdV6; + +HANDLE gInjectionHandle; + +LIST_ENTRY gConnList; +KSPIN_LOCK gConnListLock; +LIST_ENTRY gPacketQueue; +KSPIN_LOCK gPacketQueueLock; + +KEVENT gWorkerEvent; + +BOOLEAN gDriverUnloading = FALSE; +void* gThreadObj; + +// +// Callout driver implementation +// + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_UNLOAD TLInspectEvtDriverUnload; + +NTSTATUS +TLInspectLoadConfig( + _In_ const WDFKEY key + ) +{ + NTSTATUS status; + DECLARE_CONST_UNICODE_STRING(valueName, L"RemoteAddressToInspect"); + DECLARE_UNICODE_STRING_SIZE(value, INET6_ADDRSTRLEN); + + status = WdfRegistryQueryUnicodeString(key, &valueName, NULL, &value); + + if (NT_SUCCESS(status)) + { + PWSTR terminator; + // Defensively null-terminate the string + value.Length = min(value.Length, value.MaximumLength - sizeof(WCHAR)); + value.Buffer[value.Length/sizeof(WCHAR)] = UNICODE_NULL; + + status = RtlIpv4StringToAddressW( + value.Buffer, + TRUE, + &terminator, + &remoteAddrStorageV4 + ); + + if (NT_SUCCESS(status)) + { + remoteAddrStorageV4.S_un.S_addr = + RtlUlongByteSwap(remoteAddrStorageV4.S_un.S_addr); + configInspectRemoteAddrV4 = &remoteAddrStorageV4.S_un.S_un_b.s_b1; + } + else + { + status = RtlIpv6StringToAddressW( + value.Buffer, + &terminator, + &remoteAddrStorageV6 + ); + + if (NT_SUCCESS(status)) + { + configInspectRemoteAddrV6 = (UINT8*)(&remoteAddrStorageV6.u.Byte[0]); + } + } + } + + return status; +} + +NTSTATUS +TLInspectAddFilter( + _In_ const wchar_t* filterName, + _In_ const wchar_t* filterDesc, + _In_reads_opt_(16) const UINT8* remoteAddr, + _In_ UINT64 context, + _In_ const GUID* layerKey, + _In_ const GUID* calloutKey + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + FWPM_FILTER filter = {0}; + FWPM_FILTER_CONDITION filterConditions[3] = {0}; + UINT conditionIndex; + + filter.layerKey = *layerKey; + filter.displayData.name = (wchar_t*)filterName; + filter.displayData.description = (wchar_t*)filterDesc; + + filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; + filter.action.calloutKey = *calloutKey; + filter.filterCondition = filterConditions; + filter.subLayerKey = TL_INSPECT_SUBLAYER; + filter.weight.type = FWP_EMPTY; // auto-weight. + filter.rawContext = context; + + conditionIndex = 0; + + if (remoteAddr != NULL) + { + filterConditions[conditionIndex].fieldKey = + FWPM_CONDITION_IP_REMOTE_ADDRESS; + filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL; + + if (IsEqualGUID(layerKey, &FWPM_LAYER_ALE_AUTH_CONNECT_V4) || + IsEqualGUID(layerKey, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4) || + IsEqualGUID(layerKey, &FWPM_LAYER_INBOUND_TRANSPORT_V4) || + IsEqualGUID(layerKey, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4)) + { + filterConditions[conditionIndex].conditionValue.type = FWP_UINT32; + filterConditions[conditionIndex].conditionValue.uint32 = + *(UINT32*)remoteAddr; + } + else + { + filterConditions[conditionIndex].conditionValue.type = + FWP_BYTE_ARRAY16_TYPE; + filterConditions[conditionIndex].conditionValue.byteArray16 = + (FWP_BYTE_ARRAY16*)remoteAddr; + } + + conditionIndex++; + } + + filter.numFilterConditions = conditionIndex; + + status = FwpmFilterAdd( + gEngineHandle, + &filter, + NULL, + NULL); + + return status; +} + +NTSTATUS +TLInspectRegisterALEClassifyCallouts( + _In_ const GUID* layerKey, + _In_ const GUID* calloutKey, + _Inout_ void* deviceObject, + _Out_ UINT32* calloutId + ) +/* ++ + + This function registers callouts and filters at the following layers + to intercept inbound or outbound connect attempts. + + FWPM_LAYER_ALE_AUTH_CONNECT_V4 + FWPM_LAYER_ALE_AUTH_CONNECT_V6 + FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 + FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + FWPS_CALLOUT sCallout = {0}; + FWPM_CALLOUT mCallout = {0}; + + FWPM_DISPLAY_DATA displayData = {0}; + + BOOLEAN calloutRegistered = FALSE; + + sCallout.calloutKey = *calloutKey; + + if (IsEqualGUID(layerKey, &FWPM_LAYER_ALE_AUTH_CONNECT_V4) || + IsEqualGUID(layerKey, &FWPM_LAYER_ALE_AUTH_CONNECT_V6)) + { + sCallout.classifyFn = TLInspectALEConnectClassify; + sCallout.notifyFn = TLInspectALEConnectNotify; + } + else + { + sCallout.classifyFn = TLInspectALERecvAcceptClassify; + sCallout.notifyFn = TLInspectALERecvAcceptNotify; + } + + status = FwpsCalloutRegister( + deviceObject, + &sCallout, + calloutId + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + calloutRegistered = TRUE; + + displayData.name = L"Transport Inspect ALE Classify Callout"; + displayData.description = + L"Intercepts inbound or outbound connect attempts"; + + mCallout.calloutKey = *calloutKey; + mCallout.displayData = displayData; + mCallout.applicableLayer = *layerKey; + + status = FwpmCalloutAdd( + gEngineHandle, + &mCallout, + NULL, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = TLInspectAddFilter( + L"Transport Inspect ALE Classify", + L"Intercepts inbound or outbound connect attempts", + (IsEqualGUID(layerKey, &FWPM_LAYER_ALE_AUTH_CONNECT_V4) || + IsEqualGUID(layerKey, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4)) ? + configInspectRemoteAddrV4 : configInspectRemoteAddrV6, + 0, + layerKey, + calloutKey + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + +Exit: + + if (!NT_SUCCESS(status)) + { + if (calloutRegistered) + { + FwpsCalloutUnregisterById(*calloutId); + *calloutId = 0; + } + } + + return status; +} + +NTSTATUS +TLInspectRegisterTransportCallouts( + _In_ const GUID* layerKey, + _In_ const GUID* calloutKey, + _Inout_ void* deviceObject, + _Out_ UINT32* calloutId + ) +/* ++ + + This function registers callouts and filters that intercept transport + traffic at the following layers -- + + FWPM_LAYER_OUTBOUND_TRANSPORT_V4 + FWPM_LAYER_OUTBOUND_TRANSPORT_V6 + FWPM_LAYER_INBOUND_TRANSPORT_V4 + FWPM_LAYER_INBOUND_TRANSPORT_V6 + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + FWPS_CALLOUT sCallout = {0}; + FWPM_CALLOUT mCallout = {0}; + + FWPM_DISPLAY_DATA displayData = {0}; + + BOOLEAN calloutRegistered = FALSE; + + sCallout.calloutKey = *calloutKey; + sCallout.classifyFn = TLInspectTransportClassify; + sCallout.notifyFn = TLInspectTransportNotify; + + status = FwpsCalloutRegister( + deviceObject, + &sCallout, + calloutId + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + calloutRegistered = TRUE; + + displayData.name = L"Transport Inspect Callout"; + displayData.description = L"Inspect inbound/outbound transport traffic"; + + mCallout.calloutKey = *calloutKey; + mCallout.displayData = displayData; + mCallout.applicableLayer = *layerKey; + + status = FwpmCalloutAdd( + gEngineHandle, + &mCallout, + NULL, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = TLInspectAddFilter( + L"Transport Inspect Filter (Outbound)", + L"Inspect inbound/outbound transport traffic", + (IsEqualGUID(layerKey, &FWPM_LAYER_OUTBOUND_TRANSPORT_V4) || + IsEqualGUID(layerKey, &FWPM_LAYER_INBOUND_TRANSPORT_V4))? + configInspectRemoteAddrV4 : configInspectRemoteAddrV6, + 0, + layerKey, + calloutKey + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + +Exit: + + if (!NT_SUCCESS(status)) + { + if (calloutRegistered) + { + FwpsCalloutUnregisterById(*calloutId); + *calloutId = 0; + } + } + + return status; +} + +NTSTATUS +TLInspectRegisterCallouts( + _Inout_ void* deviceObject + ) +/* ++ + + This function registers dynamic callouts and filters that intercept + transport traffic at ALE AUTH_CONNECT/AUTH_RECV_ACCEPT and + INBOUND/OUTBOUND transport layers. + + Callouts and filters will be removed during DriverUnload. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + FWPM_SUBLAYER TLInspectSubLayer; + + BOOLEAN engineOpened = FALSE; + BOOLEAN inTransaction = FALSE; + + FWPM_SESSION session = {0}; + + session.flags = FWPM_SESSION_FLAG_DYNAMIC; + + status = FwpmEngineOpen( + NULL, + RPC_C_AUTHN_WINNT, + NULL, + &session, + &gEngineHandle + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + engineOpened = TRUE; + + status = FwpmTransactionBegin(gEngineHandle, 0); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + inTransaction = TRUE; + + RtlZeroMemory(&TLInspectSubLayer, sizeof(FWPM_SUBLAYER)); + + TLInspectSubLayer.subLayerKey = TL_INSPECT_SUBLAYER; + TLInspectSubLayer.displayData.name = L"Transport Inspect Sub-Layer"; + TLInspectSubLayer.displayData.description = + L"Sub-Layer for use by Transport Inspect callouts"; + TLInspectSubLayer.flags = 0; + TLInspectSubLayer.weight = 0; // must be less than the weight of + // FWPM_SUBLAYER_UNIVERSAL to be + // compatible with Vista's IpSec + // implementation. + + status = FwpmSubLayerAdd(gEngineHandle, &TLInspectSubLayer, NULL); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + if (configInspectRemoteAddrV4 != NULL) + { + status = TLInspectRegisterALEClassifyCallouts( + &FWPM_LAYER_ALE_AUTH_CONNECT_V4, + &TL_INSPECT_ALE_CONNECT_CALLOUT_V4, + deviceObject, + &gAleConnectCalloutIdV4 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = TLInspectRegisterALEClassifyCallouts( + &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, + &TL_INSPECT_ALE_RECV_ACCEPT_CALLOUT_V4, + deviceObject, + &gAleRecvAcceptCalloutIdV4 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = TLInspectRegisterTransportCallouts( + &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, + &TL_INSPECT_OUTBOUND_TRANSPORT_CALLOUT_V4, + deviceObject, + &gOutboundTlCalloutIdV4 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = TLInspectRegisterTransportCallouts( + &FWPM_LAYER_INBOUND_TRANSPORT_V4, + &TL_INSPECT_INBOUND_TRANSPORT_CALLOUT_V4, + deviceObject, + &gInboundTlCalloutIdV4 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + } + + if (configInspectRemoteAddrV6 != NULL) + { + status = TLInspectRegisterALEClassifyCallouts( + &FWPM_LAYER_ALE_AUTH_CONNECT_V6, + &TL_INSPECT_ALE_CONNECT_CALLOUT_V6, + deviceObject, + &gAleConnectCalloutIdV6 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = TLInspectRegisterALEClassifyCallouts( + &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + &TL_INSPECT_ALE_RECV_ACCEPT_CALLOUT_V6, + deviceObject, + &gAleRecvAcceptCalloutIdV6 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = TLInspectRegisterTransportCallouts( + &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, + &TL_INSPECT_OUTBOUND_TRANSPORT_CALLOUT_V6, + deviceObject, + &gOutboundTlCalloutIdV6 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = TLInspectRegisterTransportCallouts( + &FWPM_LAYER_INBOUND_TRANSPORT_V6, + &TL_INSPECT_INBOUND_TRANSPORT_CALLOUT_V6, + deviceObject, + &gInboundTlCalloutIdV6 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + } + + status = FwpmTransactionCommit(gEngineHandle); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + inTransaction = FALSE; + +Exit: + + if (!NT_SUCCESS(status)) + { + if (inTransaction) + { + FwpmTransactionAbort(gEngineHandle); + _Analysis_assume_lock_not_held_(gEngineHandle); // Potential leak if "FwpmTransactionAbort" fails + } + if (engineOpened) + { + FwpmEngineClose(gEngineHandle); + gEngineHandle = NULL; + } + } + + return status; +} + +void +TLInspectUnregisterCallouts(void) +{ + FwpmEngineClose(gEngineHandle); + gEngineHandle = NULL; + + FwpsCalloutUnregisterById(gOutboundTlCalloutIdV6); + FwpsCalloutUnregisterById(gOutboundTlCalloutIdV4); + FwpsCalloutUnregisterById(gInboundTlCalloutIdV6); + FwpsCalloutUnregisterById(gInboundTlCalloutIdV4); + + FwpsCalloutUnregisterById(gAleConnectCalloutIdV6); + FwpsCalloutUnregisterById(gAleConnectCalloutIdV4); + FwpsCalloutUnregisterById(gAleRecvAcceptCalloutIdV6); + FwpsCalloutUnregisterById(gAleRecvAcceptCalloutIdV4); +} + +_Function_class_(EVT_WDF_DRIVER_UNLOAD) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +void +TLInspectEvtDriverUnload( + _In_ WDFDRIVER driverObject + ) +{ + + KLOCK_QUEUE_HANDLE connListLockHandle; + KLOCK_QUEUE_HANDLE packetQueueLockHandle; + + UNREFERENCED_PARAMETER(driverObject); + + KeAcquireInStackQueuedSpinLock( + &gConnListLock, + &connListLockHandle + ); + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + gDriverUnloading = TRUE; + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + KeReleaseInStackQueuedSpinLock(&connListLockHandle); + + if (IsListEmpty(&gConnList) && IsListEmpty(&gPacketQueue)) + { + KeSetEvent( + &gWorkerEvent, + IO_NO_INCREMENT, + FALSE + ); + } + + NT_ASSERT(gThreadObj != NULL); + + KeWaitForSingleObject( + gThreadObj, + Executive, + KernelMode, + FALSE, + NULL + ); + + ObDereferenceObject(gThreadObj); + + TLInspectUnregisterCallouts(); + + FwpsInjectionHandleDestroy(gInjectionHandle); +} + +NTSTATUS +TLInspectInitDriverObjects( + _Inout_ DRIVER_OBJECT* driverObject, + _In_ const UNICODE_STRING* registryPath, + _Out_ WDFDRIVER* pDriver, + _Out_ WDFDEVICE* pDevice + ) +{ + NTSTATUS status; + WDF_DRIVER_CONFIG config; + PWDFDEVICE_INIT pInit = NULL; + + WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); + + config.DriverInitFlags |= WdfDriverInitNonPnpDriver; + config.EvtDriverUnload = TLInspectEvtDriverUnload; + + status = WdfDriverCreate( + driverObject, + registryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + pDriver + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + pInit = WdfControlDeviceInitAllocate(*pDriver, &SDDL_DEVOBJ_KERNEL_ONLY); + + if (!pInit) + { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + WdfDeviceInitSetDeviceType(pInit, FILE_DEVICE_NETWORK); + WdfDeviceInitSetCharacteristics(pInit, FILE_DEVICE_SECURE_OPEN, FALSE); + WdfDeviceInitSetCharacteristics(pInit, FILE_AUTOGENERATED_DEVICE_NAME, TRUE); + + status = WdfDeviceCreate(&pInit, WDF_NO_OBJECT_ATTRIBUTES, pDevice); + if (!NT_SUCCESS(status)) + { + WdfDeviceInitFree(pInit); + goto Exit; + } + + WdfControlFinishInitializing(*pDevice); + +Exit: + return status; +} + +NTSTATUS +DriverEntry( + DRIVER_OBJECT* driverObject, + UNICODE_STRING* registryPath + ) +{ + NTSTATUS status; + WDFDRIVER driver; + WDFDEVICE device; + HANDLE threadHandle; + + // Request NX Non-Paged Pool when available + ExInitializeDriverRuntime(DrvRtPoolNxOptIn); + + status = TLInspectInitDriverObjects( + driverObject, + registryPath, + &driver, + &device + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = WdfDriverOpenParametersRegistryKey( + driver, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &gParametersKey + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = TLInspectLoadConfig(gParametersKey); + + if (!NT_SUCCESS(status)) + { + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto Exit; + } + + if ((configInspectRemoteAddrV4 == NULL) && + (configInspectRemoteAddrV6 == NULL)) + { + status = STATUS_DEVICE_CONFIGURATION_ERROR; + goto Exit; + } + + status = FwpsInjectionHandleCreate( + AF_UNSPEC, + FWPS_INJECTION_TYPE_TRANSPORT, + &gInjectionHandle + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + InitializeListHead(&gConnList); + KeInitializeSpinLock(&gConnListLock); + + InitializeListHead(&gPacketQueue); + KeInitializeSpinLock(&gPacketQueueLock); + + KeInitializeEvent( + &gWorkerEvent, + NotificationEvent, + FALSE + ); + + gWdmDevice = WdfDeviceWdmGetDeviceObject(device); + + status = TLInspectRegisterCallouts(gWdmDevice); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = PsCreateSystemThread( + &threadHandle, + THREAD_ALL_ACCESS, + NULL, + NULL, + NULL, + TLInspectWorker, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = ObReferenceObjectByHandle( + threadHandle, + 0, + NULL, + KernelMode, + &gThreadObj, + NULL + ); + NT_ASSERT(NT_SUCCESS(status)); + + ZwClose(threadHandle); + +Exit: + + if (!NT_SUCCESS(status)) + { + if (gEngineHandle != NULL) + { + TLInspectUnregisterCallouts(); + } + if (gInjectionHandle != NULL) + { + FwpsInjectionHandleDestroy(gInjectionHandle); + } + } + + return status; +}; + diff --git a/network/trans/inspect/sys/inspect.c b/network/trans/inspect/sys/inspect.c new file mode 100644 index 00000000..26b01e9b --- /dev/null +++ b/network/trans/inspect/sys/inspect.c @@ -0,0 +1,1418 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + This file implements the classifyFn callout functions for the ALE connect, + recv-accept, and transport callouts. In addition the system worker thread + that performs the actual packet inspection is also implemented here along + with the eventing mechanisms shared between the classify function and the + worker thread. + + connect/Packet inspection is done out-of-band by a system worker thread + using the reference-drop-clone-reinject as well as ALE pend/complete + mechanism. Therefore the sample can serve as a base in scenarios where + filtering decision cannot be made within the classifyFn() callout and + instead must be made, for example, by an user-mode application. + +Environment: + + Kernel mode + +--*/ + + +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + +#include + +#include "inspect.h" +#include "utils.h" + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +TLInspectALEConnectClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#else + +void +TLInspectALEConnectClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +/* ++ + + This is the classifyFn function for the ALE connect (v4 and v6) callout. + For an initial classify (where the FWP_CONDITION_FLAG_IS_REAUTHORIZE flag + is not set), it is queued to the connection list for inspection by the + worker thread. For re-auth, we first check if it is triggered by an ealier + FwpsCompleteOperation call by looking for an pended connect that has been + inspected. If found, we remove it from the connect list and return the + inspection result; otherwise we can conclude that the re-auth is triggered + by policy change so we queue it to the packet queue to be process by the + worker thread like any other regular packets. + +-- */ +{ + NTSTATUS status; + + KLOCK_QUEUE_HANDLE connListLockHandle; + KLOCK_QUEUE_HANDLE packetQueueLockHandle; + + TL_INSPECT_PENDED_PACKET* pendedConnect = NULL; + TL_INSPECT_PENDED_PACKET* connEntry; + TL_INSPECT_PENDED_PACKET* pendedPacket = NULL; + + ADDRESS_FAMILY addressFamily; + FWPS_PACKET_INJECTION_STATE packetState; + BOOLEAN signalWorkerThread; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(classifyContext); +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(filter); + UNREFERENCED_PARAMETER(flowContext); + + // + // We don't have the necessary right to alter the classify, exit. + // + if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0) + { + goto Exit; + } + + if (layerData != NULL) + { + // + // We don't re-inspect packets that we've inspected earlier. + // + packetState = FwpsQueryPacketInjectionState( + gInjectionHandle, + layerData, + NULL + ); + + if ((packetState == FWPS_PACKET_INJECTED_BY_SELF) || + (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)) + { + classifyOut->actionType = FWP_ACTION_PERMIT; + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + goto Exit; + } + } + + addressFamily = GetAddressFamilyForLayer(inFixedValues->layerId); + + if (!IsAleReauthorize(inFixedValues)) + { + // + // If the classify is the initial authorization for a connection, we + // queue it to the pended connection list and notify the worker thread + // for out-of-band processing. + // + pendedConnect = AllocateAndInitializePendedPacket( + inFixedValues, + inMetaValues, + addressFamily, + layerData, + TL_INSPECT_CONNECT_PACKET, + FWP_DIRECTION_OUTBOUND + ); + + if (pendedConnect == NULL) + { + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + goto Exit; + } + + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, + FWPS_METADATA_FIELD_COMPLETION_HANDLE)); + + // + // Pend the ALE_AUTH_CONNECT classify. + // + status = FwpsPendOperation( + inMetaValues->completionHandle, + &pendedConnect->completionContext + ); + + if (!NT_SUCCESS(status)) + { + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + goto Exit; + } + + KeAcquireInStackQueuedSpinLock( + &gConnListLock, + &connListLockHandle + ); + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + signalWorkerThread = IsListEmpty(&gConnList) && + IsListEmpty(&gPacketQueue); + + InsertTailList(&gConnList, &pendedConnect->listEntry); + pendedConnect = NULL; // ownership transferred + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + KeReleaseInStackQueuedSpinLock(&connListLockHandle); + + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + + if (signalWorkerThread) + { + KeSetEvent( + &gWorkerEvent, + 0, + FALSE + ); + } + } + else // re-auth @ ALE_AUTH_CONNECT + { + FWP_DIRECTION packetDirection; + // + // The classify is the re-authorization for an existing connection, it + // could have been triggered for one of the three cases -- + // + // 1) The re-auth is triggered by a FwpsCompleteOperation call to + // complete a ALE_AUTH_CONNECT classify pended earlier. + // 2) The re-auth is triggered by an outbound packet sent immediately + // after a policy change at ALE_AUTH_CONNECT layer. + // 3) The re-auth is triggered by an inbound packet received + // immediately after a policy change at ALE_AUTH_CONNECT layer. + // + + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, + FWPS_METADATA_FIELD_PACKET_DIRECTION)); + packetDirection = inMetaValues->packetDirection; + + if (packetDirection == FWP_DIRECTION_OUTBOUND) + { + LIST_ENTRY* listEntry; + BOOLEAN authComplete = FALSE; + + // + // We first check whether this is a FwpsCompleteOperation-triggered + // reauth by looking for a pended connect that has the inspection + // decision recorded. If found, we return that decision and remove + // the pended connect from the list. + // + + KeAcquireInStackQueuedSpinLock( + &gConnListLock, + &connListLockHandle + ); + + for (listEntry = gConnList.Flink; + listEntry != &gConnList; + listEntry = listEntry->Flink) + { + connEntry = CONTAINING_RECORD( + listEntry, + TL_INSPECT_PENDED_PACKET, + listEntry + ); + + if (IsMatchingConnectPacket( + inFixedValues, + addressFamily, + packetDirection, + connEntry + ) && (connEntry->authConnectDecision != 0)) + { + // We found a match. + pendedConnect = connEntry; + + NT_ASSERT((pendedConnect->authConnectDecision == FWP_ACTION_PERMIT) || + (pendedConnect->authConnectDecision == FWP_ACTION_BLOCK)); + + classifyOut->actionType = pendedConnect->authConnectDecision; + if (classifyOut->actionType == FWP_ACTION_BLOCK || + filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + RemoveEntryList(&pendedConnect->listEntry); + + if (!gDriverUnloading && + (pendedConnect->netBufferList != NULL) && + (pendedConnect->authConnectDecision == FWP_ACTION_PERMIT)) + { + // + // Now the outbound connection has been authorized. If the + // pended connect has a net buffer list in it, we need it + // morph it into a data packet and queue it to the packet + // queue for send injecition. + // + pendedConnect->type = TL_INSPECT_DATA_PACKET; + + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + signalWorkerThread = IsListEmpty(&gPacketQueue) && + IsListEmpty(&gConnList); + + InsertTailList(&gPacketQueue, &pendedConnect->listEntry); + pendedConnect = NULL; // ownership transferred + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + + if (signalWorkerThread) + { + KeSetEvent( + &gWorkerEvent, + 0, + FALSE + ); + } + } + + authComplete = TRUE; + break; + } + } + + KeReleaseInStackQueuedSpinLock(&connListLockHandle); + + if (authComplete) + { + goto Exit; + } + } + + // + // If we reach here it means this is a policy change triggered re-auth + // for an pre-existing connection. For such a packet (inbound or + // outbound) we queue it to the packet queue and inspect it just like + // other regular data packets from TRANSPORT layers. + // + + NT_ASSERT(layerData != NULL); + + pendedPacket = AllocateAndInitializePendedPacket( + inFixedValues, + inMetaValues, + addressFamily, + layerData, + TL_INSPECT_REAUTH_PACKET, + packetDirection + ); + + if (pendedPacket == NULL) + { + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + goto Exit; + } + + if (packetDirection == FWP_DIRECTION_INBOUND) + { + pendedPacket->ipSecProtected = IsSecureConnection(inFixedValues); + } + + KeAcquireInStackQueuedSpinLock( + &gConnListLock, + &connListLockHandle + ); + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + if (!gDriverUnloading) + { + signalWorkerThread = IsListEmpty(&gPacketQueue) && + IsListEmpty(&gConnList); + + InsertTailList(&gPacketQueue, &pendedPacket->listEntry); + pendedPacket = NULL; // ownership transferred + + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + } + else + { + // + // Driver is being unloaded, permit any connect classify. + // + signalWorkerThread = FALSE; + + classifyOut->actionType = FWP_ACTION_PERMIT; + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + } + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + KeReleaseInStackQueuedSpinLock(&connListLockHandle); + + if (signalWorkerThread) + { + KeSetEvent( + &gWorkerEvent, + 0, + FALSE + ); + } + + } + +Exit: + + if (pendedPacket != NULL) + { + FreePendedPacket(pendedPacket); + } + if (pendedConnect != NULL) + { + FreePendedPacket(pendedConnect); + } + + return; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +TLInspectALERecvAcceptClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#else + +void +TLInspectALERecvAcceptClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +/* ++ + + This is the classifyFn function for the ALE Recv-Accept (v4 and v6) callout. + For an initial classify (where the FWP_CONDITION_FLAG_IS_REAUTHORIZE flag + is not set), it is queued to the connection list for inspection by the + worker thread. For re-auth, it is queued to the packet queue to be process + by the worker thread like any other regular packets. + +-- */ +{ + NTSTATUS status; + + KLOCK_QUEUE_HANDLE connListLockHandle; + KLOCK_QUEUE_HANDLE packetQueueLockHandle; + + TL_INSPECT_PENDED_PACKET* pendedRecvAccept = NULL; + TL_INSPECT_PENDED_PACKET* pendedPacket = NULL; + + ADDRESS_FAMILY addressFamily; + FWPS_PACKET_INJECTION_STATE packetState; + BOOLEAN signalWorkerThread; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(classifyContext); +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(filter); + UNREFERENCED_PARAMETER(flowContext); + + // + // We don't have the necessary right to alter the classify, exit. + // + if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0) + { + goto Exit; + } + + NT_ASSERT(layerData != NULL); + _Analysis_assume_(layerData != NULL); + + // + // We don't re-inspect packets that we've inspected earlier. + // + packetState = FwpsQueryPacketInjectionState( + gInjectionHandle, + layerData, + NULL + ); + + if ((packetState == FWPS_PACKET_INJECTED_BY_SELF) || + (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)) + { + classifyOut->actionType = FWP_ACTION_PERMIT; + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + goto Exit; + } + + addressFamily = GetAddressFamilyForLayer(inFixedValues->layerId); + + if (!IsAleReauthorize(inFixedValues)) + { + // + // If the classify is the initial authorization for a connection, we + // queue it to the pended connection list and notify the worker thread + // for out-of-band processing. + // + pendedRecvAccept = AllocateAndInitializePendedPacket( + inFixedValues, + inMetaValues, + addressFamily, + layerData, + TL_INSPECT_CONNECT_PACKET, + FWP_DIRECTION_INBOUND + ); + + if (pendedRecvAccept == NULL) + { + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + goto Exit; + } + + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, + FWPS_METADATA_FIELD_COMPLETION_HANDLE)); + + // + // Pend the ALE_AUTH_RECV_ACCEPT classify. + // + status = FwpsPendOperation( + inMetaValues->completionHandle, + &pendedRecvAccept->completionContext + ); + + if (!NT_SUCCESS(status)) + { + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + goto Exit; + } + + KeAcquireInStackQueuedSpinLock( + &gConnListLock, + &connListLockHandle + ); + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + signalWorkerThread = IsListEmpty(&gConnList) && + IsListEmpty(&gPacketQueue); + + InsertTailList(&gConnList, &pendedRecvAccept->listEntry); + pendedRecvAccept = NULL; // ownership transferred + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + KeReleaseInStackQueuedSpinLock(&connListLockHandle); + + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + + if (signalWorkerThread) + { + KeSetEvent( + &gWorkerEvent, + 0, + FALSE + ); + } + + } + else // re-auth @ ALE_AUTH_RECV_ACCEPT + { + FWP_DIRECTION packetDirection; + // + // The classify is the re-authorization for a existing connection, it + // could have been triggered for one of the two cases -- + // + // 1) The re-auth is triggered by an outbound packet sent immediately + // after a policy change at ALE_AUTH_RECV_ACCEPT layer. + // 2) The re-auth is triggered by an inbound packet received + // immediately after a policy change at ALE_AUTH_RECV_ACCEPT layer. + // + + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, + FWPS_METADATA_FIELD_PACKET_DIRECTION)); + packetDirection = inMetaValues->packetDirection; + + pendedPacket = AllocateAndInitializePendedPacket( + inFixedValues, + inMetaValues, + addressFamily, + layerData, + TL_INSPECT_REAUTH_PACKET, + packetDirection + ); + + if (pendedPacket == NULL) + { + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + goto Exit; + } + + if (packetDirection == FWP_DIRECTION_INBOUND) + { + pendedPacket->ipSecProtected = IsSecureConnection(inFixedValues); + } + + KeAcquireInStackQueuedSpinLock( + &gConnListLock, + &connListLockHandle + ); + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + if (!gDriverUnloading) + { + signalWorkerThread = IsListEmpty(&gPacketQueue) && + IsListEmpty(&gConnList); + + InsertTailList(&gPacketQueue, &pendedPacket->listEntry); + pendedPacket = NULL; // ownership transferred + + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + } + else + { + // + // Driver is being unloaded, permit any connect classify. + // + signalWorkerThread = FALSE; + + classifyOut->actionType = FWP_ACTION_PERMIT; + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + } + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + KeReleaseInStackQueuedSpinLock(&connListLockHandle); + + if (signalWorkerThread) + { + KeSetEvent( + &gWorkerEvent, + 0, + FALSE + ); + } + } + +Exit: + + if (pendedPacket != NULL) + { + FreePendedPacket(pendedPacket); + } + if (pendedRecvAccept != NULL) + { + FreePendedPacket(pendedRecvAccept); + } + + return; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +TLInspectTransportClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#else + +void +TLInspectTransportClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#endif +/* ++ + + This is the classifyFn function for the Transport (v4 and v6) callout. + packets (inbound or outbound) are ueued to the packet queue to be processed + by the worker thread. + +-- */ +{ + + KLOCK_QUEUE_HANDLE connListLockHandle; + KLOCK_QUEUE_HANDLE packetQueueLockHandle; + + TL_INSPECT_PENDED_PACKET* pendedPacket = NULL; + FWP_DIRECTION packetDirection; + + ADDRESS_FAMILY addressFamily; + FWPS_PACKET_INJECTION_STATE packetState; + BOOLEAN signalWorkerThread; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(classifyContext); +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(filter); + UNREFERENCED_PARAMETER(flowContext); + + // + // We don't have the necessary right to alter the classify, exit. + // + if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0) + { + goto Exit; + } + + NT_ASSERT(layerData != NULL); + _Analysis_assume_(layerData != NULL); + + // + // We don't re-inspect packets that we've inspected earlier. + // + packetState = FwpsQueryPacketInjectionState( + gInjectionHandle, + layerData, + NULL + ); + + if ((packetState == FWPS_PACKET_INJECTED_BY_SELF) || + (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)) + { + classifyOut->actionType = FWP_ACTION_PERMIT; + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + goto Exit; + } + + addressFamily = GetAddressFamilyForLayer(inFixedValues->layerId); + + packetDirection = + GetPacketDirectionForLayer(inFixedValues->layerId); + + if (packetDirection == FWP_DIRECTION_INBOUND) + { + if (IsAleClassifyRequired(inFixedValues, inMetaValues)) + { + // + // Inbound transport packets that are destined to ALE Recv-Accept + // layers, for initial authorization or reauth, should be inspected + // at the ALE layer. We permit it from Tranport here. + // + classifyOut->actionType = FWP_ACTION_PERMIT; + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + goto Exit; + } + else + { + // + // To be compatible with Vista's IpSec implementation, we must not + // intercept not-yet-detunneled IpSec traffic. + // + FWPS_PACKET_LIST_INFORMATION packetInfo = {0}; + FwpsGetPacketListSecurityInformation( + layerData, + FWPS_PACKET_LIST_INFORMATION_QUERY_IPSEC | + FWPS_PACKET_LIST_INFORMATION_QUERY_INBOUND, + &packetInfo + ); + + if (packetInfo.ipsecInformation.inbound.isTunnelMode && + !packetInfo.ipsecInformation.inbound.isDeTunneled) + { + classifyOut->actionType = FWP_ACTION_PERMIT; + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + goto Exit; + } + } + } + + pendedPacket = AllocateAndInitializePendedPacket( + inFixedValues, + inMetaValues, + addressFamily, + layerData, + TL_INSPECT_DATA_PACKET, + packetDirection + ); + + if (pendedPacket == NULL) + { + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + goto Exit; + } + + KeAcquireInStackQueuedSpinLock( + &gConnListLock, + &connListLockHandle + ); + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + if (!gDriverUnloading) + { + signalWorkerThread = IsListEmpty(&gPacketQueue) && + IsListEmpty(&gConnList); + + InsertTailList(&gPacketQueue, &pendedPacket->listEntry); + pendedPacket = NULL; // ownership transferred + + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + } + else + { + // + // Driver is being unloaded, permit any connect classify. + // + signalWorkerThread = FALSE; + + classifyOut->actionType = FWP_ACTION_PERMIT; + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + } + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + KeReleaseInStackQueuedSpinLock(&connListLockHandle); + + if (signalWorkerThread) + { + KeSetEvent( + &gWorkerEvent, + 0, + FALSE + ); + } + +Exit: + + if (pendedPacket != NULL) + { + FreePendedPacket(pendedPacket); + } + + return; +} + +NTSTATUS +TLInspectALEConnectNotify( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter + ) +{ + UNREFERENCED_PARAMETER(notifyType); + UNREFERENCED_PARAMETER(filterKey); + UNREFERENCED_PARAMETER(filter); + + return STATUS_SUCCESS; +} + +NTSTATUS +TLInspectALERecvAcceptNotify( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter + ) +{ + UNREFERENCED_PARAMETER(notifyType); + UNREFERENCED_PARAMETER(filterKey); + UNREFERENCED_PARAMETER(filter); + + return STATUS_SUCCESS; +} + +NTSTATUS +TLInspectTransportNotify( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter + ) +{ + UNREFERENCED_PARAMETER(notifyType); + UNREFERENCED_PARAMETER(filterKey); + UNREFERENCED_PARAMETER(filter); + + return STATUS_SUCCESS; +} + +void TLInspectInjectComplete( + _Inout_ void* context, + _Inout_ NET_BUFFER_LIST* netBufferList, + _In_ BOOLEAN dispatchLevel + ) +{ + TL_INSPECT_PENDED_PACKET* packet = context; + + UNREFERENCED_PARAMETER(dispatchLevel); + + FwpsFreeCloneNetBufferList(netBufferList, 0); + + FreePendedPacket(packet); +} + +NTSTATUS +TLInspectCloneReinjectOutbound( + _Inout_ TL_INSPECT_PENDED_PACKET* packet + ) +/* ++ + + This function clones the outbound net buffer list and reinject it back. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + NET_BUFFER_LIST* clonedNetBufferList = NULL; + FWPS_TRANSPORT_SEND_PARAMS sendArgs = {0}; + + status = FwpsAllocateCloneNetBufferList( + packet->netBufferList, + NULL, + NULL, + 0, + &clonedNetBufferList + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + sendArgs.remoteAddress = (UINT8*)(&packet->remoteAddr); + sendArgs.remoteScopeId = packet->remoteScopeId; + sendArgs.controlData = packet->controlData; + sendArgs.controlDataLength = packet->controlDataLength; + + // + // Send-inject the cloned net buffer list. + // + + status = FwpsInjectTransportSendAsync( + gInjectionHandle, + NULL, + packet->endpointHandle, + 0, + &sendArgs, + packet->addressFamily, + packet->compartmentId, + clonedNetBufferList, + TLInspectInjectComplete, + packet + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + clonedNetBufferList = NULL; // ownership transferred to the + // completion function. + +Exit: + + if (clonedNetBufferList != NULL) + { + FwpsFreeCloneNetBufferList(clonedNetBufferList, 0); + } + + return status; +} + +NTSTATUS +TLInspectCloneReinjectInbound( + _Inout_ TL_INSPECT_PENDED_PACKET* packet + ) +/* ++ + + This function clones the inbound net buffer list and, if needed, + rebuild the IP header to remove the IpSec headers and receive-injects + the clone back to the tcpip stack. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + NET_BUFFER_LIST* clonedNetBufferList = NULL; + NET_BUFFER* netBuffer; + ULONG nblOffset; + NDIS_STATUS ndisStatus; + + // + // For inbound net buffer list, we can assume it contains only one + // net buffer. + // + netBuffer = NET_BUFFER_LIST_FIRST_NB(packet->netBufferList); + + nblOffset = NET_BUFFER_DATA_OFFSET(netBuffer); + + // + // The TCP/IP stack could have retreated the net buffer list by the + // transportHeaderSize amount; detect the condition here to avoid + // retreating twice. + // + if (nblOffset != packet->nblOffset) + { + NT_ASSERT(packet->nblOffset - nblOffset == packet->transportHeaderSize); + packet->transportHeaderSize = 0; + } + + // + // Adjust the net buffer list offset to the start of the IP header. + // + ndisStatus = NdisRetreatNetBufferDataStart( + netBuffer, + packet->ipHeaderSize + packet->transportHeaderSize, + 0, + NULL + ); + _Analysis_assume_(ndisStatus == NDIS_STATUS_SUCCESS); + + // + // Note that the clone will inherit the original net buffer list's offset. + // + + status = FwpsAllocateCloneNetBufferList( + packet->netBufferList, + NULL, + NULL, + 0, + &clonedNetBufferList + ); + + // + // Undo the adjustment on the original net buffer list. + // + + NdisAdvanceNetBufferDataStart( + netBuffer, + packet->ipHeaderSize + packet->transportHeaderSize, + FALSE, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + if (packet->ipSecProtected) + { + // + // When an IpSec protected packet is indicated to AUTH_RECV_ACCEPT or + // INBOUND_TRANSPORT layers, for performance reasons the tcpip stack + // does not remove the AH/ESP header from the packet. And such + // packets cannot be recv-injected back to the stack w/o removing the + // AH/ESP header. Therefore before re-injection we need to "re-build" + // the cloned packet. + // + status = FwpsConstructIpHeaderForTransportPacket( + clonedNetBufferList, + packet->ipHeaderSize, + packet->addressFamily, + (UINT8*)&packet->remoteAddr, + (UINT8*)&packet->localAddr, + packet->protocol, + 0, + NULL, + 0, + 0, + NULL, + 0, + 0 + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + } + + if (packet->completionContext != NULL) + { + NT_ASSERT(packet->type == TL_INSPECT_CONNECT_PACKET); + + FwpsCompleteOperation( + packet->completionContext, + clonedNetBufferList + ); + + packet->completionContext = NULL; + } + + status = FwpsInjectTransportReceiveAsync( + gInjectionHandle, + NULL, + NULL, + 0, + packet->addressFamily, + packet->compartmentId, + packet->interfaceIndex, + packet->subInterfaceIndex, + clonedNetBufferList, + TLInspectInjectComplete, + packet + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + clonedNetBufferList = NULL; // ownership transferred to the + // completion function. + +Exit: + + if (clonedNetBufferList != NULL) + { + FwpsFreeCloneNetBufferList(clonedNetBufferList, 0); + } + + return status; +} + +void +TlInspectCompletePendedConnection( + _Inout_ TL_INSPECT_PENDED_PACKET** pendedConnect, + _In_ BOOLEAN permitTraffic + ) +/* ++ + + This function completes the pended connection (inbound or outbound) + with the inspection result. + +-- */ +{ + + TL_INSPECT_PENDED_PACKET* pendedConnectLocal = *pendedConnect; + + if (pendedConnectLocal->direction == FWP_DIRECTION_OUTBOUND) + { + HANDLE completionContext = pendedConnectLocal->completionContext; + + pendedConnectLocal->authConnectDecision = + permitTraffic ? FWP_ACTION_PERMIT : FWP_ACTION_BLOCK; + + // + // For pended ALE_AUTH_CONNECT, FwpsCompleteOperation will trigger + // a re-auth during which the inspection decision is to be returned. + // Here we don't remove the pended entry from the list such that the + // re-auth can find it along with the recorded inspection result. + // + pendedConnectLocal->completionContext = NULL; + + FwpsCompleteOperation( + completionContext, + NULL + ); + + *pendedConnect = NULL; // ownership transferred to the re-auth path. + } + else + { + if (!configPermitTraffic) + { + FreePendedPacket(pendedConnectLocal); + *pendedConnect = NULL; + } + + // + // Permitted ALE_RECV_ACCEPT will pass thru and be processed by + // TLInspectCloneReinjectInbound. FwpsCompleteOperation will be called + // then when the net buffer list is cloned; after which the clone will + // be recv-injected. + // + } +} + +void +TLInspectWorker( + _In_ void* StartContext + ) +/* ++ + + This worker thread waits for the connect and packet queue event when the + queues are empty; and it will be woken up when there are connects/packets + queued needing to be inspected. Once awaking, It will run in a loop to + complete the pended ALE classifies and/or clone-reinject packets back + until both queues are exhausted (and it will go to sleep waiting for more + work). + + The worker thread will end once it detected the driver is unloading. + +-- */ +{ + NTSTATUS status; + + TL_INSPECT_PENDED_PACKET* packet = NULL; + LIST_ENTRY* listEntry; + + KLOCK_QUEUE_HANDLE packetQueueLockHandle; + KLOCK_QUEUE_HANDLE connListLockHandle; + + UNREFERENCED_PARAMETER(StartContext); + + for(;;) + { + KeWaitForSingleObject( + &gWorkerEvent, + Executive, + KernelMode, + FALSE, + NULL + ); + + if (gDriverUnloading) + { + break; + } + + configPermitTraffic = IsTrafficPermitted(); + + listEntry = NULL; + + KeAcquireInStackQueuedSpinLock( + &gConnListLock, + &connListLockHandle + ); + + if (!IsListEmpty(&gConnList)) + { + _Analysis_assume_(gConnList.Flink != NULL); + listEntry = gConnList.Flink; + + packet = CONTAINING_RECORD( + listEntry, + TL_INSPECT_PENDED_PACKET, + listEntry + ); + if (packet->direction == FWP_DIRECTION_INBOUND) + { + RemoveEntryList(&packet->listEntry); + } + + // + // Leave the pended ALE_AUTH_CONNECT in the connection list, it will + // be processed and removed from the list during re-auth. + // + } + + KeReleaseInStackQueuedSpinLock(&connListLockHandle); + + if (listEntry == NULL) + { + NT_ASSERT(!IsListEmpty(&gPacketQueue)); + + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + listEntry = RemoveHeadList(&gPacketQueue); + + packet = CONTAINING_RECORD( + listEntry, + TL_INSPECT_PENDED_PACKET, + listEntry + ); + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + } + + if (packet->type == TL_INSPECT_CONNECT_PACKET) + { + TlInspectCompletePendedConnection( + &packet, + configPermitTraffic); + } + + if ((packet != NULL) && configPermitTraffic) + { + if (packet->direction == FWP_DIRECTION_OUTBOUND) + { + status = TLInspectCloneReinjectOutbound(packet); + } + else + { + status = TLInspectCloneReinjectInbound(packet); + } + + if (NT_SUCCESS(status)) + { + packet = NULL; // ownership transferred. + } + + } + + if (packet != NULL) + { + FreePendedPacket(packet); + } + + KeAcquireInStackQueuedSpinLock( + &gConnListLock, + &connListLockHandle + ); + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + if (IsListEmpty(&gConnList) && IsListEmpty(&gPacketQueue) && + !gDriverUnloading) + { + KeClearEvent(&gWorkerEvent); + } + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + KeReleaseInStackQueuedSpinLock(&connListLockHandle); + } + + NT_ASSERT(gDriverUnloading); + + while (!IsListEmpty(&gConnList)) + { + packet = NULL; + + KeAcquireInStackQueuedSpinLock( + &gConnListLock, + &connListLockHandle + ); + + if (!IsListEmpty(&gConnList)) + { + listEntry = gConnList.Flink; + packet = CONTAINING_RECORD( + listEntry, + TL_INSPECT_PENDED_PACKET, + listEntry + ); + } + + KeReleaseInStackQueuedSpinLock(&connListLockHandle); + + if (packet != NULL) + { + TlInspectCompletePendedConnection(&packet, FALSE); + NT_ASSERT(packet == NULL); + } + } + + // + // Discard all the pended packets if driver is being unloaded. + // + + while (!IsListEmpty(&gPacketQueue)) + { + packet = NULL; + + KeAcquireInStackQueuedSpinLock( + &gPacketQueueLock, + &packetQueueLockHandle + ); + + if (!IsListEmpty(&gPacketQueue)) + { + listEntry = RemoveHeadList(&gPacketQueue); + + packet = CONTAINING_RECORD( + listEntry, + TL_INSPECT_PENDED_PACKET, + listEntry + ); + } + + KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle); + + if (packet != NULL) + { + FreePendedPacket(packet); + } + } + + PsTerminateSystemThread(STATUS_SUCCESS); + +} diff --git a/network/trans/inspect/sys/inspect.h b/network/trans/inspect/sys/inspect.h new file mode 100644 index 00000000..baff6485 --- /dev/null +++ b/network/trans/inspect/sys/inspect.h @@ -0,0 +1,214 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + This header files declares common data types and function prototypes used + throughout the Transport Inspect sample. + +Environment: + + Kernel mode + +--*/ + +#ifndef _TL_INSPECT_H_ +#define _TL_INSPECT_H_ + +typedef enum TL_INSPECT_PACKET_TYPE_ +{ + TL_INSPECT_CONNECT_PACKET, + TL_INSPECT_DATA_PACKET, + TL_INSPECT_REAUTH_PACKET +} TL_INSPECT_PACKET_TYPE; + +// +// TL_INSPECT_PENDED_PACKET is the object type we used to store all information +// needed for out-of-band packet modification and re-injection. This type +// also points back to the flow context the packet belongs to. + +#pragma warning(push) +#pragma warning(disable: 4201) //NAMELESS_STRUCT_UNION + +typedef struct TL_INSPECT_PENDED_PACKET_ +{ + LIST_ENTRY listEntry; + + ADDRESS_FAMILY addressFamily; + TL_INSPECT_PACKET_TYPE type; + FWP_DIRECTION direction; + + UINT32 authConnectDecision; + HANDLE completionContext; + + // + // Common fields for inbound and outbound traffic. + // + UINT8 protocol; + NET_BUFFER_LIST* netBufferList; + COMPARTMENT_ID compartmentId; + union + { + FWP_BYTE_ARRAY16 localAddr; + UINT32 ipv4LocalAddr; + }; + union + { + UINT16 localPort; + UINT16 icmpType; + }; + union + { + UINT16 remotePort; + UINT16 icmpCode; + }; + + // + // Data fields for outbound packet re-injection. + // + UINT64 endpointHandle; + union + { + FWP_BYTE_ARRAY16 remoteAddr; + UINT32 ipv4RemoteAddr; + }; + + SCOPE_ID remoteScopeId; + WSACMSGHDR* controlData; + ULONG controlDataLength; + + // + // Data fields for inbound packet re-injection. + // + BOOLEAN ipSecProtected; + ULONG nblOffset; + UINT32 ipHeaderSize; + UINT32 transportHeaderSize; + IF_INDEX interfaceIndex; + IF_INDEX subInterfaceIndex; +} TL_INSPECT_PENDED_PACKET; + +#pragma warning(pop) + +// +// Pooltags used by this callout driver. +// +#define TL_INSPECT_CONNECTION_POOL_TAG 'olfD' +#define TL_INSPECT_PENDED_PACKET_POOL_TAG 'kppD' +#define TL_INSPECT_CONTROL_DATA_POOL_TAG 'dcdD' + +// +// Shared global data. +// +extern BOOLEAN configPermitTraffic; + +extern HANDLE gInjectionHandle; + +extern LIST_ENTRY gConnList; +extern KSPIN_LOCK gConnListLock; + +extern LIST_ENTRY gPacketQueue; +extern KSPIN_LOCK gPacketQueueLock; + +extern KEVENT gWorkerEvent; + +extern BOOLEAN gDriverUnloading; + +// +// Shared function prototypes +// + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +TLInspectALEConnectClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +void +TLInspectALERecvAcceptClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +void +TLInspectTransportClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +#else /// (NTDDI_VERSION >= NTDDI_WIN7) + +void +TLInspectALEConnectClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +void +TLInspectALERecvAcceptClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +void +TLInspectTransportClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +NTSTATUS +TLInspectALEConnectNotify( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter + ); + +NTSTATUS +TLInspectALERecvAcceptNotify( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter + ); + +NTSTATUS +TLInspectTransportNotify( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter + ); + +KSTART_ROUTINE TLInspectWorker; + +#endif // _TL_INSPECT_H_ diff --git a/network/trans/inspect/sys/inspect.inf b/network/trans/inspect/sys/inspect.inf new file mode 100644 index 00000000..125f9601 --- /dev/null +++ b/network/trans/inspect/sys/inspect.inf @@ -0,0 +1,63 @@ +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved +;;; +;;; Abstract: +;;; Traffic Inspect Callout sample driver install configuration. +;;; + +[Version] + Signature = "$Windows NT$" + Class = WFPCALLOUTS + ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} + Provider = %Contoso% + CatalogFile = Inspect.cat + DriverVer = 11/24/2014,14.24.55.836 + +[SourceDisksNames] + 1 = %InspectDisk%,,,"" + +[SourceDisksFiles] + Inspect.sys = 1,, + +[DestinationDirs] + DefaultDestDir = 12 ; %WinDir%\System32\Drivers + Inspect.DriverFiles = 12 ; %WinDir%\System32\Drivers + +[DefaultInstall] + OptionDesc = %InspectServiceDesc% + CopyFiles = Inspect.DriverFiles + +[DefaultInstall.Services] + AddService = %InspectServiceName%,,Inspect.Service + +[DefaultUninstall] + DelFiles = Inspect.DriverFiles + +[DefaultUninstall.Services] + DelService = %InspectServiceName%,0x200 ; SPSVCINST_STOPSERVICE + DelReg = Inspect.DelRegistry + +[Inspect.DriverFiles] + Inspect.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY + +[Inspect.Service] + DisplayName = %InspectServiceName% + Description = %InspectServiceDesc% + ServiceType = 1 ; SERVICE_KERNEL_DRIVER + StartType = 3 ; SERVICE_DEMAND_START + ErrorControl = 1 ; SERVICE_ERROR_NORMAL + ServiceBinary = %12%\Inspect.sys ; %WinDir%\System32\Drivers\Inspect.sys + AddReg = Inspect.AddRegistry + +[Inspect.AddRegistry] + HKR,"Parameters","BlockTraffic",0x00010001,"0" ; FLG_ADDREG_TYPE_DWORD + HKR,"Parameters","RemoteAddressToInspect",0x00000000,"10.0.0.1" ; FLG_ADDREG_TYPE_SZ + +[Inspect.DelRegistry] + HKR,"Parameters",,, + +[Strings] + Contoso = "Contoso Ltd." + InspectDisk = "Traffic Inspect Installation Disk" + InspectServiceDesc = "Traffic Inspect Callout Driver" + InspectServiceName = "Inspect" \ No newline at end of file diff --git a/network/trans/inspect/sys/inspect.vcxproj b/network/trans/inspect/sys/inspect.vcxproj new file mode 100644 index 00000000..d403b025 --- /dev/null +++ b/network/trans/inspect/sys/inspect.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {43AC1739-03F1-4465-8A16-F5FCD06BC2E9} + $(MSBuildProjectName) + 1 + Debug + Win32 + {CA2EEED8-4078-42F4-947A-29D46ACCC30F} + + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + inspect + + + inspect + + + inspect + + + inspect + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/trans/inspect/sys/inspect.vcxproj.Filters b/network/trans/inspect/sys/inspect.vcxproj.Filters new file mode 100644 index 00000000..7fbd1b25 --- /dev/null +++ b/network/trans/inspect/sys/inspect.vcxproj.Filters @@ -0,0 +1,32 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {89A4FDF7-7B2B-42A3-A13C-4855905159BF} + + + h;hpp;hxx;hm;inl;inc;xsd + {893A15B9-96A4-4181-9353-D9C59D6D343C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {174E13A1-2D15-477C-9E80-060424681A41} + + + inf;inv;inx;mof;mc; + {3D55D549-7A89-4DEB-9C7B-149E8332DD7C} + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/network/trans/inspect/sys/utils.c b/network/trans/inspect/sys/utils.c new file mode 100644 index 00000000..b6cbf0ce --- /dev/null +++ b/network/trans/inspect/sys/utils.c @@ -0,0 +1,448 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + This file implements the utility/helper functions for use by the classify + functions and worker thread of the Transport Inspect sample. + +Environment: + + Kernel mode + +--*/ + + +#include +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + +#include + +#include "inspect.h" +#include "utils.h" + + +BOOLEAN IsAleReauthorize( + _In_ const FWPS_INCOMING_VALUES* inFixedValues + ) +{ + UINT flagsIndex; + + GetFlagsIndexesForLayer( + inFixedValues->layerId, + &flagsIndex + ); + + if((flagsIndex != UINT_MAX) && ((inFixedValues->incomingValue\ + [flagsIndex].value.uint32 & FWP_CONDITION_FLAG_IS_REAUTHORIZE) != 0)) + { + return TRUE; + } + + return FALSE; +} + +BOOLEAN IsSecureConnection( + _In_ const FWPS_INCOMING_VALUES* inFixedValues + ) +{ + UINT flagsIndex; + + GetFlagsIndexesForLayer( + inFixedValues->layerId, + &flagsIndex + ); + + if ((flagsIndex != UINT_MAX) && ((inFixedValues->incomingValue\ + [flagsIndex].value.uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED) != 0)) + { + return TRUE; + } + + return FALSE; +} + +BOOLEAN +IsAleClassifyRequired( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues + ) +{ + // + // Note that use of FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY has been + // deprecated in Vista SP1 and Windows Server 2008. + // + UNREFERENCED_PARAMETER(inFixedValues); + return FWPS_IS_METADATA_FIELD_PRESENT( + inMetaValues, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED + ); +} + +BOOLEAN +IsMatchingConnectPacket( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ ADDRESS_FAMILY addressFamily, + _In_ FWP_DIRECTION direction, + _Inout_ TL_INSPECT_PENDED_PACKET* pendedPacket + ) +{ + UINT localAddrIndex; + UINT remoteAddrIndex; + UINT localPortIndex; + UINT remotePortIndex; + UINT protocolIndex; + + NT_ASSERT(pendedPacket->type == TL_INSPECT_CONNECT_PACKET); + + GetNetwork5TupleIndexesForLayer( + inFixedValues->layerId, + &localAddrIndex, + &remoteAddrIndex, + &localPortIndex, + &remotePortIndex, + &protocolIndex + ); + + if(localAddrIndex == UINT_MAX) + { + return FALSE; + } + + if (addressFamily != pendedPacket->addressFamily) + { + return FALSE; + } + + if (direction != pendedPacket->direction) + { + return FALSE; + } + + if (inFixedValues->incomingValue[protocolIndex].value.uint8 != + pendedPacket->protocol) + { + return FALSE; + } + + if (RtlUshortByteSwap( + inFixedValues->incomingValue[localPortIndex].value.uint16 + ) != pendedPacket->localPort) + { + return FALSE; + } + + if (RtlUshortByteSwap( + inFixedValues->incomingValue[remotePortIndex].value.uint16 + ) != pendedPacket->remotePort) + { + return FALSE; + } + + if (addressFamily == AF_INET) + { + UINT32 ipv4LocalAddr = + RtlUlongByteSwap( + inFixedValues->incomingValue[localAddrIndex].value.uint32 + ); + UINT32 ipv4RemoteAddr = + // Prefast thinks we are ignoring this return value. + // If driver is unloading, we give up and ignore it on purpose. + // Otherwise, we put the pointer onto the list, but we make it opaque + // by casting it as a UINT64, and this tricks Prefast. + RtlUlongByteSwap( /* host-order -> network-order conversion */ + inFixedValues->incomingValue[remoteAddrIndex].value.uint32 + ); + if (ipv4LocalAddr != pendedPacket->ipv4LocalAddr) + { + return FALSE; + } + + if (ipv4RemoteAddr != pendedPacket->ipv4RemoteAddr) + { + return FALSE; + } + } + else + { + if (RtlCompareMemory( + inFixedValues->incomingValue[localAddrIndex].value.byteArray16, + &pendedPacket->localAddr, + sizeof(FWP_BYTE_ARRAY16)) != sizeof(FWP_BYTE_ARRAY16)) + { + return FALSE; + } + + if (RtlCompareMemory( + inFixedValues->incomingValue[remoteAddrIndex].value.byteArray16, + &pendedPacket->remoteAddr, + sizeof(FWP_BYTE_ARRAY16)) != sizeof(FWP_BYTE_ARRAY16)) + { + return FALSE; + } + } + + return TRUE; +} + +void +FillNetwork5Tuple( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ ADDRESS_FAMILY addressFamily, + _Inout_ TL_INSPECT_PENDED_PACKET* packet + ) +{ + UINT localAddrIndex; + UINT remoteAddrIndex; + UINT localPortIndex; + UINT remotePortIndex; + UINT protocolIndex; + + GetNetwork5TupleIndexesForLayer( + inFixedValues->layerId, + &localAddrIndex, + &remoteAddrIndex, + &localPortIndex, + &remotePortIndex, + &protocolIndex + ); + + if (addressFamily == AF_INET) + { + packet->ipv4LocalAddr = + RtlUlongByteSwap( /* host-order -> network-order conversion */ + inFixedValues->incomingValue[localAddrIndex].value.uint32 + ); + packet->ipv4RemoteAddr = + RtlUlongByteSwap( /* host-order -> network-order conversion */ + inFixedValues->incomingValue[remoteAddrIndex].value.uint32 + ); + } + else + { + RtlCopyMemory( + (UINT8*)&packet->localAddr, + inFixedValues->incomingValue[localAddrIndex].value.byteArray16, + sizeof(FWP_BYTE_ARRAY16) + ); + RtlCopyMemory( + (UINT8*)&packet->remoteAddr, + inFixedValues->incomingValue[remoteAddrIndex].value.byteArray16, + sizeof(FWP_BYTE_ARRAY16) + ); + } + + packet->localPort = + RtlUshortByteSwap( + inFixedValues->incomingValue[localPortIndex].value.uint16 + ); + packet->remotePort = + RtlUshortByteSwap( + inFixedValues->incomingValue[remotePortIndex].value.uint16 + ); + + packet->protocol = inFixedValues->incomingValue[protocolIndex].value.uint8; + + return; +} + +void +FreePendedPacket( + _Inout_ __drv_freesMem(Mem) TL_INSPECT_PENDED_PACKET* packet + ) +{ + if (packet->netBufferList != NULL) + { + FwpsDereferenceNetBufferList(packet->netBufferList, FALSE); + } + if (packet->controlData != NULL) + { + ExFreePoolWithTag(packet->controlData, TL_INSPECT_CONTROL_DATA_POOL_TAG); + } + if (packet->completionContext != NULL) + { + NT_ASSERT(packet->type == TL_INSPECT_CONNECT_PACKET); + NT_ASSERT(packet->direction == FWP_DIRECTION_INBOUND); // complete for ALE connect + // is done prior to freeing + // of the packet. + FwpsCompleteOperation(packet->completionContext, NULL); + } + ExFreePoolWithTag(packet, TL_INSPECT_PENDED_PACKET_POOL_TAG); +} + +__drv_allocatesMem(Mem) +TL_INSPECT_PENDED_PACKET* +AllocateAndInitializePendedPacket( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _In_ ADDRESS_FAMILY addressFamily, + _Inout_opt_ void* layerData, + _In_ TL_INSPECT_PACKET_TYPE packetType, + _In_ FWP_DIRECTION packetDirection + ) +{ + TL_INSPECT_PENDED_PACKET* pendedPacket; + + pendedPacket = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(TL_INSPECT_PENDED_PACKET), + TL_INSPECT_PENDED_PACKET_POOL_TAG + ); + + if (pendedPacket == NULL) + { + return NULL; + } + + RtlZeroMemory(pendedPacket, sizeof(TL_INSPECT_PENDED_PACKET)); + + pendedPacket->type = packetType; + pendedPacket->direction = packetDirection; + + pendedPacket->addressFamily = addressFamily; + + FillNetwork5Tuple( + inFixedValues, + addressFamily, + pendedPacket + ); + + if (layerData != NULL) + { + pendedPacket->netBufferList = layerData; + + // + // Reference the net buffer list to make it accessible outside of + // classifyFn. + // + FwpsReferenceNetBufferList(pendedPacket->netBufferList, TRUE); + } + + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, + FWPS_METADATA_FIELD_COMPARTMENT_ID)); + pendedPacket->compartmentId = inMetaValues->compartmentId; + + if ((pendedPacket->direction == FWP_DIRECTION_OUTBOUND) && + (layerData != NULL)) + { + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT( + inMetaValues, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)); + pendedPacket->endpointHandle = inMetaValues->transportEndpointHandle; + + pendedPacket->remoteScopeId = inMetaValues->remoteScopeId; + + if (FWPS_IS_METADATA_FIELD_PRESENT( + inMetaValues, + FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA)) + { + NT_ASSERT(inMetaValues->controlDataLength > 0); + + pendedPacket->controlData = ExAllocatePoolWithTag( + NonPagedPool, + inMetaValues->controlDataLength, + TL_INSPECT_CONTROL_DATA_POOL_TAG + ); + if (pendedPacket->controlData == NULL) + { + goto Exit; + } + + RtlCopyMemory( + pendedPacket->controlData, + inMetaValues->controlData, + inMetaValues->controlDataLength + ); + + pendedPacket->controlDataLength = inMetaValues->controlDataLength; + } + } + else if (pendedPacket->direction == FWP_DIRECTION_INBOUND) + { + UINT interfaceIndexIndex = 0; + UINT subInterfaceIndexIndex = 0; + + GetDeliveryInterfaceIndexesForLayer( + inFixedValues->layerId, + &interfaceIndexIndex, + &subInterfaceIndexIndex + ); + + pendedPacket->interfaceIndex = + inFixedValues->incomingValue[interfaceIndexIndex].value.uint32; + pendedPacket->subInterfaceIndex = + inFixedValues->incomingValue[subInterfaceIndexIndex].value.uint32; + + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT( + inMetaValues, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)); + NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT( + inMetaValues, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)); + pendedPacket->ipHeaderSize = inMetaValues->ipHeaderSize; + pendedPacket->transportHeaderSize = inMetaValues->transportHeaderSize; + + if (pendedPacket->netBufferList != NULL) + { + FWPS_PACKET_LIST_INFORMATION packetInfo = {0}; + FwpsGetPacketListSecurityInformation( + pendedPacket->netBufferList, + FWPS_PACKET_LIST_INFORMATION_QUERY_IPSEC | + FWPS_PACKET_LIST_INFORMATION_QUERY_INBOUND, + &packetInfo + ); + + pendedPacket->ipSecProtected = + (BOOLEAN)packetInfo.ipsecInformation.inbound.isSecure; + + pendedPacket->nblOffset = + NET_BUFFER_DATA_OFFSET(\ + NET_BUFFER_LIST_FIRST_NB(pendedPacket->netBufferList)); + } + } + + return pendedPacket; + +Exit: + + if (pendedPacket != NULL) + { + FreePendedPacket(pendedPacket); + } + + return NULL; +} + +extern WDFKEY gParametersKey; + +BOOLEAN +IsTrafficPermitted(void) +{ + NTSTATUS status; + + DECLARE_CONST_UNICODE_STRING(valueName, L"PermitTraffic"); + ULONG result; + + status = WdfRegistryQueryULong( + gParametersKey, + &valueName, + &result + ); + + if (!NT_SUCCESS(status)) + { + result = 1; + } + + return (result != 0); +} + + diff --git a/network/trans/inspect/sys/utils.h b/network/trans/inspect/sys/utils.h new file mode 100644 index 00000000..3570cd8a --- /dev/null +++ b/network/trans/inspect/sys/utils.h @@ -0,0 +1,298 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + This file declares the utility/helper functions for use by the classify + functions and worker thread of the Transport Inspect sample. + +Environment: + + Kernel mode + +--*/ + +#include + +#ifndef _TL_INSPECT_UTILS_H_ +#define _TL_INSPECT_UTILS_H_ + +__inline +ADDRESS_FAMILY GetAddressFamilyForLayer( + _In_ UINT16 layerId + ) +{ + ADDRESS_FAMILY addressFamily; + + switch (layerId) + { + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + addressFamily = AF_INET; + break; + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + addressFamily = AF_INET6; + break; + default: + addressFamily = AF_UNSPEC; + NT_ASSERT(0); + } + + return addressFamily; +} + +__inline +FWP_DIRECTION GetPacketDirectionForLayer( + _In_ UINT16 layerId + ) +{ + FWP_DIRECTION direction; + + switch (layerId) + { + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + direction = FWP_DIRECTION_OUTBOUND; + break; + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + direction = FWP_DIRECTION_INBOUND; + break; + default: + direction = FWP_DIRECTION_MAX; + NT_ASSERT(0); + } + + return direction; +} + +__inline +void +GetFlagsIndexesForLayer( + _In_ UINT16 layerId, + _Out_ UINT* flagsIndex + ) +{ + switch (layerId) + { + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + *flagsIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V4_FLAGS; + break; + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + *flagsIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V6_FLAGS; + break; + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + *flagsIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_FLAGS; + break; + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + *flagsIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_FLAGS; + break; + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + *flagsIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V4_FLAGS; + break; + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + *flagsIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V6_FLAGS; + break; + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + *flagsIndex = FWPS_FIELD_INBOUND_TRANSPORT_V4_FLAGS; + break; + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + *flagsIndex = FWPS_FIELD_INBOUND_TRANSPORT_V6_FLAGS; + break; + default: + *flagsIndex = UINT_MAX; + NT_ASSERT(0); + break; + } +} + +__inline +void +GetDeliveryInterfaceIndexesForLayer( + _In_ UINT16 layerId, + _Out_ UINT* interfaceIndexIndex, + _Out_ UINT* subInterfaceIndexIndex + ) +{ + *interfaceIndexIndex = 0; + + *subInterfaceIndexIndex = 0; + + switch (layerId) + { + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + *interfaceIndexIndex = + FWPS_FIELD_ALE_AUTH_CONNECT_V4_INTERFACE_INDEX; + *subInterfaceIndexIndex = + FWPS_FIELD_ALE_AUTH_CONNECT_V4_SUB_INTERFACE_INDEX; + break; + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + *interfaceIndexIndex = + FWPS_FIELD_ALE_AUTH_CONNECT_V6_INTERFACE_INDEX; + *subInterfaceIndexIndex = + FWPS_FIELD_ALE_AUTH_CONNECT_V6_SUB_INTERFACE_INDEX; + break; + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + *interfaceIndexIndex = + FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_INTERFACE_INDEX; + *subInterfaceIndexIndex = + FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_SUB_INTERFACE_INDEX; + break; + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + *interfaceIndexIndex = + FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_INTERFACE_INDEX; + *subInterfaceIndexIndex = + FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_SUB_INTERFACE_INDEX; + break; + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + *interfaceIndexIndex = + FWPS_FIELD_INBOUND_TRANSPORT_V4_INTERFACE_INDEX; + *subInterfaceIndexIndex = + FWPS_FIELD_INBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX; + break; + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + *interfaceIndexIndex = + FWPS_FIELD_INBOUND_TRANSPORT_V6_INTERFACE_INDEX; + *subInterfaceIndexIndex = + FWPS_FIELD_INBOUND_TRANSPORT_V6_SUB_INTERFACE_INDEX; + break; + default: + NT_ASSERT(0); + break; + } +} + +__inline +void +GetNetwork5TupleIndexesForLayer( + _In_ UINT16 layerId, + _Out_ UINT* localAddressIndex, + _Out_ UINT* remoteAddressIndex, + _Out_ UINT* localPortIndex, + _Out_ UINT* remotePortIndex, + _Out_ UINT* protocolIndex + ) +{ + switch (layerId) + { + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + *localAddressIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS; + *remoteAddressIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS; + *localPortIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT; + *remotePortIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT; + *protocolIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL; + break; + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + *localAddressIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_LOCAL_ADDRESS; + *remoteAddressIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_ADDRESS; + *localPortIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_LOCAL_PORT; + *remotePortIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_PORT; + *protocolIndex = FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_PROTOCOL; + break; + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + *localAddressIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS; + *remoteAddressIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_ADDRESS; + *localPortIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_PORT; + *remotePortIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_PORT; + *protocolIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL; + break; + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + *localAddressIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_LOCAL_ADDRESS; + *remoteAddressIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_REMOTE_ADDRESS; + *localPortIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_LOCAL_PORT; + *remotePortIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_REMOTE_PORT; + *protocolIndex = FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_PROTOCOL; + break; + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + *localAddressIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS; + *remoteAddressIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS; + *localPortIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_PORT; + *remotePortIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_PORT; + *protocolIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_PROTOCOL; + break; + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + *localAddressIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V6_IP_LOCAL_ADDRESS; + *remoteAddressIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V6_IP_REMOTE_ADDRESS; + *localPortIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V6_IP_LOCAL_PORT; + *remotePortIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V6_IP_REMOTE_PORT; + *protocolIndex = FWPS_FIELD_OUTBOUND_TRANSPORT_V6_IP_PROTOCOL; + break; + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + *localAddressIndex = FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS; + *remoteAddressIndex = FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS; + *localPortIndex = FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_PORT; + *remotePortIndex = FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_REMOTE_PORT; + *protocolIndex = FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_PROTOCOL; + break; + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + *localAddressIndex = FWPS_FIELD_INBOUND_TRANSPORT_V6_IP_LOCAL_ADDRESS; + *remoteAddressIndex = FWPS_FIELD_INBOUND_TRANSPORT_V6_IP_REMOTE_ADDRESS; + *localPortIndex = FWPS_FIELD_INBOUND_TRANSPORT_V6_IP_LOCAL_PORT; + *remotePortIndex = FWPS_FIELD_INBOUND_TRANSPORT_V6_IP_REMOTE_PORT; + *protocolIndex = FWPS_FIELD_INBOUND_TRANSPORT_V6_IP_PROTOCOL; + break; + default: + *localAddressIndex = UINT_MAX; + *remoteAddressIndex = UINT_MAX; + *localPortIndex = UINT_MAX; + *remotePortIndex = UINT_MAX; + *protocolIndex = UINT_MAX; + NT_ASSERT(0); + } +} + +BOOLEAN IsAleReauthorize( + _In_ const FWPS_INCOMING_VALUES* inFixedValues + ); + +BOOLEAN IsSecureConnection( + _In_ const FWPS_INCOMING_VALUES* inFixedValues + ); + +BOOLEAN +IsAleClassifyRequired( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues + ); + +void +FillNetwork5Tuple( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ ADDRESS_FAMILY addressFamily, + _Inout_ TL_INSPECT_PENDED_PACKET* packet + ); + +BOOLEAN +IsMatchingConnectPacket( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ ADDRESS_FAMILY addressFamily, + _In_ FWP_DIRECTION direction, + _Inout_ TL_INSPECT_PENDED_PACKET* pendedPacket + ); + +__drv_allocatesMem(Mem) +TL_INSPECT_PENDED_PACKET* +AllocateAndInitializePendedPacket( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _In_ ADDRESS_FAMILY addressFamily, + _Inout_opt_ void* layerData, + _In_ TL_INSPECT_PACKET_TYPE packetType, + _In_ FWP_DIRECTION packetDirection + ); + +void +FreePendedPacket( + _Inout_ __drv_freesMem(Mem) TL_INSPECT_PENDED_PACKET* packet + ); + +BOOLEAN +IsTrafficPermitted(void); + +#endif // _TL_INSPECT_UTILS_H_ diff --git a/network/trans/msnmntr/ReadMe.md b/network/trans/msnmntr/ReadMe.md new file mode 100644 index 00000000..9f247ff5 --- /dev/null +++ b/network/trans/msnmntr/ReadMe.md @@ -0,0 +1,79 @@ +Windows Filtering Platform MSN Messenger Monitor Sample +======================================================= + +This sample application and driver demonstrate the stream inspection capabilities of the Windows Filtering Platform (WFP). + +The sample consists of a user mode application (Monitor.exe) that registers traffic of interest. In this case, all Transmission Control Protocol (TCP) data segments that are sent and received by an application of your choice. + +**Note**  Originally this sample was written to monitor the MSN Messenger application. Now it can monitor any application that you specify. + +Monitor.exe adds filters and callouts to Windows through the Windows Filtering Platform (WFP) Win32 API. A kernel-mode WFP callout driver (Msnmntr.sys) intercepts TCP traffic and parses out communication patterns. Monitor.exe controls the operations of the callout driver through I/O controls (IOCTLs). + +The filters and callouts added by Monitor.exe are persistent across system restarts and removed only by Monitor.exe. Adding filters and callouts requires administrator privileges. Therefore, Monitor.exe must be run from an elevated command prompt. + +Msnmntr.sys registers itself at two different WFP layers: FLOW-ESTABLISHED and STREAM. For simplicity, only Internet Protocol version 4 (IPv4) traffic is inspected. Msnmntr.sys registers at the FLOW-ESTABLISHED layer to associate a callout driver-specific data structure with application identity (that is, path) recorded such that the STREAM layer will only be invoked if traffic is sent or received from that particular application. + +After the filters and callouts are in place and registered, WFP indicates TCP data segments to the Msnmntr.sys for inspection. As the data flows through Msnmntr.sys, it copies them (described by a chain of NET\_BUFFER\_LIST structures) to a flat buffer, parses out the communication patterns (such as client-to-server/client-to-client), and sends them to the Windows Software Trace Preprocessor (WPP) for tracing. + +Automatic deployment +-------------------- + +Before you automatically deploy a driver, you must provision the target computer. For instructions, see [Configuring a Computer for Driver Deployment, Testing, and Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/). After you have provisioned the target computer, continue with these steps: + +1. On the host computer, in Visual Studio, in Solution Explorer, right click **package** (lower case), and choose **Properties**. Navigate to **Configuration Properties \> Driver Install \> Deployment**. +2. Check **Enable deployment**, and check **Remove previous driver versions before deployment**. For **Target Computer Name**, select the name of a target computer that you provisioned previously. Select **Do not install**. Click **OK**. +3. On the **Build** menu, choose **Build Solution**. +4. On the target computer, navigate to DriverTest\\Drivers, and locate the file msnmntr.inf. Right click msnmntr.inf, and choose **Install**. + +Manual deployment +----------------- + +Before you manually deploy a driver, you must turn on test signing and install a certificate on the target computer. You also need to copy the [DevCon](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544707) tool to the target computer. For instructions, see [Preparing a Computer for Manual Driver Deployment](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265571). After you have prepared the target computer for manual deployment, continue with these steps: + +1. Copy all of the files in your driver package to a folder on the target computer (for example, c:\\WfpMsnMessengerMonitorSamplePackage). +2. On the target computer, navigate to your driver package folder. Right click msnmntr.inf, and choose **Install** + +Copy additional files to the target computer +-------------------------------------------- + +Copy the user-mode application, monitor.exe to a folder on the target computer (for example, c:\\WfpMsnMessengerMonitorSampleApp). + +Copy the PDB file, msnmntr.pdb to a folder on the target computer (for example, c:\\Symbols). + +Copy the tool TraceView.exe to a folder on the target computer (for example c:\\Tools). TraceView.exe comes with the WDK. You can find it in your WDK installation folder under Tools (for example, c:\\Program Files (x86)\\Windows Kits\\8.1\\Tools\\x64\\TraceView.exe). + +Start the msnmntr service +------------------------- + +On the target computer, open a Command Prompt window as Administrator, and enter **net start msnmntr**. (To stop the driver, enter **net stop msnmntr**.) + +Running the user-mode application +--------------------------------- + +On the target computer, open a Command Prompt window as Administrator, and navigate to the folder that contains monitor.exe. Enter **monitor.exe addcallouts**. Then enter **monitor.exe monitor** *TargetAppPath*, where *TargetAppPath* is the path to the application that you want to monitor. Here is an example that initiates monitoring of Internet Explorer. + +``` {.syntax xml:space="preserve"} +monitor.exe addcallouts +monitor.exe monitor "C:\Program Files (x86)\Internet Explorer\iexplore.exe" +``` + +Start a logging session in TraceView +------------------------------------ + +On the target computer, open TraceView.exe as Administrator. On the **File** menu, choose **Create New Log Session**. Click **Add Provider**. Select **PDB (Debug Information File)**, and enter the path to your PDB file, msnmntr.pdb. Click **OK**, and finish working through the setup procedure. Open Internet Explorer, and watch the communication patterns being displayed in the Traceview.exe tool. + +Tracing for the sample driver can be started at any time before the driver is started or while the driver is already running. + +For more information on creating a Windows Filtering Platform Callout Driver, see [Windows Filtering Platform Callout Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff571068). + +Using MSBuild +------------- + +As an alternative to building the WFP MSN Messenger Monitor Sample in Visual Studio, you can build it in a Visual Studio Command Prompt window. In Visual Studio, on the **Tools** menu, choose **Visual Studio Command Prompt**. In the Visual Studio Command Prompt window, navigate to the folder that has the solution file, msnmntr.sln. Use the [MSBuild](http://go.microsoft.com/fwlink/p/?linkID=262804) command to build the solution. Here are some examples: + +**msbuild /p:configuration=â€Win7 Debug†/p:platform=â€x64†msnmntr.sln** + +**msbuild /p:configuration=â€Win8 Release†/p:platform=â€win32†msnmntr.sln** + +For more information about using [MSBuild](http://go.microsoft.com/fwlink/p/?linkID=262804) to build a driver package, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + diff --git a/network/trans/msnmntr/exe/monitor.cpp b/network/trans/msnmntr/exe/monitor.cpp new file mode 100644 index 00000000..def6625c --- /dev/null +++ b/network/trans/msnmntr/exe/monitor.cpp @@ -0,0 +1,715 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Stream monitor sample executable + +Environment: + + User mode + +--*/ + +#include "windows.h" +#include "winioctl.h" +#include "strsafe.h" + +#ifndef _CTYPE_DISABLE_MACROS +#define _CTYPE_DISABLE_MACROS +#endif + +#include "fwpmu.h" + +#include "winsock2.h" +#include "ws2def.h" + +#include +#include + +#include "ioctl.h" + +#define INITGUID +#include +#include "mntrguid.h" + + +#define MONITOR_FLOW_ESTABLISHED_CALLOUT_DESCRIPTION L"Monitor Sample - Flow Established Callout" +#define MONITOR_FLOW_ESTABLISHED_CALLOUT_NAME L"Flow Established Callout" + +#define MONITOR_STREAM_CALLOUT_DESCRIPTION L"Monitor Sample - Stream Callout" +#define MONITOR_STREAM_CALLOUT_NAME L"Stream Callout" + +HANDLE quitEvent; + +DWORD +MonitorAppOpenMonitorDevice( + _Out_ HANDLE* monitorDevice) +/*++ + +Routine Description: + + Opens the Monitor Sample monitorDevice + +Arguments: + + [out] HANDLE* monitorDevice + +Return Value: + + NO_ERROR, ERROR_INVALID_PARAMETER or a CreateFile specific result. + +--*/ +{ + if (!monitorDevice) + { + return ERROR_INVALID_PARAMETER; + } + *monitorDevice = CreateFileW(MONITOR_DOS_NAME, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (*monitorDevice == INVALID_HANDLE_VALUE) + { + return GetLastError(); + } + + return NO_ERROR; +} + +BOOL MonitorAppCloseMonitorDevice( + _In_ HANDLE monitorDevice) +/*++ + +Routine Description: + + Closes the Monitor Sample monitorDevice + +Arguments: + +Return Value: + + None. + +--*/ +{ + return CloseHandle(monitorDevice); +} + +DWORD +MonitorAppAddCallouts() +/*++ + +Routine Description: + + Adds the callouts during installation + +Arguments: + + [in] PCWSTR AppPath - The path to the application to monitor. + +Return Value: + + NO_ERROR or a specific FWP result. + +--*/ +{ + FWPM_CALLOUT callout; + DWORD result; + FWPM_DISPLAY_DATA displayData; + HANDLE engineHandle = NULL; + FWPM_SESSION session; + RtlZeroMemory(&session, sizeof(FWPM_SESSION)); + + session.displayData.name = L"Monitor Sample Non-Dynamic Session"; + session.displayData.description = L"For Adding callouts"; + + printf("Opening Filtering Engine\n"); + result = FwpmEngineOpen( + NULL, + RPC_C_AUTHN_WINNT, + NULL, + &session, + &engineHandle + ); + + if (NO_ERROR != result) + { + goto cleanup; + } + + printf("Starting Transaction for adding callouts\n"); + result = FwpmTransactionBegin(engineHandle, 0); + if (NO_ERROR != result) + { + goto abort; + } + + printf("Successfully started the Transaction\n"); + + RtlZeroMemory(&callout, sizeof(FWPM_CALLOUT)); + displayData.description = MONITOR_FLOW_ESTABLISHED_CALLOUT_DESCRIPTION; + displayData.name = MONITOR_FLOW_ESTABLISHED_CALLOUT_NAME; + + callout.calloutKey = MONITOR_SAMPLE_FLOW_ESTABLISHED_CALLOUT_V4; + callout.displayData = displayData; + callout.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4; + callout.flags = FWPM_CALLOUT_FLAG_PERSISTENT; // Make this a persistent callout. + + printf("Adding Persistent Flow Established callout through the Filtering Engine\n"); + + result = FwpmCalloutAdd(engineHandle, &callout, NULL, NULL); + if (NO_ERROR != result) + { + goto abort; + } + + printf("Successfully Added Persistent Flow Established callout.\n"); + + RtlZeroMemory(&callout, sizeof(FWPM_CALLOUT)); + + displayData.description = MONITOR_STREAM_CALLOUT_DESCRIPTION; + displayData.name = MONITOR_STREAM_CALLOUT_DESCRIPTION; + + callout.calloutKey = MONITOR_SAMPLE_STREAM_CALLOUT_V4; + callout.displayData = displayData; + callout.applicableLayer = FWPM_LAYER_STREAM_V4; + callout.flags = FWPM_CALLOUT_FLAG_PERSISTENT; // Make this a persistent callout. + + printf("Adding Persistent Stream callout through the Filtering Engine\n"); + + result = FwpmCalloutAdd(engineHandle, &callout, NULL, NULL); + if (NO_ERROR != result) + { + goto abort; + } + + printf("Successfully Added Persistent Stream callout.\n"); + + printf("Committing Transaction\n"); + result = FwpmTransactionCommit(engineHandle); + if (NO_ERROR == result) + { + printf("Successfully Committed Transaction.\n"); + } + goto cleanup; + +abort: + printf("Aborting Transaction\n"); + result = FwpmTransactionAbort(engineHandle); + if (NO_ERROR == result) + { + printf("Successfully Aborted Transaction.\n"); + } + +cleanup: + + if (engineHandle) + { + FwpmEngineClose(engineHandle); + } + return result; +} + +DWORD +MonitorAppRemoveCallouts() +/*++ + +Routine Description: + + Sets the kernel callout ID's through the Monitor Sample device + +Arguments: + + [in] HANDLE monitorDevice - Monitor Sample device + [in] CALLOUTS* callouts - Callout structure with ID's set + [in] DWORD size - Size of the callout structure. + +Return Value: + + NO_ERROR or a specific DeviceIoControl result. + +--*/ +{ + DWORD result; + HANDLE engineHandle = NULL; + FWPM_SESSION session; + + RtlZeroMemory(&session, sizeof(FWPM_SESSION)); + + session.displayData.name = L"Monitor Sample Non-Dynamic Session"; + session.displayData.description = L"For Adding callouts"; + + printf("Opening Filtering Engine\n"); + result = FwpmEngineOpen( + NULL, + RPC_C_AUTHN_WINNT, + NULL, + &session, + &engineHandle + ); + + if (NO_ERROR != result) + { + goto cleanup; + } + + printf("Starting Transaction for Removing callouts\n"); + + result = FwpmTransactionBegin(engineHandle, 0); + if (NO_ERROR != result) + { + goto abort; + } + printf("Successfully started the Transaction\n"); + + printf("Deleting Flow Established callout\n"); + result = FwpmCalloutDeleteByKey(engineHandle, + &MONITOR_SAMPLE_FLOW_ESTABLISHED_CALLOUT_V4); + if (NO_ERROR != result) + { + goto abort; + } + + printf("Successfully Deleted Flow Established callout\n"); + + printf("Deleting Stream callout\n"); + + result = FwpmCalloutDeleteByKey(engineHandle, + &MONITOR_SAMPLE_STREAM_CALLOUT_V4); + if (NO_ERROR != result) + { + goto abort; + } + printf("Successfully Deleted Stream callout\n"); + + printf("Committing Transaction\n"); + result = FwpmTransactionCommit(engineHandle); + if (NO_ERROR == result) + { + printf("Successfully Committed Transaction.\n"); + } + goto cleanup; + +abort: + printf("Aborting Transaction\n"); + result = FwpmTransactionAbort(engineHandle); + if (NO_ERROR == result) + { + printf("Successfully Aborted Transaction.\n"); + } + +cleanup: + + if (engineHandle) + { + FwpmEngineClose(engineHandle); + } + + return result; +} + +DWORD +MonitorAppEnableMonitoring( + _In_ HANDLE monitorDevice, + _In_ MONITOR_SETTINGS* monitorSettings) +/*++ + +Routine Description: + + Enables monitoring on new connections. + +Arguments: + + [in] HANDLE monitorDevice - Monitor Sample device + [in] MONITOR_SETTINGS* monitorSettings - Settings for the Monitor Sample driver. + +Return Value: + + NO_ERROR or a specific DeviceIoControl result. + +--*/ +{ + DWORD bytesReturned; + + if (!DeviceIoControl(monitorDevice, + MONITOR_IOCTL_ENABLE_MONITOR, + monitorSettings, + sizeof(MONITOR_SETTINGS), + NULL, + 0, + &bytesReturned, + NULL)) + { + return GetLastError(); + } + + return NO_ERROR; +} + +DWORD +MonitorAppDisableMonitoring( + _In_ HANDLE monitorDevice) +/*++ + +Routine Description: + + Disables monitoring of new flows (existing flows will continue to be + monitored until the driver is stopped or the flows end). + +Arguments: + + [in] HANDLE monitorDevice - Monitor Sample device handle. + +Return Value: + + NO_ERROR or DeviceIoControl specific code. + +--*/ +{ + DWORD bytesReturned; + + if (!DeviceIoControl(monitorDevice, + MONITOR_IOCTL_DISABLE_MONITOR, + NULL, + 0, + NULL, + 0, + &bytesReturned, + NULL)) + { + return GetLastError(); + } + + return NO_ERROR; +} + +DWORD +MonitorAppAddFilters( + _In_ HANDLE engineHandle, + _In_ FWP_BYTE_BLOB* applicationPath) +/*++ + +Routine Description: + + Adds the required sublayer, filters and callouts to the Windows + Filtering Platform (WFP). + +Arguments: + + [in] HANDLE engineHandle - Handle to the base Filtering engine + [in] FWP_BYTE_BLOB* applicationPath - full path to the application including + the NULL terminator and size also + including the NULL the terminator + [in] CALLOUTS* callouts - The callouts that need to be added. + +Return Value: + + NO_ERROR or a specific result + +--*/ +{ + DWORD result = NO_ERROR; + FWPM_SUBLAYER monitorSubLayer; + FWPM_FILTER filter; + FWPM_FILTER_CONDITION filterConditions[2]; // We only need two for this call. + + RtlZeroMemory(&monitorSubLayer, sizeof(FWPM_SUBLAYER)); + + monitorSubLayer.subLayerKey = MONITOR_SAMPLE_SUBLAYER; + monitorSubLayer.displayData.name = L"Monitor Sample Sub layer"; + monitorSubLayer.displayData.description = L"Monitor Sample Sub layer"; + monitorSubLayer.flags = 0; + // We don't really mind what the order of invocation is. + monitorSubLayer.weight = 0; + + printf("Starting Transaction\n"); + + result = FwpmTransactionBegin(engineHandle, 0); + if (NO_ERROR != result) + { + goto abort; + } + printf("Successfully Started Transaction\n"); + + printf("Adding Sublayer\n"); + + result = FwpmSubLayerAdd(engineHandle, &monitorSubLayer, NULL); + if (NO_ERROR != result) + { + goto abort; + } + + printf("Sucessfully added Sublayer\n"); + + RtlZeroMemory(&filter, sizeof(FWPM_FILTER)); + + filter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4; + filter.displayData.name = L"Flow established filter."; + filter.displayData.description = L"Sets up flow for traffic that we are interested in."; + filter.action.type = FWP_ACTION_CALLOUT_INSPECTION; // We're only doing inspection. + filter.action.calloutKey = MONITOR_SAMPLE_FLOW_ESTABLISHED_CALLOUT_V4; + filter.filterCondition = filterConditions; + filter.subLayerKey = monitorSubLayer.subLayerKey; + filter.weight.type = FWP_EMPTY; // auto-weight. + + filter.numFilterConditions = 2; + + RtlZeroMemory(filterConditions, sizeof(filterConditions)); + + // + // Add the application path to the filter conditions. + // + filterConditions[0].fieldKey = FWPM_CONDITION_ALE_APP_ID; + filterConditions[0].matchType = FWP_MATCH_EQUAL; + filterConditions[0].conditionValue.type = FWP_BYTE_BLOB_TYPE; + filterConditions[0].conditionValue.byteBlob = applicationPath; + + // + // For the purposes of this sample, we will monitor TCP traffic only. + // + filterConditions[1].fieldKey = FWPM_CONDITION_IP_PROTOCOL; + filterConditions[1].matchType = FWP_MATCH_EQUAL; + filterConditions[1].conditionValue.type = FWP_UINT8; + filterConditions[1].conditionValue.uint8 = IPPROTO_TCP; + + printf("Adding Flow Established Filter\n"); + + result = FwpmFilterAdd(engineHandle, + &filter, + NULL, + NULL); + + if (NO_ERROR != result) + { + goto abort; + } + + printf("Successfully added Flow Established filter\n"); + + RtlZeroMemory(&filter, sizeof(FWPM_FILTER)); + + filter.layerKey = FWPM_LAYER_STREAM_V4; + filter.action.type = FWP_ACTION_CALLOUT_INSPECTION; // We're only doing inspection. + filter.action.calloutKey = MONITOR_SAMPLE_STREAM_CALLOUT_V4; + filter.subLayerKey = monitorSubLayer.subLayerKey; + filter.weight.type = FWP_EMPTY; // auto-weight. + + filter.numFilterConditions = 0; + + RtlZeroMemory(filterConditions, sizeof(filterConditions)); + + filter.filterCondition = filterConditions; + + filter.displayData.name = L"Stream Layer Filter"; + filter.displayData.description = L"Monitors TCP traffic."; + + printf("Adding Stream Filter\n"); + + result = FwpmFilterAdd(engineHandle, + &filter, + NULL, + NULL); + + if (NO_ERROR != result) + { + goto abort; + } + + printf("Successfully added Stream filter\n"); + + printf("Committing Transaction\n"); + result = FwpmTransactionCommit(engineHandle); + if (NO_ERROR == result) + { + printf("Successfully Committed Transaction\n"); + } + goto cleanup; + +abort: + printf("Aborting Transaction\n"); + result = FwpmTransactionAbort(engineHandle); + if (NO_ERROR == result) + { + printf("Successfully Aborted Transaction\n"); + } + +cleanup: + + return result; +} + +DWORD +MonitorAppIDFromPath( + _In_ PCWSTR fileName, + _Out_ FWP_BYTE_BLOB** appId) +{ + DWORD result = NO_ERROR; + + result = FwpmGetAppIdFromFileName(fileName, appId); + + return result; +} + +DWORD +MonitorAppDoMonitoring(PCWSTR AppPath) +{ + HANDLE monitorDevice = NULL; + HANDLE engineHandle = NULL; + DWORD result; + MONITOR_SETTINGS monitorSettings; + FWPM_SESSION session; + FWP_BYTE_BLOB* applicationId = NULL; + + RtlZeroMemory(&monitorSettings, sizeof(MONITOR_SETTINGS)); + RtlZeroMemory(&session, sizeof(FWPM_SESSION)); + + session.displayData.name = L"Monitor Sample Session"; + session.displayData.description = L"Monitors traffic at the Stream layer."; + + // Let the Base Filtering Engine cleanup after us. + session.flags = FWPM_SESSION_FLAG_DYNAMIC; + + printf("Opening Filtering Engine\n"); + result = FwpmEngineOpen( + NULL, + RPC_C_AUTHN_WINNT, + NULL, + &session, + &engineHandle + ); + + if (NO_ERROR != result) + { + goto cleanup; + } + + printf("Successfully opened Filtering Engine\n"); + + printf("Looking up Application ID from BFE\n"); + result = MonitorAppIDFromPath(AppPath, &applicationId); + + if (NO_ERROR != result) + { + goto cleanup; + } + + printf("Successfully retrieved Application ID\n"); + + printf("Opening Monitor Sample Device\n"); + + result = MonitorAppOpenMonitorDevice(&monitorDevice); + if (NO_ERROR != result) + { + goto cleanup; + } + + printf("Successfully opened Monitor Device\n"); + + printf("Adding Filters through the Filtering Engine\n"); + + result = MonitorAppAddFilters(engineHandle, + applicationId); + + if (NO_ERROR != result) + { + goto cleanup; + } + + printf("Successfully added Filters through the Filtering Engine\n"); + + printf("Enabling monitoring through the Monitor Sample Device\n"); + + monitorSettings.monitorOperation = monitorTraffic; + + result = MonitorAppEnableMonitoring(monitorDevice, + &monitorSettings); + if (NO_ERROR != result) + { + goto cleanup; + } + + printf("Successfully enabled monitoring.\n"); + + printf("Events will be traced through WMI. Please press any key to exit and cleanup filters.\n"); + +#pragma prefast(push) +#pragma prefast(disable:6031, "by design the return value of _getch() is ignored here") + _getch(); +#pragma prefast(pop) + +cleanup: + + if (NO_ERROR != result) + { + printf("Monitor.\tError 0x%x occurred during execution\n", result); + } + + if (monitorDevice) + { + MonitorAppCloseMonitorDevice(monitorDevice); + } + + // + // Free the application Id that we retrieved. + // + if (applicationId) + { + FwpmFreeMemory((void**)&applicationId); + } + + if (engineHandle) + { + result = FwpmEngineClose(engineHandle); + engineHandle = NULL; + } + + return result; +} + +void +MonitorPrintUsage() +{ + wprintf(L"Usage: monitor ( addcallouts | delcallouts | monitor )\n"); +} + +DWORD +MonitorAppProcessArguments(_In_ int argc, _In_reads_(argc) PCWSTR argv[]) +{ + if (argc == 2) + { + if (_wcsicmp(argv[1], L"addcallouts") == 0) + { + return MonitorAppAddCallouts(); + } + if (_wcsicmp(argv[1], L"delcallouts") == 0) + { + return MonitorAppRemoveCallouts(); + } + } + + if (argc == 3) + { + if (_wcsicmp(argv[1], L"monitor") == 0) + { + return MonitorAppDoMonitoring(argv[2]); + } + } + + MonitorPrintUsage(); + return ERROR_INVALID_PARAMETER; +} + +int __cdecl wmain(_In_ int argc, _In_reads_(argc) PCWSTR argv[]) +{ + DWORD result; + + result = MonitorAppProcessArguments(argc, argv); + + return (int)result; +} diff --git a/network/trans/msnmntr/exe/monitor.vcxproj b/network/trans/msnmntr/exe/monitor.vcxproj new file mode 100644 index 00000000..06902c7c --- /dev/null +++ b/network/trans/msnmntr/exe/monitor.vcxproj @@ -0,0 +1,251 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {BF8F75F9-23E9-4925-96A1-76FD729EBA93} + $(MSBuildProjectName) + Debug + Win32 + {DED4B73F-AFBE-4142-AD53-004F9F079AA7} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + monitor + + + monitor + + + monitor + + + monitor + + + + %(PreprocessorDefinitions);WIN32 + + + %(PreprocessorDefinitions);WIN32 + + + %(PreprocessorDefinitions);WIN32 + %(AdditionalOptions) -N + + + + + %(PreprocessorDefinitions);WIN32 + + + %(PreprocessorDefinitions);WIN32 + + + %(PreprocessorDefinitions);WIN32 + %(AdditionalOptions) -N + + + + + %(PreprocessorDefinitions);WIN32 + + + %(PreprocessorDefinitions);WIN32 + + + %(PreprocessorDefinitions);WIN32 + %(AdditionalOptions) -N + + + + + %(PreprocessorDefinitions);WIN32 + + + %(PreprocessorDefinitions);WIN32 + + + %(PreprocessorDefinitions);WIN32 + %(AdditionalOptions) -N + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + Static + NTDDI_WIN7 + + + Static + NTDDI_WIN7 + + + Static + NTDDI_WIN7 + + + Static + NTDDI_WIN7 + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;netapi32.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib;ntdll.lib;kernel32.lib;setupapi.lib;rpcrt4.lib;fwpuclnt.lib + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;netapi32.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib;ntdll.lib;kernel32.lib;setupapi.lib;rpcrt4.lib;fwpuclnt.lib + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;netapi32.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib;ntdll.lib;kernel32.lib;setupapi.lib;rpcrt4.lib;fwpuclnt.lib + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;netapi32.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib;ntdll.lib;kernel32.lib;setupapi.lib;rpcrt4.lib;fwpuclnt.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/trans/msnmntr/exe/monitor.vcxproj.Filters b/network/trans/msnmntr/exe/monitor.vcxproj.Filters new file mode 100644 index 00000000..2838b33f --- /dev/null +++ b/network/trans/msnmntr/exe/monitor.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {AB53852E-BB5F-49F9-8184-98DAFA476C33} + + + h;hpp;hxx;hm;inl;inc;xsd + {253A5086-E26E-4352-B8E1-BBBEB6A81F31} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {714E88E3-7CAD-4F49-8A28-32DD65C6691E} + + + + + Source Files + + + \ No newline at end of file diff --git a/network/trans/msnmntr/exe/mtrace.cmd b/network/trans/msnmntr/exe/mtrace.cmd new file mode 100644 index 00000000..f7c6147f --- /dev/null +++ b/network/trans/msnmntr/exe/mtrace.cmd @@ -0,0 +1,288 @@ +@echo off +@setlocal + +@rem ------------------------------------------------------------------------- +@rem OBTAIN INPUT +@rem ------------------------------------------------------------------------- + +set TR_MODULE=%1 +shift +set TR_LEVEL=%1 +shift +set TR_VERB=%1 + +@rem ------------------------------------------------------------------------- +@rem VALIDATE COMPONENT & TRACING LEVEL +@rem ------------------------------------------------------------------------- + +if /i "%TR_MODULE%"=="MONITOR" ( + set TR_GUID={dd65554d-9925-49d1-83b6-46125feb4207} + set TR_MODULE=MsnMntrMonitor + + if "%TR_LEVEL%"=="0" ( + set TR_BITS= + set TR_LEVEL=0 + ) else if "%TR_LEVEL%"=="1" ( + set TR_BITS= + set TR_LEVEL=1 + ) else if /i "%TR_LEVEL%"=="2" ( + set TR_BITS= + set TR_LEVEL=2 + ) else if /i "%TR_LEVEL%"=="9" ( + set TR_BITS= + set TR_LEVEL=9 + ) else ( + echo. + echo Error: Monitor component does not support this trace detail. + goto :show_usage_MsnMntrMonitor + ) +) else if /i "%TR_MODULE%"=="NOTIFY" ( + set TR_GUID={aca2f74a-7a0d-4f47-be4b-66900813b8e5} + set TR_MODULE=MsnMntrNotify + + if "%TR_LEVEL%"=="0" ( + set TR_BITS= + set TR_LEVEL=0 + ) else if "%TR_LEVEL%"=="1" ( + set TR_BITS= + set TR_LEVEL=1 + ) else if /i "%TR_LEVEL%"=="2" ( + set TR_BITS= + set TR_LEVEL=2 + ) else if /i "%TR_LEVEL%"=="3" ( + set TR_BITS= + set TR_LEVEL=3 + ) else if /i "%TR_LEVEL%"=="9" ( + set TR_BITS= + set TR_LEVEL=9 + ) else ( + echo. + echo Error: Notify component does not support this trace detail. + goto :show_usage_MsnMntrNotify + ) + +) else if /i "%TR_MODULE%"=="CONTROL" ( + set TR_GUID={eab718af-52de-477c-874d-cb49746bb131} + set TR_MODULE=MsnMntrCtl + + if "%TR_LEVEL%"=="0" ( + set TR_BITS= + set TR_LEVEL=0 + ) else if "%TR_LEVEL%"=="1" ( + set TR_BITS= + set TR_LEVEL=1 + ) else if /i "%TR_LEVEL%"=="2" ( + set TR_BITS= + set TR_LEVEL=2 + ) else if /i "%TR_LEVEL%"=="9" ( + set TR_BITS= + set TR_LEVEL=9 + ) else ( + echo. + echo Error: Control component does not support this trace detail. + goto :show_usage_MsnMntrCtl + ) + +) else if /i "%TR_MODULE%"=="INIT" ( + set TR_GUID={e7db16bb-41be-4c05-b73e-5feca06f8207} + set TR_MODULE=MsnMntrInit + + if "%TR_LEVEL%"=="0" ( + set TR_BITS= + set TR_LEVEL=0 + ) else if "%TR_LEVEL%"=="1" ( + set TR_BITS= + set TR_LEVEL=1 + ) else if /i "%TR_LEVEL%"=="9" ( + set TR_BITS= + set TR_LEVEL=9 + ) else ( + echo. + echo Error: Init component does not support this trace detail. + goto :show_usage_MsnMntrInit + ) + +) else ( + echo. + echo Error: No module was selected. + goto :show_usage +) + +set TR_NAME=%TR_MODULE% +set TR_DIR=%SystemRoot%\Tracing\%TR_NAME% +set TR_LOG=%TR_DIR%\%TR_NAME%.etl +set TR_BITS=0xFFFFFFFF +set TR_OPTS= +set TR_RT_OPTS=-rt -ft 1 + +set TRACE_FORMAT_PREFIX=%%9!d!:%%3!04X! %%!FUNC!: +set TRACE_FORMAT_SEARCH_PATH=%TR_DIR% + +@rem ------------------------------------------------------------------------- +@rem VALIDATE VERB +@rem ------------------------------------------------------------------------- + +if /i "%TR_VERB%"=="start" ( + call :start_trace +) else if /i "%TR_VERB%"=="stop" ( + call :stop_trace +) else if /i "%TR_VERB%"=="pdb" ( + call :extract_format_info %1 +) else if /i "%TR_VERB%"=="rt" ( + call :format_realtime +) else if /i "%TR_VERB%"=="fmt" ( + call :format_offline +) else ( + echo. + echo Error: A supported verb has not been specified. + goto :show_usage +) + +goto :eof + +:ShowSummary + echo. + echo Trace name : %TR_NAME% + echo Trace directory : %TR_DIR% + echo Trace log : %TR_LOG% + echo Trace level : %TR_LEVEL% + +@rem ------------------------------------------------------------------------- +@rem START TRACING +@rem ------------------------------------------------------------------------- +:start_trace + if not exist %TR_DIR% mkdir %TR_DIR% + logman query %TR_NAME% -ets 1 > NUL + if errorlevel 1 ( + logman start %TR_NAME% %TR_OPTS% -p %TR_GUID% %TR_BITS% %TR_LEVEL% -o %TR_LOG% -ets + ) else ( + echo Collection is already started. + ) + goto :eof + +@rem ------------------------------------------------------------------------- +@rem STOP TRACING +@rem ------------------------------------------------------------------------- +:stop_trace + logman query %TR_NAME% -ets 1>NUL + if NOT errorlevel 1 ( + logman stop %TR_NAME% -ets + ) + goto :eof + +@rem ------------------------------------------------------------------------- +@rem EXTRACT FORMAT INFO +@rem ------------------------------------------------------------------------- +:extract_format_info + if "%1" == "" ( + set TR_PDB=.\%TR_MODULE%.pdb + )else ( + set TR_PDB=%1 + ) + tracepdb -f %TR_PDB% -p %TR_DIR% + goto :eof + +@rem ------------------------------------------------------------------------- +@rem FORMAT REALTIME +@rem ------------------------------------------------------------------------- +:format_realtime + call :stop_trace + set TR_OPTS=%TR_RT_OPTS% + call :start_trace + start "%TR_NAME% Tracing" /low tracefmt -displayonly -rt %TR_NAME% + goto :eof + +@rem ------------------------------------------------------------------------- +@rem FORMAT OFFLINE +@rem ------------------------------------------------------------------------- +:format_offline + tracefmt -o %TR_NAME%.txt %TR_LOG% -display + goto :eof + +goto :eof + +@rem ------------------------------------------------------------------------- +@rem CONTEXT SENSITIVE HELP +@rem ------------------------------------------------------------------------- +:show_usage + call :show_usage_header + echo 9 Display all trace events + echo Select a component to see individual supported tracing levels. + call :show_usage_footer + + goto :eof + +:show_usage_MsnMntrMonitor + call :show_usage_header + echo 0 Established flow + echo 1 Change of state information + echo 2 Layer notifications + echo 9 Display all trace events + call :show_usage_footer + + goto :eof + +:show_usage_MsnMntrNotify + call :show_usage_header + echo 0 Client to server + echo 1 Peer to peer + echo 2 Unknown + echo 3 All traffic + echo 9 Display all trace events + call :show_usage_footer + + goto :eof + +:show_usage_MsnMntrCtl + call :show_usage_header + echo 0 Initialization + echo 1 Device control + echo 2 State + echo 9 Display all trace events + call :show_usage_footer + + goto :eof + +:show_usage_MsnMntrInit + call :show_usage_header + echo 0 Initialization + echo 1 Shutdown + echo 9 Display all trace events + call :show_usage_footer + + goto :eof + +:show_usage_header + echo. + echo Usage: monitor_trace COMPONENT LEVEL VERB + echo. + echo Components: + echo MONITOR, NOTIFY, CONTROL and INIT + echo. + echo Trace detail: + goto :eof + +:show_usage_footer + echo. + echo Verbs: + echo start Start collection. + echo stop Stop collection. + echo pdb Extract format information from the pdb in the current + echo directory. + echo pdb [file] Like the above, but allows the full path to the pdb + echo to be specified. + echo rt Displays the trace output in real-time. This + echo automatically stops any existing collection and begins + echo a new one with appropriate parameters for real-time. + echo fmt Format the trace logfile to the console. + echo. + echo Note: + echo The most common scenario is to extract the format information from + echo the pdb, and then display the output in real-time. + echo. + echo Example: + echo cd /d MySymbolDir + echo monitor_trace init 0 pdb + echo monitor_trace init 0 rt + + goto :eof \ No newline at end of file diff --git a/network/trans/msnmntr/inc/ioctl.h b/network/trans/msnmntr/inc/ioctl.h new file mode 100644 index 00000000..ea08dc42 --- /dev/null +++ b/network/trans/msnmntr/inc/ioctl.h @@ -0,0 +1,36 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Monitor Sample callout driver IOCTL header + +Environment: + + Kernel mode + +--*/ + +#pragma once + +#define MONITOR_DEVICE_NAME L"\\Device\\MonitorSample" +#define MONITOR_SYMBOLIC_NAME L"\\DosDevices\\Global\\MonitorSample" +#define MONITOR_DOS_NAME L"\\\\.\\MonitorSample" + +typedef enum _MONITOR_OPERATION_MODE +{ + invalidOperation = 0, + monitorTraffic = 1, + monitorOperationMax +} MONITOR_OPERATION_MODE; + +typedef struct _MONITOR_SETTINGS +{ + MONITOR_OPERATION_MODE monitorOperation; + UINT32 flags; +} MONITOR_SETTINGS; + +#define MONITOR_IOCTL_ENABLE_MONITOR CTL_CODE(FILE_DEVICE_NETWORK, 0x1, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define MONITOR_IOCTL_DISABLE_MONITOR CTL_CODE(FILE_DEVICE_NETWORK, 0x2, METHOD_BUFFERED, FILE_ANY_ACCESS) + diff --git a/network/trans/msnmntr/inc/mntrguid.h b/network/trans/msnmntr/inc/mntrguid.h new file mode 100644 index 00000000..8b072853 --- /dev/null +++ b/network/trans/msnmntr/inc/mntrguid.h @@ -0,0 +1,44 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Monitor Sample callout driver IOCTL header + +Environment: + + Kernel mode + +--*/ + +#pragma once + +// b3241f1d-7cd2-4e7a-8721-2e97d07702e5 +DEFINE_GUID( + MONITOR_SAMPLE_SUBLAYER, + 0xb3241f1d, + 0x7cd2, + 0x4e7a, + 0x87, 0x21, 0x2e, 0x97, 0xd0, 0x77, 0x02, 0xe5 +); + +// 3aaccbc0-2c29-455f-bb91-0e801c8994a4 +DEFINE_GUID( + MONITOR_SAMPLE_FLOW_ESTABLISHED_CALLOUT_V4, + 0x3aaccbc0, + 0x2c29, + 0x455f, + 0xbb, 0x91, 0x0e, 0x80, 0x1c, 0x89, 0x94, 0xa4 +); + +// cea0131a-6ed3-4ed6-b40c-8a8fe8434b0a +DEFINE_GUID( + MONITOR_SAMPLE_STREAM_CALLOUT_V4, + 0xcea0131a, + 0x6ed3, + 0x4ed6, + 0xb4, 0x0c, 0x8a, 0x8f, 0xe8, 0x43, 0x4b, 0x0a +); + + diff --git a/network/trans/msnmntr/msnmntr.sln b/network/trans/msnmntr/msnmntr.sln new file mode 100644 index 00000000..ac6d8e3e --- /dev/null +++ b/network/trans/msnmntr/msnmntr.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{18ABBE0C-EBB5-4181-AB5D-AD526B2CB283}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{B9075E40-A5B9-42B8-A185-CB3290C4EC12}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monitor", "exe\monitor.vcxproj", "{BF8F75F9-23E9-4925-96A1-76FD729EBA93}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msnmntr", "sys\msnmntr.vcxproj", "{A0314F39-C7C2-463C-B2BE-DF387AB755F5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BF8F75F9-23E9-4925-96A1-76FD729EBA93}.Debug|Win32.ActiveCfg = Debug|Win32 + {BF8F75F9-23E9-4925-96A1-76FD729EBA93}.Debug|Win32.Build.0 = Debug|Win32 + {BF8F75F9-23E9-4925-96A1-76FD729EBA93}.Release|Win32.ActiveCfg = Release|Win32 + {BF8F75F9-23E9-4925-96A1-76FD729EBA93}.Release|Win32.Build.0 = Release|Win32 + {BF8F75F9-23E9-4925-96A1-76FD729EBA93}.Debug|x64.ActiveCfg = Debug|x64 + {BF8F75F9-23E9-4925-96A1-76FD729EBA93}.Debug|x64.Build.0 = Debug|x64 + {BF8F75F9-23E9-4925-96A1-76FD729EBA93}.Release|x64.ActiveCfg = Release|x64 + {BF8F75F9-23E9-4925-96A1-76FD729EBA93}.Release|x64.Build.0 = Release|x64 + {A0314F39-C7C2-463C-B2BE-DF387AB755F5}.Debug|Win32.ActiveCfg = Debug|Win32 + {A0314F39-C7C2-463C-B2BE-DF387AB755F5}.Debug|Win32.Build.0 = Debug|Win32 + {A0314F39-C7C2-463C-B2BE-DF387AB755F5}.Release|Win32.ActiveCfg = Release|Win32 + {A0314F39-C7C2-463C-B2BE-DF387AB755F5}.Release|Win32.Build.0 = Release|Win32 + {A0314F39-C7C2-463C-B2BE-DF387AB755F5}.Debug|x64.ActiveCfg = Debug|x64 + {A0314F39-C7C2-463C-B2BE-DF387AB755F5}.Debug|x64.Build.0 = Debug|x64 + {A0314F39-C7C2-463C-B2BE-DF387AB755F5}.Release|x64.ActiveCfg = Release|x64 + {A0314F39-C7C2-463C-B2BE-DF387AB755F5}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {BF8F75F9-23E9-4925-96A1-76FD729EBA93} = {18ABBE0C-EBB5-4181-AB5D-AD526B2CB283} + {A0314F39-C7C2-463C-B2BE-DF387AB755F5} = {B9075E40-A5B9-42B8-A185-CB3290C4EC12} + EndGlobalSection +EndGlobal diff --git a/network/trans/msnmntr/sys/ctl.c b/network/trans/msnmntr/sys/ctl.c new file mode 100644 index 00000000..09a418c3 --- /dev/null +++ b/network/trans/msnmntr/sys/ctl.c @@ -0,0 +1,149 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Monitor Sample driver IO control routines + +Environment: + + Kernel mode + +--*/ + +#include +#include + +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + + +#include "ioctl.h" + +#include "msnmntr.h" +#include "ctl.h" + +#include "notify.h" + +// +// Software Tracing Definitions +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(MsnMntrCtl,(eab718af, 52de, 477c, 874d, cb49746bb131), \ + WPP_DEFINE_BIT(TRACE_INIT) \ + WPP_DEFINE_BIT(TRACE_DEVICE_CONTROL) \ + WPP_DEFINE_BIT(TRACE_STATE) ) + +#include "ctl.tmh" + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL MonitorEvtDeviceControl; + +NTSTATUS +MonitorCtlDriverInit( + _In_ WDFDEVICE* pDevice + ) +/*++ + +Routine Description: + + Initializes the request queue for our driver. This is how + DeviceIoControl requests are sent to KMDF drivers. + +Arguments: + + [in] WDFDEVICE* pDevice - Our device. + +--*/ +{ + NTSTATUS status; + WDF_IO_QUEUE_CONFIG queueConfig; + + DoTraceMessage(TRACE_INIT, "MonitorSample Control Initialization in progress."); + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( + &queueConfig, + WdfIoQueueDispatchSequential + ); + + queueConfig.EvtIoDeviceControl = MonitorEvtDeviceControl; + + status = WdfIoQueueCreate( + *pDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + NULL + ); + + return status; +} + +VOID +MonitorEvtDeviceControl ( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +/*++ + + Handles device IO control requests. This callback drives all communication + between the usermode exe and this driver. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(OutputBufferLength); + + DoTraceMessage(TRACE_DEVICE_CONTROL, "MonitorSample Dispatch Device Control: 0x%x", IoControlCode); + + switch (IoControlCode) + { + case MONITOR_IOCTL_ENABLE_MONITOR: + { + WDFMEMORY pMemory; + void* pBuffer; + + if (InputBufferLength < sizeof(MONITOR_SETTINGS)) + { + status = STATUS_INVALID_PARAMETER; + } + else + { + status = WdfRequestRetrieveInputMemory(Request, &pMemory); + + if (NT_SUCCESS(status)) + { + pBuffer = WdfMemoryGetBuffer(pMemory, NULL); + status = MonitorCoEnableMonitoring((MONITOR_SETTINGS*) pBuffer); + } + } + break; + } + + case MONITOR_IOCTL_DISABLE_MONITOR: + { + status = STATUS_SUCCESS; + + MonitorCoDisableMonitoring(); + + break; + } + + default: + { + status = STATUS_INVALID_PARAMETER; + } + } + + WdfRequestComplete(Request, status); +} diff --git a/network/trans/msnmntr/sys/ctl.h b/network/trans/msnmntr/sys/ctl.h new file mode 100644 index 00000000..6e268be2 --- /dev/null +++ b/network/trans/msnmntr/sys/ctl.h @@ -0,0 +1,19 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Monitor Sample driver IO control header + +Environment: + + Kernel mode + +--*/ + +NTSTATUS +MonitorCtlDriverInit( + _In_ WDFDEVICE* pDevice + ); + diff --git a/network/trans/msnmntr/sys/init.c b/network/trans/msnmntr/sys/init.c new file mode 100644 index 00000000..a85ff103 --- /dev/null +++ b/network/trans/msnmntr/sys/init.c @@ -0,0 +1,244 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Monitor Sample driver initialization routines + +Environment: + + Kernel mode + +--*/ + +#include +#include +#include + +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + +#include "ioctl.h" + +#include "msnmntr.h" +#include "ctl.h" + +#include "notify.h" + +// +// Software Tracing Definitions +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(MsnMntrInit,(e7db16bb, 41be, 4c05, b73e, 5feca06f8207), \ + WPP_DEFINE_BIT(TRACE_INIT) \ + WPP_DEFINE_BIT(TRACE_SHUTDOWN) ) + +#include "init.tmh" + +DEVICE_OBJECT* gWdmDevice; + +// =========================================================================== +// +// LOCAL PROTOTYPES +// +// =========================================================================== + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_UNLOAD MonitorEvtDriverUnload; + +// We're using what looks like a EVT_WDF_DRIVER_DEVICE_ADD callback, to keep +// this looking like a normal KMDF driver. However, since this is a non-pnp +// driver, it will not be used as a callback; we will call it ourselves at the +// end of DriverEntry. So, do not declare it as a callback. +// The NONPNP sample demonstrates this as well. +NTSTATUS +MonitorEvtDeviceAdd( + _In_ PWDFDEVICE_INIT pInit + ); + +// =========================================================================== +// +// PUBLIC FUNCTIONS +// +// =========================================================================== + +NTSTATUS +DriverEntry( + _In_ DRIVER_OBJECT* driverObject, + _In_ UNICODE_STRING* registryPath + ) +/*++ + +Routine Description: + + Main driver entry point. Called at driver load time + +Arguments: + + driverObject Our driver + registryPath A reg key where we can keep parameters + +Return Value: + + status of our initialization. A status != STATUS_SUCCESS aborts the + driver load and we don't get called again. + + Each component is responsible for logging any error that causes the + driver load to fail. + +--*/ +{ + NTSTATUS status; + WDF_DRIVER_CONFIG config; + WDFDRIVER driver; + PWDFDEVICE_INIT pInit = NULL; + + // Request NX Non-Paged Pool when available + ExInitializeDriverRuntime(DrvRtPoolNxOptIn); + + // + // This macro is required to initialize software tracing on XP and beyond + // For XP and beyond use the DriverObject as the first argument. + // + + WPP_INIT_TRACING(driverObject,registryPath); + + DoTraceMessage(TRACE_INIT, "Initializing MonitorSample Driver"); + + WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); + config.DriverInitFlags |= WdfDriverInitNonPnpDriver; + config.EvtDriverUnload = MonitorEvtDriverUnload; + + status = WdfDriverCreate( + driverObject, + registryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + &driver + ); + + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + + pInit = WdfControlDeviceInitAllocate(driver, &SDDL_DEVOBJ_SYS_ALL_ADM_ALL); + + if (!pInit) + { + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + status = MonitorEvtDeviceAdd(pInit); + +cleanup: + if (!NT_SUCCESS(status)) + { + DoTraceMessage(TRACE_INIT, "MonitorSample Initialization Failed."); + + WPP_CLEANUP(driverObject); + } + + return status; +} + +NTSTATUS +MonitorEvtDeviceAdd( + _In_ PWDFDEVICE_INIT pInit + ) +{ + NTSTATUS status; + WDFDEVICE device; + DECLARE_CONST_UNICODE_STRING(ntDeviceName, MONITOR_DEVICE_NAME); + DECLARE_CONST_UNICODE_STRING(symbolicName, MONITOR_SYMBOLIC_NAME); + + WdfDeviceInitSetDeviceType(pInit, FILE_DEVICE_NETWORK); + WdfDeviceInitSetCharacteristics(pInit, FILE_DEVICE_SECURE_OPEN, FALSE); + status = WdfDeviceInitAssignName(pInit, &ntDeviceName); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + + status = WdfDeviceCreate(&pInit, WDF_NO_OBJECT_ATTRIBUTES, &device); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + + status = WdfDeviceCreateSymbolicLink(device, &symbolicName); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + + status = MonitorCtlDriverInit(&device); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + + gWdmDevice = WdfDeviceWdmGetDeviceObject(device); + status = MonitorCoInitialize(gWdmDevice); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + + status = MonitorNfInitialize(gWdmDevice); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + + WdfControlFinishInitializing(device); + +cleanup: + // If WdfDeviceCreate was successful, it will set pInit to NULL. + if (pInit) + { + WdfDeviceInitFree(pInit); + } + + return status; +} + +void +MonitorEvtDriverUnload( + _In_ WDFDRIVER Driver + ) +/*++ + +Routine Description: + + Called to indicate that we are being unloaded and to cause an orderly + shutdown + +Arguments: + + driverObject Our driver + +Return Value: + + None + +--*/ +{ + DRIVER_OBJECT* driverObject; + + MonitorCoUninitialize(); + MonitorNfUninitialize(); + + DoTraceMessage(TRACE_SHUTDOWN, "MonitorSample Driver Shutting Down"); + + driverObject = WdfDriverWdmGetDriverObject(Driver); + WPP_CLEANUP(driverObject); +} diff --git a/network/trans/msnmntr/sys/msnmntr.c b/network/trans/msnmntr/sys/msnmntr.c new file mode 100644 index 00000000..1aa33431 --- /dev/null +++ b/network/trans/msnmntr/sys/msnmntr.c @@ -0,0 +1,883 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Monitor Sample driver callout routines + +Environment: + + Kernel mode +--*/ + +#include +#include + +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + +#include "ioctl.h" + +#include "msnmntr.h" +#include "notify.h" +#include "intsafe.h" + +#define INITGUID +#include +#include "mntrguid.h" + +// +// Software Tracing Definitions +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(MsnMntrMonitor,(dd65554d, 9925, 49d1, 83b6, 46125feb4207), \ + WPP_DEFINE_BIT(TRACE_FLOW_ESTABLISHED) \ + WPP_DEFINE_BIT(TRACE_STATE_CHANGE) \ + WPP_DEFINE_BIT(TRACE_LAYER_NOTIFY) ) + +#include "msnmntr.tmh" + +#define TAG_NAME_CALLOUT 'CnoM' + +UINT32 flowEstablishedId = 0; +UINT32 streamId = 0; +long monitoringEnabled = 0; +LIST_ENTRY flowContextList; +KSPIN_LOCK flowContextListLock; + +NTSTATUS MonitorCoFlowEstablishedNotifyV4( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter); + +NTSTATUS MonitorCoStreamNotifyV4( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter); + +void MonitorCoStreamFlowDeletion( + _In_ UINT16 layerId, + _In_ UINT32 calloutId, + _In_ UINT64 flowContext); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +NTSTATUS MonitorCoFlowEstablishedCalloutV4( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* packet, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut); + +NTSTATUS MonitorCoStreamCalloutV4( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* packet, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut); + +#else + +NTSTATUS MonitorCoFlowEstablishedCalloutV4( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* packet, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut); + +NTSTATUS MonitorCoStreamCalloutV4( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* packet, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +NTSTATUS +MonitorCoRegisterCallout( + _Inout_ void* deviceObject, + _In_ FWPS_CALLOUT_CLASSIFY_FN ClassifyFunction, + _In_ FWPS_CALLOUT_NOTIFY_FN NotifyFunction, + _In_opt_ FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN FlowDeleteFunction, + _In_ const GUID* calloutKey, + _In_ UINT32 flags, + _Out_ UINT32* calloutId + ) +{ + FWPS_CALLOUT sCallout; + NTSTATUS status = STATUS_SUCCESS; + + memset(&sCallout, 0, sizeof(FWPS_CALLOUT)); + + sCallout.calloutKey = *calloutKey; + sCallout.flags = flags; + sCallout.classifyFn = ClassifyFunction; + sCallout.notifyFn = NotifyFunction; + sCallout.flowDeleteFn = FlowDeleteFunction; + + status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId); + + return status; +} + +NTSTATUS +MonitorCoRegisterCallouts( + _Inout_ void* deviceObject + ) +{ + NTSTATUS status; + + // + // We won't be called for flow deletion for the flow established layer + // since we only establish a flow for the stream layer, so we don't + // specify a flow deletion function. + // + status = MonitorCoRegisterCallout(deviceObject, + MonitorCoFlowEstablishedCalloutV4, + MonitorCoFlowEstablishedNotifyV4, + NULL, // We don't need a flow delete function at this layer. + &MONITOR_SAMPLE_FLOW_ESTABLISHED_CALLOUT_V4, + 0, // No flags. + &flowEstablishedId); + + if (NT_SUCCESS(status)) + { + status = MonitorCoRegisterCallout(deviceObject, + MonitorCoStreamCalloutV4, + MonitorCoStreamNotifyV4, + MonitorCoStreamFlowDeletion, + &MONITOR_SAMPLE_STREAM_CALLOUT_V4, + FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW, + &streamId); + } + + return status; +} + +NTSTATUS +MonitorCoUnregisterCallout( + _In_ const GUID* calloutKey + ) +{ + NTSTATUS status; + + status = FwpsCalloutUnregisterByKey(calloutKey); + + return status; +} + +NTSTATUS +MonitorCoUnregisterCallouts(void) +{ + NTSTATUS status; + + status = MonitorCoUnregisterCallout(&MONITOR_SAMPLE_FLOW_ESTABLISHED_CALLOUT_V4); + + if (NT_SUCCESS(status)) + { + status = MonitorCoUnregisterCallout(&MONITOR_SAMPLE_STREAM_CALLOUT_V4); + } + + return status; +} + + +NTSTATUS +MonitorCoInsertFlowContext( + _Inout_ FLOW_DATA* flowContext) +{ + KLOCK_QUEUE_HANDLE lockHandle; + NTSTATUS status; + + KeAcquireInStackQueuedSpinLock(&flowContextListLock, &lockHandle); + + // Catch the case where we disabled monitoring after we had intended to + // associate the context to the flow so that we don't bugcheck due to + // our driver being unloaded and then receiving a call for a particular + // flow or leak the memory because we unloaded without freeing it. + if (monitoringEnabled) + { + DoTraceMessage(TRACE_FLOW_ESTABLISHED, "Creating flow for traffic.\r\n"); + + InsertTailList(&flowContextList, &flowContext->listEntry); + status = STATUS_SUCCESS; + } + else + { + DoTraceMessage(TRACE_FLOW_ESTABLISHED, "Unable to create flow, driver shutting down.\r\n"); + + // Our driver is shutting down. + status = STATUS_SHUTDOWN_IN_PROGRESS; + } + + KeReleaseInStackQueuedSpinLock(&lockHandle); + return status; +} + +void +MonitorCoCleanupFlowContext( + _In_ __drv_freesMem(Mem) FLOW_DATA* flowContext + ) +/* +Routine Description + + Called to cleanup a flow context on flow deletion. ProcessPath is passed + as a second parameter so Prefast can see that it's being freed here. + +*/ +{ + if (flowContext->processPath) + { + ExFreePoolWithTag(flowContext->processPath, TAG_NAME_CALLOUT); + } + ExFreePoolWithTag(flowContext, TAG_NAME_CALLOUT); +} + +NTSTATUS +MonitorCoAllocFlowContext( + _In_ SIZE_T processPathSize, + _Out_ FLOW_DATA** flowContextOut + ) +{ + NTSTATUS status = STATUS_SUCCESS; + FLOW_DATA* flowContext = NULL; + + *flowContextOut = NULL; + + flowContext = ExAllocatePoolWithTag(NonPagedPool, + sizeof(FLOW_DATA), + TAG_NAME_CALLOUT); + + if (!flowContext) + { + status = STATUS_NO_MEMORY; + goto cleanup; + } + + RtlZeroMemory(flowContext, + sizeof(FLOW_DATA)); + + + flowContext->processPath = ExAllocatePoolWithTag(NonPagedPool, + processPathSize, + TAG_NAME_CALLOUT); + if (!flowContext->processPath) + { + status = STATUS_NO_MEMORY; + goto cleanup; + + } + + *flowContextOut = flowContext; + + cleanup: + if (!NT_SUCCESS(status)) + { + if (flowContext) + { + if (flowContext->processPath) + { + ExFreePoolWithTag(flowContext->processPath, TAG_NAME_CALLOUT); + } + ExFreePoolWithTag(flowContext, TAG_NAME_CALLOUT); + } + } + + return status; +} + + +UINT64 +MonitorCoCreateFlowContext( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Out_ UINT64* flowHandle) +/* +Routine Description + + Creates a flow context that is associated with the current flow + +Arguments + [IN] FWPS_CALLOUT_NOTIFY_TYPE notifyType - Type of notification + + [IN] GUID* filterKey - Key of the filter that was + added/deleted/modified. + + [IN] struct FWPS_FILTER_* filter - pointer to the Filter itself. + +Return values + + STATUS_SUCCESS or a specific error code. + +Notes + + +*/ +{ + FLOW_DATA* flowContext = NULL; + NTSTATUS status; + FWP_BYTE_BLOB* processPath; + UINT32 index; + + *flowHandle = 0; + + if (!FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_PROCESS_PATH)) + { + status = STATUS_NOT_FOUND; + goto cleanup; + } + + processPath = inMetaValues->processPath; + + status = MonitorCoAllocFlowContext(processPath->size, &flowContext); + if (!NT_SUCCESS(status)) + { + goto cleanup; + } + + // Flow context is always created at the Flow established layer. + + // flowContext gets deleted in MonitorCoCleanupFlowContext + + flowContext->deleting = FALSE; + flowContext->flowHandle = inMetaValues->flowHandle; + *flowHandle = flowContext->flowHandle; + + index = FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS; + flowContext->localAddressV4 = inFixedValues->incomingValue[index].value.uint32; + + + index = FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT; + flowContext->localPort = inFixedValues->incomingValue[index].value.uint16; + + index = FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS; + flowContext->remoteAddressV4 = inFixedValues->incomingValue[index].value.uint32; + + index = FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT; + flowContext->remotePort = inFixedValues->incomingValue[index].value.uint16; + + index = FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL; + flowContext->ipProto = inFixedValues->incomingValue[index].value.uint16; + + // flowContext->processPath gets deleted in MonitorCoCleanupFlowContext + memcpy(flowContext->processPath, processPath->data, processPath->size); + + status = MonitorCoInsertFlowContext(flowContext); + +cleanup: + + if (!NT_SUCCESS(status)) + { + flowContext = NULL; + } + + return (UINT64) flowContext; +} + +NTSTATUS MonitorCoInitialize(_Inout_ DEVICE_OBJECT* deviceObject) +/* +Routine Description + + Initializes our flow tracking so that we can handle the case where + the driver is shutdown with flows that are still active. + +Arguments + + None. + +Return values + + STATUS_SUCCESS or a specific error code. + +Notes + + +*/ +{ + NTSTATUS status; + + // Initialize the flow context list and lock. We need this to be able + // to handle the case where our driver is stopped while we still have + // contexts associated with flows. + InitializeListHead(&flowContextList); + KeInitializeSpinLock(&flowContextListLock); + + status = MonitorCoRegisterCallouts(deviceObject); + + return status; +} + +void MonitorCoUninitialize(void) +/* +Routine Description + + Uninitializes the callouts module (this module) by ensuring that all + flow contexts are no longer associated with a flow to ensure that + our driver is not called after it is unloaded. + +Arguments + + None. + +Return values + + STATUS_SUCCESS or a specific error code. + +Notes + + +*/ +{ + LIST_ENTRY list; + KLOCK_QUEUE_HANDLE lockHandle; + + // Make sure we don't associate any more contexts to flows. + MonitorCoDisableMonitoring(); + + InitializeListHead(&list); + + KeAcquireInStackQueuedSpinLock(&flowContextListLock, &lockHandle); + + while (!IsListEmpty(&flowContextList)) + { + FLOW_DATA* flowContext; + LIST_ENTRY* entry; + + entry = RemoveHeadList(&flowContextList); + + flowContext = CONTAINING_RECORD(entry, FLOW_DATA, listEntry); + flowContext->deleting = TRUE; // We don't want our flow deletion function + // to try to remove this from the list. + + InsertHeadList(&list, entry); + } + + KeReleaseInStackQueuedSpinLock(&lockHandle); + + while (!IsListEmpty(&list)) + { + FLOW_DATA* flowContext; + LIST_ENTRY* entry; + NTSTATUS status; + + entry = RemoveHeadList(&list); + + flowContext = CONTAINING_RECORD(entry, FLOW_DATA, listEntry); + + status = FwpsFlowRemoveContext(flowContext->flowHandle, + FWPS_LAYER_STREAM_V4, + streamId); + NT_ASSERT(NT_SUCCESS(status)); + _Analysis_assume_(NT_SUCCESS(status)); + } + + MonitorCoUnregisterCallouts(); +} + +NTSTATUS MonitorCoEnableMonitoring( + _In_ MONITOR_SETTINGS* monitorSettings) +/* +Routine Description + + Enables monitoring of traffic. Before this is called the driver will not + associate any context to flows and will therefore not do any inspection. + Once this is called we will start to track flows for the applications that + we are interested in. + +Arguments + [IN] MONITOR_SETTINS monitorSettings - Settings that govern our behavior. + Nothing is specified at this time. + +Return values + + STATUS_SUCCESS or a specific error code. + +Notes + + +*/ +{ + KLOCK_QUEUE_HANDLE lockHandle; + + if (!monitorSettings) + { + return STATUS_INVALID_PARAMETER; + } + + DoTraceMessage(TRACE_STATE_CHANGE, "Enabling monitoring.\r\n"); + + KeAcquireInStackQueuedSpinLock(&flowContextListLock, &lockHandle); + + monitoringEnabled = 1; + + KeReleaseInStackQueuedSpinLock(&lockHandle); + + return STATUS_SUCCESS; +} + +void +MonitorCoDisableMonitoring(void) +/* +Routine Description + + Disables monitoring of new connections so that we can safely shutdown. + +Arguments + + None. + +Return values + + None. + +Notes + + +*/ +{ + KLOCK_QUEUE_HANDLE lockHandle; + + DoTraceMessage(TRACE_STATE_CHANGE, "Disabling monitoring.\r\n"); + + KeAcquireInStackQueuedSpinLock(&flowContextListLock, &lockHandle); + + monitoringEnabled = 0; + + KeReleaseInStackQueuedSpinLock(&lockHandle); +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +NTSTATUS MonitorCoFlowEstablishedCalloutV4( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* packet, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut) + +#else + +NTSTATUS MonitorCoFlowEstablishedCalloutV4( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* packet, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut) + + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +/* +Routine Description + + Our flow established callout for Ipv4 traffic. + +Arguments + [IN] const FWPS_INCOMING_VALUES* inFixedValues - The fixed values passed in + based on the traffic. + [IN] const FWPS_INCOMING_METADATA_VALUES* inMetaValues - Metadata the + provides additional + information about the + connection. + [IN] void* packet - Depending on the layer and protocol this can be NULL or a + layer specific type. + [IN, OPTIONAL] const VOID* classifyContext - context data associated with the callout driver + [IN] const FWPS_FILTER* filter - The filter that has specified this callout. + [IN] UINT64 flowContext - Flow context associated with a flow + [OUT] FWPS_CLASSIFY_OUT* classifyOut - Out parameter that is used to inform + the filter engine of our decision + +Return values + + STATUS_SUCCESS or a specific error code. + +Notes + + +*/ +{ + + NTSTATUS status = STATUS_SUCCESS; + UINT64 flowHandle; + UINT64 flowContextLocal; + + UNREFERENCED_PARAMETER(packet); +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(classifyContext); +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(flowContext); + + if (monitoringEnabled) + { + flowContextLocal = MonitorCoCreateFlowContext(inFixedValues, inMetaValues, &flowHandle); + + if (!flowContextLocal) + { + classifyOut->actionType = FWP_ACTION_CONTINUE; + goto cleanup; + } + + status = FwpsFlowAssociateContext(flowHandle, + FWPS_LAYER_STREAM_V4, + streamId, + flowContextLocal); + if (!NT_SUCCESS(status)) + { + classifyOut->actionType = FWP_ACTION_CONTINUE; + goto cleanup; + } + } + + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + +cleanup: + + return status; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +NTSTATUS MonitorCoStreamCalloutV4( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* packet, + _In_opt_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut) + +#else + +NTSTATUS MonitorCoStreamCalloutV4( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_opt_ void* packet, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +/* +Routine Description + + Our stream layer callout for traffic to/from the application we're + interested in. Since we specified the filter that matches this callout + as conditional on flow, we only get called if we've associated a flow with + the traffic. + +Arguments + [IN] const FWPS_INCOMING_VALUES* inFixedValues - The fixed values passed in + based on the traffic. + [IN] const FWPS_INCOMING_METADATA_VALUES* inMetaValues - Metadata the + provides additional + information about the + connection. + [IN] void* packet - Depending on the layer and protocol this can be NULL or a + layer specific type. + [IN] const FWPS_FILTER* filter - The filter that has specified this callout. + [IN, OPTIONAL] const VOID* classifyContext - context data associated with the callout driver + [IN] UINT64 flowContext - Flow context associated with a flow + [OUT] FWPS_CLASSIFY_OUT* classifyOut - Out parameter that is used to inform + the filter engine of our decision + +Return values + + STATUS_SUCCESS or a specific error code. + +Notes + + +*/ +{ + FLOW_DATA* flowData; + FWPS_STREAM_CALLOUT_IO_PACKET* streamPacket; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN inbound; + + UNREFERENCED_PARAMETER(inFixedValues); + UNREFERENCED_PARAMETER(inMetaValues); +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(classifyContext); +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(filter); + UNREFERENCED_PARAMETER(flowContext); + + _Analysis_assume_(packet != NULL); + + if (!monitoringEnabled) + { + goto cleanup; + } + + streamPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*) packet; + + if (streamPacket->streamData != NULL && + streamPacket->streamData->dataLength != 0) + { + flowData = *(FLOW_DATA**)(UINT64*) &flowContext; + + inbound = (BOOLEAN) ((streamPacket->streamData->flags & FWPS_STREAM_FLAG_RECEIVE) == FWPS_STREAM_FLAG_RECEIVE); + + status = MonitorNfNotifyMessage(streamPacket->streamData, + inbound, + flowData->localPort, + flowData->remotePort); + } + +cleanup: + + // Return CONTINUE to the filter engine, we're just monitoring. + + classifyOut->actionType = FWP_ACTION_CONTINUE; + + return status; +} + +NTSTATUS MonitorCoFlowEstablishedNotifyV4( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter) +/* +Routine Description + + Notification routine that is called whenever a filter is added, deleted or + modified on the layer that our callout is registered against. + +Arguments + [IN] FWPS_CALLOUT_NOTIFY_TYPE notifyType - Type of notification + + [IN] GUID* filterKey - Key of the filter that was + added/deleted/modified. + + [IN] struct FWPS_FILTER_* filter - pointer to the Filter itself. + +Return values + + STATUS_SUCCESS or a specific error code. + +Notes + + +*/ +{ + UNREFERENCED_PARAMETER(filterKey); + UNREFERENCED_PARAMETER(filter); + + switch (notifyType) + { + case FWPS_CALLOUT_NOTIFY_ADD_FILTER: + DoTraceMessage(TRACE_LAYER_NOTIFY, + "Filter Added to Flow Established layer.\r\n"); + + break; + case FWPS_CALLOUT_NOTIFY_DELETE_FILTER: + DoTraceMessage(TRACE_LAYER_NOTIFY, + "Filter Deleted from Flow Established layer.\r\n"); + break; + } + + return STATUS_SUCCESS; +} + +void MonitorCoStreamFlowDeletion( + _In_ UINT16 layerId, + _In_ UINT32 calloutId, + _In_ UINT64 flowContext) +{ + KLOCK_QUEUE_HANDLE lockHandle; + FLOW_DATA* flowData; + HRESULT result; + ULONG_PTR flowPtr; + + + UNREFERENCED_PARAMETER(layerId); + UNREFERENCED_PARAMETER(calloutId); + + result = ULongLongToULongPtr(flowContext, &flowPtr); + ASSERT(result == S_OK); + _Analysis_assume_(result == S_OK); + + + flowData = ((FLOW_DATA*)flowPtr); + + // + // If we're already being deleted from the list then we mustn't try to + // remove ourselves here. + // + KeAcquireInStackQueuedSpinLock(&flowContextListLock, &lockHandle); + + if (!flowData->deleting) + { + RemoveEntryList(&flowData->listEntry); + } + + KeReleaseInStackQueuedSpinLock(&lockHandle); + + MonitorCoCleanupFlowContext(flowData); +} + +NTSTATUS MonitorCoStreamNotifyV4( + _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, + _In_ const GUID* filterKey, + _Inout_ const FWPS_FILTER* filter) +/* +Routine Description + + Notification routine that is called whenever a filter is added, deleted or + modified on the layer that our callout is registered against. + +Arguments + [IN] FWPS_CALLOUT_NOTIFY_TYPE notifyType - Type of notification + + [IN] GUID* filterKey - Key of the filter that was + added/deleted/modified. + + [IN] struct FWPS_FILTER_* filter - pointer to the Filter itself. + +Return values + + STATUS_SUCCESS or a specific error code. + +Notes + + +*/ +{ + UNREFERENCED_PARAMETER(notifyType); + UNREFERENCED_PARAMETER(filterKey); + UNREFERENCED_PARAMETER(filter); + + switch (notifyType) + { + case FWPS_CALLOUT_NOTIFY_ADD_FILTER: + DoTraceMessage(TRACE_LAYER_NOTIFY, + "Filter Added to Stream layer.\r\n"); + + break; + case FWPS_CALLOUT_NOTIFY_DELETE_FILTER: + DoTraceMessage(TRACE_LAYER_NOTIFY, + "Filter Deleted from Stream layer.\r\n"); + break; + } + return STATUS_SUCCESS; +} diff --git a/network/trans/msnmntr/sys/msnmntr.h b/network/trans/msnmntr/sys/msnmntr.h new file mode 100644 index 00000000..adb470c0 --- /dev/null +++ b/network/trans/msnmntr/sys/msnmntr.h @@ -0,0 +1,55 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Monitor Sample driver callout header + +Environment: + + Kernel mode + +--*/ + +#pragma once + +typedef struct _FLOW_DATA +{ + UINT64 flowHandle; + UINT64 flowContext; + UINT64 calloutId; + ULONG localAddressV4; + USHORT localPort; + USHORT ipProto; + ULONG remoteAddressV4; + USHORT remotePort; + WCHAR* processPath; + LIST_ENTRY listEntry; + BOOLEAN deleting; +} FLOW_DATA; + +NTSTATUS +MonitorCoInitialize(_Inout_ DEVICE_OBJECT* deviceObject); + +void +MonitorCoUninitialize(void); + +NTSTATUS +MonitorCoRegisterCallouts(_Inout_ void* deviceObject); + +NTSTATUS +MonitorCoUnregisterCallouts(void); + +NTSTATUS +MonitorCoSetApplication( + _In_reads_(size) wchar_t* fileName, + _In_ ULONG size); + +NTSTATUS +MonitorCoEnableMonitoring( + _In_ MONITOR_SETTINGS* monitorSettings); + +void +MonitorCoDisableMonitoring(void); + diff --git a/network/trans/msnmntr/sys/msnmntr.inf b/network/trans/msnmntr/sys/msnmntr.inf new file mode 100644 index 00000000..30d012b1 --- /dev/null +++ b/network/trans/msnmntr/sys/msnmntr.inf @@ -0,0 +1,54 @@ +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved +;;; +;;; Abstract: +;;; Monitor Callout sample driver install configuration. +;;; + +[Version] + Signature = "$Windows NT$" + Class = WFPCALLOUTS + ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} + Provider = %Contoso% + CatalogFile = MSNMntr.cat + DriverVer = 11/24/2014,14.24.55.836 + +[SourceDisksNames] + 1 = %MSNMntrDisk%,,,"" + +[SourceDisksFiles] + MSNMntr.sys = 1,, + +[DestinationDirs] + DefaultDestDir = 12 ; %WinDir%\System32\Drivers + MSNMntr.DriverFiles = 12 ; %WinDir%\System32\Drivers + +[DefaultInstall] + OptionDesc = %MSNMntrServiceDesc% + CopyFiles = MSNMntr.DriverFiles + +[DefaultInstall.Services] + AddService = %MSNMntrServiceName%,,MSNMntr.Service + +[DefaultUninstall] + DelFiles = MSNMntr.DriverFiles + +[DefaultUninstall.Services] + DelService = %MSNMntrServiceName%,0x200 ; SPSVCINST_STOPSERVICE + +[MSNMntr.DriverFiles] + MSNMntr.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY + +[MSNMntr.Service] + DisplayName = %MSNMntrServiceName% + Description = %MSNMntrServiceDesc% + ServiceType = 1 ; SERVICE_KERNEL_DRIVER + StartType = 3 ; SERVICE_DEMAND_START + ErrorControl = 1 ; SERVICE_ERROR_NORMAL + ServiceBinary = %12%\MSNMntr.sys ; %WinDir%\System32\Drivers\MSNMntr.sys + +[Strings] + Contoso = "Contoso Ltd." + MSNMntrDisk = "Monitor Installation Disk" + MSNMntrServiceDesc = "Monitor Callout Driver" + MSNMntrServiceName = "MSNMntr" \ No newline at end of file diff --git a/network/trans/msnmntr/sys/msnmntr.vcxproj b/network/trans/msnmntr/sys/msnmntr.vcxproj new file mode 100644 index 00000000..19d05a61 --- /dev/null +++ b/network/trans/msnmntr/sys/msnmntr.vcxproj @@ -0,0 +1,194 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A0314F39-C7C2-463C-B2BE-DF387AB755F5} + $(MSBuildProjectName) + 1 + Debug + Win32 + {2235E7F5-485C-4A50-AC6E-7D5127C63D50} + + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + + + + msnmntr + + + msnmntr + + + msnmntr + + + msnmntr + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters b/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters new file mode 100644 index 00000000..7a507ae9 --- /dev/null +++ b/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters @@ -0,0 +1,35 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {061ABC02-158F-4210-A282-D00E6E173776} + + + h;hpp;hxx;hm;inl;inc;xsd + {EAF55685-C4D1-47E0-A3A8-B3E417170A02} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {7449D543-948C-4CDB-AB03-EE885873121D} + + + inf;inv;inx;mof;mc; + {E3258914-96DA-44CC-9DB9-3F58E7232911} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/network/trans/msnmntr/sys/notify.c b/network/trans/msnmntr/sys/notify.c new file mode 100644 index 00000000..22253797 --- /dev/null +++ b/network/trans/msnmntr/sys/notify.c @@ -0,0 +1,294 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Monitor Sample driver notification routines + +Environment: + + Kernel mode + +--*/ + +#include + +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + + +#include "ioctl.h" + +#include "msnmntr.h" + +#include "notify.h" + +// +// Software Tracing Definitions +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(MsnMntrNotify,(aca2f74a, 7a0d, 4f47, be4b, 66900813b8e5), \ + WPP_DEFINE_BIT(TRACE_CLIENT_SERVER) \ + WPP_DEFINE_BIT(TRACE_PEER_TO_PEER) \ + WPP_DEFINE_BIT(TRACE_UNKNOWN) \ + WPP_DEFINE_BIT(TRACE_ALL_TRAFFIC) ) + +#include "notify.tmh" // This file will be auto generated + + +#define TAG_NAME_NOTIFY 'oNnM' + +NTSTATUS +MonitorNfInitialize( + _In_ DEVICE_OBJECT* deviceObject) +{ + UNREFERENCED_PARAMETER(deviceObject); + + return STATUS_SUCCESS; +} + +NTSTATUS +MonitorNfUninitialize(void) +{ + return STATUS_SUCCESS; +} + +__forceinline +void* +MonitorNfpFindCharacters( + _In_reads_bytes_(streamLength) const char* stream, + _In_ size_t streamLength, + _In_reads_bytes_(subStreamLength) const char* subStream, + _In_ size_t subStreamLength, + _Out_ size_t* bytesLeft) +{ + size_t currentOffset = 0; + void* subStreamPtr = NULL; + + *bytesLeft = streamLength; + + if (subStreamLength > streamLength) + { + return NULL; + } + + while (currentOffset+subStreamLength <= streamLength) + { + if (0 == memcmp((void*)(stream+currentOffset), subStream, subStreamLength)) + { + subStreamPtr = (void*)(char*)(stream+currentOffset); + *bytesLeft = streamLength; + *bytesLeft -= currentOffset; + *bytesLeft -= subStreamLength; + break; + } + currentOffset += subStreamLength; + } + + return subStreamPtr; +} + +NTSTATUS +MonitorNfParseMessageInbound( + _In_reads_bytes_(streamLength) BYTE* stream, + _In_ size_t streamLength, + _In_ USHORT localPort, + _In_ USHORT remotePort) +{ + UNREFERENCED_PARAMETER(stream); + + DoTraceMessage(TRACE_CLIENT_SERVER, + "%Id bytes received. Local Port: %d Remote Port: %d.", + streamLength, + localPort, + remotePort); + return STATUS_SUCCESS; +} + +NTSTATUS +MonitorNfParseMessageInboundHttpHeader( + _In_reads_bytes_(streamLength) BYTE* stream, + _In_ size_t streamLength, + _In_ USHORT localPort, + _In_ USHORT remotePort) +{ + BYTE* msgStart = NULL; + size_t bytesLeft; + NTSTATUS status = STATUS_INVALID_PARAMETER; + + // Walk past the HTTP header. + msgStart = (BYTE*) MonitorNfpFindCharacters((char*)stream, + streamLength, + "\r\n\r\n", + (ULONG)strlen("\r\n\r\n"), + &bytesLeft); + if (msgStart && (bytesLeft > 0)) + { + size_t msgLength; + + msgStart += 4; // step past \r\n\r\n. + + msgLength = streamLength - (ULONG)(ULONG_PTR)(msgStart - stream); + + // Do the final inbound message processing. + status = MonitorNfParseMessageInbound(msgStart, + msgLength, + localPort, + remotePort); + } + + return status; +} + +NTSTATUS +MonitorNfParseMessageOutbound( + _In_reads_bytes_(streamLength) BYTE* stream, + _In_ size_t streamLength, + _In_ USHORT localPort, + _In_ USHORT remotePort) +{ + UNREFERENCED_PARAMETER(stream); + + DoTraceMessage(TRACE_CLIENT_SERVER, + "%Id bytes sent. Local Port: %d Remote Port: %d.", + streamLength, + localPort, + remotePort); + return STATUS_SUCCESS; +} + +NTSTATUS +MonitorNfParseMessageOutboundHttpHeader( + _In_reads_bytes_(streamLength) BYTE* stream, + _In_ size_t streamLength, + _In_ USHORT localPort, + _In_ USHORT remotePort) +{ + BYTE* msgStart = NULL; + size_t bytesLeft; + NTSTATUS status = STATUS_SUCCESS; + + // Walk past the HTTP header. + msgStart = (BYTE*) MonitorNfpFindCharacters((char*)stream, + streamLength, + "\r\n\r\n", + (ULONG)strlen("\r\n\r\n"), + &bytesLeft); + if (msgStart && (bytesLeft > 0)) + { + size_t msgLength; + + msgStart += 4; // step past \r\n\r\n. + + msgLength = streamLength - (ULONG)(ULONG_PTR)(msgStart - stream); + status = MonitorNfParseMessageOutbound(msgStart, + msgLength, + localPort, + remotePort); + } + + return status; +} + +NTSTATUS +MonitorNfParseStreamAndTraceMessage( + _In_reads_bytes_(streamLength) BYTE* stream, + _In_ size_t streamLength, + _In_ BOOLEAN inbound, + _In_ USHORT localPort, + _In_ USHORT remotePort) +{ + NTSTATUS status; + + if (!inbound) + { + if ((_strnicmp((const char*)stream, "POST", streamLength) == 0) + || (_strnicmp((const char*)stream, "GET", streamLength) == 0)) + { + if ((MonitorNfParseMessageOutboundHttpHeader(stream, + streamLength, + localPort, + remotePort)) != STATUS_SUCCESS) + return STATUS_INSUFFICIENT_RESOURCES; + } + else + { + if ((MonitorNfParseMessageOutbound(stream, + streamLength, + localPort, + remotePort)!= STATUS_SUCCESS)) + return STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + if (_strnicmp((const char*)stream, "HTTP", streamLength) == 0) + { + if ((MonitorNfParseMessageInboundHttpHeader(stream, + streamLength, + localPort, + remotePort)) != STATUS_SUCCESS) + return STATUS_INSUFFICIENT_RESOURCES; + } + else + { + if ((MonitorNfParseMessageInbound(stream, + streamLength, + localPort, + remotePort)) != STATUS_SUCCESS) + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + { + status = STATUS_SUCCESS; + } + + return status; +} + + +NTSTATUS MonitorNfNotifyMessage( + _In_ const FWPS_STREAM_DATA* streamBuffer, + _In_ BOOLEAN inbound, + _In_ USHORT localPort, + _In_ USHORT remotePort +) +{ + NTSTATUS status = STATUS_SUCCESS; + BYTE* stream = NULL; + SIZE_T streamLength = streamBuffer->dataLength; + SIZE_T bytesCopied = 0; + + if(streamLength == 0) + return status; + + stream = ExAllocatePoolWithTag(NonPagedPool, + streamLength, + TAG_NAME_NOTIFY); + if (!stream) + return STATUS_INSUFFICIENT_RESOURCES; + + RtlZeroMemory(stream,streamLength); + + FwpsCopyStreamDataToBuffer( + streamBuffer, + stream, + streamLength, + &bytesCopied); + + NT_ASSERT(bytesCopied == streamLength); + + status = MonitorNfParseStreamAndTraceMessage(stream, streamLength, inbound, localPort, remotePort); + + ExFreePoolWithTag(stream, TAG_NAME_NOTIFY); + + return status; +} diff --git a/network/trans/msnmntr/sys/notify.h b/network/trans/msnmntr/sys/notify.h new file mode 100644 index 00000000..ba37fab2 --- /dev/null +++ b/network/trans/msnmntr/sys/notify.h @@ -0,0 +1,32 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Monitor Sample driver notification header + +Environment: + + Kernel mode + +--*/ + +#pragma once + +#define TAG_NOTIFY 'yftN' + +NTSTATUS +MonitorNfInitialize( + _In_ DEVICE_OBJECT* deviceObject); + +NTSTATUS +MonitorNfUninitialize(void); + +NTSTATUS MonitorNfNotifyMessage( + _In_ const FWPS_STREAM_DATA* streamBuffer, + _In_ BOOLEAN inbound, + _In_ USHORT localPort, + _In_ USHORT remotePort); + + diff --git a/network/trans/stmedit/ReadMe.md b/network/trans/stmedit/ReadMe.md new file mode 100644 index 00000000..da921272 --- /dev/null +++ b/network/trans/stmedit/ReadMe.md @@ -0,0 +1,60 @@ +Windows Filtering Platform Stream Edit Sample +============================================= + +This sample driver demonstrates replacing a string pattern for a Transmission Control Protocol (TCP) connection using the Windows Filtering Platform (WFP). + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +The sample consists of a kernel-mode Windows Filtering Platform (WFP) callout driver (Stmedit.sys) that can operate in one of the following modes: + +- Inline editing where all modification is done within the `ClassifyFn` callout function. +- Out-of-band editing where all modification is done by a worker thread (the default). + +The sample performs inspection for both Internet Protocol version 4 (IPv4) and Internet Protocol version 6 (IPv6) traffic. + +Before experimenting with the sample, add an exception for the InspectionPort to your host firewall. + +Automatic deployment +-------------------- + +Before you automatically deploy a driver, you must provision the target computer. For instructions, see [Configuring a Computer for Driver Deployment, Testing, and Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/). After you have provisioned the target computer, continue with these steps: + +1. On the host computer, in Visual Studio, in Solution Explorer, right click **package** (lower case), and choose **Properties**. Navigate to **Configuration Properties \> Driver Install \> Deployment**. +2. Check **Enable deployment**, and check **Remove previous driver versions before deployment**. For **Target Computer Name**, select the name of a target computer that you provisioned previously. Select **Do not install**. Click **OK**. +3. On the **Build** menu, choose **Build Solution**. +4. On the target computer, navigate to DriverTest\\Drivers, and locate the file stmedit.inf. Right click stmedit.inf, and choose **Install**. + +Manual deployment +----------------- + +Before you manually deploy a driver, you must turn on test signing and install a certificate on the target computer. You also need to copy the [DevCon](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544707) tool to the target computer. For instructions, see [Preparing a Computer for Manual Driver Deployment](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265571). After you have prepared the target computer for manual deployment, continue with these steps: + +1. Copy all of the files in your driver package to a folder on the target computer (for example, c:\\WfpStreamEditSamplePackage). +2. On the target computer, navigate to your driver package folder. Right click stmedit.inf, and choose **Install** + +Create Registry values +---------------------- + +- On the target computer, open Regedit, and navigate to this key: + + **HKLM**\\**System**\\**CurrentControlSet**\\**Services**\\**strmedit**\\**Parameters** + +You can create and set values for the following registry entries. + +- **EditInline** (REG\_DWORD type): 1 for inline editing, 0 for out-of-band editing (the default) +- **StringToFind** (REG\_SZ type): default = "rainy" +- **StringToReplace** (REG\_SZ type): default = "sunny" +- **InspectionPort** (REG\_DWORD type): TCP port (default = 5001) +- **InspectOutbound** (REG\_DWORD type): TCP port (default = 0) + +Start the stmedit service +------------------------- + +On the target computer, open a Command Prompt window as Administrator, and enter **net start stmedit**. (To stop the driver, enter **net stop stmedit**.) + +Remarks +------- + +For more information on creating a Windows Filtering Platform Callout Driver, see [Windows Filtering Platform Callout Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff571068). + diff --git a/network/trans/stmedit/stmedit.sln b/network/trans/stmedit/stmedit.sln new file mode 100644 index 00000000..7f1cb436 --- /dev/null +++ b/network/trans/stmedit/stmedit.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stmedit", "sys\stmedit.vcxproj", "{98513F1C-C28D-4F03-99A8-88FD4BD707C7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {98513F1C-C28D-4F03-99A8-88FD4BD707C7}.Debug|Win32.ActiveCfg = Debug|Win32 + {98513F1C-C28D-4F03-99A8-88FD4BD707C7}.Debug|Win32.Build.0 = Debug|Win32 + {98513F1C-C28D-4F03-99A8-88FD4BD707C7}.Release|Win32.ActiveCfg = Release|Win32 + {98513F1C-C28D-4F03-99A8-88FD4BD707C7}.Release|Win32.Build.0 = Release|Win32 + {98513F1C-C28D-4F03-99A8-88FD4BD707C7}.Debug|x64.ActiveCfg = Debug|x64 + {98513F1C-C28D-4F03-99A8-88FD4BD707C7}.Debug|x64.Build.0 = Debug|x64 + {98513F1C-C28D-4F03-99A8-88FD4BD707C7}.Release|x64.ActiveCfg = Release|x64 + {98513F1C-C28D-4F03-99A8-88FD4BD707C7}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/network/trans/stmedit/sys/inline_edit.c b/network/trans/stmedit/sys/inline_edit.c new file mode 100644 index 00000000..16685a9e --- /dev/null +++ b/network/trans/stmedit/sys/inline_edit.c @@ -0,0 +1,546 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Stream Edit Callout Driver Sample. + + This sample demonstrates inline stream inspection/editing + via the WFP stream API. + +Environment: + + Kernel mode + +--*/ + +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + +#include + +#include "inline_edit.h" +#include "oob_edit.h" +#include "stream_callout.h" + +void +InlineEditInit( + _Out_ STREAM_EDITOR* streamEditor + ) +{ + streamEditor->editInline = TRUE; + streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA; +} + +void +NTAPI StreamInjectCompletionFn( + _Inout_ void* context, + _Inout_ NET_BUFFER_LIST* netBufferList, + _In_ BOOLEAN dispatchLevel + ) +{ + MDL* mdl = (MDL*)context; + + UNREFERENCED_PARAMETER(dispatchLevel); + + if (mdl != NULL) + { + IoFreeMdl(mdl); + } + + FwpsFreeNetBufferList(netBufferList); +} + +NTSTATUS +StreamEditFlushData( + _Inout_ STREAM_EDITOR* streamEditor, + UINT64 flowId, + UINT32 calloutId, + UINT16 layerId, + UINT32 streamFlags + ) +/* ++ + + This function re-injects buffered data back to the data stream upon + receiving a FIN. The data was buffered because it was not big enough + (size wise) to make an editing decision. + +-- */ +{ + NTSTATUS status; + + MDL* mdl = NULL; + NET_BUFFER_LIST* netBufferList = NULL; + + NT_ASSERT(streamEditor->dataOffset == 0); + + mdl = IoAllocateMdl( + streamEditor->scratchBuffer, + (ULONG)(streamEditor->dataLength), + FALSE, + FALSE, + NULL + ); + + if (mdl == NULL) + { + status = STATUS_NO_MEMORY; + goto Exit; + } + + MmBuildMdlForNonPagedPool(mdl); + + status = FwpsAllocateNetBufferAndNetBufferList( + gNetBufferListPool, + 0, + 0, + mdl, + 0, + streamEditor->dataLength, + &netBufferList + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + streamFlags &= ~(FWPS_STREAM_FLAG_SEND_DISCONNECT | FWPS_STREAM_FLAG_RECEIVE_DISCONNECT); + + status = FwpsStreamInjectAsync( + gInjectionHandle, + NULL, + 0, + flowId, + calloutId, + layerId, + streamFlags, + netBufferList, + streamEditor->dataLength, + StreamInjectCompletionFn, + mdl + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + mdl = NULL; + netBufferList = NULL; + +Exit: + + if (mdl != NULL) + { + IoFreeMdl(mdl); + } + if (netBufferList != NULL) + { + FwpsFreeNetBufferList(netBufferList); + } + + return status; +} + +void +StreamInlineEdit( + _Inout_ STREAM_EDITOR* streamEditor, + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _In_ const FWPS_FILTER* filter, + _In_ const FWPS_STREAM_DATA* streamData, + _Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) +/* ++ + + This function implements the state machine that scans the content + and computes the number of bytes to permit, bytes to block, and + performs stream injection to replace the blocked data. + +-- */ +{ + UINT findLength = (UINT) strlen(configStringToFind); + UINT replaceLength = (UINT) strlen(configStringToReplace); + + if ((streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) || + (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)) + { + if (streamEditor->dataLength > 0) + { + StreamEditFlushData( + streamEditor, + inMetaValues->flowHandle, + filter->action.calloutId, + inFixedValues->layerId, + streamData->flags + ); + + streamEditor->dataLength = 0; + streamEditor->dataOffset = 0; + } + + NT_ASSERT(streamEditor->inlineEditState == INLINE_EDIT_WAITING_FOR_DATA); + + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + goto Exit; + } + + if (streamData->dataLength == 0) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + goto Exit; + } + + if (streamEditor->inlineEditState != INLINE_EDIT_SKIPPING) + { + if ((streamData->dataLength < findLength) && + !(classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA; + ioPacket->countBytesRequired = findLength; + + classifyOut->actionType = FWP_ACTION_NONE; + goto Exit; + } + } + + switch (streamEditor->inlineEditState) + { + case INLINE_EDIT_WAITING_FOR_DATA: + { + if (StreamCopyDataForInspection( + streamEditor, + streamData + ) == FALSE) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + classifyOut->actionType = FWP_ACTION_NONE; + goto Exit; + } + + // + // Pass-thru to scanning + // + } + case INLINE_EDIT_SCANNING: + { + UINT i; + BYTE* dataStart = (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset; + BOOLEAN found = FALSE; + + for (i = 0; i < streamEditor->dataLength; ++i) + { + if (i + findLength <= streamEditor->dataLength) + { + if (RtlCompareMemory( + dataStart + i, + configStringToFind, + findLength + ) == findLength) + { + found = TRUE; + + streamEditor->inlineEditState = INLINE_EDIT_MODIFYING; + + if (i != 0) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + ioPacket->countBytesEnforced = i; + + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + streamEditor->dataOffset += i; + streamEditor->dataLength -= i; + + break; + } + else + { + goto modify_data; + } + } + } + else + { + if (classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA) + { + break; + } + + if (RtlCompareMemory( + dataStart + i, + configStringToFind, + streamEditor->dataLength - i + ) == streamEditor->dataLength - i) + { + found = TRUE; // this is a partial find + + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + ioPacket->countBytesEnforced = i; + + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + RtlMoveMemory( + streamEditor->scratchBuffer, + dataStart + i, + streamEditor->dataLength - i + ); + + streamEditor->dataOffset = 0; + streamEditor->dataLength = streamEditor->dataLength - i; + + streamEditor->inlineEditState = INLINE_EDIT_SKIPPING; + + break; + } + } + } + + if (!found) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + ioPacket->countBytesEnforced = 0; + + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + streamEditor->dataOffset = 0; + streamEditor->dataLength = 0; + + streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA; + } + + break; + } + case INLINE_EDIT_SKIPPING: + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + ioPacket->countBytesEnforced = 0; + + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + + streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA; + + break; + } + case INLINE_EDIT_MODIFYING: + +modify_data: + + { + NTSTATUS status; + NET_BUFFER_LIST* netBufferList; + + status = FwpsAllocateNetBufferAndNetBufferList( + gNetBufferListPool, + 0, + 0, + gStringToReplaceMdl, + 0, + replaceLength, + &netBufferList + ); + + if (!NT_SUCCESS(status)) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + classifyOut->actionType = FWP_ACTION_NONE; + goto Exit; + } + + status = FwpsStreamInjectAsync( + gInjectionHandle, + NULL, + 0, + inMetaValues->flowHandle, + filter->action.calloutId, + inFixedValues->layerId, + streamData->flags, + netBufferList, + replaceLength, + StreamInjectCompletionFn, + NULL + ); + + if (!NT_SUCCESS(status)) + { + FwpsFreeNetBufferList(netBufferList); + + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + classifyOut->actionType = FWP_ACTION_NONE; + goto Exit; + } + + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + ioPacket->countBytesEnforced = findLength; + + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + + streamEditor->dataOffset += findLength; + streamEditor->dataLength -= findLength; + + if (streamEditor->dataLength > 0) + { + streamEditor->inlineEditState = INLINE_EDIT_SCANNING; + } + else + { + streamEditor->dataOffset = 0; + + streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA; + } + + break; + } + default: + NT_ASSERT(FALSE); + break; + }; + +Exit: + + return; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +NTAPI +StreamInlineEditClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_ void* layerData, + _In_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#else + +void +NTAPI +StreamInlineEditClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Out_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +/* ++ + + This is the ClassifyFn function registered by the inline stream edit callout. + + An inline stream modification callout performs editing from within the + ClassifyFn call by permitting sections of the content and replacing other + sections by removing them and injecting new content. + +-- */ +{ + FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket; + FWPS_STREAM_DATA* streamData; + + ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)layerData; + NT_ASSERT(ioPacket != NULL); + + streamData = ioPacket->streamData; + NT_ASSERT(streamData != NULL); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(classifyContext); +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(flowContext); + + RtlZeroMemory(classifyOut, sizeof(FWPS_CLASSIFY_OUT)); + + // + // Let go the traffic that the editor does not care about. + // + + if ((configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_RECEIVE)) || + (!configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_SEND))) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + goto Exit; + } + + // + // In this sample we don't edit TCP urgent data + // + + if ((streamData->flags & FWPS_STREAM_FLAG_SEND_EXPEDITED) || + (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_EXPEDITED)) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + goto Exit; + } + + StreamInlineEdit( + &gStreamEditor, + inFixedValues, + inMetaValues, + filter, + streamData, + ioPacket, + classifyOut + ); + +Exit: + + return; +} diff --git a/network/trans/stmedit/sys/inline_edit.h b/network/trans/stmedit/sys/inline_edit.h new file mode 100644 index 00000000..a9345324 --- /dev/null +++ b/network/trans/stmedit/sys/inline_edit.h @@ -0,0 +1,65 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Stream Edit Callout Driver Sample. + + This sample demonstrates inline stream inspection/editing + via the WFP stream API. + +Environment: + + Kernel mode + +--*/ + +#ifndef _INLINE_EDIT_H +#define _INLINE_EDIT_H + +typedef enum INLINE_EDIT_STATE_ +{ + INLINE_EDIT_WAITING_FOR_DATA, + INLINE_EDIT_SKIPPING, + INLINE_EDIT_MODIFYING, + INLINE_EDIT_SCANNING +} INLINE_EDIT_STATE; + +typedef struct STREAM_EDITOR_ STREAM_EDITOR; + +void +InlineEditInit( + _Out_ STREAM_EDITOR* + ); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +NTAPI +StreamInlineEditClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_ void* layerData, + _In_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +#else + +void +NTAPI +StreamInlineEditClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif // _INLINE_EDIT_H diff --git a/network/trans/stmedit/sys/oob_edit.c b/network/trans/stmedit/sys/oob_edit.c new file mode 100644 index 00000000..ee53bc5e --- /dev/null +++ b/network/trans/stmedit/sys/oob_edit.c @@ -0,0 +1,1363 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Stream Edit Callout Driver Sample. + + This sample demonstrates Out-of-band (OOB) stream inspection/editing + via the WFP stream API. + +Environment: + + Kernel mode + +--*/ + +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + +#include + +#include "inline_edit.h" +#include "oob_edit.h" +#include "stream_callout.h" + +#define STREAM_EDITOR_OUTGOING_DATA_TAG 'doeS' +#define STREAM_EDITOR_MDL_DATA_TAG 'dmeS' + +void* gThreadObj; + +KSTART_ROUTINE StreamOobEditWorker; + +NTSTATUS +OobEditInit( + _Out_ STREAM_EDITOR* streamEditor + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + HANDLE threadHandle; + + streamEditor->editInline = FALSE; + + KeInitializeSpinLock(&streamEditor->oobEditInfo.editLock); + + KeInitializeEvent( + &streamEditor->oobEditInfo.editEvent, + NotificationEvent, + FALSE + ); + + streamEditor->oobEditInfo.busyThreshold = 32 * 1024; + streamEditor->oobEditInfo.editState = OOB_EDIT_IDLE; + + InitializeListHead(&streamEditor->oobEditInfo.outgoingDataQueue); + + + status = PsCreateSystemThread( + &threadHandle, + THREAD_ALL_ACCESS, + NULL, + NULL, + NULL, + StreamOobEditWorker, + &gStreamEditor + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = ObReferenceObjectByHandle( + threadHandle, + 0, + NULL, + KernelMode, + &gThreadObj, + NULL + ); + NT_ASSERT(NT_SUCCESS(status)); + + ZwClose(threadHandle); + +Exit: + + return status; +} + +void +OobEditShutdown( + _Out_ STREAM_EDITOR* streamEditor + ) +{ + KLOCK_QUEUE_HANDLE editLockHandle; + + KeAcquireInStackQueuedSpinLock( + &streamEditor->oobEditInfo.editLock, + &editLockHandle + ); + + streamEditor->oobEditInfo.shuttingDown = TRUE; + + switch (streamEditor->oobEditInfo.editState) + { + case OOB_EDIT_IDLE: + { + streamEditor->oobEditInfo.editState = OOB_EDIT_SHUT_DOWN; + + KeSetEvent( + &gStreamEditor.oobEditInfo.editEvent, + IO_NO_INCREMENT, + FALSE + ); + + break; + } + default: + break; + }; + + KeReleaseInStackQueuedSpinLock(&editLockHandle); + + NT_ASSERT(gThreadObj != NULL); + + KeWaitForSingleObject( + gThreadObj, + Executive, + KernelMode, + FALSE, + NULL + ); + + ObDereferenceObject(gThreadObj); +} + +__inline +NET_BUFFER_LIST* +TailOfNetBufferListChain( + _In_ NET_BUFFER_LIST* netBufferListChain + ) +{ + NT_ASSERT(netBufferListChain != NULL); + + while (netBufferListChain->Next != NULL) + { + netBufferListChain = netBufferListChain->Next; + } + + return netBufferListChain; +} + +void +NTAPI +StreamOobInjectCompletionFn( + _Inout_ void* context, + _Inout_ NET_BUFFER_LIST* netBufferList, + BOOLEAN dispatchLevel + ) +/* ++ + + Injection completion function for injecting an NBL created using + FwpsAllocateNetBufferAndNetBufferList. This function frees up + resources allocated during StreamOobReinjectData(). + +-- */ +{ + MDL* mdl = (MDL*)context; + + UNREFERENCED_PARAMETER(dispatchLevel); + + FwpsFreeNetBufferList(netBufferList); + + if (mdl != NULL) + { + IoFreeMdl(mdl); + + // + // The MDL mapped over a pool alloc which we need to free here. + // + + ExFreePoolWithTag( + mdl->MappedSystemVa, + STREAM_EDITOR_MDL_DATA_TAG + ); + } +} + +void +NTAPI StreamOobInjectCloneCompletionFn( + _Inout_ void* context, + _Inout_ NET_BUFFER_LIST* netBufferList, + BOOLEAN dispatchLevel + ) +/* ++ + + Injection completion function for injecting one of the NBLs cloned + via FwpsCloneStreamData. + + FwpsCloneStreamData can return a chain of cloned NBLs; each NBL will + complete separately. + +-- */ +{ + UNREFERENCED_PARAMETER(context); + UNREFERENCED_PARAMETER(dispatchLevel); + + FwpsFreeCloneNetBufferList(netBufferList, 0); +} + +NTSTATUS +StreamOobQueueUpIncomingData( + _Inout_ STREAM_EDITOR* streamEditor, + _Inout_ FWPS_STREAM_DATA* streamData + ) +/* ++ + + This function clones the indicated stream data into a NBL chain and + appends the chain at the end of the existing chain (if exists) inside + the streamEditor. + + This function assumes that the oobEditInfo lock inside streamEditor is + being held. + +-- */ +{ + NTSTATUS status; + + NET_BUFFER_LIST* clonedNetBufferListChain; + + status = FwpsCloneStreamData( + streamData, + NULL, + NULL, + 0, + &clonedNetBufferListChain + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + // + // TCP Fin (EOF) is indicated by an empty NBL with disconnect flag + // set, since it does not contain any data we queue it up separately. + // + + if ((streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) || + (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)) + { + NT_ASSERT(streamEditor->oobEditInfo.noMoreData); + NT_ASSERT(streamEditor->oobEditInfo.nblEof == NULL); + NT_ASSERT(streamData->dataLength == 0); + + streamEditor->oobEditInfo.nblEof = clonedNetBufferListChain; + + status = STATUS_SUCCESS; + goto Exit; + } + + if (streamEditor->oobEditInfo.nblTail != NULL) + { + NT_ASSERT(streamEditor->oobEditInfo.nblHead != NULL); + NT_ASSERT(streamEditor->oobEditInfo.nblTail->Next == NULL); + + streamEditor->oobEditInfo.nblTail->Next = clonedNetBufferListChain; + } + else + { + NT_ASSERT(streamEditor->oobEditInfo.nblHead == NULL); + + streamEditor->oobEditInfo.nblHead = clonedNetBufferListChain; + } + + streamEditor->oobEditInfo.nblTail = + TailOfNetBufferListChain(clonedNetBufferListChain); + + streamEditor->oobEditInfo.totalDataLength += streamData->dataLength; + + streamEditor->oobEditInfo.streamFlags = streamData->flags; + +Exit: + + return status; +} + +void +StreamOobEdit( + _Inout_ STREAM_EDITOR* streamEditor, + const FWPS_INCOMING_VALUES* inFixedValues, + const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + const FWPS_FILTER* filter, + _Inout_ FWPS_STREAM_DATA* streamData, + _Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) +/* ++ + + This function queues up incoming data and notifies the worker thread + to process them. The incoming data is blocked and removed from the + stream while data is pending. + + If the editor is shutdown (e.g. during driverUnload) as indicated + by OOB_EDIT_SHUT_DOWN state, it permits the indicated data inline after + flushing all pended data (to be carried out by the caller). + +-- */ + +{ + NTSTATUS status; + + KLOCK_QUEUE_HANDLE editLockHandle; + + KeAcquireInStackQueuedSpinLock( + &streamEditor->oobEditInfo.editLock, + &editLockHandle + ); + + if (streamEditor->oobEditInfo.nblEof != NULL) + { + // + // A new flow arrives before we finish processing an earlier flow. Production + // code should create 1:1 between streamEditor and flow to handle this + // condition. See the "MSN Monitor sample" for how that can be implemented. + // + + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + classifyOut->actionType = FWP_ACTION_NONE; + + goto Exit; + } + + if (classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA) + { + NT_ASSERT(streamEditor->oobEditInfo.nblEof == NULL); + + streamEditor->oobEditInfo.noMoreData = TRUE; + } + + // + // Record needed flow information etc for data (re-)injection. + // + + streamEditor->oobEditInfo.calloutId = filter->action.calloutId; + streamEditor->oobEditInfo.flowId = inMetaValues->flowHandle; + streamEditor->oobEditInfo.layerId = inFixedValues->layerId; + + switch (streamEditor->oobEditInfo.editState) + { + case OOB_EDIT_PROCESSING: + { + if ((streamEditor->oobEditInfo.totalDataLength + streamData->dataLength) > + streamEditor->oobEditInfo.busyThreshold) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_DEFER; + classifyOut->actionType = FWP_ACTION_NONE; + + streamEditor->oobEditInfo.editState = OOB_EDIT_BUSY; + } + else + { + status = StreamOobQueueUpIncomingData( + streamEditor, + streamData + ); + + if (!NT_SUCCESS(status)) + { + streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; + + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + classifyOut->actionType = FWP_ACTION_NONE; + } + else + { + // + // State remains at OOB_EDIT_PROCESSING state. Since the worker thread + // is active there is no need to set the event (to wake it up) + // + + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + ioPacket->countBytesEnforced = 0; + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + } + + break; + } + case OOB_EDIT_IDLE: + { + status = StreamOobQueueUpIncomingData( + streamEditor, + streamData + ); + + if (!NT_SUCCESS(status)) + { + streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; + + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + classifyOut->actionType = FWP_ACTION_NONE; + } + else + { + streamEditor->oobEditInfo.editState = OOB_EDIT_PROCESSING; + + // + // The worker thread is idle waiting for more work, now wake it up. + // + KeSetEvent( + &streamEditor->oobEditInfo.editEvent, + 0, + FALSE + ); + + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + ioPacket->countBytesEnforced = 0; + classifyOut->actionType = FWP_ACTION_BLOCK; + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + break; + } + case OOB_EDIT_SHUT_DOWN: + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + ioPacket->countBytesEnforced = 0; + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + break; + } + case OOB_EDIT_ERROR: + { + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + classifyOut->actionType = FWP_ACTION_NONE; + break; + } + default: + NT_ASSERT(FALSE); + }; + +Exit: + + KeReleaseInStackQueuedSpinLock(&editLockHandle); +} + +NTSTATUS +StreamOobQueueUpOutgoingData( + _Inout_ STREAM_EDITOR* streamEditor, + _Inout_ NET_BUFFER_LIST* netBufferList, + BOOLEAN isClone, + size_t dataLength, + DWORD streamFlags, + _In_opt_ MDL* mdl + ) +/* ++ + + This function queues up processed data (either sections of the indicated + data or newly created data) such that they can be (re-)injected back to + the data stream during the following context. + + 1. Before FWP_ACTION_BLOCK is returned from the ClassifyFn, or + 2. After EOF is indicated. + + Under the conditions above, the incoming data (which we pend) and the + outgoing data (which we (re-)inject) can be synchronized properly). + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + KLOCK_QUEUE_HANDLE editLockHandle; + OUTGOING_STREAM_DATA* outgoingStreamData; + + outgoingStreamData = (OUTGOING_STREAM_DATA*) ExAllocatePoolWithTag( + NonPagedPool, + sizeof(OUTGOING_STREAM_DATA), + STREAM_EDITOR_OUTGOING_DATA_TAG + ); + + if (outgoingStreamData == NULL) + { + status = STATUS_NO_MEMORY; + return status; + } + + RtlZeroMemory(outgoingStreamData, sizeof(OUTGOING_STREAM_DATA)); + + outgoingStreamData->netBufferList = netBufferList; + outgoingStreamData->isClone = isClone; + outgoingStreamData->dataLength = dataLength; + outgoingStreamData->streamFlags = streamFlags; + outgoingStreamData->mdl = mdl; + + KeAcquireInStackQueuedSpinLock( + &streamEditor->oobEditInfo.editLock, + &editLockHandle + ); + + InsertTailList( + &streamEditor->oobEditInfo.outgoingDataQueue, + &outgoingStreamData->listEntry + ); + + KeReleaseInStackQueuedSpinLock(&editLockHandle); + + return status; +} + +NTSTATUS +StreamOobFlushOutgoingData( + _Inout_ STREAM_EDITOR* streamEditor + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + KLOCK_QUEUE_HANDLE editLockHandle; + OUTGOING_STREAM_DATA* outgoingStreamData = NULL; + + for(;;) + { + KeAcquireInStackQueuedSpinLock( + &streamEditor->oobEditInfo.editLock, + &editLockHandle + ); + + if (!IsListEmpty(&streamEditor->oobEditInfo.outgoingDataQueue)) + { + LIST_ENTRY* listEntry = + RemoveHeadList(&streamEditor->oobEditInfo.outgoingDataQueue); + + outgoingStreamData = CONTAINING_RECORD( + listEntry, + OUTGOING_STREAM_DATA, + listEntry + ); + } + + KeReleaseInStackQueuedSpinLock(&editLockHandle); + + if (outgoingStreamData == NULL) + { + break; + } + + status = FwpsStreamInjectAsync( + gInjectionHandle, + NULL, + 0, + streamEditor->oobEditInfo.flowId, + streamEditor->oobEditInfo.calloutId, + streamEditor->oobEditInfo.layerId, + outgoingStreamData->streamFlags, + outgoingStreamData->netBufferList, + outgoingStreamData->dataLength, + outgoingStreamData->isClone ? StreamOobInjectCloneCompletionFn : + StreamOobInjectCompletionFn, + outgoingStreamData->mdl + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + ExFreePoolWithTag( + outgoingStreamData, + STREAM_EDITOR_OUTGOING_DATA_TAG + ); + + outgoingStreamData = NULL; + } + +Exit: + + if (outgoingStreamData != NULL) + { + NT_ASSERT(!NT_SUCCESS(status)); + + if (outgoingStreamData->isClone) + { + FwpsDiscardClonedStreamData( + outgoingStreamData->netBufferList, + 0, + FALSE + ); + } + else + { + FwpsFreeNetBufferList(outgoingStreamData->netBufferList); + + if (outgoingStreamData->mdl != NULL) + { + IoFreeMdl(outgoingStreamData->mdl); + + ExFreePoolWithTag( + outgoingStreamData->mdl->MappedSystemVa, + STREAM_EDITOR_MDL_DATA_TAG + ); + } + } + + ExFreePoolWithTag( + outgoingStreamData, + STREAM_EDITOR_OUTGOING_DATA_TAG + ); + } + + return status; +} + + +NTSTATUS +StreamOobReinjectData( + _Inout_ STREAM_EDITOR* streamEditor, + UINT32 streamFlags, + const void* data, + size_t length + ) +/* ++ + + This function injects a section of the original indicated data back + to the data stream. + + An MDL is allocated to describe the data section. + +-- */ +{ + NTSTATUS status; + + void* dataCopy = NULL; + MDL* mdl = NULL; + NET_BUFFER_LIST* netBufferList = NULL; + + dataCopy = ExAllocatePoolWithTag( + NonPagedPool, + length, + STREAM_EDITOR_MDL_DATA_TAG + ); + + if (dataCopy == NULL) + { + status = STATUS_NO_MEMORY; + goto Exit; + } + + RtlCopyMemory(dataCopy, data, length); + + mdl = IoAllocateMdl( + dataCopy, + (ULONG)length, + FALSE, + FALSE, + NULL + ); + if (mdl == NULL) + { + status = STATUS_NO_MEMORY; + goto Exit; + } + + MmBuildMdlForNonPagedPool(mdl); + + status = FwpsAllocateNetBufferAndNetBufferList( + gNetBufferListPool, + 0, + 0, + mdl, + 0, + length, + &netBufferList + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && + !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); + + status = StreamOobQueueUpOutgoingData( + streamEditor, + netBufferList, + FALSE, + length, + streamFlags, + mdl + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + dataCopy = NULL; + mdl = NULL; + netBufferList = NULL; + +Exit: + + if (netBufferList != NULL) + { + FwpsFreeNetBufferList(netBufferList); + } + if (mdl != NULL) + { + IoFreeMdl(mdl); + } + if (dataCopy != NULL) + { + ExFreePoolWithTag( + dataCopy, + STREAM_EDITOR_MDL_DATA_TAG + ); + } + return status; +} + +NTSTATUS +StreamOobInjectReplacement( + _Inout_ STREAM_EDITOR* streamEditor, + UINT32 streamFlags, + _In_opt_ MDL* data, + size_t length + ) +/* ++ + + This function injects a section of replacement data (in place of data + removed from the stream) into the data stream. + + The MDL describes the replacement data is allocated during DriverEntry + and does not need to be freed during injection completion. + +-- */ +{ + NTSTATUS status; + + NET_BUFFER_LIST* netBufferList = NULL; + + status = FwpsAllocateNetBufferAndNetBufferList( + gNetBufferListPool, + 0, + 0, + data, + 0, + length, + &netBufferList + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && + !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); + + status = StreamOobQueueUpOutgoingData( + streamEditor, + netBufferList, + FALSE, + length, + streamFlags, + NULL + ); + + if (!NT_SUCCESS(status)) + { + FwpsFreeNetBufferList(netBufferList); + goto Exit; + } + + netBufferList = NULL; + +Exit: + + if (netBufferList != NULL) + { + FwpsFreeNetBufferList(netBufferList); + } + + return status; +} + +NTSTATUS +StreamOobCopyDataToFlatBuffer( + _Inout_ STREAM_EDITOR* streamEditor, + _Inout_ NET_BUFFER_LIST* netBufferListChain, + size_t totalDataLength, + DWORD streamFlags + ) +/* ++ + + This function copies the data described by NBL(s) into a flat buffer. + + It reuses the FwpsCopyStreamDataToBuffer API (via StreamCopyDataForInspection) + by creating a FWPS_STREAM_DATA struct. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + FWPS_STREAM_DATA streamData = {0}; + + if (totalDataLength > 0) + { + streamData.netBufferListChain = netBufferListChain; + streamData.dataLength = totalDataLength; + streamData.flags = streamFlags; + + streamData.dataOffset.netBufferList = netBufferListChain; + streamData.dataOffset.netBuffer = + NET_BUFFER_LIST_FIRST_NB(streamData.dataOffset.netBufferList); + streamData.dataOffset.mdl = + NET_BUFFER_CURRENT_MDL(streamData.dataOffset.netBuffer); + streamData.dataOffset.mdlOffset = + NET_BUFFER_CURRENT_MDL_OFFSET(streamData.dataOffset.netBuffer); + + if (StreamCopyDataForInspection( + streamEditor, + &streamData + ) == FALSE) + { + status = STATUS_NO_MEMORY; + } + } + + return status; +} + +NTSTATUS +StreamOobEditData( + _Inout_ STREAM_EDITOR* streamEditor, + _Inout_ NET_BUFFER_LIST* netBufferListChain, + size_t totalDataLength, + DWORD streamFlags + ) +/* ++ + + This function first copies the stream data into a flat inspection buffer; + it then parses the buffer looking for the matching pattern. For + non-matching sections it re-injects the data back; for a match it skips + over and injects an replacement section. + + If a match can not be determined due to lack of data, it injects the + non-matching section back and moves the potential match to the beginning + of the inspection buffer. + + When an EOF is reached, it flushes all processed stream sections back + and re-injects the FIN back to end the stream. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + UINT i = 0; + BOOLEAN streamModified = FALSE; + BOOLEAN potentialMatch = FALSE; + + BYTE* dataStart; + + UINT findLength = (UINT) strlen(configStringToFind); + + status = StreamOobCopyDataToFlatBuffer( + streamEditor, + netBufferListChain, + totalDataLength, + streamFlags + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + dataStart = (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset; + + for (; i < streamEditor->dataLength; ++i) + { + if (i + findLength <= streamEditor->dataLength) + { + if (RtlCompareMemory( + dataStart + i, + configStringToFind, + findLength + ) == findLength) + { + if (i != 0) + { + status = StreamOobReinjectData( + streamEditor, + streamFlags, + dataStart, + i + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + streamEditor->dataOffset += i; + streamEditor->dataLength -= i; + + i = 0; + } + + status = StreamOobInjectReplacement( + streamEditor, + streamFlags, + gStringToReplaceMdl, + strlen(configStringToReplace) + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + streamEditor->dataOffset += findLength; + streamEditor->dataLength -= findLength; + + streamModified = TRUE; + + if (streamEditor->dataLength > 0) + { + dataStart = (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset; + + --i; + + continue; + } + else + { + streamEditor->dataOffset = 0; + } + } + } + else + { + if (streamEditor->oobEditInfo.noMoreData) + { + break; + } + + if (RtlCompareMemory( + dataStart + i, + configStringToFind, + streamEditor->dataLength - i + ) == streamEditor->dataLength - i) + { + potentialMatch = TRUE; // this is a partial find + + status = StreamOobReinjectData( + streamEditor, + streamFlags, + dataStart, + i + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + RtlMoveMemory( + (BYTE*)streamEditor->scratchBuffer, + dataStart + i, + streamEditor->dataLength - i + ); + + streamEditor->dataOffset = 0; + streamEditor->dataLength = streamEditor->dataLength - i; + + break; + } + } + } + + if (streamModified && streamEditor->dataLength > 0) + { + status = StreamOobReinjectData( + streamEditor, + streamFlags, + dataStart, + streamEditor->dataLength + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + streamEditor->dataOffset = 0; + streamEditor->dataLength = 0; + } + + if (!streamModified && !potentialMatch) + { + if (totalDataLength > 0) + { + NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && + !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); + + status = StreamOobQueueUpOutgoingData( + streamEditor, + netBufferListChain, + TRUE, + totalDataLength, + streamFlags, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + netBufferListChain = NULL; + } + else if (streamEditor->dataLength > 0) + { + status = StreamOobReinjectData( + streamEditor, + streamFlags, + dataStart, + streamEditor->dataLength + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + } + + streamEditor->dataOffset = 0; + streamEditor->dataLength = 0; + } + + if (streamEditor->oobEditInfo.nblEof != NULL) + { + status = StreamOobFlushOutgoingData(streamEditor); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = FwpsStreamInjectAsync( + gInjectionHandle, + NULL, + 0, + streamEditor->oobEditInfo.flowId, + streamEditor->oobEditInfo.calloutId, + streamEditor->oobEditInfo.layerId, + streamFlags | (configInspectionOutbound ? FWPS_STREAM_FLAG_SEND_DISCONNECT : + FWPS_STREAM_FLAG_RECEIVE_DISCONNECT), + streamEditor->oobEditInfo.nblEof, + 0, + StreamOobInjectCompletionFn, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + streamEditor->oobEditInfo.nblEof = NULL; + streamEditor->oobEditInfo.noMoreData = FALSE; + } + +Exit: + + if (netBufferListChain != NULL) + { + FwpsDiscardClonedStreamData( + netBufferListChain, + 0, + FALSE + ); + } + + if (streamEditor->oobEditInfo.nblEof != NULL) + { + FwpsDiscardClonedStreamData( + streamEditor->oobEditInfo.nblEof, + 0, + FALSE + ); + + streamEditor->oobEditInfo.nblEof = NULL; + } + + return status; +} + +_IRQL_requires_same_ +_Function_class_(KSTART_ROUTINE) +void +StreamOobEditWorker( + _In_ void* StartContext + ) +/* ++ + + This function waits for an event which gets signalled when there is data + waiting to be inspected. + + Once awaken, the worker thread edits the stream until all stream data is + processed (and then it waits for more work again). + + When requested to shutdown, it will finish the editing task and enters + "shutdown" state. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + NET_BUFFER_LIST* netBufferListChain = NULL; + size_t totalDataLength; + STREAM_EDITOR* streamEditor = (STREAM_EDITOR*)StartContext; + DWORD streamFlags; + + for(;;) + { + KLOCK_QUEUE_HANDLE editLockHandle; + + KeWaitForSingleObject( + &streamEditor->oobEditInfo.editEvent, + Executive, + KernelMode, + FALSE, + NULL + ); + + if (streamEditor->oobEditInfo.editState == OOB_EDIT_ERROR || + streamEditor->oobEditInfo.editState == OOB_EDIT_SHUT_DOWN) + { + break; + } + + KeAcquireInStackQueuedSpinLock( + &streamEditor->oobEditInfo.editLock, + &editLockHandle + ); + + NT_ASSERT(streamEditor->oobEditInfo.editState == OOB_EDIT_PROCESSING || + streamEditor->oobEditInfo.editState == OOB_EDIT_BUSY); + + netBufferListChain = streamEditor->oobEditInfo.nblHead; + totalDataLength = streamEditor->oobEditInfo.totalDataLength; + streamFlags = streamEditor->oobEditInfo.streamFlags; + + streamEditor->oobEditInfo.nblHead = NULL; + streamEditor->oobEditInfo.nblTail = NULL; + streamEditor->oobEditInfo.totalDataLength = 0; + + KeReleaseInStackQueuedSpinLock(&editLockHandle); + + _Analysis_assume_(netBufferListChain != NULL); + status = StreamOobEditData( + streamEditor, + netBufferListChain, + totalDataLength, + streamFlags + ); + + if (!NT_SUCCESS(status)) + { + streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; + break; + } + + if (streamEditor->oobEditInfo.editState == OOB_EDIT_BUSY) + { + NTSTATUS streamContinueStatus; + streamEditor->oobEditInfo.editState = OOB_EDIT_PROCESSING; + + streamContinueStatus = FwpsStreamContinue( + streamEditor->oobEditInfo.flowId, + streamEditor->oobEditInfo.calloutId, + streamEditor->oobEditInfo.layerId, + streamEditor->oobEditInfo.streamFlags + ); + + if (!NT_SUCCESS(streamContinueStatus)) + { + streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; + break; + } + } + + KeAcquireInStackQueuedSpinLock( + &streamEditor->oobEditInfo.editLock, + &editLockHandle + ); + + if (streamEditor->oobEditInfo.nblHead == NULL) + { + if (!streamEditor->oobEditInfo.shuttingDown) + { + streamEditor->oobEditInfo.editState = OOB_EDIT_IDLE; + KeClearEvent(&streamEditor->oobEditInfo.editEvent); + } + else + { + streamEditor->oobEditInfo.editState = OOB_EDIT_SHUT_DOWN; + } + } + + KeReleaseInStackQueuedSpinLock(&editLockHandle); + } + + PsTerminateSystemThread(status); +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +NTAPI +StreamOobEditClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_ void* layerData, + _In_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#else + +void +NTAPI +StreamOobEditClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +/* ++ + + This is the ClassifyFn function registered by the OOB stream edit callout. + + An OOB stream modification callout blocks all indicated data after cloning + them for processing by a kernel mode worker thread (or marshalling the data + to user mode for inspection); the resultant/edited data will then be put + back to the stream via the stream injection API. + + For such a callout, the processed data must be (re-)injected back to the + stream from within the ClassifyFn. + +-- */ +{ + FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket; + FWPS_STREAM_DATA* streamData; + + UINT findLength = (UINT) strlen(configStringToFind); + + ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)layerData; + NT_ASSERT(ioPacket != NULL); + + streamData = ioPacket->streamData; + NT_ASSERT(streamData != NULL); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(classifyContext); +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(flowContext); + + RtlZeroMemory(classifyOut, sizeof(FWPS_CLASSIFY_OUT)); + + // + // Let go the traffic that the editor does not care about. + // + + if ((configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_RECEIVE)) || + (!configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_SEND))) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + goto Exit; + } + + // + // In this sample we don't edit TCP urgent data + // + + if ((streamData->flags & FWPS_STREAM_FLAG_SEND_EXPEDITED) || + (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_EXPEDITED)) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + classifyOut->actionType = FWP_ACTION_PERMIT; + + if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) + { + classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; + } + + goto Exit; + } + + if ((streamData->dataLength < findLength) && + !(classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA; + ioPacket->countBytesRequired = findLength; + + classifyOut->actionType = FWP_ACTION_NONE; + goto Exit; + } + + StreamOobEdit( + &gStreamEditor, + inFixedValues, + inMetaValues, + filter, + streamData, + ioPacket, + classifyOut + ); + + if (classifyOut->actionType == FWP_ACTION_BLOCK || + classifyOut->actionType == FWP_ACTION_PERMIT) + { + if (!(streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && + !(streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)) + { + NTSTATUS status = StreamOobFlushOutgoingData( &gStreamEditor); + if (!NT_SUCCESS(status)) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + classifyOut->actionType = FWP_ACTION_NONE; + } + } + } + +Exit: + + return; +} diff --git a/network/trans/stmedit/sys/oob_edit.h b/network/trans/stmedit/sys/oob_edit.h new file mode 100644 index 00000000..e5a0e506 --- /dev/null +++ b/network/trans/stmedit/sys/oob_edit.h @@ -0,0 +1,83 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Stream Edit Callout Driver Sample. + + This sample demonstrates Out-of-band (OOB) stream inspection/editing + via the WFP stream API. + +Environment: + + Kernel mode + +--*/ + +#ifndef _OOB_EDIT_H +#define _OOB_EDIT_H + +typedef enum OOB_EDIT_STATE_ +{ + OOB_EDIT_IDLE, + OOB_EDIT_PROCESSING, + OOB_EDIT_BUSY, + OOB_EDIT_SHUT_DOWN, + OOB_EDIT_ERROR +} OOB_EDIT_STATE; + +typedef struct OUTGOING_STREAM_DATA_ +{ + LIST_ENTRY listEntry; + + NET_BUFFER_LIST* netBufferList; + BOOLEAN isClone; + size_t dataLength; + DWORD streamFlags; + MDL* mdl; +} OUTGOING_STREAM_DATA; + +typedef struct STREAM_EDITOR_ STREAM_EDITOR; + +NTSTATUS +OobEditInit( + _Out_ STREAM_EDITOR* + ); + +void +OobEditShutdown( + _Out_ STREAM_EDITOR* streamEditor + ); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +void +NTAPI +StreamOobEditClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_ void* layerData, + _In_ const void* classifyContext, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +#else + +void +NTAPI +StreamOobEditClassify( + _In_ const FWPS_INCOMING_VALUES* inFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, + _Inout_ void* layerData, + _In_ const FWPS_FILTER* filter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* classifyOut + ); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + +#endif // _OOB_EDIT_H diff --git a/network/trans/stmedit/sys/stmedit.inf b/network/trans/stmedit/sys/stmedit.inf new file mode 100644 index 00000000..1e19c787 --- /dev/null +++ b/network/trans/stmedit/sys/stmedit.inf @@ -0,0 +1,66 @@ +;;; +;;; Copyright (c) Microsoft Corporation. All rights reserved +;;; +;;; Abstract: +;;; Stream Edit Callout sample driver install configuration. +;;; + +[Version] + Signature = "$Windows NT$" + Class = WFPCALLOUTS + ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} + Provider = %Contoso% + CatalogFile = StmEdit.cat + DriverVer = 11/24/2014,14.24.55.836 + +[SourceDisksNames] + 1 = %StmEditDisk%,,,"" + +[SourceDisksFiles] + StmEdit.sys = 1,, + +[DestinationDirs] + DefaultDestDir = 12 ; %WinDir%\System32\Drivers + StmEdit.DriverFiles = 12 ; %WinDir%\System32\Drivers + +[DefaultInstall] + OptionDesc = %StmEditServiceDesc% + CopyFiles = StmEdit.DriverFiles + +[DefaultInstall.Services] + AddService = %StmEditServiceName%,,StmEdit.Service + +[DefaultUninstall] + DelFiles = StmEdit.DriverFiles + +[DefaultUninstall.Services] + DelService = %StmEditServiceName%,0x200 ; SPSVCINST_STOPSERVICE + DelReg = StmEdit.DelRegistry + +[StmEdit.DriverFiles] + StmEdit.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY + +[StmEdit.Service] + DisplayName = %StmEditServiceName% + Description = %StmEditServiceDesc% + ServiceType = 1 ; SERVICE_KERNEL_DRIVER + StartType = 3 ; SERVICE_DEMAND_START + ErrorControl = 1 ; SERVICE_ERROR_NORMAL + ServiceBinary = %12%\StmEdit.sys ; %WinDir%\System32\Drivers\StmEdit.sys + AddReg = StmEdit.AddRegistry + +[StmEdit.AddRegistry] + HKR,"Parameters","EditInline",0x00010001,0 ; FLG_ADDREG_TYPE_DWORD + HKR,"Parameters","StringToFind",0x00000000,"rainy" ; FLG_ADDREG_TYPE_SZ + HKR,"Parameters","StringToReplace",0x00000000,"sunny" ; FLG_ADDREG_TYPE_SZ + HKR,"Parameters","InspectionPort",0x00010001,5001 ; FLG_ADDREG_TYPE_DWORD + HKR,"Parameters","InspectOutbound",0x00010001,0 ; FLG_ADDREG_TYPE_DWORD + +[StmEdit.DelRegistry] + HKR,"Parameters",,, + +[Strings] + Contoso = "Contoso Ltd." + StmEditDisk = "Stream Edit Installation Disk" + StmEditServiceDesc = "Stream Edit Callout Driver" + StmEditServiceName = "StmEdit" \ No newline at end of file diff --git a/network/trans/stmedit/sys/stmedit.vcxproj b/network/trans/stmedit/sys/stmedit.vcxproj new file mode 100644 index 00000000..bc3ef00b --- /dev/null +++ b/network/trans/stmedit/sys/stmedit.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {98513F1C-C28D-4F03-99A8-88FD4BD707C7} + $(MSBuildProjectName) + 1 + Debug + Win32 + {72C54687-6A7C-47CA-9E90-E2D689DC07FF} + + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + stmedit + + + stmedit + + + stmedit + + + stmedit + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + %(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/trans/stmedit/sys/stmedit.vcxproj.Filters b/network/trans/stmedit/sys/stmedit.vcxproj.Filters new file mode 100644 index 00000000..54528237 --- /dev/null +++ b/network/trans/stmedit/sys/stmedit.vcxproj.Filters @@ -0,0 +1,32 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C9744BA5-8ED9-44CA-83D6-EFDEDA84812B} + + + h;hpp;hxx;hm;inl;inc;xsd + {2B1C683F-EF24-453C-B303-9CAE8061F7C3} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {FF447D8A-41FE-4980-B0AE-27782414E0C0} + + + inf;inv;inx;mof;mc; + {A061D6F2-D701-4D44-9CAB-055FBD8E95A9} + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/network/trans/stmedit/sys/stream_callout.c b/network/trans/stmedit/sys/stream_callout.c new file mode 100644 index 00000000..7816c46d --- /dev/null +++ b/network/trans/stmedit/sys/stream_callout.c @@ -0,0 +1,772 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Stream Edit Callout Driver Sample. + + This sample demonstrates finding and replacing a string pattern from a + live TCP stream via the WFP stream API. + + The driver can function in one of the two modes -- + + o Inline Editing where all modification is carried out within the + WFP ClassifyFn callout function. + + o Out-of-band (OOB) Editing where all modification is done by a + worker thread. (this is the default) + + The mode setting, along with other inspection parameters are configurable + via the following registry values + + HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\stmedit\Parameters + + o StringToFind (REG_SZ, default = "rainy") + o StringToReplace (REG_SZ, default = "sunny") + o InspectionPort (REG_DWORD, default = 5001) + o InspectOutbound (REG_DWORD, default = 0) + o EditInline (REG_DWORD, default = 0) + + The sample is IP version agnostic. It performs inspection on both IPv4 and + IPv6 data streams. + + Before experimenting with the sample, please be sure to add an exception for + the InspectionPort configured to the firewall. + +Environment: + + Kernel mode + +--*/ + +#include +#include + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +#include + +#pragma warning(pop) + +#include + +#include "inline_edit.h" +#include "oob_edit.h" +#include "stream_callout.h" + +#define INITGUID +#include + +// +// Configurable parameters +// + +USHORT configInspectionPort = 5001; +BOOLEAN configInspectionOutbound = FALSE; +BOOLEAN configEditInline = FALSE; + +CHAR configStringToFind[128] = "rainy"; +CHAR configStringToReplace[128] = "sunny"; + +// +// Callout driver keys +// + +// e6011cdc-440b-4a6f-8499-6fdb55fb1f92 +DEFINE_GUID( + STREAM_EDITOR_STREAM_CALLOUT_V4, + 0xe6011cdc, + 0x440b, + 0x4a6f, + 0x84, 0x99, 0x6f, 0xdb, 0x55, 0xfb, 0x1f, 0x92 +); +// c0bc07b4-aaf6-4242-a3dc-3ef341ffde5d +DEFINE_GUID( + STREAM_EDITOR_STREAM_CALLOUT_V6, + 0xc0bc07b4, + 0xaaf6, + 0x4242, + 0xa3, 0xdc, 0x3e, 0xf3, 0x41, 0xff, 0xde, 0x5d +); + +// +// Callout driver global variables +// + +MDL* gStringToReplaceMdl; + +STREAM_EDITOR gStreamEditor; + +HANDLE gEngineHandle; +UINT32 gCalloutIdV4; +UINT32 gCalloutIdV6; + +DEVICE_OBJECT* gWdmDevice; + +HANDLE gInjectionHandle; + +NDIS_GENERIC_OBJECT* gNdisGenericObj; +NDIS_HANDLE gNetBufferListPool; + +#define STREAM_EDITOR_NDIS_OBJ_TAG 'oneS' +#define STREAM_EDITOR_NBL_POOL_TAG 'pneS' +#define STREAM_EDITOR_FLAT_BUFFER_TAG 'bfeS' + + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_UNLOAD StreamEditEvtDriverUnload; + +NTSTATUS +StreamEditNotify( + FWPS_CALLOUT_NOTIFY_TYPE notifyType, + const GUID* filterKey, + const FWPS_FILTER* filter + ) +{ + UNREFERENCED_PARAMETER(notifyType); + UNREFERENCED_PARAMETER(filterKey); + UNREFERENCED_PARAMETER(filter); + + return STATUS_SUCCESS; +} + +NTSTATUS +RegisterCalloutForLayer( + const GUID* layerKey, + const GUID* calloutKey, + _Inout_ void* deviceObject, + _Out_ UINT32* calloutId + ) +/* ++ + + This function registers callouts and filters that intercept TCP + traffic at WFP FWPM_LAYER_STREAM_V4 or FWPM_LAYER_STREAM_V6 layer. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + FWPS_CALLOUT sCallout = {0}; + + FWPM_FILTER filter = {0}; + FWPM_FILTER_CONDITION filterConditions[1] = {0}; + + FWPM_CALLOUT mCallout = {0}; + FWPM_DISPLAY_DATA displayData = {0}; + + BOOLEAN calloutRegistered = FALSE; + + sCallout.calloutKey = *calloutKey; + sCallout.classifyFn = (configEditInline ? StreamInlineEditClassify : + StreamOobEditClassify); + sCallout.notifyFn = StreamEditNotify; + + status = FwpsCalloutRegister( + deviceObject, + &sCallout, + calloutId + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + calloutRegistered = TRUE; + + displayData.name = L"Stream Edit Callout"; + displayData.description = L"Callout that finds and replaces a token from a TCP stream"; + + mCallout.calloutKey = *calloutKey; + mCallout.displayData = displayData; + mCallout.applicableLayer = *layerKey; + status = FwpmCalloutAdd( + gEngineHandle, + &mCallout, + NULL, + NULL + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + filter.layerKey = *layerKey; + filter.displayData.name = L"Stream Edit Filter"; + filter.displayData.description = L"Filter that finds and replaces a token from a TCP stream"; + + filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; + filter.action.calloutKey = *calloutKey; + filter.filterCondition = filterConditions; + filter.numFilterConditions = 1; + filter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL; + filter.weight.type = FWP_EMPTY; // auto-weight. + + filterConditions[0].fieldKey = (configInspectionOutbound ? FWPM_CONDITION_IP_REMOTE_PORT : + FWPM_CONDITION_IP_LOCAL_PORT); + filterConditions[0].matchType = FWP_MATCH_EQUAL; + filterConditions[0].conditionValue.type = FWP_UINT16; + filterConditions[0].conditionValue.uint16 = configInspectionPort; + + status = FwpmFilterAdd( + gEngineHandle, + &filter, + NULL, + NULL); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + +Exit: + + if (!NT_SUCCESS(status)) + { + if (calloutRegistered) + { + FwpsCalloutUnregisterById(*calloutId); + } + } + + return status; +} + +NTSTATUS +StreamEditRegisterCallout( + const STREAM_EDITOR* streamEditor, + _Inout_ void* deviceObject + ) +/* ++ + + This function registers dynamic callouts and filters that intercept + TCP traffic at WFP FWPM_LAYER_STREAM_V4 and FWPM_LAYER_STREAM_V6 + layer. + + Callouts and filters will be removed during DriverUnload. + +-- */ +{ + NTSTATUS status = STATUS_SUCCESS; + + BOOLEAN engineOpened = FALSE; + BOOLEAN inTransaction = FALSE; + + FWPM_SESSION session = {0}; + + UNREFERENCED_PARAMETER(streamEditor); + + session.flags = FWPM_SESSION_FLAG_DYNAMIC; + + status = FwpmEngineOpen( + NULL, + RPC_C_AUTHN_WINNT, + NULL, + &session, + &gEngineHandle + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + engineOpened = TRUE; + + status = FwpmTransactionBegin(gEngineHandle, 0); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + inTransaction = TRUE; + + status = RegisterCalloutForLayer( + &FWPM_LAYER_STREAM_V4, + &STREAM_EDITOR_STREAM_CALLOUT_V4, + deviceObject, + &gCalloutIdV4 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = RegisterCalloutForLayer( + &FWPM_LAYER_STREAM_V6, + &STREAM_EDITOR_STREAM_CALLOUT_V6, + deviceObject, + &gCalloutIdV6 + ); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = FwpmTransactionCommit(gEngineHandle); + if (!NT_SUCCESS(status)) + { + goto Exit; + } + inTransaction = FALSE; + +Exit: + + if (!NT_SUCCESS(status)) + { + if (inTransaction) + { + NTSTATUS abortStatus; + abortStatus = FwpmTransactionAbort(gEngineHandle); + _Analysis_assume_(NT_SUCCESS(abortStatus)); + } + if (engineOpened) + { + FwpmEngineClose(gEngineHandle); + gEngineHandle = NULL; + } + } + + return status; +} + +void +StreamEditUnregisterCallout(void) +{ + FwpmEngineClose(gEngineHandle); + gEngineHandle = NULL; + + FwpsCalloutUnregisterById(gCalloutIdV6); + FwpsCalloutUnregisterById(gCalloutIdV4); +} + +_Function_class_(EVT_WDF_DRIVER_UNLOAD) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +void +StreamEditEvtDriverUnload( + _In_ WDFDRIVER driverObject + ) +{ + + UNREFERENCED_PARAMETER(driverObject); + + if (!configEditInline) + { + OobEditShutdown(&gStreamEditor); + } + + if (gStreamEditor.scratchBuffer != NULL) + { + ExFreePoolWithTag( + gStreamEditor.scratchBuffer, + STREAM_EDITOR_FLAT_BUFFER_TAG + ); + + } + + StreamEditUnregisterCallout(); + + FwpsInjectionHandleDestroy(gInjectionHandle); + + NdisFreeNetBufferListPool(gNetBufferListPool); + NdisFreeGenericObject(gNdisGenericObj); + + IoFreeMdl(gStringToReplaceMdl); +} + +NTSTATUS +StreamEditLoadConfig( + const WDFKEY key + ) +{ + NTSTATUS status = STATUS_SUCCESS; + DECLARE_CONST_UNICODE_STRING(stringToFindKey, L"StringToFind"); + DECLARE_CONST_UNICODE_STRING(stringToReplaceKey, L"StringToReplace"); + DECLARE_CONST_UNICODE_STRING(inspectionPortKey, L"InspectionPort"); + DECLARE_CONST_UNICODE_STRING(editInlineKey, L"EditInline"); + DECLARE_CONST_UNICODE_STRING(inspectOutboundKey, L"InspectOutbound"); + + UNICODE_STRING stringValue; + WCHAR buffer[128]; + USHORT requiredSize; + ULONG valueSize; + ULONG ulongValue; + + stringValue.Buffer = buffer; + stringValue.Length = 0; + stringValue.MaximumLength = sizeof(buffer) - sizeof(buffer[0]); + + if (NT_SUCCESS( + WdfRegistryQueryUnicodeString( + key, + &stringToFindKey, + &requiredSize, + &stringValue + ))) + { + stringValue.Buffer[stringValue.Length/sizeof(stringValue.Buffer[0])] = + UNICODE_NULL; + + status = RtlUnicodeToMultiByteN( + configStringToFind, + sizeof(configStringToFind) - 1, + &valueSize, + stringValue.Buffer, + (ULONG)requiredSize + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + configStringToFind[valueSize] = '\0'; + } + + if (NT_SUCCESS( + WdfRegistryQueryUnicodeString( + key, + &stringToReplaceKey, + &requiredSize, + &stringValue + ))) + { + status = RtlUnicodeToMultiByteN( + configStringToReplace, + sizeof(configStringToReplace) - 1, + &valueSize, + stringValue.Buffer, + (ULONG)requiredSize + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + configStringToReplace[valueSize] = '\0'; + } + + if (NT_SUCCESS( + WdfRegistryQueryULong( + key, + &inspectionPortKey, + &ulongValue + ))) + { + configInspectionPort = (USHORT) ulongValue; + } + + if (NT_SUCCESS( + WdfRegistryQueryULong( + key, + &editInlineKey, + &ulongValue + ))) + { + configEditInline = (ulongValue != 0); + } + + if (NT_SUCCESS( + WdfRegistryQueryULong( + key, + &inspectOutboundKey, + &ulongValue + ))) + { + configInspectionOutbound = (ulongValue != 0); + } + +Exit: + return status; +} + +NTSTATUS +StreamEditInitDriverObjects( + _Inout_ DRIVER_OBJECT* driverObject, + const UNICODE_STRING* registryPath, + _Out_ WDFDRIVER* pDriver, + _Out_ WDFDEVICE* pDevice + ) +{ + NTSTATUS status; + WDF_DRIVER_CONFIG config; + PWDFDEVICE_INIT pInit = NULL; + + WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); + + config.DriverInitFlags |= WdfDriverInitNonPnpDriver; + config.EvtDriverUnload = StreamEditEvtDriverUnload; + + status = WdfDriverCreate( + driverObject, + registryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + pDriver + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + pInit = WdfControlDeviceInitAllocate(*pDriver, &SDDL_DEVOBJ_KERNEL_ONLY); + + if (!pInit) + { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + WdfDeviceInitSetCharacteristics(pInit, FILE_AUTOGENERATED_DEVICE_NAME, TRUE); + WdfDeviceInitSetDeviceType(pInit, FILE_DEVICE_NETWORK); + WdfDeviceInitSetCharacteristics(pInit, FILE_DEVICE_SECURE_OPEN, TRUE); + status = WdfDeviceCreate(&pInit, WDF_NO_OBJECT_ATTRIBUTES, pDevice); + + if (!NT_SUCCESS(status)) + { + WdfDeviceInitFree(pInit); + goto Exit; + } + + WdfControlFinishInitializing(*pDevice); + +Exit: + return status; +} + +NTSTATUS +DriverEntry( + DRIVER_OBJECT* driverObject, + UNICODE_STRING* registryPath + ) +{ + NTSTATUS status; + WDFDEVICE device; + WDFDRIVER driver; + WDFKEY configKey; + NET_BUFFER_LIST_POOL_PARAMETERS nblPoolParams = {0}; + + // Request NX Non-Paged Pool when available + ExInitializeDriverRuntime(DrvRtPoolNxOptIn); + + status = StreamEditInitDriverObjects( + driverObject, + registryPath, + &driver, + &device + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = WdfDriverOpenParametersRegistryKey( + driver, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &configKey + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + status = StreamEditLoadConfig(configKey); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + gStringToReplaceMdl = IoAllocateMdl( + configStringToReplace, + (ULONG) strlen(configStringToReplace), + FALSE, + FALSE, + NULL + ); + if (gStringToReplaceMdl == NULL) + { + status = STATUS_NO_MEMORY; + goto Exit; + } + + MmBuildMdlForNonPagedPool(gStringToReplaceMdl); + + gNdisGenericObj = NdisAllocateGenericObject( + driverObject, + STREAM_EDITOR_NDIS_OBJ_TAG, + 0 + ); + + if (gNdisGenericObj == NULL) + { + status = STATUS_NO_MEMORY; + goto Exit; + } + + nblPoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + nblPoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + nblPoolParams.Header.Size = sizeof(nblPoolParams); + + nblPoolParams.fAllocateNetBuffer = TRUE; + nblPoolParams.DataSize = 0; + + nblPoolParams.PoolTag = STREAM_EDITOR_NBL_POOL_TAG; + + gNetBufferListPool = NdisAllocateNetBufferListPool( + gNdisGenericObj, + &nblPoolParams + ); + + if (gNetBufferListPool == NULL) + { + status = STATUS_NO_MEMORY; + goto Exit; + } + + status = FwpsInjectionHandleCreate( + AF_UNSPEC, + FWPS_INJECTION_TYPE_STREAM, + &gInjectionHandle + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + gWdmDevice = WdfDeviceWdmGetDeviceObject(device); + + status = StreamEditRegisterCallout( + &gStreamEditor, + gWdmDevice + ); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + + if (configEditInline) + { + InlineEditInit(&gStreamEditor); + } + else + { + + status = OobEditInit(&gStreamEditor); + + if (!NT_SUCCESS(status)) + { + goto Exit; + } + } + +Exit: + + if (!NT_SUCCESS(status)) + { + if (gEngineHandle != NULL) + { + StreamEditUnregisterCallout(); + } + if (gInjectionHandle != NULL) + { + FwpsInjectionHandleDestroy(gInjectionHandle); + } + if (gNetBufferListPool != NULL) + { + NdisFreeNetBufferListPool(gNetBufferListPool); + } + if (gNdisGenericObj != NULL) + { + NdisFreeGenericObject(gNdisGenericObj); + } + if (gStringToReplaceMdl != NULL) + { + IoFreeMdl(gStringToReplaceMdl); + } + } + + return status; +} + +BOOLEAN +StreamCopyDataForInspection( + _Inout_ STREAM_EDITOR* streamEditor, + const FWPS_STREAM_DATA* streamData + ) +/* ++ + + This function copies stream data described by the FWPS_STREAM_DATA + structure into a flat buffer. + +-- */ + +{ + SIZE_T bytesCopied; + + size_t existingDataLength = streamEditor->dataLength; + NT_ASSERT(streamEditor->dataOffset == 0); + if (streamEditor->bufferSize - existingDataLength < streamData->dataLength) + { + size_t newBufferSize = (streamData->dataLength + existingDataLength) * 2; + void* newBuffer = ExAllocatePoolWithTag( + NonPagedPool, + newBufferSize, + STREAM_EDITOR_FLAT_BUFFER_TAG + ); + + if (newBuffer != NULL) + { + if (existingDataLength > 0) + { + NT_ASSERT(streamEditor->scratchBuffer != NULL); + RtlCopyMemory( + newBuffer, + streamEditor->scratchBuffer, + existingDataLength + ); + } + } + + if (streamEditor->scratchBuffer != NULL) + { + ExFreePoolWithTag( + streamEditor->scratchBuffer, + STREAM_EDITOR_FLAT_BUFFER_TAG + ); + + streamEditor->scratchBuffer = NULL; + streamEditor->bufferSize = 0; + streamEditor->dataLength = 0; + } + + if (newBuffer != NULL) + { + streamEditor->scratchBuffer = newBuffer; + streamEditor->bufferSize = newBufferSize; + streamEditor->dataLength = existingDataLength; + } + else + { + return FALSE; + } + } + + FwpsCopyStreamDataToBuffer( + streamData, + (BYTE*)streamEditor->scratchBuffer + streamEditor->dataLength, + streamData->dataLength, + &bytesCopied + ); + + NT_ASSERT(bytesCopied == streamData->dataLength); + + streamEditor->dataLength += bytesCopied; + + return TRUE; +} diff --git a/network/trans/stmedit/sys/stream_callout.h b/network/trans/stmedit/sys/stream_callout.h new file mode 100644 index 00000000..7fff047f --- /dev/null +++ b/network/trans/stmedit/sys/stream_callout.h @@ -0,0 +1,79 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + + Stream Edit Callout Driver Sample. + + This sample demonstrates finding and replacing a string pattern from a + live TCP stream via the WFP stream API. + +--*/ + +#ifndef _STREAM_CALLOUT_H +#define _STREAM_CALLOUT_H + +extern MDL* gStringToReplaceMdl; +extern HANDLE gInjectionHandle; +extern NDIS_HANDLE gNetBufferListPool; +extern STREAM_EDITOR gStreamEditor; + +// +// Configurable parameters +// + +extern USHORT configInspectionPort; +extern BOOLEAN configInspectionOutbound; +extern BOOLEAN configEditInline; + +extern CHAR configStringToFind[]; +extern CHAR configStringToReplace[]; + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union + +typedef struct STREAM_EDITOR_ +{ + BOOLEAN editInline; + + union + { + INLINE_EDIT_STATE inlineEditState; + struct + { + OOB_EDIT_STATE editState; + BOOLEAN shuttingDown; + + KSPIN_LOCK editLock; + NET_BUFFER_LIST* nblHead; + NET_BUFFER_LIST* nblTail; + size_t totalDataLength; + BOOLEAN noMoreData; + NET_BUFFER_LIST* nblEof; + size_t busyThreshold; + UINT64 flowId; + UINT32 calloutId; + UINT16 layerId; + DWORD streamFlags; + KEVENT editEvent; + LIST_ENTRY outgoingDataQueue; + } oobEditInfo; + }; + + void* scratchBuffer; + size_t bufferSize; + size_t dataOffset; + size_t dataLength; + +}STREAM_EDITOR; + +#pragma warning(pop) + +BOOLEAN +StreamCopyDataForInspection( + _Inout_ STREAM_EDITOR* streamEditor, + const FWPS_STREAM_DATA* streamData + ); + +#endif // _STREAM_CALLOUT_H diff --git a/network/wlan/ReadMe.md b/network/wlan/ReadMe.md new file mode 100644 index 00000000..a90b2a5b --- /dev/null +++ b/network/wlan/ReadMe.md @@ -0,0 +1,91 @@ +Native Wifi IHV Service +======================= + +This sample code demonstrates IHV extensibility for Native WiFi. + +In particular, this sample contains the following features: + +- IHV profile validation +- IHV discovery profile creation +- IHV extension for interactive UI and Profile UI +- 802.1x extension + +The sample, after you compile and install it, enables you to connect to an Open WEP Network by using 802.1X through IHV Service extension. + +Run the sample +-------------- + +The fully compiled sample consists of two DLLs: IHVSample.dll and IHVSampleUI.dll. The functions of those DLLs are outlined below: + +### IHVSample.dll + +The IHVSample DLL supports connecting to a wireless network by using Open authentication and WEP encryption. The sample is capable of connecting to an 802.1X network and a non-802.1X network. + +During the discovery phase, the sample generates a temporary profile that the operating system uses to establish a wireless connection. The operating system requests that the IHV provide a temporary profile to use when trying to connect through Discovery. In this case, IHVSample returns a list of usable profiles for connecting by using open-WEP with and without 802.1X. The RC4 algorithm is implemented in a DLL that is provided as part of the sample. + +The sample is loaded by the IHV process and uses the public interfaces that are provided by the same process. The IHV process host initializes IHVSample in its process. + +The sample gets called to perform pre-associate security and post-associate security. The sample does not implement any of the pre-associate security. For the post-associate security, in the case of open-WEP without 802.1X, IHVSample prompts for UI in case the profile does not exist or does not already have a valid key. In the case of 802.1X networks, the post-associate security portion sets the driver packet exemptions, starts up the Microsoft 802.1X module authentication, and waits for the result that indicates a success or a failure. During this period, IHVSample forwards all 802.1X packets to the IHV process. However, it caches the EAPOL key packets. In either case, after the key is obtained, IHVSample sends it to the driver and establishes the connection. + +The operating system validates network profiles before they can be applied and persisted. The operating system performs validation of the non-IHV portion of the profile and passes the rest of data to IHVSample if IHV settings exist. IHVSample does limited XML schema validation. + +### UI Sample (IHVSampleUI.dll) + +The IHVSampleUI DLL extends the Wireless Profile UI to display IHV connectivity and security information. The security information that is displayed is for both security types based on IHV proprietary security and those based on Microsoft 802.1X. The sample adds custom IHV authentication and encryption types to illustrate the different ways the various types can be embedded in the UI. The configuration UI saves the IHV portion of the profile in accordance with the IHV schema. The sample pages that enable modification of the IHV parameters are displayed from the configuration UI. The sample also displays a wizard-based connection time UI with three sample pages. This connection time UI integrates in both the wizard and non-wizard flows. + +Installation +------------ + +After the sample is compiled, you must copy the binaries on to the target system and associate them with the matching Native Wifi-capable adapter. You can copy the binaries by adding an appropriate **CopyFiles** directive in the **DDInstall** section in the INF file for installing the adapter. You can associate the binaries by adding an appropriate **AddReg** directive in the **DDInstall** section in the INF file for installing the adapter. + +### CopyFiles Directive + +The CopyFiles Directive should name a File-List-Section. The contents of this section should have the following: + +IHVSpecifiedDLLName,,,2 + +IHVSpecifiedOtherFile,,,2 + +There should be an associated entry in the DestinationDirs section that specifies the destination to copy the file to. This section should have a directive like one of the following: + +File-List-Section= 11 ; \\system32 directory + +DefaultDestDir= 11 ; \\system32 directory + +### AddReg Directive + +The AddReg directive should name an Add-Registry-Section. + +The contents of the Miniport INF file must include the following, in order for the correct IHV Service to be started: + +- HKR,Ndi\\IHVExtensions, ExtensibilityDLL,0,"%SystemRoot%\\system32\\IhvExt.dll" + + This registry key is used to determine the location of the IHVSample.dll. + +- HKR,Ndi\\IHVExtensions,UIExtensibilityCLSID,0, "\" + + This registry key is used to determine the class ID of the COM interface that extends the 802.11 configuration UI. + +- HKR,Ndi\\IHVExtensions,GroupName,0, "IHV provided group name" +- HKR,Ndi\\IHVExtensions, AdapterOUI, 0x00010001, 0x00123456 + + This registry key is used to verify the OUI when the profile is applied to the adapter. If the AdapterOUI value is 0x??123456 in the registry, it needs to look like the following in the profile: + + \ + + \123456\ + + \??\ + + \ + + Note that ?? stands for bits ignored. + +- HKR,Ndi\\IHVExtensions, DiagnosticsID,0, "\" + +### Uninstallation Instructions + +To uninstall this sample, you must undo the AddReg directive and undo the CopyFiles directive. + +For more information about creating a Native Wi-Fi package, see [Native 802.11 Wireless LAN](http://msdn.microsoft.com/en-us/library/windows/hardware/ff560690). + diff --git a/network/wlan/ihvsample/adapters.cpp b/network/wlan/ihvsample/adapters.cpp new file mode 100644 index 00000000..919e47a6 --- /dev/null +++ b/network/wlan/ihvsample/adapters.cpp @@ -0,0 +1,439 @@ +/*++ + +Copyright (c) 2005 Microsoft Corporation + +Abstract: + + Sample IHV Extensibility DLL to extend + 802.11 LWF driver for third party protocols. + + +--*/ + +#include "precomp.h" + +LIST_ENTRY g_AdaptersList = {0}; + + + +// +// Copied Macros from wdm.h +// + +#define CONTAINING_RECORD(address, type, field) ((type *)( \ + (PCHAR)(address) - \ + (ULONG_PTR)(&((type *)0)->field))) + + +#define InitializeListHead(ListHead) (\ + (ListHead)->Flink = (ListHead)->Blink = (ListHead)) + + + +#define RemoveEntryList(Entry) {\ + PLIST_ENTRY _EX_Blink;\ + PLIST_ENTRY _EX_Flink;\ + _EX_Flink = (Entry)->Flink;\ + _EX_Blink = (Entry)->Blink;\ + _EX_Blink->Flink = _EX_Flink;\ + _EX_Flink->Blink = _EX_Blink;\ + } + + +#define InsertTailList(ListHead,Entry) {\ + PLIST_ENTRY _EX_Blink;\ + PLIST_ENTRY _EX_ListHead;\ + _EX_ListHead = (ListHead);\ + _EX_Blink = _EX_ListHead->Blink;\ + (Entry)->Flink = _EX_ListHead;\ + (Entry)->Blink = _EX_Blink;\ + _EX_Blink->Flink = (Entry);\ + _EX_ListHead->Blink = (Entry);\ + } + + + + +// +// Initialize the AdapterList data structure. +// +VOID +InitAdapterDetailsList +( + VOID +) +{ + EnterCriticalSection( &g_csSynch ); + + // Assuming that memory in g_AdaptersList + // could be garbage - just initialize the + // fields appropriately. + + InitializeListHead( &g_AdaptersList ); + + LeaveCriticalSection( &g_csSynch ); + + return; +} + + + + +// +// Free the AdapterList data structure - contention is not supported. +// +VOID +DeinitAdapterDetailsList +( + VOID +) +{ + EnterCriticalSection( &g_csSynch ); + + // ASSERT that the list is empty. The call + // to InitializeListHead is not really required + // if the following conditions are true. + ASSERT(g_AdaptersList.Flink == &g_AdaptersList ); + ASSERT(g_AdaptersList.Blink == &g_AdaptersList ); + + // clear the memory any way. + InitializeListHead( &g_AdaptersList ); + + LeaveCriticalSection( &g_csSynch ); + + return; +} + + + + + +// +// Add a single adapter to the list. Starting refcount is one. +// +DWORD +InitAdapterDetails +( + HANDLE hDot11SvcHandle, + PHANDLE phIhvExtAdapter +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + PADAPTER_DETAILS pAdapterDetails = NULL; + HANDLE hIhvExtAdapter = NULL; + + + // acquire global lock. + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // verify state. this check prevents new adapters from + // being added when service is being deinited. + if (!g_bAllowInit) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // allocate memory. + pAdapterDetails = (PADAPTER_DETAILS) PrivateMemoryAlloc( sizeof( ADAPTER_DETAILS ) ); + if ( !pAdapterDetails ) + { + dwResult = ERROR_OUTOFMEMORY; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // add the new object to the list. if anything fails + // in this function the object would be automatically + // dereferenced and removed from the list. + InsertTailList( &g_AdaptersList, &(pAdapterDetails->Link) ); + + // initialize fields that are used by object lifetime management. + hIhvExtAdapter = (HANDLE) &(pAdapterDetails->Link); + pAdapterDetails->dwRefCount = 1; + pAdapterDetails->hDot11SvcHandle = hDot11SvcHandle; + pAdapterDetails->NicState = nic_state_initialized; + + + // Event that gets triggered + // once UI response is received. + pAdapterDetails->hUIResponse = + CreateEvent + ( + NULL, + FALSE, // Auto Reset + FALSE, // Start in non-signaled state. + NULL + ); + if ( !(pAdapterDetails->hUIResponse) ) + { + dwResult = GetLastError( ); + BAIL_ON_WIN32_ERROR( dwResult ); + } + + + // fill out-params. release responsibility to + // for deiniting object to caller. + (*phIhvExtAdapter) = hIhvExtAdapter; + hIhvExtAdapter = NULL; + +error: + if ( hIhvExtAdapter ) + { + // something failed after adding the object to + // the global list. so it needs to be removed. + DerefenceAdapterDetails( hIhvExtAdapter ); + } + if ( bLocked ) + { + // leave global lock. + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + + + +// +// Add a reference to a particular adapter and find the pointer to the context. +// +DWORD +ReferenceAdapterDetails +( + HANDLE hIhvExtAdapter, + PADAPTER_DETAILS* ppAdapterDetails +) +{ + DWORD dwResult = ERROR_NOT_FOUND; + BOOL bLocked = FALSE; + PLIST_ENTRY pEntry = NULL; + PADAPTER_DETAILS pAdapterDetails = NULL; + + ASSERT( ppAdapterDetails ); + + // acquire global lock. + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + + // search the global list of adapters for a match. + for + ( + pEntry = g_AdaptersList.Flink; + pEntry != &g_AdaptersList; + pEntry = pEntry->Flink + ) + { + // Get the adapter data structure for current entry. + pAdapterDetails = CONTAINING_RECORD( pEntry, ADAPTER_DETAILS, Link ); + ASSERT( pAdapterDetails ); + + // conditions could be converted to an ASSERT. + if + ( + ( pAdapterDetails->NicState == nic_state_uninitialized ) || + ( pAdapterDetails->NicState >= nic_state_max ) + ) + { + ASSERTFAILURE(); + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // if match found - bail with success. + if ( hIhvExtAdapter == ((HANDLE) pEntry) ) + { + (pAdapterDetails->dwRefCount)++; + (*ppAdapterDetails) = pAdapterDetails; + + dwResult = ERROR_SUCCESS; + BAIL( ); + } + } + +error: + if ( bLocked ) + { + // release global lock. + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + + +// +// Find adapter context pointer using UI request GUID. +// +DWORD +ReferenceAdapterDetailsByUIRequestGuid +( + GUID* pguidUIRequest, + PADAPTER_DETAILS* ppAdapterDetails, + HANDLE* phIhvExtAdapter +) +{ + DWORD dwResult = ERROR_NOT_FOUND; + BOOL bLocked = FALSE; + PLIST_ENTRY pEntry = NULL; + PADAPTER_DETAILS pAdapterDetails = NULL; + + ASSERT( ppAdapterDetails ); + ASSERT( phIhvExtAdapter ); + + // acquire global lock. + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // look for adapter with matching ui request guid in the global list. + for + ( + pEntry = g_AdaptersList.Flink; + pEntry != &g_AdaptersList; + pEntry = pEntry->Flink + ) + { + // get the current adapter's context info. + pAdapterDetails = CONTAINING_RECORD( pEntry, ADAPTER_DETAILS, Link ); + ASSERT( pAdapterDetails ); + + // could be an ASSERT. + if + ( + ( pAdapterDetails->NicState == nic_state_uninitialized ) || + ( pAdapterDetails->NicState >= nic_state_max ) + ) + { + ASSERTFAILURE(); + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // if match found, bail with success. + if ( pAdapterDetails->currentGuidUIRequest == (*pguidUIRequest) ) + { + (pAdapterDetails->dwRefCount)++; + (*ppAdapterDetails) = pAdapterDetails; + (*phIhvExtAdapter) = (HANDLE) pEntry; + + dwResult = ERROR_SUCCESS; + BAIL( ); + } + } + +error: + if ( bLocked ) + { + // leave global lock. + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + + + +// +// Dereference an adapter - resources will be freed when the refcount +// goes to zero. +// +VOID +DerefenceAdapterDetails +( + HANDLE hIhvExtAdapter +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + PADAPTER_DETAILS pAdapterDetails = NULL; + BOOL bOk = TRUE; + + + // acquire global lock. + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // trying to reference the adapter. the part of the + // code that increments the refcount is not useful + // here, we are only using the algorithm to find + // the context pointer. the refcount would be decremented + // after the function call if the call succeeds. + dwResult = + ReferenceAdapterDetails + ( + hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + ASSERT( pAdapterDetails ); + + // Undoing the refcount increment in the previous call. + (pAdapterDetails->dwRefCount)--; + + // could be an ASSERT - the adapter + // context should not be in this state. + if ( 0 == pAdapterDetails->dwRefCount ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // performing the intended dereferencing. + (pAdapterDetails->dwRefCount)--; + + // checking if the adapter entry can go. + if ( pAdapterDetails->dwRefCount ) + { + BAIL( ); + } + + // Since this was the last reference, + // it is time to deinitialize the memory. + + // remove the adapter from the global list. + RemoveEntryList( &(pAdapterDetails->Link) ); + + // Close handle to the UI response event. + if ( pAdapterDetails->hUIResponse ) + { + bOk = CloseHandle( pAdapterDetails->hUIResponse ); + ASSERT( bOk ); + } + + // free the UI response memory. + if ( pAdapterDetails->pbResponse ) + { + PrivateMemoryFree( pAdapterDetails->pbResponse ); + pAdapterDetails->pbResponse = NULL; + } + + // frees the connection specific data in the adapter data structure. + FreeOnexData( &(pAdapterDetails->pOnexData) ); + + // frees the profiles. + FreeIhvConnectivityProfile( &(pAdapterDetails->pConnectivityProfile) ); + FreeIhvSecurityProfile( &(pAdapterDetails->pSecurityProfile) ); + + // frees the context. + SecureZeroMemory( pAdapterDetails, sizeof( ADAPTER_DETAILS ) ); + PrivateMemoryFree( pAdapterDetails ); + +error: + if ( bLocked ) + { + // release global lock. + LeaveCriticalSection( &g_csSynch ); + } + ASSERT( ERROR_SUCCESS == dwResult ); + return; +} + diff --git a/network/wlan/ihvsample/adapters.h b/network/wlan/ihvsample/adapters.h new file mode 100644 index 00000000..ad0f9779 --- /dev/null +++ b/network/wlan/ihvsample/adapters.h @@ -0,0 +1,125 @@ + + + +// +// Different states of current NIC. +// +typedef +enum _NIC_STATE +{ + nic_state_uninitialized, + nic_state_initialized, + nic_state_pre_assoc_started, + nic_state_pre_assoc_ended, + nic_state_post_assoc_started, + nic_state_onex_in_progress, + nic_state_post_assoc_ended, + + nic_state_max // should be the last one. +} +NIC_STATE, *PNIC_STATE; + + +////////////////////////////////// +// Adapter lifetime management // +////////////////////////////////// + +// +// Adapter Data Structure +// + +struct _ADAPTER_DETAILS +{ + // Adapter list and lifetime management. + LIST_ENTRY Link; + DWORD dwRefCount; + NIC_STATE NicState; + + + // Framework reference. + HANDLE hDot11SvcHandle; + + // Handler functions for different stages of connection. + LPTHREAD_START_ROUTINE pPerformPostAssociateCompletionRoutine; + POST_ASSOCIATE_FUNCTION pPerformPostAssociateRoutine; + STOP_POST_ASSOCIATE_FUNCTION pStopPostAssociateRoutine; + + + // Data for current connection. + HANDLE hConnectSession; + BOOL bModifyCurrentProfile; + PONEX_DATA pOnexData; + GUID currentGuidUIRequest; + DWORD dwResponseLen; + _Field_size_bytes_(dwResponseLen) BYTE* pbResponse; + HANDLE hUIResponse; + PIHV_CONNECTIVITY_PROFILE pConnectivityProfile; + PIHV_SECURITY_PROFILE pSecurityProfile; +}; + + + +// +// Initialize the AdapterList data structure. +// +VOID +InitAdapterDetailsList +( + VOID +); + + +// +// Free the AdapterList data structure - contention is not supported. +// +VOID +DeinitAdapterDetailsList +( + VOID +); + +// +// Add a single adapter to the list. Starting refcount is one. +// +DWORD +InitAdapterDetails +( + HANDLE hDot11SvcHandle, + PHANDLE phIhvExtAdapter +); + + +// +// Add a reference to a particular adapter and find the pointer to the context. +// +DWORD +ReferenceAdapterDetails +( + HANDLE hIhvExtAdapter, + PADAPTER_DETAILS* ppAdapterDetails +); + + +// +// Find adapter context pointer using UI request GUID. +// +DWORD +ReferenceAdapterDetailsByUIRequestGuid +( + GUID* pguidUIRequest, + PADAPTER_DETAILS* ppAdapterDetails, + HANDLE* phIhvExtAdapter +); + + + +// +// Dereference an adapter - resources will be freed when the refcount +// goes to zero. +// +VOID +DerefenceAdapterDetails +( + HANDLE hIhvExtAdapter +); + diff --git a/network/wlan/ihvsample/ihv1xext.xml b/network/wlan/ihvsample/ihv1xext.xml new file mode 100644 index 00000000..77ae471f --- /dev/null +++ b/network/wlan/ihvsample/ihv1xext.xml @@ -0,0 +1,52 @@ + + + ihv1xext + + + _1x_SSID_ + + false + + ESS + manual + false + + + + + + + 25 + 0 + 0 + 0 + + + + + + + + + + 123456 + 01 + + + + 0 + + + + + + FALSE + IHVAuthV1 + IHVCipher1 + 0 + + + + true + + diff --git a/network/wlan/ihvsample/ihvconn.xml b/network/wlan/ihvsample/ihvconn.xml new file mode 100644 index 00000000..0bb09694 --- /dev/null +++ b/network/wlan/ihvsample/ihvconn.xml @@ -0,0 +1,48 @@ + + + + ihvconn + + + + _SSID_ + + + + ESS + manual + + + + + + + + open + WEP + + + + + + + + + 123456 + 01 + + + + + + 0 + + wrongkey + + + + + + + + diff --git a/network/wlan/ihvsample/ihvmachine.xml b/network/wlan/ihvsample/ihvmachine.xml new file mode 100644 index 00000000..04b7ba21 --- /dev/null +++ b/network/wlan/ihvsample/ihvmachine.xml @@ -0,0 +1,62 @@ + + + ihvmachine + + + _1x_SSID_ + + false + + ESS + manual + false + + + + open + WEP + true + + + 3 + + + machine + + + + + 25 + 0 + 0 + 0 + + + + + + + + + + 123456 + 01 + + + + 0 + + + + + + FALSE + IHVAuthV1 + IHVCipher1 + 0 + + + + true + + diff --git a/network/wlan/ihvsample/ihvonexext.cpp b/network/wlan/ihvsample/ihvonexext.cpp new file mode 100644 index 00000000..8e80ab8a --- /dev/null +++ b/network/wlan/ihvsample/ihvonexext.cpp @@ -0,0 +1,1113 @@ + +/*++ + +Copyright (c) 2005 Microsoft Corporation + +Abstract: + + Sample IHV Extensibility DLL to extend + 802.11 LWF driver for third party protocols. + + +--*/ + +#include "precomp.h" + + + + + +#define MAX_BACKLOG 32 +#define MAX_EXEMPTIONS 1 +#define MAX_REGISTRATIONS 2 +#define ETHTYPE_EAPOL 0x888e +#define EapolTypeEapolKey 0x03 + + +DWORD +PlumbWEPKey +( + HANDLE hDot11SvcHandle, + ULONG uKeyIndex, + DOT11_DIRECTION direction, + LPBYTE pbKey, + ULONG uKeyLen +); + + +DWORD +ProcessRC4Key +( + HANDLE hIhvExtAdapter, + PDOT11_MSONEX_RESULT_PARAMS pOneXResultParams, + HANDLE hDot11SvcHandle, + HANDLE hSecuritySessionID, + ULONG uPktLen, + PBYTE pbEapolPkt +); + + + +// initialize onex data structure. +DWORD +GetNewOnexData +( + PONEX_DATA* ppOnexData +) +{ + DWORD dwResult = ERROR_SUCCESS; + PONEX_DATA pOnexData = NULL; + + ASSERT( ppOnexData ); + + if (*ppOnexData) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // allocate memory. + pOnexData = (PONEX_DATA) PrivateMemoryAlloc( sizeof( ONEX_DATA ) ); + if (!pOnexData) + { + dwResult = ERROR_OUTOFMEMORY; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // init fields here. + pOnexData->lpfnReceivePacket = Do1xReceivePacket; + pOnexData->lpfnIndicateResult = Do1xIndicateResult; + + // transfer data to caller. + (*ppOnexData) = pOnexData; + pOnexData = NULL; + +error: + if ( pOnexData ) + { + FreeOnexData( &pOnexData ); + } + return dwResult; +} + + + + +// free onex data structure. +VOID +FreeOnexData +( + PONEX_DATA* ppOnexData +) +{ + PONEX_DATA pOnexData = NULL; + + if ( ppOnexData && (*ppOnexData) ) + { + // Freeing caller's variable. + pOnexData = (*ppOnexData); + (*ppOnexData) = NULL; + + RC4UtilsFreeResultParams( &(pOnexData->pOnexResultParams) ); + ZeroMemory( pOnexData , sizeof( ONEX_DATA ) ); + PrivateMemoryFree( pOnexData ); + } +} + + +// verify if result params are available and valid. +BOOL +IsOneXResultParamsAvailable +( + PDOT11_MSONEX_RESULT_PARAMS pOneXResultParams +) +{ + BOOL bResult = FALSE; + + + bResult = + ( + pOneXResultParams && + pOneXResultParams->pbMPPERecvKey && + pOneXResultParams->dwMPPERecvKeyLen && + pOneXResultParams->pbMPPESendKey && + pOneXResultParams->dwMPPESendKeyLen + ); + + TRACE_MESSAGE_VAL( "Onex Result Params Available = ", bResult ); + + return bResult; +} + + +// free rc4 packet. +VOID +FreeRC4Pkt +( + PCACHED_PKT pPkt +) +{ + if ( pPkt ) + { + PrivateMemoryFree( pPkt->pbPkt ); + pPkt->pbPkt = NULL; + pPkt->uPktLen = 0; + } +} + +// flush rc4 packet cache. +VOID +FlushRC4PktCache +( + PONEX_DATA pOnexData +) +{ + ULONG i = 0; + + ASSERT( pOnexData ); + + for (i = 0; i< RC4_CACHE_SIZE; i++) + { + FreeRC4Pkt( &(pOnexData->RC4Cache[i]) ); + } +} + + +// cache rc4 packet +DWORD +CacheRC4Pkt +( + PONEX_DATA pOnexData, + ULONG uPktLen, + PBYTE pbEapolPkt +) +{ + DWORD dwResult = ERROR_SUCCESS; + PBYTE pbPktCopy = NULL; + + if ( !(pOnexData && uPktLen && pbEapolPkt) ) + { + dwResult = ERROR_INVALID_PARAMETER; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // copy the packet. + pbPktCopy = (PBYTE) PrivateMemoryAlloc(uPktLen); + if (!pbPktCopy) + { + dwResult = ERROR_OUTOFMEMORY; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + CopyMemory + ( + pbPktCopy, + pbEapolPkt, + uPktLen + ); + + // free packet destination. + FreeRC4Pkt( &(pOnexData->RC4Cache[pOnexData->uCacheFreeIdx]) ); + + // move packet copy to cache. + pOnexData->RC4Cache[ pOnexData->uCacheFreeIdx ].pbPkt = pbPktCopy; + pOnexData->RC4Cache[ pOnexData->uCacheFreeIdx ].uPktLen = uPktLen; + + + TRACE_MESSAGE_VAL( "RC4 Packet cached, Index = ", pOnexData->uCacheFreeIdx ); + + // increment index. + pOnexData->uCacheFreeIdx = (pOnexData->uCacheFreeIdx + 1 ) % RC4_CACHE_SIZE; + +error: + return dwResult; +} + +// process cached rc4 packets. +DWORD +ProcessCachedRC4Packets +( + PADAPTER_DETAILS pAdapterDetails +) +{ + DWORD dwResult = ERROR_SUCCESS; + ULONG ulIndex = 0; + ULONG uProcessIndex = 0; + PONEX_DATA pOnexData = NULL; + + ASSERT( pAdapterDetails ); + ASSERT( pAdapterDetails->pOnexData ); + + pOnexData = pAdapterDetails->pOnexData; + + // If no 1x result available, bail + if (!IsOneXResultParamsAvailable( pOnexData->pOnexResultParams )) + { + dwResult = ERROR_SUCCESS; + BAIL( ); + } + + TRACE_MESSAGE( "Processing cached RC4 packets." ); + + // We process last 2 received frames + + for ( ulIndex = 1; ulIndex <= RC4_CACHE_SIZE; ulIndex++ ) + { + uProcessIndex = ( pOnexData->uCacheFreeIdx - ulIndex) % RC4_CACHE_SIZE; + + if + ( + ( pOnexData->RC4Cache[uProcessIndex].pbPkt ) && + ( pOnexData->RC4Cache[uProcessIndex].uPktLen ) + ) + { + dwResult = + ProcessRC4Key + ( + (HANDLE) &(pAdapterDetails->Link), + pOnexData->pOnexResultParams, + pAdapterDetails->hDot11SvcHandle, + pOnexData->hSecuritySessionID, + pOnexData->RC4Cache[uProcessIndex].uPktLen, + pOnexData->RC4Cache[uProcessIndex].pbPkt + ); + BAIL_ON_WIN32_ERROR(dwResult); + + FreeRC4Pkt( &(pOnexData->RC4Cache[uProcessIndex]) ); + + } + } + +error: + return dwResult; +} + + + + + +// pre-associate function for onex profile. +DWORD +WINAPI +Do1xPreAssociate +( + PADAPTER_DETAILS pAdapterDetails, + DWORD* pdwReasonCode +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + ULONG uNumExemptions = 0; + ULONG uNumRegistrations = 0; + DOT11_PRIVACY_EXEMPTION PrivacyExemption[MAX_EXEMPTIONS] = {0}; + USHORT usRegistration[MAX_REGISTRATIONS] = {0}; + + ASSERT( pAdapterDetails ); + ASSERT( pdwReasonCode ); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // Reason code is set before making calls that could fail. + (*pdwReasonCode) = L2_REASON_CODE_IHV_INVALID_STATE; + + if ( nic_state_pre_assoc_started != pAdapterDetails->NicState ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // Reason code is set before making calls that could fail. + (*pdwReasonCode) = L2_REASON_CODE_IHV_OUTOFMEMORY; + + dwResult = + GetNewOnexData + ( + &(pAdapterDetails->pOnexData) + ); + BAIL_ON_WIN32_ERROR(dwResult); + + + // Reason code is set before making calls that could fail. + (*pdwReasonCode) = L2_REASON_CODE_IHV_HARDWARE_FAILURE; + + + TRACE_MESSAGE( "Setting Auth Algorithm." ); + dwResult = + (g_pDot11ExtApi->Dot11ExtSetAuthAlgorithm) + ( + pAdapterDetails->hDot11SvcHandle, + DOT11_AUTH_ALGO_80211_OPEN + ); + BAIL_ON_WIN32_ERROR(dwResult); + + TRACE_MESSAGE( "Setting Unicast cipher algorithm." ); + dwResult = + (g_pDot11ExtApi->Dot11ExtSetUnicastCipherAlgorithm) + ( + pAdapterDetails->hDot11SvcHandle, + DOT11_CIPHER_ALGO_WEP + ); + BAIL_ON_WIN32_ERROR(dwResult); + + TRACE_MESSAGE( "Setting Multicast cipher algorithm." ); + dwResult = + (g_pDot11ExtApi->Dot11ExtSetMulticastCipherAlgorithm) + ( + pAdapterDetails->hDot11SvcHandle, + DOT11_CIPHER_ALGO_WEP + ); + BAIL_ON_WIN32_ERROR(dwResult); + + TRACE_MESSAGE( "Setting exclude unencrypted flag." ); + dwResult = + (g_pDot11ExtApi->Dot11ExtSetExcludeUnencrypted) + ( + pAdapterDetails->hDot11SvcHandle, + TRUE + ); + BAIL_ON_WIN32_ERROR(dwResult); + + // set the exemption handler + + // In vanilla 1x, 802.1x packets are never encrypted + PrivacyExemption[uNumExemptions].usExemptionActionType = DOT11_EXEMPT_ALWAYS; + PrivacyExemption[uNumExemptions].usEtherType = htons(ETHTYPE_EAPOL); + PrivacyExemption[uNumExemptions].usExemptionPacketType = DOT11_EXEMPT_UNICAST; + uNumExemptions++; + ASSERT(uNumExemptions <= MAX_EXEMPTIONS); + + usRegistration[uNumRegistrations] = htons(ETHTYPE_EAPOL); + uNumRegistrations++; + ASSERT(uNumRegistrations <= MAX_REGISTRATIONS); + + TRACE_MESSAGE( "Setting ethertype handling." ); + dwResult = + g_pDot11ExtApi->Dot11ExtSetEtherTypeHandling + ( + pAdapterDetails->hDot11SvcHandle, + MAX_BACKLOG, + uNumExemptions, + PrivacyExemption, + uNumRegistrations, + usRegistration + ); + BAIL_ON_WIN32_ERROR(dwResult); + + + // Verified before, just after acquiring lock. + ASSERT( nic_state_pre_assoc_started == pAdapterDetails->NicState ); + + pAdapterDetails->NicState = nic_state_pre_assoc_ended; + + // Reason code is set to SUCCESS. + (*pdwReasonCode) = L2_REASON_CODE_SUCCESS; + + // populate post associate handler functions. + pAdapterDetails->pPerformPostAssociateCompletionRoutine = NULL; + pAdapterDetails->pPerformPostAssociateRoutine = Do1xPostAssociate; + pAdapterDetails->pStopPostAssociateRoutine = Do1xStopPostAssociate; + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + +// post-associate function for onex profile. +DWORD +WINAPI +Do1xPostAssociate +( + IN PADAPTER_DETAILS pAdapterDetails, + IN HANDLE hSecuritySessionID, + IN PDOT11_PORT_STATE pPortState, + IN ULONG uDot11AssocParamsBytes, + IN PDOT11_ASSOCIATION_COMPLETION_PARAMETERS pDot11AssocParams +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + + ASSERT( pAdapterDetails ); + + UNREFERENCED_PARAMETER( pPortState ); + UNREFERENCED_PARAMETER( uDot11AssocParamsBytes ); + UNREFERENCED_PARAMETER( pDot11AssocParams ); + + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // verify and change state. + if + ( + ( nic_state_post_assoc_started != pAdapterDetails->NicState && + nic_state_post_assoc_ended != pAdapterDetails->NicState + ) || + ( !(pAdapterDetails->pOnexData) ) + ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + pAdapterDetails->NicState = nic_state_onex_in_progress; + + // no need to free old id handle. + pAdapterDetails->pOnexData->hSecuritySessionID = hSecuritySessionID; + + TRACE_MESSAGE( "Starting OneX." ); + dwResult = + (g_pDot11ExtApi->Dot11ExtStartOneX) + ( + pAdapterDetails->hDot11SvcHandle, + NULL // EAP attributes + ); + BAIL_ON_WIN32_ERROR( dwResult ); + pAdapterDetails->pOnexData->fMSOneXStarted = TRUE; + + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + + +// receive rc4 key. +DWORD +ReceiveRC4Key +( + PADAPTER_DETAILS pAdapterDetails, + ULONG uPktLen, + PEAPOL_PACKET pEapolPkt +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + + ASSERT( pAdapterDetails ); + ASSERT( uPktLen ); + ASSERT( pEapolPkt ); + + EnterCriticalSection(&g_csSynch); + bLocked = TRUE; + + ASSERT( pAdapterDetails->pOnexData ); + + TRACE_MESSAGE( "Received RC4 Key packet." ); + + if ( IsOneXResultParamsAvailable( pAdapterDetails->pOnexData->pOnexResultParams ) ) + { + // 1x params are available + dwResult = + ProcessRC4Key + ( + (HANDLE) &(pAdapterDetails->Link), + pAdapterDetails->pOnexData->pOnexResultParams, + pAdapterDetails->hDot11SvcHandle, + pAdapterDetails->pOnexData->hSecuritySessionID, + uPktLen, + (PBYTE)pEapolPkt + ); + BAIL_ON_WIN32_ERROR(dwResult); + } + else + { + dwResult = + CacheRC4Pkt + ( + pAdapterDetails->pOnexData, + uPktLen, + (PBYTE) pEapolPkt + ); + BAIL_ON_WIN32_ERROR(dwResult); + } + +error: + if (bLocked) + { + LeaveCriticalSection(&g_csSynch); + } + return dwResult; +} + + + + +// receive packet function for onex profile. +DWORD +WINAPI +Do1xReceivePacket +( + PADAPTER_DETAILS pAdapterDetails, + DWORD dwInBufferSize, + LPVOID pvInBuffer +) +{ + DWORD dwResult = ERROR_SUCCESS; + HANDLE hDot11SvcHandle = NULL; + BOOL bLocked = FALSE; + PDOT11_SECURITY_PACKET_HEADER pSecurityPkt = NULL; + PEAPOL_PACKET pEapolPkt = NULL; + DWORD uReqdPktLen = 0; + + ASSERT( pAdapterDetails ); + + // Must include at least one byte of data + uReqdPktLen = FIELD_OFFSET(DOT11_SECURITY_PACKET_HEADER, Data) + 1; + + if ( dwInBufferSize < uReqdPktLen ) + { + dwResult = ERROR_INVALID_PARAMETER; + BAIL_ON_WIN32_ERROR(dwResult); + } + + if ( !pvInBuffer ) + { + dwResult = ERROR_INVALID_PARAMETER; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + pSecurityPkt = (PDOT11_SECURITY_PACKET_HEADER) pvInBuffer; + pEapolPkt = (PEAPOL_PACKET) pSecurityPkt->Data; + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // validate state. + if + ( + (!(pAdapterDetails->pOnexData)) || + ( + ( nic_state_onex_in_progress != pAdapterDetails->NicState ) && + ( nic_state_post_assoc_ended != pAdapterDetails->NicState ) + ) + ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + hDot11SvcHandle = pAdapterDetails->hDot11SvcHandle; + + TRACE_MESSAGE( "Received security packet." ); + + // Ignoring version + if ( EapolTypeEapolKey == pEapolPkt->PacketType ) + { + dwResult = + ReceiveRC4Key + ( + pAdapterDetails, + dwInBufferSize - FIELD_OFFSET(DOT11_SECURITY_PACKET_HEADER, Data), + pEapolPkt + ); + BAIL_ON_WIN32_ERROR(dwResult); + } + else + { + // leave lock before sending security packet to AC. + LeaveCriticalSection( &g_csSynch ); + bLocked = FALSE; + + dwResult = + (g_pDot11ExtApi->Dot11ExtProcessSecurityPacket) + ( + hDot11SvcHandle, + dwInBufferSize - FIELD_OFFSET(DOT11_SECURITY_PACKET_HEADER, Data), + pSecurityPkt->Data + ); + BAIL_ON_WIN32_ERROR(dwResult); + } + + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + +// parameters for calling AC with post assoc completion. +typedef +struct _POST_ASSOC_COMPL_DATA +{ + HANDLE hIhvExtAdapter; + HANDLE hDot11SvcHandle; + HANDLE hSecuritySessionID; + DOT11_MAC_ADDRESS PeerMacAddress; + DWORD dwSecurityReasonCode; + DWORD dwSecurityWin32Error; +} +POST_ASSOC_COMPL_DATA, *PPOST_ASSOC_COMPL_DATA; + + +// function to make the actual post assoc completion call. +// this function is started in a separate thread. +DWORD +WINAPI +PostAssocComplWorker +( + LPVOID pvCtxt +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + PPOST_ASSOC_COMPL_DATA pCtxt = (PPOST_ASSOC_COMPL_DATA) pvCtxt; + PADAPTER_DETAILS pAdapterDetails = NULL; + + ASSERT( pCtxt ); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + dwResult = + ReferenceAdapterDetails + ( + pCtxt->hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + if ( nic_state_onex_in_progress != pAdapterDetails->NicState && + nic_state_post_assoc_ended != pAdapterDetails->NicState ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + pAdapterDetails->NicState = nic_state_post_assoc_ended; + + LeaveCriticalSection( &g_csSynch ); + bLocked = FALSE; + + TRACE_MESSAGE_VAL( "Calling PostAssocCompletion, Status = ", pCtxt->dwSecurityWin32Error ); + + dwResult = + (g_pDot11ExtApi->Dot11ExtPostAssociateCompletion) + ( + pCtxt->hDot11SvcHandle, + pCtxt->hSecuritySessionID, + &(pCtxt->PeerMacAddress), + pCtxt->dwSecurityReasonCode, + pCtxt->dwSecurityWin32Error + ); + BAIL_ON_WIN32_ERROR( dwResult ); + +error: + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( pCtxt->hIhvExtAdapter ); + } + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + PrivateMemoryFree( pvCtxt ); + return dwResult; +} + + +// starts a worker thread for the post association completion. +DWORD +Do1xPostAssocCompletion +( + HANDLE hIhvExtAdapter, + HANDLE hDot11SvcHandle, + HANDLE hSecuritySessionID, + PDOT11_MAC_ADDRESS pPeer, + DWORD dwSecurityReasonCode, + DWORD dwSecurityWin32Error +) +{ + DWORD dwResult = ERROR_SUCCESS; + PPOST_ASSOC_COMPL_DATA pCtxt = NULL; + + // allocate memory and copy parameters. + pCtxt = (PPOST_ASSOC_COMPL_DATA) PrivateMemoryAlloc(sizeof(POST_ASSOC_COMPL_DATA)); + if (!pCtxt) + { + dwResult = ERROR_OUTOFMEMORY; + BAIL_ON_WIN32_ERROR(dwResult); + } + + pCtxt->hIhvExtAdapter = hIhvExtAdapter; + pCtxt->hDot11SvcHandle = hDot11SvcHandle; + pCtxt->hSecuritySessionID = hSecuritySessionID; + + if (pPeer) + { + CopyMemory + ( + &(pCtxt->PeerMacAddress), + pPeer, + sizeof(DOT11_MAC_ADDRESS) + ); + } + + pCtxt->dwSecurityReasonCode = dwSecurityReasonCode; + pCtxt->dwSecurityWin32Error = dwSecurityWin32Error; + + // start the new thread. + dwResult = + StartNewProtectedThread + ( + hIhvExtAdapter, + PostAssocComplWorker, + pCtxt + ); + BAIL_ON_WIN32_ERROR(dwResult); + + pCtxt = NULL; + +error: + PrivateMemoryFree( pCtxt ); + return dwResult; +} + + +// indicate result function for onex profile. +DWORD +WINAPI +Do1xIndicateResult +( + PADAPTER_DETAILS pAdapterDetails, + DOT11_MSONEX_RESULT msOneXResult, + PDOT11_MSONEX_RESULT_PARAMS pDot11MsOneXResultParams +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + ASSERT( pAdapterDetails ); + ASSERT( pAdapterDetails->pOnexData ); + + // validate state. + if ( nic_state_onex_in_progress != pAdapterDetails->NicState && + nic_state_post_assoc_ended != pAdapterDetails->NicState + ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + if ( msOneXResult == DOT11_MSONEX_IN_PROGRESS ) + { + // free the onex params if they exist + RC4UtilsFreeResultParams( &( pAdapterDetails->pOnexData->pOnexResultParams) ); + FlushRC4PktCache( pAdapterDetails->pOnexData ); + TRACE_MESSAGE( "Received DOT11_MSONEX_IN_PROGRESS." ); + } + else if ( msOneXResult == DOT11_MSONEX_FAILURE ) + { + TRACE_MESSAGE( "Received DOT11_MSONEX_FAILURE." ); + + // If failure, indicate right away + dwResult = + Do1xPostAssocCompletion + ( + (HANDLE) &(pAdapterDetails->Link), + pAdapterDetails->hDot11SvcHandle, + pAdapterDetails->pOnexData->hSecuritySessionID, + NULL, + (pDot11MsOneXResultParams && + pDot11MsOneXResultParams->Dot11OneXReasonCode != ONEX_REASON_CODE_SUCCESS)? + pDot11MsOneXResultParams->Dot11OneXReasonCode: + L2_REASON_CODE_IHV_ONEX_FAILURE, + msOneXResult + ); + BAIL_ON_WIN32_ERROR(dwResult); + } + else if ( msOneXResult == DOT11_MSONEX_SUCCESS ) + { + // success case. + TRACE_MESSAGE( "Received DOT11_MSONEX_SUCCESS." ); + + // free result params and get new result params. + RC4UtilsFreeResultParams( &(pAdapterDetails->pOnexData->pOnexResultParams) ); + + dwResult = + RC4UtilsDecryptResultParams + ( + pDot11MsOneXResultParams, + &(pAdapterDetails->pOnexData->pOnexResultParams) + ); + BAIL_ON_WIN32_ERROR(dwResult); + } + + // Process cached RC4 packets if any + dwResult = + ProcessCachedRC4Packets + ( + pAdapterDetails + ); + BAIL_ON_WIN32_ERROR(dwResult); + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + +// stop-post-associate function for onex profile. +DWORD +WINAPI +Do1xStopPostAssociate +( + PADAPTER_DETAILS pAdapterDetails, + PDOT11_MAC_ADDRESS pPeer, + DOT11_ASSOC_STATUS dot11AssocStatus +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + + UNREFERENCED_PARAMETER( pPeer ); + UNREFERENCED_PARAMETER( dot11AssocStatus ); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + TRACE_MESSAGE( "Performing StopPostAssociate." ); + + if + ( + ( nic_state_post_assoc_ended != pAdapterDetails->NicState ) && + ( nic_state_onex_in_progress != pAdapterDetails->NicState ) && + ( nic_state_post_assoc_started != pAdapterDetails->NicState ) + ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // Reset the port specific variables. + + if ( pAdapterDetails->pOnexData ) + { + if(pAdapterDetails->pOnexData->fMSOneXStarted) + { + // NOTE THAT THE OS WILL STOP THE AUTH EVEN IF THIS CALL IS NOT MADE. + // The sample should keep track of port downs and call start auth + // once a port up is received. + dwResult = + (g_pDot11ExtApi->Dot11ExtStopOneX) + ( + pAdapterDetails->hDot11SvcHandle + ); + if(dwResult != ERROR_SUCCESS) + { + // log error + dwResult = ERROR_SUCCESS; + } + else + { + pAdapterDetails->pOnexData->fMSOneXStarted = FALSE; + } + } + + // flush the rc4 packet cache. + FlushRC4PktCache( pAdapterDetails->pOnexData ); + + // free the result params. + RC4UtilsFreeResultParams( &(pAdapterDetails->pOnexData->pOnexResultParams) ); + } + + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + + + +DWORD +PlumbWEPKey +( + HANDLE hDot11SvcHandle, + ULONG uKeyIndex, + DOT11_DIRECTION direction, + LPBYTE pbKey, + ULONG uKeyLen +) +{ + DWORD dwResult = ERROR_SUCCESS; + PDOT11_CIPHER_DEFAULT_KEY_VALUE pDefaultKey = NULL; + ULONG uAllocLen = 0; + BOOL bLocked = FALSE; + + uAllocLen = FIELD_OFFSET(DOT11_CIPHER_DEFAULT_KEY_VALUE, ucKey) + uKeyLen; + + if (uAllocLen < uKeyLen) + { + dwResult = ERROR_ARITHMETIC_OVERFLOW; + BAIL_ON_WIN32_ERROR(dwResult); + } + + pDefaultKey = (PDOT11_CIPHER_DEFAULT_KEY_VALUE) PrivateMemoryAlloc(uAllocLen); + if (!pDefaultKey) + { + dwResult = ERROR_NOT_ENOUGH_MEMORY; + BAIL_ON_WIN32_ERROR(dwResult); + } + + pDefaultKey->AlgorithmId = DOT11_CIPHER_ALGO_WEP; + pDefaultKey->uKeyIndex = uKeyIndex; + pDefaultKey->bDelete = FALSE; + pDefaultKey->bStatic = FALSE; + pDefaultKey->usKeyLength = (USHORT) uKeyLen; + + ZeroMemory(&(pDefaultKey->MacAddr), sizeof(DOT11_MAC_ADDRESS)); + + CopyMemory + ( + pDefaultKey->ucKey, + pbKey, + uKeyLen + ); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + TRACE_MESSAGE( "Setting default key." ); + TRACE_MESSAGE_VAL( " Key Index = ", uKeyIndex ); + TRACE_MESSAGE_VAL( " Key Length = ", uKeyLen ); + TRACE_MESSAGE_VAL( " Direction = ", direction ); + + dwResult = + (g_pDot11ExtApi->Dot11ExtSetDefaultKey) + ( + hDot11SvcHandle, + pDefaultKey, + direction + ); + BAIL_ON_WIN32_ERROR(dwResult); + + +error: + if (pDefaultKey) + { + SecureZeroMemory( pDefaultKey, uAllocLen ); + PrivateMemoryFree( pDefaultKey ); + } + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + +DWORD +ProcessRC4Key +( + HANDLE hIhvExtAdapter, + PDOT11_MSONEX_RESULT_PARAMS pOneXResultParams, + HANDLE hDot11SvcHandle, + HANDLE hSecuritySessionID, + ULONG uPktLen, + PBYTE pbEapolPkt +) +{ + DWORD dwResult = ERROR_SUCCESS; + PEAPOL_PACKET pEapolPkt = NULL; + DWORD dwKeyLen = 0; + DWORD dwKeyIndex = 0; + LPBYTE pbDecryptedKey = NULL; + BOOL bUCast = FALSE; + BOOL bLocked = FALSE; + + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + ASSERT( IsOneXResultParamsAvailable ( pOneXResultParams ) ); + + TRACE_MESSAGE( "Processing RC4 key." ); + + pEapolPkt = (PEAPOL_PACKET) pbEapolPkt; + + dwResult = + RC4UtilsParseKeyPacket + ( + pEapolPkt, + uPktLen, + pOneXResultParams, + &bUCast, + &pbDecryptedKey, + &dwKeyLen, + &dwKeyIndex + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + dwResult = + PlumbWEPKey + ( + hDot11SvcHandle, + dwKeyIndex, + bUCast ? DOT11_DIR_BOTH : DOT11_DIR_INBOUND, + pbDecryptedKey, + dwKeyLen + ); + BAIL_ON_WIN32_ERROR(dwResult); + + if ( bUCast ) + { + TRACE_MESSAGE_VAL( "Setting default key ID, Key Index = ", dwKeyIndex ); + + dwResult = + (g_pDot11ExtApi->Dot11ExtSetDefaultKeyId) + ( + hDot11SvcHandle, + dwKeyIndex + ); + BAIL_ON_WIN32_ERROR(dwResult); + + dwResult = + Do1xPostAssocCompletion + ( + hIhvExtAdapter, + hDot11SvcHandle, + hSecuritySessionID, + NULL, + L2_REASON_CODE_SUCCESS, + ERROR_SUCCESS + ); + BAIL_ON_WIN32_ERROR(dwResult); + } + +error: + RC4UtilsFreeKeyMaterial( pbDecryptedKey, dwKeyLen ); + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} diff --git a/network/wlan/ihvsample/ihvonexext.h b/network/wlan/ihvsample/ihvonexext.h new file mode 100644 index 00000000..574bbdfa --- /dev/null +++ b/network/wlan/ihvsample/ihvonexext.h @@ -0,0 +1,108 @@ + + + +// cache size. +#define RC4_CACHE_SIZE 2 + + +// cached packet. +typedef +struct _CACHED_PKT +{ + ULONG uPktLen; + PBYTE pbPkt; +} +CACHED_PKT, *PCACHED_PKT; + + +// connection specific data for onex profiles. +typedef +struct _ONEX_DATA +{ + HANDLE hSecuritySessionID; + BOOL fMSOneXStarted; + CACHED_PKT RC4Cache[RC4_CACHE_SIZE]; + ULONG uCacheFreeIdx; + PDOT11_MSONEX_RESULT_PARAMS pOnexResultParams; + IHV_RECEIVE_PACKET_HANDLER lpfnReceivePacket; + IHV_INDICATE_RESULT_HANDLER lpfnIndicateResult; +} +ONEX_DATA, *PONEX_DATA; + + +// initialize onex data structure. +DWORD +GetNewOnexData +( + PONEX_DATA* ppOnexData +); + +// free onex data structure. +VOID +FreeOnexData +( + PONEX_DATA* ppOnexData +); + + + + +// pre-associate function for onex profile. +DWORD +WINAPI +Do1xPreAssociate +( + PADAPTER_DETAILS pAdapterDetails, + DWORD* pdwReasonCode +); + + + +// post-associate function for onex profile. +DWORD +WINAPI +Do1xPostAssociate +( + IN PADAPTER_DETAILS pAdapterDetails, + IN HANDLE hSecuritySessionID, + IN PDOT11_PORT_STATE pPortState, + IN ULONG uDot11AssocParamsBytes, + IN PDOT11_ASSOCIATION_COMPLETION_PARAMETERS pDot11AssocParams +); + + +// stop-post-associate function for onex profile. +DWORD +WINAPI +Do1xStopPostAssociate +( + PADAPTER_DETAILS pAdapterDetails, + PDOT11_MAC_ADDRESS pPeer, + DOT11_ASSOC_STATUS dot11AssocStatus +); + + + +// receive packet function for onex profile. +DWORD +WINAPI +Do1xReceivePacket +( + PADAPTER_DETAILS pAdapterDetails, + DWORD dwInBufferSize, + LPVOID pvInBuffer +); + + + +// indicate result function for onex profile. +DWORD +WINAPI +Do1xIndicateResult +( + PADAPTER_DETAILS pAdapterDetails, + DOT11_MSONEX_RESULT msOneXResult, + PDOT11_MSONEX_RESULT_PARAMS pDot11MsOneXResultParams +); + + diff --git a/network/wlan/ihvsample/ihvplapmachineoruser.xml b/network/wlan/ihvsample/ihvplapmachineoruser.xml new file mode 100644 index 00000000..cd86060e --- /dev/null +++ b/network/wlan/ihvsample/ihvplapmachineoruser.xml @@ -0,0 +1,64 @@ + + + ihvplapmachineoruser + + + _1x_SSID_ + + false + + ESS + auto + false + + + + open + WEP + true + + + 3 + machineOrUser + + + preLogon + 50 + + + + + 25 + 0 + 0 + 0 + + + + + + + + + + 123456 + 01 + + + + 0 + + + + + + FALSE + IHVAuthV1 + IHVCipher1 + 0 + + + + true + + diff --git a/network/wlan/ihvsample/ihvplapuser.xml b/network/wlan/ihvsample/ihvplapuser.xml new file mode 100644 index 00000000..37334800 --- /dev/null +++ b/network/wlan/ihvsample/ihvplapuser.xml @@ -0,0 +1,64 @@ + + + ihvplapuser + + + _1x_SSID_ + + false + + ESS + auto + false + + + + open + WEP + true + + + 3 + user + + + preLogon + 50 + + + + + 25 + 0 + 0 + 0 + + + + + + + + + + 123456 + 01 + + + + 0 + + + + + + FALSE + IHVAuthV1 + IHVCipher1 + 0 + + + + true + + diff --git a/network/wlan/ihvsample/ihvsample.cpp b/network/wlan/ihvsample/ihvsample.cpp new file mode 100644 index 00000000..2b73a7ed --- /dev/null +++ b/network/wlan/ihvsample/ihvsample.cpp @@ -0,0 +1,1554 @@ +/*++ + +Copyright (c) 2005 Microsoft Corporation + +Abstract: + + Sample IHV Extensibility DLL to extend + 802.11 LWF driver for third party protocols. + + +--*/ + +#include "precomp.h" + + + +// +// Get Version info. +// +DWORD +WINAPI +Dot11ExtIhvGetVersionInfo +( + OUT PDOT11_IHV_VERSION_INFO pDot11IHVVersionInfo +) +{ + if ( pDot11IHVVersionInfo ) + { + pDot11IHVVersionInfo->dwVerMin = 0; + pDot11IHVVersionInfo->dwVerMax = 0; + } + return ERROR_SUCCESS; +} + + +// +// Initialize service. +// + +DWORD +WINAPI +Dot11ExtIhvInitService +( + IN DWORD dwVerNumUsed, + IN PDOT11EXT_APIS pDot11ExtAPI, + IN LPVOID pvReserved, + OUT PDOT11EXT_IHV_HANDLERS pDot11IHVHandlers +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + + UNREFERENCED_PARAMETER( pvReserved ); + + if + ( + ( 0 != dwVerNumUsed ) || + ( !pDot11ExtAPI ) || + ( !pDot11IHVHandlers ) + ) + { + dwResult = ERROR_INVALID_PARAMETER; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + InitAdapterDetailsList( ); + + g_pDot11ExtApi = (PDOT11EXT_APIS) PrivateMemoryAlloc( sizeof( DOT11EXT_APIS ) ); + if ( !g_pDot11ExtApi ) + { + dwResult = ERROR_OUTOFMEMORY; + BAIL_ON_WIN32_ERROR( dwResult ); + } + (*g_pDot11ExtApi) = (*pDot11ExtAPI); + + HandlerInit( pDot11IHVHandlers ); + + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + + +// +// Deinitialize service. +// +VOID +WINAPI +IhvDeinitService +( + VOID +) +{ + + EnterCriticalSection( &g_csSynch ); + + // disable starting new threads or adding new adapters. + StartShutdown( ); + + LeaveCriticalSection( &g_csSynch ); + + + WaitOnZeroThreads( ); + DeinitAdapterDetailsList( ); + + if ( g_pDot11ExtApi ) + { + PrivateMemoryFree( g_pDot11ExtApi ); + g_pDot11ExtApi = NULL; + } + + return; +} + + + +// +// Initialize adapter +// +DWORD +WINAPI +IhvInitAdapter +( + IN PDOT11_ADAPTER pDot11Adapter, + IN HANDLE hDot11SvcHandle, + OUT PHANDLE phIhvExtAdapter +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + HANDLE hIhvExtAdapter = NULL; + USHORT usEtherTypeReg[] = { 0x888e, 0x8333 }; + + if (( !pDot11Adapter ) || (!hDot11SvcHandle) || (!phIhvExtAdapter) ) + { + dwResult = ERROR_INVALID_PARAMETER; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + dwResult = + InitAdapterDetails + ( + hDot11SvcHandle, + &hIhvExtAdapter + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + dwResult = + (g_pDot11ExtApi->Dot11ExtSetEtherTypeHandling) + ( + hDot11SvcHandle, + 3, + 0, + NULL, + 2, + usEtherTypeReg + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + (*phIhvExtAdapter) = hIhvExtAdapter; + hIhvExtAdapter = NULL; + +error: + + if ( hIhvExtAdapter ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + +// +// Deinit adapter. +// +VOID +WINAPI +IhvDeinitAdapter +( + IN HANDLE hIhvExtAdapter +) +{ + DerefenceAdapterDetails( hIhvExtAdapter ); + + return; +} + + + + + +// +// Handle session change notification. +// +DWORD +WINAPI +IhvProcessSessionChange +( + IN ULONG uEventType, + IN PWTSSESSION_NOTIFICATION pSessionNotification +) +{ + UNREFERENCED_PARAMETER( pSessionNotification ); + + if ( WTS_CONSOLE_CONNECT == uEventType ) + { + // The sample does not use this session ID + // anywhere. In an actual application, IHV + // developers may want to use this session ID to + // get/set the user data. + g_dwSessionID = WTSGetActiveConsoleSessionId( ); + } + return ERROR_SUCCESS; +} + + + + + +// +// Checks if UI request is pending. +// +DWORD +WINAPI +IhvIsUIRequestPending +( + IN GUID guidUIRequest, + OUT PBOOL pbIsRequestPending +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + PADAPTER_DETAILS pAdapterDetails = NULL; + HANDLE hIhvExtAdapter = NULL; + + + ASSERT( pbIsRequestPending ); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // obtain reference to the adapter using + // UI request GUID. + dwResult = + ReferenceAdapterDetailsByUIRequestGuid + ( + &guidUIRequest, + &pAdapterDetails, + &hIhvExtAdapter + ); + if ( ERROR_NOT_FOUND == dwResult ) + { + dwResult = ERROR_SUCCESS; + (*pbIsRequestPending) = FALSE; + BAIL( ); + } + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + + (*pbIsRequestPending) = TRUE; + +error: + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + +// +// Handle NIC specific notifications. +// +DWORD +WINAPI +IhvReceiveIndication +( + IN HANDLE hIhvExtAdapter, + IN DOT11EXT_IHV_INDICATION_TYPE indicationType, + IN ULONG uBufferLength, + IN LPVOID pvBuffer +) +{ + DWORD dwResult = ERROR_SUCCESS; + PDOT11_PMKID_CANDIDATE_LIST_PARAMETERS pPMKCandidateListParams = NULL; + PDOT11_TKIPMIC_FAILURE_PARAMETERS pTkipMicFailureParams = NULL; + PDOT11_PHY_STATE_PARAMETERS pPHYStateChange = NULL; + PDOT11_LINK_QUALITY_PARAMETERS pDot11LinkQualityParams = NULL; + + ASSERT( pvBuffer ); + + // Note that sample does not really use the buffer. + UNREFERENCED_PARAMETER( hIhvExtAdapter ); + UNREFERENCED_PARAMETER( uBufferLength ); + + switch ( indicationType ) + { + case IndicationTypeNicSpecificNotification: + // The pointer pvBuffer can be interpreted by IHV appropriately. Format + // is a contract between the miniport driver and the IHV extensibility module. + break; + + case IndicationTypePmkidCandidateList: + pPMKCandidateListParams = (PDOT11_PMKID_CANDIDATE_LIST_PARAMETERS) pvBuffer; + // Ihv extensibility module may choose to use this data appropriately. + break; + + case IndicationTypeTkipMicFailure: + pTkipMicFailureParams = (PDOT11_TKIPMIC_FAILURE_PARAMETERS) pvBuffer; + // Ihv extensibility module may choose to use this data appropriately. + break; + + case IndicationTypePhyStateChange: + pPHYStateChange = (PDOT11_PHY_STATE_PARAMETERS) pvBuffer; + // Ihv extensibility module may choose to use this data appropriately. + break; + + case IndicationTypeLinkQuality: + pDot11LinkQualityParams = (PDOT11_LINK_QUALITY_PARAMETERS) pvBuffer; + // Ihv extensibility module may choose to use this data appropriately. + break; + + default: + ASSERTFAILURE(); + dwResult = ERROR_INVALID_PARAMETER; + BAIL_ON_WIN32_ERROR( dwResult ); + break; + } + +error: + return dwResult; +} + + + + + + +// +// Perform the capabiity match. +// +DWORD +WINAPI +IhvPerformCapabilityMatch +( + IN HANDLE hIhvExtAdapter, + IN PDOT11EXT_IHV_PROFILE_PARAMS pIhvProfileParams, + IN PDOT11EXT_IHV_CONNECTIVITY_PROFILE pIhvConnProfile, + IN PDOT11EXT_IHV_SECURITY_PROFILE pIhvSecProfile, + IN PDOT11_BSS_LIST pConnectableBssid, + OUT PDWORD pdwReasonCode +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + PBYTE pbCurrPos = NULL; + ULONG uRemainBytes = 0; + ULONG uBssEntryBytes = 0; + PULDOT11_BSS_ENTRY pBssEntry = NULL; + PADAPTER_DETAILS pAdapterDetails = NULL; + PIHV_CONNECTIVITY_PROFILE pConnectivityProfile = NULL; + PIHV_SECURITY_PROFILE pSecurityProfile = NULL; + + ASSERT( hIhvExtAdapter ); + ASSERT( pdwReasonCode ); + + (*pdwReasonCode) = L2_REASON_CODE_UNKNOWN; + + if ( !pConnectableBssid ) + { + dwResult = + IhvValidateProfile + ( + hIhvExtAdapter, + pIhvProfileParams, + pIhvConnProfile, + pIhvSecProfile, + pdwReasonCode + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + // Only validate profile if + // pConnectableBssid is NULL. + BAIL( ); + } + + // Validating data in pConnectableBssid + if (( 0 == pConnectableBssid->uNumOfBytes ) || ( NULL == pConnectableBssid->pucBuffer )) + { + dwResult = ERROR_NO_MATCH; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + (*pdwReasonCode) = L2_REASON_CODE_IHV_BAD_PROFILE; + + // parse the connectivity profile. + dwResult = + GetIhvConnectivityProfile + ( + pIhvConnProfile, + &pConnectivityProfile + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pConnectivityProfile ); + + // parse the security profile. + dwResult = + GetIhvSecurityProfile + ( + pIhvSecProfile, + &pSecurityProfile + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pSecurityProfile ); + + (*pdwReasonCode) = L2_REASON_CODE_UNKNOWN; + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // reference the adapter. + dwResult = + ReferenceAdapterDetails + ( + hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + + // try matching each BSS description field with the profile + // and see if there is a match. + uRemainBytes = pConnectableBssid->uNumOfBytes; + pbCurrPos = pConnectableBssid->pucBuffer; + + // BSS Description might not contain any buffer! + while (uRemainBytes >= FIELD_OFFSET(DOT11_BSS_ENTRY, ucBuffer)) + { + pBssEntry = (PULDOT11_BSS_ENTRY)pbCurrPos; + uBssEntryBytes = pBssEntry->uBufferLength + FIELD_OFFSET(DOT11_BSS_ENTRY, ucBuffer); + + ASSERT (uRemainBytes >= uBssEntryBytes); + uRemainBytes -= uBssEntryBytes; + pbCurrPos += uBssEntryBytes; + + if + ( + MatchBssDescription + ( + pIhvProfileParams, + pConnectivityProfile, + pSecurityProfile, + pBssEntry + ) + ) + { + (*pdwReasonCode) = L2_REASON_CODE_SUCCESS; + dwResult = ERROR_SUCCESS; + BAIL( ); + } + } + + // Since none of the BSS entries matched. + dwResult = ERROR_NO_MATCH; + +error: + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + FreeIhvConnectivityProfile( &pConnectivityProfile ); + FreeIhvSecurityProfile( &pSecurityProfile ); + return dwResult; +} + + + + + +// +// Function to validate profile. +// +DWORD +WINAPI +IhvValidateProfile +( + IN HANDLE hIhvExtAdapter, + IN PDOT11EXT_IHV_PROFILE_PARAMS pIhvProfileParams, + IN PDOT11EXT_IHV_CONNECTIVITY_PROFILE pIhvConnProfile, + IN PDOT11EXT_IHV_SECURITY_PROFILE pIhvSecProfile, + OUT PDWORD pdwReasonCode +) +{ + DWORD dwResult = ERROR_SUCCESS; + PIHV_CONNECTIVITY_PROFILE pConnectivityProfile = NULL; + PIHV_SECURITY_PROFILE pSecurityProfile = NULL; + + + ASSERT( hIhvExtAdapter ); + ASSERT( pdwReasonCode ); + + UNREFERENCED_PARAMETER( hIhvExtAdapter ); + UNREFERENCED_PARAMETER( pIhvProfileParams ); + + (*pdwReasonCode) = L2_REASON_CODE_IHV_BAD_PROFILE; + + // parse ihv connectivity profile. + dwResult = + GetIhvConnectivityProfile + ( + pIhvConnProfile, + &pConnectivityProfile + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pConnectivityProfile ); + + // parse ihv security profile. + dwResult = + GetIhvSecurityProfile + ( + pIhvSecProfile, + &pSecurityProfile + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pSecurityProfile ); + + (*pdwReasonCode) = L2_REASON_CODE_SUCCESS; + +error: + FreeIhvConnectivityProfile( &pConnectivityProfile ); + FreeIhvSecurityProfile( &pSecurityProfile ); + return dwResult; +} + + +// +// This function does the actual preassociation work. +// +DWORD +WINAPI +DoPreAssociate +( + LPVOID pvPreAssociate +) +{ + DWORD dwResult = ERROR_SUCCESS; + DWORD dwStatus = ERROR_SUCCESS; + PADAPTER_DETAILS pAdapterDetails = NULL; + PRE_ASSOCIATE_FUNCTION lpfnPreAssociate = NULL; + BOOL bLocked = FALSE; + DWORD dwReasonCode = L2_REASON_CODE_UNKNOWN; + + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + dwResult = + ReferenceAdapterDetails + ( + (HANDLE) pvPreAssociate, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + + dwReasonCode = L2_REASON_CODE_IHV_BAD_PROFILE; + + if ( !(pAdapterDetails->pConnectivityProfile && pAdapterDetails->pSecurityProfile) ) + { + dwResult = ERROR_INVALID_PARAMETER; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // choose pre-association function based on profile content. + if + ( + ( pAdapterDetails->pSecurityProfile->bUseFullSecurity ) || + ( pAdapterDetails->pSecurityProfile->bUseIhvConnectivityOnly ) + ) + { + if ( pAdapterDetails->pSecurityProfile->bUseIhvConnectivityOnly ) + { + lpfnPreAssociate = DoIhvConnPreAssociate; + } + else if ( NULL == pAdapterDetails->pConnectivityProfile->pszParam2 ) + { + lpfnPreAssociate = DoMissingKeyWepPreAssociate; + } + else if ( 0 == pAdapterDetails->pConnectivityProfile->pszParam2[0] ) + { + lpfnPreAssociate = DoMissingKeyWepPreAssociate; + } + else + { + lpfnPreAssociate = DoWepPreAssociate; + } + } + else if + ( + ( !(pAdapterDetails->pSecurityProfile->bUseFullSecurity) ) && + ( IHVAuthV1 == pAdapterDetails->pSecurityProfile->AuthType ) + ) + { + lpfnPreAssociate = Do1xPreAssociate; + } + else + { + dwResult = ERROR_INVALID_PARAMETER; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + dwReasonCode = L2_REASON_CODE_UNKNOWN; + + LeaveCriticalSection( &g_csSynch ); + bLocked = FALSE; + + ASSERT( lpfnPreAssociate ); + ASSERT( !bLocked ); + + dwResult = + (lpfnPreAssociate) + ( + pAdapterDetails, + &dwReasonCode + ); + BAIL_ON_WIN32_ERROR( dwResult ); + +error: + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( (HANDLE) &(pAdapterDetails->Link) ); + } + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + if ( pAdapterDetails ) + { + // call the completion function directly in this thread. + dwStatus = + (g_pDot11ExtApi->Dot11ExtPreAssociateCompletion) + ( + pAdapterDetails->hDot11SvcHandle, + pAdapterDetails->hConnectSession, + dwReasonCode, + dwResult + ); + } + if ( ERROR_SUCCESS != dwStatus ) + { + // IHV specific logging can happen here. + } + return dwResult; +} + + + +// +// Function to start the preassociation thread. +// +DWORD +WINAPI +IhvPerformPreAssociate +( + IN HANDLE hIhvExtAdapter, + IN HANDLE hConnectSession, + IN PDOT11EXT_IHV_PROFILE_PARAMS pIhvProfileParams, + IN PDOT11EXT_IHV_CONNECTIVITY_PROFILE pIhvConnProfile, + IN PDOT11EXT_IHV_SECURITY_PROFILE pIhvSecProfile, + IN PDOT11_BSS_LIST pConnectableBssid, + OUT PDWORD pdwReasonCode +) +{ + DWORD dwResult = ERROR_SUCCESS; + DWORD dwStatus = ERROR_SUCCESS; + BOOL bLocked = FALSE; + PADAPTER_DETAILS pAdapterDetails = NULL; + + + ASSERT ( pIhvProfileParams ); + ASSERT ( pdwReasonCode ); + + UNREFERENCED_PARAMETER( pIhvProfileParams ); + UNREFERENCED_PARAMETER( pConnectableBssid ); + + (*pdwReasonCode) = L2_REASON_CODE_UNKNOWN; + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + dwResult = + ReferenceAdapterDetails + ( + hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + if ( nic_state_initialized != pAdapterDetails->NicState ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // Connection specific parameters should be properly initialized. + if + ( + ( pAdapterDetails->pOnexData ) || + ( pAdapterDetails->hConnectSession ) || + ( pAdapterDetails->pPerformPostAssociateCompletionRoutine ) || + ( pAdapterDetails->pPerformPostAssociateRoutine ) || + ( pAdapterDetails->pStopPostAssociateRoutine ) + ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + + + (*pdwReasonCode) = L2_REASON_CODE_IHV_BAD_PROFILE; + + // parse connectivity profile + dwResult = + GetIhvConnectivityProfile + ( + pIhvConnProfile, + &(pAdapterDetails->pConnectivityProfile) + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails->pConnectivityProfile ); + + // parse security profile. + dwResult = + GetIhvSecurityProfile + ( + pIhvSecProfile, + &(pAdapterDetails->pSecurityProfile) + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails->pSecurityProfile ); + + (*pdwReasonCode) = L2_REASON_CODE_UNKNOWN; + + pAdapterDetails->NicState = nic_state_pre_assoc_started; + + pAdapterDetails->hConnectSession = hConnectSession; + pAdapterDetails->bModifyCurrentProfile = FALSE; + + + // post a new thread to do the pre-association work. + dwResult = + StartNewProtectedThread + ( + hIhvExtAdapter, + DoPreAssociate, + (LPVOID) hIhvExtAdapter + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + + (*pdwReasonCode) = L2_REASON_CODE_SUCCESS; + + +error: + if ( ERROR_SUCCESS != dwResult ) + { + dwStatus = + IhvAdapterReset + ( + hIhvExtAdapter + ); + ASSERT( ERROR_SUCCESS == dwStatus ); + } + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + + +// +// This function starts post associate routine +// in a separate thread and returns. +// +DWORD +WINAPI +IhvPerformPostAssociate +( + IN HANDLE hIhvExtAdapter, + IN HANDLE hSecuritySessionID, + IN PDOT11_PORT_STATE pPortState, + IN ULONG uDot11AssocParamsBytes, + IN PDOT11_ASSOCIATION_COMPLETION_PARAMETERS pDot11AssocParams +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + PPOST_ASSOC_DATA ppostAssocData = NULL; + PADAPTER_DETAILS pAdapterDetails = NULL; + + + ASSERT ( pDot11AssocParams ); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + dwResult = + ReferenceAdapterDetails + ( + hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + + if ( nic_state_pre_assoc_ended != pAdapterDetails->NicState ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + pAdapterDetails->NicState = nic_state_post_assoc_started; + + // is any post-association work required ?? + if ( pAdapterDetails->pPerformPostAssociateRoutine ) + { + dwResult = + ( pAdapterDetails->pPerformPostAssociateRoutine ) + ( + pAdapterDetails, + hSecuritySessionID, + pPortState, + uDot11AssocParamsBytes, + pDot11AssocParams + ); + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // The post association completion thread may have been + // started by the perform post association routine for + // the current profile. Check if the completion thread + // needs to be started and start the routine. + if ( pAdapterDetails->pPerformPostAssociateCompletionRoutine ) + { + ppostAssocData = (PPOST_ASSOC_DATA) PrivateMemoryAlloc( sizeof( POST_ASSOC_DATA ) ); + if ( !ppostAssocData ) + { + dwResult = ERROR_OUTOFMEMORY; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + ppostAssocData->hIhvExtAdapter = hIhvExtAdapter; + ppostAssocData->hDot11SvcHandle = pAdapterDetails->hDot11SvcHandle; + ppostAssocData->hSecuritySessionId = hSecuritySessionID; + + + dwResult = + StartNewProtectedThread + ( + hIhvExtAdapter, + pAdapterDetails->pPerformPostAssociateCompletionRoutine, + ppostAssocData + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + + ppostAssocData = NULL; + } + + + +error: + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + PrivateMemoryFree( ppostAssocData ); + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + +// +// Reset the adapter to initialized state. +// +DWORD +WINAPI +IhvAdapterReset +( + IN HANDLE hIhvExtAdapter +) +{ + DWORD dwResult = ERROR_SUCCESS; + PADAPTER_DETAILS pAdapterDetails = NULL; + DWORD dwRefCount = 0; + BOOL bOk = FALSE; + BOOL bLocked = FALSE; + + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + dwResult = + ReferenceAdapterDetails + ( + hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + + for ( ;; ) + { + ASSERT( bLocked ); + + dwRefCount = pAdapterDetails->dwRefCount; + + if ( 2 == dwRefCount ) // For adapter init and self call. + { + break; + } + + if ( nic_state_pre_assoc_started == pAdapterDetails->NicState ) + { + pAdapterDetails->NicState = nic_state_pre_assoc_ended; + bOk = SetEvent( pAdapterDetails->hUIResponse ); + ASSERT( bOk ); + } + + LeaveCriticalSection( &g_csSynch ); + bLocked = FALSE; + + Sleep( 100 ); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + } + + ASSERT( bLocked ); + + // Resetting adapter. + + pAdapterDetails->NicState = nic_state_initialized; + pAdapterDetails->hConnectSession = NULL; + pAdapterDetails->bModifyCurrentProfile = FALSE; + pAdapterDetails->pPerformPostAssociateCompletionRoutine = NULL; + pAdapterDetails->pPerformPostAssociateRoutine = NULL; + pAdapterDetails->pStopPostAssociateRoutine = NULL; + + // freeing UI response data. + pAdapterDetails->dwResponseLen = 0; + + PrivateMemoryFree( pAdapterDetails->pbResponse ); + pAdapterDetails->pbResponse = NULL; + + ZeroMemory( &(pAdapterDetails->currentGuidUIRequest), sizeof( GUID ) ); + + // freeing profile and onex data + FreeOnexData( &(pAdapterDetails->pOnexData) ); + FreeIhvConnectivityProfile( &(pAdapterDetails->pConnectivityProfile) ); + FreeIhvSecurityProfile( &(pAdapterDetails->pSecurityProfile) ); + + // unblocking UI thread. + bOk = ResetEvent( pAdapterDetails->hUIResponse ); + if ( !bOk ) + { + dwResult = GetLastError( ); + BAIL_ON_WIN32_ERROR( dwResult ); + } + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + return dwResult; +} + + + + +// +// Stop post association. Get back +// to the state before post association started. +// +DWORD +WINAPI +IhvStopPostAssociate +( + IN HANDLE hIhvExtAdapter, + IN PDOT11_MAC_ADDRESS pPeer, + IN DOT11_ASSOC_STATUS dot11AssocStatus +) +{ + DWORD dwResult = ERROR_SUCCESS; + PADAPTER_DETAILS pAdapterDetails = NULL; + DWORD dwRefCount = 0; + BOOL bLocked = FALSE; + + // The dot11AssocStatus parameter can be compared with + // values DOT11_ASSOC_STATUS_* defined in the public + // headers. + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + dwResult = + ReferenceAdapterDetails + ( + hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + for ( ;; ) + { + ASSERT( bLocked ); + + dwRefCount = pAdapterDetails->dwRefCount; + + if ( 2 == dwRefCount ) // For adapter init and self call. + { + break; + } + + LeaveCriticalSection( &g_csSynch ); + bLocked = FALSE; + + Sleep( 100 ); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + } + + ASSERT( bLocked ); + + if ( nic_state_initialized == pAdapterDetails->NicState ) + { + // NO OP + // To handle the case when stop post associate + // call comes after adapter reset. + BAIL( ); + } + + // Call the post association routine - if there is any. + if ( pAdapterDetails->pStopPostAssociateRoutine ) + { + dwResult = + (pAdapterDetails->pStopPostAssociateRoutine) + ( + pAdapterDetails, + pPeer, + dot11AssocStatus + ); + BAIL_ON_WIN32_ERROR( dwResult ); + } + + pAdapterDetails->NicState = nic_state_pre_assoc_ended; + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + return dwResult; +} + + +// +// Process received packets. +// +DWORD +WINAPI +IhvReceivePacket +( + IN HANDLE hIhvExtAdapter, + IN DWORD dwInBufferSize, + IN LPVOID pvInBuffer +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + PADAPTER_DETAILS pAdapterDetails = NULL; + IHV_RECEIVE_PACKET_HANDLER lpfnReceivePacket = NULL; + + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + + dwResult = + ReferenceAdapterDetails + ( + hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + + if ( pAdapterDetails->pOnexData && pAdapterDetails->pOnexData->lpfnReceivePacket ) + { + lpfnReceivePacket = pAdapterDetails->pOnexData->lpfnReceivePacket; + } + else + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + LeaveCriticalSection( &g_csSynch ); + bLocked = FALSE; + + ASSERT( lpfnReceivePacket ); + + // Call the receive packet routine. + dwResult = + (lpfnReceivePacket) + ( + pAdapterDetails, + dwInBufferSize, + pvInBuffer + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + +error: + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + +// +// Local copy of possible discovery profiles. +// +DOT11EXT_IHV_DISCOVERY_PROFILE +g_IhvDiscoveryProfiles[] = +{ + // discovery profile 1. + { + { + L"" + L"0" + L"" + L"" + }, + + // Use MS Onex. + { + L"" + L"FALSE" + L"IHVAuthV1" + L"IHVCipher1" + L"0" + L"" + L"", + + TRUE + } + }, + + // discovery profile 2 + { + // Open Wep + { + L"" + L"0" + L"" + L"" + }, + + // Full IHV Security + { + L"" + L"TRUE" + L"IHVAuthV1" + L"IHVCipher1" + L"0" + L"" + L"", + + FALSE + } + } +}; + + + + + + + +// +// Create discovery profiles. +// +DWORD +WINAPI +IhvCreateDiscoveryProfiles +( + IN HANDLE hIhvExtAdapter, + IN BOOL bInsecure, + IN PDOT11EXT_IHV_PROFILE_PARAMS pIhvProfileParams, + IN PDOT11_BSS_LIST pConnectableBssid, + OUT PDOT11EXT_IHV_DISCOVERY_PROFILE_LIST pIhvDiscoveryProfileList, + OUT PDWORD pdwReasonCode +) +{ + DWORD dwResult = ERROR_SUCCESS; + DWORD dwIndex = 0; + DWORD dwIHVNumProfiles = ARRAY_LENGTH( g_IhvDiscoveryProfiles ); + + ASSERT( pIhvDiscoveryProfileList ); + ASSERT( pdwReasonCode ); + + UNREFERENCED_PARAMETER( hIhvExtAdapter ); + UNREFERENCED_PARAMETER( bInsecure ); + UNREFERENCED_PARAMETER( pIhvProfileParams ); + UNREFERENCED_PARAMETER( pConnectableBssid ); + + (*pdwReasonCode) = L2_REASON_CODE_IHV_OUTOFMEMORY; + + // allocate buffer for the array. + dwResult = + (g_pDot11ExtApi->Dot11ExtAllocateBuffer) + ( + dwIHVNumProfiles * sizeof( DOT11EXT_IHV_DISCOVERY_PROFILE ), + (LPVOID*) &(pIhvDiscoveryProfileList->pIhvDiscoveryProfiles) + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + ASSERT( pIhvDiscoveryProfileList->pIhvDiscoveryProfiles ); + ZeroMemory + ( + pIhvDiscoveryProfileList->pIhvDiscoveryProfiles, + dwIHVNumProfiles * sizeof( DOT11EXT_IHV_DISCOVERY_PROFILE ) + ); + + + + // prepare each discovery profile. + for ( dwIndex = 0; dwIndex < dwIHVNumProfiles; dwIndex++ ) + { + dwResult = + CopyDiscoveryProfile + ( + g_IhvDiscoveryProfiles + dwIndex, + pIhvDiscoveryProfileList->pIhvDiscoveryProfiles + dwIndex + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + } + + pIhvDiscoveryProfileList->dwCount = dwIHVNumProfiles; + (*pdwReasonCode) = L2_REASON_CODE_SUCCESS; + +error: + if ( ERROR_SUCCESS != dwResult ) + { + if ( pIhvDiscoveryProfileList->pIhvDiscoveryProfiles ) + { + for ( dwIndex = 0; dwIndex < dwIHVNumProfiles; dwIndex++ ) + { + FreeDiscoveryProfile( pIhvDiscoveryProfileList->pIhvDiscoveryProfiles + dwIndex ); + } + (g_pDot11ExtApi->Dot11ExtFreeBuffer)( pIhvDiscoveryProfileList->pIhvDiscoveryProfiles ); + } + pIhvDiscoveryProfileList->dwCount = 0; + } + return dwResult; +} + + + +// +// Process UI Response function. +// +DWORD +WINAPI +IhvProcessUIResponse +( + IN GUID guidUIRequest, + IN DWORD dwByteCount, + IN LPVOID pvResponseBuffer +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + PADAPTER_DETAILS pAdapterDetails = NULL; + HANDLE hIhvExtAdapter = NULL; + + if ( !( dwByteCount && pvResponseBuffer ) ) + { + dwResult = ERROR_INVALID_PARAMETER; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // find the adapter from the UI response guid. + dwResult = + ReferenceAdapterDetailsByUIRequestGuid + ( + &guidUIRequest, + &pAdapterDetails, + &hIhvExtAdapter + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + ZeroMemory( &(pAdapterDetails->currentGuidUIRequest), sizeof( GUID ) ); + + // Copy the UI response to the adapter data structure. + PrivateMemoryFree( pAdapterDetails->pbResponse ); + + pAdapterDetails->pbResponse = (BYTE*) PrivateMemoryAlloc( dwByteCount ); + if ( !(pAdapterDetails->pbResponse) ) + { + dwResult = ERROR_OUTOFMEMORY; + BAIL_ON_WIN32_ERROR(dwResult); + } + CopyMemory( pAdapterDetails->pbResponse, pvResponseBuffer, dwByteCount ); + + (pAdapterDetails->dwResponseLen) = dwByteCount; + + // Waiting thread can pick up the response now. + SetEvent( pAdapterDetails->hUIResponse ); + +error: + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + +// +// Handler to clear memory after sending packet. +// +DWORD +WINAPI +IhvSendPacketCompletion +( + IN HANDLE hSendCompletion +) +{ + // The sample is not sending any packets. + // So send packet completion should + // not be called. + ASSERTFAILURE(); + + // The freeing function used here + // should be the reverse of the function + // used to allocate memory before calling + // Send Packet. + PrivateMemoryFree( (LPVOID) hSendCompletion ); + return ERROR_SUCCESS; +} + + + + +// +// Function to return UI Request on OS query. +// +DWORD +WINAPI +IhvQueryUIRequest +( + IN HANDLE hIhvExtAdapter, + IN DOT11EXT_IHV_CONNECTION_PHASE connectionPhase, + OUT PDOT11EXT_IHV_UI_REQUEST* ppIhvUIRequest +) +{ + // This IHV handler is a post Vista extensibility point. + // It is currently unused. + ASSERTFAILURE(); + + UNREFERENCED_PARAMETER( hIhvExtAdapter ); + UNREFERENCED_PARAMETER( connectionPhase ); + UNREFERENCED_PARAMETER( ppIhvUIRequest ); + + return ERROR_CALL_NOT_IMPLEMENTED; +} + + + + +// +// Handler to receive the Onex Result. +// +DWORD +WINAPI +IhvOnexIndicateResult +( + IN HANDLE hIhvExtAdapter, + IN DOT11_MSONEX_RESULT msOneXResult, + IN PDOT11_MSONEX_RESULT_PARAMS pDot11MsOneXResultParams +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + PADAPTER_DETAILS pAdapterDetails = NULL; + + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + + dwResult = + ReferenceAdapterDetails + ( + hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + + // If indication of result is required. + if ( pAdapterDetails->pOnexData && pAdapterDetails->pOnexData->lpfnIndicateResult ) + { + dwResult = + (pAdapterDetails->pOnexData->lpfnIndicateResult) + ( + pAdapterDetails, + msOneXResult, + pDot11MsOneXResultParams + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + } + else + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + +error: + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + + +// +// Handler to receive Control. +// +DWORD +WINAPI +IhvControl +( + IN HANDLE hIhvExtAdapter, + IN DWORD dwInBufferSize, + IN PBYTE pInBuffer, + IN DWORD dwOutBufferSize, + OUT PBYTE pOutBuffer, + OUT PDWORD pdwBytesReturned +) +{ + // Sample does not demonstrate use of IHV control. + + UNREFERENCED_PARAMETER( hIhvExtAdapter ); + UNREFERENCED_PARAMETER( dwInBufferSize ); + UNREFERENCED_PARAMETER( pInBuffer ); + UNREFERENCED_PARAMETER( dwOutBufferSize ); + UNREFERENCED_PARAMETER( pOutBuffer ); + UNREFERENCED_PARAMETER( pdwBytesReturned ); + + return ERROR_SUCCESS; +} + + diff --git a/network/wlan/ihvsample/ihvsample.def b/network/wlan/ihvsample/ihvsample.def new file mode 100644 index 00000000..dbbfe637 --- /dev/null +++ b/network/wlan/ihvsample/ihvsample.def @@ -0,0 +1,8 @@ +; Test Ihv Extensibility DLL + +LIBRARY IhvSample.dll + +EXPORTS + DllMain + Dot11ExtIhvGetVersionInfo + Dot11ExtIhvInitService diff --git a/network/wlan/ihvsample/ihvsample.h b/network/wlan/ihvsample/ihvsample.h new file mode 100644 index 00000000..0b0484bd --- /dev/null +++ b/network/wlan/ihvsample/ihvsample.h @@ -0,0 +1,261 @@ + + + + +// +// IHV can start defining reason codes in the IHV range. +// +enum +{ + L2_REASON_CODE_IHV_BAD_USER_KEY = L2_REASON_CODE_IHV_BASE, + L2_REASON_CODE_IHV_OUTOFMEMORY, + L2_REASON_CODE_IHV_BAD_PROFILE, + L2_REASON_CODE_IHV_HARDWARE_FAILURE, + L2_REASON_CODE_IHV_ONEX_FAILURE, + L2_REASON_CODE_IHV_INVALID_STATE +}; + + + + + + +//////////////////////////////////// +// IHV provided Handler functions // +//////////////////////////////////// + + + +VOID +WINAPI +IhvDeinitService +( + VOID +); + + + + +DWORD +WINAPI +IhvInitAdapter +( + IN PDOT11_ADAPTER pDot11Adapter, + IN HANDLE hDot11SvcHandle, + OUT PHANDLE phIhvExtAdapter +); + + + + +VOID +WINAPI +IhvDeinitAdapter +( + IN HANDLE hIhvExtAdapter +); + + + + +DWORD +WINAPI +IhvProcessSessionChange +( + IN ULONG uEventType, + IN PWTSSESSION_NOTIFICATION pSessionNotification +); + + + + +DWORD +WINAPI +IhvIsUIRequestPending +( + IN GUID guidUIRequest, + OUT PBOOL pbIsRequestPending +); + + + + +DWORD +WINAPI +IhvReceiveIndication +( + IN HANDLE hIhvExtAdapter, + IN DOT11EXT_IHV_INDICATION_TYPE indicationType, + IN ULONG uBufferLength, + IN LPVOID pvBuffer +); + + + + +DWORD +WINAPI +IhvPerformCapabilityMatch +( + IN HANDLE hIhvExtAdapter, + IN PDOT11EXT_IHV_PROFILE_PARAMS pIhvProfileParams, + IN PDOT11EXT_IHV_CONNECTIVITY_PROFILE pIhvConnProfile, + IN PDOT11EXT_IHV_SECURITY_PROFILE pIhvSecProfile, + IN PDOT11_BSS_LIST pConnectableBssid, + OUT PDWORD pdwReasonCode +); + + + + +DWORD +WINAPI +IhvValidateProfile +( + IN HANDLE hIhvExtAdapter, + IN PDOT11EXT_IHV_PROFILE_PARAMS pIhvProfileParams, + IN PDOT11EXT_IHV_CONNECTIVITY_PROFILE pIhvConnProfile, + IN PDOT11EXT_IHV_SECURITY_PROFILE pIhvSecProfile, + OUT PDWORD pdwReasonCode +); + + + + +DWORD +WINAPI +IhvPerformPreAssociate +( + IN HANDLE hIhvExtAdapter, + IN HANDLE hConnectSession, + IN PDOT11EXT_IHV_PROFILE_PARAMS pIhvProfileParams, + IN PDOT11EXT_IHV_CONNECTIVITY_PROFILE pIhvConnProfile, + IN PDOT11EXT_IHV_SECURITY_PROFILE pIhvSecProfile, + IN PDOT11_BSS_LIST pConnectableBssid, + OUT PDWORD pdwReasonCode +); + + + + +DWORD +WINAPI +IhvPerformPostAssociate +( + IN HANDLE hIhvExtAdapter, + IN HANDLE hSecuritySessionID, + IN PDOT11_PORT_STATE pPortState, + IN ULONG uDot11AssocParamsBytes, + IN PDOT11_ASSOCIATION_COMPLETION_PARAMETERS pDot11AssocParams +); + + + + +DWORD +WINAPI +IhvAdapterReset +( + IN HANDLE hIhvExtAdapter +); + + + + +DWORD +WINAPI +IhvStopPostAssociate +( + IN HANDLE hIhvExtAdapter, + IN PDOT11_MAC_ADDRESS pPeer, + IN DOT11_ASSOC_STATUS dot11AssocStatus +); + + + + + +DWORD +WINAPI +IhvReceivePacket +( + IN HANDLE hIhvExtAdapter, + IN DWORD dwInBufferSize, + IN LPVOID pvInBuffer +); + + + + +DWORD +WINAPI +IhvCreateDiscoveryProfiles +( + IN HANDLE hIhvExtAdapter, + IN BOOL bInsecure, + IN PDOT11EXT_IHV_PROFILE_PARAMS pIhvProfileParams, + IN PDOT11_BSS_LIST pConnectableBssid, + OUT PDOT11EXT_IHV_DISCOVERY_PROFILE_LIST pIhvDiscoveryProfileList, + OUT PDWORD pdwReasonCode +); + + + + +DWORD +WINAPI +IhvProcessUIResponse +( + IN GUID guidUIRequest, + IN DWORD dwByteCount, + IN LPVOID pvResponseBuffer +); + + + + +DWORD +WINAPI +IhvSendPacketCompletion +( + IN HANDLE hSendCompletion +); + + + + +DWORD +WINAPI +IhvQueryUIRequest +( + IN HANDLE hIhvExtAdapter, + IN DOT11EXT_IHV_CONNECTION_PHASE connectionPhase, + OUT PDOT11EXT_IHV_UI_REQUEST* ppIhvUIRequest +); + + + + +DWORD +WINAPI +IhvOnexIndicateResult +( + IN HANDLE hIhvExtAdapter, + IN DOT11_MSONEX_RESULT msOneXResult, + IN PDOT11_MSONEX_RESULT_PARAMS pDot11MsOneXResultParams +); + + + + +DWORD +WINAPI +IhvControl +( + IN HANDLE hIhvExtAdapter, + IN DWORD dwInBufferSize, + IN PBYTE pInBuffer, + IN DWORD dwOutBufferSize, + OUT PBYTE pOutBuffer, + OUT PDWORD pdwBytesReturned +); diff --git a/network/wlan/ihvsample/ihvsample.vcxproj b/network/wlan/ihvsample/ihvsample.vcxproj new file mode 100644 index 00000000..8b0dde8a --- /dev/null +++ b/network/wlan/ihvsample/ihvsample.vcxproj @@ -0,0 +1,254 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {C2DB15CB-342E-4E84-BFD0-5E839DE380DE} + $(MSBuildProjectName) + Debug + Win32 + {D58AEDC0-211B-4E2E-B92B-D297D272865F} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + ihvsample + + + ihvsample + + + ihvsample + + + ihvsample + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + + + + + %(AdditionalDependencies);kernel32.lib;ntdll.lib;advapi32.lib;rpcrt4.lib;uuid.lib;user32.lib;wmip.lib;ole32.lib;oleaut32.lib;ws2_32.lib;..\ihvfrm\$(DDKPlatform)\rc4utils.lib + ihvsample.def + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + + + %(AdditionalDependencies);kernel32.lib;ntdll.lib;advapi32.lib;rpcrt4.lib;uuid.lib;user32.lib;wmip.lib;ole32.lib;oleaut32.lib;ws2_32.lib;..\ihvfrm\$(DDKPlatform)\rc4utils.lib + ihvsample.def + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + + + %(AdditionalDependencies);kernel32.lib;ntdll.lib;advapi32.lib;rpcrt4.lib;uuid.lib;user32.lib;wmip.lib;ole32.lib;oleaut32.lib;ws2_32.lib;..\ihvfrm\$(DDKPlatform)\rc4utils.lib + ihvsample.def + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + + + %(AdditionalDependencies);kernel32.lib;ntdll.lib;advapi32.lib;rpcrt4.lib;uuid.lib;user32.lib;wmip.lib;ole32.lib;oleaut32.lib;ws2_32.lib;..\ihvfrm\$(DDKPlatform)\rc4utils.lib + ihvsample.def + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + + + %(AdditionalIncludeDirectories);$(WDKContentRoot)\net\wlan\ihvfrm\test\rc4utils + + + + + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/wlan/ihvsample/ihvsample.vcxproj.Filters b/network/wlan/ihvsample/ihvsample.vcxproj.Filters new file mode 100644 index 00000000..7a93e51c --- /dev/null +++ b/network/wlan/ihvsample/ihvsample.vcxproj.Filters @@ -0,0 +1,43 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C3AD44B7-58F4-43D6-8928-4E57B6E0C023} + + + h;hpp;hxx;hm;inl;inc;xsd + {76ABC86D-F7B4-4BB5-8600-84AEDE01171B} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {25477ACB-EF62-4C7A-9AAC-8DF8119A6070} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/network/wlan/ihvsample/ihvtimelymachineoruser.xml b/network/wlan/ihvsample/ihvtimelymachineoruser.xml new file mode 100644 index 00000000..d115ee11 --- /dev/null +++ b/network/wlan/ihvsample/ihvtimelymachineoruser.xml @@ -0,0 +1,64 @@ + + + ihvtimelymachineoruser + + + _1x_SSID_ + + false + + ESS + auto + false + + + + open + WEP + true + + + 3 + machineOrUser + + + postLogon + 50 + + + + + 25 + 0 + 0 + 0 + + + + + + + + + + 123456 + 01 + + + + 0 + + + + + + FALSE + IHVAuthV1 + IHVCipher1 + 0 + + + + true + + diff --git a/network/wlan/ihvsample/ihvtimelyuser.xml b/network/wlan/ihvsample/ihvtimelyuser.xml new file mode 100644 index 00000000..83af761f --- /dev/null +++ b/network/wlan/ihvsample/ihvtimelyuser.xml @@ -0,0 +1,63 @@ + + + ihvtimelyuser + + + _1x_SSID_ + + false + + ESS + auto + false + + + + open + WEP + true + + + 3 + user + + postLogon + 50 + + + + + 25 + 0 + 0 + 0 + + + + + + + + + + 123456 + 01 + + + + 0 + + + + + + FALSE + IHVAuthV1 + IHVCipher1 + 0 + + + + true + + diff --git a/network/wlan/ihvsample/ihvwep.cpp b/network/wlan/ihvsample/ihvwep.cpp new file mode 100644 index 00000000..3f4b1c6d --- /dev/null +++ b/network/wlan/ihvsample/ihvwep.cpp @@ -0,0 +1,837 @@ +/*++ + +Copyright (c) 2005 Microsoft Corporation + +Abstract: + + Sample IHV Extensibility DLL to extend + 802.11 LWF driver for third party protocols. + + +--*/ + +#include "precomp.h" + + + +// +// UI Request structure to +// be parsed by IHV UI DLL. +// +typedef struct _IHV_UI_REQUEST +{ + CHAR szTitle [80]; + CHAR szHelp [80]; +} +IHV_UI_REQUEST, *PIHV_UI_REQUEST; + + +DWORD +ConvertHexCharToNibble +( + CHAR chData, + BOOL bUpper, + PBYTE pbtData +) +{ + DWORD dwResult = ERROR_SUCCESS; + BYTE btNibble = 0xF0; + + ASSERT( pbtData ); + + if ( (chData >= '0') && (chData <= '9') ) + { + btNibble = (BYTE) (chData - '0'); + } + else if ( (chData >= 'a') && (chData <= 'f') ) + { + btNibble = (BYTE) (chData - 'a') + 0xA; + } + else if ( (chData >= 'A') && (chData <= 'F') ) + { + btNibble = (BYTE) (chData - 'A') + 0xA; + } + else + { + // Wrong type input + dwResult = ERROR_BAD_FORMAT; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + ASSERT( btNibble < 0xF0 ); + + if ( bUpper ) + { + (*pbtData) = (*pbtData) & 0x0F; + (*pbtData) = (*pbtData) | (btNibble << 4); + } + else + { + (*pbtData) = (*pbtData) & 0xF0; + (*pbtData) = (*pbtData) | btNibble; + } + +error: + return dwResult; +} + + +// For a 13 byte key, user can choose to input upto 26 hex digits + +#define MAX_KEY_STRING_LENGTH 26 + +#define MAX_RESPONSE_SIZE (( MAX_KEY_STRING_LENGTH + 1 ) * sizeof( WCHAR ) ) + +// +// The UI Response is a UNICODE string since +// the UI module has just passed a BSTR to this +// module. This UNICODE string needs to converted +// to a key. Any IHV specific algorithm can be +// used here. +// + +DWORD +ConvertStringToKey +( + BYTE* pbKeyData, + DWORD* pdwKeyLen +) +{ + DWORD dwResult = ERROR_SUCCESS; + HRESULT hr = S_OK; + CHAR szKey[ MAX_KEY_STRING_LENGTH + 2 ] = {0}; + DWORD dwKeyStringLen = 0; + DWORD dwIndex = 0; + + + if + ( + (!pbKeyData) || + (!pdwKeyLen) || + ( 0 == (*pdwKeyLen) ) || + ( (*pdwKeyLen) % sizeof( WCHAR ) ) + ) + { + dwResult = ERROR_INVALID_PARAMETER; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + dwKeyStringLen = (DWORD) wcslen( (LPWSTR) pbKeyData ); + if ( MAX_KEY_STRING_LENGTH < dwKeyStringLen ) + { + dwResult = ERROR_BAD_FORMAT; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // Converting the UNICODE string to + // ANSI string in scratch pad. + hr = + StringCchPrintfA + ( + szKey, + MAX_KEY_STRING_LENGTH + 1, + "%S", + (WCHAR*) pbKeyData + ); + BAIL_ON_FAILURE( hr ); + + ASSERT( dwKeyStringLen == (DWORD) strlen( szKey )); + + + if ( ( 5 == dwKeyStringLen ) || ( 13 == dwKeyStringLen ) ) + { + // Copying the ANSI string back to original buffer. + hr = + StringCchPrintfA + ( + (CHAR*) pbKeyData, + (*pdwKeyLen), + "%s", + szKey + ); + BAIL_ON_FAILURE( hr ); + + // The strings are direct representations + // of the Wep Key and can be directly returned. + (*pdwKeyLen) = dwKeyStringLen; + + TRACE_MESSAGE_VAL( "Received WEP key of length = ", (*pdwKeyLen) ); + + BAIL( ); + } + + if (( 10 != dwKeyStringLen ) && ( 26 != dwKeyStringLen )) + { + // Wrong length input + dwResult = ERROR_BAD_FORMAT; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + for( dwIndex = 0; dwIndex < (dwKeyStringLen / 2); dwIndex++ ) + { + dwResult = + ConvertHexCharToNibble + ( + szKey[ 2 * dwIndex ], + TRUE, + &(pbKeyData[ dwIndex ]) + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + dwResult = + ConvertHexCharToNibble + ( + szKey[ 1 + (2 * dwIndex) ], + FALSE, + &(pbKeyData[ dwIndex ]) + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + } + + (*pdwKeyLen) = dwKeyStringLen / 2; + + TRACE_MESSAGE_VAL( "Received WEP key of length = ", (*pdwKeyLen) ); + +error: + return WIN32_COMBINED_ERROR( dwResult, hr ); +} + + + + + +// +// Defining UI help strings. In a realistic +// implementation these values would be numbers +// to be interpreted the IHV UI DLL that could +// be converted to strings using resource files. +// +#define UI_TITLE_STRING "Title" +#define UI_HELP_STRING "Help" + + + + +// Send the UI request and wait for the UI response. +DWORD +SendUIRequestToReceiveKey +( + PADAPTER_DETAILS pAdapterDetails, + DWORD* pdwKeyLen, + BYTE** ppbKeyData +) +{ + DWORD dwResult = ERROR_SUCCESS; + DOT11EXT_IHV_UI_REQUEST uiRequest = {0}; + PIHV_UI_REQUEST pIHVRequest = NULL; + CHAR szTitle[] = UI_TITLE_STRING; + CHAR szHelp[] = UI_HELP_STRING; + BOOL bLocked = FALSE; + HANDLE hUIResponse = NULL; + + + + // CLSID of COM class that implements the UI page. In a real + // implementation this GUID could be dynamically obtained. + CLSID uiPageClsid = + { + /* 4A01F9F9-6012-4343-A8C4-10B5DF32672A */ + 0x4A01F9F9, + 0x6012, + 0x4343, + {0xA8, 0xC4, 0x10, 0xB5, 0xDF, 0x32, 0x67, 0x2A} + }; + + ASSERT( pAdapterDetails ); + + // prepare the IHV request. + uiRequest.dwByteCount = sizeof(IHV_UI_REQUEST); + uiRequest.pvUIRequest = (BYTE*) PrivateMemoryAlloc( sizeof(IHV_UI_REQUEST) ); + if ( !(uiRequest.pvUIRequest) ) + { + dwResult = ERROR_OUTOFMEMORY; + BAIL( ); + } + + pIHVRequest = (IHV_UI_REQUEST*)uiRequest.pvUIRequest; + + + memcpy( pIHVRequest->szTitle , szTitle , sizeof(szTitle) ); + memcpy( pIHVRequest->szHelp , szHelp , sizeof(szHelp) ); + + uiRequest.dwSessionId = WTSGetActiveConsoleSessionId( ); + uiRequest.UIPageClsid = uiPageClsid; + + // acquire the lock to register the request. + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // create new request guid. + dwResult = UuidCreate( &(uiRequest.guidUIRequest) ); + BAIL_ON_WIN32_ERROR(dwResult); + + // free the existing response. + PrivateMemoryFree( pAdapterDetails->pbResponse ); + pAdapterDetails->pbResponse = NULL; + + // register the guid. + pAdapterDetails->currentGuidUIRequest = uiRequest.guidUIRequest; + + // Initializing the event this thread + // would be waiting on later. + ResetEvent( pAdapterDetails->hUIResponse ); + + hUIResponse = pAdapterDetails->hUIResponse; + + // leave the lock since this thread needs + // to wait for the response. + LeaveCriticalSection( &g_csSynch ); + bLocked = FALSE; + + + // send the request. + dwResult = + (g_pDot11ExtApi->Dot11ExtSendUIRequest) + ( + pAdapterDetails->hDot11SvcHandle, + &uiRequest + ); + BAIL_ON_WIN32_ERROR(dwResult); + + TRACE_MESSAGE( "Sent UI request to receive key." ); + + // Waiting for UI response. + // This would be triggered + // off if no UI response + // is received. + dwResult = + WaitForSingleObject + ( + hUIResponse, + 1000 * 60 * 5 // 5 minutes + ); + + // acquire the lock - required for both success and failure. + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + ZeroMemory( &(pAdapterDetails->currentGuidUIRequest), sizeof( GUID ) ); + + if ( WAIT_OBJECT_0 == dwResult ) + { + dwResult = ERROR_SUCCESS; + } + BAIL_ON_WIN32_ERROR(dwResult); + + if ( NULL == pAdapterDetails->pbResponse ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR(dwResult); + } + + + // At this point in the code a response + // has been received, and the thread + // has not been aborted. + + (*ppbKeyData) = pAdapterDetails->pbResponse; + pAdapterDetails->pbResponse = NULL; + + (*pdwKeyLen) = pAdapterDetails->dwResponseLen; + + + // Convert the Unicode string to ASCII. + dwResult = + ConvertStringToKey + ( + *ppbKeyData, + pdwKeyLen + ); + BAIL_ON_WIN32_ERROR(dwResult); + + pAdapterDetails->bModifyCurrentProfile = TRUE; + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + PrivateMemoryFree( uiRequest.pvUIRequest ); + return dwResult; +} + + +// +// Key index limits. +// +#define MIN_KEY_INDEX 0 +#define MAX_KEY_INDEX 3 + + +// +// Perform Wep based pre-association once the key is known. +// +DWORD +WINAPI +DoWepPreAssociateCommon +( + PADAPTER_DETAILS pAdapterDetails, + DWORD dwKeyLen, + BYTE* pbKeyData, + DWORD* pdwReasonCode +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + LONG lIndex = 0; + PDOT11_CIPHER_DEFAULT_KEY_VALUE pKey = 0; + ULONG uLen = 0; + + ASSERT( pAdapterDetails ); + ASSERT( dwKeyLen ); + ASSERT( pbKeyData ); + ASSERT( pdwReasonCode ); + + // Reason code is set before making calls that could fail. + (*pdwReasonCode) = L2_REASON_CODE_IHV_OUTOFMEMORY; + + uLen = FIELD_OFFSET(DOT11_CIPHER_DEFAULT_KEY_VALUE, ucKey) + dwKeyLen * sizeof(UCHAR); + pKey = (PDOT11_CIPHER_DEFAULT_KEY_VALUE) PrivateMemoryAlloc(uLen); + if (!pKey) + { + dwResult = ERROR_OUTOFMEMORY; + BAIL_ON_WIN32_ERROR(dwResult); + } + CopyMemory( &(pKey->ucKey), pbKeyData, dwKeyLen ); + + // Prepare the key. + pKey->AlgorithmId = DOT11_CIPHER_ALGO_WEP; + pKey->usKeyLength = (USHORT) dwKeyLen; + pKey->bStatic = TRUE; + pKey->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + pKey->Header.Revision = DOT11_CIPHER_DEFAULT_KEY_VALUE_REVISION_1; + pKey->Header.Size = sizeof(DOT11_CIPHER_DEFAULT_KEY_VALUE); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // Reason code is set before making calls that could fail. + (*pdwReasonCode) = L2_REASON_CODE_IHV_INVALID_STATE; + + if ( nic_state_pre_assoc_started != pAdapterDetails->NicState ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // Reason code is set before making calls that could fail. + (*pdwReasonCode) = L2_REASON_CODE_IHV_HARDWARE_FAILURE; + + // plumb the settings and keys down. + + TRACE_MESSAGE( "Setting Auth Algorithm." ); + dwResult = + (g_pDot11ExtApi->Dot11ExtSetAuthAlgorithm) + ( + pAdapterDetails->hDot11SvcHandle, + DOT11_AUTH_ALGO_80211_OPEN + ); + BAIL_ON_WIN32_ERROR(dwResult); + + TRACE_MESSAGE( "Setting Unicast cipher algorithm." ); + dwResult = + (g_pDot11ExtApi->Dot11ExtSetUnicastCipherAlgorithm) + ( + pAdapterDetails->hDot11SvcHandle, + DOT11_CIPHER_ALGO_WEP + ); + BAIL_ON_WIN32_ERROR(dwResult); + + TRACE_MESSAGE( "Setting exclude unencrypted flag." ); + dwResult = + (g_pDot11ExtApi->Dot11ExtSetExcludeUnencrypted) + ( + pAdapterDetails->hDot11SvcHandle, + TRUE + ); + BAIL_ON_WIN32_ERROR(dwResult); + + for ( lIndex = MAX_KEY_INDEX; lIndex >= MIN_KEY_INDEX; lIndex-- ) + { + pKey->uKeyIndex = lIndex; + + TRACE_MESSAGE( "Setting default key." ); + + dwResult = + (g_pDot11ExtApi->Dot11ExtSetDefaultKey) + ( + pAdapterDetails->hDot11SvcHandle, + pKey, + DOT11_DIR_BOTH + ); + BAIL_ON_WIN32_ERROR(dwResult); + } + + TRACE_MESSAGE( "Setting default key ID." ); + dwResult = + (g_pDot11ExtApi->Dot11ExtSetDefaultKeyId) + ( + pAdapterDetails->hDot11SvcHandle, + 0 + ); + BAIL_ON_WIN32_ERROR(dwResult); + + + // Verified before, just after acquiring lock. + ASSERT( nic_state_pre_assoc_started == pAdapterDetails->NicState ); + + pAdapterDetails->NicState = nic_state_pre_assoc_ended; + + // Reason code is set to SUCCESS. + (*pdwReasonCode) = L2_REASON_CODE_SUCCESS; + + // register the post-association handlers with the adapter. + + pAdapterDetails->pPerformPostAssociateCompletionRoutine = DoWepPostAssociate; + pAdapterDetails->pPerformPostAssociateRoutine = NULL; + pAdapterDetails->pStopPostAssociateRoutine = NULL; + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + PrivateMemoryFree( pKey ); + return dwResult; +} + + +// Pre-association when the profile does not have the key. +DWORD +WINAPI +DoMissingKeyWepPreAssociate +( + PADAPTER_DETAILS pAdapterDetails, + DWORD* pdwReasonCode +) +{ + DWORD dwResult = ERROR_SUCCESS; + DWORD dwKeyLen = ERROR_SUCCESS; + BYTE* pbKeyData = NULL; + + ASSERT( pAdapterDetails ); + ASSERT( pdwReasonCode ); + + // Reason code is set before making calls that could fail. + (*pdwReasonCode) = L2_REASON_CODE_IHV_BAD_USER_KEY; + + // Possible enhancement - try to call Dot11ExtGetProfileCustomUserData + // function in IHV Framework to see if the key is + // available there. Else, send UI Request. + + + // try to obtain the key through an UI request. + dwResult = + SendUIRequestToReceiveKey + ( + pAdapterDetails, + &dwKeyLen, + &pbKeyData + ); + BAIL_ON_WIN32_ERROR(dwResult); + + ASSERT ( pbKeyData ); + ASSERT ( dwKeyLen ); + + // use the key for connection. + dwResult = + DoWepPreAssociateCommon + ( + pAdapterDetails, + dwKeyLen, + pbKeyData, + pdwReasonCode + ); + BAIL_ON_WIN32_ERROR(dwResult); + + + // Possible enhancement - try to call Dot11ExtSetProfileCustomUserData + // to store the key if the key was obtained by a UI request. + +error: + PrivateMemoryFree( pbKeyData ); + return dwResult; +} + + + + + + + +// Pre-association when the profile does have the key. +DWORD +WINAPI +DoWepPreAssociate +( + PADAPTER_DETAILS pAdapterDetails, + DWORD* pdwReasonCode +) +{ + DWORD dwResult = ERROR_SUCCESS; + BOOL bLocked = FALSE; + DWORD dwKeyLen = ERROR_SUCCESS; + CHAR szKey[MAX_RESPONSE_SIZE+1] = {0}; + + ASSERT( pAdapterDetails ); + ASSERT( pdwReasonCode ); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // Reason code is set before making calls that could fail. + (*pdwReasonCode) = L2_REASON_CODE_IHV_BAD_USER_KEY; + + if + ( + (!( pAdapterDetails->pConnectivityProfile )) || + (!( pAdapterDetails->pConnectivityProfile->pszParam2 )) || + ( 0 == pAdapterDetails->pConnectivityProfile->pszParam2[0] ) + ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR(dwResult); + } + + // Convert the profile string to a usable key. + dwKeyLen = (DWORD)wcslen( pAdapterDetails->pConnectivityProfile->pszParam2 ); + dwKeyLen = sizeof(WCHAR) * ( 1 + dwKeyLen ); + + if ( dwKeyLen > sizeof( szKey ) ) + { + dwResult = ERROR_BAD_PROFILE; + BAIL_ON_WIN32_ERROR(dwResult); + } + + + // Copy string. + CopyMemory + ( + (BYTE*) szKey, + pAdapterDetails->pConnectivityProfile->pszParam2, + dwKeyLen + ); + + // Convert UNICODE to ASCII. + dwResult = + ConvertStringToKey + ( + (BYTE*) szKey, + &dwKeyLen + ); + BAIL_ON_WIN32_ERROR(dwResult); + ASSERT ( dwKeyLen ); + + + // Do pre-association with the key in the profile. + dwResult = + DoWepPreAssociateCommon + ( + pAdapterDetails, + dwKeyLen, + (BYTE*) szKey, + pdwReasonCode + ); + BAIL_ON_WIN32_ERROR(dwResult); + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + + + +extern +DOT11EXT_IHV_DISCOVERY_PROFILE +g_IhvDiscoveryProfiles[]; + + +// +// This function is responsible for the post association +// operations and completing the post association call +// for WEP scenario. +// +DWORD +WINAPI +DoWepPostAssociate +( + LPVOID pvPostAssociate +) +{ + DWORD dwResult = ERROR_SUCCESS; + DWORD dwStatus = ERROR_SUCCESS; + DWORD dwReasonCode = L2_REASON_CODE_IHV_INVALID_STATE; + BOOL bLocked = FALSE; + PPOST_ASSOC_DATA ppostAssocData = (PPOST_ASSOC_DATA) pvPostAssociate; + PADAPTER_DETAILS pAdapterDetails = NULL; + + + ASSERT( ppostAssocData ); + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + dwResult = + ReferenceAdapterDetails + ( + ppostAssocData->hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + + if ( nic_state_post_assoc_started != pAdapterDetails->NicState ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + // This could be an appropriate place to modify the current profile + + if ( pAdapterDetails->bModifyCurrentProfile ) + { + pAdapterDetails->bModifyCurrentProfile = FALSE; + + dwResult = + (g_pDot11ExtApi->Dot11ExtSetCurrentProfile) + ( + pAdapterDetails->hDot11SvcHandle, + pAdapterDetails->hConnectSession, + &(g_IhvDiscoveryProfiles[1].IhvConnectivityProfile), + &(g_IhvDiscoveryProfiles[1].IhvSecurityProfile) + ); + BAIL_ON_WIN32_ERROR( dwResult ); + } + + + // In wep connection case, function only changes the adapter state. + pAdapterDetails->NicState = nic_state_post_assoc_ended; + + + // Reason Code is set to success. + dwReasonCode = L2_REASON_CODE_SUCCESS; + +error: + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( ppostAssocData->hIhvExtAdapter ); + } + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + + // call completion function. + dwStatus = + (g_pDot11ExtApi->Dot11ExtPostAssociateCompletion) + ( + ppostAssocData->hDot11SvcHandle, + ppostAssocData->hSecuritySessionId, + NULL, + dwReasonCode, + dwResult + ); + if ( ERROR_SUCCESS != dwStatus ) + { + // IHV specific logging can happen here. + } + PrivateMemoryFree( ppostAssocData ); + return dwResult; +} + + +// no op function for preassociation when +// ihv is used only for connectivity. in a +// realistic implementation there would probably +// be calls to Dot11ExtNicSpecificExtension +// in this function to prepare the driver for +// additional connectivity settings. +DWORD +WINAPI +DoIhvConnPreAssociate +( + PADAPTER_DETAILS pAdapterDetails, + DWORD* pdwReasonCode +) +{ + DWORD dwResult = ERROR_SUCCESS; + DWORD dwKeyLen = 0; + PBYTE pbKeyData = NULL; + BOOL bLocked = FALSE; + + ASSERT( pAdapterDetails ); + ASSERT( pdwReasonCode ); + + // try to send UI request to obtain some data that could be useful here. + // the sample does not really use the data. + dwResult = + SendUIRequestToReceiveKey + ( + pAdapterDetails, + &dwKeyLen, + &pbKeyData + ); + BAIL_ON_WIN32_ERROR(dwResult); + ASSERT ( pbKeyData ); + + PrivateMemoryFree( pbKeyData ); + pbKeyData = NULL; + + // try to send UI request to obtain some data that could be useful here. + // the sample does not really use the data. + dwResult = + SendUIRequestToReceiveKey + ( + pAdapterDetails, + &dwKeyLen, + &pbKeyData + ); + BAIL_ON_WIN32_ERROR(dwResult); + ASSERT ( pbKeyData ); + + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + // Reason code is set before making calls that could fail. + (*pdwReasonCode) = L2_REASON_CODE_IHV_INVALID_STATE; + + if ( nic_state_pre_assoc_started != pAdapterDetails->NicState ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + pAdapterDetails->NicState = nic_state_pre_assoc_ended; + + // Reason code is set to SUCCESS. + (*pdwReasonCode) = L2_REASON_CODE_SUCCESS; + + pAdapterDetails->pPerformPostAssociateCompletionRoutine = NULL; + pAdapterDetails->pPerformPostAssociateRoutine = NULL; + pAdapterDetails->pStopPostAssociateRoutine = NULL; + +error: + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + PrivateMemoryFree( pbKeyData ); + return dwResult; +} diff --git a/network/wlan/ihvsample/ihvwep.h b/network/wlan/ihvsample/ihvwep.h new file mode 100644 index 00000000..d8aabe71 --- /dev/null +++ b/network/wlan/ihvsample/ihvwep.h @@ -0,0 +1,62 @@ + +// +// Structure to marshal information for +// postassociation thread. +// +typedef +struct _POST_ASSOC_DATA +{ + HANDLE hIhvExtAdapter; + HANDLE hDot11SvcHandle; + HANDLE hSecuritySessionId; +} +POST_ASSOC_DATA, *PPOST_ASSOC_DATA; + + +// Pre-association when the profile does have the key. +DWORD +WINAPI +DoWepPreAssociate +( + PADAPTER_DETAILS pAdapterDetails, + DWORD* pdwReasonCode +); + + +// Pre-association when the profile does not have the key. +DWORD +WINAPI +DoMissingKeyWepPreAssociate +( + PADAPTER_DETAILS pAdapterDetails, + DWORD* pdwReasonCode +); + + +// no op function for preassociation when +// ihv is used only for connectivity. in a +// realistic implementation there would probably +// be calls to Dot11ExtNicSpecificExtension +// in this function to prepare the driver for +// additional connectivity settings. +DWORD +WINAPI +DoIhvConnPreAssociate +( + PADAPTER_DETAILS pAdapterDetails, + DWORD* pdwReasonCode +); + + +// This function is responsible for the post association +// operations and completing the post association call +// for WEP scenario. +DWORD +WINAPI +DoWepPostAssociate +( + LPVOID pvPostAssociate +); + + + diff --git a/network/wlan/ihvsample/ihvwep1.xml b/network/wlan/ihvsample/ihvwep1.xml new file mode 100644 index 00000000..2e4403f9 --- /dev/null +++ b/network/wlan/ihvsample/ihvwep1.xml @@ -0,0 +1,54 @@ + + + + ihvwep1 + + + + _SSID_ + + + + ESS + manual + + + + + + + + + + 123456 + 01 + + + + + + 0 + + + + + + + + + + TRUE + + IHVAuthV1 + + IHVCipher1 + + 0 + + + + + + + + diff --git a/network/wlan/ihvsample/ihvwep2.xml b/network/wlan/ihvsample/ihvwep2.xml new file mode 100644 index 00000000..32b538ba --- /dev/null +++ b/network/wlan/ihvsample/ihvwep2.xml @@ -0,0 +1,54 @@ + + + + ihvwep2 + + + + _SSID_ + + + + ESS + manual + + + + + + + + + + 123456 + 01 + + + + + + 0 + + _KEY_ + + + + + + + + TRUE + + IHVAuthV1 + + IHVCipher1 + + 0 + + + + + + + + diff --git a/network/wlan/ihvsample/precomp.h b/network/wlan/ihvsample/precomp.h new file mode 100644 index 00000000..244ad664 --- /dev/null +++ b/network/wlan/ihvsample/precomp.h @@ -0,0 +1,36 @@ + +#ifndef __WLAN_IHV_SAMPLE_PRECOMP_H__ +#define __WLAN_IHV_SAMPLE_PRECOMP_H__ + + +#pragma once + +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "profile.h" +#include "utils.h" +#include "ihvwep.h" +#include "ihvonexext.h" +#include "adapters.h" +#include "rc4utils.h" +#include "ihvsample.h" + +#ifdef __cplusplus +#include +#endif + + + +#endif // __WLAN_IHV_SAMPLE_PRECOMP_H__ + diff --git a/network/wlan/ihvsample/precompsrc.c b/network/wlan/ihvsample/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/network/wlan/ihvsample/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/network/wlan/ihvsample/profile.cpp b/network/wlan/ihvsample/profile.cpp new file mode 100644 index 00000000..c6988990 --- /dev/null +++ b/network/wlan/ihvsample/profile.cpp @@ -0,0 +1,1222 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#include "precomp.h" + + + + +// +// Free BSTR +// +#define SYS_FREE_STRING( _s ) \ + if ( _s ) \ + { \ + SysFreeString( _s ); \ + (_s) = NULL; \ + } \ + + +// +// Release interface. +// +#define RELEASE_INTERFACE( _p ) \ + if ( _p ) \ + { \ + (_p)->Release( ); \ + (_p) = NULL; \ + } \ + + +// XPath strings for parsing xml blobs, + +#define CON_PARAM1_XPATH L"/IhvConnectivity/IHVConnectivityParam1" +#define CON_PARAM2_XPATH L"/IhvConnectivity/IHVConnectivityParam2" + + +#define SEC_FSFLAG_XPATH L"/IhvSecurity/IHVUsesFullSecurity" +#define SEC_ATYPE_XPATH L"/IhvSecurity/IHVAuthentication" +#define SEC_ETYPE_XPATH L"/IhvSecurity/IHVEncryption" +#define SEC_PARAM1_XPATH L"/IhvSecurity/IHVSecurityParam1" +#define SEC_PARAM2_XPATH L"/IhvSecurity/IHVSecurityParam2" + + +// Strings to match the profile with internal data types. +LPCWSTR +gppszIhvAuthTypes[] = +{ + L"IHVAuthV1", + L"IHVAuthV2", + L"IHVAuthV3" +}; + + +LPCWSTR +gppszIhvCipherTypes[] = +{ + L"None", + L"IHVCipher1", + L"IHVCipher2", + L"IHVCipher3" +}; + + +// +// Base class for profile APIs. +// +class CIhvProfileBase +{ +public: + + // Constructor + CIhvProfileBase( ) + { + m_pRootNode = NULL; + } + + // Destructor + ~CIhvProfileBase( ) + { + RELEASE_INTERFACE( m_pRootNode ); + } + + HRESULT + LoadXml + ( + IN BSTR bstrIhvProfile + ); + + + // Caller needs to know what type to + // cast the pointer to depending upon + // the type of the derived class. + // Caller needs to free memory recursively + // by using the free( ) function. + virtual + HRESULT + GetNativeData + ( + LPVOID* ppvData + ) + = 0; + +protected: + + HRESULT + GetTextFromNode + ( + IN LPCWSTR pszQuery, + OUT BSTR* pbstrText + ); + + + IXMLDOMElement* m_pRootNode; + +}; + + + + + +// +// Derived class for connectivity profiles. +// +class CIhvConnectivityProfile : public CIhvProfileBase +{ + +public: + + // Constructor Destructor + CIhvConnectivityProfile( ) { } + ~CIhvConnectivityProfile( ) { } + + // Caller needs to know what type to + // cast the pointer to depending upon + // the type of the derived class. + // Caller needs to free memory recursively + // by using the PrivateMemoryFree( ) function. + HRESULT + GetNativeData + ( + LPVOID* ppvData + ); + + + // Accessor dwParam1 + HRESULT + GetParam1 + ( + DWORD* pdwParam1 + ); + + + // Accessor for pszParam2 + HRESULT + GetParam2 + ( + BSTR* pbstrValue + ); + + +}; + + + + + + +// +// Derived class for security profiles. +// + +class CIhvSecurityProfile + : public CIhvProfileBase +{ + +public: + + // Constructor Destructor + CIhvSecurityProfile( ) { } + ~CIhvSecurityProfile( ) { } + + // Caller needs to know what type to + // cast the pointer to depending upon + // the type of the derived class. + // Caller needs to free memory recursively + // by using the PrivateMemoryFree( ) function. + HRESULT + GetNativeData + ( + LPVOID* ppvData + ); + + + // Accessor and Modifier for bUseFullSecurity + HRESULT + GetFullSecurityFlag + ( + BOOL* pbUseFullSecurity + ); + + + // Accessor for AuthType + HRESULT + GetAuthType + ( + PIHV_AUTH_TYPE pAuthType + ); + + // Accessor for CipherType + HRESULT + GetCipherType + ( + PIHV_CIPHER_TYPE pCipherType + ); + + // Accessor for dwParam1 + HRESULT + GetParam1 + ( + DWORD* pdwParam1 + ); + + // Accessor for pszParam2 + HRESULT + GetParam2 + ( + BSTR* pbstrValue + ); + +}; + + +// Convert unicode string to BSTR. NULL safe. +HRESULT +Wstr2Bstr +( + _In_ LPCWSTR pszSrc, + _Outptr_ BSTR* pbstrDest +) +{ + HRESULT hr = S_OK; + + if ( !pbstrDest ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + (*pbstrDest) = NULL; + if ( !pszSrc ) + { + BAIL( ); + } + + (*pbstrDest) = SysAllocString( pszSrc ); + if ( !(*pbstrDest) ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + +error: + return hr; +} + + + + +// Convert unicode string to unicode string. NULL safe. +// string allocated by malloc and freed by free. +HRESULT +Wstr2Wstr +( + _In_ LPCWSTR pszSrc, + _Outptr_ LPWSTR* ppszDest +) +{ + HRESULT hr = S_OK; + size_t len = 0; + + if ( !ppszDest ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + (*ppszDest) = NULL; + if ( !pszSrc ) + { + BAIL( ); + } + + len = 1 + wcslen( pszSrc ); + len *= sizeof( WCHAR ); + + (*ppszDest) = (LPWSTR) PrivateMemoryAlloc( len ); + if ( !(*ppszDest) ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + + CopyMemory( (*ppszDest), pszSrc, len ); + +error: + return hr; +} + + + +// +// convert unicode string to DWORD +// +HRESULT +Wstr2Dword +( + _In_ LPCWSTR pszSrc, + _Out_ DWORD* pdwDest +) +{ + HRESULT hr = S_OK; + + if ( (!pdwDest) || (!pszSrc) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + (*pdwDest) = (DWORD) _wtol( pszSrc ); + +error: + return hr; +} + + + + +// +// convert unicode string to BOOL +// +HRESULT +Wstr2Bool +( + _In_ LPCWSTR pszSrc, + _Out_ BOOL* pbDest +) +{ + HRESULT hr = S_OK; + + if ( (!pbDest) || (!pszSrc) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + if ( 0 == wcscmp( L"TRUE", pszSrc ) ) + { + (*pbDest) = TRUE; + } + else if ( 0 == wcscmp( L"FALSE", pszSrc ) ) + { + (*pbDest) = FALSE; + } + else + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + +error: + return hr; +} + + + + + + + +// +// convert unicode string to auth type +// +HRESULT +Wstr2AuthType +( + _In_ LPCWSTR pszSrc, + _Out_ PIHV_AUTH_TYPE pAuthType +) +{ + HRESULT hr = S_OK; + DWORD dwIndex = 0; + + if ( (!pAuthType) || (!pszSrc) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + for ( dwIndex = 0; dwIndex < MAX_AUTH_TYPES; dwIndex++ ) + { + if ( 0 == wcscmp( gppszIhvAuthTypes[dwIndex], pszSrc ) ) + { + (*pAuthType) = (IHV_AUTH_TYPE) dwIndex; + BAIL( ); + } + } + + // String not found. + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + + + + + + + +// +// convert unicode string to cipher type +// +HRESULT +Wstr2CipherType +( + _In_ LPCWSTR pszSrc, + _Out_ PIHV_CIPHER_TYPE pCipherType +) +{ + HRESULT hr = S_OK; + DWORD dwIndex = 0; + + if ( (!pCipherType) || (!pszSrc) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + for ( dwIndex = 0; dwIndex < MAX_CIPHER_TYPES; dwIndex++ ) + { + if ( 0 == wcscmp( gppszIhvCipherTypes[dwIndex], pszSrc ) ) + { + (*pCipherType) = (IHV_CIPHER_TYPE) dwIndex; + BAIL( ); + } + } + + // String not found. + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + + + + +// base function to obtain text from +// node described by XPATH. +HRESULT +CIhvProfileBase::GetTextFromNode +( + IN LPCWSTR pszQuery, + OUT BSTR* pbstrText +) +{ + HRESULT hr = S_OK; + BSTR bstrQuery = NULL; + IXMLDOMNode* pQueryNode = NULL; + + ASSERT( pszQuery ); + ASSERT( pbstrText ); + + // if node is NULL, return empty string. + if ( !m_pRootNode ) + { + hr = + Wstr2Bstr + ( + L"", + pbstrText + ); + BAIL( ); + } + + hr = + Wstr2Bstr + ( + pszQuery, + &bstrQuery + ); + BAIL_ON_FAILURE( hr ); + + hr = m_pRootNode->selectSingleNode( bstrQuery, &pQueryNode ); + BAIL_ON_FAILURE( hr ); + + if (!pQueryNode) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + hr = pQueryNode->get_text( pbstrText ); + BAIL_ON_FAILURE( hr ); + + if ( !(*pbstrText) ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + +error: + RELEASE_INTERFACE( pQueryNode ); + SYS_FREE_STRING( bstrQuery ); + return hr; +} + + + +// Load node from xml string. If xml string is null this +// function is a NO_OP. +HRESULT +CIhvProfileBase::LoadXml +( + IN BSTR bstrIhvProfile +) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pDOMDoc = NULL; + IXMLDOMElement* pDocElem = NULL; + VARIANT_BOOL vfSuccess; + + if ( m_pRootNode ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + if ( !bstrIhvProfile ) + { + BAIL( ); + } + + hr = + CoCreateInstance + ( + CLSID_DOMDocument, + NULL, + CLSCTX_ALL, + IID_IXMLDOMDocument, + (LPVOID *) &pDOMDoc + ); + BAIL_ON_FAILURE( hr ); + + hr = + pDOMDoc->loadXML + ( + bstrIhvProfile, + &vfSuccess + ); + BAIL_ON_FAILURE( hr ); + + if ( VARIANT_TRUE != vfSuccess ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + hr = + pDOMDoc->get_documentElement + ( + &pDocElem + ); + BAIL_ON_FAILURE( hr ); + + // Caching the pointer to the document element + // in a member variable. + m_pRootNode = pDocElem; + pDocElem = NULL; + + +error: + RELEASE_INTERFACE( pDOMDoc ); + RELEASE_INTERFACE( pDocElem ); + return hr; +} + + + + + +// Accessor. +HRESULT +CIhvConnectivityProfile::GetParam1 +( + DWORD* pdwParam1 +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + CON_PARAM1_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + if ( NULL == bstrData ) + { + hr = E_POINTER; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Dword + ( + bstrData, + pdwParam1 + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + + + +// Accessor. +HRESULT +CIhvConnectivityProfile::GetParam2 +( + BSTR* pbstrValue +) +{ + HRESULT hr = S_OK; + + hr = + GetTextFromNode + ( + CON_PARAM2_XPATH, + pbstrValue + ); + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + + + + +// +// Calls the accessors to build native data. +// +HRESULT +CIhvConnectivityProfile::GetNativeData +( + LPVOID* ppvData +) +{ + HRESULT hr = S_OK; + PIHV_CONNECTIVITY_PROFILE pIhvProfile = NULL; + BSTR bstrParam2 = NULL; + + if ( !ppvData ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + pIhvProfile = (PIHV_CONNECTIVITY_PROFILE) PrivateMemoryAlloc( sizeof( IHV_CONNECTIVITY_PROFILE ) ); + if ( !pIhvProfile ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + + // Ignoring errors since structure is already + // populated with defaults. + + hr = + GetParam2 + ( + &bstrParam2 + ); + BAIL_ON_FAILURE( hr ); + + if ( NULL == bstrParam2 ) + { + hr = E_POINTER; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Wstr + ( + bstrParam2, + &(pIhvProfile->pszParam2) + ); + BAIL_ON_FAILURE( hr ); + + hr = + GetParam1 + ( + &(pIhvProfile->dwParam1) + ); + + // Consuming earlier failures. + hr = S_OK; + + // Transfering local cache to OUT parameter. + (*ppvData) = pIhvProfile; + pIhvProfile = NULL; + +error: + if ( pIhvProfile ) + { + PrivateMemoryFree( pIhvProfile->pszParam2 ); // NULL Safe. + PrivateMemoryFree( pIhvProfile ); + } + SYS_FREE_STRING( bstrParam2 ); + return hr; +} + + + + + +// Accessor. +HRESULT +CIhvSecurityProfile::GetFullSecurityFlag +( + BOOL* pbUseFullSecurity +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + SEC_FSFLAG_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + if ( NULL == bstrData ) + { + hr = E_POINTER; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Bool + ( + bstrData, + pbUseFullSecurity + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + + + + + +// Accessor. +HRESULT +CIhvSecurityProfile::GetAuthType +( + PIHV_AUTH_TYPE pAuthType +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + SEC_ATYPE_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + if ( NULL == bstrData ) + { + hr = E_POINTER; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2AuthType + ( + bstrData, + pAuthType + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + + + + +// Accessor. +HRESULT +CIhvSecurityProfile::GetCipherType +( + PIHV_CIPHER_TYPE pCipherType +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + SEC_ETYPE_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + if ( NULL == bstrData ) + { + hr = E_POINTER; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2CipherType + ( + bstrData, + pCipherType + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + + + + + +// Accessor. +HRESULT +CIhvSecurityProfile::GetParam1 +( + DWORD* pdwParam1 +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + SEC_PARAM1_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + if ( NULL == bstrData ) + { + hr = E_POINTER; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Dword + ( + bstrData, + pdwParam1 + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + + + + + + +// Accessor. +HRESULT +CIhvSecurityProfile::GetParam2 +( + BSTR* pbstrValue +) +{ + HRESULT hr = S_OK; + + hr = + GetTextFromNode + ( + SEC_PARAM2_XPATH, + pbstrValue + ); + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + + +// +// Calls the accessors to build native data. +// +HRESULT +CIhvSecurityProfile::GetNativeData +( + LPVOID* ppvData +) +{ + HRESULT hr = S_OK; + PIHV_SECURITY_PROFILE pIhvProfile = NULL; + BSTR bstrParam2 = NULL; + + if ( !ppvData ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + pIhvProfile = (PIHV_SECURITY_PROFILE) PrivateMemoryAlloc( sizeof( IHV_SECURITY_PROFILE ) ); + if ( !pIhvProfile ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + + pIhvProfile->bUseIhvConnectivityOnly = ( m_pRootNode == NULL ); + + // Ignoring errors since structure is already + // populated with defaults. + hr = + GetFullSecurityFlag + ( + &(pIhvProfile->bUseFullSecurity) + ); + + hr = + GetAuthType + ( + &(pIhvProfile->AuthType) + ); + + hr = + GetCipherType + ( + &(pIhvProfile->CipherType) + ); + + hr = + GetParam1 + ( + &(pIhvProfile->dwParam1) + ); + + hr = + GetParam2 + ( + &bstrParam2 + ); + BAIL_ON_FAILURE( hr ); + + if ( NULL == bstrParam2 ) + { + hr = E_POINTER; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Wstr + ( + bstrParam2, + &(pIhvProfile->pszParam2) + ); + + // Consuming earlier failures. + hr = S_OK; + + // Transfering local cache to OUT parameter. + (*ppvData) = pIhvProfile; + pIhvProfile = NULL; + +error: + if ( pIhvProfile ) + { + PrivateMemoryFree( pIhvProfile->pszParam2 ); // NULL Safe. + PrivateMemoryFree( pIhvProfile ); + } + SYS_FREE_STRING( bstrParam2 ); + return hr; +} + + +// Converts string to connectivity profile. +DWORD +GetIhvConnectivityProfile +( + PDOT11EXT_IHV_CONNECTIVITY_PROFILE pDot11ExtIhvConnProfile, + PIHV_CONNECTIVITY_PROFILE* ppConnectivityProfile +) +{ + HRESULT hr = S_OK; + BOOL bComInitialized = FALSE; + BSTR bstrIhvProfile = NULL; + PIHV_CONNECTIVITY_PROFILE pConnectivityProfile = NULL; + + CIhvConnectivityProfile* pIhvProfile = NULL; + + ASSERT( pDot11ExtIhvConnProfile ); + ASSERT( ppConnectivityProfile ); + + + // Data structure is multi-thread safe because + // it is both allocated and freed by current + // function. + hr = + CoInitializeEx + ( + NULL, + COINIT_MULTITHREADED + ); + BAIL_ON_FAILURE( hr ); + bComInitialized = TRUE; + + + pIhvProfile = new(std::nothrow) CIhvConnectivityProfile; + if (!pIhvProfile) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Bstr + ( + pDot11ExtIhvConnProfile->pszXmlFragmentIhvConnectivity, + &bstrIhvProfile + ); + BAIL_ON_FAILURE( hr ); + + hr = + pIhvProfile->LoadXml + ( + bstrIhvProfile + ); + BAIL_ON_FAILURE( hr ); + + hr = + pIhvProfile->GetNativeData + ( + (LPVOID*) &pConnectivityProfile + ); + BAIL_ON_FAILURE( hr ); + + + (*ppConnectivityProfile) = pConnectivityProfile; + pConnectivityProfile = NULL; + + +error: + SYS_FREE_STRING( bstrIhvProfile ); + + FreeIhvConnectivityProfile ( &pConnectivityProfile ); + + delete pIhvProfile; + + if ( bComInitialized ) + { + CoUninitialize( ); + } + return WIN32_FROM_HRESULT( hr ); +} + + + + +// free connectivity profile. + +VOID +FreeIhvConnectivityProfile +( + PIHV_CONNECTIVITY_PROFILE* ppConnectivityProfile +) +{ + if ( ppConnectivityProfile && (*ppConnectivityProfile) ) + { + PrivateMemoryFree( (*ppConnectivityProfile)->pszParam2 ); + PrivateMemoryFree( (*ppConnectivityProfile) ); + (*ppConnectivityProfile) = NULL; + } +} + + +// Converts string to security profile. +DWORD +GetIhvSecurityProfile +( + PDOT11EXT_IHV_SECURITY_PROFILE pDot11ExtIhvSecProfile, + PIHV_SECURITY_PROFILE* ppSecurityProfile +) +{ + HRESULT hr = S_OK; + BOOL bComInitialized = FALSE; + BSTR bstrIhvProfile = NULL; + PIHV_SECURITY_PROFILE pSecurityProfile = NULL; + + CIhvSecurityProfile* pIhvProfile = NULL; + + ASSERT( pDot11ExtIhvSecProfile ); + ASSERT( ppSecurityProfile ); + + + // Data structure is multi-thread safe because + // it is both allocated and freed by current + // function. + hr = + CoInitializeEx + ( + NULL, + COINIT_MULTITHREADED + ); + BAIL_ON_FAILURE( hr ); + bComInitialized = TRUE; + + + + pIhvProfile = new(std::nothrow) CIhvSecurityProfile; + if (!pIhvProfile) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Bstr + ( + pDot11ExtIhvSecProfile->pszXmlFragmentIhvSecurity, + &bstrIhvProfile + ); + BAIL_ON_FAILURE( hr ); + + hr = + pIhvProfile->LoadXml + ( + bstrIhvProfile + ); + BAIL_ON_FAILURE( hr ); + + hr = + pIhvProfile->GetNativeData + ( + (LPVOID*) &pSecurityProfile + ); + BAIL_ON_FAILURE( hr ); + + if ( pDot11ExtIhvSecProfile->bUseMSOnex && pSecurityProfile->bUseFullSecurity ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + (*ppSecurityProfile) = pSecurityProfile; + pSecurityProfile = NULL; + + +error: + SYS_FREE_STRING( bstrIhvProfile ); + + FreeIhvSecurityProfile ( &pSecurityProfile ); + + delete pIhvProfile; + + if ( bComInitialized ) + { + CoUninitialize( ); + } + return WIN32_FROM_HRESULT( hr ); +} + + + + + +// free security profile. +VOID +FreeIhvSecurityProfile +( + PIHV_SECURITY_PROFILE* ppSecurityProfile +) +{ + if ( ppSecurityProfile && (*ppSecurityProfile) ) + { + PrivateMemoryFree( (*ppSecurityProfile)->pszParam2 ); + PrivateMemoryFree( (*ppSecurityProfile) ); + (*ppSecurityProfile) = NULL; + } +} diff --git a/network/wlan/ihvsample/profile.h b/network/wlan/ihvsample/profile.h new file mode 100644 index 00000000..cf676908 --- /dev/null +++ b/network/wlan/ihvsample/profile.h @@ -0,0 +1,108 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#pragma once + +#ifndef _IHVSAMPLEPROFILE_H +#define _IHVSAMPLEPROFILE_H + + + +#define MAX_AUTH_TYPES 3 + + +// IHV Auth types +typedef enum _IHV_AUTH_TYPE +{ + IHVAuthV1, + IHVAuthV2, + IHVAuthV3, + IHVAuthInvalid +} +IHV_AUTH_TYPE, *PIHV_AUTH_TYPE; + + +#define MAX_CIPHER_TYPES 4 + + +// IHV cipher types +typedef enum _IHV_CIPHER_TYPE +{ + None, + IHVCipher1, + IHVCipher2, + IHVCipher3, + IHVCipherInvalid +} +IHV_CIPHER_TYPE, *PIHV_CIPHER_TYPE; + + + + + +// Ihv connectivity profile data type. +typedef +struct _IHV_CONNECTIVITY_PROFILE +{ + DWORD dwParam1; + LPWSTR pszParam2; +} +IHV_CONNECTIVITY_PROFILE, *PIHV_CONNECTIVITY_PROFILE; + + + + + + +// Ihv security profile data type. +typedef struct _IHV_SECURITY_PROFILE +{ + BOOL bUseIhvConnectivityOnly; + BOOL bUseFullSecurity; + IHV_AUTH_TYPE AuthType; + IHV_CIPHER_TYPE CipherType; + DWORD dwParam1; + LPWSTR pszParam2; +} +IHV_SECURITY_PROFILE, *PIHV_SECURITY_PROFILE; + + + +// Converts string to connectivity profile. +DWORD +GetIhvConnectivityProfile +( + PDOT11EXT_IHV_CONNECTIVITY_PROFILE pDot11ExtIhvConnProfile, + PIHV_CONNECTIVITY_PROFILE* ppConnectivityProfile +); + + +// free connectivity profile. +VOID +FreeIhvConnectivityProfile +( + PIHV_CONNECTIVITY_PROFILE* ppConnectivityProfile +); + + +// Converts string to security profile. +DWORD +GetIhvSecurityProfile +( + PDOT11EXT_IHV_SECURITY_PROFILE pDot11ExtIhvSecProfile, + PIHV_SECURITY_PROFILE* ppSecurityProfile +); + + +// free security profile. +VOID +FreeIhvSecurityProfile +( + PIHV_SECURITY_PROFILE* ppSecurityProfile +); + + +#endif _IHVSAMPLEPROFILE_H + diff --git a/network/wlan/ihvsample/rc4utils.h b/network/wlan/ihvsample/rc4utils.h new file mode 100644 index 00000000..41ff9936 --- /dev/null +++ b/network/wlan/ihvsample/rc4utils.h @@ -0,0 +1,67 @@ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + + + +#include + +typedef struct EAPOL_PACKET +{ + BYTE ProtocolVersion; + BYTE PacketType; + BYTE PacketBodyLength[2]; + BYTE PacketBody[1]; +} EAPOL_PACKET, *UNALIGNED PEAPOL_PACKET; + +#include + + +VOID +WINAPI +RC4UtilsFreeKeyMaterial +( + PBYTE pbDecryptedKey, + DWORD dwKeyLen +); + + +DWORD +WINAPI +RC4UtilsParseKeyPacket +( + PEAPOL_PACKET pEapolPkt, + ULONG uPktLen, + PDOT11_MSONEX_RESULT_PARAMS pOneXResultParams, + BOOL* pbUCast, + PBYTE* ppbDecryptedKey, + DWORD* pdwKeyLen, + DWORD* pdwKeyIndex +); + + + +DWORD +WINAPI +RC4UtilsDecryptResultParams +( + PDOT11_MSONEX_RESULT_PARAMS pResultParamsOrig, + PDOT11_MSONEX_RESULT_PARAMS* ppResultParamsCopy +); + + +VOID +WINAPI +RC4UtilsFreeResultParams +( + PDOT11_MSONEX_RESULT_PARAMS* ppResultParams +); + + +#ifdef __cplusplus +} +#endif + diff --git a/network/wlan/ihvsample/utils.cpp b/network/wlan/ihvsample/utils.cpp new file mode 100644 index 00000000..f7dae91e --- /dev/null +++ b/network/wlan/ihvsample/utils.cpp @@ -0,0 +1,662 @@ +/*++ + +Copyright (c) 2005 Microsoft Corporation + +Abstract: + + Sample IHV Extensibility DLL to extend + 802.11 LWF driver for third party protocols. + + +--*/ + +#include "precomp.h" + +// +// Service specific global Variables +// +PDOT11EXT_APIS g_pDot11ExtApi = NULL; + +CRITICAL_SECTION g_csSynch = {0}; +DWORD g_dwThreadCount = 0; +DWORD g_dwSessionID = 0; +BOOL g_bAllowInit = TRUE; + + + + +// +// Structure to store data +// required to start a new +// thread. The thread count +// needs to be protected. +// +typedef +struct _THREAD_PROTECTOR +{ + LPTHREAD_START_ROUTINE pStartRoutine; + LPVOID pvParams; + HANDLE hIhvExtAdapter; +} +THREAD_PROTECTOR, *PTHREAD_PROTECTOR; + + + +// +// Logical copy function macro. +// +#define COPY_FUNCTION( _p, _FuncName, _Preface ) \ + (_p)->Dot11ExtIhv##_FuncName = \ + _Preface##_FuncName; \ + + + +// +// Trace utility function +// +VOID +SampleTraceFn +( + LPCSTR pszFormat, + LPCSTR pszVal1, + DWORD dwVal1 +) +{ + CHAR szMsgString[ 512 ] = {0}; + HRESULT hr = S_OK; + + hr = + StringCchPrintfA + ( + szMsgString, + sizeof( szMsgString ) - 1, + pszFormat, + pszVal1, + dwVal1 + ); + + if ( S_OK == hr ) + { + OutputDebugStringA( szMsgString ); + } + else + { + OutputDebugStringA( "ERROR: Trace message generation failed.\n" ); + } + + return; +} + +// Private Memory alloc function +LPVOID +PrivateMemoryAlloc +( + size_t MemSize +) +{ + LPVOID pvBuffer = NULL; + + if (!MemSize) + { + BAIL( ); + } + + pvBuffer = malloc( MemSize ); + if ( pvBuffer ) + { + ZeroMemory( pvBuffer, MemSize ); + } + +error: + return pvBuffer; +} + +// Private Memory free function +VOID +PrivateMemoryFree +( + LPVOID pvBuffer +) +{ + if ( pvBuffer ) + { + free( pvBuffer ); + } +} + + + +// +// Initialize the handler functions +// +VOID +HandlerInit +( + OUT PDOT11EXT_IHV_HANDLERS pDot11IHVHandlers +) +{ + + COPY_FUNCTION( pDot11IHVHandlers, DeinitService , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, InitAdapter , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, DeinitAdapter , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, ProcessSessionChange , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, IsUIRequestPending , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, ReceiveIndication , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, PerformCapabilityMatch , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, ValidateProfile , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, PerformPreAssociate , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, PerformPostAssociate , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, AdapterReset , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, StopPostAssociate , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, ReceivePacket , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, CreateDiscoveryProfiles , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, ProcessUIResponse , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, SendPacketCompletion , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, QueryUIRequest , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, OnexIndicateResult , Ihv ); + COPY_FUNCTION( pDot11IHVHandlers, Control , Ihv ); + +} + +#define ASSERT_MSG_LEN 768 + +// +// Calling DebugBreak indirectly +// to facilitate frame stepping +// in a debugger. +// +VOID +AssertFunc +( + _In_ LPCSTR pszFile, + int nLine +) +{ + HRESULT hr = S_OK; + CHAR Message[ ASSERT_MSG_LEN ] = {0}; + + hr = + StringCchPrintfA + ( + Message, + ASSERT_MSG_LEN-1, + "\n\nAssertion failed in File %s, Line %d\n\n", + pszFile, + nLine + ); + if ( S_OK == hr ) + { + OutputDebugStringA( Message ); + } + else + { + OutputDebugStringA( "Assertion Failed\n" ); + } + + DebugBreak( ); +} + +// +// Initialize global synchronization structure. +// +DWORD +InitCritSect +( + CRITICAL_SECTION* pCritSect +) +{ + DWORD dwResult = ERROR_SUCCESS; + + __try + { + InitializeCriticalSection( pCritSect ); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + dwResult = GetExceptionCode( ); + BAIL_ON_WIN32_ERROR( dwResult ); + } + +error: + return dwResult; +} + + + + + +// +// Dll Main function. +// +BOOL +WINAPI +DllMain +( + IN HINSTANCE Dll, + IN DWORD Reason, + IN PVOID Reserved +) +{ + DWORD dwResult = ERROR_SUCCESS; + + UNREFERENCED_PARAMETER( Dll ); + UNREFERENCED_PARAMETER( Reserved ); + + switch (Reason) + { + case DLL_PROCESS_ATTACH: + + dwResult = InitCritSect( &g_csSynch ); + BAIL_ON_WIN32_ERROR( dwResult ); + + g_dwThreadCount = 0; + g_dwSessionID = 0; + g_bAllowInit = TRUE; + + break; + + case DLL_PROCESS_DETACH: + DeleteCriticalSection( &g_csSynch ); + break; + + default: + break; + } + +error: + return ( ERROR_SUCCESS == dwResult ); +} + + + + +// +// Disable starting new threads or adding new adapters.. +// +VOID +StartShutdown +( + VOID +) +{ + g_bAllowInit = FALSE; +} + + + +// +// Wait for spawned threadcount +// to come down to zero. +// +VOID +WaitOnZeroThreads +( + VOID +) +{ + BOOL bZeroThreads = FALSE; + + for ( ;; ) + { + EnterCriticalSection( &g_csSynch ); + bZeroThreads = ( 0 == g_dwThreadCount ); + LeaveCriticalSection( &g_csSynch ); + + if ( bZeroThreads ) + { + break; + } + + Sleep( 100 ); + } +} + + + + + + +// +// Function uses pThreadProtector structure +// to start a new thread. Decrements the +// global thread count after this thread has +// returned. +// +DWORD +WINAPI +ProtectedThreadEntry +( + LPVOID pvThreadProtector +) +{ + DWORD dwResult = ERROR_SUCCESS; + PTHREAD_PROTECTOR pThreadProtector = NULL; + LPTHREAD_START_ROUTINE pStartRoutine = NULL; + LPVOID pvParams = NULL; + HANDLE hIhvExtAdapter = NULL; + + pThreadProtector = (PTHREAD_PROTECTOR) pvThreadProtector; + ASSERT ( pThreadProtector ) + + pStartRoutine = pThreadProtector->pStartRoutine; + pvParams = pThreadProtector->pvParams; + hIhvExtAdapter = pThreadProtector->hIhvExtAdapter; + + PrivateMemoryFree( pThreadProtector ); + pThreadProtector = NULL; + + + if ( pStartRoutine ) + { + dwResult = pStartRoutine( pvParams ); + BAIL_ON_WIN32_ERROR( dwResult ); + } + +error: + EnterCriticalSection( &g_csSynch ); + + // decrement thread count. + g_dwThreadCount--; + + // decrement adapter reference. + DerefenceAdapterDetails( hIhvExtAdapter ); + + LeaveCriticalSection( &g_csSynch ); + + return dwResult; +} + + +// +// Start a new thread. +// +DWORD +StartNewProtectedThread +( + HANDLE hIhvExtAdapter, + LPTHREAD_START_ROUTINE pStartRoutine, + LPVOID pvParams +) +{ + DWORD dwResult = ERROR_SUCCESS; + HANDLE hThread = NULL; + PTHREAD_PROTECTOR pThreadProtector = NULL; + BOOL bLocked = FALSE; + PADAPTER_DETAILS pAdapterDetails = NULL; + BOOL bCloseHandle = FALSE; + BOOL bOk = TRUE; + + + EnterCriticalSection( &g_csSynch ); + bLocked = TRUE; + + if (( !g_bAllowInit ) || (g_dwThreadCount >= MAX_THREAD_COUNT) ) + { + dwResult = ERROR_INVALID_STATE; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + dwResult = + ReferenceAdapterDetails + ( + hIhvExtAdapter, + &pAdapterDetails + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pAdapterDetails ); + + + // This memory is initialized, used and freed entirely by the Extensibility + // DLL. Hence this memory can be allocated and freed using any method. + pThreadProtector = (PTHREAD_PROTECTOR) PrivateMemoryAlloc( sizeof( THREAD_PROTECTOR ) ); + if ( !pThreadProtector ) + { + dwResult = ERROR_OUTOFMEMORY; + BAIL_ON_WIN32_ERROR( dwResult ); + } + + pThreadProtector->pStartRoutine = pStartRoutine; + pThreadProtector->pvParams = pvParams; + pThreadProtector->hIhvExtAdapter = hIhvExtAdapter; + + hThread = + CreateThread + ( + NULL, // security attributes + 0, // default stack size + ProtectedThreadEntry, // pointer to function to run + pThreadProtector, // parameter + 0, // run thread immediately + NULL // Thread ID receiver + ); + if ( !hThread ) + { + dwResult = GetLastError( ); + BAIL_ON_WIN32_ERROR( dwResult ); + } + bCloseHandle = TRUE; + + // Thread successfully started, memory + // would be freed by ProtectedThreadEntry + pThreadProtector = NULL; + + // Transfering dereference duty to ProtectedThreadEntry function. + pAdapterDetails = NULL; + + g_dwThreadCount++; + +error: + if ( bCloseHandle ) + { + bOk = CloseHandle( hThread ); + ASSERT( bOk ); + } + + PrivateMemoryFree( pThreadProtector ); + + if ( pAdapterDetails ) + { + DerefenceAdapterDetails( hIhvExtAdapter ); + } + + if ( bLocked ) + { + LeaveCriticalSection( &g_csSynch ); + } + return dwResult; +} + + +// +// Function to match beacon and profile. +// +BOOL +WINAPI +MatchBssDescription +( + PDOT11EXT_IHV_PROFILE_PARAMS pIhvProfileParams, + PIHV_CONNECTIVITY_PROFILE pConnectivityProfile, + PIHV_SECURITY_PROFILE pSecurityProfile, + PULDOT11_BSS_ENTRY pBssEntry +) +{ + UNREFERENCED_PARAMETER( pIhvProfileParams ); + UNREFERENCED_PARAMETER( pConnectivityProfile ); + UNREFERENCED_PARAMETER( pSecurityProfile ); + UNREFERENCED_PARAMETER( pBssEntry ); + + // Try to match the current profile with the beacon. + return TRUE; +} + + + +DWORD +CopyConnectivityProfile +( + IN PDOT11EXT_IHV_CONNECTIVITY_PROFILE pSrc, + OUT PDOT11EXT_IHV_CONNECTIVITY_PROFILE pDst +) +{ + DWORD dwResult = ERROR_SUCCESS; + DWORD dwLen = 0; + + ASSERT( pSrc ); + ASSERT( pDst ); + + ZeroMemory( pDst, sizeof( DOT11EXT_IHV_CONNECTIVITY_PROFILE ) ); + + if ( pSrc->pszXmlFragmentIhvConnectivity ) + { + dwLen = (DWORD) wcslen( pSrc->pszXmlFragmentIhvConnectivity ); + + dwResult = + (g_pDot11ExtApi->Dot11ExtAllocateBuffer) + ( + (dwLen+1) * sizeof( WCHAR ), + (LPVOID*) &(pDst->pszXmlFragmentIhvConnectivity) + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pDst->pszXmlFragmentIhvConnectivity ); + + CopyMemory + ( + (LPVOID) pDst->pszXmlFragmentIhvConnectivity, + (LPVOID) pSrc->pszXmlFragmentIhvConnectivity, + (dwLen+1) * sizeof( WCHAR ) + ); + } + +error: + return dwResult; +} + + +VOID +FreeConnectivityProfile +( + IN PDOT11EXT_IHV_CONNECTIVITY_PROFILE pSrc +) +{ + if ( pSrc && pSrc->pszXmlFragmentIhvConnectivity ) + { + (g_pDot11ExtApi->Dot11ExtFreeBuffer)( (LPVOID) pSrc->pszXmlFragmentIhvConnectivity ); + pSrc->pszXmlFragmentIhvConnectivity = NULL; + } +} + + + +DWORD +CopySecurityProfile +( + IN PDOT11EXT_IHV_SECURITY_PROFILE pSrc, + OUT PDOT11EXT_IHV_SECURITY_PROFILE pDst +) +{ + DWORD dwResult = ERROR_SUCCESS; + DWORD dwLen = 0; + + ASSERT( pSrc ); + ASSERT( pDst ); + + ZeroMemory( pDst, sizeof( DOT11EXT_IHV_SECURITY_PROFILE ) ); + + pDst->bUseMSOnex = pSrc->bUseMSOnex; + + if ( pSrc->pszXmlFragmentIhvSecurity ) + { + dwLen = (DWORD) wcslen( pSrc->pszXmlFragmentIhvSecurity ); + + dwResult = + (g_pDot11ExtApi->Dot11ExtAllocateBuffer) + ( + (dwLen+1) * sizeof( WCHAR ), + (LPVOID*) &(pDst->pszXmlFragmentIhvSecurity) + ); + BAIL_ON_WIN32_ERROR( dwResult ); + ASSERT( pDst->pszXmlFragmentIhvSecurity ); + + CopyMemory + ( + (LPVOID) pDst->pszXmlFragmentIhvSecurity, + (LPVOID) pSrc->pszXmlFragmentIhvSecurity, + (dwLen+1) * sizeof( WCHAR ) + ); + } + + +error: + return dwResult; +} + + +VOID +FreeSecurityProfile +( + IN PDOT11EXT_IHV_SECURITY_PROFILE pSrc +) +{ + if ( pSrc && pSrc->pszXmlFragmentIhvSecurity ) + { + (g_pDot11ExtApi->Dot11ExtFreeBuffer)( (LPVOID) pSrc->pszXmlFragmentIhvSecurity ); + pSrc->pszXmlFragmentIhvSecurity = NULL; + } +} + + + + +DWORD +CopyDiscoveryProfile +( + IN PDOT11EXT_IHV_DISCOVERY_PROFILE pSrc, + OUT PDOT11EXT_IHV_DISCOVERY_PROFILE pDst +) +{ + DWORD dwResult = ERROR_SUCCESS; + + ASSERT( pSrc ); + ASSERT( pDst ); + + dwResult = + CopyConnectivityProfile + ( + &(pSrc->IhvConnectivityProfile), + &(pDst->IhvConnectivityProfile) + ); + BAIL_ON_WIN32_ERROR( dwResult ); + + + dwResult = + CopySecurityProfile + ( + &(pSrc->IhvSecurityProfile), + &(pDst->IhvSecurityProfile) + ); + BAIL_ON_WIN32_ERROR( dwResult ); + +error: + if ( ERROR_SUCCESS != dwResult ) + { + FreeDiscoveryProfile( pDst ); + } + return dwResult; +} + + +VOID +FreeDiscoveryProfile +( + IN PDOT11EXT_IHV_DISCOVERY_PROFILE pSrc +) +{ + if ( pSrc ) + { + FreeConnectivityProfile( &(pSrc->IhvConnectivityProfile) ); + FreeSecurityProfile( &(pSrc->IhvSecurityProfile) ); + } +} + + + diff --git a/network/wlan/ihvsample/utils.h b/network/wlan/ihvsample/utils.h new file mode 100644 index 00000000..d2d4a80c --- /dev/null +++ b/network/wlan/ihvsample/utils.h @@ -0,0 +1,318 @@ + + +////////////////// +// Macros // +////////////////// + + +// Trace Macros + + +VOID +SampleTraceFn +( + LPCSTR pszFormat, + LPCSTR pszVal1, + DWORD dwVal1 +); + +#define TRACE_MESSAGE( _x ) SampleTraceFn( "INFO: %s\n",_x, 0 ); +#define TRACE_MESSAGE_VAL( _x, _y ) SampleTraceFn( "INFO: %s %lu\n", _x, _y ); + + +// +// Error Handling +// +#define BAIL_ON_WIN32_ERROR( __x ) \ + if ( ERROR_SUCCESS != (__x) ) \ + { \ + goto error; \ + } \ + + +// +// COM failure +// +#define BAIL_ON_FAILURE( __hr ) \ + if ( FAILED( __hr ) ) \ + { \ + goto error; \ + } \ + +// +// combine win32 and com error codes. +// +#define WIN32_FROM_HRESULT(hr) \ + (SUCCEEDED(hr) ? ERROR_SUCCESS : \ + (HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : (hr))) + + +// Combined Error Macro +#define WIN32_COMBINED_ERROR( _dwError, _hr ) ( (_dwError)?(_dwError):WIN32_FROM_HRESULT((_hr))) + +// +// Unconditional bail +// +#define BAIL( ) goto error; + + +// +// Maximum number of new threads +// to spawn at any given time. +// +#define MAX_THREAD_COUNT 100 + + + +// +// Debug Macro. +// +#ifdef DBG +#define ASSERT(exp) \ + if (!(exp)) \ + { \ + AssertFunc( __FILE__, __LINE__ ); \ + } + +#define ASSERTFAILURE() \ + AssertFunc( __FILE__, __LINE__ ); +#else +#define ASSERT(exp) +#define ASSERTFAILURE() +#endif // DBG + + + +// Private Memory alloc function +LPVOID +PrivateMemoryAlloc +( + size_t MemSize +); + +// Private Memory free function +VOID +PrivateMemoryFree +( + LPVOID pvBuffer +); + + +// +// Array length. +// +#define ARRAY_LENGTH( _x ) (sizeof( (_x) ) / sizeof( (_x)[0] )) + +// +// Declarations for service specific global Variables +// +extern PDOT11EXT_APIS g_pDot11ExtApi; +extern CRITICAL_SECTION g_csSynch; +extern DWORD g_dwThreadCount; +extern DWORD g_dwSessionID; +extern BOOL g_bAllowInit; + + + + + + + + +// Pre-declaration for the ADAPTER_DETAILS structure. +typedef +struct _ADAPTER_DETAILS +ADAPTER_DETAILS, *PADAPTER_DETAILS; + + + + +// +// Handler type for pre-association. +// +typedef +DWORD +(WINAPI *PRE_ASSOCIATE_FUNCTION) +( + PADAPTER_DETAILS pAdapterDetails, + DWORD* pdwReasonCode +); + + +// +// Handler type for post-association. +// +typedef +DWORD +(WINAPI *POST_ASSOCIATE_FUNCTION) +( + PADAPTER_DETAILS pAdapterDetails, + HANDLE hSecuritySessionID, + PDOT11_PORT_STATE pPortState, + ULONG uDot11AssocParamsBytes, + PDOT11_ASSOCIATION_COMPLETION_PARAMETERS pDot11AssocParams +); + + + +// +// Handler type for stop-post-association. +// +typedef +DWORD +(WINAPI *STOP_POST_ASSOCIATE_FUNCTION) +( + PADAPTER_DETAILS pAdapterDetails, + PDOT11_MAC_ADDRESS pPeer, + DOT11_ASSOC_STATUS dot11AssocStatus +); + + + +// +// Handler type for receive packet. +// +typedef +DWORD +(WINAPI *IHV_RECEIVE_PACKET_HANDLER) +( + PADAPTER_DETAILS pAdapterDetails, + DWORD dwInBufferSize, + LPVOID pvInBuffer +); + + + +// +// Handler type for IHV result indication. +// +typedef +DWORD +(WINAPI *IHV_INDICATE_RESULT_HANDLER) +( + PADAPTER_DETAILS pAdapterDetails, + DOT11_MSONEX_RESULT msOneXResult, + PDOT11_MSONEX_RESULT_PARAMS pDot11MsOneXResultParams +); + + + +// Assert function in debug builds. +VOID +AssertFunc +( + _In_ LPCSTR pszFile, + int nLine +); + + +// +// Register intention to start shut down. +// +VOID +StartShutdown +( + VOID +); + + +// +// Wait for posted thread count to go down to zero. +// +VOID +WaitOnZeroThreads +( + VOID +); + + +// +// Populate IHV handler function pointers to IHV Framework. +// +VOID +HandlerInit +( + OUT PDOT11EXT_IHV_HANDLERS pDot11IHVHandlers +); + + +// +// Call createthread to start a new thread and ensure +// the adapter stays active till the thread finishes +// by putting a refcount increment/decrement around +// the lifetime of the thread. +// +DWORD +StartNewProtectedThread +( + HANDLE hIhvExtAdapter, + LPTHREAD_START_ROUTINE pStartRoutine, + LPVOID pvParams +); + + +// Typedef for unaligned BSS Entry to process beacons. +typedef UNALIGNED DOT11_BSS_ENTRY* PULDOT11_BSS_ENTRY; + +// +// Function to match beacon and profile. +// +BOOL +WINAPI +MatchBssDescription +( + PDOT11EXT_IHV_PROFILE_PARAMS pIhvProfileParams, + PIHV_CONNECTIVITY_PROFILE pConnectivityProfile, + PIHV_SECURITY_PROFILE pSecurityProfile, + PULDOT11_BSS_ENTRY pBssEntry +); + + +// +// Functions to copy and free discovery profiles. +// + +DWORD +CopyConnectivityProfile +( + IN PDOT11EXT_IHV_CONNECTIVITY_PROFILE pSrc, + OUT PDOT11EXT_IHV_CONNECTIVITY_PROFILE pDst +); + + +VOID +FreeConnectivityProfile +( + IN PDOT11EXT_IHV_CONNECTIVITY_PROFILE pSrc +); + + +DWORD +CopySecurityProfile +( + IN PDOT11EXT_IHV_SECURITY_PROFILE pSrc, + OUT PDOT11EXT_IHV_SECURITY_PROFILE pDst +); + +VOID +FreeSecurityProfile +( + IN PDOT11EXT_IHV_SECURITY_PROFILE pSrc +); + + + +VOID +FreeDiscoveryProfile +( + IN PDOT11EXT_IHV_DISCOVERY_PROFILE pSrc +); + + + +DWORD +CopyDiscoveryProfile +( + IN PDOT11EXT_IHV_DISCOVERY_PROFILE pSrc, + OUT PDOT11EXT_IHV_DISCOVERY_PROFILE pDst +); diff --git a/network/wlan/ihvsampleui/IHVClassFactory.cpp b/network/wlan/ihvsampleui/IHVClassFactory.cpp new file mode 100644 index 00000000..7da337d5 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVClassFactory.cpp @@ -0,0 +1,166 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#include "precomp.h" +#include "ihvuiinc_i.c" + +extern long g_serverLock; //lock count on server + +// +// IUnknown Implementation +// +CIHVClassFactory::CIHVClassFactory() : m_refCount(1) +{ +} + +CIHVClassFactory::~CIHVClassFactory() +{ +} + +STDMETHODIMP_(ULONG) +CIHVClassFactory::AddRef() +{ + return InterlockedIncrement(&m_refCount); +} + +STDMETHODIMP_(ULONG) +CIHVClassFactory::Release() +{ + ULONG refCount = InterlockedDecrement(&m_refCount); + if (refCount == 0) + { + delete this; + } + return refCount; +} + +STDMETHODIMP +CIHVClassFactory::QueryInterface( + REFIID riid, + void **ppvObject + ) +{ + HRESULT hr = E_INVALIDARG; + if (NULL != ppvObject) + { + hr = S_OK; + if (riid == IID_IUnknown) + { + *ppvObject = static_cast(this); + } + else if (riid == IID_IClassFactory) + { + *ppvObject = static_cast(this); + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + reinterpret_cast(*ppvObject)->AddRef(); + } + + return hr; +} + + +// +// IClassFactory Implementaion +// +STDMETHODIMP +CIHVClassFactory::CreateInstance( + IUnknown *pUnkOuter, + REFIID riid, + void **ppvObject + ) +{ + HRESULT hr = E_NOINTERFACE; + + // aggregation not supported + if (pUnkOuter != NULL) + { + return CLASS_E_NOAGGREGATION; + } + + if (NULL == ppvObject) + { + return E_INVALIDARG; + } + + // Figure out which interface is wanted + // the ui will call us only as IID_IDot11ExtUI + if (IID_IDot11SampleExtUI == riid || + IID_IDot11ExtUI == riid || + IID_IWizardExtension == riid) + { + CDot11SampleExtUI *pExtUI = new(std::nothrow) CDot11SampleExtUI(); + if (NULL == pExtUI) + { + return E_OUTOFMEMORY; + } + + pExtUI->AddRef(); + hr = pExtUI->QueryInterface(riid, ppvObject); + + pExtUI->Release(); + } + else if (IID_IDot11SampleExtUIConProperty == riid) + { + CDot11SampleExtUIConProperty *pCDot11SampleExtUIConProperty = new(std::nothrow) CDot11SampleExtUIConProperty(); + if (NULL == pCDot11SampleExtUIConProperty) + { + return E_OUTOFMEMORY; + } + + pCDot11SampleExtUIConProperty->AddRef(); + hr = pCDot11SampleExtUIConProperty->QueryInterface(riid, ppvObject); + + pCDot11SampleExtUIConProperty->Release(); + } + else if (IID_IDot11SampleExtUISecProperty == riid) + { + CDot11SampleExtUISecProperty *pCDot11SampleExtUISecProperty = new(std::nothrow) CDot11SampleExtUISecProperty(); + if (NULL == pCDot11SampleExtUISecProperty) + { + return E_OUTOFMEMORY; + } + + pCDot11SampleExtUISecProperty->AddRef(); + hr = pCDot11SampleExtUISecProperty->QueryInterface(riid, ppvObject); + + pCDot11SampleExtUISecProperty->Release(); + } + else if (IID_IDot11SampleExtUIKeyProperty == riid) + { + CDot11SampleExtUIKeyProperty *pCDot11SampleExtUIKeyProperty = new(std::nothrow) CDot11SampleExtUIKeyProperty(); + if (NULL == pCDot11SampleExtUIKeyProperty) + { + return E_OUTOFMEMORY; + } + + pCDot11SampleExtUIKeyProperty->AddRef(); + hr = pCDot11SampleExtUIKeyProperty->QueryInterface(riid, ppvObject); + + pCDot11SampleExtUIKeyProperty->Release(); + } + + return hr; +} + + +STDMETHODIMP +CIHVClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + InterlockedIncrement(&g_serverLock); + } + else + { + InterlockedDecrement(&g_serverLock); + } + + return S_OK; +} diff --git a/network/wlan/ihvsampleui/IHVClassFactory.h b/network/wlan/ihvsampleui/IHVClassFactory.h new file mode 100644 index 00000000..60f814c3 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVClassFactory.h @@ -0,0 +1,28 @@ + +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#pragma once + +// The class factory +class CIHVClassFactory : public IClassFactory +{ +public: + // Constructor + CIHVClassFactory(); + ~CIHVClassFactory(); + + // IUnknown + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject); + + // IClassFactory + STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject); + STDMETHODIMP LockServer(BOOL fLock); + +private: + long m_refCount; +}; diff --git a/network/wlan/ihvsampleui/IHVRegistryHelper.cpp b/network/wlan/ihvsampleui/IHVRegistryHelper.cpp new file mode 100644 index 00000000..ebe8622c --- /dev/null +++ b/network/wlan/ihvsampleui/IHVRegistryHelper.cpp @@ -0,0 +1,284 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#include "precomp.h" + +#define CLSIDSTR_CALLBACK L"{4A01f9f9-6012-4343-A8C4-10B5DF32672A}" // IHV Ext UI CLSID +#define CLSID_CALLBACK_FRIENDLY_NAME L"Wireless 802.11 IHV Sample Config UI" + +#define REGCLSID L"CLSID" +#define INPROCSERVER32 L"InprocServer32" +#define THREADINGMODEL L"ThreadingModel" +#define FREETHREADING L"Both" + +#define ARRAY_SIZE(s) (sizeof(s) / sizeof(s[0])) + +extern HINSTANCE g_hInst; + + +typedef HRESULT (APIENTRY *RegisterPageWithPageProc) ( + const GUID *pguidParentPage, + const GUID *pguidChildPage, + const LPWSTR pszChildModuleFileName, + const LPWSTR pszFriendlyName, + const DWORD dwBehaviorFlags, + const DWORD dwUserFlags, + const LPWSTR pszCommandLine); + +typedef HRESULT (APIENTRY *UnregisterPageProc) ( + const GUID *pguidPage, + const BOOL fUnregisterFromCOM); + + + +// +// RegisterServer - Register the COM Server by creating required keys +// + +#pragma warning (push) +#pragma warning (disable:6262) + +HRESULT +CRegHelper::RegisterServer () +{ + HRESULT hr = S_OK; + wchar_t wszModule[_MAX_PATH] = {0}; + DWORD result = 0; + + result = GetModuleFileName(g_hInst, wszModule, ARRAY_SIZE(wszModule)); + + if (result == 0) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + wchar_t wszCLSIDKey[MAX_LENGTH] = {0}; // CLSID\\wszCLSID. + wchar_t wszInprocKey[MAX_LENGTH + 2] = {0}; // CLSID\\InprocServer32 + + // get the class ID strings. + StringCchCopyW(wszCLSIDKey, MAX_LENGTH, REGCLSID); + StringCchCatW(wszCLSIDKey, MAX_LENGTH, L"\\"); + StringCchCatW(wszCLSIDKey, MAX_LENGTH, CLSIDSTR_CALLBACK); + + // create entries under CLSID. + // Description + FAILHR(SetKeyAndValue(wszCLSIDKey, NULL, CLSID_CALLBACK_FRIENDLY_NAME)); + // set the server path. + FAILHR(SetKeyAndValue(wszCLSIDKey, INPROCSERVER32, wszModule)); + + // add the threading model information. + hr = StringCchPrintfW(wszInprocKey, MAX_LENGTH + 2, L"%s\\%s", wszCLSIDKey, INPROCSERVER32); + if(FAILED(hr)) + { + hr = S_FALSE; + return hr; + } + + FAILHR(SetRegValue(wszInprocKey, THREADINGMODEL, FREETHREADING)); + + // register the extension UI wizard page + HINSTANCE hinstLib = LoadLibrary(TEXT("connect.dll")); + if (hinstLib != NULL) + { + + // get the export function used for registering + RegisterPageWithPageProc registerPageWithPageProc = + (RegisterPageWithPageProc) GetProcAddress(hinstLib, (LPCSTR)("RegisterPageWithPage")); + + if (NULL != registerPageWithPageProc) + { + hr = (registerPageWithPageProc) (NULL, // stand alone page (no parent) + &GUID_SAMPLE_IHVUI_CLSID, // clsid of the extension UI wizard page + NULL, // filename already registered through COM + CLSID_CALLBACK_FRIENDLY_NAME, // friendly name + 0x2, // allow duplicate instances + 0, // no user flags + NULL); // no command line + } + + FreeLibrary(hinstLib); + } + + return hr; +} +#pragma warning (pop) + +// +// UnRegisterServer - Register the COM Server by creating required keys +// +HRESULT +CRegHelper::UnregisterServer() +{ + HRESULT hr = S_OK; + wchar_t wszCLSIDKey[MAX_LENGTH] = {0}; // CLSID\\wszCLSID. + + + // get the class ID strings. + StringCchCopyW(wszCLSIDKey,MAX_LENGTH, REGCLSID); + StringCchCatW(wszCLSIDKey, MAX_LENGTH, L"\\"); + StringCchCatW(wszCLSIDKey, MAX_LENGTH, CLSIDSTR_CALLBACK); + StringCchCatW(wszCLSIDKey, MAX_LENGTH, L"\\"); + StringCchCatW(wszCLSIDKey, MAX_LENGTH, INPROCSERVER32); + + + // delete the sub key of the Class ID key + FAILHR(DeleteKey(wszCLSIDKey)); + + StringCchCopyW(wszCLSIDKey,MAX_LENGTH, REGCLSID); + StringCchCatW(wszCLSIDKey, MAX_LENGTH, L"\\"); + StringCchCatW(wszCLSIDKey, MAX_LENGTH, CLSIDSTR_CALLBACK); + + // delete Class ID key + FAILHR(DeleteKey(wszCLSIDKey)); + + // unregister the extension UI wizard page + HINSTANCE hinstLib = LoadLibrary(TEXT("connect.dll")); + if (hinstLib != NULL) + { + + // get the export function used for unregistering + UnregisterPageProc unregisterPageProc = + (UnregisterPageProc) GetProcAddress(hinstLib, (LPCSTR)("UnregisterPage")); + + if (NULL != unregisterPageProc) + { + hr = (unregisterPageProc) (&GUID_SAMPLE_IHVUI_CLSID, // clsid of the extension UI wizard page + FALSE); // already unregistered from COM + } + + FreeLibrary(hinstLib); + } + + return hr; + +} + +// +// Set an entry in the registry of the form: +// HKEY_CLASSES_ROOT\wszKey\wszSubkey = wszValue +// +BOOL +CRegHelper::SetKeyAndValue( + const wchar_t* pwszKey, + const wchar_t* pwszSubkey, + const wchar_t* pwszValue + ) +{ + HKEY hKey; // handle to the new reg key. + wchar_t wszRegKey[MAX_LENGTH] = {0}; // buffer for the full key name. + + + // init the key with the base key name. + StringCchCopyW(wszRegKey, MAX_LENGTH, pwszKey); + // append the subkey name (if there is one). + if (pwszSubkey != NULL) + { + StringCchCatW(wszRegKey, MAX_LENGTH, L"\\"); + StringCchCatW(wszRegKey, MAX_LENGTH, pwszSubkey); + } + + // create the registry key. + if (RegCreateKeyEx( + HKEY_CLASSES_ROOT, + wszRegKey, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hKey, + NULL) == ERROR_SUCCESS) + { + // set the value (if there is one). + if (pwszValue != NULL) + { + RegSetValueEx( + hKey, + NULL, + 0, + REG_SZ, + (BYTE *)pwszValue, + (DWORD) ((wcslen(pwszValue) + 1) * sizeof (wchar_t)) + ); + } + + RegCloseKey(hKey); + + return TRUE; + } + + return FALSE; +} + +// +// SetRegValue - Open the key, create a new keyword and value pair under it. +// +BOOL +CRegHelper::SetRegValue( + const wchar_t* pwszKeyName, + const wchar_t* pwszKeyword, + const wchar_t* pwszValue + ) +{ + HKEY hKey; // handle to the new reg key. + + // create the registration key. + if (RegCreateKeyEx( + HKEY_CLASSES_ROOT, + pwszKeyName, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hKey, + NULL) == ERROR_SUCCESS) + { + // set the value (if there is one). + if (pwszValue != NULL) + { + RegSetValueEx( + hKey, + pwszKeyword, + 0, + REG_SZ, + (BYTE *)pwszValue, + (DWORD) ((wcslen(pwszValue) + 1) * sizeof (wchar_t)) + ); + } + + RegCloseKey(hKey); + + + return TRUE; + } + + return FALSE; +} + + +// +// Delete an entry in the registry of the form: +// HKEY_CLASSES_ROOT\wszKey\wszSubkey = wszValue +// +BOOL +CRegHelper::DeleteKey(const wchar_t* pwszSubkey) +{ + DWORD result = 0; + + if (pwszSubkey != NULL) + { + // delete the registry key. + result = RegDeleteKey(HKEY_CLASSES_ROOT, pwszSubkey); + } + else + { + return FALSE; + } + + return ((ERROR_SUCCESS == result)?TRUE:FALSE); +} + + diff --git a/network/wlan/ihvsampleui/IHVRegistryHelper.h b/network/wlan/ihvsampleui/IHVRegistryHelper.h new file mode 100644 index 00000000..76c85b1c --- /dev/null +++ b/network/wlan/ihvsampleui/IHVRegistryHelper.h @@ -0,0 +1,40 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#pragma once + +#define FAILHR(result) \ + if (result == FALSE) \ + {\ + hr = S_FALSE;\ + return hr;\ + } + + +#define MAX_LENGTH 256 + + +class CRegHelper +{ + public: + static HRESULT STDMETHODCALLTYPE RegisterServer(); + static HRESULT STDMETHODCALLTYPE UnregisterServer(); + +private: + static BOOL SetKeyAndValue( + const wchar_t *pszKey, + const wchar_t *pszSubkey, + const wchar_t *pszValue + ); + + static BOOL DeleteKey(const wchar_t *pszSubkey); + + static BOOL SetRegValue( + const wchar_t *pszKeyName, + const wchar_t *pszKeyword, + const wchar_t *pszValue + ); +}; + diff --git a/network/wlan/ihvsampleui/IHVSample.idl b/network/wlan/ihvsampleui/IHVSample.idl new file mode 100644 index 00000000..81e05140 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSample.idl @@ -0,0 +1,71 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +import "unknwn.idl"; +import "wtypes.idl"; +import "ihvuiinc.idl"; // For wireless UI extensions + +interface IWizardExtension; +interface IObjectWithSite; + +interface IDot11SampleExtUI; +interface IDot11SampleExtUIConProperty; +interface IDot11SampleExtUISecProperty; + +[ + uuid(7ca89d4b-2c5b-4368-b53a-ffa14e031179), + helpstring(" Dot11 IHV Extensibility UI Interface"), + dual +] + +interface IDot11SampleExtUI: IDot11ExtUI +{ +} + + + +[ + uuid(61055513-2f27-4962-b29c-d6d7d1500fec), + helpstring(" Dot11 IHV Extensibility UI Connection Properties Interface"), + dual +] + +interface IDot11SampleExtUIConProperty: IDot11ExtUIProperty +{ + [id(1), hidden, helpstring("method Initialize")] + HRESULT + Initialize([in] BSTR bstrPropertyName); +} + + +[ + uuid(12c211ae-1b6d-471c-9c1b-698bcc9b9d97), + helpstring(" Dot11 IHV Extensibility UI Security Properties Interface"), + dual +] + +interface IDot11SampleExtUISecProperty: IDot11ExtUIProperty +{ + [id(1), hidden, helpstring("method Initialize")] + HRESULT + Initialize([in] BSTR bstrPropertyName, [in] DWORD dwIhvSecurity); +} + + +[ + uuid(a18bae3c-39c2-4b34-ba49-0130e431d2ca), + helpstring(" Dot11 IHV Extensibility UI Key Properties Interface"), + dual +] + +interface IDot11SampleExtUIKeyProperty: IDot11ExtUIProperty +{ + [id(1), hidden, helpstring("method Initialize")] + HRESULT + Initialize([in] BYTE* pvData); +} + + + diff --git a/network/wlan/ihvsampleui/IHVSampleExtUI.cpp b/network/wlan/ihvsampleui/IHVSampleExtUI.cpp new file mode 100644 index 00000000..a5e60d7a --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleExtUI.cpp @@ -0,0 +1,971 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#include "precomp.h" +#include "ihvsample_i.c" + +extern HINSTANCE g_hInst; + +LPWSTR g_IHVAuthFriendlyName[] = { + L"IHVAuth V1", + L"IHVAuth V2", + L"IHVAuth V3" +}; + +LPWSTR g_IHVCipherFriendlyName[] = { + L"None", + L"IHVCipher 1", + L"IHVCipher 2", + L"IHVCipher 3" +}; + +IHV_AUTH_CIPHER_CAPABILITY g_IHVOneXExtCapability = +{ + 3, + { + { + IHVAuthV1, + 1, + {IHVCipher1} + }, + { + IHVAuthV2, + 3, + {None, IHVCipher1, IHVCipher2} + }, + { + IHVAuthV3, + 2, + {IHVCipher2, IHVCipher3} + } + } +}; + + +static const WCHAR c_szIhvUIRequest[] = L"_UI_Request"; +static const WCHAR c_szIhvUIResponse[] = L"_UI_Response"; + +template +T* GetThis(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static const WCHAR c_szThisPointer[]= L"_Win32_this_"; + UNREFERENCED_PARAMETER(wParam); + + T* pThis = NULL; + if (uMsg == WM_INITDIALOG) + { + if (sizeof(PROPSHEETPAGE) == ((LPPROPSHEETPAGE)lParam)->dwSize) + { + // This corresponds to MSDN + pThis = (T *)((LPPROPSHEETPAGE)lParam)->lParam; + } + else + { + // TODO: Need to determine when this abnormality happens... + pThis = (T *)lParam; + } + + SetProp(hwnd, c_szThisPointer, (HANDLE)pThis); + + } + else if (uMsg == WM_DESTROY) + { + RemoveProp(hwnd, c_szThisPointer); + } + else + { + pThis = (T *)GetProp(hwnd, c_szThisPointer); + } + + return pThis; +} + + +CDot11SampleExtUI::CDot11SampleExtUI(): m_crefCount(0) +{ + InterlockedIncrement(&g_objRefCount); + + m_pUnkSite = NULL; + m_hFirstPagePsp = NULL; + m_hLastPagePsp = NULL; + m_pUIRequest = NULL; +} + +CDot11SampleExtUI::~CDot11SampleExtUI() +{ + InterlockedDecrement(&g_objRefCount); + + if( m_pUIRequest) + { + delete m_pUIRequest; + } + + if( m_pUnkSite) + { + m_pUnkSite ->Release(); + m_pUnkSite = NULL; + } +} + +// Used to get the IHV friendly name +STDMETHODIMP +CDot11SampleExtUI::GetDot11ExtUIFriendlyName( + BSTR* bstrFriendlyName) +{ + HRESULT hr = E_INVALIDARG; + + if (NULL != bstrFriendlyName) + { + *bstrFriendlyName = SysAllocString(IHV_SAMPLE_IHV_NAME); + hr = S_OK; + } + + return hr; +} + + +// Returns the requested property type +STDMETHODIMP +CDot11SampleExtUI::GetDot11ExtUIProperties( + DOT11_EXT_UI_PROPERTY_TYPE ExtType, + ULONG *pcExtensions, + IDot11ExtUIProperty **ppDot11ExtUIProperty + ) +{ + HRESULT hr = S_OK; + if (!pcExtensions || !ppDot11ExtUIProperty) + { + hr = E_INVALIDARG; + goto error; + } + + // Initialize the out parameters + *pcExtensions = 0; + *ppDot11ExtUIProperty = NULL; + + switch(ExtType) + { + case DOT11_EXT_UI_CONNECTION: + hr = CreateConnectionProperties(pcExtensions, ppDot11ExtUIProperty); + break; + + case DOT11_EXT_UI_SECURITY: + hr = CreateSecurityProperties(pcExtensions, ppDot11ExtUIProperty); + break; + + case DOT11_EXT_UI_KEYEXTENSION: + hr = CreateKeyProperties(pcExtensions, ppDot11ExtUIProperty); + break; + + default: + hr = E_NOTIMPL; + break; + } + +error: + return hr; +} + +#define IHV_BALLOON_TEXT L"Please enter key information" + +STDMETHODIMP +CDot11SampleExtUI::GetDot11ExtUIBalloonText( + BSTR pIHVUIRequest, // the UI request structure from IHV + BSTR* pwszBalloonText // the balloon text to be displayed + ) +{ + HRESULT hr = E_INVALIDARG; + PDOT11EXT_IHV_UI_REQUEST pIhvUiRequest = (PDOT11EXT_IHV_UI_REQUEST) pIHVUIRequest; + + if (NULL != pwszBalloonText) + { + // Ihv could choose to parse the UI request data here ... + UNREFERENCED_PARAMETER( pIhvUiRequest ); + + *pwszBalloonText = SysAllocString( IHV_BALLOON_TEXT ); + hr = S_OK; + } + + return hr; +} + + + +HRESULT +CDot11SampleExtUI::CreateConnectionProperties( + ULONG *pcExtensions, + IDot11ExtUIProperty **ppDot11ExtUIProperty + ) +{ + HRESULT hr = ERROR_SUCCESS; + BSTR strName = NULL; + ULONG uCount = 0; + + IDot11SampleExtUIConProperty **pprgProps = NULL; + + uCount = PROP_COUNT_CONNECTION; + pprgProps = (IDot11SampleExtUIConProperty**) + CoTaskMemAlloc(sizeof(IDot11SampleExtUIConProperty*) * uCount); + + if (!pprgProps) + { + hr = E_UNEXPECTED; + goto error; + } + + // Since we just have one property of each, we'll + // create one interface first and initialize it separately + IDot11SampleExtUIConProperty *pTempIProp = NULL; + hr = CoCreateInstance( + GUID_SAMPLE_IHVUI_CLSID, + NULL, + CLSCTX_INPROC, + IID_IDot11SampleExtUIConProperty, + (PVOID*)&pTempIProp + ); + + if (FAILED(hr)) + { + goto error; + } + + // this will probably never be displayed + strName = SysAllocString(L"IHV Connection Settings"); + hr = pTempIProp->Initialize(strName); + pprgProps[0] = pTempIProp; + + if (SUCCEEDED(hr)) + { + *pcExtensions = uCount; + *ppDot11ExtUIProperty = (IDot11ExtUIProperty*)pprgProps; + + // Set the current pointer to NULL so it doesn't get freed at the bottom + pprgProps = NULL; + } + +error: + if (FAILED(hr) && pprgProps) + { + CoTaskMemFree(pprgProps); + pprgProps = NULL; + } + SysFreeString(strName); + return hr; +} + +HRESULT +CDot11SampleExtUI::CreateSecurityProperties( + ULONG *pcExtensions, + IDot11ExtUIProperty **ppDot11ExtUIProperty + ) +{ + HRESULT hr = ERROR_SUCCESS; + BSTR strName = NULL; + ULONG uCount = 0; + DWORD i = 0; + WCHAR wbuf[128]; + + IDot11SampleExtUISecProperty **pprgProps = NULL; + + uCount = PROP_COUNT_SECURITY; + pprgProps = (IDot11SampleExtUISecProperty**) + CoTaskMemAlloc(sizeof(IDot11SampleExtUISecProperty*) * uCount); + + if (!pprgProps) + { + hr = E_UNEXPECTED; + goto error; + } + + // Since we just have one property of each, we'll + // create one interface first and initialize it separately + IDot11SampleExtUISecProperty *pTempIProp = NULL; + + for (i = 0; i < uCount; ++i) + { + ZeroMemory( + wbuf, + 128 + ); + pTempIProp = NULL; + SysFreeString(strName); + strName = NULL; + + hr = CoCreateInstance( + GUID_SAMPLE_IHVUI_CLSID, + NULL, + CLSCTX_INPROC, + IID_IDot11SampleExtUISecProperty, + (PVOID*)&pTempIProp + ); + + if (FAILED(hr)) + { + continue; + } + + StringCchPrintf( + wbuf, + 128, + wstrSecurityTypes[i] + ); + + strName = SysAllocString(wbuf); + hr = pTempIProp->Initialize(strName, i); + pprgProps[i] = pTempIProp; + } + + if (SUCCEEDED(hr)) + { + *pcExtensions = uCount; + *ppDot11ExtUIProperty = (IDot11ExtUIProperty*)pprgProps; + + // Set the current pointer to NULL so it doesn't get freed at the bottom + pprgProps = NULL; + } + +error: + if (FAILED(hr) && pprgProps) + { + CoTaskMemFree(pprgProps); + pprgProps = NULL; + } + SysFreeString(strName); + return hr; +} + +HRESULT +CDot11SampleExtUI::CreateKeyProperties( + ULONG *pcExtensions, + IDot11ExtUIProperty **ppDot11ExtUIProperty + ) +{ + HRESULT hr = ERROR_SUCCESS; + BSTR strName = NULL; + ULONG uCount = 0; + DWORD i = 0; + WCHAR wbuf[128]; + IDot11SampleExtUIKeyProperty **pprgProps = NULL; + + uCount = g_IHVOneXExtCapability.dwAuthCount; + + pprgProps = (IDot11SampleExtUIKeyProperty**) + CoTaskMemAlloc(sizeof(IDot11SampleExtUIKeyProperty*) * uCount); + + if (!pprgProps) + { + hr = E_UNEXPECTED; + goto error; + } + + // Since we just have one property of each, we'll + // create one interface first and initialize it separately + IDot11SampleExtUIKeyProperty *pTempIProp = NULL; + + for (i = 0; i < uCount; ++i) + { + ZeroMemory( + wbuf, + 128 + ); + pTempIProp = NULL; + SysFreeString(strName); + strName = NULL; + + hr = CoCreateInstance( + GUID_SAMPLE_IHVUI_CLSID, + NULL, + CLSCTX_INPROC, + IID_IDot11SampleExtUIKeyProperty, + (PVOID*)&pTempIProp + ); + + if (FAILED(hr)) + { + continue; + } + + StringCchPrintf( + wbuf, + 128, + g_IHVAuthFriendlyName[g_IHVOneXExtCapability.IhvAuthCiphers[i].IHVAuth] + ); + + strName = SysAllocString(wbuf); + hr = pTempIProp->Initialize((BYTE *) &(g_IHVOneXExtCapability.IhvAuthCiphers[i])); + pprgProps[i] = pTempIProp; + } + + if (SUCCEEDED(hr)) + { + *pcExtensions = uCount; + *ppDot11ExtUIProperty = (IDot11ExtUIProperty*)pprgProps; + + // Set the current pointer to NULL so it doesn't get freed at the bottom + pprgProps = NULL; + } + +error: + if (FAILED(hr) && pprgProps) + { + CoTaskMemFree(pprgProps); + pprgProps = NULL; + } + SysFreeString(strName); + return hr; +} + + +HRESULT +CDot11SampleExtUI::FinalConstruct() +{ + m_pUnkSite = NULL; + m_hFirstPagePsp = NULL; + m_hLastPagePsp = NULL; + m_pUIRequest = NULL; + + return S_OK; +} + +VOID +CDot11SampleExtUI::FinalRelease() +{ + if( m_pUnkSite) + { + m_pUnkSite ->Release(); + m_pUnkSite = NULL; + } + + if( m_pUIRequest) + { + delete m_pUIRequest; + m_pUIRequest = NULL; + } + +} + +// IObjectWithSite +STDMETHODIMP +CDot11SampleExtUI::SetSite ( + IUnknown* pUnkSite + ) +{ + if( m_pUnkSite) + m_pUnkSite ->Release(); + + m_pUnkSite = pUnkSite; + + if( m_pUnkSite) + m_pUnkSite ->AddRef(); + + return S_OK; +} + +STDMETHODIMP +CDot11SampleExtUI::GetSite ( + REFIID riid, + void** ppvSite + ) +{ + *ppvSite = NULL; + + if( m_pUnkSite == NULL) + return E_FAIL; + + return m_pUnkSite ->QueryInterface(riid, ppvSite); +} + +//IWizardExtension +STDMETHODIMP CDot11SampleExtUI::AddPages ( + HPROPSHEETPAGE* aPages, + UINT cPages, + UINT *pnPagesAdded + ) +{ + IPropertyBag *pIPropertyBag = NULL; + + UNREFERENCED_PARAMETER(cPages); + + HRESULT hr = m_pUnkSite->QueryInterface(IID_IPropertyBag, + (VOID **)&pIPropertyBag); + if (SUCCEEDED(hr)) + { + VARIANT v; + VariantInit(&v); + + WCHAR ihvKeyName[IHV_KEY_LENGTH]; + GetClsidPropertyName ( + & GUID_SAMPLE_IHVUI_CLSID, + (LPWSTR) c_szIhvUIRequest, + ihvKeyName, + IHV_KEY_LENGTH + ); + hr = pIPropertyBag->Read( + ihvKeyName, + &v, + NULL); + + if (SUCCEEDED(hr) && (VT_BSTR == V_VT(&v))) + { + if( m_pUIRequest == NULL) + { + m_pUIRequest = new(std::nothrow) IHV_UI_REQUEST; + if (m_pUIRequest == NULL) + { + VariantClear(&v); + pIPropertyBag->Release(); + return E_OUTOFMEMORY; + } + } + memcpy(m_pUIRequest, v.bstrVal, sizeof(IHV_UI_REQUEST)); + } + + VariantClear(&v); + + pIPropertyBag->Release(); + } + + //////////////// + + *pnPagesAdded = 0; + + PROPSHEETPAGE psp = {0}; + + psp.dwSize = sizeof( psp); + psp.hInstance = g_hInst; + psp.dwFlags = PSP_DEFAULT | PSP_USETITLE | PSP_USEHEADERTITLE; + psp.lParam = (LPARAM) this; + + psp.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG_SHOWHELP); + psp.pfnDlgProc = (DLGPROC) CDot11SampleExtUI::HelpDlgProc; + psp.pszHeaderTitle = MAKEINTRESOURCE( IDS_TITLE_SHOWHELP); + m_hFirstPagePsp = CreatePropertySheetPage(& psp); + + psp.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG_GETKEY); + psp.pfnDlgProc = (DLGPROC) CDot11SampleExtUI::GetKeyDlgProc; + psp.pszHeaderTitle = MAKEINTRESOURCE( IDS_TITLE_GETKEY); + HPROPSHEETPAGE hPsp= CreatePropertySheetPage(& psp); + + psp.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG_LASTPAGE); + psp.pfnDlgProc = (DLGPROC) CDot11SampleExtUI::LastPageDlgProc; + psp.pszHeaderTitle = MAKEINTRESOURCE( IDS_TITLE_LASTPAGE); + m_hLastPagePsp = CreatePropertySheetPage(& psp); + + if( m_hFirstPagePsp + && hPsp + && m_hLastPagePsp) + { + aPages[0] = m_hFirstPagePsp; + aPages[1] = hPsp; + aPages[2] = m_hLastPagePsp; + + *pnPagesAdded = 3; + + return S_OK; + } + else + { + if(m_hFirstPagePsp) + { + DestroyPropertySheetPage(m_hFirstPagePsp); + } + + if(hPsp) + { + DestroyPropertySheetPage(hPsp); + } + + if(m_hLastPagePsp) + { + DestroyPropertySheetPage(m_hLastPagePsp); + } + + m_hFirstPagePsp = hPsp = m_hLastPagePsp = NULL; + + return E_FAIL; + } +} + +STDMETHODIMP CDot11SampleExtUI::GetFirstPage ( + HPROPSHEETPAGE *phpage + ) +{ + *phpage = m_hFirstPagePsp; + return S_OK; +} + +STDMETHODIMP +CDot11SampleExtUI::GetLastPage (HPROPSHEETPAGE *phpage) +{ + * phpage = m_hLastPagePsp; + return S_OK; +} + + +BOOL CALLBACK +CDot11SampleExtUI::HelpDlgProc ( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + CDot11SampleExtUI* pthis = NULL; + + switch (uMsg) + { + + case WM_INITDIALOG: + { + pthis = GetThis(hwndDlg, uMsg, wParam, lParam); + if(pthis && pthis->m_pUIRequest) + { + // + // Convert the ANSI string into a WCHAR string and display it + // + int iBufferSize = MultiByteToWideChar(CP_ACP, 0, pthis->m_pUIRequest->title, -1, NULL, 0); + if (iBufferSize > 0) + { + WCHAR *pwszBuffer = new WCHAR[iBufferSize]; + + if (NULL != pwszBuffer) + { + pwszBuffer[0] = 0; + + (VOID)MultiByteToWideChar(CP_ACP, 0, pthis->m_pUIRequest->title, -1, pwszBuffer, iBufferSize); + + SetDlgItemText(hwndDlg, IDC_EDIT_HELPER, pwszBuffer); + + delete[] pwszBuffer; + } + } + } + } + return TRUE; + + case WM_DESTROY: + { + // Don't release our properties here, wait + // rather for Abort or Commit event notifications. + // Then we will have the same values when resurrected. + } + return TRUE; + + case WM_NOTIFY: + { + LPNMHDR pnmh = (LPNMHDR) lParam; + switch (pnmh->code) + { + + case PSN_SETACTIVE : + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT); + return TRUE; + + case PSN_QUERYCANCEL: + { + IWizardSite *pIWizardSite = NULL; + HRESULT hr = S_OK; + + pthis = GetThis(hwndDlg, uMsg, wParam, lParam); + if(pthis != NULL) + { + hr = pthis->m_pUnkSite->QueryInterface(IID_IWizardSite, (VOID **)&pIWizardSite); + if (SUCCEEDED(hr)) + { + HPROPSHEETPAGE hpage = NULL; + + hr = pIWizardSite->GetCancelledPage(&hpage); + if (SUCCEEDED(hr)) + { + PropSheet_SetCurSel(GetParent(hwndDlg), hpage, 0); + } + pIWizardSite->Release(); + } + } + } + return TRUE; + } + } + return FALSE; + } + return FALSE; + +} + +BOOL CALLBACK +CDot11SampleExtUI::GetKeyDlgProc ( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) + +{ + CDot11SampleExtUI* pthis = NULL; + + switch (uMsg) + { + case WM_INITDIALOG: + { + (VOID)GetThis(hwndDlg, uMsg, wParam, lParam); + } + return TRUE; + + case WM_DESTROY: + { + // Don't release our properties here, wait + // rather for Abort or Commit event notifications. + // Then we will have the same values when resurrected. + } + return TRUE; + + case WM_NOTIFY: + { + LPNMHDR pnmh = (LPNMHDR) lParam; + switch (pnmh->code) + { + case PSN_SETACTIVE : + + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT); + return TRUE; + + case PSN_WIZNEXT : + { + WCHAR szBuffer[50 + 1] = {0}; + HRESULT hr = S_OK; + IPropertyBag *pIPropertyBag = NULL; + + GetDlgItemText(hwndDlg, IDC_EDIT_KEY, szBuffer, 50); + pthis = GetThis(hwndDlg, uMsg, wParam, lParam); + + if(pthis) + { + hr = pthis->m_pUnkSite->QueryInterface(IID_IPropertyBag, (VOID **)&pIPropertyBag); + if (SUCCEEDED(hr)) + { + VARIANT v; + VariantInit(&v); + + WCHAR ihvKeyName[IHV_KEY_LENGTH] = {0}; + pthis->GetClsidPropertyName( + &GUID_SAMPLE_IHVUI_CLSID, + (LPWSTR) c_szIhvUIResponse, + ihvKeyName, + IHV_KEY_LENGTH + ); + + // Make sure we remove the previous property if any + hr = pIPropertyBag->Read(ihvKeyName, &v, NULL); + + VariantClear(&v); + V_VT(&v) = VT_BSTR; + v.bstrVal = SysAllocStringByteLen((LPCSTR)szBuffer, IHV_KEY_LENGTH); + + // Write the updated property if any + hr = pIPropertyBag->Write(ihvKeyName, &v); + pIPropertyBag->Release(); + + // + // hr is not used below + // + hr; + } + } + } + return TRUE; + + case PSN_QUERYCANCEL: + { + IWizardSite *pIWizardSite = NULL; + HRESULT hr = S_OK; + + pthis = GetThis(hwndDlg, uMsg, wParam, lParam); + + if(pthis) + { + hr = pthis->m_pUnkSite->QueryInterface(IID_IWizardSite,(VOID **)&pIWizardSite); + if (SUCCEEDED(hr)) + { + HPROPSHEETPAGE hpage = NULL; + hr = pIWizardSite->GetCancelledPage(&hpage); + if (SUCCEEDED(hr)) + { + PropSheet_SetCurSel(GetParent(hwndDlg), hpage, 0); + } + pIWizardSite->Release(); + } + } + } + return TRUE; + } + } + return FALSE; + } + return FALSE; + +} + +BOOL CALLBACK +CDot11SampleExtUI::LastPageDlgProc( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + CDot11SampleExtUI* pthis = NULL; + + switch (uMsg) + { + case WM_INITDIALOG: + { + (VOID)GetThis(hwndDlg, uMsg, wParam, lParam); + } + return TRUE; + + case WM_DESTROY: + { + (VOID)GetThis(hwndDlg, uMsg, wParam, lParam); + } + return TRUE; + + case WM_NOTIFY: + { + LPNMHDR pnmh = (LPNMHDR) lParam; + switch (pnmh->code) + { + case PSN_SETACTIVE : + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT); + return TRUE; + + case PSN_WIZNEXT : + { + IWizardSite *pIWizardSite = NULL; + HRESULT hr = S_OK; + + pthis = GetThis(hwndDlg, uMsg, wParam, lParam); + if(pthis) + { + hr = pthis->m_pUnkSite->QueryInterface(IID_IWizardSite,(VOID **)&pIWizardSite); + if (SUCCEEDED(hr)) + { + HPROPSHEETPAGE hpage = NULL; + + hr = pIWizardSite->GetNextPage(&hpage); + if (SUCCEEDED(hr)) + { + PropSheet_SetCurSel(GetParent(hwndDlg), hpage, 0); + } + pIWizardSite->Release(); + } + } + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LPARAM)-1); + } + return TRUE; + + case PSN_QUERYCANCEL: + { + IWizardSite *pIWizardSite = NULL; + HRESULT hr = S_OK; + + pthis = GetThis(hwndDlg, uMsg, wParam, lParam); + if(pthis) + { + hr = pthis->m_pUnkSite->QueryInterface(IID_IWizardSite,(VOID **)&pIWizardSite); + if (SUCCEEDED(hr)) + { + HPROPSHEETPAGE hpage = NULL; + hr = pIWizardSite->GetCancelledPage(&hpage); + if (SUCCEEDED(hr)) + { + PropSheet_SetCurSel(GetParent(hwndDlg), hpage, 0); + } + pIWizardSite->Release(); + } + } + } + return TRUE; + + } + } + return FALSE; + } + return FALSE; + +} + +HRESULT CDot11SampleExtUI::GetClsidPropertyName ( + _In_ const CLSID* pCLSID, + _In_opt_ PCWSTR pwszPropertyName, + _Out_writes_(maxResultLen) PWSTR pwszResultStr, + _In_ UINT maxResultLen) +{ + #define MIN_BUFFER_SIZE 50 + + wchar_t *pwszCLSID = NULL; + wchar_t *pwszKeyName = NULL; + HRESULT hRetCode = S_OK; + size_t iCharCount = 0; + + // Sanity + //======= + + if( pCLSID == NULL || + pwszResultStr == NULL || + maxResultLen < MIN_BUFFER_SIZE + ) + { + return E_INVALIDARG; + } + + // Convert CLSID to string + //======================== + + hRetCode = StringFromCLSID(*pCLSID, &pwszCLSID); + if(FAILED(hRetCode)) + { + goto Done; + } + + // Allocate buffer for entire CLSID\PropertyName string + //===================================================== + + iCharCount = wcslen(pwszCLSID) + 1; + if(pwszPropertyName) + { + iCharCount += wcslen(pwszPropertyName); + } + + pwszKeyName = new(std::nothrow) wchar_t[iCharCount]; + if(pwszKeyName == NULL) + { + hRetCode = E_OUTOFMEMORY; + goto Done; + } + + swprintf_s(pwszKeyName, iCharCount, L"%s%s", pwszCLSID, pwszPropertyName ? pwszPropertyName : L""); + + // Copy as much as we can to the target buffer + //============================================ + + wcsncpy_s(pwszResultStr, maxResultLen, pwszKeyName, _TRUNCATE); + +Done: + + if(pwszKeyName != NULL) + { + delete [] pwszKeyName; + } + + if(pwszCLSID != NULL) + { + CoTaskMemFree(pwszCLSID); + } + + return hRetCode; +} + + + diff --git a/network/wlan/ihvsampleui/IHVSampleExtUI.h b/network/wlan/ihvsampleui/IHVSampleExtUI.h new file mode 100644 index 00000000..474c56a3 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleExtUI.h @@ -0,0 +1,238 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#ifndef _IHVSAMPLEEXTUI_H_ +#define _IHVSAMPLEEXTUI_H_ + +#include "precomp.h" + +// object ref count +extern long g_objRefCount; + +//lock count on server +extern long g_serverLock; + +#define IHV_KEY_LENGTH 64 + +#define MAX_IHV_CIPHERS 6 +#define MAX_IHV_AUTHS 6 + +// IHV Auth types +typedef enum _IHV_AUTH_TYPE { + IHVAuthV1, + IHVAuthV2, + IHVAuthV3, + IHVAuthInvalid +} IHV_AUTH_TYPE, *PIHV_AUTH_TYPE; + +// IHV cipher types +typedef enum _IHV_CIPHER_TYPE { + None, + IHVCipher1, + IHVCipher2, + IHVCipher3, + IHVCipherInvalid +} IHV_CIPHER_TYPE, *PIHV_CIPHER_TYPE; + +// structure holding valid ciphers for a given auth +typedef struct _IHV_AUTH_CIPHERS { + IHV_AUTH_TYPE IHVAuth; + DWORD dwCipherCount; + IHV_CIPHER_TYPE IHVCiphers[MAX_IHV_CIPHERS]; +} IHV_AUTH_CIPHERS, *PIHV_AUTH_CIPHERS; + +// structure for all auths and corresponding ciphers +typedef struct _IHV_AUTH_CIPHER_CAPABILITY { + DWORD dwAuthCount; + IHV_AUTH_CIPHERS IhvAuthCiphers[MAX_IHV_AUTHS]; +} IHV_AUTH_CIPHER_CAPABILITY, *PIHV_AUTH_CIPHER_CAPABILITY; + +typedef struct _IHV_SECURITY_CONFIG { + IHV_AUTH_TYPE Auth; + IHV_CIPHER_TYPE Cipher; +} IHV_SECURITY_CONFIG, *PIHV_SECURITY_CONFIG; + +extern IHV_AUTH_CIPHER_CAPABILITY g_IHVOneXExtCapability; + +extern LPWSTR g_IHVAuthFriendlyName[]; +extern LPWSTR g_IHVCipherFriendlyName[]; + + +#define PROP_COUNT_CONNECTION 1 + +#define PROP_COUNT_SECURITY 2 +#define PROP_COUNT_SEC_CIPHERS 2 +typedef enum _IHV_SECURITY_TYPE { + IHVSecurityV1, + IHVSecurityV2, + IHVSecurityInvalid +} IHV_SECURITY_TYPE, *PIHV_SECURITY_TYPE; +static LPWSTR wstrSecurityTypes[] = { L"IHV Security v1", L"IHV Security v2" }; + +#define PROP_COUNT_KEYEXTENSION 3 +static LPWSTR wstrAuthArray[] = { L"IHVAuth Open-with-1X", L"IHVAuth v1", L"IHVAuth v2" }; + + + + +#define IHV_CIPHER_COUNT 3 +static LPWSTR wstrCipherArray[] = { L"None", L"IHVCipher v1", L"IHVCipher v2" }; +static LPWSTR wstr1XCipherArray[] = { L"IHVCipher WEP"}; +static BSTR bstrCipherArray[IHV_CIPHER_COUNT] = {0}; + +typedef struct _IHV_CIPHERS_FOR_AUTH_INFO +{ + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO displayInfo[IHV_CIPHER_COUNT]; +} IHV_CIPHERS_FOR_AUTH_INFO; +static DOT11_EXT_UI_PROPERTY_DISPLAY_INFO cipherOne = {1, DOT11_EXT_UI_DISPLAY_INFO_CIPHER, 0}; +static DOT11_EXT_UI_PROPERTY_DISPLAY_INFO cipherTwo = {2, DOT11_EXT_UI_DISPLAY_INFO_CIPHER, 0}; +static DOT11_EXT_UI_PROPERTY_DISPLAY_INFO cipherThree = {3, DOT11_EXT_UI_DISPLAY_INFO_CIPHER, 0}; +static DOT11_EXT_UI_PROPERTY_DISPLAY_INFO ciphersInfoArray[MAX_IHV_AUTHS][MAX_IHV_CIPHERS] = {0}; + + +//////// structures for balloon ///////// + +struct IHV_UI_REQUEST +{ + char title[80]; + char help[80]; + + IHV_UI_REQUEST() + { + memset(this, 0, sizeof(IHV_UI_REQUEST)); + } +}; + +struct IHV_UI_RESPONSE +{ + char key[100]; + int num[150]; + + IHV_UI_RESPONSE() + { + memset(this, 0, sizeof(IHV_UI_RESPONSE)); + } +}; + + +class CDot11SampleExtUI: public IDot11SampleExtUI, public IWizardExtension, public IObjectWithSite +{ +public: + CDot11SampleExtUI(); + + ~CDot11SampleExtUI(); + + // IUnknown Implementation + BEGIN_INTERFACE_TABLE() + IMPLEMENTS_INTERFACE(IDot11ExtUI) + IMPLEMENTS_INTERFACE(IDot11SampleExtUI) + IMPLEMENTS_INTERFACE(IWizardExtension) + IMPLEMENTS_INTERFACE(IObjectWithSite) + END_INTERFACE_TABLE(); + + // Used to get the IHV friendly name + STDMETHODIMP + GetDot11ExtUIFriendlyName(BSTR* bstrFriendlyName); + + // Used to display an IHV specific connection page + STDMETHODIMP + GetDot11ExtUIProperties( + DOT11_EXT_UI_PROPERTY_TYPE ExtType, + ULONG *pcExtensions, + IDot11ExtUIProperty **ppDot11ExtUIProperty + ); + + STDMETHODIMP + GetDot11ExtUIBalloonText( + BSTR pIHVUIRequest, // the UI request structure from IHV + BSTR* pwszBalloonText // the balloon text to be displayed + ); + + HRESULT + CreateConnectionProperties( + ULONG *pcExtensions, + IDot11ExtUIProperty **ppDot11ExtUIProperty + ); + + HRESULT + CreateSecurityProperties( + ULONG *pcExtensions, + IDot11ExtUIProperty **ppDot11ExtUIProperty + ); + + HRESULT + CreateKeyProperties( + ULONG *pcExtensions, + IDot11ExtUIProperty **ppDot11ExtUIProperty + ); + + // IObjectWithSite + STDMETHOD (SetSite) ( + IUnknown* pUnkSite + ); + + STDMETHOD (GetSite) ( + REFIID riid, + void** ppvSite + ); + + //IWizardExtension + STDMETHOD (AddPages) ( + HPROPSHEETPAGE* aPages, + UINT cPages, + UINT *pnPagesAdded + ); + + STDMETHOD (GetFirstPage) ( + HPROPSHEETPAGE *phpage + ); + + STDMETHOD (GetLastPage) ( + HPROPSHEETPAGE *phpage + ); + +private: + HRESULT FinalConstruct(); + void FinalRelease(); + static BOOL CALLBACK GetKeyDlgProc ( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ); + + + static BOOL CALLBACK HelpDlgProc ( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ); + + static BOOL CALLBACK LastPageDlgProc ( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ); + + HRESULT GetClsidPropertyName ( + _In_ const CLSID* pCLSID, + _In_opt_ PCWSTR pwszPropertyName, + _Out_writes_(maxResultLen) PWSTR pResultStr, + _In_ UINT maxResultLen); + +private: + IHV_UI_REQUEST* m_pUIRequest; + IHV_UI_RESPONSE m_UIResponse; + + IUnknown* m_pUnkSite; + + HPROPSHEETPAGE m_hFirstPagePsp; + HPROPSHEETPAGE m_hLastPagePsp; +}; + + +#endif _IHVSAMPLEEXTUI_H_ diff --git a/network/wlan/ihvsampleui/IHVSampleExtUICon.cpp b/network/wlan/ihvsampleui/IHVSampleExtUICon.cpp new file mode 100644 index 00000000..e5eeb6cd --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleExtUICon.cpp @@ -0,0 +1,345 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#include "precomp.h" + +extern HINSTANCE g_hInst; + +// used by the con prop extensions +CIhvConnectivityProfile* pIhvConProfile; + + +CDot11SampleExtUIConProperty::CDot11SampleExtUIConProperty(): + m_crefCount(0), m_fInitialized(false), + m_ExtType(DOT11_EXT_UI_CONNECTION), m_fModified(FALSE) +{ + m_bstrFN = NULL; + InterlockedIncrement(&g_objRefCount); +} + +CDot11SampleExtUIConProperty::~CDot11SampleExtUIConProperty() +{ + SysFreeString(m_bstrFN); + InterlockedDecrement(&g_objRefCount); +} + + +STDMETHODIMP +CDot11SampleExtUIConProperty::GetDot11ExtUIPropertyFriendlyName(BSTR* bstrPropertyName) +{ + HRESULT hr = E_INVALIDARG; + if (false == m_fInitialized) + { + return hr; + } + + if (NULL != bstrPropertyName) + { + *bstrPropertyName = SysAllocString(m_bstrFN); + hr = S_OK; + } + + return hr; +} + +//Used to extend property +STDMETHODIMP +CDot11SampleExtUIConProperty::DisplayDot11ExtUIProperty( + HWND hParent, // Parent Window Handle + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ) +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(pIHVParams); + + if (!m_fInitialized) + { + hr = E_INVALIDARG; + goto error; + } + + // Store the passed-in string in a member variable so dialog box can display it + pIhvConProfile = new(std::nothrow) CIhvConnectivityProfile(); + if (pIhvConProfile == NULL) + { + hr = E_OUTOFMEMORY; + goto error; + } + + pIhvConProfile->LoadXml(bstrIHVProfile); + m_fModified = FALSE; + + // Dialog will store the string in a member variable + DialogBoxParam( + g_hInst, + MAKEINTRESOURCE(IDD_PROPPAGE_SMALL), + hParent, + SimpleDialogProcCon, + (LPARAM)pIhvConProfile + ); + + m_fModified = pIhvConProfile->GetModified(); + + if (NULL != bstrModifiedIHVProfile) + { + if (m_fModified) + { + pIhvConProfile->EmitXml(bstrModifiedIHVProfile); + } + } + + if (NULL != pbIsModified) + { + *pbIsModified = m_fModified; + } + +error: + if(pIhvConProfile) + { + delete pIhvConProfile; + pIhvConProfile = NULL; + } + + return hr; +} + + +//Used to get the currently chosen entry to display as selected in the dropdown list +STDMETHODIMP +CDot11SampleExtUIConProperty::Dot11ExtUIPropertyGetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BOOL* pfIsSelected // flag denoting if this is the selected profile + ) +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(bstrIHVProfile); + UNREFERENCED_PARAMETER(pIHVParams); + + // since there is only one connection profile always set it to true + *pfIsSelected = TRUE; + + return hr; +} + +//Used to set the current entry as chosen from the dropdown list +STDMETHODIMP +CDot11SampleExtUIConProperty::Dot11ExtUIPropertySetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ) +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(pIHVParams); + + if(bstrModifiedIHVProfile == NULL || pbIsModified == NULL) + { + hr = E_INVALIDARG; + goto error; + } + + // in case the profile is NULL this will supply the default + pIhvConProfile = new(std::nothrow) CIhvConnectivityProfile(); + if (pIhvConProfile == NULL) + { + hr = E_OUTOFMEMORY; + goto error; + } + + pIhvConProfile->LoadXml(bstrIHVProfile); + + pIhvConProfile->EmitXml(bstrModifiedIHVProfile); + *pbIsModified = pIhvConProfile->GetModified(); + +error: + if(pIhvConProfile != NULL) + { + delete pIhvConProfile; + pIhvConProfile = NULL; + } + return hr; +} + + +STDMETHODIMP +CDot11SampleExtUIConProperty::Dot11ExtUIPropertyHasConfigurationUI( + BOOL *fHasConfigurationUI + ) +{ + // this page always wants to show a config UI + *fHasConfigurationUI = TRUE; + return S_OK; +} + +//Used to get additional display data (ciphers for auth types) +STDMETHODIMP +CDot11SampleExtUIConProperty::Dot11ExtUIPropertyGetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be described + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + ULONG *pcEntries, // number of dependent strings + ULONG *puDefaultSelection, // the entry in the array to be selected by default + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO **ppDot11ExtUIProperty // array of returned info structure + ) +{ + UNREFERENCED_PARAMETER(dot11ExtUIDisplayInfoType); + UNREFERENCED_PARAMETER(pIHVParams); + UNREFERENCED_PARAMETER(bstrIHVProfile); + // we have no additional data to display + *pcEntries = 0; + *puDefaultSelection = 0; + *ppDot11ExtUIProperty = NULL; + return E_NOTIMPL; +} + +STDMETHODIMP +CDot11SampleExtUIConProperty::Dot11ExtUIPropertySetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be modified + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO *pDot11ExtUIProperty, // selected info structure + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ) +{ + UNREFERENCED_PARAMETER(dot11ExtUIDisplayInfoType); + UNREFERENCED_PARAMETER(bstrIHVProfile); + UNREFERENCED_PARAMETER(pIHVParams); + UNREFERENCED_PARAMETER(pDot11ExtUIProperty); + UNREFERENCED_PARAMETER(bstrModifiedIHVProfile); + UNREFERENCED_PARAMETER(pbIsModified); + return E_NOTIMPL; +} + +STDMETHODIMP +CDot11SampleExtUIConProperty::Dot11ExtUIPropertyIsStandardSecurity( + BOOL *fIsStandardSecurity, // if this interface is a standard auth method + DOT11_EXT_UI_SECURITY_TYPE *dot11ExtUISecurityType // which of the standard auth methods it is + ) +{ + UNREFERENCED_PARAMETER(dot11ExtUISecurityType); + *fIsStandardSecurity = FALSE; + return E_NOTIMPL; +} + +STDMETHODIMP +CDot11SampleExtUIConProperty::Initialize(BSTR bstrPropertyName) +{ + HRESULT hr = E_INVALIDARG; + if (false == m_fInitialized) + { + // Set the FriendlyName + m_bstrFN = SysAllocString(bstrPropertyName); + m_fInitialized = true; + hr = S_OK; + } + return hr; +} + +INT_PTR CALLBACK +SimpleDialogProcCon( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +) +{ + BOOL fRetVal = FALSE; + WCHAR szBuf[256] = {0}; + DWORD dwValue = 0; + BSTR bstrText = NULL; + + UNREFERENCED_PARAMETER(lParam); + + if(!pIhvConProfile) + { + goto error; + } + + switch(uMsg) + { + case WM_INITDIALOG: + { + // Dialog title + WCHAR strDialogTitle[MAX_PATH] = {0}; + (VOID)::LoadString( + g_hInst, + IDS_IHV_DEFAULT_CON_TITLE, + strDialogTitle, + MAX_PATH + ); + + SetWindowText(hwndDlg, strDialogTitle); + } + + // check the checkbox if needed + if(FAILED(pIhvConProfile->GetParamDWORD(&dwValue))) + { + dwValue = 0; + } + ::SendMessage( + GetDlgItem(hwndDlg, IDC_USE_FASTHANDOFF), + BM_SETCHECK, + (WPARAM)(int)dwValue, + 0L + ); + + // Set text in the textbox + if(FAILED(pIhvConProfile->GetParamBSTR(&bstrText))) + { + bstrText = NULL; + } + SetWindowText(GetDlgItem(hwndDlg, IDC_PARAM_BOX), bstrText); + + fRetVal = TRUE; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case ID_OK: + GetWindowText(GetDlgItem(hwndDlg, IDC_PARAM_BOX), szBuf, 255); + if(szBuf) + { + DWORD dwNewValue = 0; + + // get the button state and record it + dwNewValue = (int)::SendMessage( + GetDlgItem(hwndDlg, IDC_USE_FASTHANDOFF), + BM_GETCHECK, + 0L, + 0L + ); + + pIhvConProfile->SetParamDWORD(dwNewValue); + pIhvConProfile->SetParamBSTR(szBuf); + + // Notify the owner window to carry out the task. + EndDialog(hwndDlg, 1); + fRetVal = TRUE; + } + break; + + case ID_CANCEL: + EndDialog(hwndDlg, 0); + fRetVal = TRUE; + break; + } + break; + } + +error: + return fRetVal; +} + + + + + diff --git a/network/wlan/ihvsampleui/IHVSampleExtUICon.h b/network/wlan/ihvsampleui/IHVSampleExtUICon.h new file mode 100644 index 00000000..fd58c106 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleExtUICon.h @@ -0,0 +1,103 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#ifndef _IHVSAMPLEEXTUICON_H_ +#define _IHVSAMPLEEXTUICON_H_ + +class CDot11SampleExtUIConProperty: public IDot11SampleExtUIConProperty +{ +public: + CDot11SampleExtUIConProperty(); + + ~CDot11SampleExtUIConProperty(); + + // IUnknown Implementation + BEGIN_INTERFACE_TABLE() + IMPLEMENTS_INTERFACE(IDot11SampleExtUIConProperty) + END_INTERFACE_TABLE(); + + STDMETHODIMP + GetDot11ExtUIPropertyFriendlyName( + BSTR* bstrPropertyName // IHV friendly name + ); + + //Used to extend property + STDMETHODIMP + DisplayDot11ExtUIProperty( + HWND hParent, // Parent Window Handle + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ); + + //Used to get the currently chosen entry to display as selected in the dropdown list + STDMETHODIMP + Dot11ExtUIPropertyGetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BOOL* pfIsSelected // flag denoting if this is the selected profile + ); + + //Used to set the current entry as chosen from the dropdown list + STDMETHODIMP + Dot11ExtUIPropertySetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ); + + STDMETHODIMP + Dot11ExtUIPropertyHasConfigurationUI(BOOL *fHasConfigurationUI); + + //Used to get additional display data (ciphers for auth types) + STDMETHODIMP + Dot11ExtUIPropertyGetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be described + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + ULONG *pcEntries, // number of dependent strings + ULONG *puDefaultSelection, // the entry in the array to be selected by default + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO **ppDot11ExtUIProperty // array of returned info structure + ); + + STDMETHODIMP + Dot11ExtUIPropertySetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be modified + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO *pDot11ExtUIProperty, // selected info structure + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ); + + STDMETHODIMP + Dot11ExtUIPropertyIsStandardSecurity( + BOOL *fIsStandardSecurity, // if this interface is a standard auth method + DOT11_EXT_UI_SECURITY_TYPE *dot11ExtUISecurityType // which of the standard auth methods it is + ); + + // initialize the connection page + STDMETHODIMP + Initialize(BSTR bstrPropertyName); + +private: + bool m_fInitialized; + BSTR m_bstrFN; + DOT11_EXT_UI_PROPERTY_TYPE m_ExtType; + BOOL m_fModified; +}; + + +INT_PTR CALLBACK +SimpleDialogProcCon( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +); + +#endif _IHVSAMPLEEXTUICON_H_ diff --git a/network/wlan/ihvsampleui/IHVSampleExtUIKey.cpp b/network/wlan/ihvsampleui/IHVSampleExtUIKey.cpp new file mode 100644 index 00000000..19c9610a --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleExtUIKey.cpp @@ -0,0 +1,462 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#include "precomp.h" + +extern HINSTANCE g_hInst; + +// used by the con prop extensions +CIhvSecurityProfile* pIhvKeyProfile; + + +CDot11SampleExtUIKeyProperty::CDot11SampleExtUIKeyProperty(): + m_crefCount(0), m_fInitialized(false), + m_ExtType(DOT11_EXT_UI_KEYEXTENSION), m_fModified(FALSE) +{ + InterlockedIncrement(&g_objRefCount); + memset( + &m_IHVAuthCiphers, + 0, + sizeof(IHV_AUTH_CIPHERS) + ); + m_bstrFN = NULL; +} + +CDot11SampleExtUIKeyProperty::~CDot11SampleExtUIKeyProperty() +{ + if(m_bstrFN != NULL) { + SysFreeString(m_bstrFN); + } + + InterlockedDecrement(&g_objRefCount); +} + + +STDMETHODIMP +CDot11SampleExtUIKeyProperty::GetDot11ExtUIPropertyFriendlyName(BSTR* bstrPropertyName) +{ + HRESULT hr = E_INVALIDARG; + if (false == m_fInitialized) + { + return hr; + } + + if (NULL != g_IHVAuthFriendlyName[m_IHVAuthCiphers.IHVAuth]) + { + *bstrPropertyName = SysAllocString(g_IHVAuthFriendlyName[m_IHVAuthCiphers.IHVAuth]); + hr = S_OK; + } + + return hr; +} + +//Used to extend property +STDMETHODIMP +CDot11SampleExtUIKeyProperty::DisplayDot11ExtUIProperty( + HWND hParent, // Parent Window Handle + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ) +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(pIHVParams); + + if (!m_fInitialized) + { + hr = E_INVALIDARG; + goto error; + } + + // Store the passed-in string in a member variable so dialog box can display it + pIhvKeyProfile = new(std::nothrow) CIhvSecurityProfile(); + if (pIhvKeyProfile == NULL) + { + hr = E_OUTOFMEMORY; + goto error; + } + + pIhvKeyProfile->LoadXml(bstrIHVProfile); + m_fModified = FALSE; + + // Dialog will store the string in a member variable + DialogBoxParam( + g_hInst, + MAKEINTRESOURCE(IDD_PROPPAGE_SMALL), + hParent, + SimpleDialogProcKey, + (LPARAM)pIhvKeyProfile + ); + + m_fModified = pIhvKeyProfile->GetModified(); + + if (NULL != bstrModifiedIHVProfile) + { + if (m_fModified) + { + pIhvKeyProfile->EmitXml(bstrModifiedIHVProfile); + } + } + + if (NULL != pbIsModified) + { + *pbIsModified = m_fModified; + } + +error: + if(pIhvKeyProfile) + { + delete pIhvKeyProfile; + pIhvKeyProfile = NULL; + } + + return hr; +} + + +//Used to get the currently chosen entry to display as selected in the dropdown list +STDMETHODIMP +CDot11SampleExtUIKeyProperty::Dot11ExtUIPropertyGetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BOOL* pfIsSelected // flag denoting if this is the selected profile + ) +{ + HRESULT hr = S_OK; + IHV_AUTH_TYPE currentAuthType = IHVAuthInvalid; + UNREFERENCED_PARAMETER(pIHVParams); + + if(pfIsSelected == NULL) + { + hr = E_INVALIDARG; + goto error; + } + + *pfIsSelected = FALSE; + + pIhvKeyProfile = new(std::nothrow) CIhvSecurityProfile(); + if (pIhvKeyProfile == NULL) + { + hr = E_OUTOFMEMORY; + goto error; + } + + pIhvKeyProfile->LoadXml(bstrIHVProfile); + + hr = pIhvKeyProfile->GetAuthType(¤tAuthType); + if(FAILED(hr)) + { + // if it fails then choose a default selected auth + hr = S_OK; + if(IHVAuthV1 == m_IHVAuthCiphers.IHVAuth) + { + *pfIsSelected = TRUE; + } + } + else if(currentAuthType == m_IHVAuthCiphers.IHVAuth) + { + *pfIsSelected = TRUE; + } + +error: + if(pIhvKeyProfile != NULL) + { + delete pIhvKeyProfile; + pIhvKeyProfile = NULL; + } + return hr; +} + +//Used to set the current entry as chosen from the dropdown list +STDMETHODIMP +CDot11SampleExtUIKeyProperty::Dot11ExtUIPropertySetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ) +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(pIHVParams); + + if(bstrModifiedIHVProfile == NULL || pbIsModified == NULL) + { + hr = E_INVALIDARG; + goto error; + } + + pIhvKeyProfile = new(std::nothrow) CIhvSecurityProfile(); + if (pIhvKeyProfile == NULL) + { + hr = E_OUTOFMEMORY; + goto error; + } + + pIhvKeyProfile->LoadXml(bstrIHVProfile); + + pIhvKeyProfile->SetAuthType(m_IHVAuthCiphers.IHVAuth); + pIhvKeyProfile->SetFullSecurityFlag(FALSE); + + pIhvKeyProfile->EmitXml(bstrModifiedIHVProfile); + *pbIsModified = pIhvKeyProfile->GetModified(); + +error: + if(pIhvKeyProfile != NULL) + { + delete pIhvKeyProfile; + pIhvKeyProfile = NULL; + } + return hr; +} + + +STDMETHODIMP +CDot11SampleExtUIKeyProperty::Dot11ExtUIPropertyHasConfigurationUI( + BOOL *fHasConfigurationUI) +{ + // this page always wants to show a config UI unless its of IHVAuthOpen1X auth + *fHasConfigurationUI = (m_IHVAuthCiphers.IHVAuth == IHVAuthV3)?FALSE:TRUE; + return S_OK; +} + +//Used to get additional display data (ciphers for auth types) +STDMETHODIMP +CDot11SampleExtUIKeyProperty::Dot11ExtUIPropertyGetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be described + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + ULONG *pcEntries, // number of dependent strings + ULONG *puDefaultSelection, // the entry in the array to be selected by default + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO **ppDot11ExtUIProperty // array of returned info structure + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + DWORD dwDefaultSelection = 0; + CIhvSecurityProfile IhvSecurityProfile; + IHV_CIPHER_TYPE cipherType = IHVCipherInvalid; + + UNREFERENCED_PARAMETER(pIHVParams); + + if(dot11ExtUIDisplayInfoType != DOT11_EXT_UI_DISPLAY_INFO_CIPHER) + { + hr = E_NOTIMPL; + goto error; + } + + IhvSecurityProfile.LoadXml(bstrIHVProfile); + hr = IhvSecurityProfile.GetCipherType(&cipherType); + if(FAILED(hr)) + { + goto error; + } + + for(i = 0; i < m_IHVAuthCiphers.dwCipherCount; ++i) + { + ciphersInfoArray[m_IHVAuthCiphers.IHVAuth][i].dwDataKey = m_IHVAuthCiphers.IHVCiphers[i]; + ciphersInfoArray[m_IHVAuthCiphers.IHVAuth][i].dot11ExtUIDisplayInfoType = DOT11_EXT_UI_DISPLAY_INFO_CIPHER; + ciphersInfoArray[m_IHVAuthCiphers.IHVAuth][i].bstrDisplayText = SysAllocString(g_IHVCipherFriendlyName[m_IHVAuthCiphers.IHVCiphers[i]]); + + if(m_IHVAuthCiphers.IHVCiphers[i] == cipherType) + { + dwDefaultSelection = i; + } + } + + // for the given auth type we want to return the list of compatible ciphers + *ppDot11ExtUIProperty = ciphersInfoArray[m_IHVAuthCiphers.IHVAuth]; + *pcEntries = m_IHVAuthCiphers.dwCipherCount; + *puDefaultSelection = dwDefaultSelection; + +error: + return hr; +} + + +STDMETHODIMP +CDot11SampleExtUIKeyProperty::Dot11ExtUIPropertySetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be modified + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO *pDot11ExtUIProperty, // selected info structure + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ) +{ + HRESULT hr = S_OK; + + CIhvSecurityProfile IhvSecurityProfile; + + UNREFERENCED_PARAMETER(pIHVParams); + + if(dot11ExtUIDisplayInfoType != DOT11_EXT_UI_DISPLAY_INFO_CIPHER) + { + hr = E_NOTIMPL; + goto error; + } + + if(pDot11ExtUIProperty == NULL) + { + hr = E_INVALIDARG; + goto error; + } + + hr = IhvSecurityProfile.LoadXml(bstrIHVProfile); + if(FAILED(hr)) + { + goto error; + } + hr = IhvSecurityProfile.SetCipherType((IHV_CIPHER_TYPE)pDot11ExtUIProperty->dwDataKey); + if(FAILED(hr)) + { + goto error; + } + + hr = IhvSecurityProfile.EmitXml(bstrModifiedIHVProfile); + *pbIsModified = IhvSecurityProfile.GetModified(); + +error: + return hr; +} + + +STDMETHODIMP +CDot11SampleExtUIKeyProperty::Dot11ExtUIPropertyIsStandardSecurity( + BOOL *fIsStandardSecurity, // if this interface is a standard auth method + DOT11_EXT_UI_SECURITY_TYPE *dot11ExtUISecurityType // which of the standard auth methods it is + ) +{ + *fIsStandardSecurity = FALSE; + + if (m_IHVAuthCiphers.IHVAuth == IHVAuthV1) + { + *fIsStandardSecurity = TRUE; + *dot11ExtUISecurityType = DOT11_EXT_UI_SECURITY_8021X; + } + + return S_OK; +} + + +STDMETHODIMP +CDot11SampleExtUIKeyProperty::Initialize(BYTE* pbData) +{ + HRESULT hr = E_INVALIDARG; + PIHV_AUTH_CIPHERS pIhvAuthCiphers = NULL; + pIhvAuthCiphers = (PIHV_AUTH_CIPHERS) pbData; + + if (false == m_fInitialized) + { + // Set the FriendlyName + m_fInitialized = true; + memcpy( + &m_IHVAuthCiphers, + pIhvAuthCiphers, + sizeof(IHV_AUTH_CIPHERS) + ); + hr = S_OK; + } + return hr; +} + +INT_PTR CALLBACK +SimpleDialogProcKey( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +) +{ + BOOL fRetVal = FALSE; + WCHAR szBuf[256] = {0}; + DWORD dwValue = 0; + BSTR bstrText = NULL; + + UNREFERENCED_PARAMETER(lParam); + + if(!pIhvKeyProfile) + { + goto error; + } + + switch(uMsg) + { + case WM_INITDIALOG: + { + // Dialog title + WCHAR strDialogTitle[MAX_PATH] = {0}; + (VOID)::LoadString( + g_hInst, + IDS_IHV_DEFAULT_KEY_TITLE, + strDialogTitle, + MAX_PATH + ); + + SetWindowText(hwndDlg, strDialogTitle); + } + + // check the checkbox if needed + if(FAILED(pIhvKeyProfile->GetParamDWORD(&dwValue))) + { + dwValue = 0; + } + ::SendMessage( + GetDlgItem(hwndDlg, IDC_USE_FASTHANDOFF), + BM_SETCHECK, + (WPARAM)(int)dwValue, + 0L + ); + + // Set text in the textbox + if(FAILED(pIhvKeyProfile->GetParamBSTR(&bstrText))) + { + bstrText = NULL; + } + SetWindowText(GetDlgItem(hwndDlg, IDC_PARAM_BOX), bstrText); + + fRetVal = TRUE; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case ID_OK: + GetWindowText(GetDlgItem(hwndDlg, IDC_PARAM_BOX), szBuf, 255); + if(szBuf) + { + DWORD dwNewValue = 0; + + // get the button state and record it + dwNewValue = (int)::SendMessage( + GetDlgItem(hwndDlg, IDC_USE_FASTHANDOFF), + BM_GETCHECK, + 0L, + 0L + ); + + pIhvKeyProfile->SetParamDWORD(dwNewValue); + pIhvKeyProfile->SetParamBSTR(szBuf); + pIhvKeyProfile->SetFullSecurityFlag(FALSE); + + // Notify the owner window to carry out the task. + EndDialog(hwndDlg, 1); + fRetVal = TRUE; + } + break; + + case ID_CANCEL: + EndDialog(hwndDlg, 0); + fRetVal = TRUE; + break; + } + break; + } + +error: + return fRetVal; +} + + diff --git a/network/wlan/ihvsampleui/IHVSampleExtUIKey.h b/network/wlan/ihvsampleui/IHVSampleExtUIKey.h new file mode 100644 index 00000000..c106e549 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleExtUIKey.h @@ -0,0 +1,104 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#ifndef _IHVSAMPLEEXTUIKEY_H_ +#define _IHVSAMPLEEXTUIKEY_H_ + +class CDot11SampleExtUIKeyProperty: public IDot11SampleExtUIKeyProperty +{ +public: + CDot11SampleExtUIKeyProperty(); + + ~CDot11SampleExtUIKeyProperty(); + + // IUnknown Implementation + BEGIN_INTERFACE_TABLE() + IMPLEMENTS_INTERFACE(IDot11SampleExtUIKeyProperty) + END_INTERFACE_TABLE(); + + STDMETHODIMP + GetDot11ExtUIPropertyFriendlyName( + BSTR* bstrPropertyName // IHV friendly name + ); + + //Used to extend property + STDMETHODIMP + DisplayDot11ExtUIProperty( + HWND hParent, // Parent Window Handle + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ); + + //Used to get the currently chosen entry to display as selected in the dropdown list + STDMETHODIMP + Dot11ExtUIPropertyGetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BOOL* pfIsSelected // flag denoting if this is the selected profile + ); + + //Used to set the current entry as chosen from the dropdown list + STDMETHODIMP + Dot11ExtUIPropertySetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ); + + STDMETHODIMP + Dot11ExtUIPropertyHasConfigurationUI(BOOL *fHasConfigurationUI); + + //Used to get additional display data (ciphers for auth types) + STDMETHODIMP + Dot11ExtUIPropertyGetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be described + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + ULONG *pcEntries, // number of dependent strings + ULONG *puDefaultSelection, // the entry in the array to be selected by default + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO **ppDot11ExtUIProperty // array of returned info structure + ); + + STDMETHODIMP + Dot11ExtUIPropertySetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be modified + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO *pDot11ExtUIProperty, // selected info structure + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ); + + STDMETHODIMP + Dot11ExtUIPropertyIsStandardSecurity( + BOOL *fIsStandardSecurity, // if this interface is a standard auth method + DOT11_EXT_UI_SECURITY_TYPE *dot11ExtUISecurityType // which of the standard auth methods it is + ); + + // initialize the key extension page + STDMETHODIMP + Initialize(BYTE* pbData); + +private: + bool m_fInitialized; + BSTR m_bstrFN; + DOT11_EXT_UI_PROPERTY_TYPE m_ExtType; + BOOL m_fModified; + IHV_AUTH_CIPHERS m_IHVAuthCiphers; +}; + + +INT_PTR CALLBACK +SimpleDialogProcKey( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +); + +#endif _IHVSAMPLEEXTUIKEY_H_ diff --git a/network/wlan/ihvsampleui/IHVSampleExtUISec.cpp b/network/wlan/ihvsampleui/IHVSampleExtUISec.cpp new file mode 100644 index 00000000..1cfa3714 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleExtUISec.cpp @@ -0,0 +1,434 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#include "precomp.h" + +extern HINSTANCE g_hInst; + +// used by the con prop extensions +CIhvSecurityProfile* pIhvSecProfile; + +CDot11SampleExtUISecProperty::CDot11SampleExtUISecProperty(): + m_crefCount(0), m_fInitialized(false), + m_ExtType(DOT11_EXT_UI_SECURITY), m_fModified(FALSE), + m_IhvSecurityType(IHVSecurityInvalid) +{ + m_bstrFN = NULL; + InterlockedIncrement(&g_objRefCount); +} + +CDot11SampleExtUISecProperty::~CDot11SampleExtUISecProperty() +{ + SysFreeString(m_bstrFN); + InterlockedDecrement(&g_objRefCount); +} + + +STDMETHODIMP +CDot11SampleExtUISecProperty::GetDot11ExtUIPropertyFriendlyName(BSTR* bstrPropertyName) +{ + HRESULT hr = E_INVALIDARG; + if (false == m_fInitialized) + { + return hr; + } + + if (NULL != bstrPropertyName) + { + *bstrPropertyName = SysAllocString(m_bstrFN); + hr = S_OK; + } + + return hr; +} + +//Used to extend property +STDMETHODIMP +CDot11SampleExtUISecProperty::DisplayDot11ExtUIProperty( + HWND hParent, // Parent Window Handle + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ) +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pIHVParams); + + if (!m_fInitialized) + { + hr = E_INVALIDARG; + goto error; + } + + // Store the passed-in string in a member variable so dialog box can display it + pIhvSecProfile = new(std::nothrow) CIhvSecurityProfile(); + if (pIhvSecProfile == NULL) + { + hr = E_OUTOFMEMORY; + goto error; + } + pIhvSecProfile->LoadXml(bstrIHVProfile); + m_fModified = FALSE; + + // Dialog will store the string in a member variable + DialogBoxParam( + g_hInst, + MAKEINTRESOURCE(IDD_PROPPAGE_SMALL), + hParent, + SimpleDialogProcSec, + (LPARAM)pIhvSecProfile + ); + + m_fModified = pIhvSecProfile->GetModified(); + + if (NULL != bstrModifiedIHVProfile) + { + if (m_fModified) + { + pIhvSecProfile->EmitXml(bstrModifiedIHVProfile); + } + } + + if (NULL != pbIsModified) + { + *pbIsModified = m_fModified; + } + +error: + if(pIhvSecProfile) + { + delete pIhvSecProfile; + pIhvSecProfile = NULL; + } + + return hr; +} + + +//Used to get the currently chosen entry to display as selected in the dropdown list +STDMETHODIMP +CDot11SampleExtUISecProperty::Dot11ExtUIPropertyGetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BOOL* pfIsSelected // flag denoting if this is the selected profile + ) +{ + HRESULT hr = S_OK; + IHV_SECURITY_TYPE currentSecurityType = IHVSecurityInvalid; + + UNREFERENCED_PARAMETER(pIHVParams); + + if(pfIsSelected == NULL) + { + hr = E_INVALIDARG; + goto error; + } + + *pfIsSelected = FALSE; + + pIhvSecProfile = new(std::nothrow) CIhvSecurityProfile(); + if (pIhvSecProfile == NULL) + { + hr = E_OUTOFMEMORY; + goto error; + } + + pIhvSecProfile->LoadXml(bstrIHVProfile); + + hr = pIhvSecProfile->GetSecurityType(¤tSecurityType); + if(FAILED(hr)) + { + hr = S_OK; + if(IHVSecurityV1 == m_IhvSecurityType) + { + *pfIsSelected = TRUE; + } + } + else if(currentSecurityType == m_IhvSecurityType) + { + *pfIsSelected = TRUE; + } + +error: + if(pIhvSecProfile != NULL) + { + delete pIhvSecProfile; + pIhvSecProfile = NULL; + } + return hr; +} + +//Used to set the current entry as chosen from the dropdown list +STDMETHODIMP +CDot11SampleExtUISecProperty::Dot11ExtUIPropertySetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ) +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pIHVParams); + + if(bstrModifiedIHVProfile == NULL || pbIsModified == NULL) + { + hr = E_INVALIDARG; + goto error; + } + + pIhvSecProfile = new(std::nothrow) CIhvSecurityProfile(); + if (pIhvSecProfile == NULL) + { + hr = E_OUTOFMEMORY; + goto error; + } + + pIhvSecProfile->LoadXml(bstrIHVProfile); + + pIhvSecProfile->SetSecurityType(m_IhvSecurityType); + pIhvSecProfile->SetFullSecurityFlag(TRUE); + + pIhvSecProfile->EmitXml(bstrModifiedIHVProfile ); + *pbIsModified = pIhvSecProfile->GetModified(); + +error: + if(pIhvSecProfile != NULL) + { + delete pIhvSecProfile; + pIhvSecProfile = NULL; + } + return hr; +} + + +STDMETHODIMP +CDot11SampleExtUISecProperty::Dot11ExtUIPropertyHasConfigurationUI( + BOOL *fHasConfigurationUI) +{ + // this page always wants to show a config UI + *fHasConfigurationUI = TRUE; + return S_OK; +} + +//Used to get additional display data (ciphers for auth types) +STDMETHODIMP +CDot11SampleExtUISecProperty::Dot11ExtUIPropertyGetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be described + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + ULONG *pcEntries, // number of dependent strings + ULONG *puDefaultSelection, // the entry in the array to be selected by default + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO **ppDot11ExtUIProperty // array of returned info structure + ) +{ + HRESULT hr = S_OK; + DWORD i = 0; + CIhvSecurityProfile IhvSecurityProfile; + IHV_CIPHER_TYPE cipherType = IHVCipherInvalid; + + UNREFERENCED_PARAMETER(pIHVParams); + + if(dot11ExtUIDisplayInfoType != DOT11_EXT_UI_DISPLAY_INFO_CIPHER) + { + hr = E_NOTIMPL; + goto error; + } + + IhvSecurityProfile.LoadXml(bstrIHVProfile); + hr = IhvSecurityProfile.GetCipherType(&cipherType); + if(FAILED(hr)) + { + goto error; + } + + for(i = 0; i < PROP_COUNT_SEC_CIPHERS; ++i) + { + ciphersInfoArray[m_IhvSecurityType][i].dwDataKey = i; + ciphersInfoArray[m_IhvSecurityType][i].dot11ExtUIDisplayInfoType = DOT11_EXT_UI_DISPLAY_INFO_CIPHER; + ciphersInfoArray[m_IhvSecurityType][i].bstrDisplayText = SysAllocString(g_IHVCipherFriendlyName[i]); + } + + // for the given auth type we want to return the list of compatible ciphers + *ppDot11ExtUIProperty = ciphersInfoArray[m_IhvSecurityType]; + *pcEntries = PROP_COUNT_SEC_CIPHERS; + *puDefaultSelection = cipherType >= IHVCipherInvalid ? 0 : cipherType; + +error: + return hr; +} + +STDMETHODIMP +CDot11SampleExtUISecProperty::Dot11ExtUIPropertySetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be modified + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO *pDot11ExtUIProperty, // selected info structure + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ) +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pIHVParams); + + CIhvSecurityProfile IhvSecurityProfile; + + if(dot11ExtUIDisplayInfoType != DOT11_EXT_UI_DISPLAY_INFO_CIPHER) + { + hr = E_NOTIMPL; + goto error; + } + + if(pDot11ExtUIProperty == NULL) + { + hr = E_INVALIDARG; + goto error; + } + + hr = IhvSecurityProfile.LoadXml(bstrIHVProfile); + if(FAILED(hr)) + { + goto error; + } + hr = IhvSecurityProfile.SetCipherType((IHV_CIPHER_TYPE)pDot11ExtUIProperty->dwDataKey); + if(FAILED(hr)) + { + goto error; + } + + hr = IhvSecurityProfile.EmitXml(bstrModifiedIHVProfile); + *pbIsModified = IhvSecurityProfile.GetModified(); + +error: + return hr; +} + +STDMETHODIMP +CDot11SampleExtUISecProperty::Dot11ExtUIPropertyIsStandardSecurity( + BOOL *fIsStandardSecurity, // if this interface is a standard auth method + DOT11_EXT_UI_SECURITY_TYPE *dot11ExtUISecurityType // which of the standard auth methods it is + ) +{ + UNREFERENCED_PARAMETER(dot11ExtUISecurityType); + *fIsStandardSecurity = FALSE; + return S_OK; +} + +STDMETHODIMP +CDot11SampleExtUISecProperty::Initialize(BSTR bstrPropertyName, DWORD dwIhvSecurity) +{ + HRESULT hr = E_INVALIDARG; + if (false == m_fInitialized) + { + // Set the FriendlyName + m_bstrFN = SysAllocString(bstrPropertyName); + m_IhvSecurityType = (IHV_SECURITY_TYPE)dwIhvSecurity; + m_fInitialized = true; + hr = S_OK; + } + return hr; +} + +INT_PTR CALLBACK +SimpleDialogProcSec( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +) +{ + BOOL fRetVal = FALSE; + WCHAR szBuf[256] = {0}; + DWORD dwValue = 0; + BSTR bstrText = NULL; + + UNREFERENCED_PARAMETER(lParam); + + if(!pIhvSecProfile) + { + goto error; + } + + switch(uMsg) + { + case WM_INITDIALOG: + { + // Dialog title + WCHAR strDialogTitle[MAX_PATH] = {0}; + (VOID)::LoadString( + g_hInst, + IDS_IHV_DEFAULT_SEC_TITLE, + strDialogTitle, + MAX_PATH + ); + + SetWindowText(hwndDlg, strDialogTitle); + } + + // check the checkbox if needed + if(FAILED(pIhvSecProfile->GetParamDWORD(&dwValue))) + { + dwValue = 0; + } + ::SendMessage( + GetDlgItem(hwndDlg, IDC_USE_FASTHANDOFF), + BM_SETCHECK, + (WPARAM)(int)dwValue, + 0L + ); + + // Set text in the textbox + if(FAILED(pIhvSecProfile->GetParamBSTR(&bstrText))) + { + bstrText = NULL; + } + SetWindowText(GetDlgItem(hwndDlg, IDC_PARAM_BOX), bstrText); + + fRetVal = TRUE; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case ID_OK: + GetWindowText(GetDlgItem(hwndDlg, IDC_PARAM_BOX), szBuf, 255); + if(szBuf) + { + DWORD dwNewValue = 0; + + // get the button state and record it + dwNewValue = (int)::SendMessage( + GetDlgItem(hwndDlg, IDC_USE_FASTHANDOFF), + BM_GETCHECK, + 0L, + 0L + ); + + pIhvSecProfile->SetParamDWORD(dwNewValue); + pIhvSecProfile->SetParamBSTR(szBuf); + pIhvSecProfile->SetFullSecurityFlag(TRUE); + + // Notify the owner window to carry out the task. + EndDialog(hwndDlg, 1); + fRetVal = TRUE; + } + break; + + case ID_CANCEL: + EndDialog(hwndDlg, 0); + fRetVal = TRUE; + break; + } + break; + } + +error: + return fRetVal; +} + + diff --git a/network/wlan/ihvsampleui/IHVSampleExtUISec.h b/network/wlan/ihvsampleui/IHVSampleExtUISec.h new file mode 100644 index 00000000..fc8caa2e --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleExtUISec.h @@ -0,0 +1,105 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#ifndef _IHVSAMPLEEXTUISEC_H_ +#define _IHVSAMPLEEXTUISEC_H_ + +class CDot11SampleExtUISecProperty: public IDot11SampleExtUISecProperty +{ +public: + CDot11SampleExtUISecProperty(); + + ~CDot11SampleExtUISecProperty(); + + // IUnknown Implementation + BEGIN_INTERFACE_TABLE() + IMPLEMENTS_INTERFACE(IDot11SampleExtUISecProperty) + END_INTERFACE_TABLE(); + + STDMETHODIMP + GetDot11ExtUIPropertyFriendlyName( + BSTR* bstrPropertyName // IHV friendly name + ); + + //Used to extend property + STDMETHODIMP + DisplayDot11ExtUIProperty( + HWND hParent, // Parent Window Handle + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ); + + //Used to get the currently chosen entry to display as selected in the dropdown list + STDMETHODIMP + Dot11ExtUIPropertyGetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BOOL* pfIsSelected // flag denoting if this is the selected profile + ); + + //Used to set the current entry as chosen from the dropdown list + STDMETHODIMP + Dot11ExtUIPropertySetSelected( + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ); + + STDMETHODIMP + Dot11ExtUIPropertyHasConfigurationUI(BOOL *fHasConfigurationUI); + + //Used to get additional display data (ciphers for auth types) + STDMETHODIMP + Dot11ExtUIPropertyGetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be described + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + ULONG *pcEntries, // number of dependent strings + ULONG *puDefaultSelection, // the entry in the array to be selected by default + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO **ppDot11ExtUIProperty // array of returned info structure + ); + + STDMETHODIMP + Dot11ExtUIPropertySetDisplayInfo( + DOT11_EXT_UI_DISPLAY_INFO_TYPE dot11ExtUIDisplayInfoType, // the diapaly type to be modified + BSTR bstrIHVProfile, // IHV data from the profile + PDOT11EXT_IHV_PARAMS pIHVParams, // Select profile MS security settings + DOT11_EXT_UI_PROPERTY_DISPLAY_INFO *pDot11ExtUIProperty, // selected info structure + BSTR* bstrModifiedIHVProfile, // modified IHV data to be stored in the profile + BOOL* pbIsModified // flag to denote if profile was modified + ); + + STDMETHODIMP + Dot11ExtUIPropertyIsStandardSecurity( + BOOL *fIsStandardSecurity, // if this interface is a standard auth method + DOT11_EXT_UI_SECURITY_TYPE *dot11ExtUISecurityType // which of the standard auth methods it is + ); + + // initialize the security page + STDMETHODIMP + Initialize(BSTR bstrPropertyName, DWORD dwIhvSecurity); + +private: + bool m_fInitialized; + BSTR m_bstrFN; + DOT11_EXT_UI_PROPERTY_TYPE m_ExtType; + BOOL m_fModified; + IHV_SECURITY_TYPE m_IhvSecurityType; + IHV_CIPHER_TYPE m_IHVCipherList[MAX_CIPHER_TYPES]; +}; + + +INT_PTR CALLBACK +SimpleDialogProcSec( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +); + +#endif _IHVSAMPLEEXTUISEC_H_ diff --git a/network/wlan/ihvsampleui/IHVSampleProfile.cpp b/network/wlan/ihvsampleui/IHVSampleProfile.cpp new file mode 100644 index 00000000..d4b0b060 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleProfile.cpp @@ -0,0 +1,918 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#include "precomp.h" + +HRESULT +CIhvProfileBase::GetTextFromNode +( + IN LPCWSTR pszQuery, + OUT BSTR* pbstrText +) +{ + HRESULT hr = S_OK; + BSTR bstrQuery = NULL; + IXMLDOMNode* pQueryNode = NULL; + + if ( !m_pRootNode ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + if ( (!pszQuery) || (!pbstrText) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Bstr + ( + pszQuery, + &bstrQuery + ); + BAIL_ON_FAILURE( hr ); + + hr = m_pRootNode->selectSingleNode( bstrQuery, &pQueryNode ); + BAIL_ON_FAILURE( hr ); + + if (!pQueryNode) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + hr = pQueryNode->get_text( pbstrText ); + BAIL_ON_FAILURE( hr ); + + if ( !(*pbstrText) ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + +error: + RELEASE_INTERFACE( pQueryNode ); + SYS_FREE_STRING( bstrQuery ); + return hr; +} + + +HRESULT +CIhvProfileBase::PutTextInNode +( + IN LPCWSTR pszQuery, + IN BSTR bstrText +) +{ + HRESULT hr = S_OK; + BSTR bstrQuery = NULL; + BSTR bstrOrig = NULL; + BOOL bPut = TRUE; + IXMLDOMNode* pQueryNode = NULL; + + if ( !m_pRootNode ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + if ( (!pszQuery) || (!bstrText) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Bstr + ( + pszQuery, + &bstrQuery + ); + BAIL_ON_FAILURE( hr ); + + hr = m_pRootNode->selectSingleNode( bstrQuery, &pQueryNode ); + BAIL_ON_FAILURE( hr ); + + if (!pQueryNode) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + hr = pQueryNode->get_text( &bstrOrig ); + BAIL_ON_FAILURE( hr ); + + if ( bstrOrig && ( 0 == wcscmp( bstrOrig, bstrText ) ) ) + { + bPut = FALSE; + } + + if ( bPut ) + { + hr = pQueryNode->put_text( bstrText ); + BAIL_ON_FAILURE( hr ); + + SetModified( ); + } + +error: + RELEASE_INTERFACE( pQueryNode ); + SYS_FREE_STRING( bstrQuery ); + SYS_FREE_STRING( bstrOrig ); + return hr; +} + + +HRESULT +CIhvProfileBase::LoadXml +( + IN BSTR bstrProfileData +) +{ + HRESULT hr = S_OK; + IXMLDOMDocument* pDOMDoc = NULL; + IXMLDOMElement* pDocElem = NULL; + BSTR bstrIhvProfile = NULL; + VARIANT_BOOL vfSuccess; + + if ( !bstrProfileData ) + { + hr = GetDefaultXml( &bstrIhvProfile ); + BAIL_ON_FAILURE( hr ); + } + else + { + bstrIhvProfile = bstrProfileData; + } + + if ( m_pRootNode ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + hr = + CoCreateInstance + ( + CLSID_DOMDocument, + NULL, + CLSCTX_ALL, + IID_IXMLDOMDocument, + (LPVOID *) &pDOMDoc + ); + BAIL_ON_FAILURE( hr ); + + hr = + pDOMDoc->loadXML + ( + bstrIhvProfile, + &vfSuccess + ); + BAIL_ON_FAILURE( hr ); + + if ( VARIANT_TRUE != vfSuccess ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + hr = + pDOMDoc->get_documentElement + ( + &pDocElem + ); + BAIL_ON_FAILURE( hr ); + + // Caching the pointer to the document element + // in a member variable. + m_pRootNode = pDocElem; + pDocElem = NULL; + + +error: + if ( !bstrProfileData ) + { + SYS_FREE_STRING( bstrIhvProfile ); + } + RELEASE_INTERFACE( pDOMDoc ); + RELEASE_INTERFACE( pDocElem ); + return hr; +} + + +HRESULT +CIhvProfileBase::EmitXml +( + OUT BSTR* pbstrIhvProfile +) +{ + HRESULT hr = S_OK; + + if ( !pbstrIhvProfile ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + if ( !m_pRootNode ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + hr = m_pRootNode->get_xml( pbstrIhvProfile ); + BAIL_ON_FAILURE( hr ); + +error: + return hr; +} + + +////////////////////////////////////////////////////////// + +WCHAR g_szDefaultConnectivityProfile[] = +L"" +L" 0" +L" parameter value" +L"" +; + + + +HRESULT +CIhvConnectivityProfile::GetDefaultXml +( + BSTR* pbstrDefault +) +{ + SetModified(); + return Wstr2Bstr( g_szDefaultConnectivityProfile, pbstrDefault ); +} + + + +HRESULT +CIhvConnectivityProfile::GetParamDWORD +( + DWORD* pdwParam1 +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + CON_PARAM2_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + hr = + Wstr2Dword + ( + bstrData, + pdwParam1 + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + + +HRESULT +CIhvConnectivityProfile::SetParamDWORD +( + DWORD dwNewValue +) +{ + HRESULT hr = S_OK; + BSTR bstrText = NULL; + + hr = + Dword2Bstr + ( + dwNewValue, + &bstrText + ); + BAIL_ON_FAILURE( hr ); + + hr = + PutTextInNode + ( + CON_PARAM2_XPATH, + bstrText + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrText ); + return hr; +} + + + +HRESULT +CIhvConnectivityProfile::GetParamBSTR +( + BSTR* pbstrValue +) +{ + HRESULT hr = S_OK; + + hr = + GetTextFromNode + ( + CON_PARAM1_XPATH, + pbstrValue + ); + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + + + +HRESULT +CIhvConnectivityProfile::SetParamBSTR +( + BSTR bstrNewValue +) +{ + HRESULT hr = S_OK; + + hr = + PutTextInNode + ( + CON_PARAM1_XPATH, + bstrNewValue + ); + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + +HRESULT +CIhvConnectivityProfile::GetNativeData +( + LPVOID* ppvData +) +{ + HRESULT hr = S_OK; + PIHV_CONNECTIVITY_PROFILE pIhvProfile = NULL; + BSTR bstrParam2 = NULL; + + if ( !ppvData ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + pIhvProfile = (PIHV_CONNECTIVITY_PROFILE) malloc( sizeof( IHV_CONNECTIVITY_PROFILE ) ); + if ( !pIhvProfile ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + ZeroMemory( pIhvProfile, sizeof( IHV_CONNECTIVITY_PROFILE ) ); + + hr = + GetParamBSTR + ( + &bstrParam2 + ); + BAIL_ON_FAILURE( hr ); + + if ( NULL == bstrParam2 ) { + hr = E_POINTER; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Wstr + ( + bstrParam2, + &(pIhvProfile->pszParam2) + ); + BAIL_ON_FAILURE( hr ); + + hr = + GetParamDWORD + ( + &(pIhvProfile->dwParam1) + ); + BAIL_ON_FAILURE( hr ); + + // Transfering local cache to OUT parameter. + (*ppvData) = pIhvProfile; + pIhvProfile = NULL; + +error: + if ( pIhvProfile ) + { + free( pIhvProfile->pszParam2 ); // NULL Safe. + free( pIhvProfile ); + } + SYS_FREE_STRING( bstrParam2 ); + return hr; +} + + + +/////////////////////////////////////////// + +LPCWSTR gppszIhvAuthTypes[] = +{ + L"IHVAuthV1", + L"IHVAuthV2", + L"IHVAuthV3" +}; + +LPCWSTR gppszIhvSecurityTypes[] = +{ + L"IHVSecurityV1", + L"IHVSecurityV2", +}; + +LPCWSTR gppszIhvCipherTypes[] = +{ + L"None", + L"IHVCipher1", + L"IHVCipher2", + L"IHVCipher3" +}; + + +WCHAR g_szDefaultSecurityProfile[] = +L"" +L" TRUE" +L" IHVAuthV1" +L" IHVCipher1" +L" 0" +L" parameter value" +L""; + + + +HRESULT +CIhvSecurityProfile::GetDefaultXml +( + BSTR* pbstrDefault +) +{ + SetModified(); + return Wstr2Bstr( g_szDefaultSecurityProfile, pbstrDefault ); +} + +HRESULT +CIhvSecurityProfile::GetNativeData +( + LPVOID* ppvData +) +{ + HRESULT hr = S_OK; + PIHV_SECURITY_PROFILE pIhvProfile = NULL; + BSTR bstrParam2 = NULL; + + if ( !ppvData ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + pIhvProfile = (PIHV_SECURITY_PROFILE) malloc( sizeof( IHV_SECURITY_PROFILE ) ); + if ( !pIhvProfile ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + ZeroMemory( pIhvProfile, sizeof( IHV_SECURITY_PROFILE ) ); + + hr = + GetFullSecurityFlag + ( + &(pIhvProfile->bUseFullSecurity) + ); + BAIL_ON_FAILURE( hr ); + + hr = + GetAuthType + ( + &(pIhvProfile->AuthType) + ); + BAIL_ON_FAILURE( hr ); + + hr = + GetCipherType + ( + &(pIhvProfile->CipherType) + ); + BAIL_ON_FAILURE( hr ); + + hr = + GetParamDWORD + ( + &(pIhvProfile->dwParam1) + ); + BAIL_ON_FAILURE( hr ); + + hr = + GetParamBSTR + ( + &bstrParam2 + ); + BAIL_ON_FAILURE( hr ); + + if ( NULL == bstrParam2 ) { + hr = E_POINTER; + BAIL_ON_FAILURE( hr ); + } + + hr = + Wstr2Wstr + ( + bstrParam2, + &(pIhvProfile->pszParam2) + ); + BAIL_ON_FAILURE( hr ); + + // Transfering local cache to OUT parameter. + (*ppvData) = pIhvProfile; + pIhvProfile = NULL; + +error: + if ( pIhvProfile ) + { + free( pIhvProfile->pszParam2 ); // NULL Safe. + free( pIhvProfile ); + } + SYS_FREE_STRING( bstrParam2 ); + return hr; + +} + + + +HRESULT +CIhvSecurityProfile::GetFullSecurityFlag +( + BOOL* pbUseFullSecurity +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + SEC_FSFLAG_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + hr = + Wstr2Bool + ( + bstrData, + pbUseFullSecurity + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + + +HRESULT +CIhvSecurityProfile::SetFullSecurityFlag +( + BOOL bUseFullSecurity +) +{ + HRESULT hr = S_OK; + BSTR bstrText = NULL; + + hr = + Bool2Bstr + ( + bUseFullSecurity, + &bstrText + ); + BAIL_ON_FAILURE( hr ); + + hr = + PutTextInNode + ( + SEC_FSFLAG_XPATH, + bstrText + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrText ); + return hr; +} + + + +HRESULT +CIhvSecurityProfile::GetAuthType +( + PIHV_AUTH_TYPE pAuthType +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + SEC_ATYPE_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + hr = + Wstr2AuthType + ( + bstrData, + pAuthType + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + + +HRESULT +CIhvSecurityProfile::SetAuthType +( + IHV_AUTH_TYPE AuthType +) +{ + HRESULT hr = S_OK; + BSTR bstrText = NULL; + + hr = + AuthType2Bstr + ( + AuthType, + &bstrText + ); + BAIL_ON_FAILURE( hr ); + + hr = + PutTextInNode + ( + SEC_ATYPE_XPATH, + bstrText + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrText ); + return hr; +} + + +HRESULT +CIhvSecurityProfile::GetSecurityType +( + PIHV_SECURITY_TYPE pSecurityType +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + SEC_ATYPE_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + hr = + Wstr2SecurityType + ( + bstrData, + pSecurityType + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + +HRESULT +CIhvSecurityProfile::SetSecurityType +( + IHV_SECURITY_TYPE SecurityType +) +{ + HRESULT hr = S_OK; + BSTR bstrText = NULL; + + hr = + SecurityType2Bstr + ( + SecurityType, + &bstrText + ); + BAIL_ON_FAILURE( hr ); + + hr = + PutTextInNode + ( + SEC_ATYPE_XPATH, + bstrText + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrText ); + return hr; +} + + +HRESULT +CIhvSecurityProfile::GetCipherType +( + PIHV_CIPHER_TYPE pCipherType +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + SEC_ETYPE_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + hr = + Wstr2CipherType + ( + bstrData, + pCipherType + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + + +HRESULT +CIhvSecurityProfile::SetCipherType +( + IHV_CIPHER_TYPE CipherType +) +{ + HRESULT hr = S_OK; + BSTR bstrText = NULL; + + hr = + CipherType2Bstr + ( + CipherType, + &bstrText + ); + BAIL_ON_FAILURE( hr ); + + hr = + PutTextInNode + ( + SEC_ETYPE_XPATH, + bstrText + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrText ); + return hr; +} + + +HRESULT +CIhvSecurityProfile::GetParamDWORD +( + DWORD* pdwParam1 +) +{ + HRESULT hr = S_OK; + BSTR bstrData = NULL; + + hr = + GetTextFromNode + ( + SEC_PARAM2_XPATH, + &bstrData + ); + BAIL_ON_FAILURE( hr ); + + hr = + Wstr2Dword + ( + bstrData, + pdwParam1 + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrData ); + return hr; +} + + +HRESULT +CIhvSecurityProfile::SetParamDWORD +( + DWORD dwNewValue +) +{ + HRESULT hr = S_OK; + BSTR bstrText = NULL; + + hr = + Dword2Bstr + ( + dwNewValue, + &bstrText + ); + BAIL_ON_FAILURE( hr ); + + hr = + PutTextInNode + ( + SEC_PARAM2_XPATH, + bstrText + ); + BAIL_ON_FAILURE( hr ); + +error: + SYS_FREE_STRING( bstrText ); + return hr; +} + + + +HRESULT +CIhvSecurityProfile::GetParamBSTR +( + BSTR* pbstrValue +) +{ + HRESULT hr = S_OK; + + hr = + GetTextFromNode + ( + SEC_PARAM1_XPATH, + pbstrValue + ); + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + + +HRESULT +CIhvSecurityProfile::SetParamBSTR +( + BSTR bstrNewValue +) +{ + HRESULT hr = S_OK; + + hr = + PutTextInNode + ( + SEC_PARAM1_XPATH, + bstrNewValue + ); + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} diff --git a/network/wlan/ihvsampleui/IHVSampleProfile.h b/network/wlan/ihvsampleui/IHVSampleProfile.h new file mode 100644 index 00000000..66e8ce3f --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleProfile.h @@ -0,0 +1,296 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#pragma once + +#ifndef _IHVSAMPLEPROFILE_H +#define _IHVSAMPLEPROFILE_H + +#define RELEASE_INTERFACE( _p ) if ( _p ) { (_p)->Release( ); (_p) = NULL;} + + +class CIhvProfileBase +{ +public: + + // Constructor + CIhvProfileBase( ) + { + m_bModified = FALSE; + m_pRootNode = NULL; + } + + // Destructor + ~CIhvProfileBase( ) + { + RELEASE_INTERFACE( m_pRootNode ); + } + + HRESULT + LoadXml + ( + IN BSTR bstrIhvProfile + ); + + HRESULT + EmitXml + ( + OUT BSTR* pbstrIhvProfile + ); + + BOOL GetModified( ) { return m_bModified; } + + // Caller needs to know what type to + // cast the pointer to depending upon + // the type of the derived class. + // Caller needs to free memory recursively + // by using the free( ) function. + virtual + HRESULT + GetNativeData + ( + LPVOID* ppvData + ) + = 0; + +protected: + + virtual + HRESULT + GetDefaultXml + ( + BSTR* pbstrDefault + ) + = 0; + + HRESULT + GetTextFromNode + ( + IN LPCWSTR pszQuery, + OUT BSTR* pbstrText + ); + + HRESULT + PutTextInNode + ( + IN LPCWSTR pszQuery, + IN BSTR bstrText + ); + + + VOID SetModified( ) { m_bModified = TRUE; } + + IXMLDOMElement* m_pRootNode; + +private: + BOOL m_bModified; +}; + +/////////////////////////////////////////// + + +typedef struct _IHV_CONNECTIVITY_PROFILE +{ +#define CON_PARAM1_XPATH L"/IhvConnectivity/IHVConnectivityParam1" +#define CON_PARAM2_XPATH L"/IhvConnectivity/IHVConnectivityParam2" + + DWORD dwParam1; + LPWSTR pszParam2; +} +IHV_CONNECTIVITY_PROFILE, *PIHV_CONNECTIVITY_PROFILE; + + +extern WCHAR g_szDefaultConnectivityProfile[]; + +class CIhvConnectivityProfile + : public CIhvProfileBase +{ +protected: + HRESULT + GetDefaultXml + ( + BSTR* pbstrDefault + ); + +public: + + // Constructor Destructor + CIhvConnectivityProfile( ) { } + ~CIhvConnectivityProfile( ) { } + + // Caller needs to know what type to + // cast the pointer to depending upon + // the type of the derived class. + // Caller needs to free memory recursively + // by using the free( ) function. + HRESULT + GetNativeData + ( + LPVOID* ppvData + ); + + + // Accessor and Modifier for dwParam1 + HRESULT + GetParamDWORD + ( + DWORD* pdwParam1 + ); + HRESULT + SetParamDWORD + ( + DWORD dwNewValue + ); + + // Accessor and Modifier for pszParam2 + HRESULT + GetParamBSTR + ( + BSTR* pbstrValue + ); + HRESULT + SetParamBSTR + ( + BSTR bstrNewValue + ); + +}; + + + +/////////////////////////////////////////// + +extern LPCWSTR gppszIhvSecurityTypes[]; + +#define MAX_AUTH_TYPES 3 +extern LPCWSTR gppszIhvAuthTypes[]; + +#define MAX_CIPHER_TYPES 4 +extern LPCWSTR gppszIhvCipherTypes[]; + +typedef struct _IHV_SECURITY_PROFILE +{ +#define SEC_FSFLAG_XPATH L"/IhvSecurity/IHVUsesFullSecurity" +#define SEC_ATYPE_XPATH L"/IhvSecurity/IHVAuthentication" +#define SEC_ETYPE_XPATH L"/IhvSecurity/IHVEncryption" +#define SEC_PARAM1_XPATH L"/IhvSecurity/IHVSecurityParam1" +#define SEC_PARAM2_XPATH L"/IhvSecurity/IHVSecurityParam2" + + BOOL bUseFullSecurity; + IHV_AUTH_TYPE AuthType; + IHV_CIPHER_TYPE CipherType; + DWORD dwParam1; + LPWSTR pszParam2; +} +IHV_SECURITY_PROFILE, *PIHV_SECURITY_PROFILE; + + +extern WCHAR g_szDefaultSecurityProfile[]; + +class CIhvSecurityProfile + : public CIhvProfileBase +{ +protected: + HRESULT + GetDefaultXml + ( + BSTR* pbstrDefault + ); + +public: + + // Constructor Destructor + CIhvSecurityProfile( ) { } + ~CIhvSecurityProfile( ) { } + + // Caller needs to know what type to + // cast the pointer to depending upon + // the type of the derived class. + // Caller needs to free memory recursively + // by using the free( ) function. + HRESULT + GetNativeData + ( + LPVOID* ppvData + ); + + + // Accessor and Modifier for bUseFullSecurity + HRESULT + GetFullSecurityFlag + ( + BOOL* pbUseFullSecurity + ); + HRESULT + SetFullSecurityFlag + ( + BOOL bUseFullSecurity + ); + + + // Accessor and Modifier for AuthType + HRESULT + GetAuthType + ( + PIHV_AUTH_TYPE pAuthType + ); + HRESULT + SetAuthType + ( + IHV_AUTH_TYPE AuthType + ); + + // Accessor and Modifier for SecurityType + HRESULT + GetSecurityType + ( + PIHV_SECURITY_TYPE pSecurityType + ); + HRESULT + SetSecurityType + ( + IHV_SECURITY_TYPE SecurityType + ); + + // Accessor and Modifier for CipherType + HRESULT + GetCipherType + ( + PIHV_CIPHER_TYPE pCipherType + ); + HRESULT + SetCipherType + ( + IHV_CIPHER_TYPE CipherType + ); + + // Accessor and Modifier for dwParam1 + HRESULT + GetParamDWORD + ( + DWORD* pdwParam1 + ); + HRESULT + SetParamDWORD + ( + DWORD dwNewValue + ); + + // Accessor and Modifier for pszParam2 + HRESULT + GetParamBSTR + ( + BSTR* pbstrValue + ); + HRESULT + SetParamBSTR + ( + BSTR bstrNewValue + ); + +}; + +#endif _IHVSAMPLEPROFILE_H + diff --git a/network/wlan/ihvsampleui/IHVSampleUI.cpp b/network/wlan/ihvsampleui/IHVSampleUI.cpp new file mode 100644 index 00000000..f93218bc --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleUI.cpp @@ -0,0 +1,122 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + + +#include "precomp.h" + +CIHVClassFactory *g_pIHVClassFactory = NULL; + +// instance handle to dll +HINSTANCE g_hInst; + +// object ref count +long g_objRefCount = 0; +long g_serverLock = 0; //lock count on server + +// +// DllRegisterServer - Adds entries to the system registry +// +STDAPI DllRegisterServer() +{ + HRESULT hr = S_OK; + + hr = CRegHelper::RegisterServer(); + + return hr; +} + +// +// DllUnregisterServer - Removes entries from the system registry +// +STDAPI DllUnregisterServer() +{ + HRESULT hr = S_OK; + + hr = CRegHelper::UnregisterServer(); + + return hr; +} + + + +// +// Used to determine whether the DLL can be unloaded by COM +// +STDAPI DllCanUnloadNow(void) +{ + if ((g_objRefCount == 0) && (g_serverLock == 0) /*&& (_Module.GetLockCount() == 0)*/) + { + return S_OK; + } + else + { + return S_FALSE; + } +} + + + +STDAPI +DllGetClassObject( + _In_ REFCLSID rclsid, + _In_ REFIID riid, + _Outptr_ LPVOID *ppv) +{ + HRESULT hr = E_NOINTERFACE; + + if (NULL == g_pIHVClassFactory) + { + g_pIHVClassFactory = new(std::nothrow) CIHVClassFactory(); + if (NULL == g_pIHVClassFactory) + { + hr = E_OUTOFMEMORY; + } + } + + if(NULL != g_pIHVClassFactory && + (rclsid == GUID_SAMPLE_IHVUI_CLSID || rclsid == IID_IWizardExtension)) + { + hr = g_pIHVClassFactory->QueryInterface(riid, ppv); + } + return hr; +} + + +//+---------------------------------------------------------------------------- +// +// Function: DllMain +// +// Synopsis: Main entry point into the DLL. +// +// Arguments: HINSTANCE hinstDLL - Our HINSTANCE +// DWORD fdwReason - The reason we are being called. +// LPVOID lpvReserved - Reserved +// +// Returns: BOOL WINAPI - TRUE - always +// +//+---------------------------------------------------------------------------- +extern "C" +BOOL WINAPI +DllMain( + HINSTANCE hInstDLL, + DWORD fdwReason, + LPVOID lpvReserved + ) +{ + UNREFERENCED_PARAMETER(lpvReserved); + + if (DLL_PROCESS_ATTACH == fdwReason) + { + // Set our global instance handle + g_hInst = hInstDLL; + (VOID)DisableThreadLibraryCalls(hInstDLL); + } + else if (DLL_PROCESS_DETACH == fdwReason) + { + } + + return TRUE; +} + diff --git a/network/wlan/ihvsampleui/IHVSampleUI.def b/network/wlan/ihvsampleui/IHVSampleUI.def new file mode 100644 index 00000000..2882c292 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleUI.def @@ -0,0 +1,7 @@ +LIBRARY "IhvSampleUI.dll" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/network/wlan/ihvsampleui/IHVSampleUI.rc b/network/wlan/ihvsampleui/IHVSampleUI.rc new file mode 100644 index 00000000..2c6563d8 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleUI.rc @@ -0,0 +1,129 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "TODO: " + VALUE "FileDescription", "TODO: " + VALUE "FileVersion", "1.0.0.1" + VALUE "LegalCopyright", "TODO: (c) . All rights reserved." + VALUE "InternalName", "IHVSampleUI.dll" + VALUE "OriginalFilename", "IHVSampleUI.dll" + VALUE "ProductName", "TODO: " + VALUE "ProductVersion", "1.0.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PROPPAGE_SMALL DIALOGEX 0, 0, 227, 244 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "IHV Profile String" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "This is a sample IHV extended dialog.",IDC_TITLE_TXT, + 20,19,135,8 + LTEXT "Parameter 1",IDC_PARAM_TXT,20,49,80,8 + EDITTEXT IDC_PARAM_BOX,101,48,96,14 + CONTROL "Parameter 2",IDC_USE_FASTHANDOFF,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,20,72,178,10 + PUSHBUTTON "OK",ID_OK,96,210,49,14 + PUSHBUTTON "Cancel",ID_CANCEL,167,209,50,14 +END + +IDD_DIALOG_SHOWHELP DIALOGEX 0, 0, 317, 143 +STYLE DS_SETFONT | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Dialog" +FONT 9, "Segoe UI", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_EDIT_HELPER,8,7,171,81,ES_AUTOHSCROLL +END + +IDD_DIALOG_GETKEY DIALOGEX 0, 0, 317, 143 +STYLE DS_SETFONT | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Dialog" +FONT 9, "Segoe UI", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_EDIT_KEY,23,46,142,14,ES_AUTOHSCROLL + LTEXT "Please enter IHV keys:",IDC_STATIC,25,17,74,8 +END + +IDD_DIALOG_LASTPAGE DIALOGEX 0, 0, 317, 143 +STYLE DS_SETFONT | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Dialog" +FONT 9, "Segoe UI", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_DATETIMEPICKER1,"SysDateTimePick32", + DTS_RIGHTALIGN | WS_TABSTOP,7,7,172,12 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_PROJNAME "SampleIHVExt" + IDS_TITLE_GETKEY "IHV Get Key Page" + IDS_TITLE_SHOWHELP "IHV Helper Page" + IDS_TITLE_LASTPAGE "IHV Last Page" + IDS_IHV_DEFAULT_TITLE "IHV Properties" + IDS_IHV_DEFAULT_CON_TITLE "IHV Connection Properties" + IDS_IHV_DEFAULT_KEY_TITLE "IHV Security Properties" + IDS_IHV_DEFAULT_SEC_TITLE "IHV Security Properties" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + diff --git a/network/wlan/ihvsampleui/IHVSampleUI.vcxproj b/network/wlan/ihvsampleui/IHVSampleUI.vcxproj new file mode 100644 index 00000000..c4fe2319 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleUI.vcxproj @@ -0,0 +1,258 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A6B24475-C40B-4105-9896-6076AB284D92} + $(MSBuildProjectName) + Debug + Win32 + {BFF15FF4-CA6C-4126-AE5C-43FEAC2373DA} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + IHVSampleUI + + + IHVSampleUI + + + IHVSampleUI + + + IHVSampleUI + + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + %(PreprocessorDefinitions);UNICODE;WIN32 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(AdditionalOptions) /EHa + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib;user32.lib;uuid.lib;Kernel32.lib;Advapi32.lib;comctl32.lib + IHVSampleUI.def + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalOptions) /EHa + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib;user32.lib;uuid.lib;Kernel32.lib;Advapi32.lib;comctl32.lib + IHVSampleUI.def + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalOptions) /EHa + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib;user32.lib;uuid.lib;Kernel32.lib;Advapi32.lib;comctl32.lib + IHVSampleUI.def + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalOptions) /EHa + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib;user32.lib;uuid.lib;Kernel32.lib;Advapi32.lib;comctl32.lib + IHVSampleUI.def + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/wlan/ihvsampleui/IHVSampleUI.vcxproj.Filters b/network/wlan/ihvsampleui/IHVSampleUI.vcxproj.Filters new file mode 100644 index 00000000..35f3326f --- /dev/null +++ b/network/wlan/ihvsampleui/IHVSampleUI.vcxproj.Filters @@ -0,0 +1,60 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {94F31788-CC51-4779-82A7-949AF23515AB} + + + h;hpp;hxx;hm;inl;inc;xsd + {2AFE8613-F5FD-4251-B520-F5354B519860} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {4C783C36-ECB3-435F-B739-9B522EEF773A} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/wlan/ihvsampleui/IHVUIInc.idl b/network/wlan/ihvsampleui/IHVUIInc.idl new file mode 100644 index 00000000..1ef82407 --- /dev/null +++ b/network/wlan/ihvsampleui/IHVUIInc.idl @@ -0,0 +1 @@ +#include "wlanihvui.idl" \ No newline at end of file diff --git a/network/wlan/ihvsampleui/iunk.h b/network/wlan/ihvsampleui/iunk.h new file mode 100644 index 00000000..9b8242c5 --- /dev/null +++ b/network/wlan/ihvsampleui/iunk.h @@ -0,0 +1,54 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +// Common macro based implementation of IUnknown +// using a interface table approach + +#define IMPLEMENT_REFCOUNT()\ + ULONG m_crefCount;\ + \ + STDMETHODIMP_(ULONG) AddRef(void)\ + {\ + return InterlockedIncrement((PLONG)&m_crefCount);\ + }\ + \ + _At_(this, __drv_aliasesMem)\ + STDMETHODIMP_(ULONG) Release(void)\ + {\ + ULONG res = InterlockedDecrement((PLONG)&m_crefCount);\ + if (res == 0)\ + {\ + delete this;\ + }\ + return res;\ + } + +#define BEGIN_INTERFACE_TABLE()\ + IMPLEMENT_REFCOUNT()\ + STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject)\ + {\ + if (riid == IID_IUnknown)\ + {\ + *ppvObject = reinterpret_cast(this);\ + } + +#define IMPLEMENTS_INTERFACE(Itf)\ + else if (riid == IID_ ## Itf)\ + {\ + *ppvObject = static_cast(this);\ + } + +#define END_INTERFACE_TABLE()\ + else \ + {\ + *ppvObject = NULL;\ + return E_NOINTERFACE;\ + }\ + \ + reinterpret_cast(*ppvObject)->AddRef();\ + \ + return S_OK;\ + } + diff --git a/network/wlan/ihvsampleui/precomp.h b/network/wlan/ihvsampleui/precomp.h new file mode 100644 index 00000000..1037884d --- /dev/null +++ b/network/wlan/ihvsampleui/precomp.h @@ -0,0 +1,43 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#pragma once + +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include +#include +#include +#include +#include +#include +#include + +// MIDL generated +#include "ihvsample.h" + +#include "iunk.h" +#include "resource.h" +#include "utils.h" +#include "IHVRegistryHelper.h" +#include "IHVSampleExtUI.h" +#include "IHVSampleProfile.h" +#include "IHVSampleExtUICon.h" +#include "IHVSampleExtUISec.h" +#include "IHVSampleExtUIKey.h" +#include "IHVClassFactory.h" +#include + +const GUID GUID_SAMPLE_IHVUI_CLSID = +{ 0x4a01f9f9, 0x6012, 0x4343, { 0xa8, 0xc4, 0x10, 0xb5, 0xdf, 0x32, 0x67, 0x2a } }; + + +#define IHV_SAMPLE_IHV_NAME L"IHV" + +#define BAIL_ON_FAILURE( _hr ) if (FAILED(_hr)) goto error; +#define BAIL( ) goto error; +#define SYS_FREE_STRING( _s ) if ( _s ) { SysFreeString( _s ); (_s) = NULL;} + diff --git a/network/wlan/ihvsampleui/resource.h b/network/wlan/ihvsampleui/resource.h new file mode 100644 index 00000000..e60f854e --- /dev/null +++ b/network/wlan/ihvsampleui/resource.h @@ -0,0 +1,31 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by TestIHV.rc +// +#define IDD_PROPPAGE_SMALL 502 + +#define IDC_PARAM_BOX 1000 +#define IDC_PARAM_TXT 1001 +#define IDC_USE_FASTHANDOFF 1002 +#define ID_OK 1003 +#define ID_CANCEL 1004 +#define IDC_TITLE_TXT 1005 + +#define IDS_PROJNAME 100 +#define IDR_SAMPLEIHVEXT 101 +#define IDS_TITLE_GETKEY 101 +#define IDR_IHVBALLOONHANDLER 102 +#define IDS_TITLE_SHOWHELP 102 +#define IDS_TITLE_LASTPAGE 103 +#define IDC_EDIT_KEY 201 +#define IDC_EDIT_HELPER 203 +#define IDD_DIALOG_GETKEY 204 +#define IDC_DATETIMEPICKER1 204 +#define IDD_DIALOG_SHOWHELP 205 +#define IDD_DIALOG_LASTPAGE 206 + + +#define IDS_IHV_DEFAULT_TITLE 300 +#define IDS_IHV_DEFAULT_CON_TITLE 301 +#define IDS_IHV_DEFAULT_KEY_TITLE 302 +#define IDS_IHV_DEFAULT_SEC_TITLE 303 diff --git a/network/wlan/ihvsampleui/utils.cpp b/network/wlan/ihvsampleui/utils.cpp new file mode 100644 index 00000000..1f9bbe10 --- /dev/null +++ b/network/wlan/ihvsampleui/utils.cpp @@ -0,0 +1,408 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + +#include "precomp.h" + +HRESULT +Wstr2Bstr +( + _In_ LPCWSTR pszSrc, + _Outptr_ BSTR* pbstrDest +) +{ + HRESULT hr = S_OK; + + if ( !pbstrDest ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + (*pbstrDest) = NULL; + if ( !pszSrc ) + { + BAIL( ); + } + + (*pbstrDest) = SysAllocString( pszSrc ); + if ( !(*pbstrDest) ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + +error: + return hr; +} + + + + +HRESULT +Wstr2Wstr +( + _In_ LPCWSTR pszSrc, + _Outptr_ LPWSTR* ppszDest +) +{ + HRESULT hr = S_OK; + + if ( !ppszDest ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + (*ppszDest) = NULL; + if ( !pszSrc ) + { + BAIL( ); + } + + *ppszDest = _wcsdup( pszSrc ); + + if ( !(*ppszDest) ) + { + hr = E_OUTOFMEMORY; + } + +error: + return hr; +} + + + + +HRESULT +Wstr2Dword +( + IN LPCWSTR pszSrc, + OUT DWORD* pdwDest +) +{ + HRESULT hr = S_OK; + + if ( (!pdwDest) || (!pszSrc) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + (*pdwDest) = (DWORD) _wtol( pszSrc ); + +error: + return hr; +} + +/* +Note: +2^32 = 2^(4*8) = 16^8 < 100^8 = 10^16 +This implies that 20 decimal digits +are more than enough for a DWORD. +*/ + +HRESULT +Dword2Bstr +( + IN DWORD dwSrc, + OUT BSTR* pbstrDest +) +{ + HRESULT hr = S_OK; + WCHAR szBuffer[25] = {0}; + + hr = + StringCchPrintf + ( + szBuffer, + sizeof(szBuffer)/sizeof(szBuffer[0]), + L"%u", + dwSrc + ); + BAIL_ON_FAILURE( hr ); + + hr = + Wstr2Bstr + ( + szBuffer, + pbstrDest + ); + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + + +HRESULT +Wstr2Bool +( + IN LPCWSTR pszSrc, + OUT BOOL* pbDest +) +{ + HRESULT hr = S_OK; + + if ( (!pbDest) || (!pszSrc) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + if ( 0 == wcscmp( L"TRUE", pszSrc ) ) + { + (*pbDest) = TRUE; + } + else if ( 0 == wcscmp( L"FALSE", pszSrc ) ) + { + (*pbDest) = FALSE; + } + else + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + +error: + return hr; +} + + +HRESULT +Bool2Bstr +( + IN BOOL bSrc, + OUT BSTR* pbstrDest +) +{ + HRESULT hr = S_OK; + + if ( !pbstrDest ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + if ( bSrc ) + { + (*pbstrDest) = SysAllocString( L"TRUE" ); + } + else + { + (*pbstrDest) = SysAllocString( L"FALSE" ); + } + if ( !(*pbstrDest) ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + +error: + return hr; +} + + + + +HRESULT +Wstr2AuthType +( + IN LPCWSTR pszSrc, + OUT PIHV_AUTH_TYPE pAuthType +) +{ + HRESULT hr = S_OK; + DWORD dwIndex = 0; + + if ( (!pAuthType) || (!pszSrc) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + for ( dwIndex = 0; dwIndex < MAX_AUTH_TYPES; dwIndex++ ) + { + if ( 0 == wcscmp( gppszIhvAuthTypes[dwIndex], pszSrc ) ) + { + (*pAuthType) = (IHV_AUTH_TYPE) dwIndex; + BAIL( ); + } + } + + // String not found. + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + +HRESULT +AuthType2Bstr +( + IN IHV_AUTH_TYPE AuthType, + OUT BSTR* pbstrDest +) +{ + HRESULT hr = S_OK; + + if ( !pbstrDest ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + if ( AuthType < 0 || AuthType >= IHVAuthInvalid ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + (*pbstrDest) = SysAllocString( gppszIhvAuthTypes[(DWORD) AuthType] ); + if ( !(*pbstrDest) ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + +error: + return hr; +} + + +HRESULT +Wstr2SecurityType +( + IN LPCWSTR pszSrc, + OUT PIHV_SECURITY_TYPE pSecurityType +) +{ + HRESULT hr = S_OK; + DWORD dwIndex = 0; + + if ( (!pSecurityType) || (!pszSrc) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + for ( dwIndex = 0; dwIndex < MAX_AUTH_TYPES; dwIndex++ ) + { + if ( 0 == wcscmp( gppszIhvSecurityTypes[dwIndex], pszSrc ) ) + { + (*pSecurityType) = (IHV_SECURITY_TYPE) dwIndex; + BAIL( ); + } + } + + // String not found. + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + +HRESULT +SecurityType2Bstr +( + IN IHV_SECURITY_TYPE SecurityType, + OUT BSTR* pbstrDest +) +{ + HRESULT hr = S_OK; + + if ( !pbstrDest ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + if ( SecurityType < 0 || SecurityType >= IHVSecurityInvalid ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + (*pbstrDest) = SysAllocString( gppszIhvSecurityTypes[(DWORD) SecurityType] ); + if ( !(*pbstrDest) ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + +error: + return hr; +} + +HRESULT +Wstr2CipherType +( + IN LPCWSTR pszSrc, + OUT PIHV_CIPHER_TYPE pCipherType +) +{ + HRESULT hr = S_OK; + DWORD dwIndex = 0; + + if ( (!pCipherType) || (!pszSrc) ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + for ( dwIndex = 0; dwIndex < MAX_CIPHER_TYPES; dwIndex++ ) + { + if ( 0 == wcscmp( gppszIhvCipherTypes[dwIndex], pszSrc ) ) + { + (*pCipherType) = (IHV_CIPHER_TYPE) dwIndex; + BAIL( ); + } + } + + // String not found. + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + + +error: + return hr; +} + + +HRESULT +CipherType2Bstr +( + IN IHV_CIPHER_TYPE CipherType, + OUT BSTR* pbstrDest +) +{ + HRESULT hr = S_OK; + + if ( !pbstrDest ) + { + hr = E_INVALIDARG; + BAIL_ON_FAILURE( hr ); + } + + if ( CipherType < None || CipherType >= IHVCipherInvalid ) + { + hr = E_UNEXPECTED; + BAIL_ON_FAILURE( hr ); + } + + (*pbstrDest) = SysAllocString( gppszIhvCipherTypes[(DWORD) CipherType] ); + if ( !(*pbstrDest) ) + { + hr = E_OUTOFMEMORY; + BAIL_ON_FAILURE( hr ); + } + +error: + return hr; +} diff --git a/network/wlan/ihvsampleui/utils.h b/network/wlan/ihvsampleui/utils.h new file mode 100644 index 00000000..015e94dd --- /dev/null +++ b/network/wlan/ihvsampleui/utils.h @@ -0,0 +1,98 @@ +// +// Copyright (C) Microsoft Corporation 2005 +// IHV UI Extension sample +// + + +typedef enum _IHV_SECURITY_TYPE IHV_SECURITY_TYPE, *PIHV_SECURITY_TYPE; +typedef enum _IHV_AUTH_TYPE IHV_AUTH_TYPE, *PIHV_AUTH_TYPE; +typedef enum _IHV_CIPHER_TYPE IHV_CIPHER_TYPE, *PIHV_CIPHER_TYPE; + + +HRESULT +Wstr2Bstr +( + _In_ LPCWSTR pszSrc, + _Outptr_ BSTR* pbstrDest +); + +HRESULT +Wstr2Wstr +( + _In_ LPCWSTR pszSrc, + _Outptr_ LPWSTR* ppszDest +); + + +HRESULT +Wstr2Dword +( + IN LPCWSTR pszSrc, + OUT DWORD* pdwDest +); + + +HRESULT +Dword2Bstr +( + IN DWORD dwSrc, + OUT BSTR* pbstrDest +); + + +HRESULT +Wstr2Bool +( + IN LPCWSTR pszSrc, + OUT BOOL* pbDest +); + +HRESULT +Bool2Bstr +( + IN BOOL bSrc, + OUT BSTR* pbstrDest +); + + +HRESULT +Wstr2AuthType +( + IN LPCWSTR pszSrc, + OUT PIHV_AUTH_TYPE pAuthType +); + +HRESULT +AuthType2Bstr +( + IN IHV_AUTH_TYPE AuthType, + OUT BSTR* pbstrDest +); + +HRESULT +Wstr2SecurityType +( + IN LPCWSTR pszSrc, + OUT PIHV_SECURITY_TYPE pSecurityType +); + +HRESULT +SecurityType2Bstr +( + IN IHV_SECURITY_TYPE SecurityType, + OUT BSTR* pbstrDest +); + +HRESULT +Wstr2CipherType +( + IN LPCWSTR pszSrc, + OUT PIHV_CIPHER_TYPE pCipherType +); + +HRESULT +CipherType2Bstr +( + IN IHV_CIPHER_TYPE CipherType, + OUT BSTR* pbstrDest +); diff --git a/network/wlan/wlan.sln b/network/wlan/wlan.sln new file mode 100644 index 00000000..4d92aa78 --- /dev/null +++ b/network/wlan/wlan.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ihvsample", "Ihvsample", "{7ECA32F1-551B-4AF3-9726-A319C35A19D3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ihvsampleui", "Ihvsampleui", "{42757992-DA8D-4434-9B9C-ED549C43BA83}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ihvsample", "ihvsample\ihvsample.vcxproj", "{C2DB15CB-342E-4E84-BFD0-5E839DE380DE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IHVSampleUI", "ihvsampleui\IHVSampleUI.vcxproj", "{A6B24475-C40B-4105-9896-6076AB284D92}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C2DB15CB-342E-4E84-BFD0-5E839DE380DE}.Debug|Win32.ActiveCfg = Debug|Win32 + {C2DB15CB-342E-4E84-BFD0-5E839DE380DE}.Debug|Win32.Build.0 = Debug|Win32 + {C2DB15CB-342E-4E84-BFD0-5E839DE380DE}.Release|Win32.ActiveCfg = Release|Win32 + {C2DB15CB-342E-4E84-BFD0-5E839DE380DE}.Release|Win32.Build.0 = Release|Win32 + {C2DB15CB-342E-4E84-BFD0-5E839DE380DE}.Debug|x64.ActiveCfg = Debug|x64 + {C2DB15CB-342E-4E84-BFD0-5E839DE380DE}.Debug|x64.Build.0 = Debug|x64 + {C2DB15CB-342E-4E84-BFD0-5E839DE380DE}.Release|x64.ActiveCfg = Release|x64 + {C2DB15CB-342E-4E84-BFD0-5E839DE380DE}.Release|x64.Build.0 = Release|x64 + {A6B24475-C40B-4105-9896-6076AB284D92}.Debug|Win32.ActiveCfg = Debug|Win32 + {A6B24475-C40B-4105-9896-6076AB284D92}.Debug|Win32.Build.0 = Debug|Win32 + {A6B24475-C40B-4105-9896-6076AB284D92}.Release|Win32.ActiveCfg = Release|Win32 + {A6B24475-C40B-4105-9896-6076AB284D92}.Release|Win32.Build.0 = Release|Win32 + {A6B24475-C40B-4105-9896-6076AB284D92}.Debug|x64.ActiveCfg = Debug|x64 + {A6B24475-C40B-4105-9896-6076AB284D92}.Debug|x64.Build.0 = Debug|x64 + {A6B24475-C40B-4105-9896-6076AB284D92}.Release|x64.ActiveCfg = Release|x64 + {A6B24475-C40B-4105-9896-6076AB284D92}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {C2DB15CB-342E-4E84-BFD0-5E839DE380DE} = {7ECA32F1-551B-4AF3-9726-A319C35A19D3} + {A6B24475-C40B-4105-9896-6076AB284D92} = {42757992-DA8D-4434-9B9C-ED549C43BA83} + EndGlobalSection +EndGlobal diff --git a/network/wsk/echosrv/ReadMe.md b/network/wsk/echosrv/ReadMe.md new file mode 100644 index 00000000..826e09f4 --- /dev/null +++ b/network/wsk/echosrv/ReadMe.md @@ -0,0 +1,58 @@ +WSK TCP Echo Server +=================== + +This sample driver is a minimal driver meant to demonstrate the usage of the Winsock Kernel (WSK) programming interface. + +The sample implements a simple kernel-mode application by using the Winsock Kernel (WSK) programming interface. The application accepts incoming TCP connection requests on port 40007 over both IPv4 and IPv6 and, on each connection, it echoes all received data back to the peer until the connection is closed by the peer. The application is designed to use a single worker thread to perform all of its processing. For better performance on a multi-processor computer, the sample can be enhanced to use more worker threads. This sample is designed such that operations on a given connection should always be processed by the same worker thread. This provides a simple form of synchronization that ensures proper socket closure in a setting where multiple operations might be outstanding and completed asynchronously on a given connection. For the sake of simplicity, this sample does not enforce any limit on the number of connections accepted (other than the natural limit imposed by the available system memory) or on the amount of time that a connection stays alive. A production server application should be designed with these security points in mind. + +This sample is not intended for use in a production environment. + + +WPP SOFTWARE TRACING +-------------------- + +This sample driver uses WPP Software Tracing in order to log its actions. You can find detailed information on WPP Software Tracing in the WDK documentation. Here is a quick overview of one way to collect trace logs from the sample driver by using the tracing tools that are available in the \\tools\\tracing directory in the WDK. All code for this sample is located in the src\\network\\WSK\\echosrv directory. + +1. In a Command Prompt window, copy Echosrv.ctl and Echosrv.pdb into a directory and change to that directory (cd). +2. Start software tracing for the sample driver by typing the following command: + + **tracelog -start echosrvtrace -guid echosrv.ctl -f logfile.etl -flags 0x3** + + The value that is provided for the -flags option determines which events will be logged by the sample driver. The sample currently has two event types denoted by the TRCERROR and TRCINFO macros where TRCERROR is 0x1 and TRCINFO is 0x2. Thus, a flag value of 0x3 (0x1 combined in a bitwise OR with 0x2) in the previous tracelog command tells the sample driver to log both TRCERROR and TRCINFO events. + +3. In order to stop tracing, type the following command: + + tracelog -stop echosrvtrace + +4. Convert the trace logs in Logfile.etl into a human-readable format by typing the following command: + + **tracefmt -o logfile.txt -f logfile.etl -r . -i \\***full-path***\\ echosrv.sys** + +5. Open Logfile.txt to view the trace logs. + +Be aware that tracing for the sample driver can be started at any time before the driver is started or while the driver is already running. + +To run the sample +----------------- + +Install and run this sample driver by using the following steps: + +1. Copy the Echosrv.sys file to a directory on the test machine. +2. In a Command Prompt window, type the following command: + + **sc create echosrv type= kernel binpath= \\***full-path***\\ echosrv.sys** + + where \\*full-path*\\ is the directory that contains the Echosrv.sys file. + +3. To start the driver, type: + + **sc start echosrv** + +4. To stop the driver, type: + + **sc stop echosrv** + +After the driver is installed and started, it will listen for incoming TCP connection requests on port 40007 over both IPv4 and IPv6 protocols until the driver is stopped. On each connection, the driver will echo all the received data back to the peer until the connection is closed by the peer. + +For more information on the usage of the Winsock Kernel (WSK) programming interface, see [Winsock Kernel](http://msdn.microsoft.com/en-us/library/windows/hardware/ff571084). + diff --git a/network/wsk/echosrv/echosrv.ctl b/network/wsk/echosrv/echosrv.ctl new file mode 100644 index 00000000..c66661ee --- /dev/null +++ b/network/wsk/echosrv/echosrv.ctl @@ -0,0 +1 @@ +998bdf51-0349-4fbc-870c-d6130a955a5f WskSampleCtlGuid \ No newline at end of file diff --git a/network/wsk/echosrv/echosrv.sln b/network/wsk/echosrv/echosrv.sln new file mode 100644 index 00000000..ce0d54fc --- /dev/null +++ b/network/wsk/echosrv/echosrv.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echosrv", "echosrv.vcxproj", "{FFEE1E8D-35BB-4B3B-A59E-CC8D6DE39B5D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FFEE1E8D-35BB-4B3B-A59E-CC8D6DE39B5D}.Debug|Win32.ActiveCfg = Debug|Win32 + {FFEE1E8D-35BB-4B3B-A59E-CC8D6DE39B5D}.Debug|Win32.Build.0 = Debug|Win32 + {FFEE1E8D-35BB-4B3B-A59E-CC8D6DE39B5D}.Release|Win32.ActiveCfg = Release|Win32 + {FFEE1E8D-35BB-4B3B-A59E-CC8D6DE39B5D}.Release|Win32.Build.0 = Release|Win32 + {FFEE1E8D-35BB-4B3B-A59E-CC8D6DE39B5D}.Debug|x64.ActiveCfg = Debug|x64 + {FFEE1E8D-35BB-4B3B-A59E-CC8D6DE39B5D}.Debug|x64.Build.0 = Debug|x64 + {FFEE1E8D-35BB-4B3B-A59E-CC8D6DE39B5D}.Release|x64.ActiveCfg = Release|x64 + {FFEE1E8D-35BB-4B3B-A59E-CC8D6DE39B5D}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/network/wsk/echosrv/echosrv.vcxproj b/network/wsk/echosrv/echosrv.vcxproj new file mode 100644 index 00000000..670bbe52 --- /dev/null +++ b/network/wsk/echosrv/echosrv.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {FFEE1E8D-35BB-4B3B-A59E-CC8D6DE39B5D} + $(MSBuildProjectName) + Debug + Win32 + {356832D6-4003-4E04-991A-8885A867DFF1} + + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Desktop + WDM + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + + + true + true + + + + echosrv + + + echosrv + + + echosrv + + + echosrv + + + + true + Level4 + %(AdditionalIncludeDirectories) + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalIncludeDirectories) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\netio.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories) + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalIncludeDirectories) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\netio.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories) + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalIncludeDirectories) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\netio.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories) + + + + + %(AdditionalIncludeDirectories) + + + %(AdditionalIncludeDirectories) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\netio.lib;$(SDK_LIB_PATH)\uuid.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/wsk/echosrv/echosrv.vcxproj.Filters b/network/wsk/echosrv/echosrv.vcxproj.Filters new file mode 100644 index 00000000..f0757e26 --- /dev/null +++ b/network/wsk/echosrv/echosrv.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {7A5FF825-82E8-4B8C-A384-AFE5DDF0241F} + + + h;hpp;hxx;hm;inl;inc;xsd + {7BD60EFA-8AB3-4BA8-8624-F096458DF6FD} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {02D631B3-0A60-43EF-A6F1-92F34CD3105C} + + + inf;inv;inx;mof;mc; + {81350963-697B-4F08-9AEA-8A5426D14C60} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/wsk/echosrv/wsksmple.c b/network/wsk/echosrv/wsksmple.c new file mode 100644 index 00000000..a16d0a3e --- /dev/null +++ b/network/wsk/echosrv/wsksmple.c @@ -0,0 +1,1361 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + wsksample.c + +Abstract: + + This module implements a simple kernel-mode application by using the + Winsock Kernel (WSK) programming interface. The application accepts + incoming connection requests and, on each connection, echoes all received + data back to the peer until the connection is closed by the peer. + The application is designed to use a single worker thread to perform all of + its processing. For better performance on MP machines, the sample may be + enhanced to use more worker threads. Operations on a given connection + should always be processed by the same worker thread. This provides a + simple form of synchronization ensuring proper socket closure in a setting + where multiple operations may be outstanding and completed asynchronously + on a given connection. For the sake of simplicty, this sample does not + enforce any limit on the number of connections accepted (other than the + natural limit imposed by the available system memory) or on the amount of + time a connection stays around. A full-fledged server application should be + designed with these points in mind from a security viewpoint. + +Author: + +Environment: + + Kernel-Mode only + +Revision History: + +--*/ + +#pragma warning(push) +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4214) // bit field types other than int + +#include +#include + +#pragma warning(pop) + +// Software Tracing definitions +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(WskSampleCtlGuid, \ + (998bdf51, 0349, 4fbc, 870c, d6130a955a5f), \ + WPP_DEFINE_BIT(TRCERROR) \ + WPP_DEFINE_BIT(TRCINFO) ) + +#include "wsksmple.tmh" + +// Pool tags used for memory allocations +#define WSKSAMPLE_SOCKET_POOL_TAG ((ULONG)'sksw') +#define WSKSAMPLE_BUFFER_POOL_TAG ((ULONG)'bksw') +#define WSKSAMPLE_GENERIC_POOL_TAG ((ULONG)'xksw') + +// Default length for data buffers used in send and receive operations +#define WSKSAMPLE_DATA_BUFFER_LENGTH 2048 + +// Forward declaration for the socket context structure +typedef struct _WSKSAMPLE_SOCKET_CONTEXT *PWSKSAMPLE_SOCKET_CONTEXT; + +// Forward declaration for the socket operation context structure +typedef struct _WSKSAMPLE_SOCKET_OP_CONTEXT *PWSKSAMPLE_SOCKET_OP_CONTEXT; + +// Function prototype for socket operation handling routines +typedef +VOID +(* PWSKSAMPLE_OP_HANDLER_FN)( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext); + +// Structure that represents a work queue processed by a dedicated worker thread +typedef struct _WSKSAMPLE_WORK_QUEUE { + + // List head + SLIST_HEADER Head; + + // Wake up event + KEVENT Event; + + // Worker thread can exit safely only if queue is empty and Stop is TRUE + BOOLEAN Stop; + + // Worker thread pointer + PETHREAD Thread; + +} WSKSAMPLE_WORK_QUEUE, *PWSKSAMPLE_WORK_QUEUE; + +// Structure that represents the context for a WSK socket operation. +typedef struct _WSKSAMPLE_SOCKET_OP_CONTEXT { + + // Work queue linkage + SLIST_ENTRY QueueEntry; + + // Pointer to the function that will handle the operation + PWSKSAMPLE_OP_HANDLER_FN OpHandler; + + // Pointer to the WSK socket context. + PWSKSAMPLE_SOCKET_CONTEXT SocketContext; + + // IRP to use for the operation + PIRP Irp; + + // Data buffer and MDL used by send and receive operations + _Field_size_bytes_part_(BufferLength,DataLength) PVOID DataBuffer; + PMDL DataMdl; + SIZE_T BufferLength; // size of the buffer + SIZE_T DataLength; // length of actual data stored in the buffer + +} WSKSAMPLE_SOCKET_OP_CONTEXT; + +// Maximum number of operations that can be outstanding on a socket at any time +#define WSKSAMPLE_OP_COUNT 2 + +// Structure that represents the context for a WSK socket. +typedef struct _WSKSAMPLE_SOCKET_CONTEXT { + + // Pointer to the WSK socket. + PWSK_SOCKET Socket; + + // Work queue used for enqueueing operations on the socket + PWSKSAMPLE_WORK_QUEUE WorkQueue; + + // Socket is being closed. + BOOLEAN Closing; + + // Peer has gracefully disconnected its half of the connection and we are + // about to disconnect (or have disconnected) our half. + BOOLEAN Disconnecting; + + // Stop accepting incoming connections. Valid for listening sockets only. + BOOLEAN StopListening; + + // Embedded array of contexts for outstanding operations on the socket. + // Note that operation contexts could also be allocated separately. This + // sample preallocates a fixed number of operation contexts along with + // the socket context for a new socket. + WSKSAMPLE_SOCKET_OP_CONTEXT OpContext[WSKSAMPLE_OP_COUNT]; + +} WSKSAMPLE_SOCKET_CONTEXT; + +// Forward declaration for WskAcceptEvent in WSK_CLIENT_LISTEN_DISPATCH +NTSTATUS +WSKAPI +WskSampleAcceptEvent( + _In_ PVOID SocketContext, + _In_ ULONG Flags, + _In_ PSOCKADDR LocalAddress, + _In_ PSOCKADDR RemoteAddress, + _In_opt_ PWSK_SOCKET AcceptSocket, + _Outptr_result_maybenull_ PVOID *AcceptSocketContext, + _Outptr_result_maybenull_ CONST WSK_CLIENT_CONNECTION_DISPATCH **AcceptSocketDispatch + ); + +// Client-level callback table +const WSK_CLIENT_DISPATCH WskSampleClientDispatch = { + MAKE_WSK_VERSION(1, 0), // This sample uses WSK version 1.0 + 0, // Reserved + NULL // WskClientEvent callback is not required in WSK version 1.0 +}; + +// WSK Registration object +WSK_REGISTRATION WskSampleRegistration; + +// Socket-level callback table for listening sockets +const WSK_CLIENT_LISTEN_DISPATCH WskSampleClientListenDispatch = { + WskSampleAcceptEvent, + NULL, // WskInspectEvent is required only if conditional-accept is used. + NULL // WskAbortEvent is required only if conditional-accept is used. +}; + +// Global reference to the socket context for the listening socket +PWSKSAMPLE_SOCKET_CONTEXT WskSampleListeningSocketContext; + +// Global work queue used for enqueueing all socket operations +WSKSAMPLE_WORK_QUEUE WskSampleWorkQueue; + +// IPv6 wildcard address and port number 40007 to listen on +SOCKADDR_IN6 IPv6ListeningAddress = { + AF_INET6, + 0x479c, // 40007 in hex in network byte order + 0, + IN6ADDR_ANY_INIT, + 0}; + +// Forward function declarations + +DRIVER_INITIALIZE DriverEntry; +DRIVER_UNLOAD WskSampleUnload; +KSTART_ROUTINE WskSampleWorkerThread; +IO_COMPLETION_ROUTINE WskSampleSyncIrpCompletionRoutine; +IO_COMPLETION_ROUTINE WskSampleReceiveIrpCompletionRoutine; +IO_COMPLETION_ROUTINE WskSampleSendIrpCompletionRoutine; +IO_COMPLETION_ROUTINE WskSampleDisconnectIrpCompletionRoutine; +IO_COMPLETION_ROUTINE WskSampleCloseIrpCompletionRoutine; + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +_Must_inspect_result_ +__drv_allocatesMem(Mem) +_Success_(return != NULL) +PWSKSAMPLE_SOCKET_CONTEXT +WskSampleAllocateSocketContext( + _In_ PWSKSAMPLE_WORK_QUEUE WorkQueue, + _In_ ULONG BufferLength + ); + +_At_(SocketContext, __drv_freesMem(Mem)) +VOID +WskSampleFreeSocketContext( + _In_ PWSKSAMPLE_SOCKET_CONTEXT SocketContext + ); + +_At_(SocketOpContext, __drv_aliasesMem) +VOID +WskSampleEnqueueOp( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext, + _In_ PWSKSAMPLE_OP_HANDLER_FN OpHandler + ); + +VOID +WskSampleWorkerThread ( + _In_ PVOID Context + ); + +VOID +WskSampleSetupListeningSocket( + _In_ PWSK_PROVIDER_NPI WskProviderNpi, + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ); + +VOID +WskSampleOpStartListen( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ); + +VOID +WskSampleOpStopListen( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ); + +VOID +WskSampleOpReceive( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ); + +VOID +WskSampleOpSend( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ); + +VOID +WskSampleOpDisconnect( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ); + +VOID +WskSampleOpClose( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ); + +VOID +WskSampleOpFree( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ); + +NTSTATUS +WskSampleStartWorkQueue( + _Out_ PWSKSAMPLE_WORK_QUEUE WorkQueue + ); + +VOID +WskSampleStopWorkQueue( + _In_ PWSKSAMPLE_WORK_QUEUE WorkQueue + ); + +VOID +WskSampleUnload( + _In_ PDRIVER_OBJECT DriverObject + ); + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, WskSampleStartWorkQueue) +#pragma alloc_text(PAGE, WskSampleUnload) +#pragma alloc_text(PAGE, WskSampleStopWorkQueue) +#pragma alloc_text(PAGE, WskSampleWorkerThread) +#pragma alloc_text(PAGE, WskSampleOpStartListen) +#pragma alloc_text(PAGE, WskSampleOpStopListen) +#pragma alloc_text(PAGE, WskSampleSetupListeningSocket) +#pragma alloc_text(PAGE, WskSampleOpReceive) +#pragma alloc_text(PAGE, WskSampleOpSend) +#pragma alloc_text(PAGE, WskSampleOpDisconnect) +#pragma alloc_text(PAGE, WskSampleOpClose) +#pragma alloc_text(PAGE, WskSampleOpFree) + +#endif + +// Driver entry routine +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS status; + WSK_CLIENT_NPI wskClientNpi; + + UNREFERENCED_PARAMETER(RegistryPath); + + PAGED_CODE(); + + // Allocate a socket context that will be used for queueing an operation + // to setup a listening socket that will accept incoming connections + WskSampleListeningSocketContext = WskSampleAllocateSocketContext( + &WskSampleWorkQueue, 0); + + if(WskSampleListeningSocketContext == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Register with WSK. + wskClientNpi.ClientContext = NULL; + wskClientNpi.Dispatch = &WskSampleClientDispatch; + status = WskRegister(&wskClientNpi, &WskSampleRegistration); + + if(!NT_SUCCESS(status)) { + WskSampleFreeSocketContext(WskSampleListeningSocketContext); + return status; + } + + // Initialize and start the global work queue + status = WskSampleStartWorkQueue(&WskSampleWorkQueue); + + if(!NT_SUCCESS(status)) { + WskDeregister(&WskSampleRegistration); + WskSampleFreeSocketContext(WskSampleListeningSocketContext); + return status; + } + + // Enqueue the first operation to setup the listening socket + WskSampleEnqueueOp(&WskSampleListeningSocketContext->OpContext[0], + WskSampleOpStartListen); + + // Everything has been initiated successfully. Now we can enable the + // unload routine and return. + DriverObject->DriverUnload = WskSampleUnload; + + // Initialize software tracing + WPP_INIT_TRACING(DriverObject, RegistryPath); + + DoTraceMessage(TRCINFO, "LOADED"); + + return STATUS_SUCCESS; +} + +// Driver unload routine +VOID +WskSampleUnload( + _In_ PDRIVER_OBJECT DriverObject + ) +{ + C_ASSERT(WSKSAMPLE_OP_COUNT >= 2); + + UNREFERENCED_PARAMETER(DriverObject); + + PAGED_CODE(); + + DoTraceMessage(TRCINFO, "UNLOAD START"); + + // Enqueue an operation to stop listening for any new connections. + // This will close the listening socket, but there may still be + // connection-oriented sockets that are open. The WskDeregister call + // below will block until all sockets including the connection-oriented + // ones that were accepted over the listening socket are closed. Since + // this sample currently closes the connection-oriented sockets + // only when an error occurs or when the remote peer disconnects, the + // unload of the driver will be blocked until the last connection is + // disconnected by the peer. If the requirements for a full-fledged + // application are such that the unload of the driver (or any other + // similar event) proactively closes all the sockets (that may + // otherwise stay open indefinitely), then the application must keep + // track of all the sockets in a fashion that allows enumerating them and + // closing each socket. In such a scheme, the application must still ensure + // that a socket is closed only when other calls can NOT anymore be issued + // on the socket. + WskSampleEnqueueOp(&WskSampleListeningSocketContext->OpContext[1], + WskSampleOpStopListen); + + // Deregister with WSK. This call will wait until all the references to + // the WSK provider NPI are released and all the sockets are closed. Note + // that if the worker thread has not started yet, then when it eventually + // starts, its WskCaptureProviderNPI call will fail and the work queue + // will be flushed and cleaned up properly. + WskDeregister(&WskSampleRegistration); + + // WskDeregister returns only if all the sockets are closed. Thus, at this + // point, it's guaranteed that all socket are closed, which also means that + // there can not be any further outstanding operations on any socket. So, + // the worker thread can now safely stop processing the work queue if there + // are no queued items. Signal the worker thread to stop and wait for it. + WskSampleStopWorkQueue(&WskSampleWorkQueue); + + DoTraceMessage(TRCINFO, "UNLOAD END"); + + WPP_CLEANUP(DriverObject); +} + +// Initialize a given work queue and start the worker thread for it +NTSTATUS +WskSampleStartWorkQueue( + _Out_ PWSKSAMPLE_WORK_QUEUE WorkQueue + ) +{ + NTSTATUS status; + HANDLE threadHandle; + + PAGED_CODE(); + + InitializeSListHead(&WorkQueue->Head); + KeInitializeEvent(&WorkQueue->Event, SynchronizationEvent, FALSE); + WorkQueue->Stop = FALSE; + + status = PsCreateSystemThread( + &threadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, + WskSampleWorkerThread, WorkQueue); + + if(!NT_SUCCESS(status)) { + return status; + } + + status = ObReferenceObjectByHandle( + threadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, + &WorkQueue->Thread, NULL); + + ZwClose(threadHandle); + + if(!NT_SUCCESS(status)) { + WorkQueue->Stop = TRUE; + KeSetEvent(&WorkQueue->Event, 0, FALSE); + } + + return status; +} + +// Stop a given work queue and wait for its worker thread to exit +VOID +WskSampleStopWorkQueue( + _In_ PWSKSAMPLE_WORK_QUEUE WorkQueue + ) +{ + PAGED_CODE(); + + ASSERT(WorkQueue->Stop == FALSE); + ASSERT(WorkQueue->Thread); + WorkQueue->Stop = TRUE; + KeSetEvent(&WorkQueue->Event, 0, FALSE); + KeWaitForSingleObject(WorkQueue->Thread, Executive, KernelMode, FALSE,NULL); + ObDereferenceObject(WorkQueue->Thread); +} + +// Allocate and setup a socket context +_Must_inspect_result_ +__drv_allocatesMem(Mem) +_Success_(return != NULL) +PWSKSAMPLE_SOCKET_CONTEXT +WskSampleAllocateSocketContext( + _In_ PWSKSAMPLE_WORK_QUEUE WorkQueue, + _In_ ULONG BufferLength + ) +{ + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + // Allocate and setup a socket context with optional data buffers, and + // attach the socket to the given work queue. Even though this sample uses + // only a single global work queue, the sample is designed to be easily + // adaptable to using multiple work queues. If the sample is changed to + // use multiple work queues (say a work queue per processor), a given + // socket will/must always use the same work queue. + + socketContext = ExAllocatePoolWithTag( + NonPagedPool, sizeof(*socketContext), WSKSAMPLE_SOCKET_POOL_TAG); + + if(socketContext != NULL) { + + ULONG i; + + RtlZeroMemory(socketContext, sizeof(WSKSAMPLE_SOCKET_CONTEXT)); + + socketContext->WorkQueue = WorkQueue; + + for(i = 0; i < WSKSAMPLE_OP_COUNT; i++) { + + socketContext->OpContext[i].SocketContext = socketContext; + + socketContext->OpContext[i].Irp = IoAllocateIrp(1, FALSE); + if(socketContext->OpContext[i].Irp == NULL) { + goto failure; + } + + if(BufferLength > 0) { + socketContext->OpContext[i].DataBuffer = ExAllocatePoolWithTag( + NonPagedPool, BufferLength, WSKSAMPLE_BUFFER_POOL_TAG); + if(socketContext->OpContext[i].DataBuffer == NULL) { + goto failure; + } + socketContext->OpContext[i].DataMdl = IoAllocateMdl( + socketContext->OpContext[i].DataBuffer, + BufferLength, FALSE, FALSE, NULL); + if(socketContext->OpContext[i].DataMdl == NULL) { + goto failure; + } + MmBuildMdlForNonPagedPool(socketContext->OpContext[i].DataMdl); + socketContext->OpContext[i].BufferLength = BufferLength; + } + } + + DoTraceMessage(TRCINFO, "AllocateSocketContext: %p", socketContext); + + return socketContext; + } + +failure: + + DoTraceMessage(TRCERROR, "AllocateSocketContext: FAIL"); + + if(socketContext) { + WskSampleFreeSocketContext(socketContext); + } + + return NULL; +} + +// Cleanup and free a socket context +_At_(SocketContext, __drv_freesMem(Mem)) +VOID +WskSampleFreeSocketContext( + _In_ PWSKSAMPLE_SOCKET_CONTEXT SocketContext + ) +{ + ULONG i; + + // Socket context is freed only after all the WSK calls on the + // socket are completed and all enqueued operations for the socket + // are dequeued. So, we can safely free the Irp, Mdl, and data buffer + // pointed by the socket operation contexts. + + for(i = 0; i < WSKSAMPLE_OP_COUNT; i++) { + + if(SocketContext->OpContext[i].Irp != NULL) { + IoFreeIrp(SocketContext->OpContext[i].Irp); + SocketContext->OpContext[i].Irp = NULL; + } + if(SocketContext->OpContext[i].DataMdl != NULL) { + IoFreeMdl(SocketContext->OpContext[i].DataMdl); + SocketContext->OpContext[i].DataMdl = NULL; + } + if(SocketContext->OpContext[i].DataBuffer != NULL) { + ExFreePool(SocketContext->OpContext[i].DataBuffer); + SocketContext->OpContext[i].DataBuffer = NULL; + } + } + + DoTraceMessage(TRCINFO, "FreeSocketContext: %p", SocketContext); + + ExFreePool(SocketContext); +} + +// Enqueue an operation on a socket +_At_(SocketOpContext, __drv_aliasesMem) +VOID +WskSampleEnqueueOp( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext, + _In_ PWSKSAMPLE_OP_HANDLER_FN OpHandler + ) +{ + PWSKSAMPLE_WORK_QUEUE workQueue = SocketOpContext->SocketContext->WorkQueue; + + SocketOpContext->OpHandler = OpHandler; + + #pragma warning(disable:4054) // turn off typecast warning + + if(NULL == InterlockedPushEntrySList( + &workQueue->Head, + &SocketOpContext->QueueEntry)) { + DoTraceMessage(TRCINFO, "EnqueueOp: %p %p (%p) SetEvent", + SocketOpContext->SocketContext, SocketOpContext, (PVOID)OpHandler); + // Work queue was empty. So, signal the work queue event in case the + // worker thread is waiting on the event for more operations. + KeSetEvent(&workQueue->Event, 0, FALSE); + } + else + DoTraceMessage(TRCINFO, "EnqueueOp: %p %p (%p)", + SocketOpContext->SocketContext, SocketOpContext, (PVOID)OpHandler); + + #pragma warning(default:4054) // restore typecast warning +} + +// Worker thread which drains and processes a given work queue +VOID +WskSampleWorkerThread ( + _In_ PVOID Context + ) +{ + PWSKSAMPLE_WORK_QUEUE workQueue; + PSLIST_ENTRY listEntryRev, listEntry, next; + + PAGED_CODE(); + + workQueue = (PWSKSAMPLE_WORK_QUEUE)Context; + + for(;;) { + + // Flush all the queued operations into a local list + listEntryRev = InterlockedFlushSList(&workQueue->Head); + + if(listEntryRev == NULL) { + + // There's no work to do. If we are allowed to stop, then stop. + if(workQueue->Stop) { + DoTraceMessage(TRCINFO, "WorkerThread: WQ %p exit", workQueue); + break; + } + + DoTraceMessage(TRCINFO, "WorkerThread: WQ %p wait", workQueue); + + // Otherwise, wait for more operations to be enqueued. + KeWaitForSingleObject(&workQueue->Event, + Executive, KernelMode, FALSE, 0); + continue; + } + + DoTraceMessage(TRCINFO, "WorkerThread: WQ %p process", workQueue); + + // Need to reverse the flushed list in order to preserve the FIFO order + listEntry = NULL; + while (listEntryRev != NULL) { + next = listEntryRev->Next; + listEntryRev->Next = listEntry; + listEntry = listEntryRev; + listEntryRev = next; + } + + // Now process the correctly ordered list of operations one by one + while(listEntry) { + + PWSKSAMPLE_SOCKET_OP_CONTEXT socketOpContext = + CONTAINING_RECORD(listEntry, + WSKSAMPLE_SOCKET_OP_CONTEXT, QueueEntry); + PWSKSAMPLE_OP_HANDLER_FN opHandler = socketOpContext->OpHandler; + + listEntry = listEntry->Next; + + opHandler(socketOpContext); + } + } + + PsTerminateSystemThread(STATUS_SUCCESS); +} + +// Operation handler for creating and setting up a listening socket +VOID +WskSampleOpStartListen( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ) +{ + NTSTATUS status; + WSK_PROVIDER_NPI wskProviderNpi; + + PAGED_CODE(); + + // This is the very first operation that runs in the worker thread. + // This operation is made up of multiple WSK calls, and no other + // operations will be processed until this compound operation is + // finished synchronously in the context of the worker thread. + + // Capture the WSK Provider NPI + status = WskCaptureProviderNPI( + &WskSampleRegistration, + WSK_INFINITE_WAIT, + &wskProviderNpi); + + if(NT_SUCCESS(status)) { + + // Create a listening socket + WskSampleSetupListeningSocket(&wskProviderNpi, SocketOpContext); + + // Release the WSK provider NPI since we won't use it anymore + WskReleaseProviderNPI(&WskSampleRegistration); + } + else { + // WskCaptureProviderNPI will fail if WskDeregister is called + // from the Driver Unload routine before the WSK subsystem + // becomes ready. + DoTraceMessage(TRCINFO, + "OpStartListen: WskCaptureProviderNPI failed 0x%lx", status); + } +} + +// Create and prepare a listening socket for accepting incoming connections +VOID +WskSampleSetupListeningSocket( + _In_ PWSK_PROVIDER_NPI WskProviderNpi, + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ) +{ + NTSTATUS status; + WSK_EVENT_CALLBACK_CONTROL callbackControl; + KEVENT compEvent; + ULONG optionValue; + PWSK_SOCKET listeningSocket = NULL; + PWSK_PROVIDER_LISTEN_DISPATCH dispatch = NULL; + PWSKSAMPLE_SOCKET_CONTEXT socketContext = SocketOpContext->SocketContext; + PIRP irp = SocketOpContext->Irp; + + PAGED_CODE(); + + KeInitializeEvent(&compEvent, SynchronizationEvent, FALSE); + + if(socketContext->Socket != NULL) { + + // If there's already a socket then we must be getting called because + // the WskAcceptevent indicated a NULL AcceptSocket, i.e. the listening + // socket is no longer functional. So, we will close the existing + // listening socket and try to create a new one. + + if(socketContext->StopListening) { + // Listening socket is already being closed because the driver + // is being unloaded. So, there's no need to recreate it. + return; + } + + IoReuseIrp(irp, STATUS_UNSUCCESSFUL); + + IoSetCompletionRoutine(irp, + WskSampleSyncIrpCompletionRoutine, + &compEvent, TRUE, TRUE, TRUE); + + ((PWSK_PROVIDER_LISTEN_DISPATCH) + socketContext->Socket->Dispatch)-> + WskCloseSocket(socketContext->Socket, irp); + + KeWaitForSingleObject(&compEvent, Executive, KernelMode, FALSE, NULL); + + ASSERT(NT_SUCCESS(irp->IoStatus.Status)); + socketContext->Socket = NULL; + + IoReuseIrp(irp, STATUS_UNSUCCESSFUL); + } + else { + // First, configure the WSK client such that WskAcceptEvent callback + // will be automatically enabled on the listening socket upon creation. + callbackControl.NpiId = (PNPIID)&NPI_WSK_INTERFACE_ID; + callbackControl.EventMask = WSK_EVENT_ACCEPT; + status = WskProviderNpi->Dispatch->WskControlClient( + WskProviderNpi->Client, + WSK_SET_STATIC_EVENT_CALLBACKS, + sizeof(callbackControl), + &callbackControl, + 0, + NULL, + NULL, + NULL); + if(!NT_SUCCESS(status)) { + DoTraceMessage(TRCERROR, + "SetupListeningSocket: WSK_SET_STATIC_EVENT_CALLBACKS FAIL 0x%lx", + status); + goto failexit; + } + } + + // Create a listening socket over AF_INET6 address family. Put the socket + // into dual-family mode by setting IPV6_V6ONLY option to FALSE so that + // AF_INET traffic is also handled over the same socket. + + IoSetCompletionRoutine(irp, + WskSampleSyncIrpCompletionRoutine, + &compEvent, TRUE, TRUE, TRUE); + + // We do not need to check the return status since the actual completion + // status will be captured from the IRP after the IRP is completed. + WskProviderNpi->Dispatch->WskSocket( + WskProviderNpi->Client, + AF_INET6, + SOCK_STREAM, + IPPROTO_TCP, + WSK_FLAG_LISTEN_SOCKET, + socketContext, + &WskSampleClientListenDispatch, + NULL, // Process + NULL, // Thread + NULL, // SecurityDescriptor + irp); + + KeWaitForSingleObject(&compEvent, Executive, KernelMode, FALSE, NULL); + + if(!NT_SUCCESS(irp->IoStatus.Status)) { + DoTraceMessage(TRCERROR, "SetupListeningSocket: WskSocket FAIL 0x%lx", + irp->IoStatus.Status); + goto failexit; + } + + listeningSocket = (PWSK_SOCKET)irp->IoStatus.Information; + dispatch = (PWSK_PROVIDER_LISTEN_DISPATCH)listeningSocket->Dispatch; + + // Set IPV6_V6ONLY to FALSE before the bind operation + + optionValue = 0; + + IoReuseIrp(irp, STATUS_UNSUCCESSFUL); + IoSetCompletionRoutine(irp, + WskSampleSyncIrpCompletionRoutine, + &compEvent, TRUE, TRUE, TRUE); + + // We do not need to check the return status since the actual completion + // status will be captured from the IRP after the IRP is completed. + dispatch->WskControlSocket( + listeningSocket, + WskSetOption, + IPV6_V6ONLY, + IPPROTO_IPV6, + sizeof(optionValue), + &optionValue, + 0, + NULL, + NULL, + irp); + + KeWaitForSingleObject(&compEvent, Executive, KernelMode, FALSE, NULL); + + if(!NT_SUCCESS(irp->IoStatus.Status)) { + DoTraceMessage(TRCERROR, "SetupListeningSocket: IPV6_V6ONLY FAIL 0x%lx", + irp->IoStatus.Status); + goto failexit; + } + + // Bind the socket to the wildcard address. Once bind is completed, + // WSK provider will make WskAcceptEvent callbacks as connections arrive. + IoReuseIrp(irp, STATUS_UNSUCCESSFUL); + IoSetCompletionRoutine(irp, + WskSampleSyncIrpCompletionRoutine, + &compEvent, TRUE, TRUE, TRUE); + + // We do not need to check the return status since the actual completion + // status will be captured from the IRP after the IRP is completed. + dispatch->WskBind( + listeningSocket, + (PSOCKADDR)&IPv6ListeningAddress, + 0, + irp); + + KeWaitForSingleObject(&compEvent, Executive, KernelMode, FALSE, NULL); + + if(!NT_SUCCESS(irp->IoStatus.Status)) { + DoTraceMessage(TRCERROR, "SetupListeningSocket: WskBind FAIL 0x%lx", + irp->IoStatus.Status); + goto failexit; + } + + // Store the WSK socket pointer in the socket context only if everthing + // has succeeded. Otherwise, the listening WSK socket, if one was created + // successfully, will be closed inline below. + ASSERT(socketContext->Socket == NULL); + socketContext->Socket = listeningSocket; + + DoTraceMessage(TRCINFO, "SetupListeningSocket: %p %p", + socketContext, SocketOpContext); + + return; + +failexit: + + if(listeningSocket) { + + IoReuseIrp(irp, STATUS_UNSUCCESSFUL); + IoSetCompletionRoutine(irp, + WskSampleSyncIrpCompletionRoutine, + &compEvent, TRUE, TRUE, TRUE); + + dispatch->WskCloseSocket(listeningSocket, irp); + KeWaitForSingleObject(&compEvent, Executive, KernelMode, FALSE, NULL); + } +} + +// IRP completion routine used for synchronously waiting for completion +_Use_decl_annotations_ +NTSTATUS +WskSampleSyncIrpCompletionRoutine( + PDEVICE_OBJECT Reserved, + PIRP Irp, + PVOID Context + ) +{ + PKEVENT compEvent = (PKEVENT)Context; + _Analysis_assume_(Context != NULL); + UNREFERENCED_PARAMETER(Reserved); + UNREFERENCED_PARAMETER(Irp); + KeSetEvent(compEvent, 2, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +// Operation handler for stopping listening +VOID +WskSampleOpStopListen( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ) +{ + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + PAGED_CODE(); + + socketContext = SocketOpContext->SocketContext; + + DoTraceMessage(TRCINFO, "OpStopListen: %p %p", + socketContext, SocketOpContext); + + socketContext->StopListening = TRUE; + + if(socketContext->Socket == NULL) { + // Listening socket was NOT created due to some failure. + // All we need to do is to free up the socket context. + WskSampleEnqueueOp(SocketOpContext, WskSampleOpFree); + } + else { + // Enqueue an operation to close the listening socket + WskSampleEnqueueOp(SocketOpContext, WskSampleOpClose); + } +} + +// Listening socket callback which is invoked whenever a new connection arrives. +NTSTATUS +WSKAPI +WskSampleAcceptEvent( + _In_ PVOID SocketContext, + _In_ ULONG Flags, + _In_ PSOCKADDR LocalAddress, + _In_ PSOCKADDR RemoteAddress, + _In_opt_ PWSK_SOCKET AcceptSocket, + _Outptr_result_maybenull_ PVOID *AcceptSocketContext, + _Outptr_result_maybenull_ CONST WSK_CLIENT_CONNECTION_DISPATCH **AcceptSocketDispatch + ) +{ + PWSKSAMPLE_SOCKET_CONTEXT socketContext = NULL; + PWSKSAMPLE_SOCKET_CONTEXT listeningSocketContext; + ULONG i; + + UNREFERENCED_PARAMETER(Flags); + UNREFERENCED_PARAMETER(LocalAddress); + UNREFERENCED_PARAMETER(RemoteAddress); + + listeningSocketContext = (PWSKSAMPLE_SOCKET_CONTEXT)SocketContext; + + if(AcceptSocket == NULL) { + // If WSK provider makes a WskAcceptEvent callback with NULL + // AcceptSocket, this means that the listening socket is no longer + // functional. The WSK client may handle this situation by trying + // to create a new listening socket or by restarting the driver, etc. + // In this sample, we will attempt to close the existing listening + // socket and create a new one. Note that the WskSampleAcceptEvent + // callback is guaranteed to be invoked with a NULL AcceptSocket + // by the WSK subsystem only *once*. So, we can safely use the same + // operation context that was originally used for enqueueing the first + // WskSampleStartListen operation on the listening socket. The + // WskSampleStartListen operation will close the existing listening + // socket and create a new one. + WskSampleEnqueueOp(&listeningSocketContext->OpContext[0], + WskSampleOpStartListen); + return STATUS_REQUEST_NOT_ACCEPTED; + } + + // Allocate socket context for the newly accepted socket. + socketContext = WskSampleAllocateSocketContext( + &WskSampleWorkQueue, WSKSAMPLE_DATA_BUFFER_LENGTH); + + if(socketContext == NULL) { + return STATUS_REQUEST_NOT_ACCEPTED; + } + + socketContext->Socket = AcceptSocket; + + DoTraceMessage(TRCINFO, "AcceptEvent: %p", socketContext); + + // Enqueue receive operations on the accepted socket. Whenever a receive + // operation is completed successfully, the received data will be echoed + // back to the peer via a send operation. Whenever a send operation is + // completed, a new receive request will be issued over the connection. + // This will continue until the connection is closed by the peer. + for(i = 0; i < WSKSAMPLE_OP_COUNT; i++) { + _Analysis_assume_(socketContext == socketContext->OpContext[i].SocketContext); + WskSampleEnqueueOp(&socketContext->OpContext[i], WskSampleOpReceive); + } + + // Since we will not use any callbacks on the accepted socket, we specify no + // socketContext or callback dispatch table pointer for the accepted socket. + *AcceptSocketContext = NULL; + *AcceptSocketDispatch = NULL; + + return STATUS_SUCCESS; +} + +// Operation handler for issuing a receive request on a connected socket +VOID +WskSampleOpReceive( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ) +{ + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + PAGED_CODE(); + + socketContext = SocketOpContext->SocketContext; + + if(socketContext->Closing || socketContext->Disconnecting) { + // Do not call WskReceive if socket is being disconnected + // or closed. The operation context will not be used any more. + DoTraceMessage(TRCINFO, "OpReceive: %p %p SKIP", + socketContext, SocketOpContext); + } + else { + WSK_BUF wskbuf; + CONST WSK_PROVIDER_CONNECTION_DISPATCH *dispatch; + + dispatch = socketContext->Socket->Dispatch; + + wskbuf.Offset = 0; + wskbuf.Length = SocketOpContext->BufferLength; + wskbuf.Mdl = SocketOpContext->DataMdl; + + IoReuseIrp(SocketOpContext->Irp, STATUS_UNSUCCESSFUL); + IoSetCompletionRoutine(SocketOpContext->Irp, + WskSampleReceiveIrpCompletionRoutine, + SocketOpContext, TRUE, TRUE, TRUE); + + DoTraceMessage(TRCINFO, "OpReceive: %p %p %Iu", + socketContext, SocketOpContext, wskbuf.Length); + + // No need to check the return status here. The IRP completion + // routine will take action based on the completion status. + dispatch->WskReceive( + socketContext->Socket, + &wskbuf, + 0, + SocketOpContext->Irp); + } +} + +// IRP completion routine for WskReceive requests +_Use_decl_annotations_ +NTSTATUS +WskSampleReceiveIrpCompletionRoutine( + PDEVICE_OBJECT Reserved, + PIRP Irp, + PVOID Context + ) +{ + PWSKSAMPLE_SOCKET_OP_CONTEXT socketOpContext; + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + UNREFERENCED_PARAMETER(Reserved); + + _Analysis_assume_(Context != NULL); + + socketOpContext = (PWSKSAMPLE_SOCKET_OP_CONTEXT)Context; + socketContext = socketOpContext->SocketContext; + + DoTraceMessage(TRCINFO, + "ReceiveIrpCompletionRoutine: %p %p 0x%lx %Iu", + socketContext, socketOpContext, Irp->IoStatus.Status, + Irp->IoStatus.Information); + + if(!NT_SUCCESS(Irp->IoStatus.Status)) { + // Receive failed. Enqueue an operation to close the socket + WskSampleEnqueueOp(socketOpContext, WskSampleOpClose); + } + else { + if(Irp->IoStatus.Information == 0) { + // Successful receive completion with 0 bytes means the peer + // has gracefully disconnected its half of the connection. + // So, we enqueue an operation to disconnect our half. + WskSampleEnqueueOp(socketOpContext, WskSampleOpDisconnect); + } + else { + // Receive has completed with some data. So, we enqueue an + // operation to send the data back. Note that the data + // buffer is attached to the operation context that is being + // queued. We just need to remember the actual length of + // data received into the buffer. + socketOpContext->DataLength = Irp->IoStatus.Information; + WskSampleEnqueueOp(socketOpContext, WskSampleOpSend); + } + } + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +// Operation handler for issuing a send request on a connected socket +VOID +WskSampleOpSend( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ) +{ + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + PAGED_CODE(); + + socketContext = SocketOpContext->SocketContext; + + if(socketContext->Closing || socketContext->Disconnecting) { + // Do not call WskSend if socket is being disconnected + // or closed. The operation context will not be used any more. + DoTraceMessage(TRCINFO, "OpSend: %p %p SKIP", + socketContext, SocketOpContext); + } + else { + WSK_BUF wskbuf; + CONST WSK_PROVIDER_CONNECTION_DISPATCH *dispatch; + + dispatch = socketContext->Socket->Dispatch; + + wskbuf.Offset = 0; + wskbuf.Length = SocketOpContext->DataLength; + wskbuf.Mdl = SocketOpContext->DataMdl; + + IoReuseIrp(SocketOpContext->Irp, STATUS_UNSUCCESSFUL); + IoSetCompletionRoutine(SocketOpContext->Irp, + WskSampleSendIrpCompletionRoutine, + SocketOpContext, TRUE, TRUE, TRUE); + + DoTraceMessage(TRCINFO, "OpSend: %p %p %Iu", + socketContext, SocketOpContext, wskbuf.Length); + + // No need to check the return status here. The IRP completion + // routine will take action based on the completion status. + dispatch->WskSend( + socketContext->Socket, + &wskbuf, + 0, + SocketOpContext->Irp); + } +} + +// IRP completion routine for WskSend requests +_Use_decl_annotations_ +NTSTATUS +WskSampleSendIrpCompletionRoutine( + PDEVICE_OBJECT Reserved, + PIRP Irp, + PVOID Context + ) +{ + PWSKSAMPLE_SOCKET_OP_CONTEXT socketOpContext; + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + UNREFERENCED_PARAMETER(Reserved); + + _Analysis_assume_(Context != NULL); + + socketOpContext = (PWSKSAMPLE_SOCKET_OP_CONTEXT)Context; + socketContext = socketOpContext->SocketContext; + + DoTraceMessage(TRCINFO, + "SendIrpCompletionRoutine: %p %p 0x%lx %Iu", socketContext, + socketOpContext, Irp->IoStatus.Status, Irp->IoStatus.Information); + + if(!NT_SUCCESS(Irp->IoStatus.Status)) { + // Send failed. Enqueue an operation to close the socket. + WskSampleEnqueueOp(socketOpContext, WskSampleOpClose); + } + else { + // Send succeeded. Enqueue an operation to receive more data. + WskSampleEnqueueOp(socketOpContext, WskSampleOpReceive); + } + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +// Operation handler for issuing a disconnect request on a connected socket +VOID +WskSampleOpDisconnect( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ) +{ + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + PAGED_CODE(); + + socketContext = SocketOpContext->SocketContext; + + if(socketContext->Closing || socketContext->Disconnecting) { + // Do not call WskDisconnect if socket is already being + // disconnected or closed. A disconnect operation may get + // enqueued multiple times as a result of multiple outstanding + // receive requests getting completed with success and 0 bytes. + DoTraceMessage(TRCINFO, "OpDisconnect: %p %p SKIP", + socketContext, SocketOpContext); + } + else { + CONST WSK_PROVIDER_CONNECTION_DISPATCH *dispatch; + + dispatch = socketContext->Socket->Dispatch; + + socketContext->Disconnecting = TRUE; + + IoReuseIrp(SocketOpContext->Irp, STATUS_UNSUCCESSFUL); + IoSetCompletionRoutine(SocketOpContext->Irp, + WskSampleDisconnectIrpCompletionRoutine, + SocketOpContext, TRUE, TRUE, TRUE); + + DoTraceMessage(TRCINFO, "OpDisconnect: %p %p", + socketContext, SocketOpContext); + + // No need to check the return status here. The IRP completion + // routine will take action based on the completion status. + dispatch->WskDisconnect( + socketContext->Socket, + NULL, + 0, + SocketOpContext->Irp); + } +} + +// IRP completion routine for WskDisconnect requests +_Use_decl_annotations_ +NTSTATUS +WskSampleDisconnectIrpCompletionRoutine( + PDEVICE_OBJECT Reserved, + PIRP Irp, + PVOID Context + ) +{ + PWSKSAMPLE_SOCKET_OP_CONTEXT socketOpContext; + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + UNREFERENCED_PARAMETER(Reserved); + UNREFERENCED_PARAMETER(Irp); + + _Analysis_assume_(Context != NULL); + + socketOpContext = (PWSKSAMPLE_SOCKET_OP_CONTEXT)Context; + socketContext = socketOpContext->SocketContext; + + DoTraceMessage(TRCINFO, "DisconnectIrpCompletionRoutine: %p %p 0x%lx", + socketContext, socketOpContext, Irp->IoStatus.Status); + + // Disconnect completed. Enqueue an operation to close the socket. + WskSampleEnqueueOp(socketOpContext, WskSampleOpClose); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +// Operation handler for closing a socket +VOID +WskSampleOpClose( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ) +{ + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + PAGED_CODE(); + + socketContext = SocketOpContext->SocketContext; + + if(socketContext->Closing) { + // Do not call WskClose if socket is already being closed. + // A close operation may get enqueued multiple times as a result + // of multiple outstanding send/receive/disconnect operations + // getting completed with failure. + DoTraceMessage(TRCINFO, "OpClose: %p %p SKIP", + socketContext, SocketOpContext); + } + else { + CONST WSK_PROVIDER_BASIC_DISPATCH *dispatch; + + socketContext->Closing = TRUE; + + dispatch = socketContext->Socket->Dispatch; + + IoReuseIrp(SocketOpContext->Irp, STATUS_UNSUCCESSFUL); + IoSetCompletionRoutine(SocketOpContext->Irp, + WskSampleCloseIrpCompletionRoutine, + SocketOpContext, TRUE, TRUE, TRUE); + + DoTraceMessage(TRCINFO,"OpClose: %p %p", socketContext,SocketOpContext); + + // No need to check the return status here. The IRP completion + // routine will take action based on the completion status. + dispatch->WskCloseSocket( + socketContext->Socket, + SocketOpContext->Irp); + } +} + +// IRP completion routine for WskCloseSocket requests +_Use_decl_annotations_ +NTSTATUS +WskSampleCloseIrpCompletionRoutine( + PDEVICE_OBJECT Reserved, + PIRP Irp, + PVOID Context + ) +{ + PWSKSAMPLE_SOCKET_OP_CONTEXT socketOpContext; + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + UNREFERENCED_PARAMETER(Reserved); + UNREFERENCED_PARAMETER(Irp); + + _Analysis_assume_(Context != NULL); + + socketOpContext = (PWSKSAMPLE_SOCKET_OP_CONTEXT)Context; + socketContext = socketOpContext->SocketContext; + + // WskCloseSocket can never fail. + ASSERT(NT_SUCCESS(Irp->IoStatus.Status)); + + DoTraceMessage(TRCINFO, "CloseIrpCompletionRoutine: %p %p 0x%lx", + socketContext, socketOpContext, Irp->IoStatus.Status); + + // Enqueue an operation to free the socket context. Since the + // completion of WskCloseSocket guarantees that there are no + // outstanding requests or callbacks on the socket, we can + // safely free the socket context. However, we still can NOT + // free the socket context directly here since there may still be + // queued operations not yet issued over the socket. Thus, we need + // to queue this "free" operation behind any other queued + // operations (which will not be issued over the socket since the + // socket context is marked as "closing"). Note that NO new + // operations can be queued behind the "free" operation for a + // given socket since we enqueue new operations only from the + // context of IRP completions on the socket. + WskSampleEnqueueOp(socketOpContext, WskSampleOpFree); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +// Operation handler for freeing the context block for a closed socket +VOID +WskSampleOpFree( + _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext + ) +{ + PWSKSAMPLE_SOCKET_CONTEXT socketContext; + + PAGED_CODE(); + + socketContext = SocketOpContext->SocketContext; + + DoTraceMessage(TRCINFO, "OpFree: %p %p", socketContext, SocketOpContext); + + ASSERT(socketContext->Closing || socketContext->StopListening); + WskSampleFreeSocketContext(socketContext); +} + diff --git a/network/wsk/echosrv/wsksmple.rc b/network/wsk/echosrv/wsksmple.rc new file mode 100644 index 00000000..572afe1c --- /dev/null +++ b/network/wsk/echosrv/wsksmple.rc @@ -0,0 +1,11 @@ +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "WSK Sample TCP Echo Server" +#define VER_INTERNALNAME_STR "wsksample.sys" +#define VER_ORIGINALFILENAME_STR "wsksample.sys" + +#include "common.ver" + diff --git a/nfp/net/ReadMe.md b/nfp/net/ReadMe.md new file mode 100644 index 00000000..aeb2c630 --- /dev/null +++ b/nfp/net/ReadMe.md @@ -0,0 +1,11 @@ +Near-Field Proximity Sample Driver (UMDF Version 1) +=================================================== + +This sample demonstrates how to use User-Mode Driver Framework (UMDF) version 1 to write a near-field proximity driver. + +Typically, a near-field proximity driver would use near-field technologies such as Near Field Communication (NFC), TransferJet, or Bump. However, this sample uses a TCP/IPv6 network connection and a static configuration between two machines to simulate near-field interaction. + +Related technologies +-------------------- +[User-Mode Driver Framework](http://msdn.microsoft.com/en-us/library/windows/hardware/ff560456) + diff --git a/nfp/net/driver/Connection.cpp b/nfp/net/driver/Connection.cpp new file mode 100644 index 00000000..1aed9008 --- /dev/null +++ b/nfp/net/driver/Connection.cpp @@ -0,0 +1,296 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Author: + + Travis Martin (TravM) 06-24-2010 + +--*/ +#include "internal.h" + +#include "Connection.tmh" + +HRESULT SetSocketIpv6Only(_In_ SOCKET socket, _In_ BOOL Ipv6Only); + +HRESULT SynchronousReadSocket(_In_ SOCKET Socket, _In_reads_bytes_(cbBuffer) PVOID pBuffer, _In_ DWORD cbBuffer) +{ + HRESULT hr = S_OK; + DWORD dwIgnore; + OVERLAPPED Overlapped; + ZeroMemory(&Overlapped, sizeof(Overlapped)); + if (!ReadFile((HANDLE)Socket, pBuffer, cbBuffer, &dwIgnore, &Overlapped)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING)) + { + if (!GetOverlappedResult((HANDLE)Socket, &Overlapped, &dwIgnore, TRUE)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + else + { + hr = S_OK; + } + } + } + return hr; +} + +//CConnection +// static +HRESULT CConnection::Create(_In_ IConnectionCallback* pCallback, _Outptr_ CConnection** ppConnection) +{ + CConnection* pConnection; + pConnection = new CConnection(pCallback); + HRESULT hr = (pConnection != NULL ? S_OK : E_OUTOFMEMORY); + if (SUCCEEDED(hr)) + { + *ppConnection = pConnection; + } + + return hr; +} + +void CConnection::Terminate() +{ + MethodEntry("void"); + + // Only want to terminate once + STATE PriorState = (STATE)(InterlockedExchange((long*)&_State, (long)TERMINATED)); + if (PriorState != TERMINATED) + { + // Graceful shutdown + shutdown(_Socket, SD_SEND); + + // Don't wait for threadpool callbacks when this thread is actually the threadpool callback + if (_ThreadpoolThreadId != GetCurrentThreadId()) + { + // Let the ReceiveThreadProc gracefully shutdown + WaitForThreadpoolWorkCallbacks(_ThreadpoolWork, false); + } + + SOCKET Socket = (SOCKET)InterlockedExchangePointer((PVOID*)&_Socket, (PVOID)INVALID_SOCKET); + if (Socket != INVALID_SOCKET) + { + closesocket(Socket); + } + } + + MethodReturnVoid(); +} + +/* 9C7D2C68-5AD8-4A14-BE20-F8741D60D100 */ +const GUID MAGIC_PACKET = + {0x9C7D2C68, 0x5AD8, 0x4A14, {0xBE, 0x20, 0xF8, 0x74, 0x1D, 0x60, 0xD1, 0x00}}; + +HRESULT CConnection::InitializeAsClient(_In_ BEGIN_PROXIMITY_ARGS* pArgs) +{ + pArgs->szName[MAX_PATH-1] = L'\0'; + + MethodEntry("pArgs->szName = '%S'", + pArgs->szName); + + // Open a TCP/IP Socket to the remote Network NearFieldProximity device + HRESULT hr = S_OK; + + _Socket = socket(AF_INET6, SOCK_STREAM, 0); + if (_Socket == INVALID_SOCKET) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + + if (SUCCEEDED(hr)) + { + hr = SetSocketIpv6Only(_Socket, FALSE); + } + + if (SUCCEEDED(hr)) + { + SOCKADDR_STORAGE LocalAddress = {}; + SOCKADDR_STORAGE RemoteAddress = {}; + DWORD cbLocalAddress = sizeof(LocalAddress); + DWORD cbRemoteAddress = sizeof(RemoteAddress); + timeval Timeout = {8, 0}; + if (!WSAConnectByName(_Socket, pArgs->szName, L"9299", + &cbLocalAddress, (SOCKADDR*)&LocalAddress, + &cbRemoteAddress, (SOCKADDR*)&RemoteAddress, &Timeout, NULL)) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + TraceErrorHR(hr, L"WSAConnectByName FAILED"); + } + } + + if (SUCCEEDED(hr)) + { + if (setsockopt(_Socket, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) == SOCKET_ERROR) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + } + + if (SUCCEEDED(hr)) + { + // Send the Magic Packet + if (send(_Socket, (char*)&MAGIC_PACKET, sizeof(MAGIC_PACKET), 0) == SOCKET_ERROR) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + + if (SUCCEEDED(hr)) + { + GUID MagicPacket = {}; + hr = SynchronousReadSocket(_Socket, &MagicPacket, sizeof(MagicPacket)); + if (SUCCEEDED(hr)) + { + if (memcmp(&MagicPacket, &MAGIC_PACKET, sizeof(MAGIC_PACKET)) != 0) + { + hr = E_FAIL; + } + } + } + + if (SUCCEEDED(hr)) + { + // This doesn't take the socket, because we're the client and already + // have the socket in _Socket. + hr = FinalizeEstablish(INVALID_SOCKET); + } + } + + + if (FAILED(hr)) + { + if (_Socket != INVALID_SOCKET) + { + // Abortive shutdown of the socket + closesocket(_Socket); + _Socket = INVALID_SOCKET; + } + } + + MethodReturnHR(hr); +} + +void CConnection::ValidateAccept(_In_ SOCKET Socket, _In_ GUID* pMagicPacket) +{ + MethodEntry("..."); + + TraceASSERT(Socket != INVALID_SOCKET); + + HRESULT hr = S_OK; + if (memcmp(pMagicPacket, &MAGIC_PACKET, sizeof(MAGIC_PACKET)) != 0) + { + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + { + // Send the MAGIC_PACKET + if (send(Socket, (char*)&MAGIC_PACKET, sizeof(MAGIC_PACKET), 0) == SOCKET_ERROR) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + } + + if (SUCCEEDED(hr)) + { + hr = FinalizeEstablish(Socket); + } + + if (FAILED(hr)) + { + // Abortive shutdown of the socket + closesocket(Socket); + } + + MethodReturnVoid(); +} + +HRESULT CConnection::FinalizeEstablish(_In_ SOCKET Socket) +{ + MethodEntry("..."); + + HRESULT hr = S_OK; + STATE PriorState = (STATE)(InterlockedCompareExchange((long*)&_State, (long)ESTABLISHED, (long)INITIAL)); + if (PriorState != INITIAL) + { + // Already established (or terminated), drop this + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); + } + + if (SUCCEEDED(hr)) + { + // Init Threadpool work item for the receieve thread proc. + _ThreadpoolWork = CreateThreadpoolWork(s_ReceiveThreadProc, this, NULL); + if (_ThreadpoolWork == NULL) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + else + { + if (Socket != INVALID_SOCKET) + { + // Take ownership of the socket + _Socket = Socket; + } + SubmitThreadpoolWork(_ThreadpoolWork); + + _pCallback->ConnectionEstablished(this); + } + } + + MethodReturnHR(hr); +} + +BOOL CConnection::ReceiveThreadProc() +{ + MethodEntry("void"); + + MESSAGE* pMessage = new MESSAGE(); + if (pMessage == NULL) + { + Terminate(); + } + else + { + while (_Socket != INVALID_SOCKET) + { + if (recv(_Socket, (char*)pMessage, sizeof(*pMessage), MSG_WAITALL) == sizeof(*pMessage)) + { + _pCallback->HandleReceivedMessage(pMessage); + } + else + { + Terminate(); + break; + } + } + delete pMessage; + } + + // The connection is now terminated + BOOL fConnectionDeleted = _pCallback->ConnectionTerminated(this); + + MethodReturnBool(fConnectionDeleted); +} + +HRESULT CConnection::TransmitMessage(_In_ MESSAGE* pMessage) +{ + HRESULT hr = S_OK; + if (_Socket == INVALID_SOCKET) + { + hr = HRESULT_FROM_WIN32(WSAENOTSOCK); + } + else + { + if (send(_Socket, (char*)pMessage, sizeof(*pMessage), 0) == SOCKET_ERROR) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + Terminate(); + } + } + + return hr; +} + diff --git a/nfp/net/driver/FileContext.cpp b/nfp/net/driver/FileContext.cpp new file mode 100644 index 00000000..193c637a --- /dev/null +++ b/nfp/net/driver/FileContext.cpp @@ -0,0 +1,713 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + filecontext.cpp + +Abstract: + + This file implements the class for context associated with the file object + +Environment: + + user mode only + +Revision History: + +--*/ +#include "internal.h" + +#include "FileContext.tmh" + +CFileContext::~CFileContext() +{ + MethodEntry("void"); + + while (!IsListEmpty(&m_SubscribedMessageQueue)) + { + delete CMyPayload::FromListEntry(RemoveHeadList(&m_SubscribedMessageQueue)); + } + + if (m_pszType != NULL) + { + delete [] m_pszType; + m_pszType = NULL; + } + + if (m_pConnection != NULL) + { + delete m_pConnection; + m_pConnection = NULL; + } + + m_pWdfFile = NULL; + + EnterCriticalSection(&m_RoleLock); + CompleteRequest(E_ABORT, 0, true); + LeaveCriticalSection(&m_RoleLock); + + DeleteCriticalSection(&m_RoleLock); + + MethodReturnVoid(); +} + +HRESULT +CFileContext::Disable() +{ + MethodEntry("void"); + + EnterCriticalSection(&m_RoleLock); + + HRESULT hr = S_OK; + if ((m_Role == ROLE_UNDEFINED) || (m_Role == ROLE_PROXIMITY)) + { + // Only Pub/Sub handles can be disabled + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + } + + if (!m_fEnabled) + { + // Already disabled + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + } + + if (SUCCEEDED(hr)) + { + m_fEnabled = FALSE; + + CompleteRequest(HRESULT_FROM_NT(STATUS_CANCELLED), 0, true); + + // Purge all already received payloads + while (!IsListEmpty(&m_SubscribedMessageQueue)) + { + delete CMyPayload::FromListEntry(RemoveHeadList(&m_SubscribedMessageQueue)); + } + } + + LeaveCriticalSection(&m_RoleLock); + + MethodReturnHR(hr); +} + +HRESULT +CFileContext::Enable() +{ + MethodEntry("void"); + + EnterCriticalSection(&m_RoleLock); + + HRESULT hr = S_OK; + if ((m_Role == ROLE_UNDEFINED) || (m_Role == ROLE_PROXIMITY)) + { + // Only Pub/Sub handles can be enabled + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + } + + if (m_fEnabled) + { + // Already enabled + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + } + + if (SUCCEEDED(hr)) + { + m_fEnabled = TRUE; + } + + LeaveCriticalSection(&m_RoleLock); + + MethodReturnHR(hr); +} + +HRESULT +CFileContext::SetType(_In_ PCWSTR pszType) +{ + MethodEntry("pszType = '%S'", pszType); + + HRESULT hr = S_OK; + WUDF_SAMPLE_DRIVER_ASSERT(m_pszType == NULL); + + SIZE_T cchStr = wcslen(pszType) + 1; + if ((cchStr > MinCchType) && (cchStr < MaxCchType)) + { + m_pszType = new WCHAR[cchStr]; + if (m_pszType != NULL) + { + hr = StringCchCopy(m_pszType, cchStr, pszType); + } + else + { + hr = E_OUTOFMEMORY; + } + } + else + { + hr = E_INVALIDARG; + } + + MethodReturnHR(hr); +} + +#define STATUS_BUFFER_TOO_SMALL 0xC0000023L +#define STATUS_BUFFER_OVERFLOW 0x80000005L + + +bool +CFileContext::CompleteOneGetNextSubscribedMessage( + _In_ DWORD cbPayload, + _In_reads_bytes_opt_(cbPayload) PBYTE pbPayload + ) +/* + * m_RoleLock must already be acquired + */ +{ + MethodEntry("cbPayload = 0x%d", + (DWORD)cbPayload); + + WUDF_SAMPLE_DRIVER_ASSERT(m_pWdfRequest != NULL); + + bool fDelivered = false; + IWDFMemory* pWdfOutputMemory; + m_pWdfRequest->GetOutputMemory(&pWdfOutputMemory); + if (pWdfOutputMemory != NULL) + { + SIZE_T cbOutputBuffer = 0; + // Set the first 4 bytes as the size of the payload as a hint for future + // subscriptions. + HRESULT hr = pWdfOutputMemory->CopyFromBuffer(0, &cbPayload, 4); + if (SUCCEEDED(hr)) + { + cbOutputBuffer = 4; + if (pbPayload != NULL) + { + if (pWdfOutputMemory->GetSize() < (cbPayload + 4)) + { + // We are unable to copy the payload into the output memory, + // Returning this signals to the client to send a bigger buffer + hr = HRESULT_FROM_NT(STATUS_BUFFER_OVERFLOW); + } + else + { + hr = pWdfOutputMemory->CopyFromBuffer(4, pbPayload, cbPayload); + } + } + + if (SUCCEEDED(hr)) + { + fDelivered = true; + cbOutputBuffer += cbPayload; + if (m_dwQueueSize > 0) + { + m_dwQueueSize--; + } + } + } + pWdfOutputMemory->Release(); + + if (!CompleteRequest(hr, cbOutputBuffer, true)) + { + fDelivered = false; + } + } + + MethodReturnBool(fDelivered); +} + +HRESULT +CFileContext::GetNextSubscribedMessage(_In_ IRequestCallbackCancel* pCallbackCancel, _In_ IWDFIoRequest* pWdfRequest) +{ + MethodEntry("..."); + + EnterCriticalSection(&m_RoleLock); + + HRESULT hr = S_OK; + if (m_pWdfRequest != NULL) + { + // Only one pended request at a time allowed + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + } + + if (!m_fEnabled) + { + // The handle is disabled + hr = HRESULT_FROM_NT(HRESULT_FROM_NT(STATUS_CANCELLED)); + } + + if (SUCCEEDED(hr)) + { + IWDFMemory* pWdfInputMemory; + pWdfRequest->GetInputMemory(&pWdfInputMemory); + SIZE_T cbInput; + if (pWdfInputMemory->GetDataBuffer(&cbInput) == NULL) + { + if (m_Role == ROLE_SUBSCRIPTION) + { + m_pWdfRequest = pWdfRequest; + m_pWdfRequest->MarkCancelable(pCallbackCancel); // MarkCancelable can run the OnCancel routine in this thread + + if ((m_pWdfRequest != NULL) && !IsListEmpty(&m_SubscribedMessageQueue)) + { + CMyPayload* pMyPayload = + CMyPayload::FromListEntry(m_SubscribedMessageQueue.Flink); + + if (CompleteOneGetNextSubscribedMessage(pMyPayload->GetSize(), + pMyPayload->GetPayload())) + { + RemoveHeadList(&m_SubscribedMessageQueue); + delete pMyPayload; + } + } + } + else if (m_Role == ROLE_ARRIVEDSUBSCRIPTION) + { + m_pWdfRequest = pWdfRequest; + m_pWdfRequest->MarkCancelable(pCallbackCancel); // MarkCancelable can run the OnCancel routine in this thread + if (m_pWdfRequest != NULL) + { + CompleteOneArrivalEvent(); + } + } + else if (m_Role == ROLE_DEPARTEDSUBSCRIPTION) + { + m_pWdfRequest = pWdfRequest; + m_pWdfRequest->MarkCancelable(pCallbackCancel); // MarkCancelable can run the OnCancel routine in this thread + if (m_pWdfRequest != NULL) + { + CompleteOneRemovalEvent(); + } + } + else + { + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + } + } + else + { + hr = E_INVALIDARG; + } + pWdfInputMemory->Release(); + } + + LeaveCriticalSection(&m_RoleLock); + + MethodReturnHR(hr); +} + +HRESULT +CFileContext::SetPayload(_In_ IWDFIoRequest* pWdfRequest) +{ + MethodEntry("..."); + + EnterCriticalSection(&m_RoleLock); + + HRESULT hr = S_OK; + if ((m_Role != ROLE_PUBLICATION) || (m_MyPayload.GetPayload() != NULL)) + { + // SetPayload can only be called once per handle + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + } + + if (SUCCEEDED(hr)) + { + IWDFMemory* pWdfOutputMemory; + pWdfRequest->GetOutputMemory(&pWdfOutputMemory); + SIZE_T cbOutput; + if (pWdfOutputMemory->GetDataBuffer(&cbOutput) == NULL) + { + IWDFMemory* pWdfMemory; + pWdfRequest->GetInputMemory(&pWdfMemory); + if (pWdfMemory != NULL) + { + SIZE_T cbPayload = pWdfMemory->GetSize(); + if ((cbPayload > 0) && (cbPayload <= MaxCbPayload)) + { + hr = m_MyPayload.Initialize((DWORD)cbPayload, (PBYTE)pWdfMemory->GetDataBuffer(NULL)); + } + else + { + hr = HRESULT_FROM_NT(STATUS_INVALID_BUFFER_SIZE); + } + + pWdfMemory->Release(); + } + else + { + hr = E_INVALIDARG; + } + pWdfOutputMemory->Release(); + } + else + { + hr = E_INVALIDARG; + } + } + + LeaveCriticalSection(&m_RoleLock); + + MethodReturnHR(hr); +} + +HRESULT +CFileContext::GetNextTransmittedMessage(_In_ IRequestCallbackCancel* pCallbackCancel, _In_ IWDFIoRequest* pWdfRequest) +{ + MethodEntry("..."); + + EnterCriticalSection(&m_RoleLock); + + HRESULT hr = S_OK; + if ((m_Role != ROLE_PUBLICATION) || (m_pWdfRequest != NULL)) + { + // Only one pended request at a time allowed + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + } + + if (!m_fEnabled) + { + // The handle is disabled + hr = HRESULT_FROM_NT(HRESULT_FROM_NT(STATUS_CANCELLED)); + } + + if (SUCCEEDED(hr)) + { + IWDFMemory* pWdfInputMemory; + pWdfRequest->GetInputMemory(&pWdfInputMemory); + SIZE_T cbInput; + if (pWdfInputMemory->GetDataBuffer(&cbInput) == NULL) + { + IWDFMemory* pWdfOutputMemory; + pWdfRequest->GetOutputMemory(&pWdfOutputMemory); + SIZE_T cbOutput; + if (pWdfOutputMemory->GetDataBuffer(&cbOutput) == NULL) + { + m_pWdfRequest = pWdfRequest; + + if (m_cCompleteReady > 0) + { + if (CompleteRequest(S_OK, 0, false)) + { + m_cCompleteReady--; + } + } + } + else + { + hr = E_INVALIDARG; + } + pWdfOutputMemory->Release(); + } + else + { + hr = E_INVALIDARG; + } + pWdfInputMemory->Release(); + } + + if (SUCCEEDED(hr) && (m_pWdfRequest != NULL)) + { + m_pWdfRequest->MarkCancelable(pCallbackCancel); + } + + LeaveCriticalSection(&m_RoleLock); + + MethodReturnHR(hr); +} + +HRESULT +CFileContext::BeginProximity( + _In_ IWDFIoRequest* pWdfRequest, + _In_ IConnectionCallback* pCallback + ) +{ + MethodEntry("..."); + + EnterCriticalSection(&m_RoleLock); + + HRESULT hr = S_OK; + if (m_Role != ROLE_UNDEFINED) + { + // BeginProximity can only be called once per handle + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + } + else + { + m_Role = ROLE_PROXIMITY; + } + + LeaveCriticalSection(&m_RoleLock); + + if (SUCCEEDED(hr)) + { + IWDFMemory* pWdfMemory; + pWdfRequest->GetInputMemory(&pWdfMemory); + if (pWdfMemory != NULL) + { + if (pWdfMemory->GetSize() == sizeof(BEGIN_PROXIMITY_ARGS)) + { + hr = CConnection::Create(pCallback, &m_pConnection); + if (SUCCEEDED(hr)) + { + BEGIN_PROXIMITY_ARGS* pArgs = + (BEGIN_PROXIMITY_ARGS*)pWdfMemory->GetDataBuffer(NULL); + hr = m_pConnection->InitializeAsClient(pArgs); + } + } + else + { + hr = E_INVALIDARG; + } + + pWdfMemory->Release(); + } + else + { + hr = E_INVALIDARG; + } + } + + + if (FAILED(hr)) + { + m_pConnection = NULL; + } + + MethodReturnHR(hr); +} + +VOID +CFileContext::HandleArrivalEvent() +{ + WUDF_SAMPLE_DRIVER_ASSERT(m_Role == ROLE_ARRIVEDSUBSCRIPTION); + + EnterCriticalSection(&m_RoleLock); + + if (m_fEnabled) + { + m_cCompleteReady++; + CompleteOneArrivalEvent(); + } + + LeaveCriticalSection(&m_RoleLock); +} + +VOID +CFileContext::HandleRemovalEvent() +{ + WUDF_SAMPLE_DRIVER_ASSERT(m_Role == ROLE_DEPARTEDSUBSCRIPTION); + + EnterCriticalSection(&m_RoleLock); + + if (m_fEnabled) + { + m_cCompleteReady++; + CompleteOneRemovalEvent(); + } + + LeaveCriticalSection(&m_RoleLock); +} + +VOID +CFileContext::CompleteOneArrivalEvent() +{ + MethodEntry("void"); + + WUDF_SAMPLE_DRIVER_ASSERT(m_Role == ROLE_ARRIVEDSUBSCRIPTION); + + EnterCriticalSection(&m_RoleLock); + + if (m_cCompleteReady > 0) + { + if (m_pWdfRequest != NULL) + { + // Arrival payload should either be a DWORD = 1 or 0 + // 1 == Device capable of bi-directional communication + // 0 == Device is a dumb tag + DWORD ArrivalFlags = 0x1; + if (CompleteOneGetNextSubscribedMessage(sizeof(ArrivalFlags), (PBYTE)&ArrivalFlags)) + { + m_cCompleteReady--; + } + } + } + + LeaveCriticalSection(&m_RoleLock); + + MethodReturnVoid(); +} + +VOID +CFileContext::CompleteOneRemovalEvent() +{ + MethodEntry("void"); + + WUDF_SAMPLE_DRIVER_ASSERT(m_Role == ROLE_DEPARTEDSUBSCRIPTION); + + EnterCriticalSection(&m_RoleLock); + + if (m_cCompleteReady > 0) + { + if (m_pWdfRequest != NULL) + { + // Removal payload should be a single zeroed DWORD + DWORD RemovalFlags = 0x0; + if (CompleteOneGetNextSubscribedMessage(sizeof(RemovalFlags), (PBYTE)&RemovalFlags)) + { + m_cCompleteReady--; + } + } + } + + LeaveCriticalSection(&m_RoleLock); + + MethodReturnVoid(); +} + +void +CFileContext::HandleReceivedPublication( + _In_ PCWSTR pszType, + _In_ DWORD cbPayload, + _In_reads_bytes_(cbPayload) PBYTE pbPayload + ) +{ + MethodEntry("..."); + + WUDF_SAMPLE_DRIVER_ASSERT(m_Role == ROLE_SUBSCRIPTION); + + EnterCriticalSection(&m_RoleLock); + + if (m_fEnabled) + { + bool fSubscriptionMatches = false; + BYTE* pbNewPayload = NULL; + + if ((CompareStringOrdinal(m_pszType, -1, WINDOWSMIME_PROTOCOL, -1, FALSE) == CSTR_EQUAL) && + (wcslen(pszType) > WINDOWSMIME_PROTOCOL_CHARS) && + (CompareStringOrdinal(pszType, WINDOWSMIME_PROTOCOL_CHARS, + WINDOWSMIME_PROTOCOL, -1, FALSE) == CSTR_EQUAL)) + { + // If this is a WindowsMime message, and the subscription is for the general WINDOWSMIME_PROTOCOL type, + // the Mime type needs to be added to the message payload. + CHAR szMimeType[MaxCchMimeType + 1] = {}; + // Copy the mime type and convert from wide to multibyte chars + if (SUCCEEDED(StringCchPrintfA(szMimeType, ARRAYSIZE(szMimeType), "%S", pszType + WINDOWSMIME_PROTOCOL_CHARS + 1))) + { + pbNewPayload = new BYTE[cbPayload + MaxCchMimeType]; + if (pbNewPayload != NULL) + { + TraceInfo("Received Mime message of type = '%s'", szMimeType); + CopyMemory(pbNewPayload, szMimeType, MaxCchMimeType); + CopyMemory(pbNewPayload + MaxCchMimeType, pbPayload, cbPayload); + cbPayload += MaxCchMimeType; + pbPayload = pbNewPayload; + + fSubscriptionMatches = true; + } + } + } + else if (CompareStringOrdinal(pszType, -1, m_pszType, -1, FALSE) == CSTR_EQUAL) + { + fSubscriptionMatches = true; + } + + if (fSubscriptionMatches) + { + bool fDelivered = false; + + if (m_pWdfRequest != NULL) + { + fDelivered = CompleteOneGetNextSubscribedMessage(cbPayload, pbPayload); + } + + if ((!fDelivered) && (m_dwQueueSize < MAX_MESSAGE_QUEUE_SIZE)) + { + // Add message to the client delivery queue + + CMyPayload* pMyPayload = new CMyPayload(); + if (pMyPayload) + { + if (SUCCEEDED(pMyPayload->Initialize(cbPayload, pbPayload))) + { + InsertTailList(&m_SubscribedMessageQueue, pMyPayload->GetListEntry()); + m_dwQueueSize++; + } + else + { + delete pMyPayload; + } + } + } + } + + delete [] pbNewPayload; + } + else + { + TraceErrorHR(HRESULT_FROM_NT(STATUS_CANCELLED), "Subscription Disabled!"); + } + + LeaveCriticalSection(&m_RoleLock); + + MethodReturnVoid(); +} + +void +CFileContext::HandleMessageTransmitted() +{ + MethodEntry("void"); + + WUDF_SAMPLE_DRIVER_ASSERT(m_Role == ROLE_PUBLICATION); + + EnterCriticalSection(&m_RoleLock); + + if (!CompleteRequest(S_OK, 0, true)) + { + m_cCompleteReady++; + } + + LeaveCriticalSection(&m_RoleLock); + + MethodReturnVoid(); +} + +void +CFileContext::OnCancel() +{ + EnterCriticalSection(&m_RoleLock); + + CompleteRequest(E_ABORT, 0, false); + + LeaveCriticalSection(&m_RoleLock); +} + +bool +CFileContext::CompleteRequest(_In_ HRESULT hr, _In_ SIZE_T cbSize, _In_ bool fIsCancelable) +/* + * m_RoleLock must already be acquired + */ +{ + MethodEntry("hr = %!HRESULT!, cbSize = %d, fIsCancelable = %!bool!", + hr, (DWORD)cbSize, fIsCancelable); + + bool fCompleted = false; + if (m_pWdfRequest != NULL) + { + bool fCompleteRequest = true; + if (fIsCancelable) + { + if (m_pWdfRequest->UnmarkCancelable() == HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)) + { + fCompleteRequest = false; + } + } + + if (fCompleteRequest) + { + m_pWdfRequest->CompleteWithInformation(hr, cbSize); + m_pWdfRequest = NULL; + fCompleted = true; + } + } + + MethodReturnBool(fCompleted); +} diff --git a/nfp/net/driver/FileContext.h b/nfp/net/driver/FileContext.h new file mode 100644 index 00000000..0247636e --- /dev/null +++ b/nfp/net/driver/FileContext.h @@ -0,0 +1,340 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + filecontext.h + +Abstract: + + This header file defines the structure type for context associated with the file object + +Environment: + + user mode only + +Revision History: + +--*/ +#pragma once + +class CMyPayload +{ +public: + CMyPayload() + { + m_pbPayload = NULL; + m_cbPayload = 0; + + InitializeListHead(&m_ListEntry); + } + ~CMyPayload() + { + if (m_pbPayload != NULL) + { + delete [] m_pbPayload; + m_pbPayload = NULL; + } + } + + STDMETHOD(Initialize)( + _In_ DWORD cbPayload, + _In_reads_bytes_(cbPayload) PBYTE pbPayload + ) + { + HRESULT hr = S_OK; + m_pbPayload = new BYTE[cbPayload]; + if (m_pbPayload != NULL) + { + m_cbPayload = cbPayload; + CopyMemory(m_pbPayload, pbPayload, cbPayload); + } + else + { + hr = E_OUTOFMEMORY; + } + + return hr; + } + + PBYTE GetPayload() + { + return m_pbPayload; + } + DWORD GetSize() + { + return m_cbPayload; + } + + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + static CMyPayload* FromListEntry(PLIST_ENTRY pEntry) + { + return (CMyPayload*) CONTAINING_RECORD(pEntry, CMyPayload, m_ListEntry); + } + +private: + PBYTE m_pbPayload; + DWORD m_cbPayload; + + LIST_ENTRY m_ListEntry; +}; + +/* + * Use this to refactor to only keep one copy of received messages + * +class CMyPayloadItem +{ +public: + CMyPayloadItem(_In_ CMyPayload* pPayload) + { + m_spPayload = pPayload; + InitializeListHead(&m_ListEntry); + } + ~CMyPayloadItem() + { + } + + PBYTE GetPayload() + { + return m_spPayload->GetPayload(); + } + DWORD GetSize() + { + return m_spPayload->GetSize(); + } + + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + static CMyPayloadItem* FromListEntry(PLIST_ENTRY pEntry) + { + return (CMyPayloadItem*) CONTAINING_RECORD(pEntry, CMyPayloadItem, m_ListEntry); + } + +private: + CComPtr m_spPayload; + + LIST_ENTRY m_ListEntry; +}; +*/ + +class CFileContext +{ +public: + + CFileContext() : + m_Role(ROLE_UNDEFINED), + m_pszType(NULL), + m_fEnabled(TRUE), + m_dwQueueSize(0), + m_cCompleteReady(0), + m_pConnection(NULL), + m_pWdfRequest(NULL) + { + InitializeListHead(&m_SubscribedMessageQueue); + + InitializeListHead(&m_ListEntry); + + InitializeCriticalSection(&m_RoleLock); + } + + ~CFileContext(); + + HRESULT + Disable(); + + HRESULT + Enable(); + + HRESULT + SetType(_In_ PCWSTR pszType); + + HRESULT + GetNextSubscribedMessage(_In_ IRequestCallbackCancel* pCallbackCancel, _In_ IWDFIoRequest* pWdfRequest); + + HRESULT + SetPayload(_In_ IWDFIoRequest* pWdfRequest); + + HRESULT + GetNextTransmittedMessage(_In_ IRequestCallbackCancel* pCallbackCancel, _In_ IWDFIoRequest* pWdfRequest); + + HRESULT + BeginProximity( + _In_ IWDFIoRequest* pWdfRequest, + _In_ IConnectionCallback* pCallback + ); + + VOID + HandleArrivalEvent(); + VOID + HandleRemovalEvent(); + + void + HandleReceivedPublication( + _In_ PCWSTR pszType, + _In_ DWORD cbPayload, + _In_reads_bytes_(cbPayload) PBYTE pbPayload + ); + + void + HandleMessageTransmitted(); + + void + OnCancel(); + + void + SetRoleSubcription() + { + m_Role = ROLE_SUBSCRIPTION; + } + + void + SetRolePublication() + { + m_Role = ROLE_PUBLICATION; + } + + BOOL + SetRoleArrivedSubcription() + { + if (m_Role == ROLE_SUBSCRIPTION) + { + m_Role = ROLE_ARRIVEDSUBSCRIPTION; + return TRUE; + } + return FALSE; + } + + BOOL + SetRoleDepartedSubcription() + { + if (m_Role == ROLE_SUBSCRIPTION) + { + m_Role = ROLE_DEPARTEDSUBSCRIPTION; + return TRUE; + } + return FALSE; + } + + BOOL + IsNormalSubscription() + { + return (m_Role == ROLE_SUBSCRIPTION); + } + BOOL + IsArrivedSubscription() + { + return (m_Role == ROLE_ARRIVEDSUBSCRIPTION); + } + BOOL + IsDepartedSubscription() + { + return (m_Role == ROLE_DEPARTEDSUBSCRIPTION); + } + BOOL + IsSubscription() + { + return (m_Role == ROLE_SUBSCRIPTION); + } + BOOL + IsPublication() + { + return (m_Role == ROLE_PUBLICATION); + } + + PCWSTR + GetType() + { + return m_pszType; + } + + DWORD + GetSize() + { + return m_MyPayload.GetSize(); + } + + PBYTE + GetPayload() + { + return m_MyPayload.GetPayload(); + } + + BOOL + IsEnabled() + { + return m_fEnabled; + } + + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + static CFileContext* FromListEntry(PLIST_ENTRY pEntry) + { + return (CFileContext*) CONTAINING_RECORD(pEntry, CFileContext, m_ListEntry); + } + +private: + + VOID + CompleteOneArrivalEvent(); + VOID + CompleteOneRemovalEvent(); + + bool + CompleteOneGetNextSubscribedMessage( + _In_ DWORD cbPayload, + _In_reads_bytes_opt_(cbPayload) PBYTE pbPayload + ); + + bool + CompleteRequest( + _In_ HRESULT hr, + _In_ SIZE_T cbSize, + _In_ bool fIsCancelable + ); + +private: + + enum ROLE + { + ROLE_UNDEFINED, + ROLE_SUBSCRIPTION, + ROLE_ARRIVEDSUBSCRIPTION, + ROLE_DEPARTEDSUBSCRIPTION, + ROLE_PUBLICATION, + ROLE_PROXIMITY + }; + + ROLE m_Role; + + PWSTR m_pszType; + + BOOL m_fEnabled; + + DWORD m_dwQueueSize; + + // Queue of received messages + LIST_ENTRY m_SubscribedMessageQueue; // Unique to ROLE_SUBSCRIPTION + + CMyPayload m_MyPayload; // Unique to ROLE_PUBLICATION + SIZE_T m_cCompleteReady; // Unique to ROLE_PUBLICATION, ROLE_ARRIVEDSUBSCRIPTION, and ROLE_DEPARTEDSUBSCRIPTION + + CConnection* m_pConnection; // Unique to ROLE_PROXIMITY + + // Pended "Get Next" Request. + IWDFIoRequest* m_pWdfRequest; + + // The Fx File object this CFileObject is a companion to + IWDFFile* m_pWdfFile; + + CRITICAL_SECTION m_RoleLock; + + LIST_ENTRY m_ListEntry; +}; diff --git a/nfp/net/driver/NetNfpProvider.vcxproj b/nfp/net/driver/NetNfpProvider.vcxproj new file mode 100644 index 00000000..da21fa09 --- /dev/null +++ b/nfp/net/driver/NetNfpProvider.vcxproj @@ -0,0 +1,265 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {756016BF-4187-4482-A0BE-CEE1698BD129} + $(MSBuildProjectName) + 1 + Debug + Win32 + {9E5FA410-9825-4FD4-845A-FFEDEE9AF94B} + + + + Windows10 + False + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Desktop + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + WppDefs.h + + + $(InfArch) + true + true + .\$(IntDir)\NetNfpProvider.inf + + + true + true + WppDefs.h + + + + NetNfpProvider + Dynamic + + + NetNfpProvider + Dynamic + + + NetNfpProvider + Dynamic + + + NetNfpProvider + Dynamic + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ws2_32.lib;$(SDK_LIB_PATH)\mswsock.lib + exports.def + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ws2_32.lib;$(SDK_LIB_PATH)\mswsock.lib + exports.def + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ws2_32.lib;$(SDK_LIB_PATH)\mswsock.lib + exports.def + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\Ws2_32.lib;$(SDK_LIB_PATH)\mswsock.lib + exports.def + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nfp/net/driver/NetNfpProvider.vcxproj.Filters b/nfp/net/driver/NetNfpProvider.vcxproj.Filters new file mode 100644 index 00000000..cfe106d2 --- /dev/null +++ b/nfp/net/driver/NetNfpProvider.vcxproj.Filters @@ -0,0 +1,60 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {1A46FC37-1F58-4A1C-AD58-4C8F0BFF8004} + + + h;hpp;hxx;hm;inl;inc;xsd + {558A638D-8A37-44D7-BB58-296F1AEE8066} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {06C2D74D-F153-45FD-9B8F-09D1A0CD979F} + + + inf;inv;inx;mof;mc; + {D6F88F8A-6CD0-44DA-BC07-EEFF2E791D12} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/nfp/net/driver/Queue.cpp b/nfp/net/driver/Queue.cpp new file mode 100644 index 00000000..129a5439 --- /dev/null +++ b/nfp/net/driver/Queue.cpp @@ -0,0 +1,824 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + queue.cpp + +Abstract: + + This file implements the I/O queue interface and performs + the read/write/ioctl operations. + +Environment: + + user mode only + +Revision History: + +--*/ +#include "internal.h" + +#include "queue.tmh" + +CMyQueue::CMyQueue( + VOID + ) +{ + InitializeListHead(&m_SubsHead); + InitializeListHead(&m_ArrivalSubsHead); + InitializeListHead(&m_DepartureSubsHead); + InitializeListHead(&m_PubsHead); + InitializeListHead(&m_ConnectionHead); + + InitializeCriticalSection(&m_SubsLock); + InitializeCriticalSection(&m_PubsLock); + InitializeCriticalSection(&m_ConnectionLock); +} + +CMyQueue::~CMyQueue( + VOID + ) +{ + MethodEntry("void"); + + m_SocketListener.StopAccepting(); + + while (!IsListEmpty(&m_ConnectionHead)) + { + delete CConnection::FromListEntry(RemoveHeadList(&m_ConnectionHead)); + } + + DeleteCriticalSection(&m_SubsLock); + DeleteCriticalSection(&m_PubsLock); + DeleteCriticalSection(&m_ConnectionLock); +} + +// +// Initialize +// + +HRESULT +CMyQueue::Initialize( + _In_ CMyDevice * Device + ) +/*++ + +Routine Description: + + Queue Initialize helper routine. + This routine will Create a default parallel queue associated with the Fx device object + and pass the IUnknown for this queue + +Aruments: + Device object pointer + +Return Value: + + S_OK if Initialize succeeds + +--*/ +{ + MethodEntry("..."); + + CComPtr fxQueue; + + HRESULT hr; + + // + // Create the I/O Queue object. + // + { + CComPtr pUnk; + + HRESULT hrQI = this->QueryInterface(__uuidof(IUnknown),(void**)&pUnk); + + WUDF_SAMPLE_DRIVER_ASSERT(SUCCEEDED(hrQI)); + + hr = Device->GetFxDevice()->CreateIoQueue( + pUnk, + TRUE, + WdfIoQueueDispatchParallel, + TRUE, + FALSE, + &fxQueue + ); + } + + if (FAILED(hr)) + { + TraceErrorHR(hr, "Failed to initialize driver queue"); + } + + if (SUCCEEDED(hr)) + { + hr = m_SocketListener.Bind(); + if (FAILED(hr)) + { + TraceErrorHR(hr, "Failed to Bind"); + } + } + + if (SUCCEEDED(hr)) + { + hr = m_SocketListener.EnableAccepting(this); + if (FAILED(hr)) + { + TraceErrorHR(hr, "Failed to EnableAccepting"); + } + } + + if (SUCCEEDED(hr)) + { + m_FxQueue = fxQueue; + } + + MethodReturnHR(hr); +} + +HRESULT +CMyQueue::Configure( + VOID + ) +/*++ + +Routine Description: + + Queue configuration function . + It is called after queue object has been succesfully initialized. + +Aruments: + + NONE + + Return Value: + + S_OK if succeeds. + +--*/ +{ + MethodEntry("void"); + + HRESULT hr = S_OK; + + MethodReturnHR(hr); +} + + +STDMETHODIMP_(void) +CMyQueue::OnCreateFile( + _In_ IWDFIoQueue* /*pWdfQueue*/, + _In_ IWDFIoRequest* pWdfRequest, + _In_ IWDFFile* pWdfFile + ) + +/*++ + +Routine Description: + + Create callback from the framework for this default parallel queue + + The create request will create a socket connection , create a file i/o target associated + with the socket handle for this connection and store in the file object context. + +Aruments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + pWdfFile - WDF file object for this create + + Return Value: + + VOID + +--*/ +{ + MethodEntry("pWdfRequest = %p, pWdfFile = %p", + pWdfRequest, pWdfFile); + + HRESULT hr = S_OK; + + // + // Create file context for this file object + // + + CFileContext *pContext = new CFileContext(); + if (NULL == pContext) + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + TraceErrorHR(hr, "Could not create file context"); + } + + if (SUCCEEDED(hr)) + { + DWORD cchFileName = 0; + hr = pWdfFile->RetrieveFileName(NULL, &cchFileName); + if (SUCCEEDED(hr) && (cchFileName > 0) && (cchFileName <= MaxCchType)) + { + // Allocate a buffer big enough for the filename plus some extra to prevent + // overruns in the parsing below: The extra needs to be larger than the biggest + // *_CHARS value. The value 20 is overly big, but allows room to grow without + // hitting the OACR issue again. If the OACR issue is hit, just increase this + // to be larger than the biggest *_CHARS value used below in parsing. + DWORD cchFileNameBuffer = cchFileName + 20; + PWSTR pszFileNameBuffer = new WCHAR[cchFileNameBuffer]; + hr = (pszFileNameBuffer != NULL) ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) + { + ZeroMemory(pszFileNameBuffer, cchFileNameBuffer * sizeof(WCHAR)); + + hr = pWdfFile->RetrieveFileName(pszFileNameBuffer, &cchFileName); + } + + if (SUCCEEDED(hr)) + { + PCWSTR pszFileName = pszFileNameBuffer; + if (pszFileNameBuffer[0] == L'\\') + { + // If it exists, remove the inital slash + pszFileName++; + cchFileName--; + } + + TraceInfo("cchFileName = %d, pszFileName = %S", cchFileName, pszFileName); + + PCWSTR pszProtocol = NULL; + if (CompareStringOrdinal(pszFileName, PUBS_NAMESPACE_CHARS, + PUBS_NAMESPACE, PUBS_NAMESPACE_CHARS, + TRUE) == CSTR_EQUAL) + { + pContext->SetRolePublication(); + pszProtocol = pszFileName + PUBS_NAMESPACE_CHARS; + } + else if (CompareStringOrdinal(pszFileName, SUBS_NAMESPACE_CHARS, + SUBS_NAMESPACE, SUBS_NAMESPACE_CHARS, + TRUE) == CSTR_EQUAL) + { + pContext->SetRoleSubcription(); + pszProtocol = pszFileName + SUBS_NAMESPACE_CHARS; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + if (SUCCEEDED(hr)) + { + if (CompareStringOrdinal(pszProtocol, WINDOWS_PROTOCOL_CHARS, + WINDOWS_PROTOCOL, WINDOWS_PROTOCOL_CHARS, + TRUE) == CSTR_EQUAL) + { + pContext->SetType(pszProtocol); + } + else if (CompareStringOrdinal(pszProtocol, -1, + WINDOWSURI_PROTOCOL, -1, + TRUE) == CSTR_EQUAL) + { + pContext->SetType(pszProtocol); + } + else if (CompareStringOrdinal(pszProtocol, WINDOWSMIME_PROTOCOL_CHARS, + WINDOWSMIME_PROTOCOL, WINDOWSMIME_PROTOCOL_CHARS, + TRUE) == CSTR_EQUAL) + { + pContext->SetType(pszProtocol); + } + else if (CompareStringOrdinal(pszProtocol, -1, DEVICE_ARRIVED, -1, + TRUE) == CSTR_EQUAL) + { + if (!pContext->SetRoleArrivedSubcription()) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + } + else if (CompareStringOrdinal(pszProtocol, -1, DEVICE_DEPARTED, -1, + TRUE) == CSTR_EQUAL) + { + if (!pContext->SetRoleDepartedSubcription()) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + } + else if (CompareStringOrdinal(pszProtocol, PAIRING_PROTOCOL_CHARS, + PAIRING_PROTOCOL, PAIRING_PROTOCOL_CHARS, + TRUE) == CSTR_EQUAL) + { + pContext->SetType(pszProtocol); + } + else if (CompareStringOrdinal(pszProtocol, NDEF_PROTOCOL_CHARS, + NDEF_PROTOCOL, NDEF_PROTOCOL_CHARS, + TRUE) == CSTR_EQUAL) + { + PCWSTR pszType = pszProtocol + NDEF_PROTOCOL_CHARS; + + if (CompareStringOrdinal(pszType, NDEF_EMPTY_TYPE_CHARS, + NDEF_EMPTY_TYPE, NDEF_EMPTY_TYPE_CHARS, + TRUE) != CSTR_EQUAL) + { + pContext->SetType(pszProtocol); + } + else + { + hr = HRESULT_FROM_NT(STATUS_INVALID_PARAMETER); + } + } + else + { + hr = HRESULT_FROM_NT(STATUS_OBJECT_PATH_NOT_FOUND); + } + } + } + + if (pszFileNameBuffer != NULL) + { + delete [] pszFileNameBuffer; + } + } + } + + if (SUCCEEDED(hr)) + { + hr = pWdfFile->AssignContext(NULL, (void*)pContext); + if (FAILED(hr)) + { + TraceErrorHR(hr, "Unable to Assign Context to this File Object"); + } + } + + if (SUCCEEDED(hr)) + { + EnterCriticalSection(&m_SubsLock); + + if (pContext->IsNormalSubscription()) + { + // Place this CFileContext into a list of Subscriptions + InsertHeadList(&m_SubsHead, pContext->GetListEntry()); + } + else if (pContext->IsArrivedSubscription()) + { + // Place this CFileContext into a list of arrival registrations + InsertHeadList(&m_ArrivalSubsHead, pContext->GetListEntry()); + } + else if (pContext->IsDepartedSubscription()) + { + // Place this CFileContext into a list of removal registrations + InsertHeadList(&m_DepartureSubsHead, pContext->GetListEntry()); + } + + LeaveCriticalSection(&m_SubsLock); + } + + if (FAILED(hr)) + { + if (pContext != NULL) + { + delete pContext; + pContext = NULL; + } + } + + pWdfRequest->Complete(hr); + + MethodReturnVoid(); +} + +STDMETHODIMP_(void) +CMyQueue::OnCloseFile( + _In_ IWDFFile* pWdfFileObject + ) +/*++ + + Routine Description: + + This method is called when an app closes the file handle to this device. + This will free the context memory associated with this file object, close + the connection object associated with this file object and delete the file + handle i/o target object associated with this file object. + + Arguments: + + pWdfFileObject - the framework file object for which close is handled. + + Return Value: + + None + +--*/ +{ + MethodEntry("..."); + + HRESULT hr = S_OK ; + CFileContext* pContext = NULL; + hr = pWdfFileObject->RetrieveContext((void**)&pContext); + if (SUCCEEDED(hr) && (pContext != NULL)) + { + CRITICAL_SECTION* pCritSec = NULL; + LIST_ENTRY* pHead = NULL; + + if (pContext->IsNormalSubscription()) + { + pCritSec = &m_SubsLock; + pHead = &m_SubsHead; + } + else if (pContext->IsArrivedSubscription()) + { + pCritSec = &m_SubsLock; + pHead = &m_ArrivalSubsHead; + } + else if (pContext->IsDepartedSubscription()) + { + pCritSec = &m_SubsLock; + pHead = &m_DepartureSubsHead; + } + else if (pContext->IsPublication()) + { + pCritSec = &m_PubsLock; + pHead = &m_PubsHead; + } + + if (pHead != NULL) + { + EnterCriticalSection(pCritSec); + + LIST_ENTRY* pFindEntry = pContext->GetListEntry(); + LIST_ENTRY* pEntry = pHead->Flink; + while (pEntry != pHead) + { + if (pEntry == pFindEntry) + { + RemoveEntryList(pEntry); + break; + } + + pEntry = pEntry->Flink; + } + + LeaveCriticalSection(pCritSec); + } + + delete pContext; + } + + MethodReturnVoid(); +} + +STDMETHODIMP_ (void) +CMyQueue::OnCancel( + _In_ IWDFIoRequest* pWdfRequest + ) +{ + MethodEntry("pWdfRequest = %p", + pWdfRequest); + + IWDFFile* pFxFile; + pWdfRequest->GetFileObject(&pFxFile); + if (pFxFile != NULL) + { + CFileContext* pFileContext; + HRESULT hr = pFxFile->RetrieveContext((void**)&pFileContext); + if (SUCCEEDED(hr)) + { + pFileContext->OnCancel(); + } + } + + MethodReturnVoid(); +} + +#define IOCTL_BEGIN_PROXIMITY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1000, METHOD_BUFFERED, FILE_ANY_ACCESS) + +STDMETHODIMP_ (void) +CMyQueue::OnDeviceIoControl( + _In_ IWDFIoQueue* /*pWdfQueue*/, + _In_ IWDFIoRequest* pWdfRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T /*InBufferSize*/, + _In_ SIZE_T /*OutBufferSize*/ + ) +{ + MethodEntry("pWdfRequest = %p, ControlCode = %d", + pWdfRequest, ControlCode); + + IWDFFile* pFxFile; + + pWdfRequest->GetFileObject(&pFxFile); + + bool fCompleteRequest = true; + + CFileContext *pFileContext; + HRESULT hr = pFxFile->RetrieveContext((void**)&pFileContext); + if (SUCCEEDED(hr)) + { + switch (ControlCode) + { + case IOCTL_NFP_GET_MAX_MESSAGE_BYTES: + { + IWDFMemory* pWdfOutputMemory; + pWdfRequest->GetOutputMemory(&pWdfOutputMemory); + if (pWdfOutputMemory != NULL) + { + SIZE_T cbOutputBuffer = 0; + // Set the first 4 bytes as the maximum message size of this device/driver + DWORD dwMaxCbPayload = MaxCbPayload; + hr = pWdfOutputMemory->CopyFromBuffer(0, &dwMaxCbPayload, 4); + if (SUCCEEDED(hr)) + { + cbOutputBuffer = 4; + } + pWdfOutputMemory->Release(); + + pWdfRequest->CompleteWithInformation(hr, cbOutputBuffer); + fCompleteRequest = false; + } + } + break; + + case IOCTL_NFP_GET_KILO_BYTES_PER_SECOND: + { + IWDFMemory* pWdfOutputMemory; + pWdfRequest->GetOutputMemory(&pWdfOutputMemory); + if (pWdfOutputMemory != NULL) + { + SIZE_T cbOutputBuffer = 0; + // Set the first 4 bytes as transfer speed of this device/driver + DWORD dwKilobytesPerSecond = KilobytesPerSecond; + hr = pWdfOutputMemory->CopyFromBuffer(0, &dwKilobytesPerSecond, 4); + if (SUCCEEDED(hr)) + { + cbOutputBuffer = 4; + } + pWdfOutputMemory->Release(); + + pWdfRequest->CompleteWithInformation(hr, cbOutputBuffer); + fCompleteRequest = false; + } + } + break; + + case IOCTL_NFP_DISABLE: + hr = pFileContext->Disable(); + break; + + case IOCTL_NFP_ENABLE: + hr = pFileContext->Enable(); + break; + + case IOCTL_NFP_SET_PAYLOAD: + hr = pFileContext->SetPayload(pWdfRequest); + if (SUCCEEDED(hr)) + { + MESSAGE* pMessage = new MESSAGE(); + hr = (pMessage != NULL) ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) + { + pMessage->Initialize(pFileContext->GetType(), pFileContext->GetSize(), pFileContext->GetPayload()); + + EnterCriticalSection(&m_ConnectionLock); + + for (LIST_ENTRY* pEntry = m_ConnectionHead.Flink; + pEntry != &m_ConnectionHead; + pEntry = pEntry->Flink) + { + CConnection* pConnection = CConnection::FromListEntry(pEntry); + if (SUCCEEDED(pConnection->TransmitMessage(pMessage))) + { + pFileContext->HandleMessageTransmitted(); + } + } + + LeaveCriticalSection(&m_ConnectionLock); + + // Place this CFileContext into a list of published messages + EnterCriticalSection(&m_PubsLock); + InsertHeadList(&m_PubsHead, pFileContext->GetListEntry()); + LeaveCriticalSection(&m_PubsLock); + + delete pMessage; + } + } + break; + + case IOCTL_BEGIN_PROXIMITY: + hr = pFileContext->BeginProximity(pWdfRequest, this); + break; + + case IOCTL_NFP_GET_NEXT_SUBSCRIBED_MESSAGE: + hr = pFileContext->GetNextSubscribedMessage(this, pWdfRequest); + if (SUCCEEDED(hr)) + { + fCompleteRequest = false; + } + break; + + case IOCTL_NFP_GET_NEXT_TRANSMITTED_MESSAGE: + hr = pFileContext->GetNextTransmittedMessage(this, pWdfRequest); + if (SUCCEEDED(hr)) + { + fCompleteRequest = false; + } + break; + + default: + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + break; + } + } + + if (fCompleteRequest) + { + TraceInfo("Completing Request: %!HRESULT!", hr); + pWdfRequest->Complete(hr); + } + + MethodReturnVoid(); +} + +void +CMyQueue::ValidateAccept(_In_ SOCKET Socket, _In_ GUID* pMagicPacket) +{ + MethodEntry("..."); + + CConnection* pConnection; + HRESULT hr = CConnection::Create(this, &pConnection); + if (SUCCEEDED(hr)) + { + // Mark it as an Inbound connection, so we know to delete it when + // it's removed from the list + pConnection->SetInboundConnection(); + + pConnection->ValidateAccept(Socket, pMagicPacket); + Socket = INVALID_SOCKET; + } + + if (Socket != INVALID_SOCKET) + { + closesocket(Socket); + } + + MethodReturnVoid(); +} + +void +CMyQueue::HandleReceivedMessage(_In_ MESSAGE* pMessage) +{ + MethodEntry("pMessage->m_szType = '%S'", + pMessage->m_szType); + + if ((pMessage->m_cbPayload > 0) && (pMessage->m_cbPayload <= MaxCbPayload)) + { + EnterCriticalSection(&m_SubsLock); + + LIST_ENTRY* pEntry = m_SubsHead.Flink; + while (pEntry != &m_SubsHead) + { + CFileContext* pSub = CFileContext::FromListEntry(pEntry); + + pSub->HandleReceivedPublication(pMessage->m_szType, + pMessage->m_cbPayload, + pMessage->m_Payload); + + pEntry = pEntry->Flink; + } + + LeaveCriticalSection(&m_SubsLock); + } + + MethodReturnVoid(); +} + +void +CMyQueue::ConnectionEstablished(_In_ CConnection* pConnection) +{ + MethodEntry("..."); + + EnterCriticalSection(&m_ConnectionLock); + BOOL fFirstConnection = IsListEmpty(&m_ConnectionHead); + InsertHeadList(&m_ConnectionHead, pConnection->GetListEntry()); + LeaveCriticalSection(&m_ConnectionLock); + + if (fFirstConnection) + { + AddArrivalEvent(); + } + + MESSAGE* pMessage = new MESSAGE(); + if (pMessage != NULL) + { + EnterCriticalSection(&m_PubsLock); + + LIST_ENTRY* pEntry = m_PubsHead.Flink; + while (pEntry != &m_PubsHead) + { + CFileContext* pPub = CFileContext::FromListEntry(pEntry); + if (pPub->IsEnabled()) + { + pMessage->Initialize(pPub->GetType(), pPub->GetSize(), pPub->GetPayload()); + if (SUCCEEDED(pConnection->TransmitMessage(pMessage))) + { + pPub->HandleMessageTransmitted(); + } + } + pEntry = pEntry->Flink; + } + + LeaveCriticalSection(&m_PubsLock); + delete pMessage; + } + + MethodReturnVoid(); +} + +BOOL +CMyQueue::ConnectionTerminated(_In_ CConnection* pConnection) +{ + MethodEntry("pConnection = 0x%p", + pConnection); + + LIST_ENTRY* pRemoveListEntry = pConnection->GetListEntry(); + + EnterCriticalSection(&m_ConnectionLock); + LIST_ENTRY* pEntry = m_ConnectionHead.Flink; + while (pEntry != &m_ConnectionHead) + { + if (pEntry == pRemoveListEntry) + { + RemoveEntryList(pEntry); + break; + } + pEntry = pEntry->Flink; + } + BOOL fNoMoreConnections = IsListEmpty(&m_ConnectionHead); + + LeaveCriticalSection(&m_ConnectionLock); + + BOOL fConnectionDeleted = FALSE; + if (pConnection->IsInboundConnection()) + { + delete pConnection; + fConnectionDeleted = TRUE; + } + + if (fNoMoreConnections) + { + AddRemovalEvent(); + } + + MethodReturnBool(fConnectionDeleted); +} + +void +CMyQueue::AddArrivalEvent() +{ + MethodEntry("void"); + + EnterCriticalSection(&m_SubsLock); + + for (LIST_ENTRY* pEntry = m_ArrivalSubsHead.Flink; + pEntry != &m_ArrivalSubsHead; + pEntry = pEntry->Flink) + { + CFileContext::FromListEntry(pEntry)->HandleArrivalEvent(); + } + + LeaveCriticalSection(&m_SubsLock); + + MethodReturnVoid(); +} + +void +CMyQueue::AddRemovalEvent() +{ + MethodEntry("void"); + + EnterCriticalSection(&m_SubsLock); + + for (LIST_ENTRY* pEntry = m_DepartureSubsHead.Flink; + pEntry != &m_DepartureSubsHead; + pEntry = pEntry->Flink) + { + CFileContext::FromListEntry(pEntry)->HandleRemovalEvent(); + } + + LeaveCriticalSection(&m_SubsLock); + + MethodReturnVoid(); +} + +STDMETHODIMP_(void) +CMyQueue::OnCleanup( + _In_ IWDFObject* /*pWdfObject*/ + ) +{ + MethodEntry("..."); + + // + // CMyQueue has a reference to framework device object via m_Queue. + // Framework queue object has a reference to CMyQueue object via the callbacks. + // This leads to circular reference and both the objects can't be destroyed until this circular reference is broken. + // To break the circular reference we release the reference to the framework queue object here in OnCleanup. + // + + m_FxQueue = NULL; + + MethodReturnVoid(); +} diff --git a/nfp/net/driver/Queue.h b/nfp/net/driver/Queue.h new file mode 100644 index 00000000..55b2ff39 --- /dev/null +++ b/nfp/net/driver/Queue.h @@ -0,0 +1,175 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + queue.h + +Abstract: + + This file defines the queue callback interface. + +Environment: + + user mode only + +Revision History: + +--*/ + +#pragma once + +#include "device.h" +#include "Connection.h" + +static const int KilobytesPerSecond = 20; + +static const int MaxCbPayload = 10240; +static const int MaxCchType = 507; // maximum message type length is 5 for "?ubs\\", 250 for protocol + 250 for subtype + 1 each for dot "." and NULL terminator. +static const int MaxCchTypeNetwork = 502; // maximum message type length over the network is 250 for protocol + 250 for subtype + 1 each for dot "." and NULL terminator. +static const int MinCchType = 2; +static const int MaxCchMimeType = 256; + +#define WINDOWS_PROTOCOL L"Windows." +#define WINDOWS_PROTOCOL_CHARS 8 + +#define WINDOWSURI_PROTOCOL L"WindowsUri" + +#define WINDOWSMIME_PROTOCOL L"WindowsMime" +#define WINDOWSMIME_PROTOCOL_CHARS 11 + +#define PUBS_NAMESPACE L"Pubs\\" +#define PUBS_NAMESPACE_CHARS 5 + +#define SUBS_NAMESPACE L"Subs\\" +#define SUBS_NAMESPACE_CHARS 5 + +#define DEVICE_ARRIVED L"DeviceArrived" +#define DEVICE_DEPARTED L"DeviceDeparted" + +#define PAIRING_PROTOCOL L"Pairing:" +#define PAIRING_PROTOCOL_CHARS 8 + +#define NDEF_PROTOCOL L"NDEF" +#define NDEF_PROTOCOL_CHARS 4 + +#define NDEF_EMPTY_TYPE L"Empty" +#define NDEF_EMPTY_TYPE_CHARS 5 + +struct MESSAGE +{ + MESSAGE() : + m_cbPayload(0) + { + ZeroMemory(m_szType, sizeof(m_szType)); + ZeroMemory(m_Payload, sizeof(m_Payload)); + } + + void Initialize( + _In_ PCWSTR szType, + _In_ DWORD cbPayload, + _In_reads_bytes_(cbPayload) PBYTE pbPayload + ) + { + ZeroMemory(m_szType, sizeof(m_szType)); + ZeroMemory(m_Payload, sizeof(m_Payload)); + + m_cbPayload = cbPayload; + StringCchCopy(m_szType, ARRAY_SIZE(m_szType), szType); + CopyMemory(m_Payload, pbPayload, cbPayload); + } + + wchar_t m_szType[MaxCchTypeNetwork]; + DWORD m_cbPayload; + BYTE m_Payload[MaxCbPayload]; +}; + + +// +// Queue Callback Object. +// + +class ATL_NO_VTABLE CMyQueue : + public CComObjectRootEx, + public IQueueCallbackCreate, + public IQueueCallbackDeviceIoControl, + public IRequestCallbackCancel, + public IObjectCleanup, + public IValidateAccept, + public IConnectionCallback +{ +public: + +DECLARE_NOT_AGGREGATABLE(CMyQueue) + +BEGIN_COM_MAP(CMyQueue) + COM_INTERFACE_ENTRY(IQueueCallbackCreate) + COM_INTERFACE_ENTRY(IQueueCallbackDeviceIoControl) + COM_INTERFACE_ENTRY(IRequestCallbackCancel) + COM_INTERFACE_ENTRY(IObjectCleanup) +END_COM_MAP() + +public: + + //IQueueCallbackCreate + STDMETHOD_(void,OnCreateFile)(_In_ IWDFIoQueue* pWdfQueue, _In_ IWDFIoRequest* pWDFRequest, _In_ IWDFFile* pWdfFileObject); + + //IQueueCallbackDeviceIoControl + STDMETHOD_(void,OnDeviceIoControl)(_In_ IWDFIoQueue* pWdfQueue, _In_ IWDFIoRequest* pWDFRequest, _In_ ULONG ControlCode, _In_ SIZE_T InBufferSize, _In_ SIZE_T OutBufferSize); + + //IObjectCleanup + STDMETHOD_(void,OnCleanup)(_In_ IWDFObject* pWdfObject); + + //IValidateAccept + void ValidateAccept(_In_ SOCKET Socket, _In_ GUID* pMagicPacket); + + + //IRequestCallbackCancel + STDMETHODIMP_(void) + OnCancel( + _In_ IWDFIoRequest* pWdfRequest + ); + + //IConnectionCallback + virtual void HandleReceivedMessage(_In_ MESSAGE* pMessageData); + virtual void ConnectionEstablished(_In_ CConnection* pBthConnection); + virtual BOOL ConnectionTerminated(_In_ CConnection* pBthConnection); + +public: + CMyQueue(); + ~CMyQueue(); + + STDMETHOD(Initialize)(_In_ CMyDevice * Device); + + HRESULT + Configure( + VOID + ); + + STDMETHODIMP_(void) + OnCloseFile( + _In_ IWDFFile* pWdfFileObject + ); + +private: + + void AddArrivalEvent(); + void AddRemovalEvent(); + +private: + CComPtr m_FxQueue; + + LIST_ENTRY m_SubsHead; + LIST_ENTRY m_ArrivalSubsHead; + LIST_ENTRY m_DepartureSubsHead; + CRITICAL_SECTION m_SubsLock; + + LIST_ENTRY m_PubsHead; + CRITICAL_SECTION m_PubsLock; + + LIST_ENTRY m_ConnectionHead; + CRITICAL_SECTION m_ConnectionLock; + + CSocketListener m_SocketListener; +}; diff --git a/nfp/net/driver/connection.h b/nfp/net/driver/connection.h new file mode 100644 index 00000000..6ab900e0 --- /dev/null +++ b/nfp/net/driver/connection.h @@ -0,0 +1,125 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Abstract: + + Defines a simple NearFieldProximity Provider implementation using the network + for use in selfhosting. + +Author: + + Travis Martin (TravM) 06-24-2010 + +--*/ +#pragma once + +#include "SocketListener.h" + +class CConnection; + +struct MESSAGE; + +interface IConnectionCallback +{ + virtual void HandleReceivedMessage(_In_ MESSAGE* pMessage) = 0; + virtual void ConnectionEstablished(_In_ CConnection* pConnection) = 0; + virtual BOOL ConnectionTerminated(_In_ CConnection* pConnection) = 0; +}; + +class CConnection : public IValidateAccept +{ +private: + CConnection(_In_ IConnectionCallback* pCallback) : + _State(INITIAL), + _Socket(INVALID_SOCKET), + _pCallback(pCallback), + _ThreadpoolWork(NULL), + _fInboundConnection(false) + { + } + +public: + + virtual ~CConnection() + { + Terminate(); + + if (_ThreadpoolWork != NULL) + { + // Don't wait for threadpool callbacks when this thread is actually the threadpool callback + if (_ThreadpoolThreadId != GetCurrentThreadId()) + { + WaitForThreadpoolWorkCallbacks(_ThreadpoolWork, false); + } + CloseThreadpoolWork(_ThreadpoolWork); + _ThreadpoolWork = NULL; + } + } + + static HRESULT Create(_In_ IConnectionCallback* pCallback, _Outptr_ CConnection** ppConnection); + + void SetInboundConnection() { _fInboundConnection = true; } + bool IsInboundConnection() { return _fInboundConnection; } + + //IValidateAccept + void ValidateAccept(_In_ SOCKET Socket, _In_ GUID* pMagicPacket); + + HRESULT FinalizeEstablish(_In_ SOCKET Socket); + + HRESULT InitializeAsClient(_In_ BEGIN_PROXIMITY_ARGS* pArgs); + + HRESULT TransmitMessage(_In_ MESSAGE* pMessage); + + BOOL ReceiveThreadProc(); + static VOID CALLBACK s_ReceiveThreadProc( + _Inout_ PTP_CALLBACK_INSTANCE Instance, + _Inout_ PVOID Context, + _Inout_ PTP_WORK /*Work*/) + { + CallbackMayRunLong(Instance); + + CConnection* pConnection = (CConnection*)Context; + pConnection->_ThreadpoolThreadId = GetCurrentThreadId(); + BOOL fConnectionDeleted = pConnection->ReceiveThreadProc(); + + if (!fConnectionDeleted) + { + // Only clear the member variable if the connection object wasn't deleted. + pConnection->_ThreadpoolThreadId = 0; + } + } + + LIST_ENTRY* GetListEntry() { return &_ListEntry; } + static CConnection* FromListEntry(LIST_ENTRY* pListEntry) + { + return (CConnection*) CONTAINING_RECORD(pListEntry, CConnection, _ListEntry); + } + +private: + + void Terminate(); + +private: + + enum STATE + { + INITIAL = 0, + ESTABLISHED, + TERMINATED + }; + + volatile STATE _State; + + SOCKET _Socket; + + PTP_WORK _ThreadpoolWork; + DWORD _ThreadpoolThreadId; + + IConnectionCallback* _pCallback; + + bool _fInboundConnection; + + LIST_ENTRY _ListEntry; + +}; diff --git a/nfp/net/driver/device.cpp b/nfp/net/driver/device.cpp new file mode 100644 index 00000000..599d57b5 --- /dev/null +++ b/nfp/net/driver/device.cpp @@ -0,0 +1,261 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Device.cpp + +Abstract: + + This module contains the implementation of the UMDF sample + driver's device callback object. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "device.tmh" + +HRESULT +CMyDevice::Initialize( + _In_ IWDFDriver* FxDriver, + _In_ IWDFDeviceInitialize* FxDeviceInit + ) +/*++ + + Routine Description: + + This method initializes the device callback object and creates the + partner device object. + + The method should perform any device-specific configuration that: + * could fail (these can't be done in the constructor) + * must be done before the partner object is created -or- + * can be done after the partner object is created and which aren't + influenced by any device-level parameters the parent (the driver + in this case) might set. + + Arguments: + + FxDeviceInit - the settings for this device. + FxDriver - IWDF Driver for this device. + + Return Value: + + status. + +--*/ +{ + MethodEntry("..."); + + CComPtr fxDevice; + HRESULT hr; + + // + // Configure things like the locking model before we go to create our + // partner device. + // + + // + // Set the locking model + // + + FxDeviceInit->SetLockingConstraint(None); + + // + // TODO: Any per-device initialization which must be done before + // creating the partner object. + // + + // + // Create a new FX device object and assign the new callback object to + // handle any device level events that occur. + // + + // + // QueryIUnknown references the IUnknown interface that it returns + // (which is the same as referencing the device). We pass that to + // CreateDevice, which takes its own reference if everything works. + // + + CComPtr pUnk; + HRESULT hrQI = this->QueryInterface(__uuidof(IUnknown),(void**)&pUnk); + WUDF_SAMPLE_DRIVER_ASSERT(SUCCEEDED(hrQI)); + + hr = FxDriver->CreateDevice(FxDeviceInit, pUnk, &fxDevice); + + // + // If that succeeded then set our FxDevice member variable. + // + + if (SUCCEEDED(hr)) + { + m_FxDevice = fxDevice; + } + + MethodReturnHR(hr); +} + +HRESULT +CMyDevice::Configure( + VOID + ) +/*++ + + Routine Description: + + This method is called after the device callback object has been initialized + and returned to the driver. It would setup the device's queues and their + corresponding callback objects. + + Arguments: + + FxDevice - the framework device object for which we're handling events. + + Return Value: + + status + +--*/ +{ + MethodEntry("void"); + + // + // Create a new instance of our Queue callback object + // + CComObject * defaultQueue = NULL; + HRESULT hr = CComObject::CreateInstance(&defaultQueue); + + if (SUCCEEDED(hr)) + { + defaultQueue->AddRef(); + hr = defaultQueue->Initialize(this); + } + + if (SUCCEEDED(hr)) + { + hr = defaultQueue->Configure(); + } + + // + // Create and Enable Device Interfaces for this device. + // + if (SUCCEEDED(hr)) + { + hr = m_FxDevice->CreateDeviceInterface(&GUID_DEVINTERFACE_NETNFP, + NULL); + } + + if (SUCCEEDED(hr)) + { + hr = m_FxDevice->AssignDeviceInterfaceState(&GUID_DEVINTERFACE_NETNFP, + NULL, + TRUE); + } + + if (SUCCEEDED(hr)) + { + hr = m_FxDevice->CreateDeviceInterface(&GUID_DEVINTERFACE_NFP, + NULL); + } + + if (SUCCEEDED(hr)) + { + hr = m_FxDevice->AssignDeviceInterfaceState(&GUID_DEVINTERFACE_NFP, + NULL, + TRUE); + } + + if (SUCCEEDED(hr)) + { + // + // Save a pointer to our queue, so we can lock it during file cleanup + // + m_MyQueue = defaultQueue; + } + + // + // Release the reference we took on the queue object. + // The framework took its own references on the object's callback interfaces + // when we called m_FxDevice->CreateIoQueue, and will manage the object's lifetime. + // + SAFE_RELEASE(defaultQueue); + + MethodReturnHR(hr); +} + +STDMETHODIMP_(void) +CMyDevice::OnCloseFile( + _In_ IWDFFile* pWdfFileObject + ) +{ + MethodEntry("..."); + + m_MyQueue->OnCloseFile(pWdfFileObject); + + MethodReturnVoid(); +} + +STDMETHODIMP_(void) +CMyDevice::OnCleanupFile( + _In_ IWDFFile* /*pWdfFileObject*/ + ) +/*++ + + Routine Description: + + This method is when app with open handle device terminates. + + Arguments: + + pWdfFileObject - the framework file object for which close is handled. + + Return Value: + + None + +--*/ +{ +} + +STDMETHODIMP_(void) +CMyDevice::OnCleanup( + _In_ IWDFObject* pWdfObject + ) +/*++ + + Routine Description: + + This device callback method is invoked by the framework when the WdfObject + is about to be released by the framework. + + Arguments: + + pWdfObject - the framework device object for which OnCleanup. + + Return Value: + + None + +--*/ +{ + MethodEntry("..."); + + WUDF_SAMPLE_DRIVER_ASSERT(pWdfObject == m_FxDevice); + + m_MyQueue = NULL; + + // + // CMyDevice has a reference to framework device object via m_Device. + // Framework device object has a reference to CMyDevice object via the callbacks. + // This leads to circular reference and both the objects can't be destroyed until this circular reference is broken. + // To break the circular reference we release the reference to the framework device object here in OnCleanup. + // + m_FxDevice = NULL; + + MethodReturnVoid(); +} diff --git a/nfp/net/driver/device.h b/nfp/net/driver/device.h new file mode 100644 index 00000000..c4eeb121 --- /dev/null +++ b/nfp/net/driver/device.h @@ -0,0 +1,73 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions for the sample + driver's device callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ +#pragma once + +class CMyQueue; + +class ATL_NO_VTABLE CMyDevice : + public CComObjectRootEx, + public IFileCallbackCleanup, + public IFileCallbackClose, + public IObjectCleanup +{ +public: + +DECLARE_NOT_AGGREGATABLE(CMyDevice) + +BEGIN_COM_MAP(CMyDevice) + COM_INTERFACE_ENTRY(IFileCallbackCleanup) + COM_INTERFACE_ENTRY(IFileCallbackClose) + COM_INTERFACE_ENTRY(IObjectCleanup) +END_COM_MAP() + +public: + + //IFileCallbackCleanup + STDMETHOD_(void,OnCleanupFile)(_In_ IWDFFile* pWdfFileObject); + //IFileCallbackClose + STDMETHOD_(void,OnCloseFile)(_In_ IWDFFile* pWdfFileObject); + //IObjectCleanup + STDMETHOD_(void,OnCleanup)(_In_ IWDFObject* pWdfObject); + +public: + + STDMETHOD(Initialize)(_In_ IWDFDriver* pWdfDriver, _In_ IWDFDeviceInitialize* pWdfDeviceInit); + + HRESULT + Configure( + VOID + ); + + IWDFDevice * + GetFxDevice( + VOID + ) + { + return m_FxDevice; + } + +private: + CComPtr m_FxDevice; + + CMyQueue* m_MyQueue; + + HRESULT ReadAndAssignPropertyStoreValue(); + HRESULT GetAnsiValFromPropVariant(_In_ PROPVARIANT val, _Inout_ LPSTR *PropertyValueA); + +}; diff --git a/nfp/net/driver/dllsup.cpp b/nfp/net/driver/dllsup.cpp new file mode 100644 index 00000000..85c866ec --- /dev/null +++ b/nfp/net/driver/dllsup.cpp @@ -0,0 +1,112 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + dllsup.cpp + +Abstract: + + This module contains the implementation of the UMDF Socktecho Sample + Driver's entry point and its exported functions for providing COM support. + + This module can be copied without modification to a new UMDF driver. It + depends on some of the code in comsup.cpp & comsup.h to handle DLL + registration and creating the first class factory. + + This module is dependent on the following defines: + + MYDRIVER_TRACING_ID - A wide string passed to WPP when initializing + tracing. + + MYDRIVER_CLASS_ID - A GUID encoded in struct format used to + initialize the driver's ClassID. + + These are defined in internal.h for the sample. If you choose + to use a different primary include file, you should ensure they are + defined there as well. + +Environment: + + WDF User-Mode Driver Framework (WDF:UMDF) + +--*/ + +#include "internal.h" +#include "dllsup.tmh" + +const GUID CLSID_MyDriverCoClass = MYDRIVER_CLASS_ID; + +class CNetNfpProviderModule : public CAtlDllModuleT< CNetNfpProviderModule > +{ +}; + + +OBJECT_ENTRY_AUTO(CLSID_MyDriverCoClass, CMyDriver) + + +CNetNfpProviderModule _AtlModule; + +BOOL +WINAPI +DllMain( + HINSTANCE ModuleHandle, + DWORD Reason, + PVOID Reserved + ) +/*++ + + Routine Description: + + This is the entry point and exit point for the I/O trace driver. This + does very little as the I/O trace driver has minimal global data. + + This method initializes tracing. + + Arguments: + + ModuleHandle - the DLL handle for this module. + + Reason - the reason this entry point was called. + + Reserved - unused + + Return Value: + + TRUE + +--*/ +{ + + UNREFERENCED_PARAMETER( ModuleHandle ); + + if (DLL_PROCESS_ATTACH == Reason) + { + // + // Initialize tracing. + // + + WPP_INIT_TRACING(MYDRIVER_TRACING_ID); + TracingTlsInitialize(); + + } + else if (DLL_PROCESS_DETACH == Reason) + { + // + // Cleanup tracing. + // + + TracingTlsFree(); + WPP_CLEANUP(); + } + + return _AtlModule.DllMain(Reason, Reserved); +; +} + +_Check_return_ +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} diff --git a/nfp/net/driver/driver.cpp b/nfp/net/driver/driver.cpp new file mode 100644 index 00000000..2c28f16e --- /dev/null +++ b/nfp/net/driver/driver.cpp @@ -0,0 +1,150 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Driver.cpp + +Abstract: + + This module contains the implementation of the UMDF Socketecho Sample's + core driver callback object. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ +#include "internal.h" + +#include "driver.tmh" + +DECLARE_TRACING_TLS; + +STDMETHODIMP +CMyDriver::OnInitialize( + _In_ IWDFDriver* /*pWdfDriver*/ + ) + + +/*++ + + Routine Description: + + This routine is invoked by the framework at driver load . + This method will invoke the Winsock Library for using + Winsock API in this driver. + + Arguments: + + pWdfDriver - Framework driver object + + Return Value: + + S_OK if successful, or error otherwise. + +--*/ + +{ + MethodEntry("..."); + + HRESULT hr = S_OK; + + WSADATA wsaData; + int result = WSAStartup(MAKEWORD(2,2), &wsaData); + if (result != 0) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + TraceErrorHR(hr, "Failed to initialize Winsock 2.0"); + } + + MethodReturnHR(hr); +} + +STDMETHODIMP_(void) +CMyDriver::OnDeinitialize( + _In_ IWDFDriver* /*pWdfDriver*/ + ) + +/*++ + Routine Description: + + The FX invokes this method when it unloads the driver. + This routine will Cleanup Winsock library + + Arguments: + + pWdfDriver - the Fx driver object. + + Return Value: + + None + + --*/ +{ + MethodEntry("..."); + + WSACleanup(); + + MethodReturnVoid(); +} + +STDMETHODIMP +CMyDriver::OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ) +/*++ + + Routine Description: + + The FX invokes this method when it wants to install our driver on a device + stack. This method creates a device callback object, then calls the Fx + to create an Fx device object and associate the new callback object with + it. + + Arguments: + + FxWdfDriver - the Fx driver object. + + FxDeviceInit - the initialization information for the device. + + Return Value: + + status + +--*/ +{ + MethodEntry("..."); + + // + // Create a new instance of our device callback object + // + + CComObject * device; + HRESULT hr = CComObject::CreateInstance(&device); + if (SUCCEEDED(hr)) + { + device->AddRef(); + hr = device->Initialize(FxWdfDriver, FxDeviceInit); + if (SUCCEEDED(hr)) + { + // + // If that succeeded then call the device's configure method. This + // allows the device to create any queues or other structures that it + // needs now that the corresponding fx device object has been created. + // + hr = device->Configure(); + } + + // + // Release the reference we took on the device object. + // The framework took its own references on the object's callback interfaces + // when we called FxWdfDriver->CreateDevice, and will manage the object's lifetime. + // + SAFE_RELEASE(device); + } + + MethodReturnHR(hr); +} diff --git a/nfp/net/driver/driver.h b/nfp/net/driver/driver.h new file mode 100644 index 00000000..6affa20f --- /dev/null +++ b/nfp/net/driver/driver.h @@ -0,0 +1,53 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Driver.h + +Abstract: + + This module contains the type definitions for the UMDF Socketecho sample's + driver callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// This class handles driver events for the socktecho sample. In particular +// it supports the OnDeviceAdd event, which occurs when the driver is called +// to setup per-device handlers for a new device stack. +// + +extern const GUID CLSID_MyDriverCoClass; + +class ATL_NO_VTABLE CMyDriver : + public CComObjectRootEx, + public CComCoClass, + public IDriverEntry +{ +public: + +DECLARE_NOT_AGGREGATABLE(CMyDriver) + +DECLARE_CLASSFACTORY(); + +DECLARE_NO_REGISTRY(); + +BEGIN_COM_MAP(CMyDriver) + COM_INTERFACE_ENTRY(IDriverEntry) +END_COM_MAP() + +public: + // IDriverEntry + STDMETHOD(OnInitialize)(_In_ IWDFDriver* pWdfDriver); + STDMETHOD(OnDeviceAdd)(_In_ IWDFDriver* pWdfDriver, _In_ IWDFDeviceInitialize* pWdfDeviceInit); + STDMETHOD_(void,OnDeinitialize)(_In_ IWDFDriver* pWdfDriver); +}; + diff --git a/nfp/net/driver/exports.def b/nfp/net/driver/exports.def new file mode 100644 index 00000000..964e5f79 --- /dev/null +++ b/nfp/net/driver/exports.def @@ -0,0 +1,6 @@ +; exports.def : Declares the module parameters. + +LIBRARY "NetNfpProvider" + +EXPORTS + DllGetClassObject PRIVATE diff --git a/nfp/net/driver/internal.h b/nfp/net/driver/internal.h new file mode 100644 index 00000000..80aa0152 --- /dev/null +++ b/nfp/net/driver/internal.h @@ -0,0 +1,152 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Internal.h + +Abstract: + + This module contains the local type definitions for the UMDF Socketecho sample + driver sample. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +// +// Include the winsock headers before any other windows headers. +// +#include +#include + +// +// Include the WUDF DDI +// + +#include "wudfddi.h" + +// +// Use specstrings for in/out annotation of function parameters. +// + +#include "specstrings.h" + +// +// Define the tracing GUID for this driver +// + +#define TRACE_CONTROL_GUID (12579E92,1B46,40A6,9CFC,C718A677830B) + + +// +// Driver specific #defines +// + +#define MYDRIVER_TRACING_ID L"Microsoft\\UMDF\\NetNfpProvider" + +/* 278F44F0-FF5C-4FE3-BF20-F8AA158EA7BC */ +#define MYDRIVER_CLASS_ID { 0x278F44F0, 0xFF5C, 0x4FE3, {0xBF, 0x20, 0xF8, 0xAA, 0x15, 0x8E, 0xA7, 0xBC} } +#ifndef SAFE_RELEASE +#define SAFE_RELEASE(p) {if ((p)) { (p)->Release(); (p) = NULL; }} +#endif + +__forceinline +#ifdef _PREFAST_ +__declspec(noreturn) +#endif +VOID +WdfTestNoReturn( + VOID + ) +{ + // do nothing. +} + +#define WUDF_SAMPLE_DRIVER_ASSERT(p) \ +{ \ + if ( !(p) ) \ + { \ + DebugBreak(); \ + WdfTestNoReturn(); \ + } \ +} + +// +// define the maximum size of the message queue +// + +#define MAX_MESSAGE_QUEUE_SIZE 50 + +// +// MessageId: STATUS_CANCELLED +// +// MessageText: +// +// The I/O request was canceled. +// +#define STATUS_CANCELLED (0xC0000120L) + +// +// MessageId: STATUS_INVALID_DEVICE_STATE +// +// MessageText: +// +// The device is not in a valid state to perform this request. +// +#define STATUS_INVALID_DEVICE_STATE (0xC0000184L) + +// +// MessageId: STATUS_INVALID_BUFFER_SIZE +// +// MessageText: +// +// The size of the buffer is invalid for the specified operation. +// +#define STATUS_INVALID_BUFFER_SIZE (0xC0000206L) + +// +// MessageId: STATUS_OBJECT_PATH_NOT_FOUND +// +// MessageText: +// +// {Path Not Found} +// The path %hs does not exist. +// +#define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS)0xC000003AL) + +// +// Include the type specific headers. +// +#include +#include + +// Windows Headers +#include +#include +#include +#include +#include +#include + +// Sample headers +#include "NetNfp.h" +#include "WppDefs.h" +#include "list.h" +#include "connection.h" +#include "filecontext.h" +#include "driver.h" +#include "device.h" +#include "queue.h" + +_Analysis_mode_(_Analysis_operator_new_null_) + diff --git a/nfp/net/driver/list.h b/nfp/net/driver/list.h new file mode 100644 index 00000000..d2bfce3f --- /dev/null +++ b/nfp/net/driver/list.h @@ -0,0 +1,119 @@ +#pragma once + +FORCEINLINE +VOID +InitializeListHead( + _Out_ PLIST_ENTRY ListHead + ) +{ + ListHead->Flink = ListHead->Blink = ListHead; +} + +_Check_return_ +BOOLEAN +FORCEINLINE +IsListEmpty( + _In_ const LIST_ENTRY * ListHead + ) +{ + return (BOOLEAN)(ListHead->Flink == ListHead); +} + +FORCEINLINE +BOOLEAN +RemoveEntryList( + _In_ PLIST_ENTRY Entry + ) +{ + PLIST_ENTRY Blink; + PLIST_ENTRY Flink; + + Flink = Entry->Flink; + Blink = Entry->Blink; + Blink->Flink = Flink; + Flink->Blink = Blink; + return (BOOLEAN)(Flink == Blink); +} + +FORCEINLINE +PLIST_ENTRY +RemoveHeadList( + _Inout_ PLIST_ENTRY ListHead + ) +{ + PLIST_ENTRY Flink; + PLIST_ENTRY Entry; + + Entry = ListHead->Flink; + Flink = Entry->Flink; + ListHead->Flink = Flink; + Flink->Blink = ListHead; + return Entry; +} + + + +FORCEINLINE +PLIST_ENTRY +RemoveTailList( + _Inout_ PLIST_ENTRY ListHead + ) +{ + PLIST_ENTRY Blink; + PLIST_ENTRY Entry; + + Entry = ListHead->Blink; + Blink = Entry->Blink; + ListHead->Blink = Blink; + Blink->Flink = ListHead; + return Entry; +} + + +FORCEINLINE +VOID +InsertTailList( + _Inout_ PLIST_ENTRY ListHead, + _Inout_ __drv_aliasesMem PLIST_ENTRY Entry + ) +{ + PLIST_ENTRY Blink; + + Blink = ListHead->Blink; + Entry->Flink = ListHead; + Entry->Blink = Blink; + Blink->Flink = Entry; + ListHead->Blink = Entry; +} + + +FORCEINLINE +VOID +InsertHeadList( + _Inout_ PLIST_ENTRY ListHead, + _Inout_ __drv_aliasesMem PLIST_ENTRY Entry + ) +{ + PLIST_ENTRY Flink; + + Flink = ListHead->Flink; + Entry->Flink = Flink; + Entry->Blink = ListHead; + Flink->Blink = Entry; + ListHead->Flink = Entry; +} + +FORCEINLINE +VOID +AppendTailList( + _Inout_ PLIST_ENTRY ListHead, + _Inout_ PLIST_ENTRY ListToAppend + ) +{ + PLIST_ENTRY ListEnd = ListHead->Blink; + + ListHead->Blink->Flink = ListToAppend; + ListHead->Blink = ListToAppend->Blink; + ListToAppend->Blink->Flink = ListHead; + ListToAppend->Blink = ListEnd; +} diff --git a/nfp/net/driver/netnfpprovider.inx b/nfp/net/driver/netnfpprovider.inx new file mode 100644 index 00000000..0f2d246f --- /dev/null +++ b/nfp/net/driver/netnfpprovider.inx @@ -0,0 +1,83 @@ +; +; NetNfpProvider.inf +; + +[Version] +Signature="$WINDOWS NT$" +Class=Proximity +ClassGuid={5630831C-06C9-4856-B327-F5D32586E060} +Provider=%MSFT% +CatalogFile=nfp.cat +DriverVer=03/20/2003,5.00.3788 + +[Manufacturer] +%MSFTWUDF%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%NetNfpProviderName%=NetNfpProvider_Install,WUDF\NetNfpProvider + +[SourceDisksFiles] +NetNfpProvider.dll=1 + + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== WUDF NetNfpProvider Test Driver ================================== + +[NetNfpProvider_Install] +CopyFiles=UMDFDriverCopy + +[NetNfpProvider_Install.hw] +AddReg=NetNfpProvider_AddReg + +[NetNfpProvider_Install.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[NetNfpProvider_Install.CoInstallers] +AddReg = NetNfpProvider_Install.CoInstallers_AddReg +CopyFiles = CoInstallers_CopyFiles + +[NetNfpProvider_Install.CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WUDFCoinstaller.dll" + + + +[CoInstallers_CopyFiles] +;WudfUpdate_$UMDFCOINSTALLERVERSION$.dll + +[NetNfpProvider_Install.Wdf] +UmdfService=NetNfpProvider, NetNfpProvider_Driver_Install +UmdfServiceOrder=NetNfpProvider +UmdfDispatcher=FileHandle + +[NetNfpProvider_AddReg] +HKR,"NetNfpProvider","Server",0x00010001,1 + +[WUDFRD_ServiceInstall] +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\WUDFRd.sys + +[NetNfpProvider_Driver_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID="{278F44F0-FF5C-4FE3-BF20-F8AA158EA7BC}" +ServiceBinary="%12%\UMDF\NetNfpProvider.dll" + +[DestinationDirs] +UMDFDriverCopy=12,UMDF + +[UMDFDriverCopy] +NetNfpProvider.dll + +; =================== Generic ================================== + +[Strings] +MSFT="Microsoft" +MSFTWUDF="Microsoft Windows Driver Kit Sample (Proximity)" +MediaDescription="Microsoft Network NearFieldProximity Provider Installation Media" +NetNfpProviderName="Network NearFieldProximity Provider" + + + diff --git a/nfp/net/driver/netnfpprovider.rc b/nfp/net/driver/netnfpprovider.rc new file mode 100644 index 00000000..98d1f8ab --- /dev/null +++ b/nfp/net/driver/netnfpprovider.rc @@ -0,0 +1,18 @@ +//--------------------------------------------------------------------------- +// Skeleton.rc +// +// Copyright (c) Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include + + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WDF:UMDF Network NearFieldProximity Provider" +#define VER_INTERNALNAME_STR "NetNfpProvider" +#define VER_ORIGINALFILENAME_STR "NetNfpProvider.dll" + +#include "common.ver" diff --git a/nfp/net/driver/socketlistener.cpp b/nfp/net/driver/socketlistener.cpp new file mode 100644 index 00000000..3aaa687b --- /dev/null +++ b/nfp/net/driver/socketlistener.cpp @@ -0,0 +1,222 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Abstract: + + Implements a socket listener class + +Author: + + Travis Martin (TravM) 06-24-2010 + +Environment: + + User-mode only. + +--*/ +#include "internal.h" + +#include "SocketListener.tmh" + +HRESULT SetSocketIpv6Only(_In_ SOCKET socket, _In_ BOOL Ipv6Only) +{ + HRESULT hr = S_OK; + if (setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&Ipv6Only, sizeof(Ipv6Only)) == SOCKET_ERROR) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + USE_DEFAULT_TRACING_CONTEXT; + TraceErrorHR(hr, "setsockopt IPV6_V6ONLY"); + } + return hr; +} + +HRESULT CSocketListener::EnableAccepting(_In_ IValidateAccept* pValidator) +{ + HRESULT hr = S_OK; + if (_pValidator == NULL) + { + USE_DEFAULT_TRACING_CONTEXT; + + _ThreadpoolIo = CreateThreadpoolIo((HANDLE)_ListenSocket, s_AcceptThreadProc, this, NULL); + if (_ThreadpoolIo == NULL) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + if (SUCCEEDED(hr)) + { + int backlog = 2; + if (listen(_ListenSocket, backlog) == SOCKET_ERROR) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + } + + if (SUCCEEDED(hr)) + { + // We're now accepting + _pValidator = pValidator; + + hr = BeginAccept(); + if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING)) + { + hr = S_OK; + } + } + + TraceInfo("EnableAccepting(): %!HRESULT!", hr); + } + + return hr; +} + +void CSocketListener::StopAccepting() +{ + MethodEntry("void"); + + SOCKET listenSocket = InterlockedExchange(&_ListenSocket, INVALID_SOCKET); + if (listenSocket != INVALID_SOCKET) + { + closesocket(listenSocket); + } + + PTP_IO threadpoolIo = (PTP_IO)InterlockedExchangePointer((PVOID*)&_ThreadpoolIo, NULL); + if (threadpoolIo != NULL) + { + // Don't wait for threadpool callbacks when this thread is actually the threadpool callback + if (_ThreadpoolThreadId != GetCurrentThreadId()) + { + WaitForThreadpoolIoCallbacks(threadpoolIo, false); + } + CloseThreadpoolIo(threadpoolIo); + } + + _pValidator = NULL; + + if (_ClientSocket != INVALID_SOCKET) + { + closesocket(_ClientSocket); + _ClientSocket = INVALID_SOCKET; + } + + MethodReturnVoid(); +} + +HRESULT CSocketListener::BeginAccept() +{ + MethodEntry("void"); + + HRESULT hr = S_OK; + _ClientSocket = socket(AF_INET6, SOCK_STREAM, 0); + if (_ClientSocket == INVALID_SOCKET) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + + if (SUCCEEDED(hr)) + { + hr = SetSocketIpv6Only(_ClientSocket, FALSE); + } + + if (SUCCEEDED(hr)) + { + PTP_IO threadpoolIo = _ThreadpoolIo; + if (threadpoolIo != NULL) + { + StartThreadpoolIo(threadpoolIo); + + ULONG_PTR cbReceived = 0; + ZeroMemory(&_Overlapped, sizeof(_Overlapped)); + if (!AcceptEx(_ListenSocket, _ClientSocket, &_AcceptBuffer, + sizeof(_AcceptBuffer.MagicPacket), + sizeof(_AcceptBuffer.DestAddress), + sizeof(_AcceptBuffer.SourceAddress), + (LPDWORD)&cbReceived, &_Overlapped)) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + if (hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING)) + { + // Failed to accept, so cleanup + CancelThreadpoolIo(threadpoolIo); + StopAccepting(); + } + } + } + } + + MethodReturnHR(hr); +} + +void CSocketListener::AcceptThreadProc(_In_ HRESULT hr, _In_ ULONG_PTR cbReceived) +{ + MethodEntry("hr = %!HRESULT!, cbReceived = %d", + hr, (ULONG)cbReceived); + + if (SUCCEEDED(hr)) + { + if (cbReceived == sizeof(_AcceptBuffer.MagicPacket)) + { + // Transfer ownership of _ClientSocket + _pValidator->ValidateAccept(_ClientSocket, &_AcceptBuffer.MagicPacket); + } + else + { + // Wrong header size, close immediately + closesocket(_ClientSocket); + } + _ClientSocket = INVALID_SOCKET; + } + + // Start up another accept request + BeginAccept(); + + MethodReturnVoid(); +} + +HRESULT CSocketListener::Bind() +{ + MethodEntry("void"); + + // Create a SOCKET for connecting to this server + HRESULT hr = S_OK; + addrinfoW* pResult = NULL; + addrinfoW Hints = {}; + Hints.ai_family = AF_INET6; + Hints.ai_socktype = SOCK_STREAM; + Hints.ai_flags = AI_PASSIVE; + + // Resolve the server address and port + if (GetAddrInfoW(NULL, L"9299", &Hints, &pResult) != ERROR_SUCCESS ) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + + if (SUCCEEDED(hr)) + { + // Create a SOCKET for connecting to server + _ListenSocket = socket(AF_INET6, SOCK_STREAM, 0); + if (_ListenSocket == INVALID_SOCKET) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + + if (SUCCEEDED(hr)) + { + hr = SetSocketIpv6Only(_ListenSocket, FALSE); + } + + if (SUCCEEDED(hr)) + { + // Setup the TCP listening socket + if (bind(_ListenSocket, pResult->ai_addr, (int)pResult->ai_addrlen) == SOCKET_ERROR) + { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + } + FreeAddrInfoW(pResult); + } + + MethodReturnHR(hr); +} + diff --git a/nfp/net/driver/socketlistener.h b/nfp/net/driver/socketlistener.h new file mode 100644 index 00000000..dffb11a2 --- /dev/null +++ b/nfp/net/driver/socketlistener.h @@ -0,0 +1,84 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Abstract: + + Declares a socket listener class + +Author: + + Travis Martin (TravM) 06-24-2010 + +Environment: + + User-mode only. + +--*/ +#pragma once + +struct ACCEPT_BUFFER +{ + GUID MagicPacket; + + SOCKADDR_STORAGE DestAddress; + SOCKADDR_STORAGE SourceAddress; +}; + +interface IValidateAccept +{ + virtual void ValidateAccept(_In_ SOCKET Socket, _In_ GUID* pMagicPacket) = 0; +}; + +class CSocketListener +{ +public: + CSocketListener() : + _pValidator(NULL), + _ThreadpoolIo(NULL), + _ListenSocket(INVALID_SOCKET), + _ClientSocket(INVALID_SOCKET) + { + ZeroMemory(&_Overlapped, sizeof(_Overlapped)); + } + + ~CSocketListener() + { + StopAccepting(); + } + +public: + HRESULT Bind(); + HRESULT EnableAccepting(_In_ IValidateAccept* pValidator); + void StopAccepting(); + +private: + + HRESULT BeginAccept(); + void AcceptThreadProc(_In_ HRESULT hr, _In_ ULONG_PTR cbReceived); + static void CALLBACK s_AcceptThreadProc( + _Inout_ PTP_CALLBACK_INSTANCE /*Instance*/, + _Inout_ PVOID Context, + _Inout_opt_ PVOID /*Overlapped*/, + _In_ ULONG IoResult, + _In_ ULONG_PTR NumberOfBytesTransferred, + _Inout_ PTP_IO /*Io*/) + { + CSocketListener* pSocketListener = (CSocketListener*)Context; + pSocketListener->_ThreadpoolThreadId = GetCurrentThreadId(); + pSocketListener->AcceptThreadProc(HRESULT_FROM_WIN32(IoResult), NumberOfBytesTransferred); + pSocketListener->_ThreadpoolThreadId = 0; + } + +private: + ACCEPT_BUFFER _AcceptBuffer; + + OVERLAPPED _Overlapped; + volatile PTP_IO _ThreadpoolIo; + DWORD _ThreadpoolThreadId; + + IValidateAccept* _pValidator; + + SOCKET _ListenSocket; + SOCKET _ClientSocket; +}; diff --git a/nfp/net/driver/wppdefs.h b/nfp/net/driver/wppdefs.h new file mode 100644 index 00000000..da0a176e --- /dev/null +++ b/nfp/net/driver/wppdefs.h @@ -0,0 +1,452 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Abstract: + + WPP Macro definitions. + +Author: + + Travis Martin (TravM) + +--*/ + +// +// Helpful macros +// + +#ifndef WIDEN2 +#define WIDEN2(x) L ## x +#define WIDEN(x) WIDEN2(x) +#endif + + +// +// WPP definitions. Listed below is a set of WPP Trace macros. The comments +// between "//begin_wpp config" and "//end_wpp" are used by the WPP pre-processor +// to create the *.tmh files +// + + +#define PROXIMITY_COMMON_TRACE L"Microsoft\\Windows\\ProximityCommon" + +#ifndef TRACE_CONTROL_GUID +#define TRACE_CONTROL_GUID (93bfc19b, a967, 4339, a3e6, 3a4cc30681d1) +#endif + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(PROXIMITY, TRACE_CONTROL_GUID, \ + WPP_DEFINE_BIT(EntryExit) \ + WPP_DEFINE_BIT(AllocFree) \ + WPP_DEFINE_BIT(Info) \ + WPP_DEFINE_BIT(Warning) \ + WPP_DEFINE_BIT(Error) \ + \ + WPP_DEFINE_BIT(NoisyEntryExit) \ + WPP_DEFINE_BIT(NoisyAllocFree) \ + WPP_DEFINE_BIT(NoisyInfo) \ + WPP_DEFINE_BIT(NoisyWarning)) + + +// +// Used for trace messages indentation +// +const char __indentSpacer[] = +" " +" " +" " +" " +" " +" "; + +#define INDENT_STR(indent) \ + (__indentSpacer + ((sizeof(__indentSpacer) >= (indent)*5) ? (sizeof(__indentSpacer)-2-(indent)*5) : 0)) + + +//--------------------------------------------------------------------------- +// Stores a pointer to current tracing context +//--------------------------------------------------------------------------- +extern DWORD __g_tracingTlsSlot; + +//--------------------------------------------------------------------------- +// This macro declares the global variable that will store TLS index for the +// tracing context pointer. +//--------------------------------------------------------------------------- +#define DECLARE_TRACING_TLS DWORD __g_tracingTlsSlot = TLS_OUT_OF_INDEXES + +//--------------------------------------------------------------------------- +// To be used only within non-WDTF EXE init routines or by the Tracer. +//--------------------------------------------------------------------------- +inline bool TracingTlsInitialize() +{ + if (__g_tracingTlsSlot == TLS_OUT_OF_INDEXES) + { + __g_tracingTlsSlot = TlsAlloc(); + if (__g_tracingTlsSlot == TLS_OUT_OF_INDEXES) + { + // Return error: cannot allocate TLS slot + return false; + } + } + return true; +} + +//--------------------------------------------------------------------------- +// To be used only within non-WDTF EXE exit routines or by the Tracer. +//--------------------------------------------------------------------------- +inline void TracingTlsFree() +{ + if (__g_tracingTlsSlot != TLS_OUT_OF_INDEXES) + { + TlsFree(__g_tracingTlsSlot); + __g_tracingTlsSlot = TLS_OUT_OF_INDEXES; + } +} + + +namespace TracingInternal +{ + +//--------------------------------------------------------------------------- +// Used for storing current tracing context within a TLS slot +//--------------------------------------------------------------------------- +struct TracingContext +{ + ULONG CallDepth; // Current depth of internal calls + DWORD Context; // A context value (used to correlate scenarios that cross-threads) +}; + +//--------------------------------------------------------------------------- +// Auto-incrementing and decrementing variable +//--------------------------------------------------------------------------- +class AutoStackDepth +{ +public: + __forceinline AutoStackDepth(ULONG *pCallDepth) + : _pCallDepth(pCallDepth) + { + WUDF_SAMPLE_DRIVER_ASSERT(_pCallDepth); + if (_pCallDepth) + { + ++*_pCallDepth; + } + } + + __forceinline ~AutoStackDepth() + { + if (_pCallDepth) + { + --*_pCallDepth; + } + } + +private: + AutoStackDepth(AutoStackDepth& rh); + const AutoStackDepth& operator =(AutoStackDepth& rh); + +private: + ULONG* _pCallDepth; +}; + +//--------------------------------------------------------------------------- +// Sets new value to a variable but saves old value and restores it on +// destrutcion +//--------------------------------------------------------------------------- +template +class AutoRestoredValue +{ +public: + __forceinline AutoRestoredValue(T* pVar, T newVal) + : _pVar(pVar) + , _oldVal() + { + WUDF_SAMPLE_DRIVER_ASSERT(_pVar); + if (_pVar) + { + _oldVal = *_pVar; + *_pVar = newVal; + } + } + + __forceinline ~AutoRestoredValue() + { + if (_pVar) + { + *_pVar = _oldVal; + } + } + +private: + AutoRestoredValue(AutoRestoredValue& rh); + const AutoRestoredValue& operator =(AutoRestoredValue& rh); + +private: + + T* _pVar; + T _oldVal; +}; + +//--------------------------------------------------------------------------- +// Auto-pointer stored in TLS +//--------------------------------------------------------------------------- +template +class AutoTlsPtr +{ +public: + __forceinline AutoTlsPtr() + : _dwSlotIndex(TLS_OUT_OF_INDEXES) + { + } + + __forceinline ~AutoTlsPtr() + { + if (_dwSlotIndex != TLS_OUT_OF_INDEXES) + { + LPVOID pCtx = TlsGetValue(_dwSlotIndex); + if (pCtx) + { + TlsSetValue(_dwSlotIndex, NULL); + } + } + } + + __forceinline Attach(Pointee* pCtx, DWORD dwSlotIndex) + { + _dwSlotIndex = dwSlotIndex; + TlsSetValue(_dwSlotIndex, pCtx); + } + +private: + + DWORD _dwSlotIndex; +}; + +} + + +//--------------------------------------------------------------------------- +// This macro should be used at entry point of all functions with tracing. +// It reads from a Tracing context structure stored in the TLS. +// If the slot contains a NULL a new TracingContext is used. An object +// is created that increments CallDepth and auto-decrements it on function exit. +//--------------------------------------------------------------------------- +#define USE_DEFAULT_TRACING_CONTEXT \ + TracingInternal::TracingContext* __pCtx = (TracingInternal::TracingContext*)TlsGetValue(__g_tracingTlsSlot); \ + TracingInternal::AutoTlsPtr __autoTlsPtr; \ + TracingInternal::TracingContext __ctx; \ + if (__pCtx == NULL) \ + { \ + __pCtx = &__ctx; \ + __pCtx->CallDepth = 0; \ + __autoTlsPtr.Attach(__pCtx, __g_tracingTlsSlot); \ + } \ + TracingInternal::AutoStackDepth __autoStackDepth(&__pCtx->CallDepth); + + +//MACRO: MethodEntry +// +//begin_wpp config +//USEPREFIX (MethodEntry, "%!STDPREFIX!%s-->this(%p):%!FUNC!(", INDENT_STR(__pCtx->CallDepth), this); +//FUNC MethodEntry{ENTRYLEVEL=EntryExit}(MSG, ...); +//USESUFFIX (MethodEntry, ")"); +//end_wpp +#define WPP_ENTRYLEVEL_ENABLED(LEVEL) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_ENTRYLEVEL_LOGGER(LEVEL) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_ENTRYLEVEL_PRE(LEVEL) USE_DEFAULT_TRACING_CONTEXT; + + +//MACRO: MethodReturn +// +//begin_wpp config +//USEPREFIX (MethodReturn, "%!STDPREFIX!%s<--this(%p):%!FUNC!(): ", INDENT_STR(__pCtx->CallDepth), this); +//FUNC MethodReturn{RETURNLEVEL=EntryExit}(RET, MSG, ...); +//end_wpp +#define WPP_RETURNLEVEL_RET_ENABLED(LEVEL, Ret) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_RETURNLEVEL_RET_LOGGER(LEVEL, Ret) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_RETURNLEVEL_RET_POST(LEVEL, Ret) ;return Ret; + + +//MACRO: MethodReturnHR +// +//begin_wpp config +//USEPREFIX (MethodReturnHR, "%!STDPREFIX!%s<--this(%p):%!FUNC!(): %!HRESULT!", INDENT_STR(__pCtx->CallDepth), this, __hr); +//FUNC MethodReturnHR{RETURNHRLEVEL=EntryExit}(HR); +//end_wpp +#define WPP_RETURNHRLEVEL_HR_ENABLED(LEVEL, hr) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_RETURNHRLEVEL_HR_LOGGER(LEVEL, hr) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_RETURNHRLEVEL_HR_PRE(LEVEL, hr) { \ + HRESULT __hr = hr; +#define WPP_RETURNHRLEVEL_HR_POST(LEVEL, hr) /*TraceMessage()*/; \ + return __hr; \ + } +//MACRO: MethodReturnVoid +// +//begin_wpp config +//USEPREFIX (MethodReturnVoid, "%!STDPREFIX!%s<--this(%p):%!FUNC!()", INDENT_STR(__pCtx->CallDepth), this); +//FUNC MethodReturnVoid{RETURNVOIDLEVEL=EntryExit}(...); +//end_wpp +#define WPP_RETURNVOIDLEVEL_ENABLED(LEVEL) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_RETURNVOIDLEVEL_LOGGER(LEVEL) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_RETURNVOIDLEVEL_POST(LEVEL) ;return; + + +//MACRO: MethodReturnBool +// +//begin_wpp config +//USEPREFIX (MethodReturnBool, "%!STDPREFIX!%s<--this(%p):%!FUNC!(): %!bool!", INDENT_STR(__pCtx->CallDepth), this, __bRet); +//FUNC MethodReturnBool{RETURNBOOLLEVEL=EntryExit}(BOOLRETVAL); +//end_wpp +#define WPP_RETURNBOOLLEVEL_BOOLRETVAL_ENABLED(LEVEL, bRet) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_RETURNBOOLLEVEL_BOOLRETVAL_LOGGER(LEVEL, bRet) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_RETURNBOOLLEVEL_BOOLRETVAL_PRE(LEVEL, bRet) { \ + bool __bRet = (bRet ? true : false); +#define WPP_RETURNBOOLLEVEL_BOOLRETVAL_POST(LEVEL, bRet) /*TraceMessage()*/; \ + return __bRet; \ + } +//MACRO: MethodReturnPtr +// +//begin_wpp config +//USEPREFIX (MethodReturnPtr, "%!STDPREFIX!%s<--this(%p):%!FUNC!(): %p", INDENT_STR(__pCtx->CallDepth), this, __ptrRetVal); +//FUNC MethodReturnPtr{RETURNPTRLEVEL=EntryExit}(TYPE, PRET); +//end_wpp +#define WPP_RETURNPTRLEVEL_TYPE_PRET_ENABLED(LEVEL, Type, pRet) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_RETURNPTRLEVEL_TYPE_PRET_LOGGER(LEVEL, Type, pRet) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_RETURNPTRLEVEL_TYPE_PRET_PRE(LEVEL, Type, pRet) { \ + Type __pRet = pRet; +#define WPP_RETURNPTRLEVEL_TYPE_PRET_POST(LEVEL, Type, pRet) /*TraceMessage()*/; \ + return __pRet; \ + } +//MACRO: MethodReturnIfNull +// +//begin_wpp config +//USEPREFIX (MethodReturnIfNull, "%!STDPREFIX!%s<-this(%p):%!FUNC!(): E_POINTER %s=NULL, bailing out!", INDENT_STR(__pCtx->CallDepth), this, #PTR); +//FUNC MethodReturnIfNull{METHOD_POINTER_LEVEL=EntryExit}(PTR); +//end_wpp +#define WPP_METHOD_POINTER_LEVEL_PTR_ENABLED(LEVEL, PTR) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_METHOD_POINTER_LEVEL_PTR_LOGGER(LEVEL, PTR) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_METHOD_POINTER_LEVEL_PTR_PRE(LEVEL, PTR) if ((PTR) == NULL) \ + { +#define WPP_METHOD_POINTER_LEVEL_PTR_POST(LEVEL, PTR) /*TraceMessage()*/; \ + return E_POINTER; \ + } +//MACRO: FunctionEntry +// +//begin_wpp config +//USEPREFIX (FunctionEntry, "%!STDPREFIX!%s-->%!FUNC!(", INDENT_STR(__pCtx->CallDepth)); +//FUNC FunctionEntry{FUNCENTRYLEVEL=EntryExit}(MSG, ...); +//USESUFFIX (FunctionEntry, ")"); +//end_wpp +#define WPP_FUNCENTRYLEVEL_ENABLED(LEVEL) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_FUNCENTRYLEVEL_LOGGER(LEVEL) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_FUNCENTRYLEVEL_PRE(LEVEL) USE_DEFAULT_TRACING_CONTEXT; + + +//MACRO: FunctionReturn +// +//begin_wpp config +//USEPREFIX (FunctionReturn, "%!STDPREFIX!%s<--%!FUNC!(): ", INDENT_STR(__pCtx->CallDepth)); +//FUNC FunctionReturn{FUNCRETURNLEVEL=EntryExit}(RET, MSG, ...); +//end_wpp +#define WPP_FUNCRETURNLEVEL_RET_ENABLED(LEVEL, Ret) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_FUNCRETURNLEVEL_RET_LOGGER(LEVEL, Ret) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_FUNCRETURNLEVEL_RET_POST(LEVEL, Ret) ;return Ret; + + +//MACRO: FunctionReturnHR +// +//begin_wpp config +//USEPREFIX (FunctionReturnHR, "%!STDPREFIX!%s<--%!FUNC!(): %!HRESULT!", INDENT_STR(__pCtx->CallDepth), __hr); +//FUNC FunctionReturnHR{FUNCRETURNHRLEVEL=EntryExit}(HR); +//end_wpp +#define WPP_FUNCRETURNHRLEVEL_HR_ENABLED(LEVEL, hr) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_FUNCRETURNHRLEVEL_HR_LOGGER(LEVEL, hr) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_FUNCRETURNHRLEVEL_HR_PRE(LEVEL, hr) { \ + HRESULT __hr = hr; +#define WPP_FUNCRETURNHRLEVEL_HR_POST(LEVEL, hr) /*TraceMessage()*/; \ + return __hr; \ + } +//MACRO: FunctionReturnVoid +// +//begin_wpp config +//USEPREFIX (FunctionReturnVoid, "%!STDPREFIX!%s<--%!FUNC!()", INDENT_STR(__pCtx->CallDepth)); +//FUNC FunctionReturnVoid{FUNCRETURNLEVELVOID=EntryExit}(...); +//end_wpp +#define WPP_FUNCRETURNLEVELVOID_ENABLED(LEVEL) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_FUNCRETURNLEVELVOID_LOGGER(LEVEL) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_FUNCRETURNLEVELVOID_POST(LEVEL) ;return; + + +//MACRO: FunctionReturnBool +// +//begin_wpp config +//USEPREFIX (FunctionReturnBool, "%!STDPREFIX!%s<--%!FUNC!(): %!bool!", INDENT_STR(__pCtx->CallDepth), __bRet); +//FUNC FunctionReturnBool{FUNCRETURNLEVELBOOL=EntryExit}(BOOLRETVAL); +//end_wpp +#define WPP_FUNCRETURNLEVELBOOL_BOOLRETVAL_ENABLED(LEVEL, bRet) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_FUNCRETURNLEVELBOOL_BOOLRETVAL_LOGGER(LEVEL, bRet) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_FUNCRETURNLEVELBOOL_BOOLRETVAL_PRE(LEVEL, bRet) { \ + bool __bRet = (bRet ? true : false); +#define WPP_FUNCRETURNLEVELBOOL_BOOLRETVAL_POST(LEVEL, bRet) /*TraceMessage()*/; \ + return __bRet; \ + } +//MACRO: FunctionReturnPtr +// +//begin_wpp config +//USEPREFIX (FunctionReturnPtr, "%!STDPREFIX!%s<--%!FUNC!(): %p", INDENT_STR(__pCtx->CallDepth), __pRet); +//FUNC FunctionReturnPtr{FUNCRETURNLEVELPTR=EntryExit}(TYPE, PRET); +//end_wpp +#define WPP_FUNCRETURNLEVELPTR_TYPE_PRET_ENABLED(LEVEL, Type, pRet) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_FUNCRETURNLEVELPTR_TYPE_PRET_LOGGER(LEVEL, Type, pRet) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_FUNCRETURNLEVELPTR_TYPE_PRET_PRE(LEVEL, Type, pRet) { \ + Type __pRet = pRet; +#define WPP_FUNCRETURNLEVELPTR_TYPE_PRET_POST(LEVEL, Type, pRet) /*TraceMessage()*/; \ + return __pRet; \ + } + + + +// Define Non-empty debug break for checked builds only +#ifndef NDEBUG + #define DEBUG_BREAK() __debugbreak() +#else + #define DEBUG_BREAK() do {} while (false) +#endif + + +//MACRO: TraceASSERT +// +// ASSERT with tracing +// +//begin_wpp config +//USEPREFIX (TraceASSERT, "%!STDPREFIX!%sWARN: ASSERTION FAILED - expression \"%s\" is false.", INDENT_STR(__pCtx->CallDepth+1), #EXPR); +//FUNC TraceASSERT{ASSERTLEVEL=Warning}(EXPR); +//end_wpp +#define WPP_ASSERTLEVEL_EXPR_ENABLED(LEVEL, EXPR) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_ASSERTLEVEL_EXPR_LOGGER(LEVEL, EXPR) WPP_LEVEL_LOGGER(LEVEL) +#define WPP_ASSERTLEVEL_EXPR_PRE(LEVEL, EXPR) if (!(EXPR)) \ + { +#define WPP_ASSERTLEVEL_EXPR_POST(LEVEL, EXPR) /*TraceMessage()*/; \ + WUDF_SAMPLE_DRIVER_ASSERT(FALSE); \ + } + +//MACRO: TraceErrorHR +// +// ERROR trace +// +//begin_wpp config +//USEPREFIX (TraceErrorHR, "%!STDPREFIX!%sERROR: ", INDENT_STR(__pCtx->CallDepth+1)); +//FUNC TraceErrorHR{ERRORLEVEL=Error}(HR, MSG, ...); +//USESUFFIX (TraceErrorHR, ": %!HRESULT!", HR); +//end_wpp +#define WPP_ERRORLEVEL_HR_ENABLED(LEVEL, HR) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_ERRORLEVEL_HR_LOGGER(LEVEL, HR) WPP_LEVEL_LOGGER(LEVEL) + +//MACRO: TraceInfo +// +//begin_wpp config +//USEPREFIX (TraceInfo, "%!STDPREFIX!%s", INDENT_STR(__pCtx->CallDepth+1)); +//FUNC TraceInfo{INFOLEVEL=Info}(MSG, ...); +//end_wpp +#define WPP_INFOLEVEL_ENABLED(LEVEL) WPP_LEVEL_ENABLED(LEVEL) +#define WPP_INFOLEVEL_LOGGER(LEVEL) WPP_LEVEL_LOGGER(LEVEL) \ No newline at end of file diff --git a/nfp/net/exe/NetNfpControl.cpp b/nfp/net/exe/NetNfpControl.cpp new file mode 100644 index 00000000..b101d19e --- /dev/null +++ b/nfp/net/exe/NetNfpControl.cpp @@ -0,0 +1,579 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Module Name: NetNfpControl.cpp +// Abstract: Windows Near-field Proximity Test tool. Designed for simulating proximity hardware. +// +// NetNfpControl console app allows control of the NetNfpProvider test driver. +// Both the local and remote machine must have the NetNfpProvider driver installed. +// +// *USAGE* +// NetNfpControl.exe [/e] +// NetNfpControl.exe [] [/k] +// NetNfpControl.exe /q +// +// Example: NetNfpControl.exe John-PC1 +// The first operating mode allows the user to specify a remote machine name (or IPv6 address) +// that the local machine should connect to and simulate proximity with. After it's connected, +// a simple key-press ends the simulated proximity link. The console app then exits. +// If the option /e is specified, rather than the waiting for a key to be pressed, the tool +// waits for the QUIT_NAMED_EVENT named event to be set. The named event can be set by running +// NetNfpControl.exe /q. +// +// Example: NetNfpControl.exe John-PC1 /k +// A second operating mode keeps the console app running with a Ctrl-F1 hotkey registered. +// This hot key remains registered and functional even when the app is in the background. +// When the hot key is intercepted, a near-field proximity event is simulated directly with +// the specified remote machine. +// Note: The console app needs to be running (only one one machine) to intercept the system hot key. +// +// Example: NetNfpControl.exe /k +// A third operating mode also keeps the console app running with a Ctrl-F1 hotkey registered. +// However, you'll have to run this on two or more machines at the same time. Pressing Ctrl-F1 +// on any two machines at the same time causes the machines to exchange their network name via +// a file on a private share with a special file name. +// - The server share used is hard coded to: \\scratch2\scratch\travm\proxrendezvous\ +// - Either create a file server with these folders shared, or change this to match yours. +// - The file has an effective lifetime of 2 seconds. +// - In the event of a collision (two clients posting an event during the same interval), +// only one client 'wins'. +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma region Includes +#include "precomp.h" +#pragma endregion + + +#pragma region Globals +wchar_t g_szMachineName[MAX_PATH]; +PCWSTR g_pszRemoteMachineName = nullptr; +#pragma endregion + +#define QUIT_NAMED_EVENT L"NetNfpControl_Quit_Event" + + +//---------------------------------------------------------------------------------------------------------------------- +// Name: DEVICE_INTERFACE_DETAIL +// Comments: +// +//---------------------------------------------------------------------------------------------------------------------- +///Device interface details. +struct DEVICE_INTERFACE_DETAIL +{ + DWORD cbSize; + wchar_t szSymbolicLink[MAX_PATH*2]; +}; + +//---------------------------------------------------------------------------------------------------------------------- +// Name: BeginProximity +// Comments: +// +//---------------------------------------------------------------------------------------------------------------------- +///Initalizes a proximity event. +/// +/// +HRESULT BeginProximity(_In_ PCWSTR pszName, _Out_ HANDLE* pHandle) +{ + HRESULT hr = S_OK; + LPGUID pGuid = (LPGUID) &GUID_DEVINTERFACE_NETNFP; + + HDEVINFO hDevSet = SetupDiGetClassDevs(pGuid, nullptr, nullptr, (DIGCF_PRESENT | DIGCF_INTERFACEDEVICE)); + if (hDevSet == INVALID_HANDLE_VALUE) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + DEVICE_INTERFACE_DETAIL deviceInterfaceDetail = {}; + if (SUCCEEDED(hr)) + { + SP_DEVICE_INTERFACE_DATA devInterfaceData = {sizeof(devInterfaceData)}; + if (!SetupDiEnumDeviceInterfaces(hDevSet, nullptr, pGuid, 0, &devInterfaceData)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + if (SUCCEEDED(hr)) + { + PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)&deviceInterfaceDetail; + pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + if (!SetupDiGetDeviceInterfaceDetail(hDevSet, &devInterfaceData, pDetail, + sizeof(deviceInterfaceDetail), nullptr, nullptr)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + + SetupDiDestroyDeviceInfoList(hDevSet); + } + + HANDLE hProximity = INVALID_HANDLE_VALUE; + if (SUCCEEDED(hr)) + { + hProximity = CreateFile(deviceInterfaceDetail.szSymbolicLink, GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hProximity == INVALID_HANDLE_VALUE) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + + BEGIN_PROXIMITY_ARGS args = {}; + if (SUCCEEDED(hr)) + { + hr = StringCchCopy(args.szName, MAX_PATH, pszName); + } + + if (SUCCEEDED(hr)) + { + DWORD ignore; + if (!DeviceIoControl(hProximity, IOCTL_BEGIN_PROXIMITY, &args, sizeof(args), nullptr, 0, &ignore, nullptr)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + + if (FAILED(hr)) + { + if (hProximity != INVALID_HANDLE_VALUE) + { + CloseHandle(hProximity); + hProximity = INVALID_HANDLE_VALUE; + } + } + + *pHandle = hProximity; + + return hr; +} + +//---------------------------------------------------------------------------------------------------------------------- +// Name: AcquireFileLock +// Comments: +// +//---------------------------------------------------------------------------------------------------------------------- +///Aquires a lock on a file. +/// +/// +HANDLE AcquireFileLock(PCWSTR pszLockFilePath) +{ + HANDLE hLock = INVALID_HANDLE_VALUE; + for (int i = 0; i < 200; i++) + { + wprintf(L"Attempting to Acquire Lock: %u\n", (DWORD)GetTickCount64()); + hLock = CreateFile(pszLockFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (hLock == INVALID_HANDLE_VALUE) + { + Sleep(50); + } + else + { + wprintf(L"Proximity Lock Acquired: %u\n", (DWORD)GetTickCount64()); + break; + } + } + + if (hLock == INVALID_HANDLE_VALUE) + { + wprintf(L"Proximity Lock Not Acquired, check network connectivity Error = %u.\n", GetLastError()); + } + + return hLock; +} + +//---------------------------------------------------------------------------------------------------------------------- +// Name: Proximity +// Comments: +// +//---------------------------------------------------------------------------------------------------------------------- +///Initiates a proximity event. +/// +/// +void Proximity() +{ + wprintf(L"Checking For Proximate Device\n"); + + SYSTEMTIME sysTime = {}; + GetLocalTime(&sysTime); + + wchar_t szDirectory[MAX_PATH]; + StringCchPrintf(szDirectory, MAX_PATH, L"\\\\scratch2\\scratch\\travm\\proxrendezvous\\%u%02u%02u-%02u", + sysTime.wYear, sysTime.wMonth, sysTime.wDay, sysTime.wHour); + + CreateDirectory(szDirectory, NULL); + + wchar_t szLockPath[MAX_PATH]; + StringCchPrintf(szLockPath, MAX_PATH, L"%s\\lock.txt", szDirectory); + + HANDLE hLock = AcquireFileLock(szLockPath); + + wchar_t szOtherMachine[MAX_PATH] = {}; + if (hLock != INVALID_HANDLE_VALUE) + { + wprintf(L"looking for available devices in proximity.\n"); + wchar_t szFindPath[MAX_PATH]; + StringCchPrintf(szFindPath, MAX_PATH, L"%s\\*.available", szDirectory); + WIN32_FIND_DATA findData; + HANDLE hFind = FindFirstFile(szFindPath, &findData); + + wchar_t szFilePath[MAX_PATH]; + bool fClient; + if (hFind != INVALID_HANDLE_VALUE) + { + fClient = true; + + wprintf(L"Proximate device found: "); + StringCchPrintf(szFilePath, MAX_PATH, L"%s\\%s", szDirectory, findData.cFileName); + HANDLE hFile = CreateFile(szFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + DWORD ignore; + (void)ReadFile(hFile, szOtherMachine, sizeof(szOtherMachine) - sizeof(wchar_t), &ignore, NULL); + szOtherMachine[MAX_PATH-1] = L'\0'; + CloseHandle(hFile); + } + + if (szOtherMachine[0] != L'\0') + { + wprintf(L"%s\n", szOtherMachine); + wchar_t szNewFileName[MAX_PATH]; + StringCchPrintf(szNewFileName, MAX_PATH, L"%s\\%s.%s.%02u%02u", szDirectory, szOtherMachine, g_szMachineName, sysTime.wMinute, sysTime.wSecond); + MoveFile(szFilePath, szNewFileName); + } + } + else + { + fClient = false; + + wprintf(L"No proximate device found yet. Placing proximity marker on share...\n"); + StringCchPrintf(szFilePath, MAX_PATH, L"%s\\%s.available", szDirectory, g_szMachineName); + HANDLE hFile = CreateFile(szFilePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + DWORD ignore; + WriteFile(hFile, g_szMachineName, sizeof(g_szMachineName), &ignore, NULL); + CloseHandle(hFile); + } + + FindClose(hFind); + hFind = INVALID_HANDLE_VALUE; + + CloseHandle(hLock); + DeleteFile(szLockPath); + + if (fClient) + { + HANDLE hProximity; + HRESULT hr = BeginProximity(szOtherMachine, &hProximity); + if (SUCCEEDED(hr)) + { + wprintf(L"In Proximity for 1 second...\n"); + Sleep(1000); + CloseHandle(hProximity); + wprintf(L"Proximity Complete.\n"); + } + else + { + wprintf(L"ERROR: BeginProximity() failed: 0x%x\n", hr); + } + } + else + { + wprintf(L"Other machine will initiate proximity...\n"); + Sleep(2000); + + bool fCompleted = true; + hLock = AcquireFileLock(szLockPath); + if (hLock != INVALID_HANDLE_VALUE) + { + wchar_t szNewFileName[MAX_PATH]; + StringCchPrintf(szNewFileName, MAX_PATH, L"%s\\%s.%02u%02u.expired", szDirectory, g_szMachineName, sysTime.wMinute, sysTime.wSecond); + if (MoveFile(szFilePath, szNewFileName)) + { + fCompleted = false; + } + + CloseHandle(hLock); + DeleteFile(szLockPath); + } + + if (fCompleted) + { + wprintf(L"Proximity Successful!\n"); + } + else + { + wprintf(L"ERROR: Proximity Unsuccessful. No proximate device found!\n"); + } + } + } +} + +//---------------------------------------------------------------------------------------------------------------------- +// Name: WndProc +// Comments: +// +//---------------------------------------------------------------------------------------------------------------------- +///Windows call-back procedure. +/// +///Used to receive call-backs for the Windows system-wide Hot Key's +/// +LRESULT CALLBACK WndProc( + HWND hwnd, // handle to window + UINT uMsg, // message identifier + WPARAM wParam, // first message parameter + LPARAM lParam) // second message parameter +{ + switch (uMsg) + { + case WM_HOTKEY: + { + if (g_pszRemoteMachineName != nullptr) + { + HANDLE hProximity; + HRESULT hr = BeginProximity(g_pszRemoteMachineName, &hProximity); + if (SUCCEEDED(hr)) + { + wprintf(L"In Proximity for 1 second...\n"); + Sleep(1000); + CloseHandle(hProximity); + wprintf(L"Proximity Complete.\n"); + } + else + { + wprintf(L"ERROR: BeginProximity() failed: 0x%x\n", hr); + } + } + else // No machine name specified, need to check for a name on the share + { + Proximity(); + } + } + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + + // + // Process other messages. + // + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } +} + +__analysis_noreturn void Usage() +{ + wprintf(L"Usage: \n"); + wprintf(L" NetNfpControl.exe \n"); + wprintf(L" NetNfpControl.exe /e \n"); + wprintf(L" NetNfpControl.exe /q\n"); + wprintf(L" NetNfpControl.exe /k\n"); + wprintf(L" NetNfpControl.exe /k \n"); + wprintf(L"\n"); + wprintf(L"The /k option registers a hotkey for entering proximity while selfhosting\n"); + wprintf(L"The /e option keeps NetNfpControl.exe running until NetNfpControl.exe /q is called\n" + L" (or until the named event %s is set)\n", QUIT_NAMED_EVENT); + wprintf(L"The /q option sets the the named event %s (and exits), causing \n" + L" all the outstanding instances of 'NetNfpControl.exe /e' to exit\n", QUIT_NAMED_EVENT); + + exit(1); +} + +//---------------------------------------------------------------------------------------------------------------------- +// Name: wmain +// Comments: Main application entry point. +// +//---------------------------------------------------------------------------------------------------------------------- +///wmain. +/// +///Main application entry point. +/// +int _cdecl wmain(_In_ int argc, _In_reads_(argc) PWSTR* argv) +{ + wprintf(L"\n*** Network NearFieldProvider Control Executable ***\n\n"); + if (argc < 2) + { + Usage(); + } + + bool fUseHotKey = false; + bool fWaitOnNamedEvent = false; + bool fQuitGlobalEventWaiters = false; + + for (int i = 1; i < argc; i++) + { + if ((argv[i][0] == L'/') || (argv[i][0] == L'-')) + { + if ((argv[i][1] == L'k') || (argv[i][1] == L'K')) + { + fUseHotKey = true; + } + else if ((argv[i][1] == L'e') || (argv[i][1] == L'E')) + { + fWaitOnNamedEvent = true; + } + else if ((argv[i][1] == L'q') || (argv[i][1] == L'Q')) + { + fQuitGlobalEventWaiters = true; + } + else + { + wprintf(L"*** Unknown command line argument: '%ws' ***\n\n", argv[i]); + Usage(); + } + } + else + { + if (g_pszRemoteMachineName != nullptr) + { + wprintf(L"*** Can't specify two machine names ***\n\n"); + Usage(); + } + g_pszRemoteMachineName = argv[i]; + } + } + + if (fUseHotKey) + { + if (g_pszRemoteMachineName != nullptr) + { + wprintf(L"Press Ctrl-F1 on this machine to initiate proximity with: '%ws'.\n\n", g_pszRemoteMachineName); + } + else + { + // Dynamic Keyboard hotkey version + wprintf(L"Press Ctrl-F1 on two machines at the same time to initiate proximity.\n\n"); + + DWORD cchMachineName = MAX_PATH; + if (!GetComputerNameEx(ComputerNameNetBIOS, g_szMachineName, &cchMachineName)) + { + return FALSE; + } + } + + // Register the window class for the main window. + WNDCLASS wc = {}; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.hInstance = GetModuleHandle(NULL); + wc.lpszMenuName = L"MainMenu"; + wc.lpszClassName = L"MainWndClass"; + + if (!RegisterClass(&wc)) + { + return FALSE; + } + + // Create the main window. + HWND hwndMain = CreateWindow(L"MainWndClass", L"Sample", + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL, + (HMENU) NULL, GetModuleHandle(NULL), (LPVOID) NULL); + + // If the main window cannot be created, terminate + // the application. + if (!hwndMain) + { + return FALSE; + } + + if (!RegisterHotKey(hwndMain, 264334, MOD_CONTROL, VK_F1)) + { + wprintf(L"ERROR: Ctrl-F1 Hotkey already registered!\n"); + return FALSE; + } + + // Start the message loop. + + MSG msg; + BOOL bRet; + while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) + { + if (bRet == -1) + { + // handle the error and possibly exit + } + else + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + // Return the exit code to the system. + + return (int)msg.wParam; + + } + else if (fQuitGlobalEventWaiters) + { + HANDLE hEvent = CreateEventW(NULL, TRUE, FALSE, QUIT_NAMED_EVENT); + if (NULL != hEvent) + { + wprintf(L"Quitting running instances of NetNfpControl.exe /e \n"); + + BOOL bSetErr = SetEvent(hEvent); + if (!bSetErr) + { + wprintf(L"Failed to set named event %s (Err=0x%x)...\n", QUIT_NAMED_EVENT, GetLastError()); + } + + CloseHandle(hEvent); + } + else + { + wprintf(L"Failed to open named event %s (Err=0x%x)...\n", QUIT_NAMED_EVENT, GetLastError()); + } + } + else + { + if (NULL == g_pszRemoteMachineName) + { + Usage(); + } + + wprintf(L"Attempting connect: '%ws' ...\n\n", g_pszRemoteMachineName); + + HANDLE hProximity; + HRESULT hr = BeginProximity(g_pszRemoteMachineName, &hProximity); + if (SUCCEEDED(hr)) + { + if (fWaitOnNamedEvent) + { + wprintf(L"run NetNfpControl.exe /q to end Proximity (or Set the named event %s)\n", QUIT_NAMED_EVENT); + + HANDLE hEvent = CreateEventW(NULL, TRUE, FALSE, QUIT_NAMED_EVENT); + if (NULL != hEvent) + { + DWORD dwWaitErr = WaitForSingleObject(hEvent, INFINITE); + if (WAIT_OBJECT_0 != dwWaitErr) + { + dwWaitErr = (WAIT_FAILED == dwWaitErr ? GetLastError() : dwWaitErr); + wprintf(L"Failed to wait on named event %s (Err=0x%x)...\n", QUIT_NAMED_EVENT, dwWaitErr); + } + + CloseHandle(hEvent); + } + else + { + wprintf(L"Failed to create/open named event %s (Err=0x%x)...\n", QUIT_NAMED_EVENT, GetLastError()); + } + } + else + { + wprintf(L"Press Any Key to End Proximity.\n"); + (void) _getch(); + wprintf(L"Ending Proximity...\n"); + } + + CloseHandle(hProximity); + } + else + { + wprintf(L"BeginProximity() failed: 0x%x\n", hr); + } + } + + return 0; +} + diff --git a/nfp/net/exe/NetNfpControl.vcxproj b/nfp/net/exe/NetNfpControl.vcxproj new file mode 100644 index 00000000..b2eb94ff --- /dev/null +++ b/nfp/net/exe/NetNfpControl.vcxproj @@ -0,0 +1,262 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {4ABF1B83-9726-45DE-B705-A8458BCB0915} + $(MSBuildProjectName) + Debug + Win32 + {B57AFB72-58B6-45CF-8E49-3D56D66A2842} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + NetNfpControl + + + NetNfpControl + + + NetNfpControl + + + NetNfpControl + + + + MultiThreaded + MultiThreadedDebug + + + + + MultiThreaded + MultiThreadedDebug + + + + + MultiThreaded + MultiThreadedDebug + + + + + MultiThreaded + MultiThreadedDebug + + + + Dynamic + + + Dynamic + + + Dynamic + + + Dynamic + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + %(AdditionalDependencies);kernel32.lib;uuid.lib;user32.lib;ntdll.lib;user32.lib;oleacc.lib;SetupAPI.lib;Ws2_32.lib;mswsock.lib + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + %(AdditionalDependencies);kernel32.lib;uuid.lib;user32.lib;ntdll.lib;user32.lib;oleacc.lib;SetupAPI.lib;Ws2_32.lib;mswsock.lib + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + %(AdditionalDependencies);kernel32.lib;uuid.lib;user32.lib;ntdll.lib;user32.lib;oleacc.lib;SetupAPI.lib;Ws2_32.lib;mswsock.lib + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);..\inc;..\driver + + + %(AdditionalDependencies);kernel32.lib;uuid.lib;user32.lib;ntdll.lib;user32.lib;oleacc.lib;SetupAPI.lib;Ws2_32.lib;mswsock.lib + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nfp/net/exe/NetNfpControl.vcxproj.Filters b/nfp/net/exe/NetNfpControl.vcxproj.Filters new file mode 100644 index 00000000..c01e6141 --- /dev/null +++ b/nfp/net/exe/NetNfpControl.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {1C29E1A2-5E3D-4A88-A8C5-2DBBA704EF00} + + + h;hpp;hxx;hm;inl;inc;xsd + {5535C5B0-0DF3-4CF2-ACA9-2F899FA6CE1C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {534D73C8-CECE-4D3E-9B06-469E2C777F49} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/nfp/net/exe/precomp.h b/nfp/net/exe/precomp.h new file mode 100644 index 00000000..4748bcf8 --- /dev/null +++ b/nfp/net/exe/precomp.h @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Module Name: precomp.h +// Abstract: +// +// Precompiled header file for the NetNfpControl console app +// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once + +#ifndef UNICODE +#define UNICODE +#endif + +#ifndef _UNICODE +#define _UNICODE +#endif + +// Windows Headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ATL stuff +#include + +// Common NetNfp header +#include "NetNfp.h" diff --git a/nfp/net/exe/precompsrc.cpp b/nfp/net/exe/precompsrc.cpp new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/nfp/net/exe/precompsrc.cpp @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/nfp/net/inc/NetNfp.h b/nfp/net/inc/NetNfp.h new file mode 100644 index 00000000..b891313e --- /dev/null +++ b/nfp/net/inc/NetNfp.h @@ -0,0 +1,32 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + NetNfp.h + +Abstract: + + This header contains definitions common to the both the NetNfpProvider.sys + driver and the NetNfpControl.exe. + +Environment: + + User Mode + +--*/ +#pragma once + + +/* 2DD081BE-1294-440B-AB9F-F0E9FDD77FBE */ +const GUID GUID_DEVINTERFACE_NETNFP = + {0x2DD081BE, 0x1294, 0x440B, {0xAB, 0x9F, 0xF0, 0xE9, 0xFD, 0xD7, 0x7F, 0xBE}}; + +#define IOCTL_BEGIN_PROXIMITY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1000, METHOD_BUFFERED, FILE_ANY_ACCESS) + +struct BEGIN_PROXIMITY_ARGS +{ + WCHAR szName[MAX_PATH]; // Name or IP address +}; + diff --git a/nfp/net/netnfp.sln b/nfp/net/netnfp.sln new file mode 100644 index 00000000..f8b5f6b2 --- /dev/null +++ b/nfp/net/netnfp.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{6133A073-0A35-45D3-BC68-BA16B0F829D8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{4FD03920-D970-4A51-A2A1-A394DF683A03}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetNfpProvider", "driver\NetNfpProvider.vcxproj", "{756016BF-4187-4482-A0BE-CEE1698BD129}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetNfpControl", "exe\NetNfpControl.vcxproj", "{4ABF1B83-9726-45DE-B705-A8458BCB0915}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {756016BF-4187-4482-A0BE-CEE1698BD129}.Debug|Win32.ActiveCfg = Debug|Win32 + {756016BF-4187-4482-A0BE-CEE1698BD129}.Debug|Win32.Build.0 = Debug|Win32 + {756016BF-4187-4482-A0BE-CEE1698BD129}.Release|Win32.ActiveCfg = Release|Win32 + {756016BF-4187-4482-A0BE-CEE1698BD129}.Release|Win32.Build.0 = Release|Win32 + {756016BF-4187-4482-A0BE-CEE1698BD129}.Debug|x64.ActiveCfg = Debug|x64 + {756016BF-4187-4482-A0BE-CEE1698BD129}.Debug|x64.Build.0 = Debug|x64 + {756016BF-4187-4482-A0BE-CEE1698BD129}.Release|x64.ActiveCfg = Release|x64 + {756016BF-4187-4482-A0BE-CEE1698BD129}.Release|x64.Build.0 = Release|x64 + {4ABF1B83-9726-45DE-B705-A8458BCB0915}.Debug|Win32.ActiveCfg = Debug|Win32 + {4ABF1B83-9726-45DE-B705-A8458BCB0915}.Debug|Win32.Build.0 = Debug|Win32 + {4ABF1B83-9726-45DE-B705-A8458BCB0915}.Release|Win32.ActiveCfg = Release|Win32 + {4ABF1B83-9726-45DE-B705-A8458BCB0915}.Release|Win32.Build.0 = Release|Win32 + {4ABF1B83-9726-45DE-B705-A8458BCB0915}.Debug|x64.ActiveCfg = Debug|x64 + {4ABF1B83-9726-45DE-B705-A8458BCB0915}.Debug|x64.Build.0 = Debug|x64 + {4ABF1B83-9726-45DE-B705-A8458BCB0915}.Release|x64.ActiveCfg = Release|x64 + {4ABF1B83-9726-45DE-B705-A8458BCB0915}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {756016BF-4187-4482-A0BE-CEE1698BD129} = {6133A073-0A35-45D3-BC68-BA16B0F829D8} + {4ABF1B83-9726-45DE-B705-A8458BCB0915} = {4FD03920-D970-4A51-A2A1-A394DF683A03} + EndGlobalSection +EndGlobal diff --git a/pofx/UMDF2/App/PowerFxApp.cpp b/pofx/UMDF2/App/PowerFxApp.cpp new file mode 100644 index 00000000..c628d366 --- /dev/null +++ b/pofx/UMDF2/App/PowerFxApp.cpp @@ -0,0 +1,592 @@ +/*++ + +Copyright (c) Microsoft Corporation + +Module Name: + + PowerFxApp.cpp + +Abstract: + + This application can be used to exercise KMDF sample drivers for the + new power framework. See application "usage" details for more information. + +Environment: + + user mode only + +--*/ + +#include "include.h" + +int __cdecl +wmain( + _In_ int argc, + _In_reads_(argc) PWSTR argv[] + ) +{ + DWORD err; + WCHAR devicePath[MAX_DEVPATH_LENGTH] = {UNICODE_NULL}; + HANDLE hDevice = INVALID_HANDLE_VALUE; + HANDLE hCompletionPort = NULL; + + + // + // Process user input. + // + err = ProcessUserInput(argc, argv); + if (ERROR_SUCCESS != err) + { + goto clean0; + } + + if ( !GetDevicePath( + (LPGUID) &GUID_DEVINTERFACE_POWERFX, + devicePath, + COUNT_OF(devicePath))) + { + printf("Unable to get device path. Has the device driver been installed? \n"); + err = ERROR_OPEN_FAILED; + goto clean0; + } + + hDevice = CreateFile(devicePath, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL ); + + if (hDevice == INVALID_HANDLE_VALUE) { + err = GetLastError(); + printf("Failed to open device. Error %d.\n",err); + goto clean0; + } + + hCompletionPort = CreateIoCompletionPort(hDevice, NULL, 1, 0); + if (hCompletionPort == NULL) { + err = GetLastError(); + printf("Cannot open completion port %d.\n",err); + goto clean0; + } + + err = SendIO(hDevice, + hCompletionPort, + GetSetting(COMPONENT), + GetSetting(MAX_OUTSTANDING_IO), + GetSetting(DELAY), + (BOOLEAN)GetSetting(CANCEL)); + if (ERROR_SUCCESS != err) + { + goto clean0; + } + +clean0: + if (INVALID_HANDLE_VALUE != hDevice) + { + CloseHandle(hDevice); + } + if (NULL != hCompletionPort) + { + CloseHandle(hCompletionPort); + } + return err; +} + +DWORD Initialize( + _In_ ULONG Count, + _Out_ LPOVERLAPPED *pOverlappedPtr, + _Out_ PPOWERFX_READ_COMPONENT_INPUT *pInput, + _Out_ PPOWERFX_READ_COMPONENT_OUTPUT *pOutput + ) +{ + PPOWERFX_READ_COMPONENT_INPUT inputBuffer = NULL; + PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer = NULL; + LPOVERLAPPED pOverlapped = NULL; + DWORD err = ERROR_SUCCESS; + + pOverlapped = new OVERLAPPED[Count]; + if (NULL == pOverlapped) + { + err = ERROR_OUTOFMEMORY; + goto clean0; + } + + inputBuffer = new POWERFX_READ_COMPONENT_INPUT[Count]; + if (NULL == inputBuffer) + { + err = ERROR_OUTOFMEMORY; + goto clean0; + } + + outputBuffer = new POWERFX_READ_COMPONENT_OUTPUT[Count]; + if (NULL == outputBuffer) + { + err = ERROR_OUTOFMEMORY; + goto clean0; + } + + ZeroMemory(pOverlapped, + sizeof(OVERLAPPED)*Count); + + for (UINT i = 0; i < Count; i++) + { + // + // When the component number is set to UNUSED it indicates + // that a request has not been issued (or has completed) + // using this input buffer. Hence the buffer along with the + // overlapped structure at the corresponding index is available + // for issuing a request. + // + inputBuffer[i].ComponentNumber = UNUSED; + } + + ZeroMemory(outputBuffer, + sizeof(POWERFX_READ_COMPONENT_OUTPUT)*Count); + +clean0: + if (err != ERROR_SUCCESS) + { + delete[] pOverlapped; + delete[] inputBuffer; + delete[] outputBuffer; + } + else + { + *pOverlappedPtr = pOverlapped; + *pInput = inputBuffer; + *pOutput = outputBuffer; + } + return err; +} + +/*++ + +Routine Description: + + This function sends requests to the driver based on the settings passed + in the arguments. The requests are sent indefinitely until an error occurs. + + Depending on the number of maximum outstanding I/O requests, + an array of overlapped structures and input/output buffers is allocated. + The method then loops through the overlapped structure array + to issue asynchronous requests. When a request completes, the + overlapped structure for that request is not immediately re-used to issue + a new request. Instead the method goes in-order through the array to + ensure that each issued request is completed in a reasonable amount + of time and it is able to detect if one or more requests do not complete + at all (or within the specified timeout). + +--*/ +DWORD SendIO( + _In_ HANDLE DeviceHandle, + _In_ HANDLE CompletionPortHandle, + _In_ ULONG Component, + _In_ ULONG MaxOutstandingIo, + _In_ ULONG Delay, + _In_ BOOLEAN Cancel + ) +{ + LPOVERLAPPED pOverlapped = NULL; + LPOVERLAPPED pOv = NULL; + PPOWERFX_READ_COMPONENT_INPUT inputBuffer = NULL; + PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer = NULL; + DWORD err = ERROR_SUCCESS; + UINT outstandingIoCount = 0; + UINT index = 0; + + srand((DWORD)GetTickCount64()); + + err = Initialize(MaxOutstandingIo, + &pOverlapped, + &inputBuffer, + &outputBuffer); + if (ERROR_SUCCESS != err) + { + goto clean0; + } + + UINT k = 0; + + for (;;k++) + { + k = k % MaxOutstandingIo; + + if (UNUSED == inputBuffer[k].ComponentNumber) + { + // + // This indicates the input buffer and corresponding overlapped + // structure is available to issue a new request. + // + pOv = &pOverlapped[k]; + } + else + { + // + // Wait for request #k to complete. + // + DWORD completionStatus; + ULONGLONG startTime = GetTickCount64(); + + for(;;) + { + ULONG_PTR completedRequestIndex; + + err = WaitForIoCompletion(CompletionPortHandle, + &pOv, + &completionStatus); + if (ERROR_SUCCESS != err) { + goto clean0; + } + + completedRequestIndex = pOv-pOverlapped; + + printf(" Request %d completed with status 0x%X.\n", + (DWORD)completedRequestIndex, completionStatus); + + if (ERROR_SUCCESS != completionStatus) + { + if (!Cancel || + ERROR_OPERATION_ABORTED != completionStatus) + { + // + // If there is a setting to cancel requests it is ok for + // the requests to complete with aborted status. + // + err = completionStatus; + printf(" Unexpected completion status %d. \n", + completionStatus); + goto clean0; + } + } + else + { + // + // If request completed successfully verify the contents of the buffer. + // + if (! VerifyRequest(&inputBuffer[completedRequestIndex], + &outputBuffer[completedRequestIndex])) + { + printf(" Request completed with unexpected data in" + " output buffer. \n"); + err = ERROR_INVALID_DATA; + goto clean0; + } + } + + inputBuffer[completedRequestIndex].ComponentNumber = UNUSED; + outstandingIoCount--; + + if (k == completedRequestIndex) + { + // + // The request we are looking for has completed. + // + pOv = &pOverlapped[k]; + break; + } + else if (GetTickCount64() - startTime > REQUEST_TIMEOUT) + { + // + // The request we are looking for did not complete on time. + // + err = ERROR_TIMEOUT; + printf(" Request %d did not complete within the expected" + " time. \n", k); + assert(0); + goto clean0; + } + } + + } + + // + // We now have an overlapped structure to use. Set the input buffer + // to the target component and send the request. + // + ZeroMemory(pOv, sizeof(OVERLAPPED)); + inputBuffer[k].ComponentNumber = (Component == RANDOM_COMPONENT) ? + (rand() % COMPONENT_COUNT): + Component; + + if (0 != Delay) + { + // + // If there is a setting to introduce a delay, sleep and then send the + // request. + // + Sleep(rand() % Delay); + } + + err = SendRequest(DeviceHandle, + pOv, + &inputBuffer[k], + &outputBuffer[k]); + if (ERROR_SUCCESS != err) + { + goto clean0; + } + + outstandingIoCount++; + + printf(" Request number %d sent to component %d.\n", k, + inputBuffer[k].ComponentNumber); + + if (Cancel) + { + // + // If there is a setting to cancel the request then cancel it after + // issuing it. + // + CancelIoEx(DeviceHandle, + pOv); + } + } + +clean0: + if (outstandingIoCount > 0) + { + CancelIo(DeviceHandle); + for (index=0; index < outstandingIoCount; index++) + { + WaitForIoCompletion(CompletionPortHandle, + NULL, + NULL); + } + } + delete[] pOverlapped; + delete[] inputBuffer; + delete[] outputBuffer; + return err; +} + +BOOLEAN +VerifyRequest( + _In_ PPOWERFX_READ_COMPONENT_INPUT input, + _In_ PPOWERFX_READ_COMPONENT_OUTPUT output) +{ + return (output->ComponentData == ~input->ComponentNumber); +} + +DWORD +SendRequest( + _In_ HANDLE DeviceHandle, + _In_ LPOVERLAPPED OverlappedPtr, + _In_ PPOWERFX_READ_COMPONENT_INPUT inputBuffer, + _In_ PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer) +{ + + BOOL bResult = FALSE; + DWORD err = ERROR_SUCCESS; + + bResult = DeviceIoControl(DeviceHandle, + (DWORD) IOCTL_POWERFX_READ_COMPONENT, + (PVOID)inputBuffer, + sizeof(POWERFX_READ_COMPONENT_INPUT), + (PVOID)outputBuffer, + sizeof(POWERFX_READ_COMPONENT_OUTPUT), + NULL, + OverlappedPtr); + if (FALSE == bResult) + { + err = GetLastError(); + if (ERROR_IO_PENDING == err) + { + // + // This is not really an error. + // + err = ERROR_SUCCESS; + } + else + { + printf("Unable to send request. DeviceIoControl failed with " + "error 0x%X. \n", err); + goto clean0; + } + } +clean0: + return err; +} + +DWORD +WaitForIoCompletion( + _In_ HANDLE CompletionPortHandle, + _In_opt_ LPOVERLAPPED* POvPtr, + _Out_opt_ PDWORD CompletionStatus) +{ + BOOL bResult; + DWORD err; + DWORD numBytes; + ULONG_PTR completionKey; + LPOVERLAPPED ovPtr; + DWORD completionStatus; + + // + // Assume successful completion of I/O request + // + completionStatus = ERROR_SUCCESS; + + // + // Dequeue a completion packet + // + bResult = GetQueuedCompletionStatus(CompletionPortHandle, + &numBytes, + &completionKey, + &ovPtr, + REQUEST_TIMEOUT); + if (FALSE == bResult) + { + err = GetLastError(); + if (NULL == ovPtr) + { + printf("Could not dequeue a completion packet. " + "GetQueuedCompletionStatus failed with error 0x%X.", + err); + assert(0); + goto clean0; + } + + // + // We dequeued a completion packet for an I/O operation that failed. + // Make a note of the failure status, but we need to return success from + // this function because we got a completion packet (even though it was + // for a failed I/O operation). + // + completionStatus = err; + err = ERROR_SUCCESS; + } + + if (NULL != POvPtr) { + *POvPtr = ovPtr; + } + + if (NULL != CompletionStatus) { + *CompletionStatus = completionStatus; + } + + err = ERROR_SUCCESS; + +clean0: + return err; +} + +BOOL +GetDevicePath( + IN LPGUID InterfaceGuid, + _Out_writes_(BufLen) PWSTR DevicePath, + _In_ size_t BufLen + ) +{ + HDEVINFO HardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; + PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = NULL; + ULONG Length, RequiredLength = 0; + BOOL bResult; + HRESULT hr; + + HardwareDeviceInfo = SetupDiGetClassDevs( + InterfaceGuid, + NULL, + NULL, + (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); + + if (HardwareDeviceInfo == INVALID_HANDLE_VALUE) { + printf("SetupDiGetClassDevs failed!\n"); + return FALSE; + } + + DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + bResult = SetupDiEnumDeviceInterfaces(HardwareDeviceInfo, + 0, + InterfaceGuid, + 0, + &DeviceInterfaceData); + + if (bResult == FALSE) { + + LPVOID lpMsgBuf; + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &lpMsgBuf, + 0, + NULL + )) { + + printf("SetupDiEnumDeviceInterfaces failed: %ws", (LPTSTR)lpMsgBuf); + LocalFree(lpMsgBuf); + } + + printf("SetupDiEnumDeviceInterfaces failed.\n"); + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + return FALSE; + } + + SetupDiGetDeviceInterfaceDetail( + HardwareDeviceInfo, + &DeviceInterfaceData, + NULL, + 0, + &RequiredLength, + NULL + ); + + DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, RequiredLength); + + if (DeviceInterfaceDetailData == NULL) { + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + printf("Failed to allocate memory.\n"); + return FALSE; + } + + DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + Length = RequiredLength; + + bResult = SetupDiGetDeviceInterfaceDetail( + HardwareDeviceInfo, + &DeviceInterfaceData, + DeviceInterfaceDetailData, + Length, + &RequiredLength, + NULL); + + if (bResult == FALSE) { + + LPVOID lpMsgBuf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &lpMsgBuf, + 0, + NULL + ); + + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + printf("Error in SetupDiGetDeviceInterfaceDetail: %ws\n", (LPWSTR)lpMsgBuf); + LocalFree(DeviceInterfaceDetailData); + LocalFree(lpMsgBuf); + return FALSE; + } + + hr = StringCchCopy(DevicePath, + BufLen, + DeviceInterfaceDetailData->DevicePath) ; + + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); // It must be executed in both success and failure traces + LocalFree(DeviceInterfaceDetailData); + + return ( !FAILED(hr) ); // Result depends on StringCchCopy() +} + + diff --git a/pofx/UMDF2/App/PowerFxApp.vcxproj b/pofx/UMDF2/App/PowerFxApp.vcxproj new file mode 100644 index 00000000..6201baec --- /dev/null +++ b/pofx/UMDF2/App/PowerFxApp.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {DA8D50B3-7692-4E79-9181-21576805D3E5} + $(MSBuildProjectName) + Debug + Win32 + {86082276-A94B-42CE-B3D2-1AF13BE34A92} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + PowerFxApp + + + PowerFxApp + + + PowerFxApp + + + PowerFxApp + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + true + Level4 + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + true + Level4 + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + true + Level4 + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + true + Level4 + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pofx/UMDF2/App/PowerFxApp.vcxproj.Filters b/pofx/UMDF2/App/PowerFxApp.vcxproj.Filters new file mode 100644 index 00000000..73e699bc --- /dev/null +++ b/pofx/UMDF2/App/PowerFxApp.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {B5C219BE-1582-4433-8AEB-794DA8983AB0} + + + h;hpp;hxx;hm;inl;inc;xsd + {86E06376-FA08-4058-A118-F8CB221B0462} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {0F043EAF-993B-4742-95AE-C40C10DD5D19} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/pofx/UMDF2/App/UserInput.cpp b/pofx/UMDF2/App/UserInput.cpp new file mode 100644 index 00000000..dcd3ca75 --- /dev/null +++ b/pofx/UMDF2/App/UserInput.cpp @@ -0,0 +1,201 @@ +#include "include.h" + + +// +// The supported settings with their default values. +// +CONFIGURATION g_UserSettings[] = { + {COMPONENT ,0}, + {MAX_OUTSTANDING_IO ,1}, + {DELAY ,0}, + {CANCEL ,0} +}; + +DWORD +ProcessUserInput( + _In_ int argc, + _In_reads_(argc) PWSTR argv[] + ) +{ + DWORD err = ERROR_SUCCESS; + + for (int i=1; i < argc; i++) + { + PWSTR arg = argv[i]; + + // + // Setting must begin with / or - + // + if (arg[0] != L'-' && arg[0] != L'/') + { + printf("\n Invalid command-line argument %ws. \n", arg); + err = ERROR_INVALID_PARAMETER; + goto clean0; + } + + if (L'?' == arg[1]) + { + err = ERROR_INVALID_PARAMETER; + goto clean0; + } + + // + // Setting must contain ':' as the seperator. + // + PWSTR settingName = arg + 1; + PWSTR settingValue = NULL; + PWSTR seperator = wcschr(settingName, L':'); + if (seperator == NULL) + { + printf("\n Invalid command-line argument %ws. \n", arg); + err = ERROR_INVALID_PARAMETER; + goto clean0; + } + + settingValue = seperator + 1; + *seperator = L'\0'; + + // + // Store the value of the setting in the appropriate location. + // + err = ProcessSwitch(settingName, settingValue); + if (err != ERROR_SUCCESS) + { + goto clean0; + } + } + +clean0: + + if (ERROR_SUCCESS != err) + { + PrintUsage(argv); + } + return err; +} + + +PCONFIGURATION +LookupSwitch( + _In_ PWSTR Param) +{ + for (UINT i=0; i < COUNT_OF(g_UserSettings); i++) + { + if (_wcsicmp(Param, g_UserSettings[i].Option) == 0) + { + return &g_UserSettings[i]; + } + } + + return NULL; +} + +ULONG +GetSetting( + _In_ PWSTR Switch) +{ + return LookupSwitch(Switch)->Value; +} + +DWORD +ProcessSwitch( + _In_ PWSTR Param, + _In_ PWSTR Value) +{ + PCONFIGURATION config; + + config = LookupSwitch(Param); + if (NULL == config) + { + printf("\n '%ws' is not a valid switch", Param); + return ERROR_INVALID_PARAMETER; + } + + if (0 == _wcsicmp(Param, COMPONENT)) + { + if (0 == _wcsicmp(Value, L"*")) + { + config->Value = RANDOM_COMPONENT; + } + else + { + config->Value = _wtoi(Value); + if (config->Value >= COMPONENT_COUNT) + { + printf("Invalid component count '%ws' specified. " + "Component count must be less than %d. \n", Value, COMPONENT_COUNT); + return ERROR_INVALID_PARAMETER; + } + } + + } + else if (0 == _wcsicmp(Param, CANCEL)) + { + if (0 == _wcsicmp(Value, L"yes")) + { + config->Value = TRUE; + } + else if (0 == _wcsicmp(Value, L"no")) + { + config->Value = FALSE; + } + else + { + printf("'%ws' is not a valid option for '%ws'. Must be 'yes' or 'no'. \n", + Value, Param); + return ERROR_INVALID_PARAMETER; + } + } + else + { + // + // For MaxOutstandingIo or Delay this is sufficient. + // + config->Value = _wtoi(Value); + if (0 == config->Value) + { + printf("'%ws' is not a valid option for '%ws'", Value, Param); + return ERROR_INVALID_PARAMETER; + } + } + + return ERROR_SUCCESS; +} + + +void PrintUsage( + _In_ PWSTR argv[] + ) +{ + printf("\n This application can be used to send IO requests to a specified component"); + printf("\n of the WDF Power Fx sample driver. The requests will be sent indefinitely "); + printf("\n until a request fails or the user terminates the execution with ^C"); + printf("\n More details on the driver and application are in the associated help file."); + printf("\n "); + printf("\n Usage:"); + printf("\n %ws [/: ...]", argv[0]); + printf("\n "); + printf("\n Available settings are:"); + printf("\n "); + printf("\n /Component:"); + printf("\n Where can be a specific component number, or if you are running with"); + printf("\n the multi-component sample driver you can specify '*'(without quotes) and "); + printf("\n each request will be sent to a random component between 0 and "); + printf("\n (COMPONENT_COUNT-1). Default value is component 0."); + printf("\n "); + printf("\n /Delay:"); + printf("\n There is an approximate delay between 0 and ms"); + printf("\n between each request. Default value is 0 (no delay)."); + printf("\n "); + printf("\n /MaxOutstandingIO:"); + printf("\n The maximum number of I/O requests that can be outstanding at any point."); + printf("\n Default value is 1 (equivalent to synchronously sending requests)."); + printf("\n "); + printf("\n /Cancel:"); + printf("\n Can be 'yes' or 'no' (without quotes)."); + printf("\n When set to 'yes' the application will attempt to cancel the requests once "); + printf("\n sent. Default value is 'no'."); + printf("\n "); + return; +} + diff --git a/pofx/UMDF2/App/include.h b/pofx/UMDF2/App/include.h new file mode 100644 index 00000000..29f0cb5e --- /dev/null +++ b/pofx/UMDF2/App/include.h @@ -0,0 +1,95 @@ +/*++ + + +--*/ +#include + _Analysis_mode_(_Analysis_code_type_user_code_) + +#define INITGUID + +#include +#include +#include +#include +#include +#include +#include +#include "AppInterface.h" + +#define COUNT_OF(x) sizeof(x)/sizeof(x[0]) + +#define MAX_DEVPATH_LENGTH 256 + +#define COMPONENT L"Component" +#define MAX_OUTSTANDING_IO L"MaxOutStandingIO" +#define DELAY L"Delay" +#define CANCEL L"Cancel" + +typedef struct _CONFIGURATION { + PWSTR Option; + ULONG Value; +} CONFIGURATION, *PCONFIGURATION; + +#define RANDOM_COMPONENT (DWORD)-1 +#define UNUSED COMPONENT_COUNT +#define REQUEST_TIMEOUT 10000 +#define MAX_DEVPATH_LENGTH 256 + +PCONFIGURATION +LookupSwitch( + _In_ PWSTR Param); + +DWORD +ProcessUserInput( + _In_ int argc, + _In_reads_(argc) PWSTR argv[] + ); + +ULONG +GetSetting( + _In_ PWSTR Switch); + +DWORD +ProcessSwitch( + _In_ PWSTR Param, + _In_ PWSTR Value); + +void PrintUsage( + _In_ PWSTR argv[] + ); + +BOOL +GetDevicePath( + IN LPGUID InterfaceGuid, + _Out_writes_(BufLen) PWCHAR DevicePath, + _In_ size_t BufLen + ); + + +DWORD SendIO( + _In_ HANDLE DeviceHandle, + _In_ HANDLE CompletionPortHandle, + _In_ ULONG Component, + _In_ ULONG MaxOutstandingIo, + _In_ ULONG Delay, + _In_ BOOLEAN Cancel + ); + +DWORD +WaitForIoCompletion( + _In_ HANDLE CompletionPortHandle, + _In_opt_ LPOVERLAPPED* POvPtr, + _Out_opt_ PDWORD CompletionStatus); + +BOOLEAN +VerifyRequest( + _In_ PPOWERFX_READ_COMPONENT_INPUT input, + _In_ PPOWERFX_READ_COMPONENT_OUTPUT output); + +DWORD +SendRequest( + _In_ HANDLE DeviceHandle, + _In_ LPOVERLAPPED OverlappedPtr, + _In_ PPOWERFX_READ_COMPONENT_INPUT inputBuffer, + _In_ PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer); + diff --git a/pofx/UMDF2/Driver/SingleComp/HwSim.c b/pofx/UMDF2/Driver/SingleComp/HwSim.c new file mode 100644 index 00000000..d41bbac2 --- /dev/null +++ b/pofx/UMDF2/Driver/SingleComp/HwSim.c @@ -0,0 +1,197 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + HwSim.c + +Abstract: + This module implements a simple hardware simulator that simulates reading of + data from the device's components. In this sample, the "data" that is read + is simply the bitwise complement of the component number. In other words, + the data for component number 'X' is simply '~X'. + + The hardware simulator also verifies that when a component is read, the + device is in D0. If not, it breaks into the debugger. + +Environment: + + User mode + +--*/ + +#include "include.h" +#include "HwSim.h" +#include "HwSim.tmh" + +NTSTATUS +HwSimInitialize( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine initializes the hardware simulator + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttributes; + PHWSIM_CONTEXT devCtx; + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry\n"); + + // + // Allocate our context for this device + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, + HWSIM_CONTEXT); + status = WdfObjectAllocateContext((WDFOBJECT) Device, + &objectAttributes, + (PVOID*) &devCtx); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfObjectAllocateContext failed with %!status!", + status); + goto exit; + } + + devCtx->FirstD0Entry = TRUE; + + status = STATUS_SUCCESS; + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit\n"); + +exit: + return status; +} + +VOID +HwSimD0Entry( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine simulates the device entering D0 + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + None + +--*/ +{ + PHWSIM_CONTEXT devCtx; + + devCtx = HwSimGetDeviceContext(Device); + + if (devCtx->FirstD0Entry) { + devCtx->FirstD0Entry = FALSE; + } + + devCtx->DevicePoweredOn = TRUE; + + return; +} + +VOID +HwSimD0Exit( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine simulates the device exiting D0 + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + None + +--*/ +{ + PHWSIM_CONTEXT devCtx; + + devCtx = HwSimGetDeviceContext(Device); + + devCtx->DevicePoweredOn = FALSE; + + return; +} + +ULONG +HwSimReadComponent( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine simulates the reading of data from a component + +Arguments: + + Device - Handle to the framework device object + + Component - Component from which data is being read + +Return Value: + + A ULONG value representing the data that was read from the component + +--*/ +{ + ULONG componentData; + PHWSIM_CONTEXT devCtx; + ULONG component = 0; + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry\n"); + + devCtx = HwSimGetDeviceContext(Device); + + // + // Verify that the device is powered on + // + if (FALSE == devCtx->DevicePoweredOn) { + // + // This means that our driver is attempting to read from the component + // while the device is not powered on. + // + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Expected device to be powered on, but it was not."); + + WdfVerifierDbgBreakPoint(); + } + + assert(devCtx->DevicePoweredOn); + + // + // In this sample, component data is just a bit-wise complement of the + // component number. + // + componentData = ~component; + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit\n"); + + return componentData; +} diff --git a/pofx/UMDF2/Driver/SingleComp/HwSim.h b/pofx/UMDF2/Driver/SingleComp/HwSim.h new file mode 100644 index 00000000..e884019d --- /dev/null +++ b/pofx/UMDF2/Driver/SingleComp/HwSim.h @@ -0,0 +1,63 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + HwSim.h + +Abstract: + Header file for the hardware simulation module of the UMDF2 sample driver for + a single-component device. + +Environment: + + User mode + +--*/ + +#pragma once + +NTSTATUS +HwSimInitialize( + _In_ WDFDEVICE Device + ); + +VOID +HwSimD0Entry( + _In_ WDFDEVICE Device + ); + +VOID +HwSimD0Exit( + _In_ WDFDEVICE Device + ); + +ULONG +HwSimReadComponent( + _In_ WDFDEVICE Device + ); + +// +// This structure represents the hardware simulation module's device context +// space +// +typedef struct _HWSIM_CONTEXT { + // + // The following member tracks whether or not the device is in D0 + // + BOOLEAN DevicePoweredOn; + + // + // The following member tracks whether or not we have previously entered the + // D0 state for this device + // + BOOLEAN FirstD0Entry; +} HWSIM_CONTEXT, *PHWSIM_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HWSIM_CONTEXT, HwSimGetDeviceContext) diff --git a/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.inx b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.inx new file mode 100644 index 00000000..b5e35b21 --- /dev/null +++ b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.inx @@ -0,0 +1,90 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +;Module Name: +; SingleComponentSingleStateSampleUm.INF +; +;Abstract: +; INF file for installing the SingleComponentSingleStateUm Driver +; +;Installation Notes: +; Using Devcon: Type "devcon install SingleComponentSingleStateUm.inf root\SingleComponentSingleState" to install +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFT% +DriverVer=03/20/2003,5.00.3788 +CatalogFile=wudf.cat + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +SingleComponentSingleStateUm.dll = 1,, + +;***************************************** +; SCSS Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%SCSS.DeviceDesc%=SCSS_Device, root\SingleComponentSingleState + +;---------------- copy files + +[SCSS_Device.NT] +CopyFiles=UMDriverCopy + +[UMDriverCopy] +SingleComponentSingleStateUm.dll + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to drivers\umdf + +;-------------- Service installation +[SCSS_Device.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +;-------------- WDF specific section ------------- +[SCSS_Device.NT.Wdf] +UmdfService=SingleComponentSingleStateUm, SCSS_Install +UmdfServiceOrder=SingleComponentSingleStateUm + +[SCSS_Install] +UmdfLibraryVersion=$UMDFVERSION$ +ServiceBinary=%12%\UMDF\SingleComponentSingleStateUm.dll + +[Strings] +MSFT = "Microsoft" +StdMfg = "(Standard system devices)" +DiskId1 = "WDF Sample Single Component Single State Device Installation Disk #1" +SCSS.DeviceDesc = "UMDF 2.0 Single Component Single State Device" +ClassName = "Sample Device" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" + diff --git a/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj new file mode 100644 index 00000000..f74a6321 --- /dev/null +++ b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj @@ -0,0 +1,213 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {C2742046-68D5-46C4-8974-1523D6E81E44} + $(MSBuildProjectName) + 2 + Debug + Win32 + {2F2C473D-EF60-4754-A765-016CE7E59AF5} + + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + False + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + Windows10 + True + Universal + UMDF + WindowsUserModeDriver10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + driver.h + TraceEvents(LEVEL,FLAGS,MSG,...) + + + $(InfArch) + true + .\$(IntDir)\SingleComponentSingleStateUm.inf + + + + SingleComponentSingleStateUm + + + SingleComponentSingleStateUm + + + SingleComponentSingleStateUm + + + SingleComponentSingleStateUm + + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj.Filters b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj.Filters new file mode 100644 index 00000000..1acbb43d --- /dev/null +++ b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj.Filters @@ -0,0 +1,37 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {827A6CD7-9B1B-48EA-B00F-776EAD83C5D5} + + + h;hpp;hxx;hm;inl;inc;xsd + {A90EB6DF-7C46-439E-83E4-284A2D766EA8} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9C2DE4D9-2409-4940-AF45-0F2E072F10A3} + + + inf;inv;inx;mof;mc; + {62D85CBA-F225-41CA-BF89-4C98DB9363E5} + + + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + \ No newline at end of file diff --git a/pofx/UMDF2/Driver/SingleComp/driver.c b/pofx/UMDF2/Driver/SingleComp/driver.c new file mode 100644 index 00000000..597c742d --- /dev/null +++ b/pofx/UMDF2/Driver/SingleComp/driver.c @@ -0,0 +1,540 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Driver.c + +Abstract: + This module implements a UMDF2 sample driver for a single-component device. + The driver uses the power framework to manage the power state of the + component that represents device. + + The device used in this sample is a root-enumerated device whose components + are simulated entirely in software. The simulation of the components is + implemented in HwSim.h and HwSim.c. + + This driver works only on Win8.1 and above. + +Environment: + + User mode + +--*/ + +#include "include.h" +#include "hwsim.h" + +#include +#include "AppInterface.h" + +#include "driver.tmh" + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Driver initialization entry point. This entry point is called directly by + the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path to the + driver-specific key in the registry. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG config; + WDF_OBJECT_ATTRIBUTES attributes; + + WPP_INIT_TRACING(DriverObject, RegistryPath); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry of driver"); + + // + // Initiialize driver config to control the attributes that are global to + // the driver. Note that framework by default provides a driver unload + // routine. If DriverEntry creates any resources that require clean-up in + // driver unload, you can manually override the default by supplying a + // pointer to the EvtDriverUnload callback in the config structure. In + // general xxx_CONFIG_INIT macros are provided to initialize most commonly + // used members. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.EvtCleanupCallback = SingleCompEvtDriverCleanup; + WDF_DRIVER_CONFIG_INIT( + &config, + SingleCompEvtDeviceAdd + ); + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + &attributes, // Driver Attributes + &config, // Driver Config Info + WDF_NO_HANDLE + ); + + if (FALSE == NT_SUCCESS(status)) { + KdPrint( ("WdfDriverCreate failed with status 0x%x\n", status)); + WPP_CLEANUP(NULL); + } + + return status; +} + +VOID +SingleCompEvtDriverCleanup( + _In_ WDFOBJECT Driver + ) +{ + UNREFERENCED_PARAMETER(Driver); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry\n"); + WPP_CLEANUP(NULL); +} + +NTSTATUS +SingleCompEvtDeviceAdd( + _In_ WDFDRIVER Driver, + _Inout_ PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + EvtDeviceAdd is called by UMDF in response to AddDevice call from + the PnP manager. + +Arguments: + + Driver - Handle to the UMDF driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + WDFDEVICE device; + WDFQUEUE queue; + WDF_IO_QUEUE_CONFIG queueConfig; + FDO_DATA *fdoContext = NULL; + WDF_OBJECT_ATTRIBUTES objectAttributes; + WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks; + + UNREFERENCED_PARAMETER(Driver); + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry\n"); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, FDO_DATA); + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks); + pnpCallbacks.EvtDeviceD0Entry = SingleCompEvtDeviceD0Entry; + pnpCallbacks.EvtDeviceD0Exit = SingleCompEvtDeviceD0Exit; + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpCallbacks); + + status = WdfDeviceCreate(&DeviceInit, &objectAttributes, &device); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceCreate failed with %!status!.", + status); + goto exit; + } + + fdoContext = FdoGetContext(device); + + // + // Our initial state is active + // + fdoContext->IsActive = TRUE; + + // + // Create a power-managed queue for IOCTL requests. + // + WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, + WdfIoQueueDispatchParallel); + queueConfig.EvtIoDeviceControl = SingleCompEvtIoDeviceControl; + + // + // By default, Static Driver Verifier (SDV) displays a warning if it + // doesn't find the EvtIoStop callback on a power-managed queue. + // The 'assume' below causes SDV to suppress this warning. If the driver + // has not explicitly set PowerManaged to WdfFalse, the framework creates + // power-managed queues when the device is not a filter driver. Normally + // the EvtIoStop is required for power-managed queues, but for this driver + // it is not needed b/c the driver doesn't hold on to the requests or + // forward them to other drivers. This driver completes the requests + // directly in the queue's handlers. If the EvtIoStop callback is not + // implemented, the framework waits for all driver-owned requests to be + // done before moving in the Dx/sleep states or before removing the + // device, which is the correct behavior for this type of driver. + // If the requests were taking an indeterminate amount of time to complete, + // or if the driver forwarded the requests to a lower driver/another stack, + // the queue should have an EvtIoStop/EvtIoResume. + // + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(device, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if (FALSE == NT_SUCCESS (status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfIoQueueCreate for IoDeviceControl failed with %!status!.", + status); + goto exit; + } + + status = WdfDeviceConfigureRequestDispatching(device, + queue, + WdfRequestTypeDeviceControl); + if (FALSE == NT_SUCCESS (status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceConfigureRequestDispatching for " + "WdfRequestTypeDeviceControl failed with %!status!.", + status); + goto exit; + } + + status = AssignS0IdleSettings(device); + if (!NT_SUCCESS(status)) { + goto exit; + } + + // + // Create a device interface so that applications can open a handle to this + // device. + // + status = WdfDeviceCreateDeviceInterface(device, + &GUID_DEVINTERFACE_POWERFX, + NULL /* ReferenceString */); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceCreateDeviceInterface failed with %!status!.", + status); + goto exit; + } + + // + // Initialize the hardware simulator + // + status = HwSimInitialize(device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit\n"); + +exit: + return status; +} + +NTSTATUS +SingleCompEvtDeviceD0Entry( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousState + ) +/*++ +Routine Description: + + UMDF calls this routine when the device has entered D0. + +Arguments: + + Device - Handle to the framework device object + + PreviousState - Previous device power state + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + UNREFERENCED_PARAMETER(PreviousState); + + HwSimD0Entry(Device); + + return STATUS_SUCCESS; +} + +NTSTATUS +SingleCompEvtDeviceD0Exit( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) +/*++ +Routine Description: + + UMDF calls this routine when the device is about to leave D0. + +Arguments: + + Device - Handle to the framework device object + + TargetState - Device power state that the device is about to enter + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + UNREFERENCED_PARAMETER(TargetState); + + HwSimD0Exit(Device); + + return STATUS_SUCCESS; +} + +NTSTATUS +AssignS0IdleSettings( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + Helper function to assign S0 idle settings for the device + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS powerPolicy; + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry\n"); + + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&powerPolicy, + IdleCannotWakeFromS0); + powerPolicy.IdleTimeoutType = SystemManagedIdleTimeout; + + status = WdfDeviceAssignS0IdleSettings(Device, &powerPolicy); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceAssignS0IdleSettings failed with %!status!.", + status); + } + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit\n"); + + return status; +} + +VOID +SingleCompEvtIoDeviceControl( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +/*++ +Routine Description: + + Callback invoked by WDFQUEUE for a Device Io Control request. + +Arguments: + + Queue - Device I/O control queue + + Request - Device I/O control request + + OutputBufferLength - Output buffer length for the I/O control + + InputBufferLength - Input buffer length for the I/O control + + IoControlCode - I/O control code + +--*/ +{ + NTSTATUS status; + PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer = NULL; + WDFDEVICE device = NULL; + ULONG componentData; + ULONG_PTR information = 0; + FDO_DATA *fdoContext = NULL; + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry\n"); + + // + // When we complete the request, make sure we don't get the I/O manager to + // copy any more data to the client address space than what we write to the + // output buffer. The only data that we write to the output buffer is the + // component data and the C_ASSERT below ensures that the output buffer does + // not have room to contain anything other than that. + // + C_ASSERT(sizeof(componentData) == sizeof(*outputBuffer)); + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + // + // This is a power-managed queue. So our queue stop/start logic should have + // ensured that we are in the active condition when a request is dispatched + // from this queue. + // + device = WdfIoQueueGetDevice(Queue); + fdoContext = FdoGetContext(device); + if (FALSE == fdoContext->IsActive) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - IOCTL %d was dispatched from WDFQUEUE %p when the " + "component was not in an active condition.", + IOCTL_POWERFX_READ_COMPONENT, + Queue); + WdfVerifierDbgBreakPoint(); + } + + // + // Validate Ioctl code + // + if (IOCTL_POWERFX_READ_COMPONENT != IoControlCode) { + status = STATUS_NOT_SUPPORTED; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! -Unsupported IoControlCode. Expected: %d. Actual: %d." + " %!status!.", + IOCTL_POWERFX_READ_COMPONENT, + IoControlCode, + status); + goto exit; + } + + // + // Get the output buffer + // + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof(*outputBuffer), + (PVOID*) &outputBuffer, + NULL // Length + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfRequestRetrieveOutputBuffer failed with %!status!.", + status); + goto exit; + } + + // + // Read the data from the component + // + componentData = HwSimReadComponent(device); + outputBuffer->ComponentData = componentData; + information = sizeof(*outputBuffer); + + status = STATUS_SUCCESS; + + Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Exit\n"); + +exit: + // + // Complete the request + // + WdfRequestCompleteWithInformation(Request, status, information); + return; +} + +// +// Read and write queues are only for illustration purposes - on how to stop +// multiple queues. Currently app doesn't send Read/Write to the driver. +// +VOID +SingleCompEvtIoRead( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength + ) +/*++ +Routine Description: + + Callback invoked by WDFQUEUE for a read request. + +Arguments: + + Queue - Read queue + + Request - Read request + + OutputBufferLength - Length of read + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(OutputBufferLength); + + status = STATUS_NOT_SUPPORTED; + + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! -Reads are currently not supported: %!status!.", + status); + + WdfRequestComplete(Request, status); +} + +VOID +SingleCompEvtIoWrite( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t InputBufferLength + ) +/*++ +Routine Description: + + Callback invoked by WDFQUEUE for a write request. + +Arguments: + + Queue - Write queue + + Request - Write request + + InputBufferLength - Length of write + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(InputBufferLength); + + status = STATUS_NOT_SUPPORTED; + + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! -Writes are currently not supported: %!status!.", + status); + + WdfRequestComplete(Request, status); +} + diff --git a/pofx/UMDF2/Driver/SingleComp/driver.h b/pofx/UMDF2/Driver/SingleComp/driver.h new file mode 100644 index 00000000..a707792a --- /dev/null +++ b/pofx/UMDF2/Driver/SingleComp/driver.h @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Driver.h + +Abstract: + Header file for the UMDF2 sample driver for a single-component device. + +Environment: + + User mode + +--*/ + +#define QUEUE_COUNT 3 + +// +// This structure represents the driver's device context space +// +typedef struct _FDO_DATA +{ + // + // Tracks the active/idle state of the component + // + BOOLEAN IsActive; +} FDO_DATA, *PFDO_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DATA, FdoGetContext) + +// +// Driver's UMDF2 callbacks +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD SingleCompEvtDeviceAdd; +EVT_WDF_OBJECT_CONTEXT_CLEANUP SingleCompEvtDriverCleanup; + +EVT_WDF_DEVICE_D0_ENTRY SingleCompEvtDeviceD0Entry; +EVT_WDF_DEVICE_D0_EXIT SingleCompEvtDeviceD0Exit; + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL SingleCompEvtIoDeviceControl; + +// +// Helper functions +// + +NTSTATUS +AssignS0IdleSettings( + _In_ WDFDEVICE Device + ); + +// +// Define the tracing flags. +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + MyDriverTraceControl, (f9eb5c3a,c292,4c69,8b52,ccf043c25ab0), \ + \ + WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ + ) + +#define WPP_FLAGS_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAGS_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC Trace{FLAGS=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); +// end_wpp +// diff --git a/pofx/UMDF2/Driver/SingleComp/include.h b/pofx/UMDF2/Driver/SingleComp/include.h new file mode 100644 index 00000000..5b693dcc --- /dev/null +++ b/pofx/UMDF2/Driver/SingleComp/include.h @@ -0,0 +1,7 @@ +#include +#include +#pragma warning( disable: 4201 ) // nonstandard extension used : nameless struct/union +#include +#include +#include +#include "driver.h" \ No newline at end of file diff --git a/pofx/UMDF2/ReadMe.md b/pofx/UMDF2/ReadMe.md new file mode 100644 index 00000000..7272604e --- /dev/null +++ b/pofx/UMDF2/ReadMe.md @@ -0,0 +1,53 @@ +Power Framework (PoFx) Sample (UMDF Version 2) +============================================== + +This solution demonstrates how a User-Mode Driver Framework (UMDF) version 2 driver can implement F-state-based power management. The SingleComp project demonstrates how a UMDF version 2 driver can implement F-state-based power management for a device that has only a single component. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Related technologies +-------------------- +For related information, see the [KMDF Power Framework (PoFx) Sample](http://code.msdn.microsoft.com/windowshardware/PoFx-1974b51c). + +[User-Mode Driver Framework](http://msdn.microsoft.com/en-us/library/windows/hardware/ff560456) + +Run the sample +-------------- + +The computer where you install the driver is called the *target computer* or the *test computer*. Typically this is a separate computer from where you develop and build the driver package. The computer where you develop and build the driver is called the *host computer*. + +The process of moving the driver package to the target computer and installing the driver is called *deploying the driver*. You can deploy a driver sample automatically or manually. + +### Automatic deployment (root enumerated) + +Before you automatically deploy a driver, you must provision the target computer. For instructions, see [Configuring a Computer for Driver Deployment, Testing, and Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/). + +1. On the host computer, in Visual Studio, in Solution Explorer, right click **package** (lower case), and choose **Properties**. Navigate to **Configuration Properties \> Driver Install \> Deployment**. +2. Check **Enable deployment**, and check **Remove previous driver versions before deployment**. For **Target Computer Name**, select the name of a target computer that you provisioned previously. Select **Hardware ID Driver Update**, and enter **root\\SingleComponentSingleState** for the hardware ID. Click **OK**. +3. On the **Build** menu, choose **Build Solution**. + +### Manual deployment (root enumerated) + +Before you manually deploy a driver, you must turn on test signing and install a certificate on the target computer. You also need to copy the [DevCon](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544707) tool to the target computer. For instructions, see [Preparing a Computer for Manual Driver Deployment](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265571). + +1. Copy all of the files in your driver package to a folder on the target computer (for example, c:\\PoFx). +2. On the target computer, open a Command Prompt window as Administrator. Navigate to your driver package folder, and enter the following command: + + **devcon install SingleComponentSingleStateUm.inf root\\SingleComponentSingleState** + +### View the root enumerated driver in Device Manager + +On the target computer, in a Command Prompt window, enter **devmgmt** to open Device Manager. In Device Manager, on the **View** menu, choose **Devices by type**. In the device tree, locate **UMDF 2.0 Single Component Single State Device** (for example, this might be under the **Sample Device** node). + +In Device Manager, on the **View** menu, choose **Devices by connection**. Locate **UMDF 2.0 Single Component Single State Device** as a child of the root node of the device tree. + +Build the sample using MSBuild +------------------------------ + +As an alternative to building the driver sample in Visual Studio, you can build it in a Visual Studio Command Prompt window. In Visual Studio, on the **Tools** menu, choose **Visual Studio Command Prompt**. In the Visual Studio Command Prompt window, navigate to the folder that has the solution file, PoFx.sln. Use the MSBuild command to build the solution. Here is an example: + +**msbuild /p:configuration=â€Win8 Release†/p:platform=â€Win32†PoFx.sln** + +For more information about using MSBuild to build a driver package, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + diff --git a/pofx/UMDF2/inc/AppInterface.h b/pofx/UMDF2/inc/AppInterface.h new file mode 100644 index 00000000..60173369 --- /dev/null +++ b/pofx/UMDF2/inc/AppInterface.h @@ -0,0 +1,69 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + AppInterface.h + +Abstract: + + This module contains the common declarations shared by the driver and the + user application for the PowerFx sample. + +Environment: + + user and kernel + +--*/ + +#if !defined(_APPINTERFACE_H_) +#define _APPINTERFACE_H_ + +#include + +// +// Component count for multi-component device +// +#define COMPONENT_COUNT 4 + +// +// Interface GUID for the PowerFx driver +// {D21FD4DB-5FB6-4df2-B73B-5E7F8632B390} +// +DEFINE_GUID( + GUID_DEVINTERFACE_POWERFX, + 0xd21fd4db, 0x5fb6, 0x4df2, 0xb7, 0x3b, 0x5e, 0x7f, 0x86, 0x32, 0xb3, 0x90 + ); + +// +// IOCTLs that the application sends to the PowerFx driver +// +#define IOCTL_INDEX_POWERFX 0x900 +#define FILE_DEVICE_POWERFX 0x9000 + +// +// This IOCTL is used to read a data from a component. The app initializes the +// input buffer with the component number to read. The driver writes the data +// from that component to the output buffer. +// In this sample, the "data" for a given component is just the bit-wise +// complement of the component number. In other words, the data for component +// number 'X' is simply '~X'. +// +#define IOCTL_POWERFX_READ_COMPONENT CTL_CODE(FILE_DEVICE_POWERFX, \ + IOCTL_INDEX_POWERFX, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + +// +// Input and output buffers for IOCTL_POWERFX_READ_COMPONENT +// +typedef struct _POWERFX_READ_COMPONENT_INPUT { + ULONG ComponentNumber; +} POWERFX_READ_COMPONENT_INPUT, *PPOWERFX_READ_COMPONENT_INPUT; + +typedef struct _POWERFX_READ_COMPONENT_OUTPUT { + ULONG ComponentData; +} POWERFX_READ_COMPONENT_OUTPUT, *PPOWERFX_READ_COMPONENT_OUTPUT; + +#endif // _APPINTERFACE_H_ \ No newline at end of file diff --git a/pofx/UMDF2/pofx.sln b/pofx/UMDF2/pofx.sln new file mode 100644 index 00000000..627cffb2 --- /dev/null +++ b/pofx/UMDF2/pofx.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{83BD6E56-4CF0-40D7-8D4B-AB8FBA52B0C6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SingleComp", "SingleComp", "{35021FFA-A126-4138-8574-9D62921627EB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{72A7C89C-0E64-4C4E-BF27-BD8187423B39}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerFxApp", "App\PowerFxApp.vcxproj", "{DA8D50B3-7692-4E79-9181-21576805D3E5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SingleComponentSingleStateUm", "Driver\SingleComp\SingleComponentSingleStateUm.vcxproj", "{C2742046-68D5-46C4-8974-1523D6E81E44}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DA8D50B3-7692-4E79-9181-21576805D3E5}.Debug|Win32.ActiveCfg = Debug|Win32 + {DA8D50B3-7692-4E79-9181-21576805D3E5}.Debug|Win32.Build.0 = Debug|Win32 + {DA8D50B3-7692-4E79-9181-21576805D3E5}.Release|Win32.ActiveCfg = Release|Win32 + {DA8D50B3-7692-4E79-9181-21576805D3E5}.Release|Win32.Build.0 = Release|Win32 + {DA8D50B3-7692-4E79-9181-21576805D3E5}.Debug|x64.ActiveCfg = Debug|x64 + {DA8D50B3-7692-4E79-9181-21576805D3E5}.Debug|x64.Build.0 = Debug|x64 + {DA8D50B3-7692-4E79-9181-21576805D3E5}.Release|x64.ActiveCfg = Release|x64 + {DA8D50B3-7692-4E79-9181-21576805D3E5}.Release|x64.Build.0 = Release|x64 + {C2742046-68D5-46C4-8974-1523D6E81E44}.Debug|Win32.ActiveCfg = Debug|Win32 + {C2742046-68D5-46C4-8974-1523D6E81E44}.Debug|Win32.Build.0 = Debug|Win32 + {C2742046-68D5-46C4-8974-1523D6E81E44}.Release|Win32.ActiveCfg = Release|Win32 + {C2742046-68D5-46C4-8974-1523D6E81E44}.Release|Win32.Build.0 = Release|Win32 + {C2742046-68D5-46C4-8974-1523D6E81E44}.Debug|x64.ActiveCfg = Debug|x64 + {C2742046-68D5-46C4-8974-1523D6E81E44}.Debug|x64.Build.0 = Debug|x64 + {C2742046-68D5-46C4-8974-1523D6E81E44}.Release|x64.ActiveCfg = Release|x64 + {C2742046-68D5-46C4-8974-1523D6E81E44}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {DA8D50B3-7692-4E79-9181-21576805D3E5} = {83BD6E56-4CF0-40D7-8D4B-AB8FBA52B0C6} + {C2742046-68D5-46C4-8974-1523D6E81E44} = {35021FFA-A126-4138-8574-9D62921627EB} + {35021FFA-A126-4138-8574-9D62921627EB} = {72A7C89C-0E64-4C4E-BF27-BD8187423B39} + EndGlobalSection +EndGlobal diff --git a/pofx/WDF/App/PowerFxApp.cpp b/pofx/WDF/App/PowerFxApp.cpp new file mode 100644 index 00000000..c628d366 --- /dev/null +++ b/pofx/WDF/App/PowerFxApp.cpp @@ -0,0 +1,592 @@ +/*++ + +Copyright (c) Microsoft Corporation + +Module Name: + + PowerFxApp.cpp + +Abstract: + + This application can be used to exercise KMDF sample drivers for the + new power framework. See application "usage" details for more information. + +Environment: + + user mode only + +--*/ + +#include "include.h" + +int __cdecl +wmain( + _In_ int argc, + _In_reads_(argc) PWSTR argv[] + ) +{ + DWORD err; + WCHAR devicePath[MAX_DEVPATH_LENGTH] = {UNICODE_NULL}; + HANDLE hDevice = INVALID_HANDLE_VALUE; + HANDLE hCompletionPort = NULL; + + + // + // Process user input. + // + err = ProcessUserInput(argc, argv); + if (ERROR_SUCCESS != err) + { + goto clean0; + } + + if ( !GetDevicePath( + (LPGUID) &GUID_DEVINTERFACE_POWERFX, + devicePath, + COUNT_OF(devicePath))) + { + printf("Unable to get device path. Has the device driver been installed? \n"); + err = ERROR_OPEN_FAILED; + goto clean0; + } + + hDevice = CreateFile(devicePath, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL ); + + if (hDevice == INVALID_HANDLE_VALUE) { + err = GetLastError(); + printf("Failed to open device. Error %d.\n",err); + goto clean0; + } + + hCompletionPort = CreateIoCompletionPort(hDevice, NULL, 1, 0); + if (hCompletionPort == NULL) { + err = GetLastError(); + printf("Cannot open completion port %d.\n",err); + goto clean0; + } + + err = SendIO(hDevice, + hCompletionPort, + GetSetting(COMPONENT), + GetSetting(MAX_OUTSTANDING_IO), + GetSetting(DELAY), + (BOOLEAN)GetSetting(CANCEL)); + if (ERROR_SUCCESS != err) + { + goto clean0; + } + +clean0: + if (INVALID_HANDLE_VALUE != hDevice) + { + CloseHandle(hDevice); + } + if (NULL != hCompletionPort) + { + CloseHandle(hCompletionPort); + } + return err; +} + +DWORD Initialize( + _In_ ULONG Count, + _Out_ LPOVERLAPPED *pOverlappedPtr, + _Out_ PPOWERFX_READ_COMPONENT_INPUT *pInput, + _Out_ PPOWERFX_READ_COMPONENT_OUTPUT *pOutput + ) +{ + PPOWERFX_READ_COMPONENT_INPUT inputBuffer = NULL; + PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer = NULL; + LPOVERLAPPED pOverlapped = NULL; + DWORD err = ERROR_SUCCESS; + + pOverlapped = new OVERLAPPED[Count]; + if (NULL == pOverlapped) + { + err = ERROR_OUTOFMEMORY; + goto clean0; + } + + inputBuffer = new POWERFX_READ_COMPONENT_INPUT[Count]; + if (NULL == inputBuffer) + { + err = ERROR_OUTOFMEMORY; + goto clean0; + } + + outputBuffer = new POWERFX_READ_COMPONENT_OUTPUT[Count]; + if (NULL == outputBuffer) + { + err = ERROR_OUTOFMEMORY; + goto clean0; + } + + ZeroMemory(pOverlapped, + sizeof(OVERLAPPED)*Count); + + for (UINT i = 0; i < Count; i++) + { + // + // When the component number is set to UNUSED it indicates + // that a request has not been issued (or has completed) + // using this input buffer. Hence the buffer along with the + // overlapped structure at the corresponding index is available + // for issuing a request. + // + inputBuffer[i].ComponentNumber = UNUSED; + } + + ZeroMemory(outputBuffer, + sizeof(POWERFX_READ_COMPONENT_OUTPUT)*Count); + +clean0: + if (err != ERROR_SUCCESS) + { + delete[] pOverlapped; + delete[] inputBuffer; + delete[] outputBuffer; + } + else + { + *pOverlappedPtr = pOverlapped; + *pInput = inputBuffer; + *pOutput = outputBuffer; + } + return err; +} + +/*++ + +Routine Description: + + This function sends requests to the driver based on the settings passed + in the arguments. The requests are sent indefinitely until an error occurs. + + Depending on the number of maximum outstanding I/O requests, + an array of overlapped structures and input/output buffers is allocated. + The method then loops through the overlapped structure array + to issue asynchronous requests. When a request completes, the + overlapped structure for that request is not immediately re-used to issue + a new request. Instead the method goes in-order through the array to + ensure that each issued request is completed in a reasonable amount + of time and it is able to detect if one or more requests do not complete + at all (or within the specified timeout). + +--*/ +DWORD SendIO( + _In_ HANDLE DeviceHandle, + _In_ HANDLE CompletionPortHandle, + _In_ ULONG Component, + _In_ ULONG MaxOutstandingIo, + _In_ ULONG Delay, + _In_ BOOLEAN Cancel + ) +{ + LPOVERLAPPED pOverlapped = NULL; + LPOVERLAPPED pOv = NULL; + PPOWERFX_READ_COMPONENT_INPUT inputBuffer = NULL; + PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer = NULL; + DWORD err = ERROR_SUCCESS; + UINT outstandingIoCount = 0; + UINT index = 0; + + srand((DWORD)GetTickCount64()); + + err = Initialize(MaxOutstandingIo, + &pOverlapped, + &inputBuffer, + &outputBuffer); + if (ERROR_SUCCESS != err) + { + goto clean0; + } + + UINT k = 0; + + for (;;k++) + { + k = k % MaxOutstandingIo; + + if (UNUSED == inputBuffer[k].ComponentNumber) + { + // + // This indicates the input buffer and corresponding overlapped + // structure is available to issue a new request. + // + pOv = &pOverlapped[k]; + } + else + { + // + // Wait for request #k to complete. + // + DWORD completionStatus; + ULONGLONG startTime = GetTickCount64(); + + for(;;) + { + ULONG_PTR completedRequestIndex; + + err = WaitForIoCompletion(CompletionPortHandle, + &pOv, + &completionStatus); + if (ERROR_SUCCESS != err) { + goto clean0; + } + + completedRequestIndex = pOv-pOverlapped; + + printf(" Request %d completed with status 0x%X.\n", + (DWORD)completedRequestIndex, completionStatus); + + if (ERROR_SUCCESS != completionStatus) + { + if (!Cancel || + ERROR_OPERATION_ABORTED != completionStatus) + { + // + // If there is a setting to cancel requests it is ok for + // the requests to complete with aborted status. + // + err = completionStatus; + printf(" Unexpected completion status %d. \n", + completionStatus); + goto clean0; + } + } + else + { + // + // If request completed successfully verify the contents of the buffer. + // + if (! VerifyRequest(&inputBuffer[completedRequestIndex], + &outputBuffer[completedRequestIndex])) + { + printf(" Request completed with unexpected data in" + " output buffer. \n"); + err = ERROR_INVALID_DATA; + goto clean0; + } + } + + inputBuffer[completedRequestIndex].ComponentNumber = UNUSED; + outstandingIoCount--; + + if (k == completedRequestIndex) + { + // + // The request we are looking for has completed. + // + pOv = &pOverlapped[k]; + break; + } + else if (GetTickCount64() - startTime > REQUEST_TIMEOUT) + { + // + // The request we are looking for did not complete on time. + // + err = ERROR_TIMEOUT; + printf(" Request %d did not complete within the expected" + " time. \n", k); + assert(0); + goto clean0; + } + } + + } + + // + // We now have an overlapped structure to use. Set the input buffer + // to the target component and send the request. + // + ZeroMemory(pOv, sizeof(OVERLAPPED)); + inputBuffer[k].ComponentNumber = (Component == RANDOM_COMPONENT) ? + (rand() % COMPONENT_COUNT): + Component; + + if (0 != Delay) + { + // + // If there is a setting to introduce a delay, sleep and then send the + // request. + // + Sleep(rand() % Delay); + } + + err = SendRequest(DeviceHandle, + pOv, + &inputBuffer[k], + &outputBuffer[k]); + if (ERROR_SUCCESS != err) + { + goto clean0; + } + + outstandingIoCount++; + + printf(" Request number %d sent to component %d.\n", k, + inputBuffer[k].ComponentNumber); + + if (Cancel) + { + // + // If there is a setting to cancel the request then cancel it after + // issuing it. + // + CancelIoEx(DeviceHandle, + pOv); + } + } + +clean0: + if (outstandingIoCount > 0) + { + CancelIo(DeviceHandle); + for (index=0; index < outstandingIoCount; index++) + { + WaitForIoCompletion(CompletionPortHandle, + NULL, + NULL); + } + } + delete[] pOverlapped; + delete[] inputBuffer; + delete[] outputBuffer; + return err; +} + +BOOLEAN +VerifyRequest( + _In_ PPOWERFX_READ_COMPONENT_INPUT input, + _In_ PPOWERFX_READ_COMPONENT_OUTPUT output) +{ + return (output->ComponentData == ~input->ComponentNumber); +} + +DWORD +SendRequest( + _In_ HANDLE DeviceHandle, + _In_ LPOVERLAPPED OverlappedPtr, + _In_ PPOWERFX_READ_COMPONENT_INPUT inputBuffer, + _In_ PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer) +{ + + BOOL bResult = FALSE; + DWORD err = ERROR_SUCCESS; + + bResult = DeviceIoControl(DeviceHandle, + (DWORD) IOCTL_POWERFX_READ_COMPONENT, + (PVOID)inputBuffer, + sizeof(POWERFX_READ_COMPONENT_INPUT), + (PVOID)outputBuffer, + sizeof(POWERFX_READ_COMPONENT_OUTPUT), + NULL, + OverlappedPtr); + if (FALSE == bResult) + { + err = GetLastError(); + if (ERROR_IO_PENDING == err) + { + // + // This is not really an error. + // + err = ERROR_SUCCESS; + } + else + { + printf("Unable to send request. DeviceIoControl failed with " + "error 0x%X. \n", err); + goto clean0; + } + } +clean0: + return err; +} + +DWORD +WaitForIoCompletion( + _In_ HANDLE CompletionPortHandle, + _In_opt_ LPOVERLAPPED* POvPtr, + _Out_opt_ PDWORD CompletionStatus) +{ + BOOL bResult; + DWORD err; + DWORD numBytes; + ULONG_PTR completionKey; + LPOVERLAPPED ovPtr; + DWORD completionStatus; + + // + // Assume successful completion of I/O request + // + completionStatus = ERROR_SUCCESS; + + // + // Dequeue a completion packet + // + bResult = GetQueuedCompletionStatus(CompletionPortHandle, + &numBytes, + &completionKey, + &ovPtr, + REQUEST_TIMEOUT); + if (FALSE == bResult) + { + err = GetLastError(); + if (NULL == ovPtr) + { + printf("Could not dequeue a completion packet. " + "GetQueuedCompletionStatus failed with error 0x%X.", + err); + assert(0); + goto clean0; + } + + // + // We dequeued a completion packet for an I/O operation that failed. + // Make a note of the failure status, but we need to return success from + // this function because we got a completion packet (even though it was + // for a failed I/O operation). + // + completionStatus = err; + err = ERROR_SUCCESS; + } + + if (NULL != POvPtr) { + *POvPtr = ovPtr; + } + + if (NULL != CompletionStatus) { + *CompletionStatus = completionStatus; + } + + err = ERROR_SUCCESS; + +clean0: + return err; +} + +BOOL +GetDevicePath( + IN LPGUID InterfaceGuid, + _Out_writes_(BufLen) PWSTR DevicePath, + _In_ size_t BufLen + ) +{ + HDEVINFO HardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; + PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = NULL; + ULONG Length, RequiredLength = 0; + BOOL bResult; + HRESULT hr; + + HardwareDeviceInfo = SetupDiGetClassDevs( + InterfaceGuid, + NULL, + NULL, + (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)); + + if (HardwareDeviceInfo == INVALID_HANDLE_VALUE) { + printf("SetupDiGetClassDevs failed!\n"); + return FALSE; + } + + DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + bResult = SetupDiEnumDeviceInterfaces(HardwareDeviceInfo, + 0, + InterfaceGuid, + 0, + &DeviceInterfaceData); + + if (bResult == FALSE) { + + LPVOID lpMsgBuf; + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &lpMsgBuf, + 0, + NULL + )) { + + printf("SetupDiEnumDeviceInterfaces failed: %ws", (LPTSTR)lpMsgBuf); + LocalFree(lpMsgBuf); + } + + printf("SetupDiEnumDeviceInterfaces failed.\n"); + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + return FALSE; + } + + SetupDiGetDeviceInterfaceDetail( + HardwareDeviceInfo, + &DeviceInterfaceData, + NULL, + 0, + &RequiredLength, + NULL + ); + + DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, RequiredLength); + + if (DeviceInterfaceDetailData == NULL) { + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + printf("Failed to allocate memory.\n"); + return FALSE; + } + + DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + Length = RequiredLength; + + bResult = SetupDiGetDeviceInterfaceDetail( + HardwareDeviceInfo, + &DeviceInterfaceData, + DeviceInterfaceDetailData, + Length, + &RequiredLength, + NULL); + + if (bResult == FALSE) { + + LPVOID lpMsgBuf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &lpMsgBuf, + 0, + NULL + ); + + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); + printf("Error in SetupDiGetDeviceInterfaceDetail: %ws\n", (LPWSTR)lpMsgBuf); + LocalFree(DeviceInterfaceDetailData); + LocalFree(lpMsgBuf); + return FALSE; + } + + hr = StringCchCopy(DevicePath, + BufLen, + DeviceInterfaceDetailData->DevicePath) ; + + SetupDiDestroyDeviceInfoList(HardwareDeviceInfo); // It must be executed in both success and failure traces + LocalFree(DeviceInterfaceDetailData); + + return ( !FAILED(hr) ); // Result depends on StringCchCopy() +} + + diff --git a/pofx/WDF/App/PowerFxApp.vcxproj b/pofx/WDF/App/PowerFxApp.vcxproj new file mode 100644 index 00000000..32b104c3 --- /dev/null +++ b/pofx/WDF/App/PowerFxApp.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {3E233D0A-F988-4C9D-B15A-49B54025E11F} + $(MSBuildProjectName) + Debug + Win32 + {11455AB9-264E-4853-AE1E-615C1C5CA620} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + PowerFxApp + + + PowerFxApp + + + PowerFxApp + + + PowerFxApp + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + true + Level4 + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + true + Level4 + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + true + Level4 + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + true + Level4 + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pofx/WDF/App/PowerFxApp.vcxproj.Filters b/pofx/WDF/App/PowerFxApp.vcxproj.Filters new file mode 100644 index 00000000..8d16d12b --- /dev/null +++ b/pofx/WDF/App/PowerFxApp.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C8B06C0E-8C3C-4875-8327-4C592F8FD738} + + + h;hpp;hxx;hm;inl;inc;xsd + {8091FCE2-2451-4561-B199-CB021902EA1B} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {CC2C62EE-9135-480E-9DF9-5DB6BEB61DE8} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/pofx/WDF/App/UserInput.cpp b/pofx/WDF/App/UserInput.cpp new file mode 100644 index 00000000..dcd3ca75 --- /dev/null +++ b/pofx/WDF/App/UserInput.cpp @@ -0,0 +1,201 @@ +#include "include.h" + + +// +// The supported settings with their default values. +// +CONFIGURATION g_UserSettings[] = { + {COMPONENT ,0}, + {MAX_OUTSTANDING_IO ,1}, + {DELAY ,0}, + {CANCEL ,0} +}; + +DWORD +ProcessUserInput( + _In_ int argc, + _In_reads_(argc) PWSTR argv[] + ) +{ + DWORD err = ERROR_SUCCESS; + + for (int i=1; i < argc; i++) + { + PWSTR arg = argv[i]; + + // + // Setting must begin with / or - + // + if (arg[0] != L'-' && arg[0] != L'/') + { + printf("\n Invalid command-line argument %ws. \n", arg); + err = ERROR_INVALID_PARAMETER; + goto clean0; + } + + if (L'?' == arg[1]) + { + err = ERROR_INVALID_PARAMETER; + goto clean0; + } + + // + // Setting must contain ':' as the seperator. + // + PWSTR settingName = arg + 1; + PWSTR settingValue = NULL; + PWSTR seperator = wcschr(settingName, L':'); + if (seperator == NULL) + { + printf("\n Invalid command-line argument %ws. \n", arg); + err = ERROR_INVALID_PARAMETER; + goto clean0; + } + + settingValue = seperator + 1; + *seperator = L'\0'; + + // + // Store the value of the setting in the appropriate location. + // + err = ProcessSwitch(settingName, settingValue); + if (err != ERROR_SUCCESS) + { + goto clean0; + } + } + +clean0: + + if (ERROR_SUCCESS != err) + { + PrintUsage(argv); + } + return err; +} + + +PCONFIGURATION +LookupSwitch( + _In_ PWSTR Param) +{ + for (UINT i=0; i < COUNT_OF(g_UserSettings); i++) + { + if (_wcsicmp(Param, g_UserSettings[i].Option) == 0) + { + return &g_UserSettings[i]; + } + } + + return NULL; +} + +ULONG +GetSetting( + _In_ PWSTR Switch) +{ + return LookupSwitch(Switch)->Value; +} + +DWORD +ProcessSwitch( + _In_ PWSTR Param, + _In_ PWSTR Value) +{ + PCONFIGURATION config; + + config = LookupSwitch(Param); + if (NULL == config) + { + printf("\n '%ws' is not a valid switch", Param); + return ERROR_INVALID_PARAMETER; + } + + if (0 == _wcsicmp(Param, COMPONENT)) + { + if (0 == _wcsicmp(Value, L"*")) + { + config->Value = RANDOM_COMPONENT; + } + else + { + config->Value = _wtoi(Value); + if (config->Value >= COMPONENT_COUNT) + { + printf("Invalid component count '%ws' specified. " + "Component count must be less than %d. \n", Value, COMPONENT_COUNT); + return ERROR_INVALID_PARAMETER; + } + } + + } + else if (0 == _wcsicmp(Param, CANCEL)) + { + if (0 == _wcsicmp(Value, L"yes")) + { + config->Value = TRUE; + } + else if (0 == _wcsicmp(Value, L"no")) + { + config->Value = FALSE; + } + else + { + printf("'%ws' is not a valid option for '%ws'. Must be 'yes' or 'no'. \n", + Value, Param); + return ERROR_INVALID_PARAMETER; + } + } + else + { + // + // For MaxOutstandingIo or Delay this is sufficient. + // + config->Value = _wtoi(Value); + if (0 == config->Value) + { + printf("'%ws' is not a valid option for '%ws'", Value, Param); + return ERROR_INVALID_PARAMETER; + } + } + + return ERROR_SUCCESS; +} + + +void PrintUsage( + _In_ PWSTR argv[] + ) +{ + printf("\n This application can be used to send IO requests to a specified component"); + printf("\n of the WDF Power Fx sample driver. The requests will be sent indefinitely "); + printf("\n until a request fails or the user terminates the execution with ^C"); + printf("\n More details on the driver and application are in the associated help file."); + printf("\n "); + printf("\n Usage:"); + printf("\n %ws [/: ...]", argv[0]); + printf("\n "); + printf("\n Available settings are:"); + printf("\n "); + printf("\n /Component:"); + printf("\n Where can be a specific component number, or if you are running with"); + printf("\n the multi-component sample driver you can specify '*'(without quotes) and "); + printf("\n each request will be sent to a random component between 0 and "); + printf("\n (COMPONENT_COUNT-1). Default value is component 0."); + printf("\n "); + printf("\n /Delay:"); + printf("\n There is an approximate delay between 0 and ms"); + printf("\n between each request. Default value is 0 (no delay)."); + printf("\n "); + printf("\n /MaxOutstandingIO:"); + printf("\n The maximum number of I/O requests that can be outstanding at any point."); + printf("\n Default value is 1 (equivalent to synchronously sending requests)."); + printf("\n "); + printf("\n /Cancel:"); + printf("\n Can be 'yes' or 'no' (without quotes)."); + printf("\n When set to 'yes' the application will attempt to cancel the requests once "); + printf("\n sent. Default value is 'no'."); + printf("\n "); + return; +} + diff --git a/pofx/WDF/App/include.h b/pofx/WDF/App/include.h new file mode 100644 index 00000000..29f0cb5e --- /dev/null +++ b/pofx/WDF/App/include.h @@ -0,0 +1,95 @@ +/*++ + + +--*/ +#include + _Analysis_mode_(_Analysis_code_type_user_code_) + +#define INITGUID + +#include +#include +#include +#include +#include +#include +#include +#include "AppInterface.h" + +#define COUNT_OF(x) sizeof(x)/sizeof(x[0]) + +#define MAX_DEVPATH_LENGTH 256 + +#define COMPONENT L"Component" +#define MAX_OUTSTANDING_IO L"MaxOutStandingIO" +#define DELAY L"Delay" +#define CANCEL L"Cancel" + +typedef struct _CONFIGURATION { + PWSTR Option; + ULONG Value; +} CONFIGURATION, *PCONFIGURATION; + +#define RANDOM_COMPONENT (DWORD)-1 +#define UNUSED COMPONENT_COUNT +#define REQUEST_TIMEOUT 10000 +#define MAX_DEVPATH_LENGTH 256 + +PCONFIGURATION +LookupSwitch( + _In_ PWSTR Param); + +DWORD +ProcessUserInput( + _In_ int argc, + _In_reads_(argc) PWSTR argv[] + ); + +ULONG +GetSetting( + _In_ PWSTR Switch); + +DWORD +ProcessSwitch( + _In_ PWSTR Param, + _In_ PWSTR Value); + +void PrintUsage( + _In_ PWSTR argv[] + ); + +BOOL +GetDevicePath( + IN LPGUID InterfaceGuid, + _Out_writes_(BufLen) PWCHAR DevicePath, + _In_ size_t BufLen + ); + + +DWORD SendIO( + _In_ HANDLE DeviceHandle, + _In_ HANDLE CompletionPortHandle, + _In_ ULONG Component, + _In_ ULONG MaxOutstandingIo, + _In_ ULONG Delay, + _In_ BOOLEAN Cancel + ); + +DWORD +WaitForIoCompletion( + _In_ HANDLE CompletionPortHandle, + _In_opt_ LPOVERLAPPED* POvPtr, + _Out_opt_ PDWORD CompletionStatus); + +BOOLEAN +VerifyRequest( + _In_ PPOWERFX_READ_COMPONENT_INPUT input, + _In_ PPOWERFX_READ_COMPONENT_OUTPUT output); + +DWORD +SendRequest( + _In_ HANDLE DeviceHandle, + _In_ LPOVERLAPPED OverlappedPtr, + _In_ PPOWERFX_READ_COMPONENT_INPUT inputBuffer, + _In_ PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer); + diff --git a/pofx/WDF/Driver/MultiComp/WdfMultiComp.mht b/pofx/WDF/Driver/MultiComp/WdfMultiComp.mht new file mode 100644 index 00000000..f6dc1775 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/WdfMultiComp.mht @@ -0,0 +1,2807 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CC4DF5.1FA44F90" + +This document is a Single File Web Page, also known as a Web Archive file. If you are seeing this message, your browser or editor doesn't support Web Archive files. Please download a browser that supports Web Archive, such as Windows® Internet Explorer®. + +------=_NextPart_01CC4DF5.1FA44F90 +Content-Location: file:///C:/EEB34A10/WdfMultiComp.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="us-ascii" + + + + + + + + + + + + + + + + + + + + + +
+ +

F-state-based= + power +management for multi-component device

+ +

Summary<= +/o:p>

+ +

This sample demonstrates how a K= +MDF +driver can implement F-state-based power management for a device that has an +arbitrary number of components that can be individually power-managed.= +

+ +

 

+ +

The sample driver statically lin= +ks +to a helper library (WdfPoFx.lib) that encapsulates all of the generic code= + to +interact with the power framework. The device-specific code is implemented = +in +the driver itself, outside of the helper library. The idea behind this +organizing the code in this manner is make the helper library reusable by o= +ther +drivers. The directory structure for the sample is as follows:

+ +

-       +The helper library is implemente= +d in +the ‘lib’ subdirectory

+ +

-       +The interface between the helper +library and the rest of the driver code is defined in the ‘inc’ subdirectory

+ +

-       +The driver is implemented in the +‘driver’ subdirectory

+ +

Installation<= +o:p>

+ +

The +driver can be installed on a root-enumerated device using the devcon.exe to= +ol.

+ +

 

+ +

1.     +Obtain +the devcon.exe tool from the WDK

+ +

2.     +Copy +the driver binary, INF file and the KMDF coinstaller +to a directory on your test machine.

+ +

3.     +Run +the command “devcon.exe install WdfMultiComp.inf WDF\WdfMultiComp

+ +

Testing<= +/o:p>

+ +

Use +the PowerFxApp.exe application to send I/O requests to the driver. Running = +the +command “PowerFxApp.exe /?” displays detailed usage information= +.

+ +

Design overvi= +ew

+ +

The +driver controls a device that has more than one component. It needs to acce= +ss +one of those components for processing each I/O request that it receives. T= +he +specific component that it needs to access depends on the I/O request that = +it +receives.

+ +

 

+ +

In +order to support this, the driver creates one top-level, power-managed queu= +e to +receive all its requests. It also creates one secondary, power-managed queue +for each of its components. These secondary queues are called component que= +ues. +This is shown in the diagram below.

+ +

+ +

 

+ +

When +the driver’s dispatch routine for the top-level queue is invoked, it +examines the request to determine which component it needs to access in ord= +er +to process the request. Then, it forwards the request to the component queue +for the component that it needs to access for that request. When the +driver’s dispatch routine for the component queue is invoked, it acce= +sses +the component hardware to process the request.

+ +

 

+ +

The +driver’s top-level queue and component queues are all power-managed so +KMDF ensures that the device is in D0 while the queues are in a dispatching +state. The key point to note is that the driver is designed to maintain a c= +omponent +queue in a dispatching state only when the component is active. In order to +achieve this, the driver stops the component queue when the component becom= +es +idle and starts the component queue when the component becomes active. (To = +be +precise, this mechanism of stopping and starting queues is encapsulated in = +the +power framework helper library used by the driver). Thus, the driver is abl= +e to +ensure that when the component queue is in a dispatching state, not only is= + the +device in D0 but the component corresponding to that queue is also active. = +Thus +it is safe to access the component hardware when the component queue’s +dispatch routine is invoked.

+ +

Implementation +notes

+ +

The +driver uses the power framework helper library to manage most of its +interactions with the power framework. In order to achieve this, during dev= +ice +initialization, the driver performs the following tasks in its EvtDriverDeviceAdd callback:

+ +

-       +Enables +the helper library to register its own KMDF callbacks for PNP and +power-management of the device.

+ +

-       +Provides +the helper library with power-framework-related information about the devic= +e.

+ +

-       +Provides +the helper library with information about the component queues.<= +/span>

+ +

During +I/O request processing, the driver uses routines provided by helper library= + to +forward requests to component queues and also to complete requests.

+ +

 

+ +

The +main tasks performed by the power framework helper library on behalf of the +driver are:

+ +

-       +Registration +and unregistration with the power framework.

+ +

-       +Stopping +component queues when the corresponding components become idle and starting +them when the corresponding components become active.

+ +

-       +Notifying +the power framework when the device returns to its working state (D0) in +response to the system returning from a low-power state to the working state +(S0).

+ +

 

+ +

The +power framework helper library does not have any hardware-specific informat= +ion, +so any tasks that are specific to the device’s hardware are performed= + by +the driver. In this sample, the device hardware is represented by a very si= +mple +simulation. The notable hardware-specific tasks in this sample are:

+ +

-       +Accessing +component hardware to process I/O requests.

+ +

-       +Accessing +component hardware to change the component’s F-state.

+ +

As +mentioned earlier, the hardware access shown is this sample is entirely +simulated in software. This sample does not work with a real device –= + it +installs on a root-enumerated software device.

+ +

S0-idle power +management support

+ +

The +power framework helper library implements support for S0-idle power managem= +ent +for the device. Note that this is different from the component power manage= +ment +support that is enabled by the power framework. Component power management +enables individual components of the device to be power-managed by putting = +them +in different F-states while the device is in a working state (D0). S0-idle +power management for the device enables the device as a whole to be +power-managed by putting it into different D-states while the system is in a +working state (S0).

+ +

 

+ +

The +code to implement S0-idle power management support for the device is +conditionally compiled based on the value of the PFH_S0IDLE_SUPPORTED compiler switc= +h. If +the switch is set to a non-zero value, the code to implement S0-idle power +management support is included. If set to zero, the code is omitted thereby +resulting in a smaller binary size. Thus, a driver that requires S0-idle po= +wer +management for the device can use the power framework helper library’s +support for it but a driver that does not require it can reduce its binary = +size +by omitting the code that is specific to S0-idle power management.

+ +
+ + + + + +------=_NextPart_01CC4DF5.1FA44F90 +Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +UEsDBBQABgAIAAAAIQDp3g+//wAAABwCAAATAAAAW0NvbnRlbnRfVHlwZXNdLnhtbKyRy07DMBBF +90j8g+UtSpyyQAgl6YLHjseifMDImSQWydiyp1X790zSVEKoIBZsLNkz954743K9Hwe1w5icp0qv +8kIrJOsbR12l3zdP2a1WiYEaGDxhpQ+Y9Lq+vCg3h4BJiZpSpXvmcGdMsj2OkHIfkKTS+jgCyzV2 +JoD9gA7NdVHcGOuJkTjjyUPX5QO2sB1YPe7l+Zgk4pC0uj82TqxKQwiDs8CS1Oyo+UbJFkIuyrkn +9S6kK4mhzVnCVPkZsOheZTXRNajeIPILjBLDsAyJX89nIBkt5r87nons29ZZbLzdjrKOfDZezE7B +/xRg9T/oE9PMf1t/AgAA//8DAFBLAwQUAAYACAAAACEApdan58AAAAA2AQAACwAAAF9yZWxzLy5y +ZWxzhI/PasMwDIfvhb2D0X1R0sMYJXYvpZBDL6N9AOEof2giG9sb69tPxwYKuwiEpO/3qT3+rov5 +4ZTnIBaaqgbD4kM/y2jhdj2/f4LJhaSnJQhbeHCGo3vbtV+8UNGjPM0xG6VItjCVEg+I2U+8Uq5C +ZNHJENJKRds0YiR/p5FxX9cfmJ4Z4DZM0/UWUtc3YK6PqMn/s8MwzJ5PwX+vLOVFBG43lExp5GKh +qC/jU72QqGWq1B7Qtbj51v0BAAD//wMAUEsDBBQABgAIAAAAIQBreZYWgwAAAIoAAAAcAAAAdGhl +bWUvdGhlbWUvdGhlbWVNYW5hZ2VyLnhtbAzMTQrDIBBA4X2hd5DZN2O7KEVissuuu/YAQ5waQceg +0p/b1+XjgzfO3xTVm0sNWSycBw2KZc0uiLfwfCynG6jaSBzFLGzhxxXm6XgYybSNE99JyHNRfSPV +kIWttd0g1rUr1SHvLN1euSRqPYtHV+jT9yniResrJgoCOP0BAAD//wMAUEsDBBQABgAIAAAAIQAw +3UMpqAYAAKQbAAAWAAAAdGhlbWUvdGhlbWUvdGhlbWUxLnhtbOxZT2/bNhS/D9h3IHRvYyd2Ggd1 +itixmy1NG8Ruhx5piZbYUKJA0kl9G9rjgAHDumGHFdhth2FbgRbYpfs02TpsHdCvsEdSksVYXpI2 +2IqtPiQS+eP7/x4fqavX7scMHRIhKU/aXv1yzUMk8XlAk7Dt3R72L615SCqcBJjxhLS9KZHetY33 +37uK11VEYoJgfSLXcduLlErXl5akD8NYXuYpSWBuzEWMFbyKcCkQ+AjoxmxpuVZbXYoxTTyU4BjI +3hqPqU/QUJP0NnLiPQaviZJ6wGdioEkTZ4XBBgd1jZBT2WUCHWLW9oBPwI+G5L7yEMNSwUTbq5mf +t7RxdQmvZ4uYWrC2tK5vftm6bEFwsGx4inBUMK33G60rWwV9A2BqHtfr9bq9ekHPALDvg6ZWljLN +Rn+t3slplkD2cZ52t9asNVx8if7KnMytTqfTbGWyWKIGZB8bc/i12mpjc9nBG5DFN+fwjc5mt7vq +4A3I4lfn8P0rrdWGizegiNHkYA6tHdrvZ9QLyJiz7Ur4GsDXahl8hoJoKKJLsxjzRC2KtRjf46IP +AA1kWNEEqWlKxtiHKO7ieCQo1gzwOsGlGTvky7khzQtJX9BUtb0PUwwZMaP36vn3r54/RccPnh0/ ++On44cPjBz9aQs6qbZyE5VUvv/3sz8cfoz+efvPy0RfVeFnG//rDJ7/8/Hk1ENJnJs6LL5/89uzJ +i68+/f27RxXwTYFHZfiQxkSim+QI7fMYFDNWcSUnI3G+FcMI0/KKzSSUOMGaSwX9nooc9M0pZpl3 +HDk6xLXgHQHlowp4fXLPEXgQiYmiFZx3otgB7nLOOlxUWmFH8yqZeThJwmrmYlLG7WN8WMW7ixPH +v71JCnUzD0tH8W5EHDH3GE4UDklCFNJz/ICQCu3uUurYdZf6gks+VuguRR1MK00ypCMnmmaLtmkM +fplW6Qz+dmyzewd1OKvSeoscukjICswqhB8S5pjxOp4oHFeRHOKYlQ1+A6uoSsjBVPhlXE8q8HRI +GEe9gEhZteaWAH1LTt/BULEq3b7LprGLFIoeVNG8gTkvI7f4QTfCcVqFHdAkKmM/kAcQohjtcVUF +3+Vuhuh38ANOFrr7DiWOu0+vBrdp6Ig0CxA9MxEVvrxOuBO/gykbY2JKDRR1p1bHNPm7ws0oVG7L +4eIKN5TKF18/rpD7bS3Zm7B7VeXM9olCvQh3sjx3uQjo21+dt/Ak2SOQEPNb1Lvi/K44e//54rwo +ny++JM+qMBRo3YvYRtu03fHCrntMGRuoKSM3pGm8Jew9QR8G9Tpz4iTFKSyN4FFnMjBwcKHAZg0S +XH1EVTSIcApNe93TREKZkQ4lSrmEw6IZrqSt8dD4K3vUbOpDiK0cEqtdHtjhFT2cnzUKMkaq0Bxo +c0YrmsBZma1cyYiCbq/DrK6FOjO3uhHNFEWHW6GyNrE5lIPJC9VgsLAmNDUIWiGw8iqc+TVrOOxg +RgJtd+uj3C3GCxfpIhnhgGQ+0nrP+6hunJTHypwiWg8bDPrgeIrVStxamuwbcDuLk8rsGgvY5d57 +Ey/lETzzElA7mY4sKScnS9BR22s1l5se8nHa9sZwTobHOAWvS91HYhbCZZOvhA37U5PZZPnMm61c +MTcJ6nD1Ye0+p7BTB1Ih1RaWkQ0NM5WFAEs0Jyv/chPMelEKVFSjs0mxsgbB8K9JAXZ0XUvGY+Kr +srNLI9p29jUrpXyiiBhEwREasYnYx+B+HaqgT0AlXHeYiqBf4G5OW9tMucU5S7ryjZjB2XHM0ghn +5VanaJ7JFm4KUiGDeSuJB7pVym6UO78qJuUvSJVyGP/PVNH7Cdw+rATaAz5cDQuMdKa0PS5UxKEK +pRH1+wIaB1M7IFrgfhemIajggtr8F+RQ/7c5Z2mYtIZDpNqnIRIU9iMVCUL2oCyZ6DuFWD3buyxJ +lhEyEVUSV6ZW7BE5JGyoa+Cq3ts9FEGom2qSlQGDOxl/7nuWQaNQNznlfHMqWbH32hz4pzsfm8yg +lFuHTUOT278QsWgPZruqXW+W53tvWRE9MWuzGnlWALPSVtDK0v41RTjnVmsr1pzGy81cOPDivMYw +WDREKdwhIf0H9j8qfGa/dugNdcj3obYi+HihiUHYQFRfso0H0gXSDo6gcbKDNpg0KWvarHXSVss3 +6wvudAu+J4ytJTuLv89p7KI5c9k5uXiRxs4s7Njaji00NXj2ZIrC0Dg/yBjHmM9k5S9ZfHQPHL0F +3wwmTEkTTPCdSmDooQcmDyD5LUezdOMvAAAA//8DAFBLAwQUAAYACAAAACEADdGQn7YAAAAbAQAA +JwAAAHRoZW1lL3RoZW1lL19yZWxzL3RoZW1lTWFuYWdlci54bWwucmVsc4SPTQrCMBSE94J3CG9v +07oQkSbdiNCt1AOE5DUNNj8kUeztDa4sCC6HYb6ZabuXnckTYzLeMWiqGgg66ZVxmsFtuOyOQFIW +TonZO2SwYIKObzftFWeRSyhNJiRSKC4xmHIOJ0qTnNCKVPmArjijj1bkIqOmQci70Ej3dX2g8ZsB +fMUkvWIQe9UAGZZQmv+z/TgaiWcvHxZd/lFBc9mFBSiixszgI5uqTATKW7q6xN8AAAD//wMAUEsB +Ai0AFAAGAAgAAAAhAOneD7//AAAAHAIAABMAAAAAAAAAAAAAAAAAAAAAAFtDb250ZW50X1R5cGVz +XS54bWxQSwECLQAUAAYACAAAACEApdan58AAAAA2AQAACwAAAAAAAAAAAAAAAAAwAQAAX3JlbHMv +LnJlbHNQSwECLQAUAAYACAAAACEAa3mWFoMAAACKAAAAHAAAAAAAAAAAAAAAAAAZAgAAdGhlbWUv +dGhlbWUvdGhlbWVNYW5hZ2VyLnhtbFBLAQItABQABgAIAAAAIQAw3UMpqAYAAKQbAAAWAAAAAAAA +AAAAAAAAANYCAAB0aGVtZS90aGVtZS90aGVtZTEueG1sUEsBAi0AFAAGAAgAAAAhAA3RkJ+2AAAA +GwEAACcAAAAAAAAAAAAAAAAAsgkAAHRoZW1lL3RoZW1lL19yZWxzL3RoZW1lTWFuYWdlci54bWwu +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CC4DF5.1FA44F90 +Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CC4DF5.1FA44F90 +Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +H4sIAAAAAAAEC+ydb2wd15neL0VRpCnalERJIWxuaN6SMDeUIkZxDAKWnSvTVFRAiwbbKGBiyVED +GVULf/CHpFAkVSu1bIlWgLofCLAQKoBYEFDXf4D+2ezqg4sSC6RQ0TgJ2iygdtVWaKOEWBioYsmK +FVNSf7+Ze4bDu7nk5SWPzNge5NG8Z+7MPM95z5kzcx4PJw2FQuFVsBFsu/vgwSvNhcLmRgrlZePf +LhSKxULhyZHf21coNBTe/d8NhZ9vKhSawg7l9RmO+zfrC4WWdYVCqWHhj6X/x4//Z32BExR2gCcB +pxtoYMcu4k1g3aaZv/SwI2W473fAMeC+g6V1yX5QsJz5cndpfaEtiQuFz5aasvh3SoXCVra3AKQU +9nHSz5Y2ZL+3lgozgfOdd975Azn/rHCq0FkYLPSUj/s2a8/tb0+BznLMqsAxrgp9IOxzgPgo+DNw +CvwIvJEre/wg+JuFb/JvWM58OUTWZz7+zZHnkO/BgweFXtaC455LVhWx9X4ZfAcYu7j/IPgaeBK4 +9IOWMhqS2s7/5u/5Rd7X2XAQVFuHnMgZYs5/plp+XqPTzdA1zpho+tx/LkwRhCWfk3wcfl+4Dnye +LsSe1noLNu9NVhWxWkNu/ama1tC27jsIVtKWQR+nOVPkn3K7PrDP94MuYJ/P9+v1pXQ/fkquo5+y +vgLcr6VUaHya2GO9jppYF0r2xzcLP05wKunjlgsNDQ1n+fmLpcJ6eV1aSg3rSqw9h8ezSvqw6zde +f/15c5SH28N+XkMtpXXL4m/g+G2l+evZcg9oAd8G6spve7b8W0fut5eIvwrCQv9MFnW2AnMg/rRw +ErxV+EkC45NZLvK52cO+8g+DwJ/nM7du9/yh/dR4ABwFfwpOgh+DN3Plx4l3g5X0l60cL/cjoLMc +U9l8336o48BBNFQbA9we8mOuQg6NQzs+QbwJeP1fprG+QTwEwlJuygdcy4WbbZdbZ9tmRPPNtivN +r2280nx844xoHeV3+2xYwnEb2HB84w9av7vxSiv7t74KjlE+xjHP8NsmcAxuxx7P0Q/CEs7RyIZj +G99ufnXjZTgvN4fjbtIQl5c47v22y80ftL0NZrLjPuC4Geq6GN8HbT9ofb/tSiv1zHSaoyvl46rV +NV8/NFPfy9T7cqudogXsKa+fZ90JKvtqaCP7c2g7494yah07Qz99lOMOgKOgkiuUH+e3lV4XQSun +WjCOFtlAur1XJmNqidjcpWNl43pz4O9hDKOtNz1daky2sbmwvpT+brwPvAuug8XG2u9zRbyT4ETh ++wXxen6sbZDP5XOlNK9qoEe8kKzS+M//aueXhg/+xe8Ot5TWN3yNbfuBGte7TvZxPP7c2yn++fMH +DzZ9+ebNm0mXDet7/+Pbf/J3nrrz5bc/2/Qfif/tN//FP3r+v/+z39nTWprn9Xy2b+W5etjWAr4N +1Os+YdtnidX8fXACvAOoYdZfOO7MU5Tdx2vHBU3JOrST20Ps/l+nPJjskf4Trr0min9SOA6+x31I +GB9fcGw4D7ue6ecf9Xr+Sg2ck63zvNU0fJ99rNcPwUmQavgevCfhF8bfq0vD/fv3OWNtGuRWw2mQ +ajgJ72n4hfHJujTcu3ePM9amQW41nAWphtPwnoVfGJ+uS8Pc3BxnrE2D3GoYA6mGs/COwS+Mz9al +4cMPP+SMtWmQWw3jINUwBu84/MJ4rC4Nv/71rzljbRrkVsM5kGoYh/cc/MJ4vC4Nd+/e5Yy1aZBb +DedBquEcvOfhF8bn6tLwwQcfcMbaNMithgmQajgP7wT8wvh8XRp+9atfccbaNMithkmQapiAdxJ+ +YTxRl4Y7d+5wxto0yK2GCyDVMAnvBfiF8WRdGt5//33OWJsGudVwEaQaLsB7EX5hfKEuDbdv3+aM +tWmQWw1TINVwEd4p+IXxxbo03Lp1izPWpmGK/dQwDVINU/BOwy+Mp+rS8N5773HG2jTIrYZLINUw +De8l+IXxdF0afvnLX3LG2jTIrYY/BqmGS/C+Dr8wvlSThj6ObwPNFc9zPoP9IRgDPoMNlOafz1pK +TVWe5+Y9oZXMb0ul0oNt8G2Cez1oAL1lIPM5wvIyH/us9i0wBHzecO0S1p7FPLlw/mQd/gk5yD+n +sG/2rPTv2fE4CPNMz/PVjVOtP2lL0EwsWsN5zGWI2fVMTvteyuVl3htRez+wHdRg7tW9A5j7pby0 +H5e9tHAOzxf4zd0BcBSo/xT4OXgjV+4kHgQrmS97DvX77Jerb9W2epl9vwPU6uIxg+Br4EngYn1a +yngYXlllfm5SobXqlVVqDeXVaMvQd0j9gjme/dI26QJpv5y/3teX5scH9/spuFLer6X0m70yvaBf +JDhF3xRvcpk+PK+sGr/XzMPyytJ5zluFWbyy2fK8J+Qin5s9aPJaGAZ0y2RM7MjFtovbq137P+K3 +k+AXgCxzj0jLj7PeDVZy7W/leLkfAZ3l+KMcBw6i4XVQbR36t7kKOTR+FpjjJ8Am4PV/uXVxr+y1 +xy63HntsRjS/9tiVxC/TgwKLemV53+ldvKdZfKhZjnmmzD0L91Je2Sx+17v4Xvh1mef12mNLe2Xf +fexy8/HH3gbzXtlxjlvKKzv+GP7eY/h7IOg0R1fI0Si6nwZhCb7CBjbk64fmVjTjt1X3ykLfDH01 +tJHXZWg7494yGKb2EpaX+dg2Df3RH0M/fZT4ADgKKrlCeTWui6AVmgXjaJENpC3zyvqJu4Bj6ko9 +Mf2wGwlO4BcJroT5MbVGT+z31rwn9g75OgFuAGqY9Quu31X1xH7Ik98P8aB+nsA4ricW6vUz6uR4 +3QTk/yGlnycwjuuJqUFuNZwGqYaT8J5GgzCO64mpQW41nM00nIb3LPzCOK4npga51TCWaTgL7xj8 +wjiuJ6YGudUwnmkYg3ccfmEc1xNTg9xqOJdpGIf3HPzCOK4npga51XA+03AO3vPwC+O4npga5FbD +RKbhPLwT8AvjuJ6YGuRWw2SmYQLeSfiFcVxPTA1yq+FCpmES3gvwC+O4npga5FbDxUzDBXgvwi+M +43piapBbDVMgHScvwjsFvzCO64mpQW41TINUwxS80/AL46kF98c+9vP5g2XR/45TqyemBrnVcAmk +GqbhvQS/MI7rialBbjUET0z+H/J08PMExiv3xI5z/ldBpSf2dGl+Lvxx8r7+C3W1zr8AbwLbVt/r +q48laCYWH5n39YsavS/1nwL3wRsglDuJB8FK5r+ew+vpo5zz+gx8EFRbh2veOVGIW7j+D1A+CkI+ +Qn6cR65V76tSayivRluG3JCSBXM2Pa1+0AVWw/vS23mQ4BS5F2/m52nR3xOrxu+8+mF5X+l85i3q +LdL5TchFPjd70ERfrdv7+jnHngQPAFnmfvCp9+U4EHwV42eBOX4CbAKJj9S6uPd1s533xNp5T6x9 +pvlmO++J4X/pKYFFva+8j/QqXtIxfCX8s8xTOlYee0bR4TUXluApNbLhGP7Vq/hY+G+Z93WzfWnv +6/123hNr5z0xND/DeTaBDzhuKe/rg3beE2vnPTEQjjNHS3lf+fqhuRXN+GfVva/QN0NfDW3kdRnG +JuPeMhim9hKWl/nYNu0EbeVftpbjR1kfAI75lVyh/Di/7QYruScGrZxmwThaZIOabMt9rG3fLrAa +3pe+170EJ/CFBHfDZXtfr6x57+sG+ToB7gFqmPWLFnL9FGXb3WvEZSXvg/2MJ7+f4TXdT2Ac1/sK +9ZpDt+N1E5D/Z5TuJzCO632pQW41nAaphpPwnkaDMI7rfalBbjWczTSchvcs/MI4rvelBrnVMJZp +OAvvGPzCOK73pQa51TCeaRiDdxx+YRzX+1KD3Go4l2kYh/cc/MI4rvelBrnVcD7TcA7e8/AL47je +lxrkVsNEpuE8vBPwC+O43pca5FbDZKZhAt5J+IVxXO9LDXKr4UKmYRLeC/AL47jelxrkVsPFTMMF +eC/CL4zjel9qkFsNU6AJyP8zSvcTGMf1vm6UudUwnWmYgncaDcI4rvelBrnVcCnTMA3vJfiFcVzv +Sw1yqyF4X/L/jKeD+wmMP/W+lvve1/8ln8dBeBZvItb3+kl7gmZi8ZF5Xw9q9L7UfwpsZ+LwRrk+ +ln1OHAQrec73HG3gt9n7qsyP88i16n1Vag3l1WjLvnJbslowZ6ucp630byT1dj7TIE5xbYk38/O0 +6N5XNX4uj4fmfaXzmbcKnXhfnXhflkMu8rnZg6YWMAy8ztSY9wT6y9ud74f2c58D4Ci4D5zPfIaN +ZDkrP0680jn+Vs6hprXy3pdz4YOg2jrkx1yFHBo/C8zxE2ATSHyk1sW9r9c2897XZt772sx7X5t5 +7wv/S08JLOp95X2kd/GSZvGV8M8yT2m2PPaMosO2DUve+5rFv3oXHwv/LfOwXtu8tPf13c2897WZ +977Q/Awn3gSOc9xS3tfxzbz3tZn3vkA4zhwt5X3l64fmVjTjn1X3vir7amgj+3NoO+PeMlbifVVy +hfJqXBdBKzIXjKNFNni9xPC+9L22NYgT+EKCq2DZ3tc/XPPel57XCbCNjkANs37B9buq3tccT35z +eE3bG4RxXO8r1Gsr9XK8bgLyz1Hazv1BHZZD37K+IWbXM44V9q1G8BToLMesCrX+LaQa5FbDadap +hpPwnoZfGMf1vtQgtxrOZhpOw3sWfmEc1/tSg9xqGMs0nIV3DH5hHNf7UoPcahjPNIzBOw6/MI7r +falBbjWcyzSMw3sOfmEc1/tSg9xqOJ9pOAfvefiFcVzvSw1yq2Ei03Ae3gn4hXFc70sNcqthMtMw +Ae8k/MI4rvelBrnVcCHTMAnvBfiFcVzvSw1yq+FipuECvBfhF8ZxvS81yK2GqUzDRXin4BfGcb0v +NcithulMwxS80/AL47jelxrkVsOlTMM0vJfgF8ZxvS81yK2G4H3JP8fTwXaeh9RhuY99vD8udt8M ++zRXPLc5/z0OXgWflPe+PizXOcwbmyjre311c4JmYvGReV/3G9bzjDOYzJFsV+dxof3oCvPzXwrr +KW9jhy+yvl8u+3w0CH7bva+D1MHn4GrrkJN8frwGDnDMURDyEfJzvZu/j2X7GZPIw+Ra+j5YpdZQ +Xo22DHmy6kX+sU/9pnnaSr2v+w1PF7avE+vJvXg6P0+L7n1V47e5H9Z7X/caGgv3Gr5U+Mw6YdyY +5SKfmz1ooq/W7X3do1LOh7bT+ckyPGn5ceLdYCXX/laOt4983Lwvr/8hkvYN6jYEwhL8J+6NhSM9 +Q42jPSVBvL9wvXt/Yba7JBpH+d1chyUct4ENs90jjTe69zeyf+M1cJXyVY4JntLV8tjjOfrDCViH +c9iWV7v3FK51D8E5VAjHHelJtS523Cs9Q4VjPXtAKTvuGMeVOOlixx3rGWl8pWd/I/XMdJqj/eXj +qtU1Xz80U98h6j3U+Bx1aAGhbz9P3AlC3wx9tYNt9i+67IL7Wi9lwTC1N1lVxHT15Hwe67IVGD8K +DoCjoJIrlFfjuqg2ji42pq70bx7nGnYXtq4T6wpzDYKre5ne1/vb7w8fPPiVF9byd8Dm6Ay271b+ +oYZZv6A/rar39SE8HzY08MwkjBdy5du4Hx32Ly6Hv+Y7cd2ydf5Y9wnHqvnrlAdBqFcHXO7TBOT/ +kPvCNu4P6rCcPzbE7Loq3pca5FaDY1WqoRHeDfAL48a6NNT6HTA1yK0G7yuphg3wPgK/MN5Ql4Za +vwOmBrnVYLumGh6Btw1+YfxIXRpq/Q6YGuRWQ3umoQ3edviFcVtdGmr9Dpga5FZDR6ahHd4O+IVx +e10aav0OmBrkVsP2TEMHvNvhF8YddWmo9TtgapBbDV2Zhu3wdsEvjLfXpaHW74CpQW41cOst98ku +eLvhF8ZddWmo9TtgapBbDcVMQze8RfiFcXddGmr9Dpga5FaD4196bRbh7YNfGBfr0lDrd8DUILca +HPtTDX3w9sMvjPvq0lDrd8DUILcadmQa+uHdAb8w7q9LQ61/86gGudWwK9OwA95d8AvjHXVpqPU7 +YGqQWw1fyDTsgnc3/MJ4V00abNM28Kn3VSj8mry6hGdx+/hM95ONpZ4Exkk5n7MQs+uZWp7R889N ++zhoCNif0r8tW/zb+XoHtXhf6l/POXey/iLrUO4kHgQrmf96DvuLz3i5+jq9KS8Lv832Mlu/A5CS +LB4zCL4GngQu5qSljFq+93WQfVfifYV8hPw4jyxxzjO2Pw+Da8n7qtQayqvRlvm+W6Tq5Xb9a3+f +s1LvS2/n8wnWJ/6X5dw8Lbr3VY3f5n5Y3lc6n/lSYRfe165kbtOY5SKfmz1o8loYLreHGn0WtG2M +vVaMvZ5C+7n9ADgKnLfRhcl36seE8mrM8bdyXrk/bt6X1/9S3tf14lDj1WJJFK4X9yf+l54SWNT7 +yvtIh/CSRvGV8M8yT2kU7hI5HQW2bVjy3tco/tUhfCz8t8zDul5c2vu6URwqzBb3gHnva5bjSnSQ +xfhmi/h1Rfw68AyCNgFztJT3la8fmhvRjH9W3fsKfTP01Xw/z/ftXvgFI/TeZFUR09WX9L4quUJ5 +Na6LoFWBpDcbR/Ox93rbtwusxt886nvtSLAu8b8s58ZUZsnp8rlSmrvOpDjzQnkzq5k/P7Gx6cW1 +7n3pefE/6hrX+/JZugO/aWeC9Nk6tGsL7RpipJyxHc2v4+xTwNwau9TqfYV6DcDrsU1A/g7uDTsT +GMf1vtQgtxo2ZBoa4d2ABmEc1/tSg9xq8L6S5mEDvI/AL4zjel9qkFsNtmuq4RF42+AXxnG9LzXI +rYb2TEMbvO3wC+O43pca5FZDR6ahHd4O+IVxXO9LDXKrYXumoQPe7fAL47jelxrkVkNXpmE7vF3w +C+O43pca5FZDd6ahC95u+IVxXO9LDXKroZhp6Ia3CL8wjut9qUFuNTgGp9dmEd4++IVxXO9LDXKr +wbE/1dAHbz/8wjiu96UGudWwI9PQD+8O+IVxXO9LDXKrYVemYQe8u+AXxnG9LzXIrYYvZBp2wbsb +fmH8qfe13L953EI+XcKzuH281PNk40wxgXFS9jpoA/qFIaYY3fv6fI3el/rXI2iE9RdZh7LPaIPg +k+59hXyE/DiPLJGXteh9VWoN5dVoy3zfJQVJn3bOvY/YcdZ7b+rJzn/fb30p3Y+fCu73U3AFuF9L +6Td/615vZ1+C9fRF8XR+nhbd+6rG34Duh+V9pfOZLxW+gvf1lfL8JuQin5s9aGoBw8AxRo0+C4bY +djHm0k7ux2H7AcpHgX5uI+t9rMlyVl6NOf5Wzief84TOcuxcr5dYcAU9VA/8dRgPgmrr0L/NVcih +8bPAHD8BNgGv/6W8ryO9vPfVi8/VWyoc6eW9L/wvPSWwqPeV95Gu4SVdxVe6yjHPlLmvwl0iHgW2 +bVi8Dl1sy6v4V9fwsfDfMu/rCAkf4rfFjnull/e+ennvC82B7xjHlTjpYscd6+W9r979jdQz02mO +lvK+8vVDcyOa8c+qe1+VfTW0kX0+tJ1xrn/tpVhezmTxOrZ0Avumy1ZgnH/vq5IrlFfjughaoTxD +mhJu2y4f72N7P+gCq+F96Xu9mGAdvpDYnR9Ta/S+Nq5570vPi/9R17jel8/SA3hNIwnSZ+vQri20 +a4iRcsZ2tH95bdbrfYV6DcPreZqA/APcG0YSGMf1vtQgtxo2ZBoa4d2ABmEc1/tSg9xq8L6S5mED +vI/AL4zjel9qkFsNtmuq4RF42+AXxnG9LzXIrYb2TEMbvO3wC+O43pca5FaDY3Gah3Z4O+AXxnG9 +LzXIrYbtmYYOeLfDL4zjel9qkFsNjtdpHrbD2wW/MI7rfalBbjV0Zxq64O2GXxjH9b7UILcaipmG +bniL8AvjuN6XGuRWQ1+moQhvH/zCOK73pQa51eDYn/aHPnj74RfGcb0vNcithh2Zhn54d8AvjON6 +X2qQWw27Mg074N0FvzCO632pQW41fCHTsAve3fAL40+9r+V6X58jny5h3mgf1/cq9SYwTspeB23g +YXtf+2r0vtS/Hn2HWX+RdSh3Eg+CT7r3FfIR8uM8skRe1qL3Vak1lFejLUM/tupF/rFPO2fbx9px +tgushvelt/NygvX0RfF0fp4W3fuqxu+8+mF5X+l85kuFb+F9fas8vwm5yOdmD5pawDCwPdTos2CI +bRdjLu3kfhy2H6B8FOjnNrJ+mTVZzsqrMcffyvnkc55g/zOmu+S9iYfqfR2Ev5rv5fbQv81VyKFx +pfeV+Egk7Rv8NgTC4rXgwjhfuN7He199vPfVVyLen/hfekpgUe8r7yMdwksaxVfCP2t8hnNuAqO9 +6dgzSmzbhiVlTr2vUfyrQ/hY+G+Zh3Wdyql1seNu9PHeVx/vfaE58M1yXIm6LnbcbB/vffXx3hcI +x5mjpbyvfP3Q3Ihm/LPq3ldlXw1tZJ8PbWcMdQKGqb2E5WU+tk07gf3RJfTTvPdVyRXKq3FdBK1Q +LxhHi2woXyN/bUxd6d886nsdSrAOX0jszo+pNXpfm9e896XnZfse4h9qmPWLFnL9FGXbne6cLCv5 +1r3P0sN4TYcTpM/WoV3lCjFEZ/r5x3aVt1ID1y1b53W6TzjW83yd8iAI9XoJXvdpAvIPc284nMA4 +rvelBrnVsCHT0AjvBjQI47jelxrkVkPwvuQfxms6nMA4rvelBrnVYLumbfEIvG1oEMZxvS81yK2G +9kxDG7zt8AvjuN6XGuRWQ0emoR3eDviFcVzvSw1yq2F7pqED3u3wC+O43pca5FZDV6ZhO7xd8Avj +uN6XGuRWQ3emoQvebviFcVzvSw1yq6GYaeiGtwi/MI7rfalBbjU4jqbXZhHePviFcVzvSw1yq8Gx +P9XQB28//MI4rvelBrnVsCPT0A/vDviFcVzvSw1yq2FXpmEHvLvgF8ZxvS81yK2GL2QadsG7G35h +/Kn3tVzv6wXy6RLmjfbxEr7XTF8C46TsddAGmiueiWp5Rs8/N+3jHEPA/pR6DIv/zeP9hlM86w0m +14D8yg1anCMcAEeB34Q6xXobO7yRK3cSD4Lfdu9rsfnuQeoXcpLPTwttVS0/1xngZ2xMk8jD4Fr6 +m8eYbRnyRK0XzNnsl/bTLrAa3tf9hjf5+z5xir4p3szP06J7X9X4be6H5X3d47vB9/jO/WfWCWO+ +d1/ORT43e9BEXy0MgzagRp8FQ2y7GOf7tvscAF77fsfoJOvt7PBmrrwac/zgKXzcvC+v/8uti3tf +R3out4728F37npnmIz1Xmq93X2me7Z4Ri37rfrb7B603uq+0sn/rNXCV8lWOeYa22QSulseeUWLb +Nix57+tq99vN17ovwzn/rXv/BvEyY9Zix73Sc7n5WM/bYP5b937va6lv3R/r+UHrKz1XWqlnptMc +LfWt+3z90Ex9L1Pv6t+6r+yr+X4exib7di331U7287pwCf00731VcoXyalwXQSvUC8bRIhvUZFtW +jqkr9b7m+K7r1nXiBN+OEtwV6/ne11/87vBa/97XCduU8YwaZvf2FnJd6TutxPv6sOE43wn5Hs9M +wvj4Aq58G/eX25VHhbq9L79bYr06qJfjdROQ/0PuC9u4P6jDcuC1viFm1zOLaaj1W/dqkFsNpzMN +J+E9Db8wjvutezXIrYazmYbT8J6FXxjH/da9GuRWw1im4Sy8Y/AL47N1tcVyvvcltxrGMw1j8I7D +L4zH6tKwnO99ya2Gc5mGcXjPwS+Mx+vSsJzvfcmthvOZhnPwnodfGMf91r39QW41TGQazsM7Ab8w +jvutezXIrYbJTMMEvJPwC+OJutpiOd/7klsNFzINk/BegF8YT9alYTnf+5JbDRczDRfgvQi/MI77 +rXvbQm41TGUaLsI7Bb8wjvutezXIrYbpTMMUvNPwC+OputpiOd/7klsNlzIN0/Begl8YT9elYTnf ++5JbDX+cabgEL/+/PzwPqUM94V652H0z7NNc8dzms9px8Cpw/jtQmn+2fLo0/zdQLaWGdfyUvGOw +btPMXxIWfuQ/LG+8/vrzSEzmaWHt9rAf3tSDbaV0DrCe7TRv/jn3OYrlZf5vaTzPt8AQ8NnDtUtY +e5amdFNhud6X3/uyzmHe6HlmuqdaSz0JmonFR/ate72DWrwv9Z9Cu3/X8Ua5PpY7wSD4pHtflflx +HrlWva9KraG8Gm0Zrn26xII5W+U8beXf+3qTv3MUpxL/S68nN0+L7n3J95v4HW8elveVzmfe4ltf +Ip3feD1XatuDphYwDNqAGvOeQH95u+NgaD/3OQCOAv3uk6w/z5osZ+XHiXeDlVz7WzleTR837yvx +kVoX976uFy+3Xi3OiObrxSuJ/6WnBBb1vvI+0iG8pFF8JfyzzFPye1+OPaPk1bYNS977GsW/OoSP +hf+W/f81Xi8u7X3dKF5uni2+Dea9L7/3tZT3NVvEryvi14Hg0ZmjpbyvfP3Q3Ipm/LPq3ldlX833 +83zf7iUpgmFqb7KqiOnqyb3NvukS+mne+6rkCuXVuC6CVqgXjKOkOpr3pe+1I8GJxP+ynBtTa3zv +i+99rXHvS8/rBHn0bx2oYTbmMUauqvfVgd/Vgd+0M4FxXO8r1GuAejleNwH5O7g37ExgHNf7UoPc +atB/SjWchPc0GoRxXO9LDXKr4Wym4TS8Z+EXxnG9LzXIrYaxTMNZeMfgF8ZxvS81yK2G8UzDGLzj +8AvjuN6XGuRWw7lMwzi85+AXxnG9LzXIrQb9p7RPnoP3PPzCOK73pQa51TCRaTgP7wT8wjiu96UG +udUwmWmYgHcSfmEc1/tSg9xquJBpmIT3AvzCOK73pQa51RC8L/k78Jt2JjCO632pQW41TGV5uAjv +FBqEcVzvSw1yq2E60zAF7zT8wjiu96UGudVwKdMwDe8l+IVxXO9LDXKrIXhf8nfw/LMzgfGn3tdy +va8t5PM4eQ3zRsddfa+ZYoJmYvGReV96B7V4X+o/hfYR1m+U62O5EwyClcx/PYdzDOZmyXykl5jH +0Ko+5cv8+h2AlGRx/0HwNfAkcOkHLWXU8q17n4EPgmrrPn5To5wh5vzZe1+V+XEeuVa9r0qtoWw7 +DIKVtGXIDadZMGdbbe9L32lfglOJ/2U5N0+L7n1V42+g4g/L+0rnM2/xrS+Rzm+8niu17UGT10K9 +3pd+t/OZfazJMveDtPw48W6wkv6yleO9rj5u3lfiI7Uu7n0d6eW9r158rt6Z5iO9VxL/S08JLOp9 +5X2ka3hJV/GV8M8yT8nvfS3lfV3Fv7qGj4X/lnlfRxhIl3zvq5f3vnp57wvNwcM6xnFLeV/Hennv +q5f3vkA4zhwt5X3l64fmVjTjn1X3vkLfDH01pvdVyRXKq3FdVBtHSVm4V676e1/6Xi8mOIEvJLgb +LvO9rxMb+d7XGve+9LxOkMcXWVPDBffzpyh7H2wELit572sAr2sAr2kkgXFc7yvUa5h6OV43AfkH +uDeMJDCO632pQW416D+lGk7CexoNwjiu96UGudUQvC/5B/CaRhIYx/W+1CC3GsayPJyFdwwNwjiu +96UGudUwnmkYg3ccfmEc1/tSg9xqOJdpGIf3HPzCOK73pQa51aD/lPbJc/Ceh18Yx/W+1CC3GiYy +DefhnYBfGMf1vtQgtxomMw0T8E7CL4zjel9qkFsNFzINk/BegF8Yx/W+1CC3GoL3Jf8AXtNIAuO4 +3pca5FbDVJaHi/BOoUEYx/W+1CC3GqYzDVPwTsMvjON6X2qQWw2XMg3T8F6CXxjH9b7UILcagvcl +/wDPPyMJjD/1vpbrffm9r+PkNTyLO+7qe5V6EzQTi4/M+9pXo/el/lNoP8z6jXJ9LPucOAhWMv/1 +HM5/f5u9r8r8OI9cq95XpdZQXo227Cu3Jauo3pe+18sJTnFtiTfz87To3lc1/ofpfaXzmbf41pdI +5zchF/nc7KExWkC93pd+t/OZl1mTZe4HH1/vy7nwQVBtHfo3KcjenTN+FpjjJ8AmkPhIrYt7X9f7 +eO+rj/e++maar/fx3hf+l54SWNT7yvtIh/CSRvGVRjkmeEqj5bFnFB39ICwL3vvCvzqEj4X/lnlY +16ncUt7XjT7e++rjvS80Bz6/97WU9zXbx3tffbz3BcJx5mgp7ytfv9Fe3vvCL9Qz9D+MmO/Qt58n +7gShb4a+GtP7quQK5cfRsRus5J4Y+hmnWTCOFtnQBmzLfaxt3y7ge/Qr/ZtHfa9DCU7gCwmugmV7 +X3zva417X3peJ8jZIdbUMJr3NYzXNYzXdDiBcVzvK9TrJerleN0E5B/m3nA4gXFc70sNcqtB/ynV +cBLe02gQxnG9LzXIrYbgfck/jNd0OIFxXO9LDXKrYSzLw1l4x9AgjON6X2qQWw3jmYYxeMfhF8Zx +vS81yK2Gc5mGcXjPwS+M43pfapBbDfpPaZ88B+95+IVxXO9LDXKrYSLTcB7eCfiFcVzvSw1yq2Ey +0zAB7yT8wjiu96UGudVwIdMwCe8F+IVxXO9LDXKrIXhf8g/jNR1OYBzX+1KD3GqYyvJwEd4pNAjj +uN6XGuRWw3SmYQreafiFcVzvSw1yq+FSpmEa3kvwC+O43pca5FZD8L7kH+b553AC40+9r+V6Xy+Q +z+PkNTyLO+7qe830JWgmFh+Z93W/4TZzhsHkGdpnauRmz4LO4w+Ao8BvRN1mvY0d/gZxKDvfGAQr +ec73HOXn+UIvseCR3+lNeZmP1fcy+A4wdnH/QfA18CRwcU7QUkYt730dZF+fg6ut+/itMj+cP3vv +K+Qj5Mdv2ZTa2cEk8h+R19r3vm4jK2gN2m2HQbCStgx54jQL5myV87SV/s3j/Ybehu3rxG36ouil +mRv4ggb/XwylQnTvqxq/zf2w3vu613CLb3H1NXxmnTC+leUin5s9aPJaqNf78jtGtzh+OxccWU6+ +/2X5cbAbrKS/bOV4r6u18t7XYmOAY0Po3449HWXtxs8Cc/wE2AS8/oe2LO59HekZ2jLaUxLtR3r2 +t1/v3t8+210SW0Y5x9MgLMG32sCG2e6RLTe6929h/y3XwFXKVzkmeEp+78uxx3P0hxOwDudoJL7a +vaf9WvcQnEPt4bgjPWhe4rhXeobaj/XsAaXsOL/3VaKui/Ed6xnZ8krP/i3UM9NpjvaXj6tW13z9 +0Ex9h6j30JZq3ldlXw1tRLfN2s64twyGqb2E5WU+tk07gX3TJfTTR4kPgKOgkiuUV+O6CP0MmgXj +aJENaorhfc01FBu2rhPv8a0vUcyPqTX9zeP72+8Pv/FXf7jmv/f1HjncSiNTw6xftJDrp9huu3uN +uKzkva8PG27ynZBfcp8VxjcXcOXbuB8u21XeSg20NVvndbpPOFbNX6c8CPx+i/XqoF63WDcB+T/k +vrCN+4M6LOePDTG7nllMw3K+9yW3Gu5kGm7Bewd+YXyrLg337t3jjPN1XywPcqvhLus0D3fgvQu/ +ML5Tl4a5uTklZMcupkFuNXhEquEuvHPwC+O72Xlsx1rbYjnf+5JbDfagVMMcvA/gF8ZzdWlYzve+ +5FZDI/0z1fAA3saGbeuE8YO6NCzne19yq2FDpqGx4cOGDfAL48aGkP/ltMUHH3xA7WrrD3KroS3T +sAHeNviF8Ya6NPzqV7+qWYPcamjPNLTB2w6/MG6rS8Nyvvcltxq2ZBra4d0CvzBur0vDcr73Jbca +tmUatsC7DX5hvKUuDbdv3665LeRWQ2emYRu8nfAL4211abh1yxG4tj4ptxq6Mg2d8HbBL4w769Kw +nO99ya2G7kxDF7zd8Avjrro0LOd7X3KroSfT0A1vEX5h3F2ThjCGNDOeF2kD7+nhWe048avgk/S9 +r5vUN8wbHftnup/cUupJ0E4stuRzFmJ2PVPLM3o/O5pj78P7wBDYAdJvii/+rXu9g1q8L/V7Vft3 +HXpfodzJtkGwkvmv5yj3kfyc5LfK+wr5CPlxHrlWva9KraG8Gm2Z77uV134/7dwF0n45/32/9aW0 +/fkp6b8/ZX0FuF9LqdD4NLHH+k0/r59Cyf7X2/D5BLeJxcP1vqrxc2k8NO8rnc/0NezC+9qVzG1u +ZbnI52YPmlrAMPA6U2NHLja3bufSzp5B3ecAOAr0SL2b+rexel+hvBpz/K2cV+6Pm/eV+EhbFve+ +rheHtlwtlkT79eL+xP/SUwKLel95H+kQXtIovhL+WeYp+b2vpbyvUfyrQ/hY+G+Zh3W9uLT3daM4 +1D5b3APmva9ZjlvK+5ot4tcV8evAM7T3JmCOlvK+8vVD8xY0459V975C3wx9Nd/Pw9hk3+4tg1vs +XsLyMh/T1Zf0viq5Qnk1rougFRkLnqFIdbhXrvp7X/peOxK8hy8kisv2vk5sbHpxrXtfel7vkUf/ +1oEaZmMeY+Sqel8d+F0d+E07ExjfXMCVb+MwBjeiq17vK9RrgHo5XnuvlL+De8POBMZxvS81yK0G +/adUwy1476BBGMf1vtQgtxruZhruwHsXfmEc1/tSg9xqmMs03IV3Dn5hHNf7UoPcagjel/wd+E07 +ExjH9b7UILcagvclfwd+084ExnG9LzXIrYbgfcnfgd+0M4FxXO9LDXKrIXhf8nfgN+1MYBzX+1KD +3GoI3pf8HfhNOxMYx/W+1CC3GrawTseHdni3oEEYx/W+1CC3GoL3JX8HftPOBMZxvS81yK2G4H3J +34HftDOBcVzvSw1yqyF4X/J34DftTGAc1/tSg9xqCN6X/B34TTsTGMf1vtQgtxqC9yV/B88/OxMY +f+p9Lfe9ry3k8yZjf3gW91ov4XvNFBO0E4uPzPv6fI3el/pvo32Etd5XKHeybRB80r2vkI+QH+eR +a9X7qtQayqvRln30Bb0MlgVztn1s8Pm+C6yG96W3sy/BbfqieLjeVzV+Lo2H5n2l85m+hq/gfX2l +PL8JucjnZg+aWsAwsG3U2JGLbRe3c2lnczP3OQD0vvRznc/sY633FcqrMcf/uHpfiY+0ZXHv60gv +73314nP1ltqP9PLeF/6XnhJY1PvK+0jX8JKu4itd5ZjgKV0tpmPPKG1m24ZlwXtf+FfX8LHw3zLv +6wiG0JLvffXy3lcv732hOfD5va+lvK9jvbz31ct7XyAcZ46W8r7y9bta5L0v/EI9w2rvfYW+Gfpq +vp+Hscm+jeQEK/G+KrlCeTWui6AVmQvGUVKWXKvhvyXavl3AMXWlf/Oo7/VigvfwhUQ93tfGNe99 +6Xm9R85eZB3T+xrA6xrAaxpJYBzX+wr1GqZejtc+a8o/wL1hJIFxXO9LDXKr4U6m4Ra8d9AgjON6 +X2qQWw36T2ke7sB7F35hHNf7UoPcapjLNNyFdw5+YRzX+1KD3GoI3pf8A3hNIwmM43pfapBbDcH7 +kn8Ar2kkgXFc70sNcqsheF/yD+A1jSQwjut9qUFuNQTvS/4BvKaRBMZxvS81yK2G4H3JP4DXNJLA +OK73pQa51RC8L/kH8JpGEhjH9b7UILcagvcl/wBe00gC47jelxrkVkPwvuQfwGsaSWAc1/tSg9xq +CN6X/AN4TSMJjON6X2qQWw3B+5J/AK9pJIFxXO9LDXKrIXhf8g/w/DOSwPhT72u53pff+7rJ2B+e +xb0P63uVehO0E4uPzPvaV6P3pf7baD/MWu8rlDvZNgg+6d5XyEfIj/PItep9VWoN5dVoy2pztn30 +kfw87bOllb33pbfzcoLb9EXxcL2vavxcGg/N+0rnM30N38L7+lZ5fhNykc/NHjS1gGHQBtSY9wRs +F7dzaf9G70s/1/nMy6z1vkJ5Neb4W8vcj7C2/6nDv/HJeRNr9v3PkEPzVvk3j17/S/3N4/U+3vvq +472vPt776uO9L/wvPSWwqPeV95EO4SWN4ivhn2Wekt/7WvK9L/yrQ/hY+G+Zh3Wdi3cp7+tGH+99 +9fHeF5qDh+X3vpbyvmb7eO+rj/e+QDjOHC3lfeXrN9rLe1/4hXqG1byv0DdDXw1tZJ8PY5Nxrn/t +pVhezmSxbRr6oz9uBfbNR8EBcBRUcoXyalwXQSs0D8370vc6lOA9fCGBM9SQ/R15TX/zeGLj5jXv +fel5vUdiD7Gmhlm/YIxc1fe+hvG6hvGaDicwjut9hXq9RL0cr5uA/MPcGw4nMI7rfalBbjXoP6Ua +bsF7Bw3COK73pQa51RC8L/mH8ZoOJzCO632pQW41zGV5uAvvHBqEcVzvSw1yqyF4X/IP4zUdTmAc +1/tSg9xqCN6X/MN4TYcTGMf1vtQgtxqC9yX/MF7T4QTGcb0vNcithuB9yT+M13Q4gXFc70sNcqsh +eF/yD+M1HU5gHNf7UoPcatB/SseHdni3oEEYx/W+1CC3GoL3Jf8wXtPhBMZxvS81yK2G4H3JP4zX +dDiBcVzvSw1yqyF4X/IP4zUdTmAc1/tSg9xqCN6X/MN4TYcTGMf1vtQgtxqC9yX/MM8/hxMYf+p9 +Ldf7eoF83mTsD8/iXuslfK+ZvgTtxGJVva+fwnEFpO/XzP/NY0up0Piv2f6vgH8/tom1y0v7el78 +nxv+195vsn737t/d+w8e9L8488xrpW/8090v/kvWzz2658X9rL/5n4Zf/Hf8/hLr/8r+rv9bxfqn +5fJLLb0vfJnjjjX+rRcOcZ6/x/o45/37luE5wu/yXmP/QuOC5R9vLRXObELXevDOO+/8QT/rNkAq +s+dUumo2B/kn/NBIuZ1/vsg6lP2+5RfASny5MOdZK3Pz16nPQVBt3cdvIVcd5dhchbm5xx4CYQnv +gmxiw+/3/4eG3++fLZQ+91zhj3Y8V1i/6/+3dy7gUVXn3t+zB5KZMTAz4WJUJAGxTgIkIwmKcpuA +4A1KoGDR46cBscVPUFRabz0YFJUjilFAkKrFitUGLxyPek69HFOvtbWtp4qPVo/FIl6qtiiI4o3v +99971mSym7kkJGr9up788677+653rbX32uvda89o64P4W+BhX2tcY5PmJ4+fvP5psWtslR1FHTw/ +WGNACTD9EKFfaginy2PklGyDkuDRoxZv0rX41e+qT+2SM32S/hxqeHXFGDCywrrVc+hEIjQ2+4Fc +77L1pqEBoHzVnfw9stvhfwP4Gs9p/55+A+042icdGmfGrcbaEtsPhlsRv7A/14H9rZ7+AVbAz+kh +/2Drc3uI9ak91PrYrrS22XHrHftga6s9zHrFrrYesGtAtbWRcBPxt5J+s11l3UTetZRZQ9lV9mDr +ajtmLbcPsq60v2UttQdalzsosy6zhX2I62NdYfcivdi6yo6SP2I12mFrpd3DWm0XWdfbe1k32CHr +drvQussusO63u1tP2N2sZ8Gf8L9J3HukfWAHrJ120NpNmUJ/hDZErZC/mPb0BSW0TehHO/2p77yP +RweaG1JP+hzT2FR8puvmkrTrpuanCV+Jfxj4Jl03dd3LdM1UvJnj0pXRofzmurkf/gg4FqU+hKIP +SYanEl5FeCZh6ds4Mz79REyNrfJNja0EK3ym3Cwyz8tRblZsnm927HQHptwiyk3MUW5RbILvoiSO +h/9CIxTUyNUX//rYQN9zMcsXKt9sHVv+S2tF+c+sP5dfZR1aca61omKOtb1iuvXY4DHcC4ZbFw8d +bU2tnG7dUznH6lF1rjW76irrnqqfWbuqfmkdHv+TdVbc8q2LD/A9GZ/gy6YLpT/lYGJKF1vilnV6 +jjZtiZ/uez0+z4HRxQ7KrchRbkd8pW9HfJUDU+4TyqkPs8n5Cfe8T+L/DR5JySl+b+QotyP+Brze +BG+lyr1OuZ4Mpmz8Xo/3tLfEww6MnE9RriZHuafiw+0nk8jW1zfHZ9pnxy+0D4//2P6k6hf2PVWb +7FOq3rN7VPn991Tu7Z9WeZCfPvbfMmSC/7HBo/07Kg7yr6jY2z+iwu//c/l79oryTfak8l/Ye5Xf +YD8Xu9C+NXa8fVFsuJ2tTUpf5KDGNm2aHWOdlqNNs2Nhe1aspwNTTvPsrRy6nxp7kzn2hgNTTvP1 +kWQ5XeOMM/OggIhjYg/4Jsfup9z9vuNi9/lOiP0XeMgnfcYpa5wpM4GI+tgWX33sNd+CWKF9ZqzA +XhYbbF8Rq7Dvik2yX46dYReXX2VPLW+y15Q/ab9R/qo9smKHfV1FwP9hRYn/8cFDWTsN9y8eWumf +Ulniv7sy4A9W7bBPqnrVvrPqSXt7VZM9LL7cPi1+hn1dfLL9YLzCfiA+2H4hXmBvihfaW+Ov+bbG +t/jeiT8EHsG/Bqz1bYqf43shvtD3QHyK78F4nW91vNJ3WryHrzr+V2tH1bPWnVX3WSdV3WCFqi6z +7q48x6qrPMVaPHSyRZ9bjw+ebO2sOMW6ruIca2TFZdab5TdYa8rvs6aVP2sVl79nvRLr4bs7NtR3 +RazOtyw2xXdmbCFtPwcdrAVr0NcjzrzPpOMTYo+h2yfR8RPo+gnfMbFf+o5Fx5lsGOZepPWc6jTX +ZHWHuVbLPyiJPVk7enmZcGfcB42siNlq7TiQCN2bNaYmQhmmqXVktvd362n0dKB1ZCDD89wFvyk7 +4r6m39WK3lB7dO2qJbEjrA1/GXveXsOO2Nj0l7EnXjvyiAj0/GPHHzGb9AugTcoP3eChdyTDR57Q +b9xJlJs+48hxF1LPDOhq6j0OKj6TSBe9l/ze57nqhJWf3YTyqjeQKPBNRx9HAa1pu4kCuQ1NFQ+5 +eGDsjBndx/I9RmdaGjrv0ti981++f8xh1dXN+P999fX3jf2v+S8/GEq4Y6WEOjTnNXa8dQ0gLgBm +A/WN8pg4sw5IH4czSD8RGGeuDxEiltj7gh7WLbZtPQ3+Bnr6e4B9PYiRNow8w8g7jDIxp+wo6pAs +44GRJZ23GVfk6VR7zcUo+mLbh4yC/C3zTbwMX8RqiCVlI1uHz+mbudaDSlRPdyD+F7Om78kaV3Io +bPi2R4Z8v1GZLoPGhiuDH74F8Bfk93dIhny/USkZxFt60F6DK0MBfIPwF+Qv6JAM+X6jUjKIt2TQ +mHNlCMK3CP6C/MEOyZDvNyolg3hLhnBKhiL4huEvyF/UIRny/UalZBBvydArJUMYvr3gL8gf7pAM ++X6jUjKIt2Tom5KhF3z1HCjI36tDMuT7jUrJIN6SoV9Khr7w7Qd/Qf6+HZIh329USgbxlgylKRn6 +wbcU/oL8/TokQ77fqJQM4i0ZBqZkKIXvQPgL8pd2SIZ8v1EpGcRbMuha6M7NgfA9EP6C/AM7JEO+ +36iUDOItGXTtd2U4EL4x+AvyH9ghGfL9RqVkEG/JMCQlQwy+Q+AvyB/rkAz5fqNSMoi3ZKhKyTAE +vlXwF+Qf0iEZ8v1GpWQQb8lwcEqGKvgOg78gf1VeMqhPi0Bb36hsJH4J0HpzcMLNR9AKJAq7lUBV +Tms0xSFKpCZR6MQpHEj47AS0Bpg8v8Mvt6GpaQxNcNZ1hire5EskErv7JNw9F63/tBYblARLDz02 +JF2LX/WcDEYAZHEoJEVVi8asHPU71PwzOlA54ydvwwzCJ4KLVTnO7C9H8FfHbvJVx35tDazob10y +pL+1tbK/9Vz81+AmX2ucbZPmJ4+fvP7q2Nm2yho+0rvxU21DWjtrCSddQ8ovUTQHpHvJOxFIQTsg +6qeKRMtalzetxynZdc3jLtg+kDX+2kQgESioIVL1ODpfviVNp85YYO1Za01iF3CcNY3/7HklAg1r +oFcC3G71iRAC460zrfnWAv6fYZ0KFlplVkPz+jFWdPEYq+9jSfqaQ5vHF45VfMPwvkl6oEMTB9W4 +9NvjHGolprr0qRNcajWMhVXStfiLiZEuJEtJ0g9x7l2KV9w0sIAM0pfXNaogrtDqRjtmWfOs03gG +OJv/HXNLEzO1lEs9s9T/4VWnoplPVTqcIs+4lK8nuJyT4R//4cIxuxgMJn63R1hTTms0OfWf3Nxk +/e9GPtFjiqMH0aP/x+WbbJ5lyhv69i3dR4mfSTdUZeVM2FDTDlP+N5PeHKny5y2cP9ctwTuBP3Qe +W1JzSE2YDpaCDQzcntDXoGVQ1dvU9GLtjBmbapW2P2GNpUkg3Znnqnoi2xpnUrYgvRi/KPPXCXeD +ypk51oM5NpFwA1gIvNe2ikTu+aO5QDbTB6m5oDYdbCWGz8o6Ynn3gny5xqyR1+hQetyMnqTDN6Bl +0HQdKk06VFwmHdaRdjCQM/UbfWwm7nng1Uf/RH425Jebe42XDfmmiwrGy4Z83+KPxsmGfC9UNuR7 +oLIh/5h02Y7/SP5stK89YLxsyH9sHjxeNmRR2ZCdMHxKSJcNWXy9ew69E+23IV9I23V93wCVPVHh +ZUD6+ibZQmTvmAEyUTMuUEGrfbeRhAPJsidCjTPzM0LEtNjo4LTY3ECi4uHCnw55uLBb1cOFH8Tn +Bj6Ijw62RjREWog8IfKGpsWiYHRwFHWIxxhQAkw/ROiXOwj3AkVAY9zIKf+gJJjVtXiTrsWvtqg+ +lZXrDeRn7DtzZQ7U8OqKMWBkhU3DQP6Jt/Sm61AM9AOad9nOhHwVNuSv0ZzuYhvyhey73YldVVjF +dWAVa+jrsb/+hEXDLdiQ12NDvhUb8s+wId+ODfnn1lZ+B/EVewP24zvABmzITdiQf44N+XZsyLdh +Q/4ZNuRbsSGvx4Z8CzbhddiQf4KN+CbsxWuxIQtrsB8L1xB3FTbkZaRfgQ3538i/FBvy5diQL8WG +vAQb8iXYkC/GhnwRNuRF2JD/FRvyj7Ah/wgb8r9iQ16EDfkibMgN2JAXY0Negg15KW34N2zIV9Ce +5aCRtgkraeeFnWJD1nVSc+YOqJ5LFL4SDAPfpOtmtmumrqVmjutaY65T8pvr5n74I0A2qVEsVg5J +hqcS7kN4JmFdC4wz11VUiw25T3BqrDfoFTTlZpH53UD2crNi7wZmx95xYMrJhvxkjnKLYk8ELkri +ePgvNEJBjVx98a+P/TTwXKwhECqvDxxbXhtYUf6twJ/LewQOrfiocEXF64XbK54vfGzwf3MvuK/w +4qEPF06tfL7wnsrXC3tUfVQ4u6pH4J6qbwV2VdUGDo+fHDgr3hBYF7858GT8icBM6kbMlDM8pQul +P+XgyYBpk2zI7+Ro05b4O4HX4+86MOVk0+2VQ/c74r2DO+J9HJhysiGrD7PJ+Qn3vE/iY8DYVJ+J +3/dylNsR/x68vg/mpsrJhrw8R7nX48uDW+JXOzByyoZ8b45yT8XvCz6ZxPFW5r6+Of5i8Oz4p8HD +4/uFPqk6LHRP1YzQKVXzQz2qLgndU7k6NK3ythB9HLplyBOhxwY/HNpRcVtoRcXq0IiKS0J/Lp8f +WlE+IzSp/LDQXuX9Qs/FPg3eGnspeFHsvmA2HSp9kYN7U7qQDfnqHG2aHbs6OCu23IHRhebZ3Bzl +psa+zxz7ngNTTvN1bLJcDfoxzozJAiKOiR0enBw7hHKHBI+LDQ+eEBsBRgWlz7gWKElnykwgXB87 +JVgfmxVcELs8eGbssuCy2IbgFbGm4F2xZ4Ivx/4aLC7vEZpaXhFaUz4x9Eb5SaGRFQtD11UsDX1Y +sSb0+OA7WTvdF1o89K7QlMo1obsrl4aCVQtDJ1WdFLqzamJoe1VFaFi8Z+i0+F+D18V/G3ww3hR8 +IL4h+EL8suCm+OXBrfFZ4JTgO/FRYCz+ErAvadsDL8R3BB6I/z7wYPzZwOr4XYHT4lcFquNnBHZU +1QXurBoeOKmqXyBUVRC4u3J7YV3llsLFQ39bSJ8XPj74t4U7K7YUXlexvXBkRUHgzfJ+gTXlwwPT +yusCxeXzA6/ErgrcHbszcEXs2cCy2O8DZ8Z2BBbEtgfqY/uihxL0NdYZC5l0fEJsPLqdiI4noOsJ +wWNitcFj0fFodBkAo5LUrB3Nvcjcm8w1uSvWjl5eJtwZ90FzX6F5rdaOA4koAhpTE6EJIN1pHRlI +BDPszwVTa+BuFFB5OZWvRzH52JaP2xQdL1vvPiu7jZdteUPT9nGyLTdBZVu+HSrbcl/SZVOeofxZ +6DV9S8fLtjxjU/l4x7YMlW1ZYfFZQbqo+Hqf86oT7bAtU18gEepS27L2LzS+uta2fC3rrEuxF1+A +3fgC7McXsK67FFzrwTrSmsjTRN4myqwD16bmyXjkVP9L3rbmBnMqZVvuRh457OwONWNS8cav/McR +jjs53H/meted4MX2eeB8ZBTkP69VWVMPWRu45Dqyqf6DQAkwMlAnoRa+mWQwc1B75FqTujKcD98L +4S/If36HZPDalvORYVFKhgvhuwj+gvwXdkgGr205mwziLT0sTsmwCL6L4S/Iv6hDMnhty9lkEG/J +sCQlw2L4LoG/IP/iDsngtS1nk0G8JcPlKRmWwPdy+AvyL+mQDF7bcjYZxFsyLEvJcDl8l8FfkP/y +DsngtS1nk0G8JcPylAzL4KvnQ0H+ZR2SwWtbziaDeEuGlSkZlsN3JfwF+Zd3SAavbTmbDOItGVan +ZFgJ39XwF+Rf2SEZvLblbDKIt2RYm5JhNXzXwl+Qf3WHZPDalrPJIN6S4caUDGvheyP8BfnXdkgG +r205mwziLRnWpWS4Eb7r4C/If2OHZPDalrPJIN6SYX1KhnXwXQ9/Qf51HZLBa1vOJoN4S4bbUjKs +h+9t8BfkX98hGby25WwyiLdk+HlKhtvg2wR/Qf7b8pLB3NMLuad716vnUfc84LU31CRa1qhfpQ1Z ++hkB5AzVaknrCLlEIuFQ88+0NV2v5G1lQ1abzf5yBH91rH+wOnZMYGDFTYWXDLmpcGvlTYXPxY8J +PBfvH2yND4KkhcgTIm+oOvYB5foHDU/p1/iptl02ZMmr9f8zoBmoPyoSuWxgu8b9I9uDja4Y4q3s +k5cRfs22rDVQrYvT7ZOrCGt9PwmkO7PGrSfy62GfdPsmgTw1ANfKPjmsi+2T0qHsk14dyj4pHUqv +mXRYR9owIGf6CBuNY6/dTNzzwHu9yNc+ecbmS5P2yUWOffLl5nPGyz75R6jsky9BXfvkIscuOZ/8 +sk9monstvtKxT87ffI1jnxSVfdIJY5csIl32SfH1Prf2TrTfPrmdtuvacQAKlK1KYa3jDgbfpH32 +JtozA2SiZlygglbPrSMJB5JlT4QaZ+ZnhAjOqUY44xrm3GqY86thzrGGOeMKHo60xjVR0orJU0ze +Ys64RlV2FHWIxxhQAkw/yD45iH7pRVwR0Bg3cso/KAmGci3epGvxqy2qT2XlegP5Gfut7JPq864Y +A0ZWqv+7+3SMyH5A8+7rZp/8Gs3pLrZPbreW8I27iF/o4QuDnv6oL+Df22f59/N9zvchPrX3931s +9/dts8t879gDfFv5bsMr9gG+B/gu4APQjYSbiL+V9Jv5lsNN5F1LmTWUXWXv57ua71wst/f2XWn3 +9S3l2x+XO4j4LrOFIHHdfVfwjZgrbdt3le0jv+Vr5Ls1K+3PsU9+hn3yU+yTn2Cf/Aj75E7skx9i +n9yBfXIH9skPsU/uxD75EfbJj7FP7sI++Rn2SYs2+Hwhv017CkCAtglFtHN7zr0qjU3NE80fM4Y1 +3yaBOUB7QGbOaH6a8JWkDQPfpOvmDNqT6ZqpeKMf6cpcp+Q318398EeA7B0P4TkkGZbdZBXhmYSl +b+PMddVPBGdcI5xxBSsiptwsMs+LZC/HGdcIZ1wdmHLOGdcc5TjjGuGMqwPZWBYC44xcfYngjGuE +M64RzriGOeMa5oxrmDOuYc64hjnjGuaMa5gzrtwLhoc5/xjmjGuYM65hzriGOeMa5oxrmDOuYc64 +hjnjGuGMa4QzrJFsulA6Z1zBxJQunDOukey64IxrhDOuDowuZC9ckaMcZ1wjnHF1YMo5Z1xzlOOM +a4QzruCRlJzi90aOcpxxhdeb4K1UOdkne0azt48zrlHOuDowcjpnXHOU44xrlDOuDrL1NWdco5xx +jXLGNcoZ1yhnXKOccY1yxrWYM67FnHEtpo+LOe9YzBnXYs64FnPGtZgzrsWccY1yxjXKGdcoZ1yj +nHGNcsY1yhnWaLa+VjpnXEFN1LRJ9slwjjZxxjXKGVcHppzm2Vs5dM8ZV+bYGw5MOc3XR5LlaqwW +Z+ZBAVGccY1wxpVy90c44xrhjCt4KJLdPrklwhnXCGdco5xxjXLGNcoZ1yhnXKOccY1yxjXKGdco +Z1yjnHGNcsY1yhnXYs64FnPGlbXT8GLOuBZzxrWYM67FnHGNcsY1yhnXKGdco5xxjXLGNcoZ1yhn +XKOccY1yxjXKGdcoZ1wjnHGNcMYVPIJ/DVgb4YxrhDOuEc64RjjjGuGMa4QzrhHOuIY54xrmjGuY +M65hzriGOeMa5oxrmDOuYfo8zBnXMGdcw5xxDXPGNcwZ1zBnXMOccQ1zxjXMGdcIZ1wjnHGNcMY1 +whlX2n4OOlgL1qCvR5x5n0nHnHFFt0+i4yfQ9RMRzrhGOOMaGY3+A2BUkpq1o7kXaT3X1WtHLy8T +7oz7oLmv0LxWa8eBRBQBjcOJUIZpah2ZvsfTLdGy7lW+eu7T04HWm4FE298skj3wjr8sceyC+678 +kWOH/O6ms8bLDnkcVHbIGVDZIfcm3TnbqvyEN2SgRy9b5tgh9ZvNskOKyg4pKn7HkG74ep/nqhP5 +2yHd34Teq0vtkHuhR1TYxXbIkG8Ja79b+E7q0+BvoKf/cxBiHZeOEt/fWHs+DW4BS1hnLrFDPjMf +xiOnxonkNesS+c24Yu50sh1yG/uX7yOnIP+2VrwMX0ToNDvkdirTfqpodyD+F/MN0J6scSWHwoav +2mv8uWRojx3SyLAzJcN2+O6EvyB/7m+SdqPsQaAEyC/XHjukeEsPu6CuHnbCdxf8Bflzf5O0LRna +Y4cUb8nwWUqGXfD9DP6C/Lm/SdqWDO2xQ4q3ZJAl29XDZ/DdDX9B/tzfJG1LhvbYIcVbMnDEOinD +bvj6mbuC/Lm/SdqWDO2xQ4q3ZChIyeD3cc4Z/oL8ub9J2pYM7bFDirdkKErJUADfIvgL8uf+Jmlb +MrTHDinekiGckqEIvmH4C/Ln/iZpWzK0xw4p3pKhOCVDGL7F8Bfkz/1N0rZkaI8dUrwlg/kmqfhf +zDdAe/oF+XN/k7QtGdpjhxRvyWC+SSr+F3PP4vtbjhwKm+tze67V7bFDirdk6JfqixJk6Ad/Qf7c +3yRtSw/tsUOKt2QoTcnQD76l8Bfkz/1N0rZkaI8dUrwlg/kmqfhzzhn+gvy5v0kqGUx//dMOqbWH ++01Ss48cQT+cR41wljXM+dQw51TDnFcNc5YV3BRpjbOjpBWTp5i8xZxljapsun6Nn2q73A45Y9PC +8d9EO+QXKE92yAjjn79WdsgeRISImwTSnXneryfy62CHNH2TQJ4agNutthhUd7EdUjqUHdKrQ9kh +pUPJkUmHdaRVAzkzno0dsjcFzbc8Bydanl0r8A8iv9bGWNLHOSTp/7DvF+P1bSON1eeJ+5WbmNJH +hHBbfXaW9QPOUwtlbMo+M9oaOHuMFTxgjEtPdunOGxza8NIDSfqiG//wTjd8aXCs8ifOLXHpFwMc +2jAv5oZPOtShVt1El5473aHNc09x6dVnufF7cP7a6JBLuWNvvwNaQD+MgOqb08dA1R/G3q60IwlH +QKY+mkdaLp0VkEcIJqkJd0+GdW+AlZNHaSZeVM7Ibfq+gbiFwGuDzrfvE5StAbhU36vdB1vWF+dm +1XLHzwwfn9TzyW3oWWnSs2TIpOc6Vz7+/70+OjwXeBf7n3Oh9VzYiH41Fx6FeufCI8RFQKY++oea +C8m+T9CetubCsC6eCxvhq7ng1bPmgvScay4gn+O814aOzgXtB/5zLrSeCzE6QXPhcKh3LhxKRIQe ++CbMBdP3CdrT1lxgDdKl9wXpWXPBq2fNBelZus+k5zrSMq2RVpK2FLTnPvmXocPH68yP5sJ0yh4F +cKn7pLt2OJOvzFjzjnVXOGfydRmtiM78iUszrFA6du+sh/mvwEbwEngUSB9mjaI0XS8kVyYdqR3T ++SrOAqhZW+DtlHVFur4S1NnW+BlqNUx7uAt0o3ZJJxvBK8CrG6Xlcy0dSj4577X0deJeBB0dP+sp +uwbgUuNHNpF5rKR/COZZZVbL2tp6+Heujn7zskv3+dihDVsDzpqsoWhvl67Y36HNvkFuePswhya2 +1rq0qM6h1hcnubRTx2Mj8kvXG0FPBqJX50qTztXOTONxDmkZdOCsgzVGDbgEtLk2Jvrv+quRuCXA +21978v62fvtD9r9B1FsC9J0qyWQl2Cfl1MidOjnCb/P1IRwhuhvQ/IyBIqC8ZlwpXjqZA3qCFeBO +QA2p8Dr8KvNNeieoifbMAJmo0Y/a3QsYvY3EHwD7gQjYBkbwEHUINAKeDbnhmfhrgHFmD0LPUptC +I4IvhkaBBJgQ3BQ6KvgsMHVsI89R1JmtjnfJ8ZY1ASTAqOC7SLENjKas5BuVpMambvr2LuJXAdMm +9b9pq/yDkmCLqhZv0rX4pY8SIH3I9Qby8xz4pYwjIyvsnHefpaNJQPPL+77zEOLLgOZHN6iV0Lc4 +Gq3/ZHTfwckm+Zkni3sn2vfO8R1URUnqcc9HmXARYen+//d5kj6m0Idz9lV9YfRk9KY+aQ5ZwQV7 +JYLNoQXAanVuoyvHqFcWEy7qhD5MH6MDqU91tvWuR7Z3hu+lzO1A47o6YXVTHXKBRFGG8+tFqTm5 +J/cWzu7sznRv6WFdyzfArnXmjPfeMkCygdlAsmoMDEqCIaDLUtK1+G1iTgYjgMaCqJyhqqW7G2Uh +V9LnEqNjlTN+8jZ8l7D46l7fCMy9TLyetcqCG0NlrcaYKUtyQ5q8tYSTrvW1L0as2ie+E8EkkADq +J+ltCP4ykH7NaeI06X3orcm6Diz/u2uOdG7qzVdvB8GjBCi/nNGPaY/i25o/Adp5HGmSU/c+pEE2 +C8ncNq0LbQ7U7VUWXBeqB5sDpr5CyrVVH8Xy0pt0ZXQjXYUSVnM/4iJAc0PylnNqYwF3kj3VBdVR +W8u4UN2mHab9ceLKwTJwPVgDuoNGe2Og0W4INgYF+Te20oGph6x5t/sG1QvU7opE5r3w61+dVqtv +Qug5by75TwS41Dpd8tWyStUa/WirYf2KMdbSwGjr6ntdeuMvXbqBNTvxDZezZld6p6615yGD9HYZ +OARIb9KvefZT2iogWSeBdKd+lptJZEs73D1mzU8DlTF6Zl3h3OcbiFsIpMPBCXcOErTy1SdFzHos +pU/JXWVZ//t2F+hpOnUfBqSnMcCrJ6VJT5Ihk57qSEM+x3n1cRQFR4GO6kN2ms/cqlP66EN4ilXG +F15PTT4DnoPOzyHmezypnw2d4zwlnmadArVe2O3oLdFcPNYZb5X7ODSxzwEuTRzsUGtYwqHNB0xx +6TH/x6X/d65L38eKwjhtjv6rQ63mxQ4t2/syl66+yqENV17r0HWPrXLDo250qFW33g0/1OSGrbvc +sP/eZLhhrNtQ/U/3FxMuAqii1Zq2bzK+BGr07sevPn0G6Gamfl0LvP2qtFVAuszUr+eR1g496/vT +ztzQPac7KEhCfsUZaP5ITqUrTmH5lU9QWFTuQKC2m/nViH8J8I6nPVlLZHtO3du30vrQV4byMz+n +ql9iQHJKdnPPk1+O+h1q2qJ46XwO6EvhUuhO6Kq08Gv4zwVf9RpdbZMbBOJgOigDcmpzIAmfMzpb +0pSe7ricZn2O1T3e6Ec8zT1cuvI+y86Pcs0KtjzLTkaBuyg0k7w1wDjnIs4/ja2ppR/5jivd5ZtZ ++rnvhFKfPbO0wJ5eGkj9rtVC6sz1LLsgekzw9OhRwdOiRwS/H01ARwbnRw/L+Czr7VvTJrXPtFXt +k24FZn2tQzx+5SkBGl9yvUFyTnwp48jICtsOPctu9/W3vvD1Y4wPsuTP9izr1Y2ZJ9tJ6IcAX0Cl +KxOWHhgK36h5orkwA2Sipj+kq7bGFHMytXY2ejJ660aZeOlm3+bSZl99acKW39SXae3cWWPUK4sJ +d0YfmjbQvFZnGCYSEQMaO7pndOxZtkc3M//0vERVun9FahI9UnNyT+4/eq4KZNgn7eO7lrHOBY79 +nz4J9zlEfai+HwDo66/Ns2wvhNofeT6EroRq3NRFy4Lx0m2txlh6X2kuC3RbrUM8ftWh/tMYUbvV +n5NAAqg/pbch+MtA+rPs+1xvPvPta+3wDbTk915z0p9lPeNbS6Oka70H4L2ve59lVU+u+fg+utmX +fJ9BB0LVpghzsbl0nS9RWmbLb/STaT5SpCEfvUlX08FRwOiqBr/0KV11h0qR03znW0/4xvA7i8Os +yb4znDD68i3mZ717J1r2HHU/NX3RUZ1Rhbi2uv9l01mcvJPR1TDoE9DzoZK7rE9jYV2/zf5t+5X5 +n+29uUDhztTbQnjMBdJbRcIdoyWEm2c1jTuw+8GPgnHyf3jdFdg83Wdhspn1R+pZBZH1DDyoqhPW ++Eb3XHu8v4fRQFTKNYoprrN+D2PzD/ctUH0vPeX+voX5fQzFyTVOd38vww0xOJNhk39bsrzd/1Hn +9yZMWO2QM+G6/93H4XNBKPSmxpqodl1M/Kz/fGu0+/sabrlkM/+uvMl/72fvOnN5/n4DHlV9Jt7w +kz7lTPiCkBs2+T985d3RmX4PQ/PKONUtJ3nM/sJUAj8krDhdr9Kdmxt5iDw6mWDGbg/UN5G4BqAx +qPE3ONGy9tvTsVhlJaYc0glj0cirPpQucunjOyjiB+TLpY8q8siZ+o0+FlBwDuioPuKUHSTmaftU ++xDI/nx7Shu/itN8bK2rvwHTHZq4ml+30f7VBXNcevPZDq1/mmd44hPjljq0ecbVLt260o3/+fUu +PewnDq1f8FOHNjz1M5ce6e4PbB71H07YWni/G3/uQy796DGHbv7O7x3a/MwLLp30qkPXPbHVoWUv +vetQa9oOhzb02eWGW+0x7Ol+Q67+/y66Pwd9qwuyzYcppOsi4O3/BuK6Yj4cbK2b9NlXoI9HUIQu +Trn0cTB55Lz6aCRuCfDOhz1Zh+baB9nBXojWBd51qLkvaU1g/GqXWS/JL+fdBzFtn0OanpVXQHdA +4ZIK/wm/6v2q90Ekg9wgEAfTQRmQU5sDSXTmPoh49gJFQH7vPsiCKDaoYMs+SKLUDc8kbw0wztxv +NK8mlI4IHlU6CiTAhOCE0qOCCXAIaRGgOnPtg8xjD2RudAJIgFHBedERwQVA41l6GJWk2k8uAaZv +9VywirBpE8HUuJZfuhW4+9U6xOOXDlSf9CHXG8jPPcK5psyBGl5dMY7MHIRNB/dBGtm/WMnz3GrQ +qLnUbpu+npkp6eyDrIaasPQg3X+T5kkT7ZkBMlHTH+nzJH1MoY9W+yCN1KV9EOmtG4iXWsHNzIN4 +6QLw5dn0TZ8ZWUy4M/rQ6ITmdcE+SM8M+yA9U3NyT+4/ufZBPmAvRHPGe/8ZQGM19mcD6VBjQNcR +ATXospR0LX6NmZPBCKCxICpnqGrp7kZZ5jk/GUxds1TO6Ju8KZt+LwRoJM1cg8RL+yBlpV++Tf99 +33L2Gq5lD+Q6gBXdc82RzmPI1x69ee/rRj9GF9J/W9f4ADo6jrQhQPsgSOPsg1wHlS4jpZsDzego +UloPvlyb/lG+ZdZi7iR7qgvutbSkZVxIF0Yvpv1x4mQXlU3/aegaKOPHaui9MdDQG3t+P0H+Pbfp +L6TeucC7j8FOxjiik645z32MRIT3affYVm907If7NLAAHcjvdY1SHq6r9jHoD+edjvpTT9fSyLlu +iI568Tf9ze9qJkVw0sw+QTv2Md7S2ot9jLe+zvsYGouX0e6joWuSejD7GEpblYybBE13Zl1ZR+Se +7GN0ZCxWsRwOd8JYNHNT4296Escm9VHXhj6Ulo8+kM9xpn7WqM6acQHl5wDvc1tFwr1flTilMs/N +OGUHAVxqj7Fj+xgNh+7r6K+5aKirxwur3fCpo1165bEObVjk7m8kFpzmhBOrOefINWDdvyx2aFnp +ZS69e5mb/sZyh9aPXuHSh9c6tPked59j89vuPkf9e7e76fM3uulPP+jQdePcfY11j/7aCTeM/IND +rSkvOXTzU5sdWr/M3d9AsWMdfTj/0v3FxJj7mvQqv1xfIL/iTP+Y/n+duCnoV/PhcegaqNRt5oPS +1P/Seab50EDaFCvb+ylt7Su5v9mstYLuhd1BQRLyK85AeSSv0hWnsMnP42CqnNJNvPLJHQjUdjMe +G/AvBB0djwnK1gBcajxKX8OshkffcvutU/tnOnWrX9Q/v4Z6+0dpq0iTDJn6p460YUDOq49G4pYA +rz72ZF2ba1/lQ18h2waZ91XUlhhQv6mvzfpLfjnvvori1fY5wDwP76QSvUtlwq+R9kPwVT8vqm1y +g0AcqH/LgJzaHEiiM/dVxLMXMPr07qvoXZD090uml1rWaZHs75fMLD09ckLpgsjM0oWR40rPi0wt +vSAyGXoIfChq6Z2VXPsqp0Un8l5JLe+VjOY9k8PYUzk0uDDH+yUrqNv0rWmT2mfGtcaCdCtYnbCv +Ynh15jgyskrAifybCTR+NQfzOSux0+f3feEr8m33hXzye59xNP/M/PHqxswTPYNT0tkfKIKasMaI +zELfpHmSaT/FxJv+kK7aGlPMydRznNGT9jKkt26UqS+NR3i/JBIvXReR39RXSLm26uusMeqVxYQ7 +ow9NG2heh/dVbqBwI9C4rk60nJWoSXw5+yfbfd2dufGPsn+i/WLujM49T/sn8dK6VmMpvU/yucaZ +a4DGqK4zmvsJoP4IZHiPZAf35c+4rrzvC/jk915b0vdPPOM4476T9/7t3T9RPW3Nk/R5p/0TpHH2 +T0JQtSlRGonwHkkkUtoQkd/oJ9O8o0hDPnqTrqaDo4DRVQ1+6TP9PZLJvvt5h+R93tN4g3dImnhv +434J+bV6j0TXOL1H8j60O3i2d/1e2/aLhOv61fcs67MupHBn6m0hPOYC6a0i4d6HSwgn3yNx3iX5 +2rxH8nFy8wr55BrRk1xn7b/U53iPZNt3Wr9HsjkZNu+RLPC8R2LCfldMy4TNex7su7yd3H95O5/9 +F295U0+m90hMfs0DORNu73sk30bPfXbt3n0rE/9FILWb502l/Qdhxel6le46a/+lI2OxK98jqaOx +fdHH8d1458ijD6XdixJy6WNP9l+y6SMO487Zf/nneyS6DpprrebwdAN0XEL/jyywLH0wQH1t5sN0 +AtxVcvb/FPJQPFXnCZTbjzr/Bfqqp06l/SKPOsvIQ9FUnSdSrh912ozTP3nqVNoDedR5NnnS215P +uf7U+TF1vuapU2kP5VHnqeTRvc3ocw7lBlDnbup83VOn0h7Jo86zPHWeRrkDqLMbjN7w1Km0X+ZR +5w88dZ5BuW9RZwF1vu2pU2mP51HnqZ46z6FcjDoTtP0dT51KezKPOs8hD8VT+jyXchXUuYWOe89T +p9J+lUedC8mT3u8XUG4IdR4Jo7956lTar/Oo0yvnIspVUudF0Pc9dSrtmTzqLCMPRVNtb6BcFXV2 +R84PPHUq7bd51Pk98qS3/RLKxamznn7f7qlTab/Lo84zyUPxlJxLKVdNnZXIudNTp9L+J486zyZP +upzLKDecOq+EfuypU2nP5VFnGXkompLzKsodQp13IOcuT51Kez6POk8hD8VTdV5DuRHUeQYK+dRT +p9JeyKNOrz6vo9zh1HkqDxOfe+pU2ot51DmfPIE0OW+i3BjqPBs5fSiaYOo6r7RX8qhzAXnS+/0W +ytVSZwOR3Tx1Ku1PedTpbfvtlDuCOo+kTn7vqpWcSvtzHnWe4ZHzTspNpM4J1Bn01Km01/Oo81RP +nf9OuaOps446izx1Ku3NPOr0ynk/5Y6lzoEMsJ6eOpXGwtrRR7a16ULyUDQ1Pn9BucnUKRrx1Km4 +d/Kos4w8cgeCItAee0a29V2CumoArpU9o7oL3xN9iDZ/G31Mpt+iHn0o7V2EgWRd/1dLYpxXH/2I +iwA9fw5OuLoi6NQ1ByrdqW71w2F4TLh7MlzuvH1FBselW9fS/Sa9NR1IUPXpOWUiNF0WO9EiS0/S +egO5CJD//wENOK1w7N8BAN== + +------=_NextPart_01CC4DF5.1FA44F90 +Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/image002.gif +Content-Transfer-Encoding: base64 +Content-Type: image/gif + +R0lGODlhEQNIAXcAMSH+GlNvZnR3YXJlOiBNaWNyb3NvZnQgT2ZmaWNlACH5BAEAAAAALAwAAQAE +AzcBhwAAAAAAAAICAgUFBQQEBAMDAwYGBh4eHhISEh8fHwEBAREREScnJygoKDw8PCQkJCUlJTk5 +OSkpKSYmJjs7Oz4+Pj09PTo6OjExMSMjIzIyMl5eXkBAQE1NTUlJSUhISFFRUVBQUFJSUkFBQV9f +X1lZWVpaWl1dXUZGRk9PT1xcXExMTEdHR05OTlVVVVRUVFdXV1NTU0pKSlhYWFZWVkREREtLS0ND +Q1tbW0JCQm5ubmBgYHFxcWJiYnNzc3d3d2hoaG9vb3BwcGpqan19fWZmZmlpaWxsbHZ2dnp6emRk +ZHR0dGtra3h4eHV1dXJycmFhYWNjY3x8fH9/f35+fnt7e21tbWVlZXl5eWdnZ56enp+fn4mJiZSU +lJqampGRkYCAgIKCgpmZmZWVlY2NjYiIiIyMjIODg5KSkpycnI6Ojpubm4aGhpCQkIGBgY+Pj52d +nYeHh5OTk4WFhYqKioSEhJaWlpiYmJeXl4uLi6CgoKSkpKqqqrCwsKurq7W1tampqaWlpb6+vrq6 +urGxsaKiora2tq6urq2traysrLu7u6+vr7i4uKampry8vKioqKOjo7m5ubOzs6GhobS0tKenp7Ky +sr+/v7e3t729vdLS0s3Nzc7Ozs/Pz8jIyNHR0djY2NTU1MHBwdDQ0MzMzMPDw9/f397e3tPT08fH +x8XFxdvb28TExNzc3Nra2t3d3cbGxsLCwsvLy8rKytfX18nJydXV1dnZ2cDAwNbW1vz8/P7+/ubm +5uvr6+fn5/39/fj4+Pf39/Ly8vv7++Xl5fr6+uTk5PDw8Ojo6Pn5+e7u7vPz8+np6ePj4+zs7PT0 +9OLi4vb29uHh4fX19eDg4Orq6vHx8e3t7e/v7////wECAwECAwECAwECAwECAwECAwECAwECAwEC +AwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwEC +AwECAwECAwECAwECAwECAwECAwECAwECAwECAwECAwj/AAEIHEiwoMGDCBMqXMiwocOHECNKnEix +osWLGDNq3Mixo0ePOgDsEElypMmSKEduSMnypEsAK1/KbAmT5syXIT/q3BmSBwCfPnH+HOpjp9Gj +SJMqXcq0qdOnUKNKJchBy5arWLNq3cqlg9WtYMF2/Rq27NWxZtN28TC16UgQIURwmMshiJC7eMle +9cID790hI+h+iEuyreHDiBMrXsy4sWOLHHTtmky5suXLmkpIvsyZsyYTmzuLnvw59OjOvDo8rtgD +AAgAc08ICWN1E6dep3Nf9tXJk54tRISUGIECQIjCq5MrX868ufPnSSPr7pzZ9HTKpa9jBq3dMq8Q +e6Ab/ywBAoWIIGL62P7VvX1uYJ04+RnDo8OKFCrE69/Pv7///9FZp1117pHGXYHZFfgdAHpAdwJs +RpDxByfBFGhhgcJ04gcXJLBQE4AghijiiCQyx8Ewv6So4oostvhJCSi2KKOMn5gQ44w4pljjjTnO +SAx4zIXUgQhgALKJLj0mqeSSTLYYTCdbIMFCCyVWaeWVWGaZ0YlNsvgij13u2OWKYo6Z4o8MrtZa +B0kAAgqSZsYpp5mhBCKEh0BoqeeefPYJIpdxfilnmWYSOuaP4TWmBAgimCEKnHNGKmmTwQhyxgcv ++Knpppx2ahgHxegi6qiklmrqKCWEauqqq45igqqsxv8qqquwysqqMcc1mNgRAHRgBinD2CrssMQW +a+ywpXARAwBFeerss9BGOxGox5KKaq3H0lrtqNpuqwuuiSImwhKDBOvtueimK+wxhDAhg7Twxisv +tNR6e+253W6bb7W4ptnWBjOggYy6BBds8KimcCFDa/M27PDDJHKQzDAUV2zxxRh3MsPEGHfcMaoc +eywyxSCPbDK4hsFQiDImt+zyyzDHLLPJxHRRAsQ456xzcxLPXLHGIc9css/DDO1zv7o+pcQJaSxD +9NNQRy11xcyo0cEQO2et9dZR9Uw00E8bLXQJQcuMMlQtmOHL1Gy37bbJpjRxM9d01233RhwAU8ze +fPf/7fffGuv99+CDoyo44YjvbXjijDeTgr9NndBJMoxXbvnlmGeu+eZ9G/La3aCHLrpCeXO+d+Cm +F7O46atz7ni4TDnhTOq012777YSfwuvovPd+d+mmo856CYdv3vrmjkOuVBrF4+7889Az/gwRvldv +vc4cBJPM9tx37/33nMyg/ffkk99JCeOXr/7256e/fvnQpAB7Umu4//79+Oev//7890/+Mly4ngAH +CK/s+W974bPf/tp3wGQw8IDxU95R2KDABlrwghjMYPmW8QQCevCDmjLgARPYwAf6z4T9i9/8jKKE +Y2jwhTCMYQObAQMQ2vCGVuJANIDBwx768IdA3MQM/3YIxCIWkRMlIKIRl8hDJCqRiUaUxuOSZpQO +oCIYUMyiFoERii/44YlbDKMYgXEIU2BxjGjkISBygsM2upE/OkwjD4UIRjE6UY7AuKMcpbhCnShh +GngUIyIYoAYogKGOgdwiGgDJQ2dEQQCiOGMitSgMF4zhjZjMpIkQuUU64lGPaQQlGqUoQZ0kQpKT +ZOIAfMHDUASDGmhowymCkQpU/CYah9hCNcioiS+oAoup+IIuyWgKNHwRGLA85iFU4ctgHIIAZJgl +MKSBChGUApWpZKIaAPAFTXrzm4qJoxw9KUdRjtGcYuRjUlpAoWwyMRUgeEYPqdGAKbihAL5AQwGo +4P+ABwRhBzyYxgA68IYCcEIRhIRCCp4RAwiogQGAMEUDCgmGaRggAlMowCoQAU1p8jAG13RnFhex +O3Ca9KRPEWcayRnKJH7SpXucIlI6wAyRLhGe8uRhLHe4BFZ8wQ3TSIUIniHUZwxgGcCIZQhCKoFl +xIARwYhlG0LwhjYUQBgG4AUwQBoMAwjjh1y1KRNVsYM+ovSsaM0Ir2igUjSyFI3oDGNct6hOpKDA +GGItojNW2covtIGnPv1rUYs6gK/GEgRMdeo1pUrVNmwhGl7d6jUj68Ow5rWIZE2rZjc7kdaEgC41 +AMAIphGM0pr2tKhNLSlmQNrUuta1myhBa19L29L/xna2tX0tNWSKlE1EI7fADW4wzjABMvBgB60Y +ABWEQIFlfOENQQXBMuC5jAGsQA0FcEUiGECGEIDhGdaMxnNfMQAzvIEPFrVGMMIrgSm84rfU+AID +dIBe4dqXtoAAgFk5y1/+BoYucwktAOIgjfuidrW4NfBtDXzaBTO4tLvdL0c2oIcEP1i4qWhDfeOr +BWbQEhbRoEYipiFigcaiDar4bYbri4hTRCMVII7veafxBdK2OBqo+IKHX/ncGV+YwWUAQ3+HTGSB +rADAHChOIwDAht9eGME/dvCDpczg3ZbyIyd4xo+3LNzCcvnLYC4tL16z5CKbebP/TTIaltwIDjwj +/xpwjrOc50xnWczgzXTOc55JUQI86/nPcOaznwGtZ1PITykucMQ0CM3oRjuazl8Y9KMnTelKy/kR +QZDwmTeNyQ+gYC5KBsCS3WxpONtZ0pMWdKmjoepSG/rKH+kBMlZN61rb+taTdgUMGkRFTvvajUD4 +wM1+wAEPsVkgpC71qVfdaks3u9KG1nRHmoBqXFv72thu9DFgIAdR//rbbnyXQArhGm+XGTbVdvSy +S/1sSrd70q/uNQvnkO5s2/veuO6FFOjgbXD7+4MdcMFA1lCQc8NmGdNIuMIXzvCGz2IGCG+4xCUu +ixJEfOIYT3jFL57xiZ/i0EzRQR2u0fGSm/zkKP9PucpXjvFqbKAM/f63zK33mqJs4SAGPzjLE/5w +jqt84zufBtB3/nFY78QHQKDFM4LO9KY7/ekS58QLupnzmVsddHCRCAd8nvKeB33oLAf7yj8ubZ3Q +4A9Lh7ra1852jKdhBl6I+dXnXjeBhwQOEeGAMJ7B9777/e+Ar8UM9g74whd+FiUgvOEXz3fEK57x +hn8Fb5sC8yOMYhmQz7zmN8/5znv+87HIVHiqTvfS58wFKxjIzfP+eM8LvvWdd/zn+y772T9D8mXf +SRlewAVp2P73wA++8PuOCidsQA6BkLvpl/+wEsgAawSHudZhz/nX/772s8f+5yVvdKVIoQNnWMX/ +8MdP/vL3fRNVeAHMlyxv5rsfXh8YSPsfwgFrLOP++M+//vdfCxjYf/8ACIAP938BWID3N4AGmIC4 +ZxhwMAUdQASzIAwJOIEUWIEWeIEYuH+iEAQ0MAfJR3rvF4LOcgIfoAQAcEndBBkEmIH9t4IYiIAZ +eIAz4IIXyH3z9xR1QAMqAAnSEIM++INAiIGvYAcdcAL8dmwimITQEn/jphH194Mt+IMwGINTmIEL +qBh2YAU2QASL4AtB+IVg+IPQ4AdG0AJIYG4AIAZKuIacsgMfMBLddElOeAzCUId2eId4mIeeAAN0 +mId+6IeC14d/OIh1GIiEeIitMHmJQW5FgAI///AI1HCIkjiJlFiJlpiHrRAISrACRgAAHwgA5MaG +ougnTNh9FMEBgniJwrCHqXiJhqiKhTgDrWiJiZh7UdEIcTAENgAFZBAJvQCLwBiMsCgNf1AHLdAB +R4AGn3iDo9iMJNIDH5AfAqGGHoGKwciKwfiKsKiNqpiIptgWl7RkSQAFN1AEciAJtmANwriO7CgM +n9AHdHACKxAFU4CG5JaCzpiPViJu3wgZ2GANABmQAjmQBLmH/0iQCImQgneQCdmQALmQDhmRqwBy +zvEGP7AoKMAEeCAJtXAMEfmRIBmSDgkLi9AFVjACInAFANAFaAiC+viSABJsMzBwR8EBDCmSAf9p +kDg5kBC5kwHZkz5pDRPZj4vRIEtmBgAQBWyFA0+AB5DACqpQDUE5lQTJC7CgCltwB1bQAhxgAkUA +AHnQkgCAdzBZliTSATPJFDZJlTo5lW0ZlEDpkxNpi49xbJfkA1aAAyZwAxxAA0fQlHfACKrgCh5J +lQL5CapACWKglUdwZChgAiYgBEWhhse2ZHRplpgZJAAgcAKxetGBB3cQmqI5mqRZmmewAqBZmqqp +mqeZmqv5mqHZmrA5m2RAJczoHHigX2KJlEUBBSbQAUjWl0cwnMRZnMR5ZEgmAiYQBcwCAEhZmWjY +IPiYmdSZHLTQEIwiEATnFDvgA975neAZnuL/6QNKsARVMJ7oKZ7leZ7p2Z5PcAXm2Z7yyQbJVyVk +KZbQqYZmsJ/OyZ/7iZRpgJ8Cep/VWaDL8Qi9kA3Z8AgLcRwCcZtGAZ0SKqBLZggUeqEViqEXaqET +2qHRuSdImZt4N3oaOqENgnfTaaAqyhy+sAsKmg3MgBCot6I0WqN1c3Mvegtj4KIHkXoPaqNAGqQO +QwsvqqBKMAHGAKMDUQKlKKRO+qTRUqQKeguEQAhL4KJLVoqhCKVc2qV6MgtSGqYMkKSvwDBeeqZo +aiXDUKR7QAq8IKWWcKXZQJRpWqd2+hiXEAxhagkT4AMuWqQVsASQ4Ax3WqiGyhzLEKYKagki/yAL +UuoEIOCoC3qolFqph0EIiRqmYzABcrALtyACf6qgb/qiMWqppnqqS4ENisoLTtAFwtCo2eAEkEAI +oSqlqHqruNoRLSqlvDAGjloBjkoKoLoMStAHtaqgw+AJubqszCoRpnCs2UAKSiALFiAL0uqiMSCp +YZqs+jE3JfKVzRquz0KkipoNvnoJsAqrvDCqtrofBwAAMoAAJCKv4lqvm6IJinoJExAFu7AHFmCs +y8ALpAAJ0JoNncAfn1MQ/PiV7wKuAHAz/CgQEVtuApGwEDsQ8mqmAuGtDluxA+Gw3pqwAtEaE2uv +JmslYKqobMCvbOCqDCAI6AoK5XqwInIAF/8AAAEgEAGAARggrzlAr+/6rgCAADmAs/AaADnQABQA +AO+aA+/aAPJKrwhwsxhbtDnLjw0AAAkAAE4rsQKRtQlQtEJ7smSrJamwplJKCreQDS27C8vAANbQ +q66qqKAAIFlLEHcLr0yrs3ybt1nLj61RtOCKAPEnr3n7Lnn7tQNBsgAwuIWrt4orr/FqECVbtpbL +H3rKqxYgB5srDBYgDObaBQWbDXUbIhGbuABwtzlrtH5big4Auak7tJB7t+KGundru7LLj7U7tPE3 +sZV7ucDbHJGQuZp6CW47pmMgusuwtmG6CiQSADcbtVxLr6rbt4QLADWUs0vrtYNbtIg7EO//8rpm +qr0B8CA5K7REq7dFEADxl7WSq7CxG7zy2xyZWqS8IAIQAApjQKtsKwvL4AQFSw1Xwo8iC74H4a2V +i8AP8b0EocAKUbm/O78SbBiJoKqaGgXCcAsTQAxwuwwiQAzlKsDSgro6obETfMIh4gthegtviq4u +OgazOgGRqqjIgMI2/KQ3B617sAQTILMiILOXILrlWsM3XMRAugrH+r8gwK/CugukMAF1YAEgHKa9 +4JJGfMVmma/TqsG/kA3pqq1Fig1YPMYF2glhKrABKwBd7ATGSgoA3MRhTMZynJloW6RjEAVzoADW +AMO7wAsT0MUvagl/ugxzXMgvmaflagEu/7rDwgC32XAJiroMmGDIlNyMxPui6Cq6DNDFHtyvpBDJ +lRzKaxgJ9SuliqygsroLljAHBQsMuCDKsByCFmy/L3rK2UCsUAy6UgoMsdzLy9cIKsyrY8DKCgqr +pADJijoMpeDLzDx3zVCwkBqqGgwCIKDLRToMzZzNM/es2aAEhKCg0goCgrAMtqygy7utyqrN6vxr +5JoNn+qivPC5GkwMyTu6CrrO+OxrYdqnl0AKQrwHrjoB1iylNJvPBt1fZ3vG5dnHf/zIosu87XrQ +Es1ZdayoP8y2UdAFn6uotzDRHo1WmXDJqAwCUxytoPrIl1DK9yyHH93S31S//9vH/nysF/+tqCLs +0jiNSYcwyy8qw7IQzyCgAH3wouxapHSa00jdO8FcpG46BiBQqyetqM2Q1FRtQ1uw1C/qwV0AwtOq +oHf8zWGqfFU91tXDzUy9DKTAymsbz9Z6CyqdDURM1nLtO5d0rPfbBRsMqSDAynFa0i+KDYkw14LN +O2asqGPgpiJgrLcc1VIqxoP92KETyaB6x3MAwsQaBTIrpctACJDd2XTDChWtoAQbq4LgxxLoBGCc +1Z692lsj0ssAyeo6AbuArjOs2ZPM2rgNMZig0v8bBQqQwSed2OXKy7ld3A7D0y+6v9mww7sgq5cw +B2BdpK5s3NQdLwlapMswq7vABsi8wf//OwcDnQ3EXd3k/SxYbc4iIAhL0AWkcMoWIKdSyq3lPd+c +YgphegmQ4M+3oAQEywZLLAsbfM30PeCaQgvQurIZzKmgu7YsjNIvKgsEHuF6MgoKSghdEN1sK8Qi +kN/erKgS/uFY8smLzeHwTc6gm91dANEvOgog3uIlsqagOqu1rK31XK4d7eI4DiCIrKCyjdovaqWD +DMArnONE/h8VrcjMLaqMHdZF3uT9YQlPXczEsAwKkNlArqim4ORajrCSetiPrAByMAfVHKZZvuVm +rh8Q8MJgzQuQoOIKGtdnHufOsQTfPNNDLOd4/hypINukAN9F2gt5HujK4YYjQedeXKvY/8DZgr7o +jdGkPWiuf+rYjD7piAGND0IQmYDdlL7phzGx95mohMzpoh4VH5CWALCdBXHbox68x9HqxvHqrh7r +sA4AVCLrtj7rrb4CtY7rvG7rut7rwC7rUqEaBAGhZpYft57ss556yt7sAMDswR7t0O7stz4SfgIU +NCHrJIASrYHtaQp3nBDu4j7u5F7uUucF5p7u5u4BfKDu7i7u7P7u726CTxFwzMcBtiHv6Z4IMZDv ++l7u/E4K/27uAT/w5e4IERwiO2ACcBEbfqEXV+Hv4e4bWsEXeDEcKJACIaAC1v6khnAhOPDxFrIC +nHAhJH8hQsAGTVHAxs5p0oEgmmEhCf/iHjPfHrzwOEj5H1gTAzIwF3ZBBlugHpzAHhcyGvAhH1vA +BTwAGBwAAJmCNSo6AyJfICFv8iU/8ldfIEJgBlbMEVkXgi9P8zGPIAdC82Vv80CyHz1AAzMwAkZA +G6lADURf9HRPGatQCnpABFcwAiswA/SemYYwJyE/JyRP+EMvJ0GQ80eBemzU8t8GKIUCI4NiI5MP +Jk2CJo5/GF8JnDpwB4pgDJMS+kviCpigBkwwAo/DRvko9YIf+HJS+K9/+HESBGLdET6qhJA/JoIS +KJTP+5bPJIjCHCdgH2CwCG8i+shvJp0QCFLSAiuRj64fJ4Mf+4Y/J4m/E0zqsJnvb7n/HyaS7/uV +LyeYvxo0wCZuAinJn/5xEgpbwAQx4K1rOAN/gC44MP/nsgJHcv/57y2J3/UUkfAAAUDgQIIFDR5E +mFDhQoYNHT6EGFHiQw7FdF3EmFHjxlElLG4ECXKUiY8hTV4cWfJkSGMh9kyEKRFGBzCDsK3EmVPn +Tp4hqxH60aFETKJFjR5FmtTon54acTBtinHFpqhSqVbVFcSMUgAnPvQYCIfrWLJlzZ4lWBFrR5U9 +U64libUlAD1oJW4QkWTQNKx9/f7VeAwTExkk7B5GnBjxDKhVn/adCvlq1SAAGiWVUVCsYs6dPSdV +W5Ut3LY831Zt+fIzwg2FmAGGHduv/6k1L1bfxp1b4J9hvX3/Bh58mAnewo0HX0Hq+HLfyZkz13p0 +x4cdA9foxp4dN4dkz493mtHdu/CO4scDL38++Ny6uVPoEFRM/Xz69e2rx2TChXb+/Y8yvm+44uxz +7r4C7dPqMqI+KGgL/x6EcCzu7gPPPPrSsw/D+lLLLYQkRpEvQBFHJPG8WK6gIUIVVzRowPqIC/DA ++mSkLzqYlPjghIHwAGArFn8EkqEJ7avwPg0vLMHC+dhbjQYjZgmxRCmnpLI3R0jYIEgtswPwPhgN +VA7MACtTMKIPTCCovS3XXHPI+orMMEkj5bSPw89OYCTKKvfk875pAgGBTUE9o6QYQ/8PRTRRRYsx +odBFH1V0BVkgpfRQSSut1MaHjpBhhoHkAOCLQUf9kQNgMIUUvFNRXbSjVVlN1FVYFW0mBbo8c8KZ +WXfltVdff6X0lCJIJdasGRz1tVFgL/2VWV8TfKiD/QZSs1hr+zP1V1V/ldXXbnutVTXFXJjkVWDP +RTdddZ9541p3j0K2V2WbnZReYDVViAcApg0VgDHeBRi7bH3d1tsSzN31W15rvVWxUpJRN2KJJ57V +wYAvdmgGSZLhuGOPPwY5GRM2DrlkkCU1OeWOUVY5ZR0sWyiEENLEuObVOAimZZM5mSFnnUPupASf +f/446KGJ7hiaFMQ9LIRSgEE6aqn/p6a66qgXsSsbrbfmumuvvwY7bLHHJrtss73GRimSqR7Zapap +fntqHXw86AcAZiZIDLIOObtvv/8GPHDBtc4O56p5Pjpqo6tenGqlGz6sksStprxyyy//mAu0/r6l +i8E/B33wtJHS2Oq2q45b6tSjnrtMglxYgSCLzeqaDV5Cxz133c/GReDJiUaccaGF//1npZlGC4vi +MWe+eedNXiaLs7K5BYRltJ7jllvm2PoWQrae45JsLokChDmu393sOUC4veztx84+7NGRkgQY++/H +P3/9gRl5f//3X8Es/jfA+wWQgASc20EyQ5DrnOUQbNiF1pZhgV0sgw0giIIg0me2/2UoIQoR3OAG +lzEGEchhF4WLxgH/t4kZpFCF++NECVz4wvzFcIY0vJ80bFUts4jAGDgEYv4mAQIhMCOIRySgM9AA +AjVMA4lBxEUUaLc+SGgNBKQgBQi2RgrPac0B44PAJUgRhS+G8H1fuwUESNE+smVxbLdA39fm55AP +ZOkgGnsi/+r3RAPyUYBPfFmZZvABIFgnMSAAISGqaIE5kIIQCtBgCNkACrDtIYMhxN0tILi1MXSB +FBboQ+8Qs4NAIYQDNwQiC1GJQxs+sZVI1CHyzIKFVebxf2eAAiv08AQn2jKIMXjG/eTQBlVYABC1 +9OX+cGCHsmTDAbwo4xXdqDUuav/tEkvIxhW35oBbaO0WkFhCFbsAgi6OswvcI8T33neLKEShm5cA +gRYdEAAtjpN7XqTn+OIpvnRGgRQSXIL5tLiMgIovCtYkRPzgCQI4FnSODeFARGNnkD0isX9+zGMf +kZhAMACAQbJLzCEQqTULEIMUIgAh+UDIhkuIQBi38CAlYRqFPnwPpdlgQzfHQM4KOgEENVWAA/oA +UxBQknoTgEAddkHCKFCSDV1YAgiX4dM57KKlINAgS0UwB1AoYQkvhcT6BNFTEHwVp10oYQRJaMKz +prUCCgiBVLU2BkhwJqI5AMAQCnLKJ6rSlTL8KzJVqEPInQUTwUhmEgewDPs5Axj/igBBChgRjEMk +ogc7eIUPdjCKYNRBCyB4QwqXuNnORkIERXzsaY1YhzYwMRp1KEAHOAsMxwIDDVsQbGLv5wUA6G0s +l/BcFG4nTS1Ss4tLEF8AuKbNbJAiAITo3BJIEU5SOICLyu2C57K4jDBeAgLZCAAWswEJ696ivFsj +LxYdcIlLBIAXXVhv+6A6Ri2CABLVZec/2SdN65JiGfatLikiElECc4BfeHziRZGo0SMyOIg6YIMQ +PACW24yUFxTsJAhPCkIRREEWy5iAIC4xgV2IABKXsIAJFRBBEcgCqrIIJzhlwYsLF4IXSrgv+qa6 +BFB0wcOQILEA5PBPrYmgEKS4/8UnRawAY4jAAbKwgAMEgWNSCKAPlxCANUTgSSdEVQA+EMQEQDGG +JcjCCZDYxZfDTAtIWEAWXVtGBYghysPsoMAcQEFacktAvyLxlUf8cxBjeZgOoAKxuvVfKkAQTPul +wgKYGAQBVoGGAhjCBwJ4xBegMI0BkKGYmNC0LhmwjBhQoBQigEMxS6EHKDzDADpgRAM6gYoBiKIX ++HNGBVpxaET77w9GIMjMhH03Yg/b2MVG9hIcAAIIcI+4W+zid8EbR+ZOEwTLXnZ2tYZd7V6RniAI +L3y/Z21/brGeXWyktremTTd+GwJdIMQSoJlNLnYRvPGEQByOvW9it+DOBK6Bp/8qekQFN/iPCz74 +ETuQgw6IAACB8hS/JY7siU98iRGERBXDquGbZqPFzVVAPBUgixVnI8Mlb7GTrw0CYlhgCcTw+JtR +LAdhcLILJX5zNigogF9wjedzvTlOZfHxDHdywzH/+TJQ+vOUYxuRTJfF0bnmBELsAhCBqjjFtZ71 +EHzg3xHNDF+R2GdAA9bPZj8iYXlIFhRgo9eJXvT90NCGFNZBFV+geypE8Ay9P2Oxtm2D0xBbgWfE +oBTBmHsbGNCCFAhAGAYQBjAMHwzI528JftgzomVxgrpyBQhqxOJ3n23c8WEzG1GoIvWUS83iRsGT +awxrNpaBXe7BM41YJPIy/Dn/zWzwQtrN1WLss9k5e59efPAErxj9mw0IQOJ7VxR+8rEojQF/XUcw +GHgQCx5EBwOx+zg8gbQA0NHVACqCJT1qzbOB4619/BYTiPo/FVDzp6bZGjqXhRJe374NfxzoIMww +JRgrEEuzntuaLNOaPagqjwMFors5o7OAnqsAUEBAUlCCAow5/Yu624G6kzJA2VOCPoggRkiMGfg6 +FNCrEci8FWqhwHJBWFqaw/gAU3g7/xkATkCsQ9iCMHCiGBgFvIuGvuu7AfAFYPAsKJgsZ5AAUju8 +uduCXIIFQ6u8yau8xooCPlhB3YoigxCLLgQALwTDLxTDMLwnehu95vIc5NIa/99btucyN2qCgHgi +BF6gp3nqvTrUIjKKp2xiNl6YPYaKpzICPtmDAAeAAGxSN2vCw/GKQ4bKhiUIgOu5oj9kKEhoRE4I +w0wUwy+4s5lJA5i5hGAQxVEkxVI0xWAwAUs4xVU8xRWoBVaExVF0xViMxQSigR3YCk3UxTHkxV3U +xFhApAsDwJCLQ/WLOa1RgmVDJDaYAEM0IWYsxKibgHiqKSerqjGYgD5YHwiIJJO7uSoDAQUQwZ/j +pGmcA2FgAEP0AZzzxqXyJAEoRETaA7hSAFrIhg6cxp/CwBj4MAGIq7mCK/PpxYH0RYI0yDsDgDOA +GTaQBlpkRVKYgWlwyFXchP8SkMiJNMWKvEiMJEVq2KHDEIVo4MiRJEVEIIAVgAAyYIYGuAAI0IFn ++II3mAZFWwaaHIAM6AAB4IVUGIAVEIBKmAYRKIVoiElmkIAOaAEwcDVrCAahjAYRoABOEEk0OMmk +fAaSxMpgqCtZOoo4ojE4WoZukqDb+T1qWr6tCUu0xKLbWQb/Wj1e+CexTLJ/wiL04QU4Ei+0FMvm +Eksa6xq4pJ41rEvZk8vracvlg0tSMIbqk4Ej+JfLuIwZUMWsRMXJzMpZpEzMzMq5OQQAqIMUGIrP +6KaM4xq43EtviqO59Cbii6A18kPZW0vT3KKwXKPSbJ+25D+vSTKxRDLU7L3/26GxkzKprYFL9CEy +OILN2jzO67lL4sS9W1CFxFCCiKIBKYAZyOSAjSRJiNTOkdRIyvzOrPRIrhyLHWiD7qRMWuyFVDgF +kQyGVNAEkaQGU4iGXkCF+rzPAdjJahDF9WzPYEAF/pxPkUwFWIjPVBDJAA2G9XRPaihQA3XP9JzI +H6CDZjKbawqd1cMkTHqohggkmCEIy8TKVExPzcRKEyXJBBIIB/kozuAbnYO5z8mwDQUdqdtQzqgO +yNwr9MRI7gRPi/xRHp1Ijywss4ABZZDQJIVFyFPSJpXQVbAN10mKs3nNGqVRDp0IKRWIGSCEaPDS +LwXTMBXTaDCBLh3TMxXT/xXwBDRl0y9V0zZt0w8liB+wAR1RDG8KHb+80sFJyyulM8TQUoHggGeA +UzSVhRkg1EIdU1IogURV1DBlVEd91C81hRg8jBMIhGmY1E3l1DCFBU3t1FAV1UIlAykIhLIwBQBI +1VVV1VZl1Vd11ViF1VmV1Vql1Vu11VzF1V3lCjMN1TId1TcVVWENVRXVDADwgMQoBF1lVl511maF +1meV1milVoGR1E091Gud1EgVVW4N1Uot0rOgBVAd1XI113NF1zMVhCh4idmxmZrh0lEF1mFdU3od +1dZJiCCgjnfl15vR1kfN1m5tVIH9V0WtVPIsix5AhnRl2IZ12EelBhgQi/9A7dd3IYRpwNiM1diN +5dhpKNOOBdmOtQFPCNmSzdiRNVmTtQK6QQi9adGKhVm04IBlSNmQnYUZoNma7VhZKIGc1dmN5Vmf +/dmMPYWPRAwfsAJkGNqlZdqmddqnHdpQOAEeodiYLRYuhdqPfVqU3VqSfVorANGFAIKXtdqyRYqZ +fdqbFdqlDdqnbVunLVqENQsroIVngNq7xdu81VuN5YSpDVuztdisvdiuhVquddqVfQiXBdzFJQq0 +dVq1dduejdy1HdqiDdfkoYE/sNu95dzO9dyOLYQZ0IK/Zdyr7YNnQN3UVd3VZd1nMIHTbd3YZV0b +SAXZtd3Upd3bvd2VrVr/hICCDxiW0hVeIREG3ZXdWpiB4jXe1p2FElDe5V3d5n1e6E3dV7DUzogC +OvAF6uXe7vXe7wXf2D0FIeCBl+jd4V0T2P3e1w3f3AVf9/1exJWILvAo9LVfg+CA6eVe5NVf6pVe +8P3f77Xey0WMDoCE/g3fBFbgBYZeZsADhzvV871fIJkB9fVe9n3f2s3g8AVbCU6IEpioCUbf/AVf +/gVg5z1hBF5e65Xbw7ACFYiEZWDgGabhGn4GZagEEUAC0hXhUbHg7sXg74Vf7x3i7pXfmFADAGiB +HhZeEv5eE/7eAPZeKe7eAV47zhAVGDgEGbbhLvZi42WGSbAV3rqMzWDi/0Gp4GVQ4zVm4zZ242Uo +gT544zl+YxuABTrG4zW24zzOY949ChHAmzMuWw6wBj6m41qAgUI25De+WUVe5DZu5Ed2YxbOjiKw +ATiQBknW5E3m5E42ZFCwgxQQgusU5GKRY0+OY09ehj32ZFbu5CM2ijjYl1KOWUL2ZER25E2O5E7e +ZU624uzgERv4AUoQBlU25mNG5jVGBkIwgkBxkMu4YlrWkjRG5VPuZFfmZGze5A5WCjBYYmnmV1vu +ZFz25F7W5RnIZU2m5P6IAyWwgTAYhGJO5nmmZzpGBkOYghW4AjIgZXAmlkUQhoAW6IEm6IIWhhIA +aINW6IK2AVVY6IcW6P+GhmiIhuWkoAIA8BR/vhgOOIaJXmhPgIGO9miDRl6RHmmCLumTLuhWMFr+ +cBAwgAIUIAJJ4AWVtumbxumTboU+wAIWAIAwIOUG0mg0TuicRuicDmiJRmqlzmk/HgsWzeihvhaO +RmqQNumbTumczmqcZukWzo2XqAMlQAEoIINL6AWkRuu0hmhp+AM6SIEOuAI2IOVoluotKWqcPuql +dmi9RuuKHosm8AA7rWtSoeqctmqk3mqsnoGrtmmWJmD+eAn63YAbKIIyeIROsAa11mychoVAqIMT +WAEpuoO5HuyrPQRrQO3UVu3VZm1rKIHTbu3YZu2Glu3aTm3atu3aPgL/HuYKOOABsi3tLeEAbMht +2QZp4i7u1kZe5E7u1V7u5mbtVbjeILkMKRiCGOAAJogDLWAFU4Du7wbv6FYFMTgDIBgBEziCKSDl +Mg7ua4Ht8Hbt9w5v3IZv+g7vI2BZs9gKfa2O9taS4YZvazjuAH9u+C7w8Jbux44QvVnW6gYAHLCB +G2CCJsCDOxAEVXCFYwjw8M4FVYgEMbgDHziCEegANOmoCCZlHvFva5kB+Qbv195w+wZvGf9u/PZg +pBCDIQDuFYcQAIfvAf9xGGBu8D5w8JZurwYSHTUDNvABH9gAE+gADpDwIyCDO0AEVlCFLM/yDM/t +DtfyLN+DOygDKxhx/w4IARPYgSfwAS7YCh2FZh7HGBU4gjmn8zq38zs/AhSQczzn8zu/ASjo80Cn +8z8XdEG3gSRWDFEZ2/6G8wfhgEIP9ChAAUjvc0lnAkrH8x5AgUvHdDvPgpmh6y0pAzF0c5jZiiQA +gCIwgVVn9Sj/uk5kdVbfbets8/UmZd9qdIwJAzPYCl7vdV7vEWD39WCvAwC4gy/49WQPdmXn9WI/ +dmYX9mj/dWdH9mW39mHHdjNI4hvnCjHAkYZI1Vz3jGyHdmZngzbwAnK/dmnvkXP3gjxgd3XHdneH +93Jf914XA24nlYm19VL3934HeH0X93f594K3dT8I+IS/DIQ3+IYHAP+GV3iA/4x/2QDgTYhhIJyB +TwyHd3hDiHiD93iOB/g/+HiAD/WaARVNNPgw1BtQ0fiXF+Qu2IAFKohGOM1bgPmc1/md53l+1Zuh +0KuBGILv6ZqeN/qjR/qkBxI1gIFAhgAQygaq2xpZUPqqt/qrx3q7uA6Hy4FI6iCi35phyPqxJ/uy +N/uG8IGRmqA+oJ4uOOCtAYY/Pfu5p/u673kOeDOl+x4UEzERMCqtAQa7F/zBJ3we14FduAULoKRl +UIAY5QUSg7PCl/zJp/we5pv3MypwgvqSQ5vK9/zPB/1+9YWo38sxEB9rukD5Cf3VZ/3WF5SvibcI +ArGcay6wQQbXx/3/3Nd97aAFqNeaC+qCbERLBXA9398aatj95Ff+gGkAAGh+jf8X34dLNjqrXfCv +s+wa5Kd7CgAAw3iXobiA5Rf/hTiAiAiAiHj+0jUF4ydOyH8/OSgqsAGFs0cAgUj/a9EAgeD+8ed/ +ggjegQCIBgAG5gAgcCDCAAMpANgBoCBBADIkAiiCcODEiRdLIDz4ECFHhwwvkixp8iTKlCpTbvGV +7SXMbE4IvRQBghgpJ7ti8kS18ifQoEKHEi1qlCREkh5BGKRI4qPHHk0HcsSAUAPCGlMBKISBUOpI +ABwvYqWKMKnVkhaPsm3r9i3cuHLn0q1r9y5Rj1ZBIOA68QBJhQcB/1PEiJCpRwCEBSP8C4Dp1oGE +0+KtPNBlzGUwLRBzAgKSMM+geMK0bPo06osIvC4eqJDrQKsjW8t4bbUvgL4NAA8miVuhxqa4cWsU +OLxk2dTKlzNv7vw59LYeAb+meFBhAOMD++IGTJikxuoUZSAQyNiwbvHR2SbCRjrbBGHZeC0T0WeZ +kjk7SQ/ztP7/c4ltNdF1rm0lUHBSTbRWAAzA5hGBSn1FEYMOAtfRg+oBEBaAHXr4IYghBhgbQn29 +lpSB6skGAGUDLZXQgCxedBBELYpYlHsx3aLEEsLcIsJOY+gHiWb83XikXC0SVpyBDyKEAWRNBacd +hoUJSFgCEuJ2lv+LuZ3EIZJhijkmmSGuVVF2AXAUQHnqvaYbjNs1daaBB7ymEGHlfbRDAIBNBGeZ +P0VSJE+XdLGTBcKQAuR87wGDS6CRpsQdABog8F2BGTIQgFSUGnYmbzGS59VAWdp5AmzfhcqVgwJh +JeB5kso6K621xqWhXLja2hahLy1jQRcgHCqCLPNNsB9py+y6bHQCMvsstNFK+5OucFU7LVCXBMMT +KfHdYsFOTlySDSFdEPPeMoRgu6yzdUm1LrzxyjsvvZYNA9MYh367yyVAsrGELCKc+x429Rp8MMIJ +K7wwwyWlcu8lRV6iBASy8KJAD7J0Qcgye9zyHgCNNDwyySWbfDL/ys+RRko2pHQhzBgTEKNEH0uM +uwcvPCGTMs89+/wz0D53QhovnMHEBiS71AcBsjEBoEfQUUs9NdVVz0oLT7w4oW82RovQhX7vZQOK +1WWbfTbaaVeGNU/4CaIEKIvuNLEgYqt9N9556723KU13TBOxLwnwixMDZ7PMGKO91MnejTv+OOQm +902aANa8NFNMvIgAyaEwDRM56KGLPvqzmMF0CySk0AcuTCIoYHhMw5RCOu212347gL40zYsSXbBe +Ux+XNB0TMLgbfzzyycNFSI4wkULoJTrV50AU8iWLifLZa7899wD0GpPMMiW94/AwKds9+umrD/kf +3/Myxk7HHn7s/xiXfExawevrvz//Vm9L2gR+kY18CWMCdQBB+V7CjP4xsIEOLJm2eGIJHjkgNIRI +IE8W+MANcrCD8LqX+T7GC/mdTglRUBxPqOHBFbKwhZJ6GLfGZb4JgEJzGFyFC3Oowx16SGwv2UPn +lvE36/GEbDw8IhKTWJmhvccJxXqJBeRACBEQkSeMUyIWs6hFo5DmR124BSGWEKQLsuw9/tkiGtOo +RoSwzXzPG0MZRRC3CYCgD+X73BrzqEckbgEApJmYMPYAJAuMxgJ9KCNPHrXHRTKShabgVn6EQQid +dK0QkIhCAovXyE1ysn+PAED5GHAuSxxqGQz4xS2+5yvsdbKVrv/kXi/adjgGWC5fuxBBFJaAwpjk +75W+/OXtmJe1cZnSGhPbxb7Ehg2RAbOZzgSdKi1xQVEeDlwu2yVMNPjMbXLzbu0jjSUUYIwBduES +FnibHUEGtW6ys51V+99LGBCwJSSKmtk4ZSrfYwp38rOfPovgLeC3jDjuhBRKuCUIlgC70ozBnw59 +aMNgyJMKWMwCsjimQTF4RYhytKPzkqivxhAFQeyCmsfkBSJ54tGVshRb77EEJYlFiChS8T39aSlO +c0orJmamZUoQhgDqADMylk+TOj0qUsPURphYAqVskIUSHDDHOhY1E0m9KlY9tIouMupjFRiNHOez +B79ltaxmdc7/I2OCi6/tAqY7+SohJpDOtqnrrHa9K16a8R5qym1RUTDc82DSS7wStrBu0V1PsxHW +wMHkffbZCR4NK9nJEqV5vhqr3N4zuGyckbKe/WxKDmHZH4btaIKAhAV+EVnQsra1F/mexzJnAaFm +w7W2va333jPWlgHJGrj9bWvh+RJLEMsJ+vECAHwA3OVSNoKNHUPSduaGFriAudaVLAidNhA4AKAD +1/0uXlMRExWSpAkemAF402vWl4jBJFDzgHrje9U/qIQHH1CBfPNr1z5+wDlK4ACAAyzgARMYwDMo +MIIRfOAEMzjAC24wg19wMB40pMJMCQEAMBwCAqNAwxkGgAgq/+wQHej3rkP4gEOUwwFd7KLFLn4x +jGOsiRKwOMY2trEmTFDjG/O4xTnecY9vzAsbwMshIZABgIMgBDJsoclb2AQnoizlX8AYGJ2QcpQ9 +oQcnc4EHQxgBB1aA4aeUOKlrAEB/U7PiIPd4xkBmM4x/DOcby3nOMeYFhs9cK+WaAAAoEIGStbAF +RXCCGlS2M6JjvApOYEIPXuCBEDqwAgDgl8RlZqkSPqCE06w50S52s6c/reNQ+3jUpMbzHmgVAxHM +QAhi6MMmgkHqWc9ZGJ3wwxh40IEPVPfSHT1zmivDgWH8otjGPjayk/2JEhA72c529idM0OxnU7vY +0Z52tZ9NDP8Mr3NMJpDBCnQAh1RUI9vmPje6011sXmTCC0IYAQBQ5euHbkAGQ8DLsNWN7GVjW9/X +1vex/w3wYm871WFSgaSTsIhT6GLgDn+4ukMRCCSwIAXz9icaADBpu+Tb4fx+uMAHHnKAb/tpIiJx +DJQACFA0HOIufzm6O+GFHUzkBxd3JwwwTBcOFEMXPv850IMu9FGUoOdCP/rRR2ECoyO96T5XOtOd +jnRjhMDgIAL0IZgh9a1zvete/7rTTRGIK8D35uzkwoflwnOwA53oUQc71Nn+87jLXRdUN3mHUnCC +R2i97n7/O+CdTow1vCAGZucmd0XQ67esve5u9zvd5R55tlP/3erRIcEMzKCJYQS+857/vCfOEJzD +O9MNAFg8WziQjGGwvvWufz3sOzGD1cO+9rUnOu1tr3vW4373vr97t58TgzQgw/fGPz7yk6/85eue +GGVAL+m3SeS2qJ75rZd97pnfe+sPY/vWrzx0dgCDSjyD++Y/P/rTPwxmdGEFQIi+M80L/aJUn/vY +N7/3tV+C7C8f+M45AR5cg/oNIAEWYPPVgc7B3y+9F34RBQcAQzFEoAROIAVWoOxBYAVmYAYSHQZq +oAdGIAd+oAg2QwpYnnI4gTSIoAquIAu2oAu+IAxKoCbQiQL6kn1twFA8YAxG4AXuYDGE4A4CYQyS +IN4pxyR0/6APJqESLiETSuAzoF0NAtMWWEGw/YQO7mAPBmEJIOELCiEMkqAJmkYQdEIyNKEZniEa +sqAjNGAUvlJDnViKIUQVIgQHBEMy3CEe5qEe7iEnzIAd7iEgAmInlMAfBqIh3uEgFuIhBiI0WFzw +WYYOUMMiTiIlVqIlXiImZuIiwsJYtKEvAdtFjACKDEQdauId9qEiYmIimmIyrKIpNmIY4kUUSCIr +1qIt3iIuUuInsKEnulJDRcEHWASAoQBJlKIpoiIruqImKmMmNmIRWkYtAEMuTiM1VqMp8kEvNtMa +lICAeRcdpuIlIqMpMqMqEiIrwiJqrAE4WiM7tqM7NkE2Av/TFQyY4ZFiNAADPuajPu4jP27CDNwj +PwZkQHJCCQCkQB4kPhKkQSKkQEqDI5pGB2ADQ04kRQKDM0SBAGRAJwRDRXakR+KjIzSAAnQAMnyk +SeZjLpBKPLpSDhCYvHHAQn6kP8akRyrkSSZkQd4kMDhkLNbFF9CkTiKkCMBBNITCGQBlUFKkAQgD +PyoBK3BkUlbkEZjBSrpSChTYFQAATOrkTOqkTd7kV56kQz7jXWQCVEYlQlJDAzyDPp5BAQQAFUwD +GjzBWz4CAyiAIQRDBZCAAHQAMzhDDAhAADxCNMQAGBRABrRCMNQBYm5kBaRAXz5DYAaAKJylM1TA +RqIlRUL/AgDoWVW20hn4gAnYAIA9wVbeZFeCZU6qJlJSJE+ahgzcwllqZkCmAgiwJT4eQg/8ZQN0 +whc4ADJMAgO0giLc5gDEQjBEAR88wRsU5QAIQwwIwTQswRv4QQ88QyhYwDMYwCP0QgWUQjAspT4a +QADoADPQ5kQOAg1+ZiOJjHsCAFWewWzK5D965WqeZFia5Fg+Yl2ggESip0BmJ24CAxq0wT2igSp8 +gYGmggg8A4M+wwAsA4G2QQiAJzDEgGSCZ4G2wVsGQAAIg3jGAHiK5z7O5TQA6EHKwglwJnv6knuy +gTRwZX2ypn225kS+pmXE5nyiKD42QBpEAzWcgXUyg1q2/0KBRsODPuhxJudyNmcqNMAyiGgwFKh1 +DmiIjigv8KOJ8mhAqmeLOhMHTEMwjCmZlqmZnikpzICYnimbsukmlMCatqmcjumbxumctik1PKRl +ZEI03Kmf/mkwoAIIZKQhRIMPCGYlTMMXvME02OYyOOoArIAArMAyUMOgZgAnRIMIlEI0LOo0+MBb +UqoBWEMwbGo0MOYm9CkaEEAArMAq9CmgxmqbtldPfukmhamsmmma2mmu1mmulqmv/uqY5mmtzkVc +Ciuy/upzJiuzNqucFkEb2Cow4Sqy7mqyBquwYuuv5ilZ2sUHGIOzhmubLqW4lmuyzgKqMJO0uhIH +PEM0vP8rvMarvM6rLMyAu84rvuIrKZTAvearv77rvvbrv+arKZQgatCBwA6swi4swzaswz4sxCrs +E+RBIKyrL7VrxL5rvSaswwZsxkaDx2ZswXbrXTjCNHwsyqasyq5sw3JXqnGXxbYSxmbsxn5syEbs +zUJswRYrXaSAJpwsywat0A7tw7LCDvBszOoRByzDNDSt0z4t1EbtLMwA00at1VqtLJRA1V4t1zZt +1m5t117tKegpanzCM4Qt2qat2q4t27at23atJETBJIRM0rIr2Lbt1N4t237t23qt1vbtNIwt0tJF +CfzB2QIu4iau4i7u1RZCFsytutYtJy0t4OYt4PJt32L/7tuOLclaBh1cA+OGruiObtgaAxIAQKpF +ruTeqjA8g+u+LuzGruzWwgy0ruze7u3OQgnYLu72ruvqLu/6Lu6+gsEyxw8AgSosg/AuL/M2r/M+ +L/RGr+syAg2UQcWq7uo2EgcEL/TSLvc+L/BK7+/urvg+A/F2rmngQBOsQvm2r/u+L/xuAhNsmsjA +bPZ20vaWr/eWb/iKb/9KL/EOLl6EAQioAfvCLwInsALDLipUAQ1QJfbe761agxBVsAVfMAYvQy3A +AAVnsAdf8NR28AePcAiPsAmfL38qBx50VxjYgjCYMAzHsAzPMA3XsAenghNI2PUCQENJsMyKsA1v +MBDX/3AJ27AQFbERBzCAgAEIPAElvLARR7EUT7ENI0MiGAEOlAHdRrAPa+8Q07AQSzES2/AY1zAK +A0gfKQEKlIELU7Ebv7Ebi8IcdIARAMAO228Xy+wxCAMf97Ef/zEgewIM7DEgF3Ih0y4hG7Ii8zEi +L7Ijt0LxegjUgAANkEEnWIMjZ7ImbzInd7IiKwIddACqFALd5nEzcUAie7IwCHIqe3IjqzIjz0Ar +dzIko+9zNBRyxYANlEEk8AIs/zIwB7MwtEIf1AGKdeYWA8AXmPK0zjInszIwvzIsS7MqQ7IAQwcc +EAEUoAAUyMEl9IIwh7M497E0/AEdnEALXMEU9NF7Mv/zM6EyMEPzL1OzK8syMNdyCn/ImYnMD2zA +DeAAEUACK2DyOBe0IzsDLHgBFrQAkVVBMovMCrvzO2ODNVS0RV80Rme0IFN0Rnd0R9MuR3u0SFc0 +SI+0Sa9CJJMJKY9BHVgBDnAADjwBHkhCLRyDSd80Tuf0RcPCIoiBFbTADZjAE7DBHTy0RLcTB4S0 +Tlv0Ri81Rpe0U1s0VEe1NaC0LR8JO/NwckUBDXAACjDBEaDBHSACK8yCTVP1UsOCKvDBHaiBFRxB +DXBAnylXe71n/R41PyU1WltDU6N1X1P1VEc1Sl8zktg1fALAD/hAEZhAV3PADhxBGNyBZE82WauC +ZV//NmZbNltPtmRjwRGoAJiZgAkMgQ9gwWEbtsjkM153U1xDGIOBmWsnGGzHNm3Xdp+ptqQsM3eh +NgB0AQCAQXIFt2KLNnEXd3ELd3CzgVbz9ntyF1Wutkcxt1Eb9iMwQiVMN3a/5yMYwnVLt3Rvd3dn +t3gbjHeLd3nfNXQnVeqaN3bT13ljNyWwt3RLgnxjNx4fzDLLAQDot35zl38DwH/z936nN4EXuIEf +OIInuIIvOIM3uIM/OIRHuIRPOIVXuIVfOIZnuIZvOId3uNogQACM4q4kQAAsgIdr+HcwR7ucOIu3 +BZiUgIc+CYgbRAD0l3ngRg7E+ELoOFcEgFfcuGvUvnhjsAkAKEhFJEQAVNeUiAWPX8QFtDiH40ZZ +3EmTDARkpEVfyABhUIluMIQDnMRxLPlxWImXlHlCpDiUR3hydIlhZEqsIMiELEiQxziERMZWGDmD +eOiFdImea0gRbEmaR3iKSzmbU3kDzKGciPmMGIadj3laUMmTSASkmwSgBzqEe+iTgwCPu3lTEDkA +OACPy/mcx0gPBEALZEWfIIBUkHhS6DmMCISmNzmIV7ql17pdrLit5/pQ4LpcvMszBQQAOw== + +------=_NextPart_01CC4DF5.1FA44F90 +Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAA +LAAAAC0AAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA3AAAAOAAAADkAAAA6 +AAAAOwAAADwAAAA9AAAAPgAAAD8AAABAAAAAQQAAAEIAAABDAAAARAAAAEUAAABGAAAARwAAAEgA +AABJAAAASgAAAEsAAABMAAAATQAAAE4AAABPAAAAUAAAAFEAAABSAAAAUwAAAFQAAABVAAAAVgAA +AFcAAABYAAAA/v////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKASDcwvTswB +/v///wAAAAAAAAAAXwAxADMANwAzADQANQAyADAAMAAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAAta0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALIC +AHic7J0NfFxVnffPPVOSdho6bWYSum50yOzMOu2dJuHNjRpg4E5CfGEFFtjKFCxo17CioOCavmwk +bjS7RqMuwWCW7BPXaLSAL6ywEesSV4txbVd2XXy6a1erNCWP4trSUimkyfP7/+fcm0M2ydxOOZMs +n4yfHzP/5N75/c59Peeb0+vjP1xz4LNfe8XPxYzXRSIgJqdWiBLtZwHIcovVQkhVT05NTbk/XgYd +h6aWXv8rXiehZ9U+/GPsTKppn5dCy6EVUBBaCZVBZ0KroFDuEBBroHIoDEWgCqgSOgtaC/0O9Aro +d6Eq6JXQq6AodDZUDcWg34PiUAL6fejVUBJaB62HbCgFbYBqoFqoDjoHOhc6DzofugB6DfQHUD30 +Wuh10OuhBuhCPraFuBhKQ5dAl0IOlIEaoSboMqgZegP0RuhN0Juhy6E/hN4CXQFdCV0F/RF0NXQN +dC30x9BG6K3QdVAW2gRdD90AvQ3aDN0I3QS9HXoHtAX6E+idUAt0M/Sn0LugW6B3Q++BboVug94L +vQ+6HboDej/0Z9AHoFZoK7QN2g7tgP4caoM+CN3J2W/F/+7AvmjE996B76I1/L8qxRneuU7nfuQV +kn8+kvt1k77syG+Ce2r/cI9F15E7A7mfWdiSt3CrC3sFhbT049nvehH1HkC7r8BWeAP26tUF+K/G +VdASuWujX/8zoDt+lruSWjjybsVevQ3b4Sbe06f2Ki+g/eR8u/pM571U767ynf+Thq9JS6/ivazc +bbzgl4WjIRDMHfszz326/l9+89vfd+vtt/7JHWdfe/PtN996duZ9N37g5ve8k+8ZuZ+cc05N3dl/ +1HLjbVtu57sJ/7RGLVdzzjni2GsffO88CQLTfZICXpNT1GcpfBPQ+iXyf65PP5FP7H3i3prfXX1X +73Jhp058me4vZ8z42R0IX2flzi9qx2a1Ll376T5L13S6194pcvfUAZFb9osid//9qsjde4dF7r47 +InLn6KjIna/Pqe87uCx3312m8rmf26EPQX8BdUAfhj4CdUJ/Cf0V9FGoC/oY9HGoG/oE9EnoU9Bf +Q3dBPdDd0KehXuge6DNQH/Q30L1QP/S30P9Rbfks9HfQ56BB6PPQF6Ah1cYvQTuh+6D7oQegL0Nf +UW3/GvQg9PfQ16GHoIehf1Db5BvQI9A3oV3Qt6B/hB5V2+rb0D9B34G+C+2GHoO+p7bh96F/hn4A +7YH2Qv8C/RB6HPpX6N+gH0H/Dj0B/Rj6v9A+6D+g/4R+Au2H/gv6KfQz6ABEHe9fQE/SPoLGoEPQ +U9A49P+gX0K/gp6Gfg39N/Qb6DB0BHoGOgodE7lr87WqH0f7/gT0PPQCNAGdpGUgvlFYuWOO+hV0 +TFWcmJraggv/GnVvptdKdGxi6KCd3Xh5Ey1N961Dq3P3MP11J9b7Kg6o5TjY0jNOyPRv8MtfLOPO +APXhzobwdbaFBalPiK8TcvXIT9zjn0TL0rHfopatS0teLnf83nlxNL2Mj296vSp9hvf5lencvZ3u +XXTcN1n0+xLv98G0GHE99+7d+0Hy/Af0jNaiN1mt1qP+WFlu83A/dK2YHvdgHX5PaMtQv/AdInfM +UR+Ljo/7tJrWrxPUl7xO32IXz/559tda5Yfdyv3keG69C7Xv8D5Tu29Q28+9MsVVBurjnK1+llTt +Xc7tyLXQ/d3MF/lS35bOxbne3W0itc/47jvn2j634aAbwaFxp+pAfZ+vCLNtk/zbx/Wjr9P3jbat +LtG+z/tMWd1tS69i7Es3Hy0cE95+naJjnvYJHZ90zOvH9bL0dEZajq41o2q55WkROF+tS+cRn5tp +ynw/rlOkHXyMUy0sy2oPBALnpcUy9/uWpy2JxXn8ROsL1W563bdz50VSbSdXQluOzqHlaXlK/rRf +KrTzmerZzj33Z69X72HtdzSeukLbpm5/hvJRf/UMpYcx8nkYd43HWdu5drPo2RqUh6N56H5J8T+P +bf3cp3sOjbHovnC/VtPYl8anp3O8RJQf9cUXw3VgvmsAvevXgbD22d2PxAJWi9z5PxzMjZHrX+zB +LxqDHC4bDo6XjZBKD5eNlt62crS0deUIKUjj6/NnWY/GL60rdwffv3I0iOWDt0AtqFuwzmuUd4u6 +9mxU7Z/5HQFeZlfpLSuH4Tlc6q53GI0ZzrPes2XDpc+V7YJGvPWew3ojwfnXe65sd/DZstEg2unl +pG00qtabq616+5AZ7R1Gu4eDF6rt7R7bxD3o+Jl5rOrH+elcO93jlPqfxTgv5rqOzrymptW2y10r +A8vczO41DNt+9fnpwJzXWup3HRDzX2sfwtG/l7UNn7dxrV1rLff71qdz23QtVyOXTrdm5J9+ueEC +55on1jnL08ssOj+blccyMX3+3rdz/a6cPnrRNdeccfHhw4f5OHDfT/7nTV+/8dXHL971qjMexeev +XfeJD130H3/1yoag5utec2d+13zX4Fepdal/TTyJekH6PZ/u825fye0+ItOL9lNgxvLEyeq0/eke +07RNvy5aoa24D5FaudbX1fd9Uvv+mRmmFBLJl8FtF/Xx6fjMZdgKbYf/ds5BdSEZJicnfWfYrjK0 +eRm2Q23wb+McVBeS4eTJk74ztKkM7V6GNqgd/u2cg+pCMkxMTPjO0K4ydHgZ2qEO+HdwDqoLyfDC +Cy/4ztChMnR6GTqgTvh3cg6qC8nw/PPP+87QqTJ0eRk6oS74d3EOqgvJcOLECd8ZulSGbi9DF9QN +/27OQXUhGZ577jnfGbpVhh4vQzfUA/8ezkF1IRl++9vf+s7QozL0ehl6oF7493IOqgvJcPz4cd8Z +elWGPi9DL9QH/z7OQXUhGZ599lnfGfpUhn4vQx/UD/9+zkF1IRmOHTvmO0O/yjDgZeiHBuA/wDmo +LiTD0aNHfWcYUBkGvQwD0CD8BzkH1YVkeOaZZ3xnGFQZhrwMg9AQ/Ic4B9WFZDhy5IjvDEMqw5e8 +DEPQTvjv5BxU+8mgj+Fn9uFaRY5TUR/MTk/3z/R+2+mMY9Pp9FRFWrxoTOpnfEV/x6tX+d0xzPRY +xvI4Gb5f6K/ZtusZ2nb9e9Vmt99M33PFyoHg42WsUnwmBWfjHhTST99d3/ZNKndKuOxhfmb2Q8XM +8o2LKf8OkWOa92n1EhObffvQ+HKxMjGT+3KusdxLzcSI+TzF2sHH8A+LzMTm8i8mE8uNZx4Q46zc ++MbNomdrEKfHxGh70XiG/pZxv1a/HJnYqVwH5mNizJfyMLHbVg0HW1aNkEpvWzXKXIxYEzQvE9P5 +0tPQOGpiay5rGi/Lz8TGy3aVPl02DM9pJnbbqvxM7P2rhktbV+2CpplY66r8TKx11e7g+1eNBtFO +Lydto3xMTG8fMqO9w0FiiXMxsZnHqkkmZvK8mOs6Ot819XTZF3GvMdY25l97C2Jfly969kXMixjR +mDDLvvag57dHbMV9ltTKdSF9eL/sy20X/f3ZZV/kvwfVIdZWrgvJ4Jd97VXelMFlX+S/B9Uh1nau +C8ngl33tVd6UwWVf5L8H1SFWG9eFZPDLvvYqb8rgsi/y34PqEKud60Iy+GVfe5U3ZXDZF/nvQXWI +1cF1IRn8sq+9ypsyuOyL/PegOsTq5LqQDH7Z117lTRlc9kX+e1AdYnVxXUgGv+xrr/KmDC77Iv89 +qA6xurkuJINf9rVXeVMGl32R/x5Uh1g9XJtkX3uVN2Vw2Rf570F1iNXLtUn2tVd5UwaXfZH/HlSH +WH1cm2Rfe5U3ZRjwMvRDA/Af4Bx7DLOvvcqbMrjsi/z3oDrEGuDaJPvaq7wpg8u+yH8PqkOsQa5N +sq+9ypsyuOyL/Pegd3CINcT1EvtKC/2Vj339QLXZ7R/T9xD3umIVq5T4F9ULxb6e8sm+KP8OkZtv +d59Wv1zY16nMA5mNfc3cPjSOXKzsy+S+LBb7IrYzxdrBx/BTRWZfc/kXk33lxjMPYCVSbnzjZtGz +NYjTY1/ESGk8QyOw+7V6iX3Nzb6YI+WbDxYaDo6HRkilh0OjzL+IKUHzzwfTONItUAtqYmjefLBV +PuaDrdpVesuqYXhq88FCPuaDhYZLnwvtgrT5YCEf88FCu4PPhkaDaOf0fLBVPuaDae1DZrR3OEjM +cC72NfNYNcm+TJ4XC8G+iHudZG1j/jVWEPvaotjXGUbZF2UulH0R8yJGRCTFJPs6iJ7fQbEV91lS +K9cm2Zfbrgkxzb7I/yCqSdZWrk2yrzHlTRlc9kX+B1FNsrZzbZJ9jSlvyuCyL/I/iGqS1ca1SfY1 +prxpaZd9kf9BVJOsdq5Nsq8x5U0ZXPZF/gdRTbI6uDbJvsaUN2Vw2Rf5H0Q1yerkupAMftnXmPKm +DC77Iv+DqCZZXVwXksEv+xpT3pTBZV/kfxDVJKub60Iy+GVfY8qbMrjsi/wPoppk9XBdSAa/7GtM +eVMGl32R/0FUk6xergvJ4Jd9jSlvyuCyL/I/iGqS1cd1IRn8sq8x5U0ZBrwM/dAA/Ac4x0HD7GtM +eVMGl32R/0FUk6wBrgvJ4Jd9jSlvyuCyL/I/iGqSNch1IRn8sq8x5U0ZXPZF/gfRO5hkDXHtJ4M+ +Vp/ZV/ukyF2PZ7Kv5emSOebxl2jLvHyY2JMix8TcfjN9D/Gwx0OsUuJiVM/GPUQRmNiUTyZG+XdA +lVaOo7j1EhObffvQ+HKxMjGT+3KusdxLzcSI+ZxlkXbwMTxVZCY2l38xmVhunPOAWGuRcuMeN4ue +rUGcHhOj0RCNc86yctcwt15iYnMzMeZL+eaDrRkOtqwZIZXetmaUuRixJmj++WAaX3oaGkdNbM2b +DxbyMR8stKv06dAwPLX5YGt8zAdbM1zaumYXpM0HW+NjPtia3cH3rxkNop3T88FCPuaDae1DZrR3 +OEgscS4mNvNYNcnETJ4Xc11HTTIx4mEVFmkbc7GTBTGxP1/0TIzICrGjCsssE5tAz29CbMV9ltTK +dSF9e79MzG1XxJpmYuQ/garS2s45JgwzMcqwXWVo8zJsh9rg38Y5JgwzMVqqTWVo9zK0Qe3wb+cc +E4aZGGVoVxk6vAztUAf8OzjHhGEmRhk6VIZOL0MH1An/Ts4xYZiJUYZOlaHLy9AJdcG/i3NMGGZi +lKFLZej2MnRB3fDv5hwThpkYZehWGXq8DN1QD/x7OMeEYSZGGXpUhl4vQw/UC/9ezjFhmIlRhl6V +oc/L0Av1wb+Pc0wYZmKUoU9l6Pcy9EH98O/nHBOGmRhl6FcZBrwM/dAA/Ac4x4RhJkYZBlQGl4mR +/wSqSmuQc0wYZmKUYVBlGPIyDEJD8B/iHBOGmRhlGFIZvuRlGIJ2wn8n55gwOh/s5cm+XlBtdvvH +9D3Eva5Ywyol/kX1QrGvSWuZL/Y1aeW2Z4XMPa/WrZfY1+zb50A09+yWxci+TO7LYrGvSet8USlJ +y/gYprqY7Gsu/2Kyr5NWALpAnCVJAa7dLHq2BnF67Ouklbu2VMrcdnTrJfY1N/ui878+MD/72lxd +H9hYnSbhczPWaRbj0TQpMB8PGo82BsaizQEsH9gP7UO9D+u4TGmfuvbMx6L2RRvE/mg9POuFu97m +6lzW+dbbUl0vWqoboLS3XgvWSwfyzD+rbgxsqW4OoJ1eTtpGzYH52ZfePmRGe+vR7vrAXOxr5rFq +kn2ZPC8Wgn1NWOeKiCRJfJZcnyr7erZy0rnmmssuXczsa8LKZYnI3H4yxb5esEgW7rMW32upNsm+ +3HaFZW6ZXAYLCsA/wDmoNsm+JtQ5QBlKvAwBqAT+JZyDapPsizKUqAwrvAwl0Ar4r+AcVJtkX5Rh +hcpQ5mVYAZXBv4xzUG2SfVGGMpUh5GUog0LwD3EOqk2yL8oQUhnCXoYQFIZ/mHNQbZJ9UYawylDp +ZQhDlfCv5BxUm2RflKFSZajyMlRCVfCv4hxUm2RflKFKZYh6GaqgKPyjnINqk+yLMkRVhpiXIQrF +4B/jHFSbZF+UIaYyJLwMMSgB/wTnoNok+5pQ9wLKkPQyJKAk/JOcg2qT7IsyJFWGlJchCaXgn+Ic +VJtkX5QhpTLUehlSUC38azkH1SbZF2WoVRnO8TLUQufC/1zOQfUS+0oL/ZWPfT2fG255/WP6npHo +2YF0NYs+c71Q7IvGx37YF+Wn7blB8RK3XmJfs28fGkemxeJkXyb3ZbHYF7GdGtYyPoapLib7msu/ +mOwrN565QNSycuMbN4uerUGcHvuqUGOpGnUNc+uXI/t6qZ6NzxwpD/s6EKsP7IulSfjczPyLmFJL +9fzsS+dIm6CNqImhuUxpY3V+9rWxukFsqq4n/uYxrAOx/OxrLFYvxmMN0DT7Go/lZ1/jscbAWKw5 +gHZ6OWkb5WNfevuQGe2tDxAznIt9zTxWTbIvk+fFQrAv4l4plmT+RfWpsq9tK8/ILHb2RcyLsqQM +sy8e70sL91mL77XuWOtU+/B+2ZfbLltjX+Qfxn1hA8vi2iT7iihvW2Nf5B+WJfAv4RxUm2RfEeVt +a+yL/MNyBfxXcA6qTbKviPK2NfZF/mFZBv8yzkG1SfYVUd62xr7IPyxD8A9xDqpNsq+I8rY19kX+ +YRmGf5hzUG2SfUWUt62xL/IPy0r4V3IOqk2yr4jytjX2Rf5hWQX/Ks5BtUn2FVHetsa+yD8so/CP +cg6qTbKviPK2NfZF/mEZg3+Mc1Btkn1FlLetsS/yD8sE/BOcg2qT7CuivG2NfZF/WCbhn+QcVJtk +XxHlbWvsi/zDMgX/FOeg2iT7iihvW2Nf5B+WtfCv5RxUm2RfEeVta+yL/MPo/2xg1XK9xL7SQn/l +Y1/lKqDbP6bvIe41EmPRZ64Xin3V+GRfNYqPNCpe4tYvF/Y133jXzzPwZ24fGkemxeJkXyb3ZbHY +F7GdJtYyPoZrisy+5vIvJvvKjWcuEJexcuMbN4uerUGcHvvaoMYzTeoa5tZL7Gtu9sUcKQ/72hyv +D2yMp0n43Mz8i5gSNP+8L40j7Yf2oSaG5s37ivmY9xVrEPtj9cTfpud9xX3M+4rXi5Z4A6TN+4r7 +mPcVbwxsiTcH0M7peV8xH/O+tPYhM9pbHyBmOBf7mnmsmmRfJs+LhWBfxL0yLMn8K1UQ+1q56NlX +SjGijGH2RX1pW1q4z1p8r3XHWqfah/fLvtx2ORr7In8b94VGlsW1SfaVUt6Oxr7I35Yl8C/hHLZh +9pVS3o7Gvsjflivgv4Jz2IbZV0p5Oxr7In9blsG/jHPYhtlXSnk7Gvsif1uG4B/iHLZh9pVS3o7G +vsjflmH4hzmHbZh9pZS3o7Ev8rdlJfwrOYdtmH2llLejsS/yt2UV/Ks4h22YfaWUt6OxL/K3ZRT+ +Uc5hG2ZfKeXtaOyL/G0Zg3+Mc9iG2VdKeTsa+yJ/Wybgn+ActmH2lVLejsa+yN+WSfgnOYdtmH2l +lLejsS/yt2UK/inOYRtmXynl7Wjsi/xtWQv/Ws5hG2ZfKeXtaOyL/G30fxpZtVwvsa+00F/52Nd6 +FdDtH9P3EPdKx1n0meuFYl9NPtlXk+Ij1yte4tZL7Gv27UPjyLRYnOzL5L4sFvsitnMDaxkfw01F +Zl9z+ReTfeXGMxeIt7Fy4xs3i56tQZwe+2pU45kb1DXMrZfY19zsizlSHvZ1IFEf2JdIk/C5mfkX +MaWWeJ55XxpH2gRtRE0MzZv3Ffcx7yveIDbF64m/Tc/7SviY95WoF+OJBkib95XwMe8r0RgYSzQH +0M7peV9xH/O+tPYhM9pbHyBmOBf7mnmsmmRfJs+LhWBfxL02sSTzr0xB7GvNomdfGcWINhlmX9SX +dqSF+6zF91p3rHWqfXi/7MttV1ZjX+Tv4L5wPcvi2iT7yijvrMa+yN+RJfAv4RyOYfaVUd5ZjX2R +vyNXwH8F53AMs6+M8s5q7Iv8HVkG/zLO4RhmXxnlndXYF/k7MgT/EOdwDLOvjPLOauyL/B0Zhn+Y +cziG2VdGeWc19kX+jqyEfyXncAyzr4zyzmrsi/wdWQX/Ks7hGGZfGeWd1dgX+TsyCv8o53AMs6+M +8s5q7Iv8HRmDf4xzOIbZV0Z5ZzX2Rf6OTMA/wTkcw+wro7yzGvsif0cm4Z/kHI5h9pVR3lmNfZG/ +I1PwT3EOxzD7yijvrMa+yN+RtfCv5RyOYfaVUd5ZjX2Rv4P+z/WsWq6X2Fda6K987OtSFdDtH9P3 +EPcaSbDoM9cLxb4mLX/PuqdnQu0QuX/XcZ9WL7Gv2bcPPctmsT7r3uS+LBb7mrTuF5WStIOPYaqL ++7yv2f2Lyb5OWtuhB8RZkrSdazeLnq1BnB77oucY0fOL6d/G3q/VS+xrbvZF53++Z91vrh4Obqwe +IZVurh4tPRAdLR2PjpDmfdb9eHR3cCw6GsTywf3QPtT7otPPut8Xzf+s+33RXaX7o8PwnH7WPf0b +xHzPut9SPVzaUr0Lmn7WPT3vK9+z7luqdwe3VI8G0U4vJ22jfM+619uHzGjvMNo997PuZx6rJtmX +yfNiIdjXhLVTRCRpGz5v47qg530t8mfd0/M9+Jnw0uyz7l+wWqGtuM+SWrk2yb7cdtG/rXSfdU/+ +L+C+UCG3cw6qTbKvCXUOUIY2L8N2qA3+bZyDapPsizK0qQztXoY2qB3+7ZyDapPsizK0qwwdXoZ2 +qAP+HZyDapPsizJ0qAydXoYOqBP+nZyDapPsizJ0qgxdXoZOqAv+XZyDapPsizJ0qQzdXoYuqBv+ +3ZyDapPsizJ0qww9XoZuqAf+PZyDapPsizL0qAy9XoYeqBf+vZyDapPsizL0qgx9XoZeqA/+fZyD +apPsizL0qQz9XoY+qB/+/ZyDapPsizL0qwwDXoZ+aAD+A5yDapPsizIMqAyDXoYBaBD+g5yDapPs +izIMqgxDXoZBaAj+Q5yDapPsizIMqQxf8jIMQTvhv5NzUL3EvtJCf+VjX/S8L2qz2z+m7xmJDgTT +1axSfCYt2LPuaXzs93lfO0Tu33Xcp9VL7Gv27UPjyMXKvkzuy2KxL2I7NawdfAxTXdznfc3uX0z2 +lRvPPCBqWbnxjZtFz9YgTo99VaixVI26hrn1y5F9ncp1YD72xRwpD/s6EBsO7ouNkEoPxEaZfxFT +aqmen33pHGkTtBE1MTRv3ld1fva1sXpX6abqYXhOsy/6N4j52NdYbLh0PLYLmmZf9LyvfOxrPLY7 +OBYbDaKdXk7aRvnYl94+ZEZ7h4PEDOd73pd+rJpkXybPi4VgX8S9UqxtzL+oLuh5X4ucfRHzIkaU +Msy+wrIV2or7LKmVa5Psy22XrbEv8g/jvrCBtZVrk+wrorxtjX2Rf1i2wb+Nc1Btkn1FlLetsS/y +D8t2+LdzDqpNsq+I8rY19kX+YdkB/w7OQbVJ9hVR3rbGvsg/LDvh38k5qDbJviLK29bYF/mHZRf8 +uzgH1SbZV0R52xr7Iv+w7IZ/N+eg2iT7iihvW2Nf5B+WPfDv4RxUm2RfEeVta+yL/MOyF/69nINq +k+wrorxtjX2Rf1j2wb+Pc1Btkn1FlLetsS/yD8t++PdzDqpNsq+I8rY19kX+YTkA/wHOQbVJ9hVR +3rbGvsg/LAfhP8g5qDbJviLK29bYF/mH5RD8hzgH1SbZV0R52xr7Iv8w+j8bWENcL7GvtNBf+dgX +Pe+L2uz2j+l7iHuNxFilxL+oXij2VeOTfdUoPtKoeIlbv1zY1+k+637m9qFx5GJlXyb3ZbHYF7Gd +JtYOPoZrisy+5vIvJvvKjWceEJexcuMbN4uerUGcHvvaoMYzTeoa5tZL7Gtu9sUcKd+8r/hwcGN8 +hFS6OT7K/IuYEjT/vC+NI+2H9qEmhubN+4r5mPcV21W6PzYMT23eV9zHvK/4cGlLfBekzfuK+5j3 +Fd8d3BIfDaKd0/O+Yj7mfWntQ2a0dzhIzHC+533px6pJ9mXyvFgI9kXcK8PaxvwrVRD7Wrno2VdK +MaKMYfZly1ZoK+6zpFauTbIvt12Oxr7I38Z9oZG1lWuT7CulvB2NfZG/Ldvg38Y5bMPsK6W8HY19 +kb8t2+Hfzjlsw+wrpbwdjX2Rvy074N/BOWzD7CulvB2NfZG/LTvh38k5bMPsK6W8HY19kb8tu+Df +xTlsw+wrpbwdjX2Rvy274d/NOWzD7CulvB2NfZG/LXvg38M5bMPsK6W8HY19kb8te+Hfyzlsw+wr +pbwdjX2Rvy374N/HOWzD7CulvB2NfZG/Lfvh3885bMPsK6W8HY19kb8tB+A/wDlsw+wrpbwdjX2R +vy0H4T/IOWzD7CulvB2NfZG/LYfgP8Q5bMPsK6W8HY19kb+N/k8ja4jrJfaVFvorH/tar9iX2z+m +7yHulY6zSol/Ub1Q7KvJJ/tqUnzkesVL3HqJfc2+fWgcuVjZl8l9WSz2RWznBtYOPoabisy+5vIv +JvvKjWceEG9j5cY3bhY9W4M4PfbVqMYzN6hrmFsvsa+52RdzpHzzvhLDwX2JEVLpgcQo8y9iSi3x +PPO+NI60CdqImhiaN+8r7mPeV3xX6ab4MDy1eV8JH/O+EsOl44ldkDbvK+Fj3ldid3AsMRpEO6fn +fcV9zPvS2ofMaO9wkJjhfM/70o9Vk+zL5HmxEOyLuNcm1jbmX5mC2NeaRc++MooRbTLMvhzZCm3F +fZbUyrVJ9uW2K6uxL/J3cF+4nrWVa5PsK6O8sxr7In9HtsG/jXM4htlXRnlnNfZF/o5sh38753AM +s6+M8s5q7Iv8HdkB/w7O4RhmXxnlndXYF/k7shP+nZzDMcy+Mso7q7Ev8ndkF/y7OIdjmH1llHdW +Y1/k78hu+HdzDscw+8oo76zGvsjfkT3w7+EcjmH2lVHeWY19kb8je+Hfyzkcw+wro7yzGvsif0f2 +wb+PcziG2VdGeWc19kX+juyHfz/ncAyzr4zyzmrsi/wdOQD/Ac7hGGZfGeWd1dgX+TtyEP6DnMMx +zL4yyjursS/yd+QQ/Ic4h2OYfWWUd1ZjX+TvoP9zPWuI6yX2lRb6Kx/7ulSxL7d/TN9D3GskwSol +/kX1QrGvSeuY7+d90VlN/67j96zpeol9zb596Fk26dDiZF8m92Wx2NekFbcqJekYH8NUF/d5X7P7 +F5N9nbSOQgnrLEk6yrWbRc/WIE6PfdFzjOhuSv82Fq306iX2NTf7ovO/vjzf877qyzdWp0mhzdXN +oQPR5tB4NE0qn48HjUcby8eizeVYvnw/tA/1PqyjP++Lrj3zsah90YbQ/mg9POtD+r9BrM+z3pbq ++lBLdQOU9taj532ly/M976uxfEt1czna6eWkbdRcnu95X9PtQ2a0tx7tri+f73lf+rFqkn2ZPC8W +gn1NWDErIknP4PMzXBfyvK/7fvnJRc2+6Pke1DPn/697yxz7esE6DB3BfZZ0mGuT7MttFz235Khw +n1tyBDoK/6Ocg2qT7GtCnQOU4biX4Sh0HP7HOQfVJtkXZTiuMpzwMhyHTsD/BOeg2iT7ogwnVIYJ +L8MJaAL+E5yDapPsizJMqAxTXoYJaAr+U5yDapPsizJMqQwBy80wBQWsCkma4tok+6IM5E0ZSrwM +AesFqwT+JZyDapPsizKUqAxlXoYS+JbBv4xzUG2SfVGGMpUh5GUog28I/iHOQbVJ9kUZQipDuZch +BN9y+JdzDqpNsi/KUK4yVHgZyuFbAf8KzkG1SfZFGSpUhrVehgr4roX/Ws5BtUn2RRnWqgxVXoa1 +8K2CfxXnoNok+6IMVSpD1MtQBd8o/KOcg2qT7IsyRFWGai9DFL4x+Mc4B9VL7Cst9Fc+9kXP+zos +pvvH9D0j0bPL09WsED6TyheKfdH42O/zvuis3qB4iVsvsa/Ztw+NIxcr+zK5L4vFvojt1LCO8TFM +dXGf9zW7fzHZV248k7BqWbnxjZtFz9YgTo99VaixVI26hrn1y5F9vVTPumeOlId9HYjVl++LpUmh +A7Fm5l/ElFqq52dfOkfaBG1ETQzNm/dVnZ99baxuCG2qrofnNPuif4OYj32NxepD47EGaJp90fO+ +8rGv8Vhj+VisuRzt9HLSNsrHvvT2ITPaW19OzHC+533px6pJ9mXyvFgI9kXcK8V6RkRYp86+6Hlf +i519EfOinnnKMPsKy8PQEdxnSYe5Nsm+3HbZGvsi/zDuCxtYR7g2yb4iytvW2Bf5h+Vx+B/nHFSb +ZF8R5W1r7Iv8w/IE/E9wDqpNsq+I8rY19kX+YTkB/wnOQbVJ9hVR3rbGvsg/LKfgP8U5qDbJviLK +29bYF/mHZcDawJri2iT7iihvW2Nf5B+WJfAv4RxUm2RfEeVta+yL/MOyDP5lnINqk+wrorxtjX2R +f1iG4B/iHFSbZF8R5W1r7Iv8w7Ic/uWcg2qT7CuivG2NfZF/WFbAv4JzUG2SfUWUt62xL/IPy7Xw +X8s5qDbJviLK29bYF/mHZRX8qzgH1SbZV0R52xr7Iv+wjMI/yjmoNsm+Isrb1tgX+YfR/9nAinKe +JfaVFvorH/ui530dFtP9Y/oe4l4jMVaI+BfVC8W+anyyrxrFRxoVL3Hrlwv7mm+86+dZ9zO3D40j +Fyv7Mrkvi8W+iO00sY7xMVxTZPY1l38x2VduPJOwLmPlxjduFj1bgzg99rVBjWea1DXMrZfY19zs +izlSHva1OV5fvjGeJoU2x5uZfxFTguaf96VxpP3QPtTE0Lx5XzEf875iDaH9sXp4avO+4j7mfcXr +Qy3xBkib9xX3Me8r3li+Jd5cjnZOz/uK+Zj3pbUPmdHe+nJihvM970s/Vk2yL5PnxUKwL+JeGdYz +IsUqhH2tXPTsK6UYUcYw+7LlYegI7rOkw1ybZF9uuxyNfZG/jftCI+sI1ybZV0p5Oxr7In9bHof/ +cc5hG2ZfKeXtaOyL/G15Av4nOIdtmH2llLejsS/yt+UE/Cc4h22YfaWUt6OxL/K35RT8pziHbZh9 +pZS3o7Ev8rdlwGpkTXFtkn2llLejsS/yt2UJ/Es4h22YfaWUt6OxL/K3ZRn8yziHbZh9pZS3o7Ev +8rdlCP4hzmEbZl8p5e1o7Iv8bVkO/3LOYRtmXynl7Wjsi/xtWQH/Cs5hG2ZfKeXtaOyL/G25Fv5r +OYdtmH2llLejsS/yt2UV/Ks4h22YfaWUt6OxL/K3ZRT+Uc5hG2ZfKeXtaOyL/G30fxpZUc6zxL7S +Qn/lY1/rFfty+8f0PcS90nFWiPgX1QvFvpp8sq8mxUeuV7zErZfY1+zbh8aRi5V9mdyXxWJfxHZu +YB3jY7ipyOxrLv9isq/ceCZhvY2VG9+4WfRsDeL02FejGs/coK5hbr3EvuZmX8yR8rCvA4n68n2J +NCl0INHM/IuYUks8z7wvjSNtgjaiJobmzfuK+5j3FW8IbYrXw1Ob95XwMe8rUR8aTzRA2ryvhI95 +X4nG8rFEcznaOT3vK+5j3pfWPmRGe+vLiRnO97wv/Vg1yb5MnhcLwb6Ie21iPSMyrELY15pFz74y +ihFtMsy+HHkYOoL7LOkw1ybZl9uurMa+yN/BfeF61hGuTbKvjPLOauyL/B15HP7HOYdjmH1llHdW +Y1/k78gT8D/BORzD7CujvLMa+yJ/R07Af4JzOIbZV0Z5ZzX2Rf6OnIL/FOdwDLOvjPLOauyL/B0Z +sK5nTXFtkn1llHdWY1/k78gS+JdwDscw+8oo76zGvsjfkWXwL+McjmH2lVHeWY19kb8jQ/APcQ7H +MPvKKO+sxr7I35Hl8C/nHI5h9pVR3lmNfZG/IyvgX8E5HMPsK6O8sxr7In9HroX/Ws7hGGZfGeWd +1dgX+TuyCv5VnMMxzL4yyjursS/yd2QU/lHO4RhmXxnlndXYF/k76P9cz4pyniX2lRb6Kx/7ulSx +L7d/TN9D3GskwQoR/6L6pWRfOjt41Yu2qwh8Ee/3qu21Wv0821Sd+a+Sn15yHd6fPvHOS/5sKpkZ +ec1t6bd+5NzMPXi/8MyGTDPer3vMyTyI32fx/m9Ynt5/NOP939V7dnn80ouxXkvgLZduwvfcjPdW +fO+fUg2fzfg9+e7H8iLwotdfRNLiTnf/EX/INzb/sMy1PYT/nKfV9HzLc8TLa2x+KoxOH/e5Y3Na +btOLv5Nfq/H5quS3rKuS4yK9/kLxd6kLxbLaC8UzdePQt6wX61MSvwtgmQCWDVyV/JSkdWeOQ939 +sDqQO5dNjkNNHgMvBc+LoKHLrdxyLzWb+19wTgdOlwdeq7ah+3KPWzrWOmQAugDHGemVOAZeKVYF +qsXyAO6GAVuclCnxgtwgnpM14rCsE7+S54gxea7YL88Tj8jzofPEV1HvxM8/j99/VtaKv8WyfVjn +Hqx7t7TFJ2RSdMtXi4/J3xd/KWOik3W2+Ajrd/CzCvFRGcbvy8XH5Rosv1p8UoZEjzxT9Moy8Rm5 +Utwrg+KLslR8WZaIh+UZYrdcJh6HfobPT+Fnv8bvnpHLxXG5QkxhndLAarRhjQgGytGeSmgt2kaq +QjsDp800O7Rz5nyt/phYYpru55lM800w3gU7l99difpua37ud2XybuvKZA90l+WudyMWviXPejcm +b7FuSr6L5a7XhoWb8qzXlmy0Pqj0VrUtZy5Xic+DyZj1o6SwgusOiDet+7a4a90XxC/WfVz8wfoP +iLvWv0McXX+1+I59Ee4FF4gPbbhQXFlztXiw5h3izNoPiJtqPy4erP2COFH7bfG6up+J99YJa6Cu +2nqsrtGaLxv9/nusJq9NT2LnvitPm56se5d1sO4WlrveMax3V571jtX1WMfq7ma56z1fl9uH8633 +PO5zz9f9I/Toi/wO5fU7BK+noHFvvYNYb5Wcf72Ddavkk3Uhlrve97De+XnW+17dBfIxpfn29Wfr +Nsr31W2Xr6v7G/l87Tfkg7VPyLfX/lqeWRsIPFhzVuCqmlcHsI8Dn0s1Br5jXxg4tv7VgbvWnxWo +Xx8I/GLdr+Vd656Qb173Dbly3b3yR8nt8vPJt8oPJi+Q82Wj37exzvfadBMWDuVp003JkLwxuYql +n2fjec+zp3COHWK569H5+qg1P19/Y/IR6/Lkw1jvYeva5EPWdclhaBefO3Wz3HMa8Xlz8klrc/Ln +1m3JUnlrskR2JW350eR6+eXkm+VPku+R5es+Lq9ct1Pes+4xeWjdT+Xr1x+Tn16/PPDs+rWB79ob +0He6INC+oSbwlpq1ga/ULA+sqD0mb6j9qXyg9jF5tHanPLeuW95c9x756brL5Tfr1stH6mz547oS ++URdqRyr+7k1Vvek9au6XdCj+HwP1Gc9UXe79eO6O6xH6t5ifbPuCqu3rsa6ue5M67y6/xbHah8X +D9Q+JG6ovVcEaz8ivlJzu7ii5u2ifcPlAvtcfNe+XBxf/3bx6fW3i9ev/4h4at294p51D4mr1j0u +ytf9WuxPnml9JbnB+mjyCqsr+Rbr1uQdaPvt2AZ90D3YXo9a823j65LfwbZ9DNt4N7b1buuNyW9b +b8I2nutvGDPvTSb7jibvg4X9DaNkzn7kZjT6amv6bxiz9f22/eDszEM7/+USer/3kjdccndHMiPu +++XFrSvPzXx15y8v3vTXr8+sxvvWNzmZm/D7bXjfScvj/b4Z7/er98uuq7r0Bqx39TWXXbod33MN +3nvxvdfinXzejN/T+9ex/MzxnO+/m2B9+t7l6VIffzd55OLZ/m5yy4eTX3/3Tx6+6LXnnTeCz1/r +/cxDFw+/+yffDGq+9HdHa5bvOtU+6XxjqQ75CuhM8Tkpxfeh30CrAmdCr5ihJH53LpY5F8uei3WS +vG6+vp2pv9d8SJIs5CLlapN/r3HPtTMDufdcBgsKwD/AOag2+fcaPUOJlyEAlcC/hHNQbfLvNZSh +RGVY4WUogVbAfwXnoNrk32sowwqVoczLsAIqg38Z56Da5N9rKEOZyhDyMpRBIfiHOAfVJv9eQxlC +KkPYyxCCwvAPcw6qTf69hjKEVYZKL0MYqlRjwTDXJv9eQxkq/z97VwLXxNH2ZzcBshHNgUQUNAii +CQKJIt4YELVeFKRg1bZyKFbqhai11r4SEI+KCq0c6muVCh6l3ldbqwXvo33Vty1Wa98Conig5RTB +i++Z3U0MEZIUiD2+8vs9zM7OzjP/fWZmd+b5z05YDA5aDBIQByjfgcaB4+bkazAGBxaDVIvBAUQK +5UtpHDhuTr4GY5CyGJy0GKQgTlC+E40Dx83J12AMTiwGFy0GJxAXKN+FxoHj5uRrEth3AcYg02Jw +AZHR7zGMA8fNyddgDDIWg5sWgwzEDcp3o3HguDn5GozBjcWg0GJwA1FA+QoaB46bk6/BGBQshh5a +DAqQnlB+TxoHjpuPr3m+zubvwNdoxnLxLECNHxnj8pRtJDxl55GTaye02K0TuunRCf2gPA+ykagv +MSSkceAaDlzL8ZTFkDhvS3I72ED0N9tQHwbG0oPfr3SCMfx6H54Pz1J3nRNaXaRjU2Ys7Id8YS40 +Ag1GQfAfod4+PPVaxMx/4K+OYIql1136oVloBoqG/zNRJMhc5IjUuVmDkChuEJKcYMNCOsz1s1Lh +UO0lYUMXOvTp1osJXx1Mh8hnDBOemcCEjcyxxOj5+Fd3Tidhj/G5IJBo4nl/0v1LJjT3zIX7CId2 +HQVj/Rj437S/5Iu/0qElG8dzS9xvLk2cRp/is+cHXvm2U60VE6fYc/h4T5so+rpe3zN6Ml4fR8dH +X2HimmsDf5hKn7/Cnu/Lhhr9mj9NuY5TmdDHm9F3J9NiIC4foVY6V7dCIba4z3HpOQ5OWdze7or7 +x5p0pa9G/7ejbw3A+ced8aAtiPvp5Hfp6Um9/oTnbMtBPoeG2wbCQpLxyeJM2dlXfENC8nxxWkcW ++2g9/Jp5SBhquJ1Zsram2FAjFmzIZfVoMMEcX437jBoxfjH9Z5gp/Qf3BbhM49fQ9gUsPZCPV7jB +Fmtam9W3IZYC1obFDdiwgLWhxpffkA0DEfNeaMgeBYjhiPXtYSpXfC3Xxg/zShtjLf0wr3Qw7uFg +zCsdgBDzSvsgxLzSvyEd80Y/w/WGQgnZ2Q/zSj/ndvfDvBIOMa9Ex6EcO0jXlNsSXDH+LU78fP9c +J/4PV/zcVqZxxd5UkGwqz8f1qNVmt6NWXMVRqwrlVF6F0puqLyI+pPHhGj5cyw+Sifg4r8a/ocsV +43rAXPEOZH6u2Fxt4K/KFf+J+rSZueKFIDuhnWFJhTaQCmPldYjH2QQP4Ez0lMxCj8ktqIbcisrI +7aiE/AzdJLPRL+Tn6DC5A+RztAfi2XB+C6R/Sm5DG+Ha9ZBnLeRNJTNREpmBVpOb0EpyI1pOrkfL +aFmLltLyEZxbhVaQiZC+Aq0iP4Trl6NkchlKIZegdDIBrSMXow1kPNpOxqJd5CJ0iPwXOkV+gC6B +5MPxLTh3H9IqSDWqJuNQHeSx4iyHe/gQ8Tkr4H5WgyTDvWFJgftc2CJcsabP7NCJ4zHi340rbspz +szGueCBVnyu2pYxxWLbUGFlbEBtKlyu+xzPGFd/jRchKaNHlik8bybdIdooXy4phrngz7weZmseX +h/FGyX15a+RdedflrXl9XB9arXG9YVXp+qPVie7fwLvgoFW8+1GrMR4/Wu3zuGHVWvHQKkLRmrdP +0ZVXq/Dl9VeG8mYr1bwM5ae808pTPEPYcPoZWk5r7wlzxSVG7qlIWcK7obxHiy53a2PE9lXKtlSV +0pYWXa54oJF8j+A990g5CERF6ZY3xWh5U6Cst0GmavNhrni1kXw3lKupImUSLbpc8QEj+c4oD1Kn +WTHMFV+hYpSPqf5Ke/4jRT/+PkUIf5JiBr+1YjF/n0c6P8hjGx/qmJ/pdop/ovtRfpXrNv4a13R+ +X9fF/OvyGfw18hD+aHk/fiu5A/8H2WNqi+wqFSs7SBnChtMX0XJAe0+YK04yck8RsiQqXLaaFt1+ +NtVoP3sb+tgUWnS5YhVljCvuT/nLekO+3tRYmRc1QdYXZCBlmCueRIXJwqlo2TJqlmwplSj7nFoh +y6Z2yb6jrsl+o8Ty1vwxclf+WvkwfrF8In+A61x+muty/gPXtfyT3XfC2OkgP859Fz/AYy1/t8dy +PqWYy5+omMjfqRjGr1S48nsq2/CjlL9Racr/UF8rs6nDys+py8qlVJ5yGXVTGQ4yiSpRDgRRwbEd +SAdIq+RdVlbxDisv8r5WXuKlK3fxopSreJ7KmbwqRSBvp8KLN1HhwOMrLHm7PSqtAj2KrOLc/2MF +dW51svt/rKpdi6zSXCutBrha8m7JHXhr5V68IHkgTyyfwftFtoq3W7aTt0J2iZcou8ibJaviRcsq +eWGyDmAHO7CXijJk4wkyP7DtMLDxULD1UGqkzJcaBTY2xBXrvpvMzRWb6z3YNK648e/dTOWKx+aJ +/HDYPoXrh7niz7MrB2OuOBtCzBVvhxBzxRJIx1xwCL7eQPiRROqHueKQPLkfzRVDiLliHOJy1kC6 +ptxmccWgj+dDmZUrxmMS83PFH4MsQZnk++gcSClIG84SkI/1JAPSsuGabLg2G/Jk0HmNje0a4oo1 +vhJ9rpiLGvY36+Nm/M3vgSwAXFjeo+PG/M1Yvz6GOj2uuDEMmr6Gfd44ZDAsAFkI5S+kceB4UzDo +c8WmYFikxbAQZBGUv4jGgeNNwaDPFRvCsIjFEKfFsAgkDsqPo3HgeFMw6HPFhjDEsRgStBjiQBKg +/AQaB443BYM+V2wIQwKLYZkWQwLIMih/GY0Dx5uCQZ8rNoRhGYshUYthGUgilJ9I48DxpmDQ54oN +YUhkMazWYkgEWc3OBRPpeFMw6HPFhjCsZjGkaDGsBkmB8lNoHDjeFAz6XLEhDCkshnQthhSQdCg/ +ncaB403BoM8VG8KQzmJYr8WQDrIeyl9P48DxpmDQ54oNYVjPYvhEi2E9yCdQ/ic0DhxvCgZ9rtgQ +hk9YDBlaDJ+AZNDvMYwDx5uCQZ8rNoQhg8WQpcWQAZIF5WfROHC8KRj0uWJDGLJYDNu0GLJAtkH5 +22gcON4UDPpcsSEM21gMn2kxbAPJhvKzaRw4bgqGvzJXzEVN44p171ufK8b3rPEjY1yesk6Up2wk +z8l1o9Vit41WNz02Wv2gHMn7QdmJqi8VFKTx4Ro+XMv3lFVQOG9LcMUYL66P70Bykalcce3gvzLv +2xgPuRQxPORa9CIPmYr+KjwkUzdwWYM8ZE8z85DYhsUN2LCAtaExHrJnI/YoQM3jIWcWLGE5i0U0 +Z3Etd47feJprmOOHOYurEDKcxSKak5gB1xsKW8WtpDmLGQUf0ZwFDjFnQcehHGtI15TbEjwkfovh +Zwfef0wT/zvykE359sp0HvKoMEh2W+Dj6i3Y7OYt4Cq8BRXK2yBHhfXlIxGkieEaMVwrDpJ9JMJ5 +NXNnXR4S1wPmIc29d5I528BflYf8E/VpM/OQlSAuhJCDpTUhAGnDERE8TjsCceyJp/h3rsmORA3Z +iSgjHYkSsjNxk3QifiGdicNkFxBnYg/Es+H8Fkj/lJQSG+Ha9ZBnLeRNJe2JJNKOWE22I1aSEmI5 +KSaW0SIkltJCwTkLYgXJgXSSWEUScD0iksk6lEI+RenkE7SOfIw2kI/QdvIh2kVWo0PkA3SKrEKX +QPLh+Bacuw9pFWQNqiZrUR3kseIguAeC4HNIuB9LEB7cGxZruM/KFuEhNX0G909NHI9Z/uEhDXyz +KtT7ZlVo9JtV4RhZCsgaYb1vVo3kC5dNF0bIptFS75tVI/kWyYYKY1kx8s2q8AcZEvLlBYJR8mOC +NfKtguvyVYI+rvMFa1wnCypdgwUnug+Cd4GXIN7dWzDGI1iwz2OyoLViviBCsUqwT7FVUKs4Juiv +zBfMViJhhrKz8LRyqNAQNpx+hpZh2nuiv1k1ck9FymnCG8rptNT7ZtVIviplirBKmUpLvW9WjeR7 +BO+5R8pvQHLqlVdstLxiKOsWyG1tPvqbVZHRb1ZFRUoBLfW+WTWS74zSS3SaFSPfrIpilAtF/ZX/ +Fj1SfCXap8gTTVLcF7VWcMT7PNqJgzy6iaGOxZluQ8UnunuLq1y7ide4thP3deWIr8vvi9bI80Sj +5V+JWsk3iH6QLRRtkY0Xxcq8RIaw4fRFtPTS3hP9zaqRe4qQCUThsja01Ptm1Yjtx8huQR8rpqXe +N6tCo9+sCv1lhyDfIeFY2UHhBNmXIEeERr5ZFYbJCoXRMivRLJmlKFHWXbRC5iraJRstuiabKRLL +V4nGyLNFa+WnRcXyX0UDXKtEaa488QNXO/HJ7u4wdvISx7l7iAM87MS7PXhiSlElmqj4VbRTcVpU +qcgW9VSuFkUpZ4rSlP6ir5WuosPK7qLLSktRntJKdFNZKLypLBKWKI+A5MDxWpD1wjzlHOFl5Vzh +YWWA8GtloDBd6SGMUrYWeip/E1QpLgl2Kg4KJio2CPiKpYLdHnMEgR6TBHHu/gKoc8HJ7v6CatdJ +gjTXOYIBrksFt+QbBGvlBwVB8ksCsfy+4BdZa+FumbtwhSxQmCgLEM6SzYV7nwM2WA+yFuyVIzRk +4wmyE2Db02DjU2DrU8KRsmPCUWBjQzyk7rvJ3Dykud6DfxQPueNuAs0Ldkj5gOYhX8+b7Yd5yLEQ +vknzibP9MA/ZDtLpb1Tx9QbCEYmJNA+J9+LEPCQOMQ+JQ1zOSEjXlNscHpLZ65NvVh4S+ynMz0Py +iQQY+2WSFegcSClIG85TED5RX+yIUhh7ngPJBEmAcSbOa2xsZz4esgykHHBiKaPj5uYhcV/D/lQc +MhjKQSqh/EoaR7wJe4w2l4fUYKjWYqgEqYbyq2kc8SbsMdpcHrKaxVCrxVANUgvl19I44k3YY7S5 +PGQti+GJFkMtyBMo/wmNI96EPUaby0M+YTHUaTE8AamD8utoHPEm7DHaXB6yjsWg2WMUlx8P87s2 +HCx1dNzcPCQum/6OWouBQ8STluxcEB8b32O0uTykJYvBWovBEsq1hvKtaRzxJuwx2lwe0prFINBi +sIZyBVC+gMYRb8Ieo83lIQUsBrEWgwDKFUP5YhpHvAl7jDaXhxSzGGy1GMRQri2Ub0vjiDdhj9Hm +8pC2LAY7LQZbKNeOfo9hHPEm7DHaXB7SjsXgoMVgB+U6QPkONI54E/YYbS4P6cBikGoxOEC5Uihf +SuOIN2GP0ebykFIWQ2ctBimU6wTlO9E44k3YY/QfHvJFHrIMPfcjY1yeso1CT9l5gZNrJ8Fit06C +mx6dBD8oz4NsFNaXGBGkieEaMVwr9pTFiHDeP4qHDMmb6/d35CHxqA7zkELiRR6yNfHX4CE1dQOX +NchDepqZh8Q2LG7AhgWsDY3xkJ6N2EOX5zDVHg8kz/zwXkXYHpjDPKtnDyFquM5mo3lwjMURoSPf +eSOniEGIch7EhKFMWL2BDtVXD7PhFeb80WomvoRS4dBnvh0TPutMh+rpMiY+sQ8dosBhTDg/mA5z +p05iwqTZzPkWbOc7QCxJ5nmG95AeierXEU57hbVLY3U03QSbmdLmyQbOa56tLdEXdOseLmvk22D0 +bL5BKze9L4xn7RzagJ3Hs3Y21hd6NGKPJveFPLnfP32hfl/Yg5i+cBy92Bdy0N+oL7B1D5c1sj7F +vH0B2zm0ATuPZ+3c1PUpTe0L2B/4T1+o3xdkBNMX+hMv9oU+xN+nL2jqHi5rZIxk3r6A7RzagJ3H +s3Zu6hgpBTFj2d9jj7vuXtpng8Y3rmsPBvssqFU0fRTTkmfFseGmQS1vozDE9Mc9IFfRi88LnJbD +Xt+YjYKfY9a2oYbspUa/v/3o2gsua7D9uCN10FEz2CaYtQm2zS8N2OYqMu1Z6t6IPW6AXGmGPbIQ +s/5Q1x54z5rp0PvfBZle73mAjl5gbPTtNSZsX8M8IW/ymCeidTsmXNORefIRXZh4ZU/mSXnTlwmt +A5m++myiGfpsMmtrbPM2xIs2/4W1eSsDNp/cuA3o552Fjmh8GfrPwIbqC2NLaKC+mr/2C3E09Y3P +09f44P0WU9BO/JQhCELft6LxJzS2Jgjvk7MGZCdinlOaeAab5++0Jqil9rEvA+mrszfBJT4TH4ca +58Pz+H2pK/yBID4gQ6k8/nDqEkhvHZ3Djei4B1fcRkNBfEAGUveg1DKQxjh1TV3uQsxaY3Ny6uZs +R7o+NNy/xrHlNvQbPW6IaQMaHhn3jx3QI78AVDtQOn0M/STu9645xmMxyAl6mG+UNHHW1/q36ieG ++oihvY/0/da4LvTthu2dy0dUdCsfKpcfTeFjXX+pOduoOetQt40aWuthaM3wBhaP/prhlvS9N/YO +aY0+Rp+D4L6h/w7pjF5cJ/FH+OxfZ8tsxdpJ86zBZV1CjtQevmOLfgM0mjGNdu1NQ8+WbLQaHQS7 +ZaM0+lj/2YJtrtFrqt00HBGhZx/d/mCs3+F+ir8pPQiSxmLJ4BfwAls5Uhn8MAofG+t3ptptGFum +I2srvg/Kxe0d2wD3AaxTjhJh3N8aNdcW+utMGrp/JV0e8y3AOsSMfXFbSyb38JJJNZVMYcHHe3jN +aS/6fdbQeHzdr0G+eI8HPB6firTriLTjcYzPF0aheBw6Aqmz1gxCy3neKOkAE35yjAk/v0CH6mXX +mHiLjqmns3bD3yz1Ri9+s4TTUlmsjY2px9W7D2aMrP88aon5nq494bIG53sKhP53xwx2wtKPtdOg +BuzUD5n2bZeiEXsMh4wDf6fvTNce2O/2RM8etiABUCcxMLth5jlzwOZz4MwUmJHHQDiZnglFoUl4 +/nO5jrabT65YRbc3j/Z06NPemQl9etAh6ulDh7nOAUw48i0mfGcqE5bPZkLRv5jrc+Po0LHdUiZM +X8XoX/kxHWacSGXiAz9hrg/MYuJHspk42sXEOQdULV+v34F4s/W6voF69Wbr1dZAvb73++xM7yGM ++wV+PmvmlJr5JVdHSBanvj9Od37amD8uGb3c+Wg7IgU9IBwNzkf156aa57ymHNBf715I9HwcJyGY +PeWrCaY+NPFCkPnojx+La95VL3POqjsmaGjOOkMEzyydOas/GKyWMDzfHCN9SIyV1hLjpE+JCVKC +HCe1JIOlPO3vSs0VGZ+zRotGUtNEw6ko0RDqbZEPhAOoGaJ+jc5Z9eu2oXEJiVpmPmDOdtTcOWsl +0Qk9IxwAWxf62NCcVd82mvurJJh5xzOCsZUmrmnff6d+Yuqc1ZSxs77dsL2V0gKiQJpLhEl9SHxs +bOzcUm3UnHX4V5+z2hIfgz2kf4k5qw3B7Dv+gHg+Zw0UOVJKaVmL/MbB75mzlsNz5QnRAVURTvSx +oTmrXjs2Ok/T1J/+nNUUX1E5JHSA8AnB/F4LxiKEPpcrzSB8pI4kPm7JOSt+3gzXsVUv9Py3HixY +WwURC9ApYhDKIXoif2ImHcfjmjgOh6NrM9xvmmsz/bmtMZspQfwJhoM/BSEgo9uno22yVaBDAafM +3pFzqW2BJY63pN3ws3sqenHOmxuePdjFosdxkMH4+EHaCj9jczSY63ZRtMBYXmN7PEYOQvV+x0KN +dP6S2ZdOS/2ORdi7HWga6OqZhYPw7zxgLLgt+Ox/hz6vecdpfseC0DmHw2g2P9npOP07EZo4i10b +d7zbng6D7PmHsH4c4pI058O/uO3N/E7Fc90N5deEB57co9viDPvO9rrnNdfL9PIH2TNxzfUPfrnn +3djvWATr2EczFtSdR42ByLvI+Px4BHuuKf6CprRFBfIJ6G2GeaUxe7wGkXkm2KMxfwFu65ON+AsM +2UNJMN9B6tqjPTI2j53UwKqS3FG+jP06BzN+gqQJTPz9yUz4aQwdhp1TM+mDlzP+gZAkJryZwpz/ +bB0T9tvEXB+9mZn3n9nKhK8wfoCCgfsZvXMPMefnH2HChyeY9NcuMnq/u8yEo39l/AynbjL+h6v3 +mPxBVUw+29o/oP5fh8gcE+o/ADFz/pfVH3qgjNFP/gB74N8c9jbBHj3Yc38Gf0cViDH+XXe+pu/X +1vd3aO5dM0/FXGoVO3bUxPPRPxx9Y/6OaFF9jt5HapyjHyrtSw2XDgTxARlKDZUOp3ykzzn6aBP8 +HdNFw6mpoqEgPiADqemivlS0qHGOXlOXDwz4O/TmLs3yd5irHTXf35EMc9wUCNPp46Zw9HhuDDnp +uXK6TpwdM/+/7yeG/B3J6LndGH8HogqgHyil0RQ+flkcvTnr8O/g76gg/hocvQ1bb5pnjcbf4Sh9 ++Rx9ObEaPQG7lRNp9PGfhaPH/g7M0WN/h4ajF0oLeLlgI6E0jMLHL5OjH04koriXyNFjnhNz9OeI +5xy9uu0enrqtmkp2wIKPm8/RN+aveGENukn+Ch8hz7z+inp/Le6vmFffX4GfGbj+MyKmmeavmKfn +r5in56+Y94K/4gvWX/GFSf6Keb/TXzFPz18xr2n+CtwWMe87gniR9x1OmMbnN8df0ZS2qIBhr8BM +87NRrD0CG7DHKBPt0Rx/hSF7tJy/Qt2nA+MXsHZn7LjQk4lHejPhylGMX2AR48fwiY5iwvT5jB/h +DXY9g5Rdz7A7kUkvXs34LbzXMOHR9Yy+fYw/o+AO488Iu7+dCWfsYdLPfc3oHcz4LzKOn2fKH/A9 +gy/gKpP/TAGTL/GmGer/BrYlW/8nG6j/ALb+2xuof3WT6qN56yE017+Mb7pN6Z89kfr4bTP1z5Ns +/ZxvoH5Omtg/ezZij2T0R6wXsSLMuV4Ez3Mxz4/XRmnihYjxQ//R80LNu68l5oVN4cEb8p/gtR26 +60WCpQhFCQ37PsZJpwknSKOF46RzhWOl7wnHSN8X+kOo0THDBP9JlGgY9bbIl4oSeVPTRP2oaFEf +aq6R9SK6dWvu9SLmakfN9Z9UExziGWFNVBJ8Ah83db0I3oMKz73xHkyaOL6O38z7+7P1k6asq2ps +HqdvN2zvMKlSWCDNFSqlGUJ8/DLXi5irDv8O/pNKwoL4K/lPsF8Y3ow660UCW3R/IVP8J1XwXn4C +z5Vygkfg4z/TehFAQ/tP+Gy/85EKhbnQ54RStRAfv+z1Iv7EIZRDlKNTRDEKIrLp+J9tvQh+xuH1 +IuWIGRtfahvWqsxeKAh0CGvjaJvBx/GXuF6E5iX/NOtFalijsn/m9r9o1ovkfmniepHf73/5kvW/ +fPln9r+8CgBsa+vqtkCjuULWn8/gtP3opawX+V1t0ZzrRbDfRQL2GA8Pi6t69sBpB0ywRwusF2nQ +Hv+sFzH/ehG857cd1P8A6E3X9Oofpx0yof4160U0OidABnvQ+QaEv+rpxGlfmaDTETHPdY3ONyGD +A+gkoZ3m6+nEaYdN0Bmjd+9hkKET6KzhMvv+6erEaUdM0BmJmHebRidu751BZx3ovKGnE6eZsj/H +bD2dUZDBGXRyLZi99XR14rRjJuicp6dzJmToCjot4eQdPZ047WQT7n0OZJCBTh+49xI9nTjttAk6 +8fokro7O+ZDBFXQWQcXd19OJ086aoBM/k3Xr/X3I4AY6X4GCSvV04rTzTcC5CDJ4gM5YPN7R04nT +vjNBpyOq3+bVkEEBOi2goAo9nTjtPybonKJ374shgxJ0hkHFVerpxGkXTNA5C9Wv9+WQwRN0egDO +aj2dOO2/JujU75uJkMELdK6EsEZPJ077oQn2XAUZeoPOHYCzVk8nTvvRBJ2TUP16/wgy9AWdM8Eg +j/V04rTLTbBnGmToDzojYdz0VE8nTrtigs4ZiJlnanRuhAyDQGcMFERw6uvEab+YoDNaD2cmZPAF +nWo4ydXTidPym3Dv2yHDENw34aSVnk6cdt0EnTP1dO6EDMNA51A4SenpxGk3TNCp/6zbCxlGgM5A +OGmtpxOn3WoCzkOQYRTodIIG1kZPJ067Y4JO/WfdV5DBH3TiUKinE58rMUGnI3vuZY13Pc24HvQI +ZHgV7OEPhhfp2QOn3TPBHo3tv4b9UsIG7KHxmWnw4nroRzyPW7BxOT1jbuhujfvM9P1kulhIHSx4 +v5y27LFQ5/jP8seB0WQEegfmWDPpEf3v/RMjksDPfDwexbYwrczne2yNhRn3HJBZaAjIJBg3zWBn +Eab9OUD5dTp/puTBv1igmKZfvsYCMYAgHMqPomc0xv6c4I2Hnyl4ym1q+VzEtBf8Z4Feo+8ZlxmD +FvxuFDL2/jXrPUwp3x3a/o5A5hj72UiC8SVzQSwI5rcisG8ScyEUwfjgWhGM3xnvKY33ZcO/oyAE +ERHM7xlgvybeGxT/rgDmT9oRzP7+7UE6gNgTzF77HUE6Ecye944Es+e9E8Hs0Y7nni4gXUG6Ecy+ +jXI8HgXpjseQIM/gHj3wOI1gftOrB0hPPB4C6YXHMHjMQTD7OvYlmH6O930cQDB7L3jj9zKICsQH +v09BBoP44fcgyFD87gJ5hWDWjuB1IyMJZt3EaILxs2G/BebK8Xwdf/MSBIK/9cDztxAWo0b+7H9j +o+ZEzXKUBfvLHYfEhM+Pmvl2az7TMloxfSSBoJ9ZNQfPW/wcxdxfEJ1+0pr2M60X0S7S0RsEHrpx +H7atleV1Q7pxjU0uTfyNfgTcX3m3nON0pwJOLUZ11RV1lY/LrIiacvSh+m65hKuWwNuwPVLXodzc +XFRcXFxHM8L79+8vRevXr+8M78QpdVOmIH9/f+RZ5+mJOnTogG7X/fYQfXGuCI2oXJBbW3G3fM+p +IhR7v7wG/6KSFynAPmk7E/qJKdd4ie9U2BY+itoW0qGkrK6ijCopc7hdmfGoykpQUlZdXvxg5K0q +bv+SsiElZQQK9iJuV6orykcI3hBUlBc87kEUP5h691aVsKRsXEkZiYKX2JWU+eQ/SSFSIIXYtQ6e +rBwPrkVJmWV2SZlVSRmvA5dfUtYKItYlZa1LytqUlAkg/5qiGsVZMoQgaonjxC14HkcQKO9ZRzHh +LCGdJZyIdLD73fIludy75et6C22UIW+Jl6i+4WRa9jxDkKNtUiDHm5KutmcHC3u0ixXD+a423IgQ +0qWtRarEMlVilSpZx/tKFCumOmdL7pYv5Z4ERZwTyEY0y9LfxrpLr8k2jvLAgsdlqg7SFAfHAe32 +t+1ie1wcK7YsAXVDrRLdeIlud+06ivlxXeWtnLtutrHOc2vtBaF7m5K2Zaoh4mVnuWW2BTZCZ4VC +5W/Tt1eBytE6RFlRVurbSnCYv6htnpstkedG5rlx8rrNVix2cQzsndJlfO+fbdNsqVTJeRm/xD7j +X7wCoc001TFOx7YozTalfZrtdHma7XHpj/ZPVcHnnTqKW6VKrFMlrVMlIW1SJYJUiTBVIkqVpIpT +JTa+tlmX4J48e1k6X699OoukuINe83H+YHD/aphbhqNnoUEhRGQIGRnCiQzhRobYWkSGWEaGWEUG +ZgYcfLXvzwH3hpQQpDBstwguFEWGiCNDbCJD2kaGhNhGhkgiQ9pFhthlhRxtnyrpEEFxr79mn3In +KFbsYDEg4UcoVWTtFeBvizKvcF0sbISDxgzPipo9oCgI3QkqCiLOvu1Cnn2bcyfobvm2X7izp24f +NCb3nbvlX3TgPRTaOPZwzLRUxGZa8ni+sRNjyWHdHF0ki6M3e37WLrNjrHgeN+0Di88cLCN46cO2 +/xS9xTntAyrtA37aB62yPmt3t/zoDSh877tRZVLVcpvlNt4BcwZ2bLf9Y8+StkVe5HsrOYlu3PhE +N4tEtxWKbPsdAOCbtrchxxhhW3JG5PgkR/uEw8LZbxHxIT4pH425W56D7nHdSbfQdzeNtg0NnR26 +OBSlhm4LPRhwJuBcaEnA0wDB+OyO4rapEttUicTXNvdkGSh7FLn2BJHJzVWNTNp5KWPXlX270rbP +eUtM3Ly89b1NNbuXblq7KXvTkU3owqaCTRWbuJ92sRXudsqKFcuFp7ZuthEdOCL+FMItNj8eabsf +DmyrjkjWHzjS7sARO8eftmy2ab+91xmfyWm29qkSh1RJx/Opkk6+tmeruPaqSyqUfvGz0yPfGNL5 +BGonTI08QaD4/tcOpntdO7gN5AsQn21LrJX7Vlgrk4Z0sbVyFmVL9vEcDlIOB/kOB1vdfeOg0Fki +cpaII6z4fjYf1/B6RW76Iv9c/rX8wPv5dfmi0uu1e84SrX8igr4liMtk/+tE72JO72KC+xNxhhhe +RDhfI/oXxRSvmH2LE1PMvUZEFKVGcj4l8uHsruKdtzi7irkO1ZEWnRGKyuWeIB6VZ2zgqqkci+VT +blWlFNUEu239SW0l7JZge6p4l4WXX47SWqC2XD6lY1vuGwFoRkCsnyL/o6Gpzn7VQfmT87M77bd2 +ONja4WAbh4OCDIc9u/ZHVh898LWbZ6qk18hUiVdc195vVne16TcsfPCj8HdCb94vrSuNepw6vWZv +360/9VXdtOzsWUNWcic/jKwmvIPfeFi820s6X+Xzoerfqp19H1runtvL2qKXFVf1a0EI707Q1Bzx +6AUo3z0h+YtH4dP6FKcItgreebQ8YXrNvCgiaPi6yNuFGwuHkEcjghd3rplkGZ2msIy+VJi83T3q +8cbCvg+mvpX6OEym7txtOjGt1s23u3yhyxGXNfHTazyeJlzKXTDfOd05lZQFXO6991H4kiVT3b0W ++1WJlhw/Gf3uxkIyYnlmBZxe+q3slacJe2fsnXRTEMiLenzu2L8HLpP0VV5PPf29akP34d1GwjNm +ldoyupTwHSgdKn1dGiVdaP/E4YsK8gPSol3ICuJ2qatioOIk33FKjzzBLUGtoE/1M1vptFD0r9C2 +6/9bsS5ytcVixYnQbQrhV4qHo392uVTY5ZdX3kzt+wBdKiwZv9Clo0qh8lW9popUofkq2lCqHFW3 +0CLVAxXaEmTnsi80MHZszOh5UxeUhqLv3TcWvurz69DLNw9MU8eOnpUxcVb0rPjpPN+4BxuuT+rZ +x+HMbA/1DpV7kmqpMq5yaMB7sVEBJFItXTqEn7SSTFrJSVq2OeS98lgF8o/9PvRG6BnF1a7Bhb2I +/CE+132n1QYXTr3Z2vu9cosLsRkFsRWx3KRVMwon96yV1X2KnE8dfufDhGx7Irma5ES0uzircFbl +LIvYdrHPVKjTFtekgerRu7useT0pJmlhUglJ9rLmfObAjSD5i05kedb0VmR2WTf+tcUouvfWY6di +q1xIIpwQOBE5c9de+LRDYNbNhJyzC1xIEZq0561DLiQ56cCN0sKAtpUBHZ7FvNmNm7/GuyrEmcgd +VfdW3ay6uMeCzZernE7xBU7n5q7t+/Piq1+NPe4Y3sUxfOr11gdOZl1vc+zk5evC+Z2DC8ceu7qS +eycpPHZOLMo484PbB0TxR98fKO484twHBPqACN00Oz24MLnwu0IXYuEnvoqVmzZu2pOy/eGqGcNX +ZdW9an/7QNyr01SnL+xFRPGmZUvv5z2ztU594FL0/uK9YZxhXVK7Jgn9S0cmHd8d3OWO7bWEpLSk +S9uTDiclZoU4f6TItKMHGO3xCCOW6DBiQS6yH7a7Ztzud7ZIr6OI2yji4m8o4qH7DemNL1B+xDkU +UTSilhMROwJFLEARuSNqBdyIOqSu9LjV8atPJgtHnBuY8+qR1ztnzF2btzfzieupf30zbm17RRb5 +xrrx39UePXJqw9My/IAeb3OvzlmAErLf63ijbuCKGMdwklSPUt58Zy+ZmNd1meA7wYcLjuf8mFOc +M7jmm2T+uQff259468YR+UXhgAtrikMuvrm/lY1aWtqjwC7mxEgJldkv4suyb4dmhwgfCihpXcBY +p4XtQjspBoT6+L/OV08I/bB0oWLEuWm93uNdlZZIR55dPvJszrHMx1vHoJxjVy+WXHx6UZDfOd8z +f2g+ej0/Kn9h/qr8Tfl780/k5+WjW/m1+a1KO5YqSn1L3yyNLFXPL31H8e/SnaU5pf8tLSpFlahP +KbJz7AjjMinqBAMzq7r2dRE7//d21n6kvrE3bHPwmz+Wy/eGUcPuoukLAn/yQ7MWBM7iHX//2jd7 +wyLaIvX/9obF/VxKXN0bltlpX4+IPbdQxKk3qtPHJquLkoN8S4jwPZODx/5Y/vGxsEL+u6E/fbQi +dMO4+yhCUI4ianaFhtfNqVtSlxFr65G1te7LunPvdug1uyMqdO43+9MpK2J+qssVPK5rLUDfz+oh +8BNcgL6wQJAo+ESAdgt8Y4fF3hRsVXypsJe6S5FKOkY6STpPukzqE/C59KgUXZQWxlbGWijaKeSK +AQp/BQpV7AuYvClVcSTpQtK3iv8phP859d2x/XF1bqRA1VnlqUL7tgeqIlRzVUtVa1XZqiOq5Auq +AlWFqqt65371I7Uy9rDNvZwL/tPS/SY7BHRVvzVxwn+PZfS4MuaBYNGYmYo9kTBjDpm7OOh1zz1R +bwZO7bNnfXlwZlX4GsfyLQfemsg7NWFZdF5wG7ecRRPTX+89e8SavIwDb42/O/h8yLBD4SGeV8YI +P8wJjAic22fPlsglgTNHHDq0nzj6zRvjHsEVVr4ZXlfGiG7lhAT12zPv/6g797Amrr3fr5nJlduQ +QFCKmiAK3uqEm6itHYKCKEJEwbuGi4hUZBQEi1oHFBRFO6KobW0bb1ir245XWqp2vGt3cUdrRW2l +A6IYwZqES+QizFnB7vPu/T7vec77vOevw8PMyvplZTJk1nw/v+9a6yHEycXVK7IWzHgo/+kCF1eI +ip5x2WB35OwrM6tXrAk+aUvozR21pUy8GUYjYDV9/pVj1/UHUEmSi/kH0xdXj10P2VImS1IOazRV +3zx4O3VLmfzviVPij++f8DBe5cYnFE9P/pY4mQNWpEzPDj6ZhYaezE82nVvw/NmoiqrkrgKgnaGd +McLwvmGUcPbAlGXrisZ28IHURCq8Fz9z5f7+kr0P5uksz+6l67MspZMGgOt39+qnGs/rb+t5vU1/ +t+FE2JmreETK9NS1O9LHFk9PuL7egABs74Ndncl7v6TXUNfesIXYwEe7TyAP9yBIiR0tsWPHSuyi +Eru4xC4pscucSuzypJXOXMs56iv6EQWaqR4Kp33pYHosPpvuEcBaejstCPn4VbrIuMfYSQNnZiBD +MDpmJpPGrGa2MJJ9jMMtOJy/5H97B0ctsaDZGtJsff95G9rdJmDShtBLcVM1Qnjj2E8axro53w1v +6Ohtb7I5N1ujO+lIUaYrtOPN1t26d1FtMMgLkzTZEPC47e1RB/4Pj1ziOPKN/zjyimBw559Hdni/ +Df/J84jBf/8nHG7mympJgevbNSMz/g9txn5XLfnt39qopsGTr+0VPRa88GFu/177fz2rSLg9hO/4 +udvbEbr/6qwcbYZ9Xy05+m9tEqP6bB4GHZ25dUlXG8BsLW64IDTZpqtd67odjm8pdHzN1ovD+hwf +NHTu0/k3c5G5DkcHTV6E7NzOho6iLWiiztwqWx5+3PUSeVHMTTG3HrKSJsk6eUOHw0p9Qzbi9V1I +u03zouW7VcUwVdZXYO7u4Y3t1dHqeV4i0Tz1ajVydeeLlltb4JPRFZjM885K8m/qH70QHyuJjPFM +lPp64Wbl4QjZogps/JPOXg20NT1aibSfvQQRBW5BfJI8IIco8BEYBbIF4NCSVSCZA1kgHWRO0Yx6 +D6ToNTkgI0aj0iibbGnFohJkwCJpvevKoSOedG5s6HCqwMQHPc2tBzK2iHKJzQQ0rg0dc9aJA9bB +vBRoE4cHnbsuG+R7R8Rpa3ulYJ0kTPWlMrfnRYum1QqABZr+T4Xp8JG0zirC4WXFx+MA4F2CsK1E +xAiCde02MBstFcRWA16wEt+I78a/xqvwn/H72DYpeCL0QBs8CWRDfyakAw0896UgN63TmtNjTQYr +hJbZFMjqtlKdHYtbW75eDTIUoMO2or0lrRuMbrY6li3kJmDAE1k+HKwRrIs7rXtTQL5akv7aOkKc +nKAAc5AcwZopWFO7raoMHNWI0SxfsAYZj/y1Imd5SkOHmTO31pFtbb2YqI7rSyooHzM3QIXWkQOw +ZWbSE6vrbgOXQpxdrd1tVm53dF/fkTS3wROH9xrsJeIXLS02havdVqR2ffoa9o9gsQ5psi398Hmb +rNk69gPU0YEOaPg3C5GFyDg52mzFElCZ3SZ93ravoaN9O7rP0Y06SA6HDvqHm2LzTXEdGT9GbpqP +eW1HRLk7QFKmiKojxb0ipTi3HPzdzTuiDEHliA5BRCI5IkakMrQcifbYAbc5OrhrskmBVomN8RKL +w72QqV6lyjnKUmWKclipMlMZixZNlQ2swkJdu4HLdgQtmAZATHhvk+3QdtgJa6qwKT0tVkHRKxoS +6rxlOyIgSaTjS2pWCCC/b21VelcCWLUMDIOf/vBvl4EgoI3QjFsGYn8YrUkdpaFAjr9myeGQwUN9 +s4dpkn2TQzQZr/w1WWD0Mnh9dcdB5jqNZtSwnILBaccSNGmjNHk7NYtXacBq0EtsR5OUs0Zr6Bxg +ADNByoeantHjNUvBcuALaD8QAkJBWBKQhxz0bbLtL4PnuhS5NWCI6Z1m62EU1t5tJJz99slFzgFW +cpzcJYj7RAIF1V9FTpOEObk3PW/bFCGbUIX1N78S/kr8EPJt4ufiN37MvPFj+kY/YkXFB2Q9flVY +UF+GScAUc8AEFN6yO5BEdFlY5gcy7Qdo4lIELEWedNptwfJtuLl1oG9je0I5ckuBHEa3TpUlVDmd +Ams04yWFgShwUqZMlYLeryMQjfJ0FCZDt8oRp4IgrFR5NyIWLXX/s/8d3wv4wTjkrP65slkHaoe+ +1pvGKA1VkwfFP490CQ2PmP951NyFZ2Lz5y5VBX1iaHpPjOsjsnWIE7h0rrd1BtI5E7n0XLnSUz9J +joxKkXryb25HRumdnMI+a+iYNXFek+Sp+8eZTuMC37gMRh67i5QnZuL9kwYbFqXGijOXmSR15Cdp +CCeM2ZuxcKFIeWEi3n9BGrCqMqnHouKsadrPqOPUhYUgKbl2aNTaz6Os6YtWi+hvZ8s0dFC+S+iC +yfTLD7dRH9EFCh+BbrZqlc/bCgPTwATh95qpb97RRm8Sjg3sLfxc1p1egIrEyFIkTXSJcQprsv20 +taFDoY9I0IntttrBZ8/+Nqx1ZkKmaFR4bCGCxqIZEpEO6QxME93fMkE8InOCQ5TmhonG7GQ2cAEg +CFqp0h0XJo0f09ju25rmp3vR8sJtKCK/vylz/H14Y8J79ItmayJ4PQugL1rKzrzK7uq1CY7pnC/b +soR5xsSnhxCA9PZA5g6eykyLnW29M/RJp+R5mwMM/auqJVooYrRjWLkG+Flswlp3bKU7AFO7SBTe +8rIBLtj5roVgTbKk5FiWONSigLB2yNjIH5uttzqqJeMKQfA6Em2xHf2hWpJZCLAD2HTkBLJ7EEQH +VIzWthYbkA8EIrvt/0sULfgvUEQ4UKSEupyp02SAFSDHwaJRUDbyzoNknWYUSJmpydkZqUn19P0n +haY7MEQN/SeF5H0YghTKIYohhZ45rvhfFDquTQwI+otCYgeG3lKoeTJU2T4Mjeu1tOwWZlmlgiDU +AccECt0Ld+QbANQdFOSRtR8mrVPjouFib0T9AeIFFIgrIgc9iBjgzUJRCSIwWwXEWgQ2CCUlAgrw +IoHHgIzZlAQ2C69Ra0k2kG4WMOK1jClKAltWI8XgT+kW8FpWDPCtgiCROVBHb30tiFcYkSJBtJoW +XtMbIfWMyAdnwGuBpl+fcfoHuHUGcu9vEkkNegggKz7fiyHLACLFPEsAkmZdi5R8CrIxZEW66P0M +4KEoAjVCz5H/ER5nOvDY1po+XJKsAAvzETQN/GcKzoMUnLEASRWsSiR7OEgdDrxXxffNsvwTjC0Q +jOa3YDRzbqt+C/9acG3hBjgIGeYDuSgifQZi3W3LHGz8VzRK+9C49V/RKJFZ2vzkfWy02yaIm2z3 +YaeWw2TQwcaIZuuQf2FjPIbK/BC7TdZHx4vLdQAFfXw8QV51RcOvurb8LDaf7+NjHx7/dPBR42C4 +OLZX1gdIpff6MsT7L0DKsYnIeFXBNEhHxyj5NA8xMqufU9E45RqlOEW13uMT5Raly7APkHHKg0oQ +osTGe21RotFKJM7rZ+UC5c/KJXATrVDe93L6Tlk04Dvlp3BLOPgXRbejiaGuvQeUaB9FwysgRb9w +UPSFzk83sFeB2kidGGlHrmJsgUDqMv3atw21ZO0e+oycPkYeDmRKFerm/dJHhQ3xyPT7xT8RuHm8 +UaC8jwoZ4nG4HOnv4R7qdVApknmqZe4+/YOGBogHeXq4K72bbMMPOQBYo/ODrF4baF7nu84X0hoL +gqwW+r7FC7La4oB1gmMtJ/eW1qe1QceB9kftuNPa2GPa1NFHtZDWB7VLTgYdCPwm+7C2NjD5pDbj +oDYLplentRrH/4o/1qHVHB2b8yYo7Y42remoNs8tfLFVC0Bv7VtaH9NCWPfRulE7ulIrvKW1HxD6 +aA3GAnngIB3ENdqH60mjVsWMgLh2gPndFP2YzcFy0eYwB66LB8u5L2Eb/zK9A9f9A5uabA5e35/4 +X/C6JLgyanpl1KnhpyMgritkfvcnCkF/DQgdIgdUDXnetgNBn0/OdLoQPayP1j2Rje1ipGBuZIlr +k+2zwMb2cmSSL7I10kHreblDGtutEqToTYR1SIoUIOND3k/2cjr2JgLjvZxi0R0Zwz7fgXz4WFI6 +9E9px8kAD8+OfiTi7pOBDKP6eV8fEe0Z+tuQ8+liN+UQpeR8SlFyWmboToQqot5bULEUX7Mdfcvq +3uRsVyQ9B7l0n3qeFbAEoWc0W1eWif7J7OlyZJKqBOYSDmr7relftnTtrLWzAOjwJbGXwzWl66TB +rnlOhbHiBf2YVgwQ28/pJhYddI9l3vHSZkmKQeZQr1K3pdCubvGhfmFWbgaZoV6M4sv+xqnMAmbs +Tkkx/ONT99FL1xl9jZVUpDGiY1Z0f0jqiQ3D3YcL0Y+mFRT41EXhy5ex6ZK/QJ3wIXth/k8Q09vR +BJhV+iF+iFvQ2YyxyTkJHp4eo8I7fEf7ZXwJOZ0bPz1N9OPcCWJzrEO170/uFNUdHHewj9MVK0NP +XqiMgpxmJ3EqbWP7i62h8h9nmaf+G6dvODj96Oyr7HYx1BAHqItbhPzXu6O8GxGo0g5QT/OaoGdr +xpap34K6t9na/8dqyXkVEPrmhCGoYUjmJcVee4Fm0ABJnYioXTD+t34ArEqWHs/ixHH934J6LRiZ +iEy/LZlRCDpD+zjNVUs2OxWC0VBzH7fsvtJshbTd+lio70KboIg9byObrZ9WVnMSDTSRec3WhG4w +CVpuaD4ru0ZKINZvQY/palEB8HmzdTb4CeYBD1XVkq/1wLOr2RoViV2slpwbIJou9oIJu+j/tnzk +vzNlWdvgSAdUDR0dglAvlEitmAh0Ck4A9mRO2hAqXIqb6u4YJSAbxrp1k8hjwWYd8T6WPgxA1R08 +Al+Ey1bgG/AnnUIAHiz7Eb+D0I6FoSgY2GztkTV0dAMzhkJ7Z5WjCtwPD8Un43PxZbj2Y5zBD+DP +7GhBY7ub+PU1xMlinQjGA2EBmA4xRYGe9McQVsshq4QoKD+Ob3BLzHkIYl+C1Mdozh9g9hKwygz0 +IvclHWhV2kPg7B0IQh6CvGMvQc5LQJlB6h8gq0ninjZeuWCIuxYE9051XwAioBTN7AZTfoXGIBGM +dgazEtxnHgf3V/VdqPebrQXSXWbssWBuRT2giIxw9ATXv4tbB8KeEN9sneyLeUsx46RBAJwlUMeY +iEiUqOqD1YBma5K59U1Xm60FgHIlGJvnMAEDMPfwxJHoVZ/wP3pabHJsGkwHExy5WFRfLgavfQAC +avJh+oFFSHa47hdLD0vrunMdM8GFErAWDOmbCIa5mq8L/+Yh8tCRq9Wi5tZ2R6pWDFM1aYPQN5IT +rmsU3g7lhOtqzK2XyLEqbqynlVRM8RF3O1I3D1dnVan6R9RDwIBo3YuWfddEnk22Mo1HkOTyQCv5 +0b2Bt/rneCMmzL0YeeyslCoQkVct9IxjnN2TNFDzeiWDu9F33AeLvQaLcx8CzfD3lBGy/js0HmLY +Nkh06XfV1Hz/OxtHP9w9On9Yzki08FmVP6a6S+wewIMgUXq4yDMXaUDqRB7kFWcoBbtHFqMJDxFx +dMhppfRwYoizUp4S0o2eZfvjC3TBvkMiwntbrMhpZUl2oLNq/Xva7e93i9e/B3aDT8LSZJ4+Azzr +RJkF/JuXgS8DBdeHSC4SEyg5lhgiSwlxTIk/FblCUXRbGyLxVTRblc3WCo9mq2dzm5dvP0mIN2zx +zvM2mCySOj93+IdGP7Nv1p1W7ZrkrPgqxlkFdk16iHwV0y3eNSkXltfEMBAT+FVMTCATTei+iiF0 +gImeAwNzYCAMBsIcgTQYSIMBEgZIR2AifDBRx0Q/h8d43ncMGIiBAT0s9Y4W6+FL1sOXSHy/ipH4 +wsB8+Mx82MIAS4OjRSp8kAoD6bBMdwRe2b6KeWVjor3gS7wcL8mBz+TAFg3wXdxD4bushYG1MEDD +ko7U7Jr0MvCrySGB3eLuWixVV5ILPyqpQhGmC9M5KxTh6wPXB88Nl/hKfHMRxXzdpPm6mECFQWfQ +ETpFqm5Sqm5OoCJdlw5bK17ZJr2ypQUqvHy9fEmdIkc3JEc3UadoQBrEirW6tbrBMToFraN1ep3C +OXJ9oEJht9ltsyOgDrwojN0QCLXNXmjQKdTNVqzsSapO8ZVOqiAToRMB+fCcds2UqlpsMYE78EhX +jefuJpuz6o44JnBUrVSdGz7Y/905/fTm1m8Vje2R5z0FaQD6WXyLQB4Pfxqo7N04um4NQSLxhz5b +k6pWnFaFBy/VHN7nvz4CwxqQgRvQ6CPd6LNNsv5lzu5+7mqr9bUV+DghgzS3b3ffLlh/ACCIbi3t ++e3k+jXl5NaKes75m0Go1P0xluQU1Q0yurSZjq/W/bFL61iGFdRsXfPn77fcewfXIkiSvO17trrC +vxbBkpxnjjZBvVp8ef/qBrb9UL7oDevGoWiS8+OKxOkBJ1clnky7uT/r5yPbR0wYnOQ6pGoGd+eA +9ttaRJLk/rgi4eb+5G8XOka8A05mN2izCJhj5W85L/2UO8aN/8ehkFpElqQcUvULl3D2vP2g0+Dz +M+mT+V3atqeBKd87Z52fA/QgcUnqpZwG7SrDeZes8y/mJpvu7h9di7gm4TOGVE2q2u95OZU+mXF5 +/w+LvhtTi7gleQypih0RcDJ94cmM7Zcyzp1cf8rz0s9NtsMdMm+p++i/fRJ4MFUrCrg3UNbY/rIY +GVgkVTxEEIlyxi87g2b8gkXE1YikqqR7RWjyAzSuJlEcVyOJq5HG1chgxSmuxjmuxiWuxjWuptAt +r0bitfPeml+W/bLmF3l1Qc0pLyy4oKblFChec1n2HKoPNODT6oRM0texDgc5PNZT7oGe8vi1YlrV +/qzpDVotd7Ii79nsBm3GG41u6bJbEefynq2Dn92w14cSXfKezbi8P3fhycWrnwzo8d32vTzJPe+Z +xHT2PJTmDNehi0R1omtyF3/+zX2YTTwd+bUoSeIeUAFinl4Y/zuUVB8R57nzFHT8p8Tya5J/+P9a +EZnQoP3oZt16y68Hwar6Ky1XszLBlcuZN389GJxUKtd5WzNULe6u9oMhMUnOlw4s97GGX/4IdwOf +2LoDnRWEbudpJsd9AqGLjk88Zhw9e8tLN23FVra/LF33o3prVGny0XrnUv8OOVigclG3lHdJY9vr +8SmDtImiFHXZ5yvHFtHGMuOn+LnPkc9KoneeKuuX72+ReQwZdzeJeNCqGlyBjSwlrNyA/hotsbgC +I1r2+QVwucSlMGTxJ+qFI328iQcRBw9mTzuBRhxEVOS8Q68Vse1lhGogGEqUtnXURxcSuhkHotpM +Xe7bZF35pZEjDiW8V7hLLNmguaeKbcmOz47XzjiLlYRt9EnQbGsdFR5wl76H6SbFtuMxEYOmaRPx +GO2g6Em/1WsT35/YmtyzRdox/lfMSk42+oZ7l68QDzAmNYIw4xTjfONy41n1DfVD9Tlj780vijwK +q4d1Ikj4H0arEWU92U9stsLRLNjWmsRms0XsHvYo+wNbzYI/WCs7jPPkPqfGclO5BRzI4qzUTs5T +qOSC6En0Sw70cnn0YFOIKco0x/ShaZ0J9NL7TadN10w1JrOpy+TC3xnEa/mIR1+LkSMSBDlm3oSa +N2HmTSLzJjGsSMybpOZNMvMmJ1hxNm9yMW9yNW9yK3gWad245t6qPeKdkoZVe+ROvYlYzyyse8OQ +hiN7gi0g0jLbkmFZa9luMVpOWVQ0uG95bum0OAvHaULQCTMFkCasFrYI+4QTAifcFRoE0C5IcW98 +JD4B1+PJeA4OjpN78W/w8/htvFQ9QI2pgUodoD5qilFXmyh1obrQBCrUYwzRhlDTn2o9qSBySBBK +TCbmEsuIj4l9/AHiDAGuEw2PG4kOYmFTDLmQrOJBIbmLrCC/I38ifyf/JAUSKPR++lD9ZP1c/TL9 +x9G9ifA8Duo95b3xd3QftN/RT/4VvNAn4a4GtWEav4hfwacbRPmG0hlYmeGQ4ZzhpuGRATQbegw4 +5UsFU5HUbCqDAmup7dR16hSFEdBvUJ0UcKYH0gR9ip1J7yNOEFtosI/mCY6+SzfQ7bSU8WbASGYC +o2eSmRymmNnLfMOEn2duM/ym5Ktjkbgo+JuIxkVhcVGiuCgxrEjioqRxUbK4KCdYcY6LcomLco2L +cosRh8+auGHXb1HLIvbAvfzDZ2IsoxFLjvhxl8us20beCGxGjFWxAew4NoZdS1AsKGR3sRWsgvyJ +/Z39kxVYoOD8uFBuMjeXW8Z9zDEcOMCd4a5zD7gXXDfnalIIINA00ZRoSjcN5StNX5pYE7hscmOe +mYIYOe/DP6dJHnyBf4vvozfz5yzH+UcWEw/q+VZebOlnQYzvWWItBgtYadloWW782lJm/NkSYLRY +ACJ4CEOFMGGKMF9YLtACKBMOCeeEm8IjISrpzu75HJDha6oOHxiDLKoYg88/rMfBDS4HL8ZPM9eY +GuY2zuMbbKjYMRXvmFnenjLzbKT6UXZETBnmPf8wchvcuoG889ONxzcQH/DTDeuNR9lJ6mx1kXqP ++qga/KCuVv+htqqHEZ7EUX4sAaYSC4gsopffSXhSlUS4BcywvCR6iSLLYDKEjCLnkOBDch3pIuwn +T5PXyBrSTIIu0kU/SK/VR0xZdGDGRLs9EbHbUbsds9tFsCK22yV2u9Rul8GKk93ubLe72O2udjvi +dupcb6t54gaJB/oG7p1+WoJNW4LJ0SEzz0qCDSDSMNuQYVhr2G4wGk4Z/CzgvuG5odPgTFVZCEpH +zaRAGrWa2kLto05QHHWXaqBAOyWlvemR9ARaT/et71jN7qW/oc/Tt+lSZgCDMUDFBDCVZAzzG0kx +hQxDggoGNXmaJpJ/Mj6swkiyINRYfWS6McWIHrty5e7NuyZZg6m9+soVhICJzkxD701QbNxrDGPP +Gx29ymY0G4CjV13Xx7ALWYp1dCpQwX7H/rNXKbjhNIC96of2gYu5PK7q1NbkL06Bi6cgrc9ePHWO +a6Efcc1c3Tx9pn69PtgU+Y8rW8LpJeVfqMLDs9sQTCrvBRmmtabtpvVjf0qVnTQVx9+4ha6c9WaG +jH+HB6P4D/jpfAq/it/Ef8rf4cEF/m/GOr6FF1m8LCg73gIuqBdZJrGz2HLLEUsJ+3cLeGx5ZQGC +UhgiuHPRwjwBZArrhR3CQeGscEN4KDR1Hn2xsqu1qxLpqkS7KrFjXZWirkpxV6Wkq1IKK7KuSqeu +SueuShdYuHZVutXmold3YJtuHO135TAs5HHFWOwmDLheqca+v/E7/icu4Aq1nxqEqier56oD2I/V +jPqA+owaXGQfqF+ou9WuhJoIJCYSIJFIJ/KJUuJLgiUuE/cI8Ix4TchJH/IQRZLxZCoJcslg/TXy +OHmRNJE/CK0k+EPopx+u3yXE6kcz4cxkAezWT6aq9MuoWv3f3IBBaQi3zHsrVc2GsKeLx+OgH+Wj +frVhzeyeklcbVszu2QZIKp5KVOdSmynHnMNFCvzHKqbviPfo3wlgoFeu2yjfRn9F9+S+WHlrJSgZ +eovODamnj5Biph+zCNc4TK2BWcls3OC1hdnHyE4wHDPuieIB49roYhxkBFpjhDHBuMT4kXGr8Qvj +r0ZwyRhleGq0G2XsO+wXhg9YcIlPYf8wWA2fssfYwdQ/WFDHtrAizosbxh2jpnGLOLCC28CVc0e4 +77m/c4+5V1Xztg1FSv0RpDwPLc9LxMrzROV54vI8CaxIy/Nk5XlO5XnO5Xkql/I81/I8t7LQL3LK +r07XbDqVffSqZpP8Zih2YwyCfZ195+qMTbzJZsJ4FQ8C+HF8DL+Qv2wo5HfxFTz4jp9N/c6/XZTo +Zwm1gMmWuZZllo8tjOWA5YzlugU8sLywdFtcBbUwm5goJAogXcgXwvBbAitcFu4JCRx4LXzE+eDv +4u9y8fgTso0E9exneLf6Iq4m6vFWXKwG/dTD1ZNNsWqDeqW6pnqnGhxWy397rZYTaaZ3CZKIhyku +4Zgl/ow4TlwkTEQ9AVoJMdmPHD629ptwcgZ5KhR8FCqr/4jcGiKr3w3lqYos+Jns4JxMA0we+qH6 +sIh+YIJer0/W5+iL9Xv13+jP68HbNR6YQWUIMIwzrDSAhQYWLzTsMlQYvjP8ZPgdBoyCwdFrQ6nJ +VKl6GQU+phjqAHUGIu4B9YLqpoArraYD6Yl0Ip1O59OleUdl9Z/mdPkjXf5olz+8efxFXf7iLn9J +lz+8efxlXf5OXf7OXf7w5vF37fJ3e/0MDR69cdMYWb+gCFjIZfXyM9+PcQ0CH278fkw+U8p8ybDM +ZeYeA54xr5kAtY/xXSNpjDdeVINc42bjZ8bjxotGk7He2GoEYrYfO5x9j41lDexKdiMLdrNfs1Xs +Rb6WtbAI58GBu1wKN4Wbzy3nfqDKuD8ocI67ye2imvt0YDIVDD0JP9tUzDvkxmg6ZbpqAifo56ZO +kzM/kCd4b2YmDyYwq/l79D7+BM/xd/kGHrTzUou3ZaRlgkVvSbbkWECxpSX8+1Ape9bi/01W2ZN6 +YKv/w2J9aqvvtmSzaiFQCF9Er6A30PlCqfBlS/9DAngLu2ahR8BxXzwYB5H4bDwDX4tvx434A/wq +DlQmxypKZ/VANaHWqa/qQZr6hIkz7VOfUNtMd9UNatCulhLexEhiAqEnkokcAhQTe4lviPPEbYIn +bP62+oRev6RGJKkRTWrEkhoTRUmN4qRGSVKjFFZkSY1OSY3OSY0uMOqa1Oi2sGJpQ3nwy8aPn4iO +wkI++1DHykH2nCd3gsEgO0/aSEyv0gfox+lj9GChfrupUL9LX6H/Tu/H/64Hf+oFvcLgZwg1TDbM +NfSt+mQMBwxnDNcNDwwvDN0G4EqpKT/jRCqRSqfyqTAa3KJY6jJ1j9IQr6lJhA8N3qU71fH0t+wl +9oT6M/omAy7SzUzfAjGoOMMZDxLEMn2Kw+xmaLKKOUSCWmYciRg9jEONb9NqsNzosAiHjOeMN42P +jM3GgjlgUPtywzvstrnhB6IPyIGOnfn1kfhk9qGhmN3LXiPqaggzwbM2FoO+OJx+2jdE1Bset78v +L7rATlzAvbMwEHP5NXmFDkF/WzLoSTZXxIE93FHuB66a+4OzcqhpvAn4m3bSU00LTFmmApOZPgwD ++C3TCOZ9ptfkzmcxITyI4ufwH/Lr+E94O3Oav8aDGt7Md/EulkEWrSXCkvDi6JH4xQ11e5C6PWjd +HuxY3R5R3R5x3R5J3R4prMjq9jjV7XGu2+MCC9e6PW5XE827uhoyj8h6drq1Zh6RH4m/7/HVkdad +4H/x9S3gOZth/3feU98e06qW2kgpdV5UaZ3zarXYkB5fYyw9UGwIRWltS+tQY7WgdM5R1aJGpqZm +Q0optpGVqbO0Sh3K8vagzvJPWh37rv/39bqaN3eu58nzJLlzP/fvPiWgbmteiDJWmaYsVFYqnAL7 +lWLFmb2nPFcc0bZoDgtD0Uh0MjofXY5uQveiAgqlaCX6BLXDvLDu2GCMxCAWm4MtxZaTu7HfsHOY +hMEW/CPcA++M98e3SRNxdfZpeCaeLB1SVSZfuqek4NCT9CHCyeHEOOJL4mtCVTMy5QPEKeIy4bGq +vfyG0L1x/TvuYtytGX/HITuVR/GdSAgkByqfkTNIhlxN5pAHSThNXiW10PctaHvqOBpCpY6dsDW3 +PZZKWbzywveFO8BO6vDY8W6nqQxM08KHyd5WearcqIbHedJ0Gm3OpHNp7ITbUbqusJY2MODJdGEG +MKOYptfvVwbymB7E78wN5h8G2EVERxbyhBGsptR9w65izeTPLJSwV9iH7CvWhVtF9uaGcWDlpnIp +XGNMLHeC+3vDrvFudzJTrEiKVZdi1eenWA0pVmOK1ZRitVMJc4rVIcXqmGJ1Un+cU6wu8z0WRabk +mBLMP0R8r/7Yj3eLvtgvYWUEFOb0SzjFX+Yf8C95ZwETwE8IFnYSU4Rk4TthizCYhOPCReGu8FSw +Fz8Qe4qECOFivDhPXCZuEPeIR0VRhAqxTjRKg9Gu0kBptERJ0FbeJ62TdkqHpQHcTelzDhDZXXbn +AuUz2DXsNMvIUKHkyEb0tHxVrpZfy6gCPXl/ReOXIz8uUFYocj7cVGTlE94d7YQGolruDcxAGXQ1 +moMeRE+jV9FqFF6jaOt+k5eJfbC8s+Pd4twQx8+wGa2Kji/EakUO24+9RbLPMUe8bYdWPvh7UI3F +30K1y/gD/CXuTGDEWCKYgHXyFCKZ+I7YQvDEcSKYgbtET4VQPiB7krOVcDKehHnkMnIDuYc8Sopk +BVlHgpFqRXWlBlKjKYqabS06HvVNZLkJKTfpyk36clO0odxkLDeZyk12KmEuNzmUmxzLTU7qUedy +k8vNU/f1LeOne77WGbqoP/ZFx+XFWz3rdGHxsNWTomfTi+l19E76MP0HDTdpZwVh3JlOTCCTo3zG +wAyGYVYzOcxB5jRzlalm4DWDsu1ZfzaEHctOYxeysJLl2ByhmL3E3mOfs4c4GMHh3FAukttGzudO +kps42Mslk6XcR5JF6kl68cMFGMx/KcTyc3hNMO3mMyk4xzcKJsFDUKj+gg8NE4XDVJqQKeQKh4Sz +wnUBHguK4Cb6iH3F4eI48Usx9Yr3Vo/HzEbRaXnR8TPHnVSwdvG0b1qF+ClrlFpJwyhvKzWVoqTZ +0uILrZdLmyRoXtOalzRoXtOWykfl3fJLDs7Jklwj6xUPpbOyG/9EAQ1CpimZymX+kHJWua7AY0VR +3vddQJPv4gB6Cr1s8k27o88PLUFCS3ShJfrQEoNKGENLTKEldqElZpVwCC1xDC1xCi1xVhu4BC8d +U5zifqEk/sT37roLJfa+adHBhrJxJwrdDWVwCvsf6vMcvll9thf+oz73JP5Vn5cRG96qzxWEqj4b +SXumKzmQHE1SZFtqH6mqzzvJw6QJvUl2QxHKnQJJCaTWsnlspsJQe+kcCkrpJm1aw/3PUX8amoD/ +QnoyxtHLsWIaMEwD/k24fygTycBkZj6znNnE7GU03D+k3GK4FEkAm9jfN+2jNOfOLPRfluE3nD1G +fMl+zeajR1DbefQUe5l9sEhdhc6YQks/vPOMdeA+5D7atM7nWhDXJthN/0FkR0QHRaGGE59yX3Bf +cd9z27gCDk5yZdx9zpt34hdQvfggHqL4BP48tYJ3kvfxrnQHGu7wDXwU3UboIQwRwlR9COYKN2jN +dHpEOC+UC7VCqkH0FLuIA85m+H1cXJKE5Jck6UqS9CVJhpIko0qYSpLsSpLMJUkOKuFYkuRUkuRc +kuSSurvv+Tnlqi5dmVivbu0hw++3uiXpVxM/vLgkvbs0WAJSipXmSEulH6TdUh11TgJJqpH08nK6 +s9xf/kSeKAMtp8mZcq58SD4rX5cfqzBNdlN8lL7KcGWc8qVC48Aq2coB5ZSyGG3ZFGiOoelCMJov +TEGTUUrYgjaCcXfhLuqO22OB+HuB5sniBkwLNN/2MsZOvIPdP/9rEBSRiFH/axCyUroQrEonvL0E +Q/FIfDI+H1+Oa/YaAYdSvBJfLNsRO+XuxOBBS5Z62ykJhO/zDL+1fi4rCS4QLDV7iUSllKgkuomD +xDGiuTupqtpBntHkFDKZ/I7UGbe75ZBXs66QD8lXpAsF3lRvahilvXzbqAyqBQY/USeov6kqairm +QGdwH9GQjx3BJtFJ9D/YRvpH+hgNf9G36Xo6CW/NdGMGMWMYiGESmSVMFrOL+TXZUvP7nPyYACQm +QBcToI8JMKiEMSbAFBNgFxNgVgmHmADHmACnmABntYHLxCdT/WMWPwr4uvfcxbpHAfaWmoEH2oXO +6b15cbtQ2MMeZUW2gq1jjVwrrisHK7HRHMXN5hZzPvhO7jAHf3A3OZlDeHe+Ex/Ij+ThM34Gz/Cr ++Rz+IH+av8pDNe8jo0J7wV8IEfTiWgEWCisFTvBmioVhzD3huQDP6bbiPqVI2UtPFk9Ly0WoljR1 +tlSsFJ+I7qyXBBrHbM/QrG6XVjTmRfix7/IiIuXJMsyXl8vv8iKe3Id2ITP4NkrGbkvNiJphyDAF +rPXDkFjlCr9U+UE5yZQxrvcZSalR9LpW9qiGx4BAw9F4dB66DN2A7kGPoiCiTYBssIrHFqp4DDRA +thhbh+3EulJ/YO4SdJIQ3B0fKwXiI/HP8Bk4MPhqPAc/iJ/Gr+LV+GscmpITQoixAe45P2WXVO3a +XbDAv6RKX1JlKKlSX6AqU0mVXUmVuaRKfYGqHEuqnEqqnEuqXFKLc89XvvCPaKi87RIc0WAP+Qc+ +T1vSsPvnfkOXNISQY0mYRi4kV5IcuZ8sJuvEeyQ8Jx2pttRySTPMTKbmU7Cc2kTtpQSqlKqknlB2 +NHjR3enBNEnH0nPopfRyDnbTv9HnaInewnzEeDCdGejPpOMTmXw8jclkKPwQAy0FX8EdV5ienA8b +zg1nYRyrySWWTSYOsJpYggfsS/YugXH2ZDDHEVM4SOa+47ZwPHecu8jd5Z5yYM9/wPfkCT6cPwZL +nlykXFP52nH5+/dlu6qIbKdTLJzmh9DV/Gu+GzGIGEOECIaxwrSC1rTQvKCBtqK9v6CBtqLliKxY +wRwQT4mXRXggvhSdJRb1kzScOEWCZOkou0XipePSRemu9FQCe/kDuadMyOFyvDxPXiZbNsh75KNV +TrG/V/bIQ3rkRet65Ol75Bl65BlVwtQjz65HnrlHnoNKOPbIc+qR59wjz6XrZEufnJi6X/JGbp+r +bu3VyQ/0eZhHbN9c9zBvj3JUERVVi6pTjGgrtCs6jh2NUihoEdcy2xRxfROVUUAwd6wTFoiNxFTd +CGMwWI3lYAex05iWICSTKA7tcX88BNcTa/GF+Eqcw+GRVIy7yvfw5/gpqS2RQkMGnSxNJjLJ5cQh +UsuHA03kPCEk2YtslDgkxJKfKEtJWtlNGhUN3MM7dP8JNZGiKWhG9x2LHub2x6oo+NTLKbZlbIfZ +25CWNDZ3EAJabkjf09uQeDrsqu+JWKl8jrTJLZ8+Qp+Pb2uxjE7qtt/Ovs9PX554em8mcu/9ZW4y +A+8vc/bsEwY+IbzYd+btJwL8wGpTPsdKbGdSz3lw0Jnrz33CTeRoLo3L5HI5OMSd5a5zjzmFc+N9 +8jJ8or5yy7IiWVZ9ltWQ0T6sJCej/bOcLKsuy2rMskabsqx2WVZzltVBJRyzrE5ZVucsq8sxK3zX +4a+cY9YDEXdyzli/66A7AGes/7VvAysUE8327XHkyKgHmwvu9+r0mm9RRViOREUGPPGuJyw/+twY +sH3enT3z/iTCBmAtNWzXT2zGdmtEFdsVimfEa6KLpGG7DpKG7a4OQjokfnaxd9tBSE1y2tVtmgo+ +WT7xotfiVbu79FvV9UWYb0BDnG95yfwig52l5s7m1VHRuzadVOJ8wXEWXOLS5fVyvnxEPi+/enVm +PdTIFUo7ZcJHnugRfrKicLCHNwh7lRZ7k4SPhTrlJ86QKqw54vLPgiLk4ax6j3+WznUIf+E39/oq +P3AgEmGTmwWNQCehSei36Eb0RxSOoX+ht9F61IS1bh16qnvLlmeiP5R0JT8/i8Z+cM1or0txa6Hi +vVbRpZnYMukQlnoWu44FigqWyPvgfbveId6i4idhOMThc/F0fD2ejx/Bz+PlONTiBsKT6EIMIBKJ +z1WdHRYRa4k84hfid+IG8TkDJJxSLisB5AjSHtUMvqtI2E7+TJaQzYujNwXNi2MKlTE2unRjZH6y +J5LsqUv21Cd7GlTCmOxpSva0S/Y0q4RDsqdjsqdTsqez2sAlunRAvNE3o8UwdQvbWnwaH11KZQaq +B6bHB/o2+mY4ej9dTL/1zeQpzcw7CNV4F5qZ93/1zUCjb4atYTXm1XgXmpjXR9CYV+Nd0Ji3s+DG +H6L68sP5cTx0pL7mJwnZvCic4ufQxwRoQnjn6PV0tHCEThagM7NFWEl5MjTTCPD+QHq3Fpt0iRgx +UVwipt4Wd4m/in+Kt0SbqJOiLIGdFBaX2m2ILp1Y6m6VYOrlfquTpC+4jdKP0jGpDRt2W4qmTXLr +B2eS23/I9YL3paAmBOGoLMoVcp1sVFoppDJQAQfhQ2G2slhZp8QIh5WXSiPu+1nQcF+V8L/iPqw9 +5t+UANy23+qor90nlCETynQTyvQTyqINE8qME8pME8rsVMI8ocxhQpnjhDIn9ajzhDKXfqvtfe6X +0Rfa+sD9ssUX/Hz6rQ6KbFNFXyB9oE3V5/gsfBG+FteyBX/H4Qa+XACiBdGRCCAwcTwB04lviFXE +duJnooS4Qjwk4BXhQnqTvclhpJWcSqaQkEFuJX8iT5B/k1XkM9KBgg+pjygLFUGZmCTqW2ojBT9S +x6i/KAemntqItaa70TCItsNi6OHMErqA2UVbcchmbtE2Wsf8pIrkfowqkicwDkQqMwt7jkcQZ5hr +jO7RQv+XjDOLsX5sMBvNwhR1gXxns7zLIlFt7t4mW3DIuH6rLauD3SB2tWXzoTqEmmsZv3ZUtNNI ++OuH/vab1pnxNngP/CY3J/fY0w0TWlksieMrlu02Tip/Wlub7nEl3eP2F+keZzIaUoeuFvWPkQN6 +XbDbjlqDZ/eCeSmJ22KmDdOyVNrH9dV3TjD6xRnndbcMnHYhaG/bSH5gC6fwgtRRZpdLoRWJuX9F +yhm9psrpwa10urjWMZW3di+Rs2TYJZfLtbJB8VS6KE6XZ2Z3aHOJWpiTvj1tO2TsTCh33cDv4a/N +uB5nrPhyBjzkdYYU5CRfttsgDBK6CGEsbBZuCUXCBWEb2yCYxQss9BCHiGFinDhXTBfXi/li6hHx +vFgu1ooGybP00JOX5m/ovtLwG+fHXgh27xQ+Ii8NGZE3Ku7bi8FuDRfXRevzP9tpGpFnNyLPPCLP +IX9EnuOIPKcRec4j8lxWzcoLauFQOSvPPiVvrVWP+KfkDb1dhuyowYdmLLqnL6rvaxjpNPLgxN3R +bbFoWIzg/m0z/B7gHSP84qBl4LQlYW/84pCJ0wr3P6tBvotvvfy3J9ZtAp7zi/D7b9DmUrnwPOq5 +4Ch+fvqvg4eewHJhpLhXmCGW/rw7+nuxVoACFRlZXMfMPhz9eJDZeXyIruXodp2CE9xyowe2GBmS +0AKsQwd6fEtuHJngsY/cGLKtBkbl5CtHlPNKuVKraHlaoCVqjUI/RzWPiZbab/kF7fd8X/8VUceJ +i4Gzg2dtmfxxrTf43erlE+7xL89Oopp41hYkaDx7O1yzTOr7uaQFaEqe12R1zY41jKWDE3QTa7rH +D49fZ6quhfeNFjdpmYZmo8VI5jPGyDEMvG+0eM0MxeB9o8VKdi8G+9lL+D1cM1p4EW05nAPNaDGZ +m88t5zZxezmBg1KuknvC2fFefHd+8K6JNYYrOStCkRWhO3SPcibWjMoJKwjdEhSTUxBqKAg1FYRG +2xWEmgtCHQpCHQtCPZwKQp0LQl0m1oy92M0KW4LgQDdrE3CoUcYKME1YKDRpeW3ES8I9Ae5QcaKZ +xsWh4hA6jJ4vwlx6k/iQyqdLxfN0b7qWBiutAYd3SBN2S79J56QmpOkhd5ahCWq+Q5rQBDX/RZov +kd7W+nikKc/5B2W3UvSbck6RlGWUHvVALd66V7ozOPqv0dQecrM32F/O24eGkxfQO2gDCmasDdYD +G4OHYXHYXCwdg/VYEHEEO4+VY7WYAffEoQs+AP9GaBJyv6pCDv4Wf8ediH9wIDpKmpADqzSeeF/I +gSbl3hdyoEq50UFzhsw/MfTPqh05e4MsudndFtdXSbd/D66vMtRXRZvqq+zqq8z1VQ4q4Vhf5VRf +5Vxf5ZKbDRvmTG+QbgdFTm8Ip+KpeRT48xuoPdRRqslxPIGGVnRX2hfdQX+MalyUisIa9DBdiN6k +e6CPUHfGFYO5aB9sPfrO9AXNbNRs+oJmNuLY/axm+gKNjRy5tm9NX5O5liobbfJfv7aVZrk4yYFm +uXjBOfHt+BIliI/KVxpDYZL1FsXSZIl/Cppbp8mr88fuCr/ru8HLauMzZQ2y/BsEs0pcI+wQCoUz +wjVhiQRvBFfxP0EwRVRTEMwr8b7oyThJMEYLgpESmARpgbSe2SzBPqlIuiDdkRoks9xG7iHDEDlM +jpPnym8Vs8fmCr+/Kl2yEJesLhH/VFb4bZvuE5DVfo1TXUCWISDLlB+QZReQZQ7IcgjIcuwWkOUU +kOUckOWiTtgd9qntltTty8pUcpVDyjwSriuaxUzDBKuxreg4FL5Ep/JVaAavWcxO8H/z8BJ9xmPY +TP4jIRqLEAp5SBKu8Tx2HLuI3cU0JyF8gDd5CZudhNDsJXzrJCRgIDGaoIjZxGJiHfGppSHiVz4w +qi5mFvRUEpN02bOm/z7guv/R43eJp8SQKkCyRz32JfuRZHFB4m/Fb05ZLhwMP6VCup3GXHakCxfz +ht/vMsbQO+eOvI7YM+zNqZBXB3cW/Pzol4M7r4ScO/GtvueEiHn/fBYWM8/BGGa4E0fpwG6ec98a +xwENP+hT3WaWmYKf+T9LzW4xs9Qls1+tf61+SI1Jt7AOGVEbXJtR7x559HjbyJb3SNSq/6nB+Jp8 +PlRX+mQLdcZ8+86sWR1WPfXqJNjua0GNgeG1r233EP2F8H3PHPinpp8awF+HnBiMkg6rLj1NQIev +Usk0NNO+64kJEbNKj3tlhxedtUNgS+pImyXFf2ZpsU7f5Sx63WXKxHsLKEtKzPdjEecN4QX7f6Iu +0H9TVRNNiWN1MY5jPcL2L5qbPQtJPPxtA2Wm29A96CF038gW1sndL7HIhTalDQEVe/V/h3Swxi07 +dibTt82PxJ/T8W98kQ/vp71YUk6sKP2p3NMir7T2ujb0SuA/FbUxaFDgrHj4sjY3NfFPT6pdHAJ6 +c37kyaV0iM/tyuvsYzYuFGmwX3P0+Ehbr7Cjx/cVRwaYd+jGZEYGGHTF8X2PqDsWvnbFmojAUbrp +KmGODIh2iAxwjAxwigxwjgwY6rIwADm5x+ibuTDAfmlAui1L77804NKaiFhj7uouVPwXcZPj2s2a +gmgaMXAxsz0e0q+mkB7ryDd0AJPagblOPh6z0fVwSC5mTjdCi+9wagyzqtHitJjaxcCvzEHqFmNj +dGxL1pftx4KW7DCTTWXXsDvYQvbaOvDKnu3+aE0fW8vz7c8evtuJ1+0tQAbzXWN5IV9XIqzhYdTX +yLMpzJBDmsLQRuwhwn8VhiMi/KswSF2kAdIoCTRj0A5prZQn/SL9Lt24WR8923Zl6PogbxgJufjU +GG9bBFo5Z2PMHAen8oQWEd4xMcaKP2Jb3UW8vOXe8jBZcw+lyBly+VZZn3RU/mjZJTnackJZbklY +O8rVYnmdeCXNzmCxtFyEPVwzbL8x0HXiwYqc109aPnPLdeh/bDdyS0rrZsuvM2b/rJS89ioESfmj +NlP5/mmaMl6Z/rqncp9QwhsaXo+3M6iCi/uzy3OTpeaeuP3Dox5lufYWBIp10H5WSIdnBu8SCUUw +0U/0Pqm3z35qTCz5XNTPFhef78LpvQpPnDZ5FeoW6otFuHTesJ1bXCNKJ1sfain9CI1hcP2xT7CJ +mBYGl4nBOOoQpuGzx5hMueFSmqkzDvB17OADI4uTq06cgBCf5KooPAH/TV6Bb8b34VCEX8Dv4A24 +mWhD9CCG9ANj9mh/Y/ZXi7mGEye+9OcaEIMxm1GPLFf3Wxds8Q/jGspO7FcJM9fgwDVEO3INTlyD +M9fgcroBDNuR4NMNQwo8gksbDNu7BkNpQz/yY3ICOZNMJdeQO0goJM+QQLXA3pABWAeqDwWhVAr2 +BfUj9T21jTqPnaSgjAL8BeVEt6N70UF0FA0J9AJ6Bb2Z3kcX0RfoOzQ00GamDdODGcKEqWvNXAbS +v6nYd4D4lfmTaeJCaGLDd1wIGhueYa+xj9g3rCvXgYM+XCj3zlJ+PcQt6LdNqu6yJf5qVHV7x/Ye +c++v8nuzto2Jb81342EQP4ZvCrekhuTsPcSPOPvj68Jcf/39Xbn+dS92mX6Y9q2eF44LFwtXCtxR +VSDPPjRJcD8QGdtWwI/eSbKMEPoU3BD+EeDPT5YiV3SG2wsq55vuJCUsjoibzS5eAfbLNrIryo8W +H2RPL/cSo8RvB4sGLva8/eYzYVmDOD2QXOxWzYM3W1rc6MCDw9IfUi4mS5oDr5M8EIeR8mfyjAeR +sZ0zT+ddWbS6Ek7nbZMLZHuhTL4vv5CdFGin9FKClCglQVmgrHgWGZu6oS4y9kzw48Iriw7VPS40 +QGTsH+qRS+p+ZeIDdbsrddFr9cf8uNDhcaFjt8eFTo8LnR8XuniURMTBaL1Hif2iWD2mEvPULSxB +s9Bd6K/on+gt1IbqMGiJjcJipI+xJdJMLBVbg8EZqRB7hl3DHmEfyq54BxzGyKH4p/gX+Ff49/g2 +vACHk7jmcH+hqj7tiF5EEAFRRAKxgFhBbCZkooi4MATwbxDURLYmu5GDyDFkDAmJ5BIyi9xF/kr+ +Sd4ibSToqJaUL9WP+piaQM2kUilYQ+2gCqkz1DXqEfWGcqW7dKD70KFxvUdN+iCKTqDhv8zVQI/S +mKueuE181efJiAVK08t+S4DPmP849hZAdvv7zIuve4e6s53YQHYkG2ixnJnr9uGdSem9Q3V+w029 +Q1cs+Y08N2bs8Psk/JWupzzGPWV7cx9wERSx1bWk11dZW60rKP0WSouBgn1cEXeBO401xUDVSzCE +D+Pj+Ln8ClmLgTqySx3vjxyPJN8ISd3+w4MAY5mOQoAwQhgvTBe+EVYJsF34WSgRrvyS3f7egez2 +nu6uA9N9I4zFA9MN2e29irPbg4+67xcUqG7PB32ibqPNA9MdBqY7Dkx3GphudB6Y7hKfvq3DpuL4 +9LFBwBfPUIlidXtBvCM2iKBF2DUH2G2U9nGwXrrAHZHOS+WSJ2+QQ2XoIg+Qv+A/l2fJmxsD7OAX ++Xf5XYBdgAJaZM37AXZwRXmoDEZdUG+Xr4eQYhQKCegCdAWqxY0WoZoGDQ1okwY9BGvSoCEdW49p +HpFmDRo8cU2DHoW/NRN0QPw3avVQ//PxK7fouAe1uqZs2GpbtKmpBmS1VmQyGq22uVbbmnJdohtz +XWwe1TZPlWhVbWtdbfOqtrVRiQ+qtTylttW2dtU2BLthazrxf+vDqK1fa5UlGkvEKB97N5WIWebi +aKl8pkN0yMMaxzfVtjOfSqatkVANEdW24J66Xs9C9hhsgVrNmKEPaptLxbT5H7lmb3OCG0u8fBsn +mV5EAoTD/7/Ey//VXyvGsiBeMn0d9X4xFq3H9P/R2vB230P9/7TTOVMfDGBcH4DLcFabg9c040p1 +Dm7q/jDQqjzfj5VMn6hn/Qi0gtXD1O0VdZ7f/Xuk+S/arznbvsv9Og/1njrdUEBLeKl6sk5Lpr9X +T96r74kMO+Grix6KdNLX1kSj2md5ahKcy1/2ND6smdrBjCB4U26XQXrVFYnqitx+fqTyWXdPXfSD +2mR73vnWawQzmR/Upk3xNTTU7EF2GbpjHzie7Vb5rJ2nLqon4hDTsli323FGy566n4vMgePc7097 +PcWw2g2Z447EVNtevmnbTRdjHwGTlaZaLjAzqKXr9AOuO12TPXoa0XHubb3v19mINxvdinWj3FKL +jZ+6hUBPRHDwMvbUjfDuZuiKDEUJPKSDSa4f2xX2o6uceyK33HYZUPdFjhn2DsXGTZXPQsrN1/HH +uII/LH+5tMjsU66VuxCaKqohqhKIWHZ0Lza9Tqi2Tdbq64KiFf7QPrAkeUM0zHxuiweJhknqJG2j +YVrkDUV6pQToHxC48SlCxPRBdI4mg1EXgRg6ItpJtILEkaAkwwyIAxqyp99QWqAd0QA09WGNUTGq +7Gln0hkRu8B/W48FZlpjEZtJT+uz59xQWqPd0NvPteYQ6KA10mr8BoPS/NGnisSXNg0Xl79UdCBU +KD+Cxf5ePfcUAfXp6VCjjtDpDHrd2/nkNs5n/jSY8cQ2D2beUOa2QG++0T+oZbuY7tWrnQzqK4uY +QK9HIpF3XcJAUa+2cVqnRj23RWssX/FC0TGw7cUrsYvnvfr5CLiqXXXGjubmTqNBiQRvte9wUOjG +DP5pUBN/Q2mD3m1Q51quvHx9+osWAOY3D2s40Kkv2WVFUu8f2OmMdub3ZzwMGO0E82DGDeWVK9oB +BV4HP72Uv7o8rZd6rWBWtM5aX+3bVM0TD2m81qfJMP2xLRYmQ416uS1R6dUyExhu3X7OaZ+LqkEc +FKPOqDfp9XroiDxu7GazwRxQ5jZWH5ip1R+YrN5rGqK9n9gm6WHOU1vsDaVdxYtgb1AfS39V9nxY +Z3tQqzebmq5dG9faxDgzVU7xi9WeuXqToc5W+exYP6PO0agzhIMO0b8/T2WqOt489SpnT2tub6h4 +8ZV25rcd9Lr3OowGSpvclKc2laUyGtunlr9sbK0YjHqjTq9Xhd+79lHAzGt8fupsBjXeQj1YKl7U +2Y79e/5/JzS8cUKSVsh80nNbMtTENt04pRtUaF/TKTLjRgRRvAG6eKv33NHwjt9DGgsst419aksC +9Tq0gVh1JJ02kjoQ/Xak9wZSn+3Uxprp85qfrTaMTm4exlt5O8z7r5U2zCjAtBLjU5/atHCX731Q +Pbi+HQZXW/+38XCQYrW7+9Q2o/EGa5ejB+//4wYHa0ybqD6/adq75g1jNA6a38S9eoC02tvPkZD/ +1961wEVVrfsFyBy6Hg9onZN1u0raOWVHu2Ca2r39DmjgG4mXaLxxeMgzQIVSsSBERcXHLTA1T5qh +14TMsuSqiSlWFP4UUxPOkF7lFpqDCPie+1/rm9nszQyDHkQa6PPH+O3//tba3/6v91ozazEUsZ42 +LT1zZen6WkP2StVjLqWyLx7AKzGX+cOYzpp8tJJl88lUqiPhqC4ejxyLTJdO5QbF5qYjGk3k+9wb +c+ecGNSn5spShsoWmd7WxnoSbznkia2LFjvRJ7MT8PyGdo5O68XWJVxDOUAhqLqtEw5xf34RDnF/ +rHUqG1u8ig2Pif8SO9GaGUnrv9A+wdunAxqVvx+1d6MFYgdknR+1RoRkl2hU3/jRcQh6GyBaIC80 +2+zXqIZMpaMT9DZAQoAMb7b5QqP6LyATmm2AaIA832zz7zm2j/gzNqLZBoinP08iyeaZHNsV/nQM +od4GyH4gzs02Q3JsLyttgAyYJkeyB+fYek2jI4v1NkCWTlO8e6lGVQxkVLPNYY3qCpAhzTZAHp9O +x2Drbb7SqLyny2POBrJsOh3pqbf5WqM6NJ0Om9PbALF/2cCPoX8xFnkrmjdf0BvQ90Lb74MMlq6m +AwwqtXrQE7k3nVd8Q5gzwAECHIvr9GRRESc1adEgurMEHQpUr9AbWkTXV2zyg/ab6fTtNxsRyWKb ++D2KIA4ZMn02409UN2jxUL69VuzvWK8kYYQeHzooqDj4U/wQT9I1bX9xhABceMJEDKii3FjE72A5 +IpyXbcTxmDBTODFHOPEMG4pYHE37MYWXdJ3WKgkmgyUT3WxxvEEY0yWKn/iHs3nqJm0yyinqN9uU +Bxg3/4vc3ANWrmwqKppb2jG3tPE92Qg1C7fRkz8a7vDTGvipQA39RDgO6QhzZE3evDabgTis5iDm +vsKCUzQP/Q6xwV0TuWjmJZ81f3uY+dvDzd9+zvztEeZvjzR/exRu9zO+3aS/7cysnExbGCJwfoCf +zdFGHEPbfMqzbVoMa9NieJsWoHLWQ6LAuHEsXWwdxdRAHxXhBKrTo6w/DzPS3M1RhsHR/RHeDvys +Uh5m/R/6Py685rZ7+082QwL1B8sIZBPGLy8E0uhmrEC+AOKlQE6+UaaKDKT2gZBx67+2zQmkQ0cI +scsoUxUobE4u0Ki+DaRNRAkZCZufA+l4Wn2o1zWq3kGM9ZcQJyBOQAZKSOp61LVBdEglIVGI540g +OkqUEEe819Yg+Xv5I57dQXSsjj4UfK4MohEhIdmwaVQ8vQjIH4PlyEkgwxSI3RvwR4E4AUlSIP5A +ViiQbCBFCqQISHmw3MOTQOoUiF2GRvVgiBxxAjJcgfgDmapAsoGkKZAiIO8okEqk8p4QOm5J/3TY +HAuRp5ddJnoBCmSwl1uPXqE0JiaeRwLprUDGAfmjAvEB8ogCCQHybwokFsjjCiQVyJ8VSDaQQQpk +NZAhCmQDEGcFsg3IcAXyGZBRCuQwkBcUSAUQVwVSDcRNgVwAMl6BXAUyWYHYebv1eEmBPATEV4E4 +ApmmQAYDCVTyDCRUyTMQtZJnINFKnoHEKXkG8oqSZyCzJISLYdaIN5Ic5Rvh8u2A+Wks/FigBxgd +Ed+TNR8rz+dQ7BmVQD4Xwo/34sdB8xkVXpPwfPYwo1qKHwHLSyE/75fXIDx/9WNUNhwZHbHOcyqv +A/gxZLxO+QujeoOX+UH4exp/f2V0vDfvqfEeHa8hnBj1Enlfjb8dP56M91CfY9Tn5P0z3td7npH4 +9jnXqD/6xPRcVo87mst64tFo28IIVst6ibmsSj6X9T/yuSzfPvhfdKaAWfXkz5I9o1J3tOq2df8e +YvfkgEfTo20fjgSPtdp5iGwbEugpxDXAisflILZsyjLMY+nFnnW0uODvZCS6tBHyCbdfk7jgLzVK +o3o6kpL5znZ9vv9M8kL2WbRGlRvV+l7RnSvcQ/+ZGtV6hYc3+trwfb6YnfXfGDt7/NiDP9fVa720 +l1XI07Y9xCSybWS9dkClbpi9u72//ZlrqfVaa5v/q+dbWWVZ/eMftzz/dZTtz3VPbU4flfr3Ez+e +ddlsv+kze7U9a9Kea3yo5sqfgjZa7Ym3RbFIrdU2ubOH+UZnX9sev1qrxSDBp2efkbs++Nr2sXpt +3wd1vXrHfK75F6ufLoew/6zVflGO8udSr33UajQ7jg5Z9kCH8fXagb9HTvi+wn52f76VWPC4T8tU +UxY7hCBuP/aETe+B36oKjy52eOyRPh/2f7y27olvvVXlix36PjioVvvZQYc5V/sx1z5+7EmEtPvz +t6o92xyiHulzy7lWezhcoxrkW4P6pl47A3XWydtLekdoVNMwslt3pB6Dt6G9evd3TLuEOnXo3zGi ++dvt/r1RcMLgTMPTTw54sbYOib+NaR0mDAx9vFPTuSOE73Wqsjaep+DFrPrNdy9fmxLl8N8r7Nhf +n/z4lBOwuZ7UkPD7IYwaHmQ70fjwM/t4bbyAUSOUx6gh2sSoMSpi1CDxc+Z4g8PPg+ONET+3jTdC +LlYU3//2oAbGMJXfUnfAn2tYVHRylDrG0Ss0zpSNXOfnQY55PsAzKSEyKTTO0T06Vp0cMDk6PCkh +OSEixXFKRER0uDqA/nMeFjBbHIkWnhCfoo5PCXB2evbZgNGu3uPHvBjs+4yft495HzjeHLU4XU3U +dBueSzt7ZGiZ1Vj+vlPorL4/6HnMkfE+nzWfd8nFFwTxM08d3Sa7c+vBU4hrHo7XfYbljwUguKgH +L+3Eo1xcLuHmmR7Mj43HcHy8OD+cdoado5+SJuE85Xi07ks6owbaUZyubBDT52bq9EcR8nM4nzK8 +owfphnisDJNhsmfKwv8mnSK/8d+58hv/nSvdmf/z58+fOnWqvr6+rKzsvJB9+/bhEspHH30EHTgM +Tp8+jc8O8qE7839KCKUC8S9dgnzpLqVLB/nQbfk/fvz4QYPs2rWLlP379+/evZv0PXv27N27l/Rv +vvnm2rVrbcZ58+ZNmDU1NR05coQCQkGBuibk9m0TRwR3W/5dXFxihUyfPn3ChAmk+/n5vfTSS6R7 +eHgEBASQPnHixK+++spkPGD16NGjBQUFr776qq+vr4uQ0NBQCgiFENyCwebNm5Ei169fl4J3Z/7r +hWzatGnmzJmkL1y4MCMjg/TAwMCioiLS4+LivvzyS+NISktLETYsLGzRokWffPJJVVVVfSuCWzCA +GYwRpKSk5MaNG7ruzX+DEORJEEJ6dnZ2ZmYm6eAfrTDpJvlftmwZ8MOHDzfcpSAIAr711lsoCN2Z +/yYhH3zwQUxMDOng/8033yQd/O/YsYN0Y/6RgdPS0praIQiOdqe78d/Y2LhmzZrg4GDwf1UI8U86 +6gfwTzr4//jjj0mPj49vwf+BAwdQn1+VSXV19TEh24QcOnQITTkhUC5cuHBVKQheXFzcffgH8xs3 +bkQ7mJube/HiRfB/XQiaTjSUpIP/rKws0oOCgnbu3Em6Mf+QVatWJSQkoGtENr/88gv6VPj88ccf +pU9SgF+XCYIgYH5+vq571P8tmCcQ/N8UsnXrVvBP+uLFi9EEkw7+0WKSDrpMtr/l5eVImvDw8KVL +lxYWFmK8cLMVwS0YwAzGeNx3331HMXRt/lHNGjNP4u7unigE9Qy6l6Sj/+nl5UX6pEmTkASSjk5m +a0/BABk9pZycHPRtXAwyU4h0iY4oEnf79u0wloftqvyjd4chz6xZs5YvX46619gA1UK5QcrKyiQd +lUMLBdKCtDuR40LaNOt6/IN5rVZLczjo7HW2O21IV+IfQ1G0bvuEIHt3tjt3JF2DfzCPngZoR21/ +J6X+1yOWzj+YR1ZHBb569eqKiorOdueuxXL5pzxP08WWyDyJJfJP9TwtjmB02dnutEssi38wX1tb +SytTltLCmpf7wz9qCTCGISQ+qZVE1kX/sEwIlPr6evMxgHn052GMsKh27olX8IFWHuEArXZBoQoN +rQkeRAmN/r+lr3/Bf7wXLa3Sp7T2Spdmwt66dWvv3r20FHhv63lyQz5LL7lHvkl3LXH9F9l13bp1 +q4QsW7YsPT2d9MzMzKysLNIzMjIwKic9Ly+vrq5OHgPy/O7du0NCQjB4379/v/lkultB9pb7tnLl +SuiLFi2CS4S//vrrkm+Qn376qc33RWE5ePDgtm3b3pIJLktKSjCabmxsvHnzZotQHcd/TU3NxIkT +84QkJSX5+vqSPm3atNjYWNJ9fHxSU1NJh3FlZSWFBfPFxcVgHnc7KO+5uLgY+xYREYG0Jt3Ly0vy +zc/Pz+T8G+TMmTNIO5rqiYyMxKshBfNkgkuAuEWTQrm5uXhNFGoK3qH8w23jNb74+Pj8/HzSg4KC +pDU+GMOx+8A8SfvXH9ETQBmB2+++++6hQ4daW3mUC8xgjOResGABFSg5/9TGSYI+3o27FASRgqO6 +gG/Ga3zgf82aNaQHBwdLa3wBAQEFBQVAZs+ejTGs3JO7daM1lyRB9mvn+iPIRGGRDO5WEBAPRRLI ++X/77bdjDILI0cHDU6RvH1EfRvqSjNRuyi8RhIK//PLLoBH1p/EaX0JCwjvvvEM62JbW+CZPnowi +r1arY5Qyd+5cqR08bxDqrpBXkidyM1Kov9QiQhQu+DBv3rz2rD8WFhYuWbKkxZLi0aNHUSg0Bqk1 +COEtjBEcSS/n/+LFi9KMK4oeno6GA/lhy5YtQD4VAkT6JHzp0qXgs0SINGeLF0f9M3XqVOM1Prz7 +2rVrSQf/0hqft7d3SkqKp6cnKoEDBw5Innz44YcrVqwoMQgQPO5TgxBCn7ikIHBMcrKqqqpcKfAN +RaCd64/IaVFRUfiU1hOPGQQNLroN0vojKS0WHyk46ts26/82e+atCfFvvMaXmJiIfhHp4F9a44Mx +/EEeQGeDKtVLly610wcz0v71RyQlfIb9iRMnrt+xwBhB8ILff/+9roPbX7h3Swit8ZEO/tevX086 +qgJkUdKJfwprMhXurYB/Y99QJ6D0kQ7+Jd9aW3+EwCYsLMzd3R02eC/UG98ZCUDcggHM0L9CsZKC +dxz/yLSgVFqAGzduHCmurq4S6ObmJukw1mq18hjkqXDPV1LCw8ONfZO7JPdt0qRJZ8+eNRMbKjQU +B5Cck5ODwhKnFICocpEQxl9i/PXP/yAV0MSgwsT4Bc1WZ7tzj+XXz79OFCUUAZoTQBLQN/e6hlgE +/zpD3xKpgN4LpYLxWN4SxVL4lwT8l5aWUue/C6SCxfGvE9URigPGyFQoLDoVLJF/EpoWPnz4MA11 +LTQVLJd/EkqFDRs2WGiNZOn8S7Jv376VK1dSHwkD/HamQkeMuE1Kl+GfBK1zcXGxlArSNHtrxrTy +uHHjRloJXb16Nf34LiUlRb4ECbMOcriL8a8TWReMYbgKJlEiPv/8c5NlAUmDkR2GdfSLOWlq2qQO +M/NJaSz0WzyTv7mTS9fjn4T6SKdPn05LSwsJCTFOBT8/P2l6wd3d3XjOQT4pMWHCBDNfwUXMeNbO +nTtzcnJmGsRNiHS5ePHiHTt2nDlzpsUURFflXy7yVJCyMVi9LITWv0jPysrKyMggPTAwsLCwkPTY +2FiT82/SLws8PT3nz5+PqKTftF4QIl3iFgxgBmP5pGJ34J+kRSqA/ytC3n//ffBP+sKFCzMzM0mn +9UfSTa4/AgGZy5cvr6qqunLHAmMEQUC0U3Cj+/BP8sMPP6BtfeWVV8B/o5DNmzfHxMSQTutfpNP6 +I+nG/KNxiYqKOnfuXOM/JQiI4OXl5d2Nf5Kampp2rn9t3bp1xYoV8iUt1DbHjh2rrq4+JITWwqqF +QDH+/R0etH379u7Jv07U/9S3KSgoAP+k0/oX6UFBQeCfdGP+6YscCFtv2F4AfaSKigp8lpaWVhiE +QCDy1X8EoYfW1dV1Z/7pCxJbtmxB80o6eingn3T6/R3pJtcfQeOqVaumTp2al5eHHm9DQ4P5b2LA +AGYwRpDc3Fwa4nVn/tcLmTVrFvqipEdGRs6YMYN0Ly+v1157jXQwhj6MyXgwiEAXCAmETizCJiQk +oBFfLxNcAsQtGKAdee+99+S/BOy2/KMCXyskPz8ffR7SkS1prRCyZMkS1PCkg7Q7WXpDFwtN6q5d +u0D7WoPg0swv+Ih/vjfeQ6SKPfUM+m9yb8WWji3gJ8OIM0RSxIb9cfgXypJYGhsPLILxczw4ksLE +YQKy8COZNTNOvbaeydhTJo7G+Gflbp9/r8XSn8/3reQ7MTbvWzkmISkxISk0JTqBp7WNsPqD2PeT +S2v/93MgW77Hp2dopHqIM3PiG057qcNTQuMjY9WMefB7brGx0YnJavZ7B9qns58sTDJr3t/SgE8O +TU5RJyULnGebnYzyEBcnve1c/b1MffhcRnGv1V9bM6pTgj3Hvxg8abzHxNGu3m7iDt/uOtiPw66T +fNy8PFx93DxcJ7t5i+Dc/WAfdVxibGiKevyLzfuTurLmY9pM7U/qM8bJ2WnYqKEjhvENRO82Re6f +/D8JQVTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + +------=_NextPart_01CC4DF5.1FA44F90 +Content-Location: file:///C:/EEB34A10/WdfMultiComp_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CC4DF5.1FA44F90-- diff --git a/pofx/WDF/Driver/MultiComp/driver/HwSim.c b/pofx/WDF/Driver/MultiComp/driver/HwSim.c new file mode 100644 index 00000000..b3c04d86 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/driver/HwSim.c @@ -0,0 +1,251 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + HwSim.c + +Abstract: + This module implements a simple hardware simulator that simulates reading of + data from the device's components. In this sample, the "data" that is read + is simply the bitwise complement of the component number. In other words, + the data for component number 'X' is simply '~X'. + + The hardware simulator also verifies that when a component is read, the + device is in D0 and the component is in F0. If not, it breaks into the + debugger. + +Environment: + + Kernel mode + +--*/ + +#include "HwSim.h" +#include "HwSim.tmh" + +NTSTATUS +HwSimInitialize( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine initializes the hardware simulator + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttributes; + PHWSIM_CONTEXT devCtx; + + // + // Allocate our context for this device + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, + HWSIM_CONTEXT); + status = WdfObjectAllocateContext((WDFOBJECT) Device, + &objectAttributes, + (PVOID*) &devCtx); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfObjectAllocateContext failed with %!status!", + status); + goto exit; + } + + devCtx->FirstD0Entry = TRUE; + + status = STATUS_SUCCESS; + +exit: + return status; +} + +VOID +HwSimD0Entry( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine simulates the device entering D0 + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + None + +--*/ +{ + PHWSIM_CONTEXT devCtx; + ULONG i; + + devCtx = HwSimGetDeviceContext(Device); + + if (devCtx->FirstD0Entry) { + devCtx->FirstD0Entry = FALSE; + + // + // Put all components in F0 + // + for (i=0; i < COMPONENT_COUNT; i++) { + devCtx->ComponentFState[i] = 0; + } + } + + devCtx->DevicePoweredOn = TRUE; + + return; +} + +VOID +HwSimD0Exit( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine simulates the device exiting D0 + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + None + +--*/ +{ + PHWSIM_CONTEXT devCtx; + + devCtx = HwSimGetDeviceContext(Device); + + devCtx->DevicePoweredOn = FALSE; + + return; +} + +VOID +HwSimFStateChange( + _In_ WDFDEVICE Device, + _In_ ULONG Component, + _In_ ULONG State + ) +/*++ +Routine Description: + + This routine simulates a component changing its F-state + +Arguments: + + Device - Handle to the framework device object + + Component - Index of the component whose F-state is changing + + State - New F-state for the component + +Return Value: + + None + +--*/ +{ + PHWSIM_CONTEXT devCtx; + + devCtx = HwSimGetDeviceContext(Device); + + // + // The caller should have ensured that this routine is called only when the + // device is powered on. Verify this. If false, we'll generate a verifier + // breakpoint. + // + if (FALSE == devCtx->DevicePoweredOn) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Expected device to be powered on, but it was not."); + WdfVerifierDbgBreakPoint(); + } + + // + // Put the component in the requested F-state + // + devCtx->ComponentFState[Component] = State; + return; +} + +ULONG +HwSimReadComponent( + _In_ WDFDEVICE Device, + _In_ ULONG Component + ) +/*++ +Routine Description: + + This routine simulates the reading of data from a component + +Arguments: + + Device - Handle to the framework device object + + Component - Component from which data is being read + +Return Value: + + A ULONG value representing the data that was read from the component + +--*/ +{ + ULONG componentData; + PHWSIM_CONTEXT devCtx; + + devCtx = HwSimGetDeviceContext(Device); + + // + // The caller should have ensured that this routine is called only when the + // device is powered on. Verify this. If false, we'll generate a verifier + // breakpoint. + // + if (FALSE == devCtx->DevicePoweredOn) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Expected device to be powered on, but it was not."); + WdfVerifierDbgBreakPoint(); + } + + // + // The caller should have ensured that this routine is called only when the + // component is in F0. Verify this. If false, we'll generate a verifier + // breakpoint. + // + if (0 != devCtx->ComponentFState[Component]) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Expected component %d to be in F0, but it was in F%d.", + Component, + devCtx->ComponentFState[Component]); + WdfVerifierDbgBreakPoint(); + } + + // + // In this sample, component data is just a bit-wise complement of the + // component number. + // + componentData = ~Component; + return componentData; +} diff --git a/pofx/WDF/Driver/MultiComp/driver/HwSim.h b/pofx/WDF/Driver/MultiComp/driver/HwSim.h new file mode 100644 index 00000000..f23395a3 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/driver/HwSim.h @@ -0,0 +1,83 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + HwSim.h + +Abstract: + Header file for the hardware simulation module of the KMDF sample driver for + a multi-component device. + +Environment: + + Kernel mode + +--*/ + +#include "WdfMultiComp.h" + +#if !defined(_HWSIM_H_) +#define _HWSIM_H_ + +NTSTATUS +HwSimInitialize( + _In_ WDFDEVICE Device + ); + +VOID +HwSimD0Entry( + _In_ WDFDEVICE Device + ); + +VOID +HwSimD0Exit( + _In_ WDFDEVICE Device + ); + +VOID +HwSimFStateChange( + _In_ WDFDEVICE Device, + _In_ ULONG Component, + _In_ ULONG State + ); + +ULONG +HwSimReadComponent( + _In_ WDFDEVICE Device, + _In_ ULONG Component + ); + +// +// This structure represents the hardware simulation module's device context +// space +// +typedef struct _HWSIM_CONTEXT { + // + // The following array tracks the F-state for each of the device's + // components. It is used for verification purposes only. + // + ULONG ComponentFState[COMPONENT_COUNT]; + + // + // The following member tracks whether or not the device is in D0. It is + // used for verification purposes only. + // + BOOLEAN DevicePoweredOn; + + // + // The following member tracks whether or not we have previously entered the + // D0 state for this device + // + BOOLEAN FirstD0Entry; +} HWSIM_CONTEXT, *PHWSIM_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HWSIM_CONTEXT, HwSimGetDeviceContext) + +#endif // _HWSIM_H_ diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.c b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.c new file mode 100644 index 00000000..f424c6b7 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.c @@ -0,0 +1,1036 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + WdfMultiComp.c + +Abstract: + This module implements a KMDF sample driver for a multi-component device. + The driver uses the power framework to manage the power state of the + components of its device. + + The device used in this sample is a root-enumerated device whose components + are simulated entirely in software. The simulation of the components is + implemented in HwSim.h and HwSim.c. + + The driver's interaction with the power framework is encapsulated in a + separate library named WdfPoFx.lib. The driver statically links to this + library. + +Environment: + + Kernel mode + +--*/ + +#include "WdfMultiComp.h" +#include "HwSim.h" +#include "WdfMultiComp.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, MCompEvtDriverCleanup) +#pragma alloc_text (PAGE, MCompEvtDeviceAdd) +#pragma alloc_text (PAGE, MCompEvtDeviceD0Exit) +#endif + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Driver initialization entry point. This entry point is called directly by + the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path to the + driver-specific key in the registry. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + WDF_DRIVER_CONFIG config; + WDF_OBJECT_ATTRIBUTES attributes; + NTSTATUS status; + WDFDRIVER hDriver; + + WPP_INIT_TRACING(DriverObject, RegistryPath); + + // + // Create a framework driver object to represent our driver. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.EvtCleanupCallback = MCompEvtDriverCleanup; + WDF_DRIVER_CONFIG_INIT( + &config, + MCompEvtDeviceAdd + ); + + status = WdfDriverCreate(DriverObject, + RegistryPath, + &attributes, + &config, + &hDriver); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDriverCreate failed with %!status!", + status); + WPP_CLEANUP(DriverObject); + } + + return status; +} + +VOID +MCompEvtDriverCleanup( + _In_ WDFOBJECT Driver + ) +/*++ + +Routine Description: + + This routine is invoked when the framework driver object that was created in + DriverEntry is about to be deleted. + +Arguments: + + Driver - Handle to the framework driver object created in DriverEntry + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + WPP_CLEANUP(WdfDriverWdmGetDriverObject((WDFDRIVER) Driver)); +} + +NTSTATUS +InitializePowerFrameworkSettings( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine initializes the power framework helper. + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + PO_FX_DEVICE_MCOMP_EXT poFxDevice; + PPO_FX_DEVICE poFxDevicePtr; + PO_FX_COMPONENT_IDLE_STATE idleState[2]; // F0 and F1 only + ULONG i; + + RtlZeroMemory(&poFxDevice, sizeof(poFxDevice)); + RtlZeroMemory(idleState, sizeof(idleState)); + + // + // Specify callbacks and context + // + poFxDevicePtr = (PPO_FX_DEVICE) &poFxDevice; + poFxDevicePtr->Version = PO_FX_VERSION_V1; + poFxDevicePtr->ComponentIdleStateCallback = + MCompComponentIdleStateCallback; + poFxDevicePtr->ComponentActiveConditionCallback = NULL; + poFxDevicePtr->ComponentIdleConditionCallback = NULL; + poFxDevicePtr->DevicePowerRequiredCallback = NULL; + poFxDevicePtr->DevicePowerNotRequiredCallback = NULL; + poFxDevicePtr->DeviceContext = Device; + poFxDevicePtr->ComponentCount = COMPONENT_COUNT; + + // + // Initialize F-state-related information + // NOTE: We're making the F1 specification be the same for all components, + // but it does not have to be that way. + // + + // + // Transition latency should be 0 for F0 + // + idleState[0].TransitionLatency = 0; + + // + // Residency requirement should be 0 for F0 + // + idleState[0].ResidencyRequirement = 0; + + // + // Nominal power for F0 + // + idleState[0].NominalPower = PO_FX_UNKNOWN_POWER; + + // + // Transition latency for F1 + // + idleState[1].TransitionLatency = TRANSITION_LATENCY_F1; + + // + // Residency requirement for F1 + // + idleState[1].ResidencyRequirement = RESIDENCY_REQUIREMENT_F1; + + // + // Nominal power for F1 + // + idleState[1].NominalPower = PO_FX_UNKNOWN_POWER; + + for (i=0; i < COMPONENT_COUNT; i++) { + // + // We're declaring that each of components support only F0 and F1 + // + poFxDevicePtr->Components[i].IdleStateCount = 2; + + // + // Specify the F-state information + // + poFxDevicePtr->Components[i].IdleStates = idleState; + } + + // + // Provide the power framework settings to the power framework helper + // + status = PfhInitializePowerFrameworkSettings(Device, poFxDevicePtr); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + status = STATUS_SUCCESS; + +exit: + return status; +} + +NTSTATUS +CreateDevice( + _In_ WDFOBJECT PfhInitializer, + _In_ PWDFDEVICE_INIT DeviceInit, + _Out_ WDFDEVICE * Device + ) +/*++ +Routine Description: + + This routine creates the KMDF device object and also initializes the power + framework helper's settings for this device object. + +Arguments: + + PfhInitializer - Handle to the initializer object for initializing the power + framework helper. + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + + Device - Handle to the KMDF device object created by this routine. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttributes; + WDFDEVICE device = NULL; + WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks; + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS s0IdleSettings; + + // + // Register the PNP/power callbacks + // + // Note: This is the function driver for the device, so the framework + // automatically enables power policy ownership. + // + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks); + pnpCallbacks.EvtDeviceD0Entry = MCompEvtDeviceD0Entry; + pnpCallbacks.EvtDeviceD0Exit = MCompEvtDeviceD0Exit; + + // + // Get the power framework helper to override our PNP/power callbacks + // + PfhInterceptWdfPnpPowerEventCallbacks(PfhInitializer, &pnpCallbacks); + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpCallbacks); + + // + // Give the power framework helper an opportunity to register WDM IRP + // preprocess callbacks for power IRPs + // + status = PfhAssignWdmPowerIrpPreProcessCallback( + PfhInitializer, + DeviceInit, + NULL, // EvtDeviceWdmPowerIrpPreprocess + NULL, // MinorFunctions + 0 // NumMinorFunctions + ); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + // + // Specify the context for the device + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, + DEVICE_EXTENSION); + + // + // Create a framework device object + // + status = WdfDeviceCreate(&DeviceInit, &objectAttributes, &device); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceCreate failed with %!status!", + status); + goto exit; + } + + // + // Specify the POHANDLE availability callbacks + // + PfhSetPoHandleAvailabilityCallbacks(PfhInitializer, + MCompPoHandleAvailable, + MCompPoHandleUnavailable); + + // + // Enable S0-idle power management + // + // Note: + // * We set IdleTimeoutType = DriverManagedIdleTimeout to make sure KMDF + // doesn't register with the power framework on our behalf, because we + // will be directly registering with the power framework ourselves. + // * We set PowerUpIdleDeviceOnSystemWake = TRUE because when we register + // with power framework, we'd like to return to D0 immediately after the + // system returns to S0. It is a power framework requirement that once + // the system returns to S0, then device must return to D0 and the + // driver must invoke PoFxReportDevicePoweredOn (the power framework + // helper library does this on our behalf). + // * We pick a really small idle timeout value so that KMDF can power down + // the device almost immediately after the power framework allows it to + // do so. + // + // If S0-idle power management support is not required, then the following + // modifications should be made: + // 1. Recompile the power framework helper library with the value of the + // WDFPOFX_S0IDLE_SUPPORTED compiler switch set to 0. This omits the + // code that is specific to S0-idle power management, thereby reducing + // the size of the library. + // 2. Remove the calls to PfhSetS0IdleConfiguration and + // WdfDeviceAssignS0IdleSettings below. + // + PfhSetS0IdleConfiguration(PfhInitializer, PfhS0IdleSupportedPowerPageable); + + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&s0IdleSettings, + IdleCannotWakeFromS0); + s0IdleSettings.IdleTimeoutType = DriverManagedIdleTimeout; + s0IdleSettings.PowerUpIdleDeviceOnSystemWake = WdfTrue; + s0IdleSettings.IdleTimeout = 1; // 1 millisecond + status = WdfDeviceAssignS0IdleSettings(device, &s0IdleSettings); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceAssignS0IdleSettings failed with %!status!", + status); + goto exit; + } + + // + // Get the power framework helper to initialize its settings for this device + // + status = PfhInitializeDeviceSettings(device, PfhInitializer); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + *Device = device; + status = STATUS_SUCCESS; + +exit: + return status; +} + +NTSTATUS +CreateComponentQueues( + _In_ WDFOBJECT PfhInitializer, + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine creates the component queues and also initializes the power + framework helper's settings for each of them. + +Arguments: + + PfhInitializer - Handle to the initializer object for initializing the power + framework helper. + + Device - Handle to the framework device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + PDEVICE_EXTENSION devExt; + ULONG i; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + + // + // Get the device extension + // + devExt = DeviceGetData(Device); + + for (i=0; i < COMPONENT_COUNT; i++) { + + // + // Create the component-specific queues + // + WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchParallel); + ioQueueConfig.EvtIoDeviceControl = MCompEvtIoDeviceControlSecondary; + + // + // Get the power framework helper to override the configuration for our + // component-specific queue + // + PfhInterceptComponentQueueConfig(PfhInitializer, &ioQueueConfig); + + // + // Associate the queue with a component + // + PfhSetComponentForComponentQueue(PfhInitializer, i); + + // + // Create the queue + // + + // + // By default, Static Driver Verifier (SDV) displays a warning if it + // doesn't find the EvtIoStop callback on a power-managed queue. + // The 'assume' below causes SDV to suppress this warning. If the driver + // has not explicitly set PowerManaged to WdfFalse, the framework creates + // power-managed queues when the device is not a filter driver. Normally + // the EvtIoStop is required for power-managed queues, but for this driver + // it is not needed b/c the driver doesn't hold on to the requests or + // forward them to other drivers. This driver completes the requests + // directly in the queue's handlers. If the EvtIoStop callback is not + // implemented, the framework waits for all driver-owned requests to be + // done before moving in the Dx/sleep states or before removing the + // device, which is the correct behavior for this type of driver. + // If the requests were taking an indeterminate amount of time to complete, + // or if the driver forwarded the requests to a lower driver/another stack, + // the queue should have an EvtIoStop/EvtIoResume. + // + __analysis_assume(ioQueueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(Device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &(devExt->Queue[i])); + __analysis_assume(ioQueueConfig.EvtIoStop == 0); + + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfIoQueueCreate failed with %!status! when " + "creating queue for component %d", + status, + i); + goto exit; + } + + // + // Get the power framework helper to initialize its settings for this + // component-specific queue + // + status = PfhInitializeComponentQueueSettings(devExt->Queue[i], + PfhInitializer); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + } + + status = STATUS_SUCCESS; + +exit: + return status; +} + +NTSTATUS +MCompEvtDeviceAdd( + _In_ WDFDRIVER Driver, + _Inout_ PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + EvtDeviceAdd is called by the framework in response to AddDevice call from + the PnP manager. + +Arguments: + + Driver - Handle to the framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + WDFOBJECT pfhInitializer = NULL; + WDFDEVICE device = NULL; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + WDFQUEUE sourceQueue = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(Driver); + + // + // Create an initializer object for the power framework helper + // + status = PfhInitializerCreate(&pfhInitializer); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + // + // Create the device object + // + status = CreateDevice(pfhInitializer, DeviceInit, &device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + // + // Create the source queue. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, + WdfIoQueueDispatchParallel); + ioQueueConfig.EvtIoDeviceControl = MCompEvtIoDeviceControlPrimary; + + // + // By default, Static Driver Verifier (SDV) displays a warning if it + // doesn't find the EvtIoStop callback on a power-managed queue. + // The 'assume' below causes SDV to suppress this warning. If the driver + // has not explicitly set PowerManaged to WdfFalse, the framework creates + // power-managed queues when the device is not a filter driver. Normally + // the EvtIoStop is required for power-managed queues, but for this driver + // it is not needed b/c the driver doesn't hold on to the requests or + // forward them to other drivers. This driver completes the requests + // directly in the queue's handlers. If the EvtIoStop callback is not + // implemented, the framework waits for all driver-owned requests to be + // done before moving in the Dx/sleep states or before removing the + // device, which is the correct behavior for this type of driver. + // If the requests were taking an indeterminate amount of time to complete, + // or if the driver forwarded the requests to a lower driver/another stack, + // the queue should have an EvtIoStop/EvtIoResume. + // + __analysis_assume(ioQueueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &sourceQueue); + __analysis_assume(ioQueueConfig.EvtIoStop == 0); + + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfIoQueueCreate failed with %!status! when creating " + "source queue.", + status); + goto exit; + } + + // + // Initialize power framework settings + // + status = InitializePowerFrameworkSettings(device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + // + // Create the component queues + // + status = CreateComponentQueues(pfhInitializer, device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + // + // Create a device interface so that applications can open a handle to this + // device. + // + status = WdfDeviceCreateDeviceInterface(device, + &GUID_DEVINTERFACE_POWERFX, + NULL /* ReferenceString */); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceCreateDeviceInterface failed with %!status!.", + status); + goto exit; + } + + // + // Initialize the hardware simulator + // + status = HwSimInitialize(device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + status = STATUS_SUCCESS; + +exit: + if (NULL != pfhInitializer) { + WdfObjectDelete(pfhInitializer); + } + return status; +} + +NTSTATUS +MCompPoHandleAvailable( + _In_ WDFDEVICE Device, + _In_ POHANDLE PoHandle + ) +{ + PDEVICE_EXTENSION devExt = NULL; + ULONG i; + + // + // Get the device extension + // + devExt = DeviceGetData(Device); + + // + // Save the POHANDLE + // + devExt->PoHandle = PoHandle; + + // + // Provide latency and residency hints to enable the power framework to + // select lower-powered F-states when we are idle. + // + for (i=0; i < COMPONENT_COUNT; i++) { + PoFxSetComponentLatency( + PoHandle, + i, + (TRANSITION_LATENCY_F1 + 1) + ); + PoFxSetComponentResidency( + PoHandle, + i, + (RESIDENCY_REQUIREMENT_F1 + 1) + ); + } + + return STATUS_SUCCESS; +} + +VOID +MCompPoHandleUnavailable( + _In_ WDFDEVICE Device, + _In_ POHANDLE PoHandle + ) +{ + PDEVICE_EXTENSION devExt = NULL; + + // + // Get the device extension + // + devExt = DeviceGetData(Device); + + // + // Clear the POHANDLE. It is not guaranteed to remain valid after we return + // from this callback. + // + devExt->PoHandle = PoHandle; + + return; +} + +NTSTATUS +MCompEvtDeviceD0Entry( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousState + ) +/*++ +Routine Description: + + In this routine the driver enters D0 + +Arguments: + + Device - Handle to the framework device object + + PreviousState - Previous device power state + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + UNREFERENCED_PARAMETER(PreviousState); + + HwSimD0Entry(Device); + + return STATUS_SUCCESS; +} + +NTSTATUS +MCompEvtDeviceD0Exit( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) +/*++ +Routine Description: + + In this routine the driver leaves D0 + +Arguments: + + Device - Handle to the framework device object + + TargetState - Device power state that the device is about to enter + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER(TargetState); + + HwSimD0Exit(Device); + + return STATUS_SUCCESS; +} + +VOID +MCompComponentIdleStateCallback( + _In_ PVOID Context, + _In_ ULONG Component, + _In_ ULONG State + ) +/*++ +Routine Description: + + The power framework helper invokes this routine to change the F-state of one + of our components. + +Arguments: + + Context - Context that we passed in to our power framework helper module + when we asked it to register with the power framework helper on our behalf + + Component - Index of component for which the F-state change is to be made + + State - The new F-state to transition the component to + +Return Value: + + None + +--*/ +{ + PDEVICE_EXTENSION devExt = NULL; + WDFDEVICE device = NULL; + + // + // Get the device object handle + // + device = (WDFDEVICE) Context; + + // + // Get the device extension + // + devExt = DeviceGetData(device); + + // + // Change the F-state of the component + // This includes hardware-specific operations such as: + // * disabling/enabling DMA capabilities associated with the component + // * disabling/enabling interrupts associated with the component + // * saving/restoring component state + // + HwSimFStateChange(device, Component, State); + + // + // F-state transition complete + // + PoFxCompleteIdleState(devExt->PoHandle, Component); + return; +} + +VOID +MCompEvtIoDeviceControlPrimary( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +/*++ +Routine Description: + + This routine is the IOCTL handler for the device's primary queue + +Arguments: + + Queue - Handle to the framework queue object for the primary queue + + Request - Handle to the framework request object for IOCTL being dispatched + + OutputBufferLength - Output buffer length for IOCTL + + InputBufferLength - Input buffer length for IOCTL + + IoControlCode - IOCTL code + +Return Value: + + None + +--*/ +{ + NTSTATUS status; + PPOWERFX_READ_COMPONENT_INPUT inputBuffer = NULL; + WDFDEVICE device = NULL; + PDEVICE_EXTENSION devExt = NULL; + WDFQUEUE componentQueue = NULL; + ULONG component; + + UNREFERENCED_PARAMETER(OutputBufferLength); + + switch (IoControlCode) { + case IOCTL_POWERFX_READ_COMPONENT: + { + // + // Validate input buffer length + // + if (InputBufferLength != sizeof(*inputBuffer)) { + status = STATUS_INVALID_PARAMETER; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! -Invalid input buffer size. Expected: %d. " + "Actual: %I64u. %!status!.", + sizeof(*inputBuffer), + InputBufferLength, + status); + goto exit; + } + + // + // Identify the component that we need to read in order to satisfy + // this request + // + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(*inputBuffer), + (PVOID*) &inputBuffer, + NULL // Length + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfRequestRetrieveInputBuffer failed with " + "%!status!.", + status); + goto exit; + } + component = inputBuffer->ComponentNumber; + + // + // Identify the queue corresponding to the above component + // + device = WdfIoQueueGetDevice(Queue); + devExt = DeviceGetData(device); + componentQueue = devExt->Queue[component]; + } + break; + + default: + { + status = STATUS_INVALID_PARAMETER; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unexpected IOCTL code %d. %!status!.", + IoControlCode, + status); + goto exit; + } + } + + // + // Forward the request to the appropriate queue. We do this via the power + // framework helper so that it can take a power reference on behalf of the + // request. + // + PfhForwardRequestToQueue(Request, componentQueue); + + status = STATUS_SUCCESS; + +exit: + if (FALSE == NT_SUCCESS(status)) { + // + // Complete the request in case of failure. In case of success, we would + // have dispatched the request to the component-specific queue, so we + // don't need to complete it here. + // + PfhCompleteRequest(Request, status, 0 /* Information */); + } + + return; +} + +VOID +MCompEvtIoDeviceControlSecondary( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +/*++ +Routine Description: + + This routine is the IOCTL handler for the device's secondary queue (i.e. + component-specific queue). + +Arguments: + + Queue - Handle to the framework queue object for the secondary queue + + Request - Handle to the framework request object for IOCTL being dispatched + + OutputBufferLength - Output buffer length for IOCTL + + InputBufferLength - Input buffer length for IOCTL + + IoControlCode - IOCTL code + +Return Value: + + None + +--*/ +{ + NTSTATUS status; + PPOWERFX_READ_COMPONENT_INPUT inputBuffer = NULL; + PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer = NULL; + WDFDEVICE device = NULL; + ULONG component; + ULONG componentData; + ULONG_PTR information = 0; + + // + // When we complete the request, make sure we don't get the I/O manager to + // copy any more data to the client address space than what we write to the + // output buffer. The only data that we write to the output buffer is the + // component data and the C_ASSERT below ensures that the output buffer does + // not have room to contain anything other than that. + // + C_ASSERT(sizeof(componentData) == sizeof(*outputBuffer)); + + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(IoControlCode); + + // + // The following ASSERTMSG checks should have already been made by the IOCTL + // dispatch routine for the primary queue. That routine should have + // completed the request with failure if the checks had failed. Therefore we + // only use ASSERTMSG here. + // + ASSERTMSG("Unexpected IOCTL code\n", + IOCTL_POWERFX_READ_COMPONENT == IoControlCode); + ASSERTMSG("Invalid input buffer size", + InputBufferLength == sizeof(*inputBuffer)); + + // + // Validate output buffer length + // + if (OutputBufferLength != sizeof(*outputBuffer)) { + status = STATUS_INVALID_PARAMETER; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! -Invalid input buffer size. Expected: %d. Actual: %I64u." + " %!status!.", + sizeof(*outputBuffer), + OutputBufferLength, + status); + goto exit; + } + + // + // Identify the component that we need to read in order to satisfy + // this request. + // + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(*inputBuffer), + (PVOID*) &inputBuffer, + NULL // Length + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfRequestRetrieveInputBuffer failed with %!status!.", + status); + goto exit; + } + component = inputBuffer->ComponentNumber; + + // + // Get the output buffer + // + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof(*outputBuffer), + (PVOID*) &outputBuffer, + NULL // Length + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfRequestRetrieveOutputBuffer failed with %!status!.", + status); + goto exit; + } + + // + // Read the data from the component + // + device = WdfIoQueueGetDevice(Queue); + componentData = HwSimReadComponent(device, component); + outputBuffer->ComponentData = componentData; + information = sizeof(*outputBuffer); + + status = STATUS_SUCCESS; + +exit: + // + // Complete the request + // + PfhCompleteRequest(Request, status, information); + return; +} diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.h b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.h new file mode 100644 index 00000000..0d56edc8 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.h @@ -0,0 +1,114 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + WdfMultiComp.h + +Abstract: + Header file for the KMDF sample driver for a multi-component device. + +Environment: + + Kernel mode + +--*/ + +#if !defined(_WDFMULTICOMP_H_) +#define _WDFMULTICOMP_H_ + + +#include +#include +#include "AppInterface.h" +#include "WdfPoFx.h" + +// +// Some dummy values +// +#define TRANSITION_LATENCY_F1 WDF_ABS_TIMEOUT_IN_MS(5) // 5 milliseconds +#define RESIDENCY_REQUIREMENT_F1 WDF_ABS_TIMEOUT_IN_MS(50) // 50 milliseconds + +// +// This structure contains the driver's power framework settings +// +typedef struct _PO_FX_DEVICE_MCOMP_EXT { + PO_FX_DEVICE PoFxDevice; + PO_FX_COMPONENT AdditionalComponents[COMPONENT_COUNT-1]; +} PO_FX_DEVICE_MCOMP_EXT, *PPO_FX_DEVICE_MCOMP_EXT; + +// +// This structure represents the driver's device context space +// +typedef struct _DEVICE_EXTENSION { + // + // Array of component queues + // + WDFQUEUE Queue[COMPONENT_COUNT]; + + // + // POHANDLE representing the device's registration with the power framework + // + POHANDLE PoHandle; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, DeviceGetData) + +// +// Driver's KMDF callbacks +// +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_OBJECT_CONTEXT_CLEANUP MCompEvtDriverCleanup; +EVT_WDF_DRIVER_DEVICE_ADD MCompEvtDeviceAdd; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL MCompEvtIoDeviceControlPrimary; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL MCompEvtIoDeviceControlSecondary; +EVT_WDF_DEVICE_D0_ENTRY MCompEvtDeviceD0Entry; +EVT_WDF_DEVICE_D0_EXIT MCompEvtDeviceD0Exit; + +// +// Driver's power framework callbacks +// +PO_FX_COMPONENT_IDLE_STATE_CALLBACK MCompComponentIdleStateCallback; + +// +// Driver's power framework helper callbacks +// +PFH_CALLBACK_POHANDLE_AVAILABLE MCompPoHandleAvailable; +PFH_CALLBACK_POHANDLE_UNAVAILABLE MCompPoHandleUnavailable; + +// +// Define the tracing flags. +// +// TODO: Use a different trace control GUID below +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + WdfMCompTraceControl, (C3061C7C,5719,4700,BD4D,A12B40347632), \ + WPP_DEFINE_BIT(WDFMCOMP_ALL_INFO) \ + ) + +#define WPP_FLAG_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAG_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC Trace{FLAG=WDFMCOMP_ALL_INFO}(LEVEL, MSG, ...); +// end_wpp +// + + +#endif // _WDFMULTICOMP_H_ diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx new file mode 100644 index 00000000..e6b93ed0 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx @@ -0,0 +1,79 @@ +; +; WdfMultiComp.inf +; + +[Version] +Signature="$Windows NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFT% +CatalogFile=KmdfSamples.cat +DriverVer=03/27/2011,0.0.0.1 + +[DestinationDirs] +WdfMultiCompDriverCopy=12 ; copy to drivers +CoInstaller_CopyFiles = 11; copy to system32 + +[SourceDisksNames] +1 = %MediaDescription% + +[SourceDisksFiles] +WdfMultiComp.sys=1 +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 + +; =================== Class install ================================== + +[ClassInstall32] +AddReg=SampleClassReg + +[SampleClassReg] +HKR,,,,%ClassName% +HKR,,Icon,,-5 + +; =================== KMDF Multi-Component Device ================================== + +[Manufacturer] +%MSFT%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%WdfMultiCompDeviceName%=WdfMultiComp_Install,WDF\WdfMultiComp + +[WdfMultiComp_Install.NT] +CopyFiles=WdfMultiCompDriverCopy + +[WdfMultiCompDriverCopy] +WdfMultiComp.sys + +[WdfMultiComp_Install.NT.Services] +AddService=WdfMultiComp,0x000001fa,WdfMultiComp_ServiceInstall + +[WdfMultiComp_ServiceInstall] +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\WdfMultiComp.sys + +[WdfMultiComp_Install.NT.CoInstallers] +AddReg=CoInstaller_AddReg +CopyFiles=CoInstaller_CopyFiles + +[CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[WdfMultiComp_Install.NT.Wdf] +KmdfService = WdfMultiComp, WdfMultiComp_wdfsect + +[WdfMultiComp_wdfsect] +KmdfLibraryVersion=$KMDFVERSION$ + +; =================== Generic ================================== + +[Strings] +MSFT="Microsoft" +MediaDescription="KMDF Multi-Component Device Driver Installation Media" +ClassName="Sample Device" +WdfMultiCompDeviceName="KMDF Multi-Component Device" + diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj new file mode 100644 index 00000000..c0905b5a --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {45DBDC5F-2109-432B-8149-29D3693D57E1} + $(MSBuildProjectName) + 1 + Debug + Win32 + {5F84F53D-DFA0-47A1-9759-FC7CFFC9599C} + + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + WdfMultiComp.h + + + $(InfArch) + true + .\$(IntDir)\WdfMultiComp.inf + + + + WdfMultiComp + + + WdfMultiComp + + + WdfMultiComp + + + WdfMultiComp + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + %(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + %(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + %(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + + + %(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters new file mode 100644 index 00000000..dfe3aac9 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters @@ -0,0 +1,37 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {A5B06A84-D70F-4D83-B76D-FEA4F09EE4FC} + + + h;hpp;hxx;hm;inl;inc;xsd + {B738D047-A23C-43C1-9D52-3E0AC54B5C3D} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9A02D86B-B4BE-4C57-BD3E-ADD87EE33EE9} + + + inf;inv;inx;mof;mc; + {000E9CB4-5A60-438C-8F77-72AE7E890BB8} + + + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + \ No newline at end of file diff --git a/pofx/WDF/Driver/MultiComp/inc/WdfPoFx.h b/pofx/WDF/Driver/MultiComp/inc/WdfPoFx.h new file mode 100644 index 00000000..bcecdc86 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/inc/WdfPoFx.h @@ -0,0 +1,731 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + WdfPoFx.h + +Abstract: + This header file describes the interface between the driver layer and the + power framework helper library. + +Environment: + + Kernel mode + +--*/ +#if !defined(_WDFPOFX_H_) +#define _WDFPOFX_H_ + +#include +#include + +// +// Routine description: +// This routine is invoked by the power framework helper library to notify +// the driver layer that it has registered with the power framework. The +// driver layer can provide this callback to the power framework helper +// library by calling PfhSetPoHandleAvailabilityCallbacks. The power +// framework helper library invokes this routine during the processing of +// the first start IRP for the device. (Note that the device might receive +// more than one start IRP if a resource rebalance occurs while it is +// operational). +// +// Arguments: +// Device - Handle to the KMDF device object +// +// PoHandle - Handle that represents the device's registration with the +// power framework. +// +// Return value: +// An NTSTATUS value representing success or failure of the function. If +// the driver layer returns an NTSTATUS value representing failure, the +// power framework helper library immediately unregisters with the power +// framework and fails the start IRP. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PFH_CALLBACK_POHANDLE_AVAILABLE( + _In_ WDFDEVICE Device, + _In_ POHANDLE PoHandle + ); + +typedef PFH_CALLBACK_POHANDLE_AVAILABLE *PPFH_CALLBACK_POHANDLE_AVAILABLE; + +// +// Routine description: +// This routine is invoked by the power framework helper library to notify +// the driver layer that it is about to unregister with the power +// framework. The driver layer can provide this callback to the power +// framework helper library by calling +// PfhSetPoHandleAvailabilityCallbacks. The POHANDLE representing the +// device's registration with the power framework is not guaranteed to be +// valid after the driver layer has returned from this routine. Therefore, +// the driver layer must stop using the POHANDLE before it returns from +// this routine. +// +// Arguments: +// Device - Handle to the KMDF device object +// +// PoHandle - Handle that represents the device's registration with the +// power framework. +// +// Return value: +// None +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PFH_CALLBACK_POHANDLE_UNAVAILABLE( + _In_ WDFDEVICE Device, + _In_ POHANDLE PoHandle + ); + +typedef PFH_CALLBACK_POHANDLE_UNAVAILABLE *PPFH_CALLBACK_POHANDLE_UNAVAILABLE; + +// +// PfhInitializerCreate +// ==================== +// Routine description: +// This routine is invoked by the driver layer to create a power framework +// helper library initializer object. This initializer object helps the +// power framework library to initialize its own settings for KMDF objects. +// After creating the initializer object, the driver layer passes it in +// as an argument to several initialization methods in the power framework +// helper library. +// +// If the driver layer calls any of the following methods when creating a +// KMDF device object, the initializer object needs to be passed in as an +// argument: +// PfhAssignWdmPowerIrpPreProcessCallback +// PfhInterceptWdfPnpPowerEventCallbacks +// PfhSetPoHandleAvailabilityCallbacks +// PfhInitializeDeviceSettings +// +// If the driver layer calls any of the following methods when creating a +// KMDF queue object, the initializer object needs to be passed in as an +// argument: +// PfhInterceptComponentQueueConfig +// PfhSetComponentForComponentQueue +// PfhInitializeComponentQueueSettings +// +// The same initializer object can be used to initialize more than one KMDF +// object, but at any given time it must be used to initialize only one +// KMDF object. For example, the driver layer could use an initializer +// object to initialize a KMDF device object and when it is done +// initializing the device object, the same object can be used to +// initialize a different KMDF object (not necessarily a device object). +// +// The initializer object is a child of the KMDF driver object and is +// deleted by KMDF when the driver object is deleted. This means that by +// default the initializer object remains in memory until the driver is +// unloaded. However, the driver layer can proactively delete the +// initializer object using the WdfObjectDelete method when it has finished +// using it. +// +// Arguments: +// Initializer - Pointer to a location that receives a handle to the new +// initializer object. +// +// Return value: +// An NTSTATUS value representing success or failure of the function. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhInitializerCreate( + _Out_ WDFOBJECT * Initializer + ); + +// +// PfhAssignWdmPowerIrpPreProcessCallback +// ====================================== +// Routine description: +// This routine is invoked by the driver layer to enable the power +// framework helper library to register a WDM preprocess callback function +// to handle power IRPs before KMDF has a chance to process them. The +// driver layer must invoke this routine before creating the KMDF device +// object. +// +// This routine must be invoked for any device whose power management is +// done using the power framework helper library, even if the driver layer +// is not interested in preprocessing power IRPs for that device. +// +// If the driver layer also wishes to pre-process power IRPs before KMDF +// has a chance to preprocess them, it can specify this via the arguments +// to this routine. The driver layer must *not* directly call the +// WdfDeviceInitAssignWdmIrpPreprocessCallback method to register a pre- +// process callback routine for power IRPs, i.e. IRPs with a major function +// code of IRP_MJ_POWER. Instead it must use +// PfhAssignWdmPowerIrpPreProcessCallback to accomplish this. However, for +// preprocessing IRPs with other major function codes, the driver should +// use WdfDeviceInitAssignWdmIrpPreprocessCallback. +// +// If the driver layer specifies a preprocess callback via +// PfhAssignWdmPowerIrpPreProcessCallback, it gets to preprocess the power +// IRP before the power framework helper library preprocesses it. Once the +// driver layer is done preprocessing the power IRP, it must call +// PfhWdmDispatchPreprocessedPowerIrp to allow the power framework helper +// library and eventually KMDF to process the IRP. The driver layer must +// *not* directly call WdfDeviceWdmDispatchPreprocessedIrp to forward a +// pre-processed power IRP, i.e. an IRP with major function code +// IRP_MJ_POWER, to KMDF. Preprocessed IRPs with other major function codes +// should be forwarded to KMDF via WdfDeviceWdmDispatchPreprocessedIrp. +// +// Arguments: +// Initializer - Handle to the initializer object that is being used to +// initialize the power framework helper library's device object +// settings. +// +// DeviceInit - Pointer to a WDFDEVICE_INIT structure +// +// EvtDeviceWdmPowerIrpPreprocess - Pointer to the driver layer's +// EVT_WDFDEVICE_WDM_IRP_PREPROCESS callback function for power IRPs. +// This argument is optional. If the driver layer does not want to +// preprocess power IRPs, it can specify NULL. +// +// MinorFunctions - Pointer to an array of one or more minor function codes +// for IRP_MJ_POWER that the driver layer wants to pre-process. This +// argument is optional. If the driver layer does not want to +// preprocess power IRPs, it can specify NULL. +// +// NumMinorFunctions - Number of minor function codes that are contained in +// the MinorFunctions array. +// +// Return value: +// An NTSTATUS value representing success or failure of the function. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhAssignWdmPowerIrpPreProcessCallback( + _In_ WDFOBJECT Initializer, + _In_ PWDFDEVICE_INIT DeviceInit, + _In_opt_ PFN_WDFDEVICE_WDM_IRP_PREPROCESS EvtDeviceWdmPowerIrpPreprocess, + _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions, + _In_ ULONG NumMinorFunctions + ); + +// +// PfhWdmDispatchPreprocessedPowerIrp +// ================================== +// Routine description: +// This routine is invoked by the driver layer to forward a pre-processed +// power IRP to the power framework helper library. It is similar to KMDF's +// WdfDeviceWdmDispatchPreprocessedIrp method. For power IRPs, the driver +// layer must use PfhWdmDispatchPreprocessedPowerIrp instead of +// WdfDeviceWdmDispatchPreprocessedIrp. The power framework helper library +// eventually forwards it to KMDF after pre-processing it. +// +// Arguments: +// Device - Handle to a KMDF device object +// +// Irp - Pointer to the pre-processed IRP +// +// Return value: +// An NTSTATUS value that the power framework helper library provides as a +// result of processing the IRP. The driver *must* use this return value as +// return value for its EvtDeviceWdmPowerIrpPreprocess callback function. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PfhWdmDispatchPreprocessedPowerIrp( + _In_ WDFDEVICE Device, + _Inout_ PIRP Irp + ); + +// +// PfhInterceptWdfPnpPowerEventCallbacks +// ===================================== +// Routine description: +// This routine is invoked by the driver layer to enable the power +// framework helper library to register its PNP and power event callbacks +// with KMDF. If the driver layer has specified a callback for a PNP or +// power event that the power framework helper library is also interested +// in, then the power framework helper library saves the driver layer's +// callback and invokes it from when KMDF invokes its own callback. The +// driver layer must call PfhInterceptWdfPnpPowerEventCallbacks after it +// has initialized the WDF_PNPPOWER_EVENT_CALLBACKS structure but before it +// has called WdfDeviceInitSetPnpPowerEventCallbacks. +// +// This routine must be invoked for any device whose power management is +// done using the power framework helper library, even if the driver layer +// does not want to specify any PNP or power event callbacks for that +// device. After invoking this routine, the driver layer must invoke +// WdfDeviceInitSetPnpPowerEventCallbacks so that the power framework +// helper library's callbacks are registered with KMDF. +// +// Arguments: +// Initializer - Handle to the initializer object that is being used to +// initialize the power framework helper library's device object +// settings. +// +// DriverLayerPnpPowerCallbacks - Pointer to a caller-initialized +// WDF_PNPPOWER_EVENT_CALLBACKS structure. +// +// Return value: +// None +// +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PfhInterceptWdfPnpPowerEventCallbacks( + _In_ WDFOBJECT Initializer, + _Inout_ PWDF_PNPPOWER_EVENT_CALLBACKS DriverLayerPnpPowerCallbacks + ); + +// +// PfhSetPoHandleAvailabilityCallbacks +// =================================== +// Routine description: +// This routine is invoked by the driver layer to receive the POHANDLE that +// represents the device's registration with the power framework and to be +// notified when the handle is about to become invalid because of an +// impending unregistration. The POHANDLE received in this manner can be +// used to directly invoke power framework routines. However, the driver +// layer must *not* invoke the following power framework routines because +// they can interfere with the operation of the power framework helper +// library: +// PoFxStartDevicePowerManagement +// PoFxUnregisterDevice +// +// PfhSetPoHandleAvailabilityCallbacks must be invoked before the driver +// layer has created the KMDF device object. The driver layer need not +// invoke this routine if it is not interested in using the POHANDLE +// directly. +// +// Arguments: +// Initializer - Handle to the initializer object that is being used to +// initialize the power framework helper library's device object +// settings. +// +// EvtPfhPoHandleAvailable - Pointer to the driver layer's +// PFH_CALLBACK_POHANDLE_AVAILABLE callback function. +// +// EvtPfhPoHandleUnavailable - Pointer to the driver layer's +// PFH_CALLBACK_POHANDLE_UNAVAILABLE callback function. +// +// Return value: +// None +// +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PfhSetPoHandleAvailabilityCallbacks( + _In_ WDFOBJECT Initializer, + _In_ PPFH_CALLBACK_POHANDLE_AVAILABLE PfhCallbackPoHandleAvailable, + _In_ PPFH_CALLBACK_POHANDLE_UNAVAILABLE PfhCallbackPoHandleUnavailable + ); + +// +// The following enum defines the S0-idle power management configurations that +// are supported by the power framework helper library +// +typedef enum _PFH_S0IDLE_CONFIG { + // + // S0-idle power management is not supported for the device + // + PfhS0IdleNotSupported, + + // + // S0-idle power management is supported for the device and the driver can + // access pageable data during the device's power transitions. This implies + // that the device can *never* be in the paging path. + // + PfhS0IdleSupportedPowerPageable, + + // + // S0-idle power management is supported for the device and the driver + // *cannot* access pageable data during the device's power transitions. This + // implies that the device can be in the paging path. + // + // If this configuration is chosen, the driver layer is responsible for + // supplying a PO_FX_DEVICE_POWER_REQUIRED_CALLBACK callback. This callback + // will be invoked when the power framework instructs the driver to power up + // the device. In this callback, the driver layer must initiate device power + // up. When the device has powered up, the driver layer must call + // PoFxReportDevicePoweredOn to inform the power framework about it. The + // driver layer should accomplish this in the following manner: + // - In the PO_FX_DEVICE_POWER_REQUIRED_CALLBACK callback it should queue + // work to its own worker thread (and not a system worker thread) to + // make a blocking call to WdfDeviceStopIdle. Since this is a blocking + // call, the driver layer cannot make it directly from within + // PO_FX_DEVICE_POWER_REQUIRED_CALLBACK for a couple of reasons. First, + // the PO_FX_DEVICE_POWER_REQUIRED_CALLBACK callback can be invoked at + // dispatch level. Second, the PO_FX_DEVICE_POWER_REQUIRED_CALLBACK + // callback might get invoked in the context of the I/O dispatch + // routine of a power-managed queue and a blocking call to + // WdfDeviceStopIdle in such a context can lead to a deadlock. Hence, + // the blocking call to WdfDeviceStopIdle must be made from a worker + // thread. The driver cannot use a system worker thread because queuing + // a system work item to the system worker thread can cause pageable + // data to be accessed. But by definition, pageable data cannot be + // accessed in the PfhS0IdleSupportedNotPowerPageable configuration. + // Hence the need for a driver-created worker thread. + // - Once the blocking call to WdfDeviceStopIdle has returned, the device + // is guaranteed to have returned to D0 and/or to remain in D0. + // Therefore the driver layer should call PoFxReportDevicePoweedOn at + // this point. If the call to WdfDeviceStopIdle failed, KMDF would have + // already declared the device to be in a failed state and initiated a + // PNP removal. However, even in the failure case, it is necessary to + // call PoFxReportDevicePoweredOn in order to unblock the power + // framework and move it to a consistent state. + // + PfhS0IdleSupportedNotPowerPageable + +} PFH_S0IDLE_CONFIG, *PPFH_S0IDLE_CONFIG; + +// +// PfhSetS0IdleConfiguration +// ========================= +// Routine description: +// This routine is invoked by the driver layer to specify whether or not +// the device supports S0-idle power management. PfhSetS0IdleConfiguration +// must be invoked before the driver layer has created the KMDF device +// object. If the driver layer does not invoke this routine, the power +// framework helper library assumes the device's S0-idle power management +// configuration to be PfhS0IdleNotSupported. +// +// Arguments: +// Initializer - Handle to the initializer object that is being used to +// initialize the power framework helper library's device object +// settings. +// S0IdleConfig - A PFH_S0IDLE_CONFIG value that specifies the device's +// S0-idle power management configuration. +// +// Return value: +// None +// +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PfhSetS0IdleConfiguration( + _In_ WDFOBJECT Initializer, + _In_ PFH_S0IDLE_CONFIG S0IdleConfig + ); + +// +// PfhInitializeDeviceSettings +// =========================== +// Routine description: +// This routine is invoked by the driver layer to enable the power +// framework helper library to initialize the latter's settings for a KMDF +// device object. The driver layer must invoke this routine for any KMDF +// device object whose power management is done using the power framework +// helper library. +// +// This routine must be invoked after the KMDF device object has been +// created and before the initializer object that is being used to +// initialize the device object is reused to initialize some other KMDF +// object. After calling this routine, the initializer object can be used +// for initializing other KMDF objects if needed. +// +// Arguments: +// Device - Handle to the KMDF device object +// +// Initializer - Handle to the initializer object that is being used to +// initialize the power framework helper library's device object +// settings. +// +// Return value: +// An NTSTATUS value representing success or failure of the function. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhInitializeDeviceSettings( + _In_ WDFDEVICE Device, + _In_ WDFOBJECT Initializer + ); + +// +// PfhInitializePowerFrameworkSettings +// =================================== +// Routine description: +// This routine is invoked by the driver layer to specify its power +// framework settings. These settings are used by the power framework +// helper library when it registers with the power framework. +// +// This routine must be invoked before the driver layer's +// EvtDeviceSelfManagedIoInit callback is invoked or from within the driver +// layer's EvtDeviceSelfManagedIoInit callback. Typically, the driver layer +// would invoke this routine from with its EvtDriverDeviceAdd callback or +// from one of the following callbacks that are invoked the first time a +// device is being started: +// EvtDevicePrepareHardware +// EvtDeviceD0Entry +// EvtDeviceD0EntryPostInterruptsEnabled +// EvtDeviceSelfManagedIoInit +// +// The driver layer must not invoke PfhInitializePowerFrameworkSettings +// more than once for a given device. Note that a device might be started +// more than once if a resource rebalance occurs while the device is +// operational. In this case, the EvtDevicePrepareHardware, +// EvtDeviceD0Entry and EvtDeviceD0EntryPostInterruptsEnabled callbacks can +// be invoked more than once. Therefore, if the driver layer chooses to +// call PfhInitializePowerFrameworkSettings from within one of these +// callbacks, it must take care to call it only when the callback is +// invoked for the first time. +// +// Arguments: +// Device - Handle to the KMDF device object +// +// PoFxDeviceInfo - Pointer to a caller-initialized +// PO_FX_DEVICE structure that contains the driver layer's power +// framework settings. +// +// Return value: +// An NTSTATUS value representing success or failure of the function. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhInitializePowerFrameworkSettings( + _In_ WDFDEVICE Device, + _In_ PPO_FX_DEVICE PoFxDeviceInfo + ); + +// +// PfhInterceptComponentQueueConfig +// ================================ +// Routine description: +// This routine is invoked by the driver layer to enable the power +// framework helper library to register its queue callbacks with KMDF. If +// the driver layer has specified a queue callback that the power framework +// helper library is also interested in, then the power framework helper +// library saves the driver layer's callback and invokes it from when KMDF +// invokes its own callback. The driver layer must call +// PfhInterceptComponentQueueConfig after it has initialized the +// WDF_IO_QUEUE_CONFIG structure but before it has created the KMDF queue +// object. +// +// This routine must be invoked for each of the driver layer's component +// queues. Component queues are described in the routine description for +// PfhInitializeComponentQueueSettings. +// +// Arguments: +// Initializer - Handle to the initializer object that is being used to +// initialize the power framework helper library's queue object +// settings. +// +// DriverLayerQueueConfig - Pointer to a caller-initialized +// WDF_IO_QUEUE_CONFIG structure. +// +// Return value: +// None +// +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhInterceptComponentQueueConfig( + _In_ WDFOBJECT Initializer, + _Inout_ PWDF_IO_QUEUE_CONFIG DriverLayerQueueConfig + ); + +// +// PfhSetComponentForComponentQueue +// ================================ +// Routine description: +// This routine is invoked by the driver layer to specify the component +// that is associated with a component queue. The current implementation of +// the power framework helper library does not support associating a queue +// with more than one component, so this routine must be called no more +// than once for a given queue. +// +// This routine must be invoked for each queue that acts as a component +// queue. Component queues are described in the routine description for +// PfhInitializeComponentQueueSettings. +// +// Arguments: +// Initializer - Handle to the initializer object that is being used to +// initialize the power framework helper library's queue object +// settings. +// +// Component - Component associated with the queue. +// +// Return value: +// None +// +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhSetComponentForComponentQueue( + _In_ WDFOBJECT Initializer, + _In_ ULONG Component + ); + +// +// PfhInitializeComponentQueueSettings +// =================================== +// Routine description: +// This routine is invoked by the driver layer to enable the power +// framework helper library to initialize the latter's settings for a KMDF +// queue object. +// +// The driver layer must invoke this routine for any KMDF queue object that +// acts as a component queue. A component queue is a queue whose state is +// tied to the active/idle state for a component. The queue can dispatch +// requests when the component is active and cannot dispatch requests when +// the component is idle. A component queue must be power-managed. The +// driver layer must not explicitly change the state of any of its +// component queues. +// +// This routine must be invoked after the KMDF queue object has been +// created and before the initializer object that is being used to +// initialize the queue object is reused to initialize some other KMDF +// object. After calling this routine, the initializer object can be used +// for initializing other KMDF objects if needed. +// +// Arguments: +// Queue - Handle to the KMDF queue object for the component queue +// +// Initializer - Handle to the initializer object that is being used to +// initialize the power framework helper library's queue object +// settings. +// +// Return value: +// An NTSTATUS value representing success or failure of the function. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PfhInitializeComponentQueueSettings( + _In_ WDFQUEUE Queue, + _In_ WDFOBJECT Initializer + ); + +// +// PfhRegisterDeviceProactive +// ========================== +// Routine description: +// This routine is invoked by the driver layer to in order to proactively +// register with the power framework. By default, the power framework +// helper library registers with the power framework in its +// EvtDeviceSelfManagedIoInit allback, after it has invoked the driver +// layer's EvtDeviceSelfManagedIoInit callback (if present). If the driver +// layer is satisfied with this default behavior, it need not invoke +// PfhRegisterDeviceProactive at all. +// +// However, if the driver layer needs to interact with the power framework +// before the point at which the power framework helper library registers +// by default, it can proactively register by calling +// PfhRegisterDeviceProactive. Before calling PfhRegisterDeviceProactive, +// the driver layer must ensure that it has already provided its power +// framework settings to the power framework helper library by calling +// PfhInitializePowerFrameworkSettings. +// +// The power framework helper library unregisters with the power framework +// helper library during device removal, in the EvtDeviceSelfManagedIoFlush +// callback. +// +// The driver layer must ensure that it does not call +// PfhRegisterDeviceProactive if the device is already registered with +// the power framework. This means that if the driver layer calls +// PfhRegisterDeviceProactive in one of the following callbacks, it needs +// to handle cases where the callback is called more than once. +// EvtDevicePrepareHardware +// EvtDeviceD0Entry +// EvtDeviceD0EntryPostInterruptsEnabled +// +// If the device is being started for the first time or if the device is a +// PDO that is being restarted after being removed, the above callbacks +// would get invoked while the device is not yet registered with the power +// framework. +// +// If the device is being restarted after being stopped for resource +// rebalance, the above callbacks would get invoked while the device is +// already registered with the power framework. +// +// If the device returns to the working state (D0) after being in a low- +// power state (Dx), then EvtDeviceD0Entry and +// EvtDeviceD0EntryPostInterruptsEnabled would get invoked while the device +// is already registered with the power framework (and +// EvtDevicePrepareHardware is not invoked at all). +// +// Therefore, the driver layer must be careful to invoke +// PfhRegisterDeviceProactive only if the device is not already registered +// with the power framework. It can determine whether or not the device is +// already registered by supplying the PFH_CALLBACK_POHANDLE_AVAILABLE and +// PFH_CALLBACK_POHANDLE_UNAVAILABLE callbacks via +// PfhSetPoHandleAvailabilityCallbacks. +// +// Note that even if the driver layer invokes PfhRegisterDeviceProactive +// more than once, it must invoke PfhInitializePowerFrameworkSettings only +// once. The power framework settings that are supplied once get used for +// subsequent registrations as well. +// +// Arguments: +// Device - Handle to the KMDF device object +// +// Return value: +// An NTSTATUS value representing success or failure of the function. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhRegisterDeviceProactive( + _In_ WDFDEVICE Device + ); + +// +// PfhForwardRequestToQueue +// ======================== +// Routine description: +// This routine is invoked by the driver layer to forward a request to an +// I/O queue. It is similar to the WdfRequestForwardToIoQueue method. At a +// minimum, the driver layer must use PfhForwardRequestToQueue instead +// of WdfRequestForwardToIoQueue when forwarding a request to or from a +// component queue. However, the driver layer can use +// PfhForwardRequestToQueue to forward requests between any two queues. +// The driver layer might find it convenient to always use +// PfhForwardRequestToQueue instead of using it only in some places and +// using WdfRequestForwardToIoQueue in others. +// +// Arguments: +// Request - Handle to the KMDF request object for the request that is to +// be forwarded. +// +// Queue - Handle to the KMDF queue object for the destination queue +// +// Return value: +// None +// +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhForwardRequestToQueue( + _In_ WDFREQUEST Request, + _In_ WDFQUEUE Queue + ); + +// +// PfhCompleteRequest +// ================== +// Routine description: +// This routine is invoked by the driver layer to complete a request. It is +// similar to the WdfRequestCompleteWithInformation method. At a minimum, +// the driver layer must use PfhCompleteRequest instead of +// WdfRequestComplete/WdfRequestCompleteWithInformation when it is +// completing a request that is associated with a component queue. However, +// the driver layer can use PfhCompleteRequest to complete any request. +// The driver layer might find it convenient to always use +// PfhCompleteRequest instead of using it only in some places and using +// WdfRequestComplete/WdfRequestCompleteWithInformation in others. +// +// Arguments: +// Request - Handle to the KMDF request object for the request that is to +// be completed. +// +// Status - An NTSTATUS value that is used as the completion status of the +// request. +// +// Information - Driver-defined completion status information for the +// request, such as the number of bytes that were transferred. +// +// Return value: +// None +// +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhCompleteRequest( + _In_ WDFREQUEST Request, + _In_ NTSTATUS Status, + _In_ ULONG_PTR Information + ); + +#endif // _WDFPOFX_H_ diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj new file mode 100644 index 00000000..ec187a39 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj @@ -0,0 +1,183 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {21F832DD-6A4C-4E5F-941E-15BF98CE4137} + $(MSBuildProjectName) + 1 + Debug + Win32 + {568CF874-EF69-46EF-AAE1-E59FAE847FBA} + + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + StaticLibrary + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + StaticLibrary + + + Windows10 + False + Desktop + KMDF + WindowsKernelModeDriver10.0 + StaticLibrary + + + Windows10 + True + Desktop + KMDF + WindowsKernelModeDriver10.0 + StaticLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + WdfPoFxPriv.h + + + + WdfPoFx + + + WdfPoFx + + + WdfPoFx + + + WdfPoFx + + + + true + Level4 + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + true + Level4 + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + true + Level4 + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + true + Level4 + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(PreprocessorDefinitions);PFH_S0IDLE_SUPPORTED=1 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters new file mode 100644 index 00000000..50376050 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters @@ -0,0 +1,35 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {A155D773-8FFA-4E28-9C42-A363BDD03866} + + + h;hpp;hxx;hm;inl;inc;xsd + {7CBF3FE5-FDC1-4483-B02B-EDCEEAF60743} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {D4BDD463-7BCE-4779-856D-4D42D6FBB5E1} + + + inf;inv;inx;mof;mc; + {29D299CC-0D9F-4BB4-8E92-8A1E35E7A85E} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h b/pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h new file mode 100644 index 00000000..6e294572 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFxPriv.h @@ -0,0 +1,382 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + WdfPoFxPriv.h + +Abstract: + Private header file for the power framework helper library. Contains + structures and function headers that are used by various source files in the + library. + +Environment: + + Kernel mode + +--*/ +#if !defined(_WDFPOFXPRIV_H_) +#define _WDFPOFXPRIV_H_ + +#include +#include +#include "WdfPoFx.h" + +#define DO_NOTHING() (0) + +// +// The following structure is used to capture the driver layer's settings for a +// device +// +typedef struct _HELPER_DEVICE_INIT { + // + // KMDF callbacks registered by the driver layer. The power framework helper + // library invokes these callbacks when KMDF invokes that corresponding + // callback that the library has registered. + // + PFN_WDF_DEVICE_SELF_MANAGED_IO_INIT EvtDeviceSelfManagedIoInit; + PFN_WDF_DEVICE_SELF_MANAGED_IO_FLUSH EvtDeviceSelfManagedIoFlush; + PFN_WDF_DEVICE_SELF_MANAGED_IO_RESTART EvtDeviceSelfManagedIoRestart; + PFN_WDF_DEVICE_D0_ENTRY EvtDeviceD0Entry; + + // + // Callbacks that notify the driver layer about registration and + // unregistration with the power framework. The power framework helper + // library invokes these callbacks after registering with the power + // framework and before unregistering from the power framework. + // + PPFH_CALLBACK_POHANDLE_AVAILABLE PfhCallbackPoHandleAvailable; + PPFH_CALLBACK_POHANDLE_UNAVAILABLE PfhCallbackPoHandleUnavailable; + + // + // The driver layer's WDM pre-process callback for power IRPs. The power + // framework helper library invokes this callback when KMDF invokes the + // corresponding callback that the library has registered. + // + PFN_WDFDEVICE_WDM_IRP_PREPROCESS EvtDeviceWdmPowerIrpPreprocess; + + // + // Memory object containing the array of power IRP minor functions that the + // driver layer is interested in preprocessing. + // + WDFMEMORY PowerIrpPreprocessMinorFunctions; + + // + // The following members track the status of the initialization performed by + // the driver layer for a given device. They are used in verification. + // + BOOLEAN PnpPowerEventCallbacksIntercepted; + BOOLEAN PoHandleAvailabilityCallbacksSet; + BOOLEAN PowerIrpPreprocessCallbackAssigned; +#if PFH_S0IDLE_SUPPORTED + BOOLEAN S0IdleConfigSet; + + // + // The driver's S0-idle configuration + // + PFH_S0IDLE_CONFIG S0IdleConfig; +#endif + +} HELPER_DEVICE_INIT, *PHELPER_DEVICE_INIT; + +// +// The following structure is used to capture the driver layer's settings for a +// component queue +// +typedef struct _HELPER_QUEUE_INIT { + // + // Component number of the component that the queue is associated with + // + ULONG Component; + + // + // The driver layer's EvtIoCanceledOnQueue callback for the component queue. + // The power framework helper library invokes this callback when KMDF + // invokes the corresponding callback that the library has registered. + // + PFN_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE EvtIoCanceledOnQueue; + + // + // The following members track the status of the initialization performed by + // the driver layer for a given queue. They are used in verification. + // + BOOLEAN ComponentQueueConfigIntercepted; + BOOLEAN ComponentSet; +} HELPER_QUEUE_INIT, *PHELPER_QUEUE_INIT; + +// +// Enumeration of KMDF objects that can be initialized using the power framework +// helper library's initializer object. +// +typedef enum _HELPER_INIT_TYPE { + HelperInitTypeNone, + HelperInitTypeDevice, + HelperInitTypeQueue, +} HELPER_INIT_TYPE, *PHELPER_INIT_TYPE; + +// +// The following structure represents the object context space for the power +// framework helper library's initializer object. +// +typedef struct _HELPER_INIT { + // + // Type of KMDF object being initialized using the power framework helper + // library's initializer object. + // + HELPER_INIT_TYPE InitType; + union { + // + // Driver layer's device initialization settings + // + HELPER_DEVICE_INIT DeviceInit; + + // + // Driver layer's queue initialization settings + // + HELPER_QUEUE_INIT QueueInit; + } u; +} HELPER_INIT, *PHELPER_INIT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HELPER_INIT, + HelperGetInitContext) + +// +// The following structure represents the array of power IRP minor functions +// that the driver layer is interested in preprocessing +// +#pragma warning(push) +#pragma warning(disable:4200) +typedef struct _POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO { + // + // Number of power IRP minor functions that the driver layer is interested + // in preprocessing + // + ULONG NumMinorFunctions; + + // + // Array of power IRP minor functions that the driver layer is interested in + // preprocessing + // + UCHAR MinorFunctions[]; +} POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO, + *PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO; +#pragma warning(pop) + +// +// Per-component information maintained by the power framework helper library +// +typedef struct _POFX_COMPONENT_INFO { + // + // Queue associated with the component + // + WDFQUEUE Queue; + + // + // Whether or not the component is active + // + BOOLEAN IsActive; +} POFX_COMPONENT_INFO, *PPOFX_COMPONENT_INFO; + +// +// The following structure represents the driver layer's power framework +// callbacks. The power framework helper library invokes these callbacks when +// the power framework invokes the corresponding callbacks that the library has +// registered. +// +typedef struct _POFX_DRIVER_LAYER_POWER_CALLBACKS { + PPO_FX_COMPONENT_IDLE_STATE_CALLBACK ComponentIdleStateCallback; + PPO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK ComponentActiveConditionCallback; + PPO_FX_COMPONENT_IDLE_CONDITION_CALLBACK ComponentIdleConditionCallback; + PPO_FX_DEVICE_POWER_REQUIRED_CALLBACK DevicePowerRequiredCallback; + PPO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK DevicePowerNotRequiredCallback; +} POFX_DRIVER_LAYER_POWER_CALLBACKS, *PPOFX_DRIVER_LAYER_POWER_CALLBACKS; + +// +// The following structure represents the power framework helper library's +// device context space. +// +typedef struct _POFX_DEVICE_CONTEXT { + // + // Pointer to the power framework settings registered by the power framework + // helper library + // + PPO_FX_DEVICE PoFxDeviceInfo; + + // + // Pointer to the driver layer's power framework callbacks + // + POFX_DRIVER_LAYER_POWER_CALLBACKS DriverLayerPoFxCallbacks; + + // + // Pointer to the driver layer's context for power framework callbacks + // + PVOID DriverLayerPoFxContext; + + // + // Pointer to the information that is tracked by the power framework helper + // library for each component + // + PPOFX_COMPONENT_INFO ComponentInfo; + + // + // POHANDLE representing the registration with the power framework + // + POHANDLE PoHandle; + + // + // Array of power IRP minor functions that the driver layer is interested in + // preprocessing. + // + PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO + DriverLayerPowerIrpPreprocessInfo; + + // + // The driver layer's settings for the device + // + HELPER_DEVICE_INIT DeviceInitSettings; + + // + // Whether or not we should call PoFxReportDevicePoweredOn from our next + // EvtDeviceD0Entry callback + // + BOOLEAN ShouldReportDevicePoweredOn; + +#if PFH_S0IDLE_SUPPORTED + // + // Whether or not we invoked WdfDeviceStopIdle during device start so that + // the device remains in D0 until the power framework permits it to go to Dx + // + BOOLEAN StopIdleInvokedOnDeviceStart; + + // + // Work item that is queued in response to device-power-required callback. + // When the work item callback is invoked, we make a blocking call to + // WdfDeviceStopIdle in order to bring the device to D0. This blocking call + // cannot be made within the device-power-required callback because it can + // be invoked at dispatch level. Therefore, we make this call at passive + // level from a work item callback. + // + WDFWORKITEM PowerRequiredWorkItem; +#endif + +} POFX_DEVICE_CONTEXT, *PPOFX_DEVICE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(POFX_DEVICE_CONTEXT, HelperGetDeviceContext) + +// +// The following structure represents the power framework helper library's +// component queue context space. +// +typedef struct _POFX_QUEUE_CONTEXT { + // + // The driver layer's settings for the component queue + // + HELPER_QUEUE_INIT QueueInitSettings; +} POFX_QUEUE_CONTEXT, *PPOFX_QUEUE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(POFX_QUEUE_CONTEXT, HelperGetQueueContext) + +// +// Power framework helper library's KMDF callbacks +// +EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT _PfhEvtSelfManagedIoInit; +EVT_WDF_DEVICE_SELF_MANAGED_IO_FLUSH _PfhEvtSelfManagedIoFlush; +EVT_WDF_DEVICE_SELF_MANAGED_IO_RESTART _PfhEvtSelfManagedIoRestart; + +EVT_WDF_DEVICE_D0_ENTRY _PfhEvtD0Entry; + +EVT_WDFDEVICE_WDM_IRP_PREPROCESS _PfhEvtWdmPowerIrpPreprocess; + +EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE _PfhEvtRequestCanceledOnComponentQueue; + +// +// Power framework helper library's power framework callbacks +// +PO_FX_COMPONENT_IDLE_STATE_CALLBACK _PfhComponentIdleStateCallback; +PO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK _PfhComponentActiveConditionCallback; +PO_FX_COMPONENT_IDLE_CONDITION_CALLBACK _PfhComponentIdleConditionCallback; +PO_FX_DEVICE_POWER_REQUIRED_CALLBACK _PfhDevicePowerRequiredCallback; +PO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK _PfhDevicePowerNotRequiredCallback; + +// +// Routines used by source files within the power framework helper library +// +PHELPER_DEVICE_INIT +GetDeviceInitSettings( + _In_ WDFOBJECT Initializer + ); + +VOID +UnregisterWithPowerFrameworkWorker( + _In_ WDFDEVICE Device + ); + +BOOLEAN +IsDeviceInitialized( + _In_ WDFDEVICE Device + ); + +BOOLEAN +IsQueueInitialized( + _In_ WDFQUEUE Queue + ); + +BOOLEAN +ArePowerFrameworkSettingsAvailable( + _In_ WDFDEVICE Device + ); + +NTSTATUS +CopyAndUpdateMinorFunctionsArray( + _In_ WDFOBJECT Initializer, + _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions, + _In_ ULONG NumMinorFunctions, + _Out_ WDFMEMORY * MinorFunctionsMemory, + _Outptr_result_buffer_(*UpdatedNumMinorFunctions) PUCHAR *UpdatedMinorFunctions, + _Out_ PULONG UpdatedNumMinorFunctions + ); + +NTSTATUS +CopyMinorFunctionsArray( + _In_ WDFDEVICE Device, + _In_ WDFMEMORY SourceMemory, + _Out_ WDFMEMORY * DestinationMemory + ); + +#include "s0idle.h" + +// +// Define the tracing flags. +// +// The (0,0,0,0,0) GUID below is a dummy. The driver layer's GUID is the one +// that gets used. +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + WdfPoFxTraceControl, (0,0,0,0,0), \ + WPP_DEFINE_BIT(WDFPOFX_ALL_INFO) \ + ) + +#define WPP_FLAG_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAG_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC Trace{FLAG=WDFPOFX_ALL_INFO}(LEVEL, MSG, ...); +// end_wpp +// + +#endif // _WDFPOFXPRIV_H_ diff --git a/pofx/WDF/Driver/MultiComp/lib/component.c b/pofx/WDF/Driver/MultiComp/lib/component.c new file mode 100644 index 00000000..b0e55f1f --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/component.c @@ -0,0 +1,562 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + component.c + +Abstract: + This module contains routines that implement power management for a + component of the device + +Environment: + + Kernel mode + +--*/ + +#include "WdfPoFxPriv.h" +#include "component.tmh" + +VOID +AcquirePowerReferenceForRequest( + _In_ WDFQUEUE Queue + ) +/*++ +Routine description: + This routine acquires a power reference on behalf of a request, if the + request is being forwarded to a component queue. If the request is not being + forwarded to a component queue, then this routine does nothing. + +Arguments: + Queue - Handle to the KMDF queue object to which the request is being + forwarded + +Return value: + None +--*/ +{ + PPOFX_QUEUE_CONTEXT qCtx = NULL; + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + ULONG component; + + // + // Get the queue context + // + qCtx = HelperGetQueueContext(Queue); + if (NULL == qCtx) { + // + // The request is not being forwarded to a component queue. So there is + // no need to take a power reference at this time. + // + return; + } + + // + // Get the component on which the power reference needs to be taken + // + component = qCtx->QueueInitSettings.Component; + + // + // Get the device that the queue is associated with + // + device = WdfIoQueueGetDevice(Queue); + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Take the power reference + // + PoFxActivateComponent(devCtx->PoHandle, component, 0 /* Flags */); + return; +} + +VOID +ReleasePowerReferenceForRequest( + _In_ WDFREQUEST Request + ) +/*++ +Routine description: + This routine releases a power reference on behalf of a request, if a power + reference had been taken previously. If no power reference had been taken + previously for this request, then this routine does nothing. + +Arguments: + Request - Handle to the KMDF request object on behalf of which the power + reference needs to be released + +Return value: + None +--*/ +{ + WDFQUEUE queue = NULL; + PPOFX_QUEUE_CONTEXT qCtx = NULL; + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + ULONG component; + + // + // Get the queue that the request is associated with + // + queue = WdfRequestGetIoQueue(Request); + if (NULL == queue) { + return; + } + + // + // Get the queue context + // + qCtx = HelperGetQueueContext(queue); + if (NULL == qCtx) { + // + // The request is currently not associated with a component queue. So + // no power reference has been taken on behalf of the request and hence + // there is no power reference to release at this time. + // + return; + } + + // + // Get the component on which the power reference has been taken on behalf + // of this request + // + component = qCtx->QueueInitSettings.Component; + + // + // Get the device that the queue is associated with + // + device = WdfIoQueueGetDevice(queue); + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Release the power reference that was previously taken + // + PoFxIdleComponent(devCtx->PoHandle, component, 0 /* Flags */); + + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhForwardRequestToQueue( + _In_ WDFREQUEST Request, + _In_ WDFQUEUE Queue + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + + // + // If the request currently has a power reference on a component, then + // release the power reference. + // + ReleasePowerReferenceForRequest(Request); + + // + // If the destination queue requires a power reference on a component, then + // take the power reference now. + // + AcquirePowerReferenceForRequest(Queue); + + // + // Queue the request to the caller-specified queue + // + status = WdfRequestForwardToIoQueue(Request, Queue); + if (FALSE == NT_SUCCESS(status)) { + // + // If the queue has not been purged or drained, forwarding a request to + // a queue should always succeeded. + // + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfRequestForwardToIoQueue failed with %!status!.", + status); + WdfVerifierDbgBreakPoint(); + } + + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhCompleteRequest( + _In_ WDFREQUEST Request, + _In_ NTSTATUS Status, + _In_ ULONG_PTR Information + ) +// See comments in WdfPoFx.h +{ + // + // If the request currently has a power reference on a component, then + // release the power reference. + // + ReleasePowerReferenceForRequest(Request); + + // + // Complete the request + // + WdfRequestCompleteWithInformation(Request, Status, Information); +} + +VOID +_PfhEvtRequestCanceledOnComponentQueue( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request + ) +/*++ +Routine Description: + + This routine is invoked when an IO request is canceled while it is in a + component queue + +Arguments: + + Queue - Handle to the framework queue object for the component queue + + Request - Handle to the framework request object for the request being + canceled + +Return Value: + + None + +--*/ +{ + PPOFX_QUEUE_CONTEXT qCtx = NULL; + PFN_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE evtIoCanceledOnQueue; + + if (FALSE == IsQueueInitialized(Queue)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializeComponentQueueSettings has not yet been " + "called for WDFQUEUE %p.", + Queue); + WdfVerifierDbgBreakPoint(); + } + + // + // Get the queue context + // + qCtx = HelperGetQueueContext(Queue); + + evtIoCanceledOnQueue = qCtx->QueueInitSettings.EvtIoCanceledOnQueue; + if (NULL != evtIoCanceledOnQueue) { + // + // The driver layer supplied a canceled-on-queue callback, so invoke it + // + evtIoCanceledOnQueue(Queue, Request); + + } else { + // + // Just complete the request + // + PfhCompleteRequest(Request, STATUS_CANCELLED, 0 /* Information */); + } + return; +} + +VOID +ComponentActive( + _In_ PPOFX_DEVICE_CONTEXT DevCtx, + _In_ ULONG Component + ) +/*++ +Routine Description: + + In this routine, we perform operations that are needed when a component + becomes active. + +Arguments: + + DevCtx - Pointer to our context space for the device object + + Component - The component that has become active + +Return Value: + + None + +--*/ +{ + // + // Mark the component as active + // + DevCtx->ComponentInfo[Component].IsActive = TRUE; + + if (NULL != DevCtx->ComponentInfo[Component].Queue) { + // + // Start the component-specific queue + // + WdfIoQueueStart(DevCtx->ComponentInfo[Component].Queue); + } + + return; +} + +VOID +ComponentIdle( + _In_ PPOFX_DEVICE_CONTEXT DevCtx, + _In_ ULONG Component + ) +/*++ +Routine Description: + + In this routine, we perform operations that are needed when a component + becomes idle. + +Arguments: + + DevCtx - Pointer to our context space for the device object + + Component - The component that has become idle + +Return Value: + + None + +--*/ +{ + ULONG queueRequests; + + // + // Mark the component as idle + // + DevCtx->ComponentInfo[Component].IsActive = FALSE; + + if (NULL != DevCtx->ComponentInfo[Component].Queue) { + // + // NOTE: Given that the component is idle, there shouldn't be any + // outstanding requests sitting in the component queue. There may be + // some driver-owned requests, but they would all have dropped their + // power references by now and would be about to complete. We also know + // that no new requests will get added to the queue while this callback + // is running. The reason is described below. + // + // We do not add any new request to this queue until the + // PoFxActivateComponent call for that request has returned. If + // PoFxActivateComponent is called on another thread while this callback + // is running or about to run, then the power framework makes sure + // that PoFxActivateComponent does not return until this callback has + // finished running. Therefore we know that no new request would get + // added to this queue while we are attempting to stop the queue + // within this callback. + // + WdfIoQueueGetState(DevCtx->ComponentInfo[Component].Queue, + &queueRequests, + NULL /* DriverRequests */); + if (0 != queueRequests) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Expected the queue for component %d to have no " + "requests, but there are currently %d requests in the queue.", + Component, + queueRequests); + WdfVerifierDbgBreakPoint(); + } + + // + // Stop the component-specific queue. + // + // No need for synchronous stop. Given that the component is idle, + // there anyway shouldn't be any outstanding requests in sitting in + // the queue. As explained above, there can be up to one outstanding + // request that is delivered to the driver layer, but that would + // also be just about to complete. We stop this queue only to + // prevent new requests that get added to the queue from being + // dispatched to the driver layer until the component becomes active + // again. + // + WdfIoQueueStop(DevCtx->ComponentInfo[Component].Queue, + NULL, // StopComplete + NULL // Context + ); + } + + return; +} + +VOID +_PfhComponentActiveConditionCallback( + _In_ PVOID Context, + _In_ ULONG Component + ) +/*++ +Routine Description: + + The power framework invokes this routine to notify us that one of our + components has become active. + +Arguments: + + Context - Context that we passed in to the power framework + + Component - Index of component that has become active or idle + +Return Value: + + None + +--*/ +{ + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + PPO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK + componentActiveConditionCallback = NULL; + + // + // Get the handle to the framework device object + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Component is active + // + ComponentActive(devCtx, Component); + + // + // If the driver layer supplied a component-active-condition callback, + // invoke it + // + componentActiveConditionCallback = + devCtx->DriverLayerPoFxCallbacks.ComponentActiveConditionCallback; + if (NULL != componentActiveConditionCallback) { + componentActiveConditionCallback(devCtx->DriverLayerPoFxContext, + Component); + } + + return; +} + +VOID +_PfhComponentIdleConditionCallback( + _In_ PVOID Context, + _In_ ULONG Component + ) +/*++ +Routine Description: + + The power framework invokes this routine to notify us that one of our + components has become idle. + +Arguments: + + Context - Context that we passed in to the power framework + + Component - Index of component that has become active or idle + +Return Value: + + None + +--*/ +{ + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + PPO_FX_COMPONENT_IDLE_CONDITION_CALLBACK + componentIdleConditionCallback = NULL; + + // + // Get the handle to the framework device object + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Component is idle + // + ComponentIdle(devCtx, Component); + + // + // If the driver layer supplied a component-idle-condition callback, invoke + // it + // + componentIdleConditionCallback = + devCtx->DriverLayerPoFxCallbacks.ComponentIdleConditionCallback; + if (NULL != componentIdleConditionCallback) { + componentIdleConditionCallback(devCtx->DriverLayerPoFxContext, + Component); + } else { + // + // Complete the transition to idle + // + PoFxCompleteIdleCondition(devCtx->PoHandle, Component); + } + + return; +} + +VOID +_PfhComponentIdleStateCallback( + _In_ PVOID Context, + _In_ ULONG Component, + _In_ ULONG State + ) +/*++ +Routine Description: + + The power framework invokes this routine to change the F-state of one of our + components. + +Arguments: + + Context - Context that we passed in to the power framework + + Component - Index of component for which the F-state change is to be made + + State - The new F-state to transition the component to + +Return Value: + + None + +--*/ +{ + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + PPO_FX_COMPONENT_IDLE_STATE_CALLBACK componentIdleStateCallback = NULL; + // + // Get the handle to the framework device object + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // If the driver layer supplied a component-idle-state callback, invoke it + // + componentIdleStateCallback = + devCtx->DriverLayerPoFxCallbacks.ComponentIdleStateCallback; + if (NULL != componentIdleStateCallback) { + componentIdleStateCallback(devCtx->DriverLayerPoFxContext, + Component, + State); + } else { + // + // Complete the idle state transition + // + PoFxCompleteIdleState(devCtx->PoHandle, Component); + } +} diff --git a/pofx/WDF/Driver/MultiComp/lib/device.c b/pofx/WDF/Driver/MultiComp/lib/device.c new file mode 100644 index 00000000..aa17eaea --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/device.c @@ -0,0 +1,868 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + device.c + +Abstract: + This module contains routines that implement power management for the device + +Environment: + + Kernel mode + +--*/ + +#include "WdfPoFxPriv.h" +#include "device.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, _PfhEvtSelfManagedIoInit) +#pragma alloc_text(PAGE, _PfhEvtSelfManagedIoFlush) +#endif + +VOID +UnregisterWithPowerFrameworkWorker( + _In_ WDFDEVICE Device + ) +/*++ +Routine description: + This routine unregisters a device with the power framework. This routine + can be called in EvtDeviceSelfManagedioRestart, which can not be made + pageable, therefore this function should also not be pageable. + +Arguments: + Device - Handle to the KMDF device object + +Return value: + None +--*/ +{ + PPOFX_DEVICE_CONTEXT devCtx; + PPFH_CALLBACK_POHANDLE_UNAVAILABLE pfhCallbackPoHandleUnavailable; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + if (NULL == devCtx->PoHandle) { + // + // We didn't successfully register with the power framework, so nothing + // to do here. + // + return; + } + + // + // Notify the driver layer that the POHANDLE is about to be invalidated + // + pfhCallbackPoHandleUnavailable = + devCtx->DeviceInitSettings.PfhCallbackPoHandleUnavailable; + if (NULL != pfhCallbackPoHandleUnavailable) { + pfhCallbackPoHandleUnavailable(Device, devCtx->PoHandle); + } + + // + // Unregister with the power framework + // + PoFxUnregisterDevice(devCtx->PoHandle); + devCtx->PoHandle = NULL; + + return; +} + +NTSTATUS +RegisterWithPowerFrameworkWorker( + _In_ WDFDEVICE Device + ) +/*++ +Routine description: + This routine registers a device with the power framework. + +Arguments: + Device - Handle to the KMDF device object + +Return value: + An NTSTATUS value representing success or failure of the function. +--*/ +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx; + PPFH_CALLBACK_POHANDLE_AVAILABLE pfhCallbackPoHandleAvailable; + ULONG i; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + if (NULL != devCtx->PoHandle) { + // + // Already registered with power framework, nothing to do here. + // + status = STATUS_SUCCESS; + goto exit; + } + + for (i=0; i < (devCtx->PoFxDeviceInfo->ComponentCount); i++) { + // + // Initially, all components are active + // + devCtx->ComponentInfo[i].IsActive = TRUE; + + // + // For PDOs, we unregister when the device is disabled and re-register + // when the device is re-enabled. Before re-registering, we need to + // start the component queues. They would all be in the purged state + // when the device was disabled. + // NOTE: It is okay to start an already-started queue, so we can handle + // first registration and re-registration in the same way. + // + if (NULL != devCtx->ComponentInfo[i].Queue) { + WdfIoQueueStart(devCtx->ComponentInfo[i].Queue); + } + } + + // + // Register with the power framework + // + status = PoFxRegisterDevice( + WdfDeviceWdmGetPhysicalDevice(Device), + devCtx->PoFxDeviceInfo, + &(devCtx->PoHandle) + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PoFxRegisterDevice failed with %!status!.", + status); + goto exit; + } + + // + // Notify the driver layer that the POHANDLE is available + // + pfhCallbackPoHandleAvailable = + devCtx->DeviceInitSettings.PfhCallbackPoHandleAvailable; + if (NULL != pfhCallbackPoHandleAvailable) { + status = pfhCallbackPoHandleAvailable(Device, devCtx->PoHandle); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + } + + // + // Tell the power framework to start its power management + // + PoFxStartDevicePowerManagement(devCtx->PoHandle); + + status = STATUS_SUCCESS; + +exit: + if (FALSE == NT_SUCCESS(status)) { + // + // If an error occurred, we need to unregister with the power framework. + // Note: the function below handles the case where we haven't registered + // yet. + // + UnregisterWithPowerFrameworkWorker(Device); + } + return status; +} + +NTSTATUS +_PfhEvtSelfManagedIoInit( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + In this routine, we initialize self-managed I/O operations. + +Arguments: + + Device - Handle to the KMDF device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx; + PFN_WDF_DEVICE_SELF_MANAGED_IO_INIT evtDeviceSelfManagedIoInit; + + PAGED_CODE(); + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + // + // If the driver layer supplied a self-managed-IO-init callback, invoke it. + // We need to do this before checking if power framework settings are + // available so that the driver has the opportunity to specify power + // framework settings in the self-managed-IO-init callback. + // + evtDeviceSelfManagedIoInit = + devCtx->DeviceInitSettings.EvtDeviceSelfManagedIoInit; + if (NULL != evtDeviceSelfManagedIoInit) { + status = evtDeviceSelfManagedIoInit(Device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + } + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // If S0-idle power management is supported, prevent the device from + // powering down to Dx due to S0-idle. We will allow the device to power + // down to Dx only after our device-power-not-required callback is invoked. + // + STOP_DEVICE_IDLE_ON_DEVICE_START(status, Device, devCtx, exit); + + // + // Register with the power framework. + // + // The EvtDeviceSelfManagedIoInit callback is called after the first D0 + // entry, but not after subsequent D0 entries. This means that it is not + // called after D0 entries that occur due to S0-idle, system resuming from + // sleep or resource rebalance. Therefore, we register with the power + // framework here. We retain the registration until the device is removed. + // + // Note: The function below handles the case where the driver layer already + // registered with the power framework by calling + // PfhRegisterDeviceProactive. + // + status = RegisterWithPowerFrameworkWorker(Device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + status = STATUS_SUCCESS; + +exit: + return status; +} + +VOID +_PfhEvtSelfManagedIoFlush( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + In this routine, we flush self-managed I/O operations. + +Arguments: + + Device - Handle to the KMDF device object + +Return Value: + + None + +--*/ +{ + PPOFX_DEVICE_CONTEXT devCtx; + PFN_WDF_DEVICE_SELF_MANAGED_IO_FLUSH evtDeviceSelfManagedIoFlush; + + PAGED_CODE(); + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p.", + Device); + WdfVerifierDbgBreakPoint(); + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + if (devCtx->ShouldReportDevicePoweredOn) { + // + // We received an S0 IRP and we were supposed to call + // PoFxReportDevicePoweredOn after entering D0 in response to the S0 + // IRP. But an error occurred that prevented us from entering D0 and + // KMDF has initiated a device teardown as a result of this error. + // Before we attempt to unregister with the power framework, we should + // call PoFxReportDevicePoweredOn in order to bring the power framework + // to a consistent state. + // + devCtx->ShouldReportDevicePoweredOn = FALSE; + PoFxReportDevicePoweredOn(devCtx->PoHandle); + } + + // + // The EvtDeviceSelfManagedIoFlush callback is called when the device is + // being removed. In this callback, we unregister with the power framework. + // Note: the function below handles the case where we did not register + // successfully. + // + UnregisterWithPowerFrameworkWorker(Device); + + // + // Perform tasks specific to S0-idle power management (if enabled) + // + SELF_MANAGED_IO_FLUSH_FOR_S0_IDLE(devCtx); + + // + // If the driver layer supplied a self-managed-IO-flush callback, invoke it + // + evtDeviceSelfManagedIoFlush = + devCtx->DeviceInitSettings.EvtDeviceSelfManagedIoFlush; + if (NULL != evtDeviceSelfManagedIoFlush) { + evtDeviceSelfManagedIoFlush(Device); + } + + return; +} + +BOOLEAN +IsS0Irp( + _In_ PIRP Irp, + _In_ BOOLEAN StackLocationAdjusted + ) +/*++ +Routine description: + This routine determines whether the given IRP is an S0 IRP. + +Arguments: + Irp - Pointer to the IRP + + StackLocationAdjusted - A BOOLEAN value that indicates whether or not the + IRP's stack location has already been adjusted for forwarding to the + next layer in the device stack. If TRUE, it means that the IRP stack + location has already been adjusted and this routine must use the next + IRP stack location. If FALSE, it means that the IRP stack location has + not yet been adjusted and this routine must use the previous IRP stack + location. + +Return value: + TRUE if the IRP is an S0 IRP, FALSE otherwise +--*/ +{ + PIO_STACK_LOCATION stackLoc; + + if (StackLocationAdjusted) { + stackLoc = IoGetNextIrpStackLocation(Irp); + } else { + stackLoc = IoGetCurrentIrpStackLocation(Irp); + } + + if ((IRP_MJ_POWER == stackLoc->MajorFunction) && + (IRP_MN_SET_POWER == stackLoc->MinorFunction) && + (SystemPowerState == stackLoc->Parameters.Power.Type) && + (PowerSystemWorking == stackLoc->Parameters.Power.State.SystemState)) { + + return TRUE; + + } else { + + return FALSE; + } +} + +NTSTATUS +PreprocessIrpAndDispatch( + _In_ WDFDEVICE Device, + _Inout_ PIRP Irp, + _In_ BOOLEAN StackLocationAdjusted + ) +/*++ +Routine description: + This routine pre-processes a power IRP and dispatches it to KMDF. + +Arguments: + Device - Handle to the KMDF device object + + Irp - Pointer to the IRP + + StackLocationAdjusted - A BOOLEAN value that indicates whether or not the + IRP's stack location has already been adjusted for forwarding to the + next layer in the device stack. If TRUE, it means that the IRP stack + location has already been adjusted and this routine must use the next + IRP stack location. If FALSE, it means that the IRP stack location has + not yet been adjusted and this routine must use the previous IRP stack + location. + +Return value: + The NTSTATUS value that is returned from the + WdfDeviceWdmDispatchPreprocessedIrp call made by this routine to dispatch + the IRP to KMDF. +--*/ +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + + if (IsS0Irp(Irp, StackLocationAdjusted)) { + // + // We have received an S0 IRP. The power framework requires that we call + // PoFxReportDevicePoweredOn after we have returned to D0 as a result of + // receiving an S0 IRP. Therefore, we make a note in our device context + // that we need to call PoFxReportDevicePoweredOn from our next + // EvtDeviceD0Entry callback. + // + devCtx = HelperGetDeviceContext(Device); + devCtx->ShouldReportDevicePoweredOn = TRUE; + } + + // + // Forward the IRP for KMDF to handle + // + if (FALSE == StackLocationAdjusted) { + IoSkipCurrentIrpStackLocation(Irp); + } + status = WdfDeviceWdmDispatchPreprocessedIrp(Device, Irp); + + return status; +} + +NTSTATUS +_PfhEvtWdmPowerIrpPreprocess( + _In_ WDFDEVICE Device, + _Inout_ PIRP Irp + ) +/*++ +Routine description: + This routine receives power IRPs before KMDF has processed them. It pre- + processes the power IRP before eventually forwarding it to KMDF. + +Arguments: + Device - Handle to the KMDF device object + + Irp - Pointer to the IRP + +Return value: + If the driver layer is not interested in pre-processing the IRP, the return + value is the NTSTATUS value that is returned from the + WdfDeviceWdmDispatchPreprocessedIrp call made by this routine to dispatch + the IRP to KMDF. + + If the driver layer is interested in pre-processing the IRP, the return + value is the NTSTATUS value that is returned by the driver layer's WDM pre- + process routine for this IRP. +--*/ +{ + NTSTATUS status; + PIO_STACK_LOCATION stackLoc; + UCHAR minorFunction; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + BOOLEAN invokeDriverLayerCallback = FALSE; + ULONG i; + PFN_WDFDEVICE_WDM_IRP_PREPROCESS evtDeviceWdmPowerIrpPreprocess = NULL; + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p.", + Device); + WdfVerifierDbgBreakPoint(); + } + + // + // Get the minor function + // + stackLoc = IoGetCurrentIrpStackLocation(Irp); + minorFunction = stackLoc->MinorFunction; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + // + // Check if the driver layer is interested in pre-processing an IRP with + // this minor function code. + // + for (i=0; + i < devCtx->DriverLayerPowerIrpPreprocessInfo->NumMinorFunctions; + i++) { + + if (minorFunction == + devCtx->DriverLayerPowerIrpPreprocessInfo->MinorFunctions[i]) { + // + // The driver layer is interested in this minor function code + // + invokeDriverLayerCallback = TRUE; + break; + } + } + + if (invokeDriverLayerCallback) { + // + // Invoke the driver layer's callback. After the driver layer has + // pre-processed the IRP, it will call + // PfhWdmDispatchPreprocessedPowerIrp. We will pre-process the IRP + // at that time. + // + evtDeviceWdmPowerIrpPreprocess = + devCtx->DeviceInitSettings.EvtDeviceWdmPowerIrpPreprocess; + ASSERT(NULL != evtDeviceWdmPowerIrpPreprocess); + + status = evtDeviceWdmPowerIrpPreprocess(Device, Irp); + + } else { + // + // Preprocess IRP and dispatch to KMDF + // + status = PreprocessIrpAndDispatch(Device, + Irp, + FALSE /* StackLocationAdjusted */); + } + + return status; +} + + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PfhWdmDispatchPreprocessedPowerIrp( + _In_ WDFDEVICE Device, + _Inout_ PIRP Irp + ) +// See comments in WdfPoFx.h +{ + PIO_STACK_LOCATION stackLoc; + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p.", + Device); + WdfVerifierDbgBreakPoint(); + } + + // + // Verify that this is a power IRP + // + stackLoc = IoGetNextIrpStackLocation(Irp); + if (IRP_MJ_POWER != stackLoc->MajorFunction) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhWdmDispatchPreprocessedPowerIrp must be called " + "for power IRPs only. It has been called for IRP %p, which is not" + " a power IRP.", + Irp); + WdfVerifierDbgBreakPoint(); + } + + // + // The driver layer would have already adjusted the stack location for KMDF + // + return PreprocessIrpAndDispatch(Device, + Irp, + TRUE /* StackLocationAdjusted */); +} + +NTSTATUS +_PfhEvtD0Entry( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousState + ) +/*++ +Routine Description: + + In this routine, we perform operations that are needed when the device + enters the D0 power state. + +Arguments: + + Device - Handle to the KMDF device object + + PreviousState - A WDF_POWER_DEVICE_STATE-typed enumerator that identifies + the previous device power state. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + PFN_WDF_DEVICE_D0_ENTRY evtDeviceD0Entry; + + if (FALSE == IsDeviceInitialized(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializeDeviceSettings has not yet been " + "called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + // + // If the driver layer supplied a D0Entry callback, invoke it + // + evtDeviceD0Entry = devCtx->DeviceInitSettings.EvtDeviceD0Entry; + if (NULL != evtDeviceD0Entry) { + status = evtDeviceD0Entry(Device, PreviousState); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + } + + if (devCtx->ShouldReportDevicePoweredOn) { + // + // This is the first time we are entering D0 after receiving an S0 IRP. + // We should call PoFxReportDevicePoweredOn. + // + devCtx->ShouldReportDevicePoweredOn = FALSE; + PoFxReportDevicePoweredOn(devCtx->PoHandle); + } + + status = STATUS_SUCCESS; + +exit: + + return status; +} + +NTSTATUS +_PfhEvtSelfManagedIoRestart( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + In this routine, we restart self-managed I/O operations. + +Arguments: + + Device - Handle to the KMDF device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + PFN_WDF_DEVICE_SELF_MANAGED_IO_RESTART evtDeviceSelfManagedIoRestart; + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + // + // If the driver layer supplied a self-managed-IO-restart callback, invoke + // it + // + evtDeviceSelfManagedIoRestart = + devCtx->DeviceInitSettings.EvtDeviceSelfManagedIoRestart; + if (NULL != evtDeviceSelfManagedIoRestart) { + status = evtDeviceSelfManagedIoRestart(Device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + } + + // + // For a PDO, if this is the first start after the device was disabled and + // if S0-idle power management is supported for the device, then we prevent + // the device from powering down to Dx due to S0-idle. We will allow the + // device to power down to Dx only after our device-power-not-required + // callback is invoked. + // + // NOTE: The function below figures out whether this is the first start + // after the device was disabled and does the right thing based on that. + // + STOP_DEVICE_IDLE_ON_DEVICE_START(status, Device, devCtx, exit); + + // + // For a PDO, we need to register with the power framework in the + // self-managed-IO-restart callback if the device was disabled and then + // re-enabled. In this case, we would have unregistered with the power + // framework in the self-managed-IO-flush callback. Therefore, we need to + // re-register now. + // + // NOTE: The function below handles the case where we are already registered + // with the power framework. + // + status = RegisterWithPowerFrameworkWorker(Device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + status = STATUS_SUCCESS; + +exit: + return status; +} + +VOID +_PfhDevicePowerRequiredCallback( + _In_ PVOID Context + ) +/*++ +Routine Description: + + The power framework invokes this routine to notify us that we need to + enter/remain in the D0 state + +Arguments: + + Context - Context that we passed in to the power framework + +Return Value: + + None + +--*/ +{ + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + + // + // Get the handle to the KMDF device object + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Perform actions specific to S0-idle power management (if enabled) + // + DEVICE_POWER_REQUIRED_FOR_S0_IDLE(device, devCtx); + return; +} + +VOID +_PfhDevicePowerNotRequiredCallback( + _In_ PVOID Context + ) +/*++ +Routine Description: + + The power framework invokes this routine to notify us that we may enter + a low-power Dx state + +Arguments: + + Context - Context that we passed in to the power framework + +Return Value: + + None + +--*/ +{ + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + + // + // Get the handle to the KMDF device object + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Perform actions specific to S0-idle power management (if enabled) + // + DEVICE_POWER_NOT_REQUIRED_FOR_S0_IDLE(device, devCtx); + + // + // Tell the power framework that we've finished processing device-power- + // not-required + // + PoFxCompleteDevicePowerNotRequired(devCtx->PoHandle); + + return; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhRegisterDeviceProactive( + _In_ WDFDEVICE Device + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + PPOFX_DEVICE_CONTEXT devCtx; + + if (FALSE == ArePowerFrameworkSettingsAvailable(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet " + "been called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + if (NULL != devCtx->PoHandle) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhRegisterDeviceProactive was called when the " + "device was already registered with the power framework. " + "%!status!.", + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + status = RegisterWithPowerFrameworkWorker(Device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + status = STATUS_SUCCESS; + +exit: + return status; +} diff --git a/pofx/WDF/Driver/MultiComp/lib/init.c b/pofx/WDF/Driver/MultiComp/lib/init.c new file mode 100644 index 00000000..780434b5 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/init.c @@ -0,0 +1,1336 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + init.c + +Abstract: + This module contains routines that are used to initialize the power + framework helper library + +Environment: + + Kernel mode + +--*/ + +#include "WdfPoFxPriv.h" +#include "init.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, GetDeviceInitSettings) +#pragma alloc_text(PAGE, PfhInitializerCreate) +#pragma alloc_text(PAGE, CopyAndUpdateMinorFunctionsArray) +#pragma alloc_text(PAGE, PfhAssignWdmPowerIrpPreProcessCallback) +#pragma alloc_text(PAGE, PfhInterceptWdfPnpPowerEventCallbacks) +#pragma alloc_text(PAGE, PfhSetPoHandleAvailabilityCallbacks) +#pragma alloc_text(PAGE, CopyMinorFunctionsArray) +#pragma alloc_text(PAGE, PfhInitializeDeviceSettings) +#pragma alloc_text(PAGE, PfhInitializePowerFrameworkSettings) +#endif + +PHELPER_DEVICE_INIT +GetDeviceInitSettings( + _In_ WDFOBJECT Initializer + ) +/*++ +Routine description: + This routine gets a pointer to the location in the initializer object's + context space where we store the driver layer's device object settings. + +Arguments: + Initializer - Handle to the initializer object that is being used to + initialize our device object settings. + +Return value: + Pointer to a HELPER_DEVICE_INIT structure where the driver layer's device + object settings are stored. +--*/ +{ + PHELPER_INIT initContext = NULL; + + PAGED_CODE(); + + initContext = HelperGetInitContext(Initializer); + + if ((initContext->InitType != HelperInitTypeNone) && + (initContext->InitType != HelperInitTypeDevice)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Cannot get device initialization settings for " + "initializer object %p because it is currently being used to " + "initialize a different object.", + Initializer); + WdfVerifierDbgBreakPoint(); + return NULL; + } + + if (initContext->InitType == HelperInitTypeNone) { + initContext->InitType = HelperInitTypeDevice; + } + + return &(initContext->u.DeviceInit); +} + +PHELPER_QUEUE_INIT +GetQueueInitSettings( + _In_ WDFOBJECT Initializer + ) +/*++ +Routine description: + This routine gets a pointer to the location in the initializer object's + context space where we store the driver layer's queue object settings. + +Arguments: + Initializer - Handle to the initializer object that is being used to + initialize our queue object settings. + +Return value: + Pointer to a HELPER_QUEUE_INIT structure where the driver layer's queue + object settings are stored. +--*/ +{ + PHELPER_INIT initContext = NULL; + + initContext = HelperGetInitContext(Initializer); + + if ((initContext->InitType != HelperInitTypeNone) && + (initContext->InitType != HelperInitTypeQueue)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Cannot get queue initialization settings for " + "initializer object %p because it is currently being used to " + "initialize a different object.", + Initializer); + WdfVerifierDbgBreakPoint(); + return NULL; + } + + if (initContext->InitType == HelperInitTypeNone) { + initContext->InitType = HelperInitTypeQueue; + } + + return &(initContext->u.QueueInit); +} + +BOOLEAN +IsDeviceInitialized( + _In_ WDFDEVICE Device + ) +/*++ +Routine description: + This routine determines whether we have initialized our settings for the + given device object. + +Arguments: + Device - Handle to the KMDF device object + +Return value: + TRUE if we have initialized our settings for the device object, FALSE + otherwise. +--*/ +{ + PPOFX_DEVICE_CONTEXT devCtx; + devCtx = HelperGetDeviceContext(Device); + return (NULL != devCtx); +} + +BOOLEAN +ArePowerFrameworkSettingsAvailable( + _In_ WDFDEVICE Device + ) +/*++ +Routine description: + This routine determines whether the driver layer has provided us with its + power framework settings for the given device object. + +Arguments: + Device - Handle to the KMDF device object + +Return value: + TRUE if we the driver layer has provided us with its power framework + settings for the device object, FALSE otherwise. +--*/ +{ + PPOFX_DEVICE_CONTEXT devCtx; + devCtx = HelperGetDeviceContext(Device); + return ((NULL != devCtx) && (NULL != devCtx->PoFxDeviceInfo)); +} + +BOOLEAN +IsQueueInitialized( + _In_ WDFQUEUE Queue + ) +/*++ +Routine description: + This routine determines whether we have initialized our settings for the + given queue object. + +Arguments: + Queue - Handle to the KMDF queue object + +Return value: + TRUE if we have initialized our settings for the queue object, FALSE + otherwise. +--*/ +{ + PPOFX_QUEUE_CONTEXT qCtx; + qCtx = HelperGetQueueContext(Queue); + return (NULL != qCtx); +} + +VOID +ResetInitializer( + _In_ WDFOBJECT Initializer + ) +/*++ +Routine description: + This routine prepares the given initializer object for initializing a new + KMDF object. + +Arguments: + Initializer - Handle to the initializer object + +Return value: + None +--*/ +{ + PHELPER_INIT initContext = NULL; + + // + // We want to make sure that when the initializer context is zeroed, the + // default S0-idle power management configuration for the device is + // PfhS0IdleNotSupported. + // + C_ASSERT(0 == PfhS0IdleNotSupported); + + // + // Reset the initializer context + // + initContext = HelperGetInitContext(Initializer); + RtlZeroMemory(initContext, sizeof(*initContext)); + initContext->InitType = HelperInitTypeNone; + + return; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhInitializerCreate( + _Out_ WDFOBJECT * Initializer + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + WDFOBJECT initializer = NULL; + WDF_OBJECT_ATTRIBUTES objectAttributes; + + PAGED_CODE(); + + // + // Create an initializer object + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, HELPER_INIT); + status = WdfObjectCreate(&objectAttributes, &initializer); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfObjectCreate failed with %!status!.", + status); + goto exit; + } + + // + // Initialize context + // + ResetInitializer(initializer); + + *Initializer = initializer; + + status = STATUS_SUCCESS; + +exit: + return status; +} + +NTSTATUS +CopyAndUpdateMinorFunctionsArray( + _In_ WDFOBJECT Initializer, + _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions, + _In_ ULONG NumMinorFunctions, + _Out_ WDFMEMORY * MinorFunctionsMemory, + _Outptr_result_buffer_(*UpdatedNumMinorFunctions) PUCHAR *UpdatedMinorFunctions, + _Out_ PULONG UpdatedNumMinorFunctions + ) +/*++ +Routine description: + This routine takes an array of power IRP minor functions and makes a copy of + it. If the input array does not include IRP_MN_SET_POWER as one of the minor + functions, the copy of the array made by this routine is expanded to include + IRP_MN_SET_POWER too. This enables us to register a WDM pre-process callback + for IRP_MN_SET_POWER, regardless of whether the driver layer is interested + in that minor function. + +Arguments: + Initializer - Handle to the initializer object + + MinorFunctions - Caller-initialized array of minor functions that the driver + layer is interested in for the IRP_MJ_POWER major function. + + NumMinorFunctions - The number of minor functions in the MinorFunctions + array + + MinorFunctionsMemory - Pointer to a location that receives a handle to the + memory object created by this routine to store a copy of the minor + functions + + UpdatedMinorFunctions - Pointer to a location that receives an array of + minor functions that the driver layer is interested in, plus + IRP_MN_SET_POWER if it was not present in the original array + + UpdatedNumMinorFunctions - Pointer to a location that receives the number of + minor functions in the UpdatedMinorFunctions array + +Return value: + An NTSTATUS value representing success or failure of the function. +--*/ +{ + NTSTATUS status; + BOOLEAN driverLayerPreprocessesSetPower = FALSE; + UCHAR setPowerMinorFunction; + ULONG i; + ULONG driverLayerMinorFunctions = 0; + ULONG extraMinorFunctions = 0; + ULONG totalMinorFunctions = 0; + ULONG totalMinorFunctionsSize = 0; + ULONG memorySize = 0; + WDF_OBJECT_ATTRIBUTES objectAttributes; + WDFMEMORY memory = NULL; + PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO memoryBuffer = NULL; + ULONG minorFunctionsOffset; + ULONG minorFunctionsSize; + ULONG extraMinorFunctionOffset; + + PAGED_CODE(); + + // + // Check if the driver layer needs to preprocess IRP_MN_SET_POWER + // + if ((NULL != MinorFunctions) && (0 != NumMinorFunctions)) { + + driverLayerMinorFunctions = NumMinorFunctions; + + for (i=0; i < NumMinorFunctions; i++) { + if (IRP_MN_SET_POWER == MinorFunctions[i]) { + driverLayerPreprocessesSetPower = TRUE; + break; + } + } + } + + // + // If the driver layer does not preprocess IRP_MN_SET_POWER, then we need to + // update the minor functions array with an extra entry for IRP_MN_SET_POWER + // + extraMinorFunctions = driverLayerPreprocessesSetPower ? 0 : 1; + + // + // Allocate memory to store a copy of the minor functions array + // + status = RtlULongAdd(driverLayerMinorFunctions, + extraMinorFunctions, + &totalMinorFunctions); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute total minor functions count for " + "power IRP preprocessing. RtlUlongAdd failed with %!status!.", + status); + goto exit; + } + + ASSERT(totalMinorFunctions > 0); + + status = RtlULongMult(sizeof(MinorFunctions[0]), + totalMinorFunctions, + &totalMinorFunctionsSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed to store the " + "functions array. RtlULongMult failed with %!status!.", + status); + goto exit; + } + status = RtlULongAdd(sizeof(POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO), + totalMinorFunctionsSize, + &memorySize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed to store the " + "minor functions array and count. RtlUlongAdd failed with " + "%!status!.", + status); + goto exit; + } + if (0 == memorySize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to set memorySize. Failed with %!status!.", + status); + goto exit; + } + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Initializer; // auto-delete when parent + // deleted + #pragma prefast(suppress:28160, "memorySize > 0") + status = WdfMemoryCreate(&objectAttributes, + NonPagedPool, + 0, // PoolTag + memorySize, + &memory, + (PVOID*) &memoryBuffer); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to allocate memory to store the minor functions" + " array and count. WdfMemoryCreate failed with %!status!.", + status); + goto exit; + } + + // + // We'll remember only the minor functions that the driver layer is + // interested in. If we add IRP_MN_SET_POWER to that list, we won't need to + // remember that beyond initialization time. + // + memoryBuffer->NumMinorFunctions = driverLayerMinorFunctions; + + // + // Copy the minor functions array + // + minorFunctionsOffset = + FIELD_OFFSET(POFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO, MinorFunctions); + minorFunctionsSize = driverLayerMinorFunctions * + sizeof(MinorFunctions[0]); // already performed + // intsafe math above + // won't overflow + if (driverLayerMinorFunctions > 0) { + if (0 == minorFunctionsSize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to set minorFunctionsSize. Failed with %!status!.", + status); + goto exit; + } + #pragma prefast(suppress:28160, "minorFunctionsSize > 0") + status = WdfMemoryCopyFromBuffer(memory, + minorFunctionsOffset, + memoryBuffer->MinorFunctions, + minorFunctionsSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to copy driver layer's minor functions " + "array. WdfMemoryCopyFromBuffer failed with %!status!.", + status); + goto exit; + } + } + + if (extraMinorFunctions > 0) { + + ASSERT(extraMinorFunctions == 1); + + // + // Add an extra minor function for IRP_MN_SET_POWER since it was not in + // the driver layer's list. + // NOTE: This minor function is added at the end and is not reflected in + // memoryBuffer.NumMinorFunctions. + // + extraMinorFunctionOffset = + minorFunctionsOffset + minorFunctionsSize;// already performed intsafe + // math above so this won't + // overflow + setPowerMinorFunction = IRP_MN_SET_POWER; + status = WdfMemoryCopyFromBuffer(memory, + extraMinorFunctionOffset, + &setPowerMinorFunction, + sizeof(setPowerMinorFunction)); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to add an extra minor function " + "(IRP_MN_SET_POWER) to the minor functions array. " + "WdfMemoryCopyFromBuffer failed with %!status!.", + status); + goto exit; + } + } + + *MinorFunctionsMemory = memory; + *UpdatedMinorFunctions = memoryBuffer->MinorFunctions; + *UpdatedNumMinorFunctions = + totalMinorFunctions;// Do not use memoryBuffer.NumMinorFunctions + // here because it does not include the extra + // minor function that we might have added. + + // + // If we added an extra minor function, it is present in the buffer but not + // reflected in memoryBuffer.NumMinorFunctions + // + ASSERT((memoryBuffer->NumMinorFunctions + extraMinorFunctions) == + totalMinorFunctions); + + status = STATUS_SUCCESS; + +exit: + if (FALSE == NT_SUCCESS(status)) { + if (NULL != memory) { + WdfObjectDelete(memory); + } + } + + return status; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhAssignWdmPowerIrpPreProcessCallback( + _In_ WDFOBJECT Initializer, + _In_ PWDFDEVICE_INIT DeviceInit, + _In_opt_ PFN_WDFDEVICE_WDM_IRP_PREPROCESS EvtDeviceWdmPowerIrpPreprocess, + _In_reads_opt_(NumMinorFunctions) PUCHAR MinorFunctions, + _In_ ULONG NumMinorFunctions + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + WDFMEMORY minorFunctionsMemory = NULL; + ULONG numMinorFunctions = 0; + PUCHAR minorFunctions = NULL; + PHELPER_DEVICE_INIT deviceInitSettings = NULL; + + PAGED_CODE(); + + deviceInitSettings = GetDeviceInitSettings(Initializer); + + if (deviceInitSettings->PowerIrpPreprocessCallbackAssigned) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhAssignWdmPowerIrpPreProcessCallback has already" + " been called on initialier %p. It should not be called again " + "before the initializer has been used to initialize a KMDF device" + " object. %!status!.", + Initializer, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Copy the minor functions array and, if needed, update it to include + // IRP_MN_SET_POWER. + // + status = CopyAndUpdateMinorFunctionsArray(Initializer, + MinorFunctions, + NumMinorFunctions, + &minorFunctionsMemory, + &minorFunctions, + &numMinorFunctions); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + // + // Assign the WDM preprocess callback + // + status = WdfDeviceInitAssignWdmIrpPreprocessCallback( + DeviceInit, + _PfhEvtWdmPowerIrpPreprocess, + IRP_MJ_POWER, + minorFunctions, + numMinorFunctions + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceInitAssignWdmIrpPreprocessCallback failed " + "with %!status!.", + status); + goto exit; + } + + deviceInitSettings->PowerIrpPreprocessMinorFunctions = minorFunctionsMemory; + deviceInitSettings->EvtDeviceWdmPowerIrpPreprocess = + EvtDeviceWdmPowerIrpPreprocess; + deviceInitSettings->PowerIrpPreprocessCallbackAssigned = TRUE; + + status = STATUS_SUCCESS; + +exit: + if (FALSE == NT_SUCCESS(status)) { + if (NULL != minorFunctionsMemory) { + WdfObjectDelete(minorFunctionsMemory); + } + } + return status; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PfhInterceptWdfPnpPowerEventCallbacks( + _In_ WDFOBJECT Initializer, + _Inout_ PWDF_PNPPOWER_EVENT_CALLBACKS DriverLayerPnpPowerCallbacks + ) +// See comments in WdfPoFx.h +{ + PHELPER_DEVICE_INIT deviceInitSettings = NULL; + + PAGED_CODE(); + + deviceInitSettings = GetDeviceInitSettings(Initializer); + + if (deviceInitSettings->PnpPowerEventCallbacksIntercepted) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInterceptWdfPnpPowerEventCallbacks has already been" + " called on initialier %p. It should not be called again before " + "the initializer has been used to initialize a KMDF device " + "object.", + Initializer); + WdfVerifierDbgBreakPoint(); + } + + // + // Save the driver layer's callbacks that we are going to replace + // + deviceInitSettings->EvtDeviceSelfManagedIoInit = + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoInit; + deviceInitSettings->EvtDeviceSelfManagedIoFlush = + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoFlush; + deviceInitSettings->EvtDeviceSelfManagedIoRestart = + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoRestart; + deviceInitSettings->EvtDeviceD0Entry = + DriverLayerPnpPowerCallbacks->EvtDeviceD0Entry; + + // + // Replace the driver layer's callbacks with our own + // + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoInit = + _PfhEvtSelfManagedIoInit; + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoFlush = + _PfhEvtSelfManagedIoFlush; + DriverLayerPnpPowerCallbacks->EvtDeviceSelfManagedIoRestart = + _PfhEvtSelfManagedIoRestart; + DriverLayerPnpPowerCallbacks->EvtDeviceD0Entry = + _PfhEvtD0Entry; + + deviceInitSettings->PnpPowerEventCallbacksIntercepted = TRUE; + + return; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PfhSetPoHandleAvailabilityCallbacks( + _In_ WDFOBJECT Initializer, + _In_ PPFH_CALLBACK_POHANDLE_AVAILABLE PfhCallbackPoHandleAvailable, + _In_ PPFH_CALLBACK_POHANDLE_UNAVAILABLE PfhCallbackPoHandleUnavailable + ) +// See comments in WdfPoFx.h +{ + PHELPER_DEVICE_INIT deviceInitSettings = NULL; + + PAGED_CODE(); + + deviceInitSettings = GetDeviceInitSettings(Initializer); + + if (deviceInitSettings->PoHandleAvailabilityCallbacksSet) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhSetPoHandleAvailabilityCallbacks has already been " + "called on initialier %p. It should not be called again before " + "the initializer has been used to initialize a KMDF device " + "object.", + Initializer); + WdfVerifierDbgBreakPoint(); + } + + // + // Save the driver layer's callbacks + // + deviceInitSettings->PfhCallbackPoHandleAvailable = + PfhCallbackPoHandleAvailable; + deviceInitSettings->PfhCallbackPoHandleUnavailable = + PfhCallbackPoHandleUnavailable; + + deviceInitSettings->PoHandleAvailabilityCallbacksSet = TRUE; + return; +} + +NTSTATUS +CopyMinorFunctionsArray( + _In_ WDFDEVICE Device, + _In_ WDFMEMORY SourceMemory, + _Out_ WDFMEMORY * DestinationMemory + ) +/*++ +Routine description: + This routine creates a new memory object to store the array of power IRP + minor functions that the driver layer is interested in. The array is + currently stored in a memory object that we had created as a child of the + initializer object that was being used to initialize the device object. Now + that the initialization of the device object is about to complete, we need + to make a copy of the contents of that memory object. Once the device object + initialization is complete, the driver layer is free to delete the + initializer object, which will result in the original memory object also + getting delete. That is why we copy the contents to a new memory object. + +Arguments: + Device - Handle to the KMDF device object + + SourceMemory - Handle to the memory object that contains the array of power + IRP minor functions that the driver layer is interested in + + DestinationMemory - Pointer to a location that receives a handle to a new + memory object created by this routine. The new memory object contains a + copy of the a + +Return value: + An NTSTATUS value representing success or failure of the function. +--*/ +{ + NTSTATUS status; + PPOFX_DRIVER_LAYER_POWER_IRP_PREPROCESS_INFO powerIrpPreprocessInfo = NULL; + size_t powerIrpPreprocessInfoSize; + WDF_OBJECT_ATTRIBUTES objectAttributes; + WDFMEMORY memory = NULL; + + PAGED_CODE(); + + // + // Get the source memory buffer + // + powerIrpPreprocessInfo = WdfMemoryGetBuffer(SourceMemory, + &powerIrpPreprocessInfoSize); + if (0 == powerIrpPreprocessInfoSize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to get powerIrpPreprocessInfoSize. Failed with %!status!.", + status); + goto exit; + } + + // + // Create a new memory object to hold a copy of the buffer + // + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device; // auto-delete when parent deleted + status = WdfMemoryCreate(&objectAttributes, + NonPagedPool, + 0, // PoolTag + powerIrpPreprocessInfoSize, + &memory, + NULL // Buffer + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to allocate memory for the driver layer's minor" + " functions array. WdfMemoryCreate failed with %!status!.", + status); + goto exit; + } + + // + // Copy the buffer into the new memory object + // + status = WdfMemoryCopyFromBuffer(memory, + 0, // DestinationOffset + powerIrpPreprocessInfo, + powerIrpPreprocessInfoSize); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + *DestinationMemory = memory; + + status = STATUS_SUCCESS; + +exit: + return status; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhInitializeDeviceSettings( + _In_ WDFDEVICE Device, + _In_ WDFOBJECT Initializer + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttributes; + PPOFX_DEVICE_CONTEXT devCtx; + PHELPER_DEVICE_INIT deviceInitSettings = NULL; + WDFMEMORY memory = NULL; + + PAGED_CODE(); + + deviceInitSettings = GetDeviceInitSettings(Initializer); + + if (IsDeviceInitialized(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializeDeviceSettings has already been called " + "for WDFDEVICE %p. It should not be called again. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + if (FALSE == deviceInitSettings->PowerIrpPreprocessCallbackAssigned) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhAssignWdmPowerIrpPreProcessCallback has not yet " + "been called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + if (FALSE == deviceInitSettings->PnpPowerEventCallbacksIntercepted) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInterceptWdfPnpPowerEventCallbacks has not yet been" + " called for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Allocate our context for this device + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, + POFX_DEVICE_CONTEXT); + status = WdfObjectAllocateContext((WDFOBJECT) Device, + &objectAttributes, + (PVOID*) &devCtx); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfObjectAllocateContext failed with %!status!", + status); + goto exit; + } + + // + // Copy the device init settings + // + devCtx->DeviceInitSettings = *deviceInitSettings; + + // + // Copy the minor functions array into a new memory object that is a child + // of the device object. The array is currently in a memory object that is + // a child of the initializer object. The initializer object can be deleted + // by the driver layer any time after initialization is complete, so we need + // to make a copy now. + // + status = CopyMinorFunctionsArray( + Device, + devCtx->DeviceInitSettings.PowerIrpPreprocessMinorFunctions, + &memory + ); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } + + // + // Perform initialization specific to S0-idle support (if enabled) + // + INITIALIZE_DEVICE_SETTINGS_FOR_S0_IDLE(Device, devCtx); + + // + // In our device context, replace the pointer to the initializer's memory + // object with a pointer to the new memory object that we just created. + // + devCtx->DeviceInitSettings.PowerIrpPreprocessMinorFunctions = memory; + + // + // For convenient access, also save the memory object's buffer in our device + // context. + // + devCtx->DriverLayerPowerIrpPreprocessInfo = WdfMemoryGetBuffer( + memory, + NULL // BufferSize + ); + + // + // By default, we do not need to call PoFxReportDevicePoweredOn from our + // EvtDeviceD0Entry callback. We only need to call it in the + // EvtDeviceD0Entry callback that is invoked right after we receive an S0 + // IRP. + // + devCtx->ShouldReportDevicePoweredOn = FALSE; + + // + // The initializer can now be used to initialize some other object + // + ResetInitializer(Initializer); + + status = STATUS_SUCCESS; + +exit: + return status; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PfhInitializePowerFrameworkSettings( + _In_ WDFDEVICE Device, + _In_ PPO_FX_DEVICE PoFxDeviceInfo + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttributes; + PPOFX_DEVICE_CONTEXT devCtx; + ULONG i; + ULONG pofxExtraComponentsSize; + ULONG pofxDeviceInfoSize; + WDFMEMORY memory = NULL; + ULONG idleStatesSize; + PVOID idleStates = NULL; + PPO_FX_DEVICE poFxDeviceInfo = NULL; + PPOFX_COMPONENT_INFO componentInfo = NULL; + ULONG componentInfoSize; + + PAGED_CODE(); + + if (FALSE == IsDeviceInitialized(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializeDeviceSettings has not yet been called " + "for WDFDEVICE %p. %!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + if (ArePowerFrameworkSettingsAvailable(Device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has already been " + "called for WDFDEVICE %p. It should not be called again. " + "%!status!.", + Device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(Device); + + // + // Allocate memory to store power framework settings + // + status = RtlULongMult(sizeof(PO_FX_COMPONENT), + (PoFxDeviceInfo->ComponentCount - 1), + &pofxExtraComponentsSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed for extra " + "components. RtlULongMult failed with %!status!.", + status); + goto exit; + } + status = RtlULongAdd(sizeof(PO_FX_DEVICE), + pofxExtraComponentsSize, + &pofxDeviceInfoSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed for power " + "framework settings. RtlUlongAdd failed with %!status!.", + status); + goto exit; + } + if (0 == pofxDeviceInfoSize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to set pofxDeviceInfoSize. Failed with %!status!.", + status); + goto exit; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device; // auto-delete when parent deleted + status = WdfMemoryCreate(&objectAttributes, + NonPagedPool, + 0, // PoolTag + pofxDeviceInfoSize, + &memory, + (PVOID*) &poFxDeviceInfo); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to allocate memory for power framework " + "settings. WdfMemoryCreate failed with %!status!.", + status); + goto exit; + } + + devCtx->PoFxDeviceInfo = poFxDeviceInfo; + + // + // Copy power framework settings + // + status = WdfMemoryCopyFromBuffer(memory, + 0, // DestinationOffset + PoFxDeviceInfo, + pofxDeviceInfoSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to copy power framework settings. " + "WdfMemoryCopyFromBuffer failed with %!status!.", + status); + goto exit; + } + + // + // Save the driver layer's callbacks and replace them with our own + // + devCtx->DriverLayerPoFxCallbacks.ComponentIdleStateCallback = + devCtx->PoFxDeviceInfo->ComponentIdleStateCallback; + devCtx->DriverLayerPoFxCallbacks.ComponentActiveConditionCallback = + devCtx->PoFxDeviceInfo->ComponentActiveConditionCallback; + devCtx->DriverLayerPoFxCallbacks.ComponentIdleConditionCallback = + devCtx->PoFxDeviceInfo->ComponentIdleConditionCallback; + devCtx->DriverLayerPoFxCallbacks.DevicePowerRequiredCallback = + devCtx->PoFxDeviceInfo->DevicePowerRequiredCallback; + devCtx->DriverLayerPoFxCallbacks.DevicePowerNotRequiredCallback = + devCtx->PoFxDeviceInfo->DevicePowerNotRequiredCallback; + devCtx->PoFxDeviceInfo->ComponentIdleStateCallback = + _PfhComponentIdleStateCallback; + devCtx->PoFxDeviceInfo->ComponentActiveConditionCallback = + _PfhComponentActiveConditionCallback; + devCtx->PoFxDeviceInfo->ComponentIdleConditionCallback = + _PfhComponentIdleConditionCallback; + devCtx->PoFxDeviceInfo->DevicePowerRequiredCallback = + _PfhDevicePowerRequiredCallback; + devCtx->PoFxDeviceInfo->DevicePowerNotRequiredCallback = + _PfhDevicePowerNotRequiredCallback; + + // + // Save the driver layer's context and replace it with our own + // + devCtx->DriverLayerPoFxContext = devCtx->PoFxDeviceInfo->DeviceContext; + devCtx->PoFxDeviceInfo->DeviceContext = (PVOID) Device; + + // + // Store the idle states for each component + // + for (i=0; i < devCtx->PoFxDeviceInfo->ComponentCount; i++) { + + status = RtlULongMult(sizeof(PO_FX_COMPONENT_IDLE_STATE), + PoFxDeviceInfo->Components[i].IdleStateCount, + &idleStatesSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed for idle " + "states for component %d. RtlUlongMult failed with " + "%!status!.", + i, + status); + goto exit; + } + if (0 == idleStatesSize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to set idleStatesSize. Failed with %!status!.", + status); + goto exit; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device;//auto-delete when parent deleted + status = WdfMemoryCreate(&objectAttributes, + NonPagedPool, + 0, // PoolTag + idleStatesSize, + &memory, + &idleStates); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to allocate memory for idle states for " + "component %d. WdfMemoryCreate failed with %!status!.", + i, + status); + goto exit; + } + + status = WdfMemoryCopyFromBuffer( + memory, + 0, // DestinationOffset + devCtx->PoFxDeviceInfo->Components[i].IdleStates, + idleStatesSize + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to copy idle states for component %d. " + "WdfMemoryCopyFromBuffer failed with %!status!.", + i, + status); + goto exit; + } + devCtx->PoFxDeviceInfo->Components[i].IdleStates = idleStates; + } + + // + // Allocate memory to store our private, per-component information + // + status = RtlULongMult(sizeof(POFX_COMPONENT_INFO), + devCtx->PoFxDeviceInfo->ComponentCount, + &componentInfoSize); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to compute buffer size needed for storing " + "private, per-component information. RtlULongMult failed with " + "%!status!.", + status); + goto exit; + } + if (0 == componentInfoSize) { + status = STATUS_INVALID_BUFFER_SIZE; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to set componentInfoSize. Failed with %!status!.", + status); + goto exit; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device; // auto-delete when parent deleted + status = WdfMemoryCreate(&objectAttributes, + NonPagedPool, + 0, // PoolTag + componentInfoSize, + &memory, + (PVOID*) &componentInfo); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Unable to allocate memory for storing private, per-" + "component information. WdfMemoryCreate failed with %!status!.", + status); + goto exit; + } + RtlZeroMemory(componentInfo, componentInfoSize); + devCtx->ComponentInfo = componentInfo; + + status = STATUS_SUCCESS; + +exit: + return status; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhInterceptComponentQueueConfig( + _In_ WDFOBJECT Initializer, + _Inout_ PWDF_IO_QUEUE_CONFIG DriverLayerQueueConfig + ) +// See comments in WdfPoFx.h +{ + PHELPER_QUEUE_INIT queueInitSettings = NULL; + + queueInitSettings = GetQueueInitSettings(Initializer); + + if (queueInitSettings->ComponentQueueConfigIntercepted) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInterceptComponentQueueConfig has already been " + "called on initialier %p. It should not be called again before " + "the initializer has been used to initialize a KMDF queue " + "object.", + Initializer); + WdfVerifierDbgBreakPoint(); + } + + // + // Save the driver layer's callbacks that we are going to replace + // + queueInitSettings->EvtIoCanceledOnQueue = + DriverLayerQueueConfig->EvtIoCanceledOnQueue; + + // + // Replace the driver layer's callbacks with our own + // + DriverLayerQueueConfig->EvtIoCanceledOnQueue = + _PfhEvtRequestCanceledOnComponentQueue; + + queueInitSettings->ComponentQueueConfigIntercepted = TRUE; + + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PfhSetComponentForComponentQueue( + _In_ WDFOBJECT Initializer, + _In_ ULONG Component + ) +// See comments in WdfPoFx.h +{ + PHELPER_QUEUE_INIT queueInitSettings = NULL; + + queueInitSettings = GetQueueInitSettings(Initializer); + + if (queueInitSettings->ComponentSet) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhSetComponentForComponentQueue has already been " + "called on initialier %p. It should not be called again before " + "the initializer has been used to initialize a KMDF queue " + "object.", + Initializer); + WdfVerifierDbgBreakPoint(); + } + + queueInitSettings->Component = Component; + + queueInitSettings->ComponentSet = TRUE; + + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PfhInitializeComponentQueueSettings( + _In_ WDFQUEUE Queue, + _In_ WDFOBJECT Initializer + ) +// See comments in WdfPoFx.h +{ + NTSTATUS status; + WDFDEVICE device = NULL; + ULONG component; + WDF_OBJECT_ATTRIBUTES objectAttributes; + PPOFX_DEVICE_CONTEXT devCtx; + PPOFX_QUEUE_CONTEXT qCtx; + PHELPER_QUEUE_INIT queueInitSettings = NULL; + + queueInitSettings = GetQueueInitSettings(Initializer); + + if (IsQueueInitialized(Queue)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializeComponentQueueSettings has already been " + "called for WDFQUEUE %p. It should not be called again. " + "%!status!.", + Queue, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + device = WdfIoQueueGetDevice(Queue); + if (FALSE == ArePowerFrameworkSettingsAvailable(device)) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInitializePowerFrameworkSettings has not yet been " + "called for WDFDEVICE %p. %!status!.", + device, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + if (FALSE == queueInitSettings->ComponentQueueConfigIntercepted) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhInterceptComponentQueueConfig has not yet been " + "called for WDFQUEUE %p. %!status!.", + Queue, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + if (FALSE == queueInitSettings->ComponentSet) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhSetComponentForComponentQueue has not yet been " + "called for WDFQUEUE %p. %!status!.", + Queue, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Validate the component number + // + component = queueInitSettings->Component; + if (component >= devCtx->PoFxDeviceInfo->ComponentCount) { + status = STATUS_INVALID_PARAMETER; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Component number %d is invalid. Component count is %d." + " %!status!.", + component, + devCtx->PoFxDeviceInfo->ComponentCount, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Allocate our context for this queue + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, + POFX_QUEUE_CONTEXT); + status = WdfObjectAllocateContext((WDFOBJECT) Queue, + &objectAttributes, + (PVOID*) &qCtx); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfObjectAllocateContext failed with %!status!", + status); + goto exit; + } + + if (NULL != devCtx->ComponentInfo[component].Queue) { + status = STATUS_INVALID_DEVICE_REQUEST; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Component %d has already been associated with WDFQUEUE" + " %p. Associating a component with more than one queue is not " + "supported. %!status!.", + component, + devCtx->ComponentInfo[component].Queue, + status); + WdfVerifierDbgBreakPoint(); + goto exit; + } + + // + // Copy the queue init settings + // + qCtx->QueueInitSettings = *queueInitSettings; + + // + // The initializer can now be used to initialize some other object + // + ResetInitializer(Initializer); + + // + // Save the queue handle in our private component information + // + devCtx->ComponentInfo[component].Queue = Queue; + + status = STATUS_SUCCESS; + +exit: + return status; +} diff --git a/pofx/WDF/Driver/MultiComp/lib/s0idle.c b/pofx/WDF/Driver/MultiComp/lib/s0idle.c new file mode 100644 index 00000000..59ab1bd3 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/s0idle.c @@ -0,0 +1,471 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + s0idle.c + +Abstract: + This module contains routines that are used to implement support for + S0-idle power management. Note the use of the compile-time switch + PFH_S0IDLE_SUPPORTED to control whether or not S0-idle power management + support is implemented in the power framework helper library. Drivers that + do not require S0-idle power management support should set the + PFH_S0IDLE_SUPPORTED compile-time switch to 0 in order to reduce the + code size of the power framework helper library. + +Environment: + + Kernel mode + +--*/ + +#if PFH_S0IDLE_SUPPORTED + +#include "WdfPoFxPriv.h" +#include "s0idle.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, PfhSetS0IdleConfiguration) +#pragma alloc_text(PAGE, InitializeDeviceSettingsForS0Idle) +#pragma alloc_text(PAGE, _PowerRequiredPassiveHandler) +#endif + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PfhSetS0IdleConfiguration( + _In_ WDFOBJECT Initializer, + _In_ PFH_S0IDLE_CONFIG S0IdleConfig + ) +// See comments in WdfPoFx.h +{ + PHELPER_DEVICE_INIT deviceInitSettings = NULL; + + PAGED_CODE(); + + deviceInitSettings = GetDeviceInitSettings(Initializer); + + if (deviceInitSettings->S0IdleConfigSet) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - PfhSetS0IdleConfiguration has already been called on " + "initialier %p. It should not be called again before the " + "initializer has been used to initialize a KMDF device object.", + Initializer); + WdfVerifierDbgBreakPoint(); + } + + // + // Save the driver layer's S0-idle configuration + // + deviceInitSettings->S0IdleConfig = S0IdleConfig; + + deviceInitSettings->S0IdleConfigSet = TRUE; + return; +} + +NTSTATUS +InitializeDeviceSettingsForS0Idle( + _In_ WDFDEVICE Device, + _Inout_ PPOFX_DEVICE_CONTEXT DevCtx + ) +/*++ +Routine Description: + In this routine we initialize our S0-idle power management related settings + for a device. + +Arguments: + + Device - Handle to the KMDF device object + + DevCtx - Pointer to our context space for the device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. +--*/ +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttributes; + WDF_WORKITEM_CONFIG workItemConfig; + + PAGED_CODE(); + + // + // Create a work item that we'll queue in response to the device-power- + // required callback. This is only needed if S0-idle power management is + // supported. Also, queuing a work item can cause pageable data to be + // accessed, so we do this only if the device is not in the paging path. + // Therefore, we create the work item only if we know that the device will + // never be in the paging path. + // + if (PfhS0IdleSupportedPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig) { + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device;//auto-delete when device deleted + + WDF_WORKITEM_CONFIG_INIT(&workItemConfig, _PowerRequiredPassiveHandler); + workItemConfig.AutomaticSerialization = FALSE; + + status = WdfWorkItemCreate(&workItemConfig, + &objectAttributes, + &(DevCtx->PowerRequiredWorkItem)); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfWorkItemCreate failed with %!status!", + status); + goto exit; + } + } + + + // + // Initialize StopIdleInvokedOnDeviceStart to FALSE so that when the device + // is started, we invoke WdfDeviceStopIdle to prevent device idling until + // the power framework permits it. + // + DevCtx->StopIdleInvokedOnDeviceStart = FALSE; + + status = STATUS_SUCCESS; + +exit: + return status; +} + +NTSTATUS +StopDeviceIdleOnDeviceStart( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ) +/*++ +Routine Description: + This routine is invoked during device start. In this routine, we invoke + WdfDeviceStopIdle in order to prevent the device from going to Dx as a + result of S0-idle power management. We do this only if S0-idle power + management is enabled and then only in the following cases: + 1. the device is being started for the first time after creation + 2. the device is being started for the first time after being disabled + (applicable to PDOs only) + + When WdfDeviceStopIdle is called KMDF increments its power idle reference + count. Device idling can occur only when the power idle reference count + drops to 0. Thus by calling WdfDeviceStopIdle, we prevent device idling + until we make a balancing call to WdfDeviceResumeIdle to decrement the power + idle reference count. + + When a device is disabled, KMDF resets the power idle reference count of the + device to 0. This is the reason we need to call WdfDeviceStopIdle in case #2 + above. + +Arguments: + + Device - Handle to the KMDF device object + + DevCtx - Pointer to our context space for the device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + + if (DevCtx->StopIdleInvokedOnDeviceStart) { + // + // This is neither the first device start after creation nor the first + // device start after it was disabled. So nothing to do here. + // + status = STATUS_SUCCESS; + goto exit; + } + + DevCtx->StopIdleInvokedOnDeviceStart = TRUE; + + // + // If S0-idle power management is supported, prevent device idling until the + // power framework permits. + // + if ((PfhS0IdleSupportedPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig) || + (PfhS0IdleSupportedNotPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig)) { + // + // We are currently in the process of entering D0. Therefore we specify + // 'FALSE' for the WaitForD0 parameter in the WdfDeviceDeviceStopIdle + // call below. Specifying 'TRUE' would result in the deadlock because we + // can't block waiting for D0 while we are in the process of entering + // D0. However, the call to WdfDeviceStopIdle will ensure that once we + // enter D0, we will remain in D0 until WdfDeviceResumeIdle is called. + // + status = WdfDeviceStopIdle(Device, FALSE /* WaitForD0 */); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceStopIdle failed for WDFDEVICE %p. " + "%!status!.", + Device, + status); + goto exit; + } + } + + status = STATUS_SUCCESS; + +exit: + return status; +} + +VOID +DevicePowerRequiredForS0Idle( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ) +/*++ +Routine Description: + + In this routine we perform actions that are necessary in response to the + PO_FX_DEVICE_POWER_REQUIRED_CALLBACK callback when S0-idle power management + support is enabled for the device. + +Arguments: + + Device - Handle to the KMDF device object + + DevCtx - Pointer to our context space for the device object + +Return Value: + + None + +--*/ +{ + PPO_FX_DEVICE_POWER_REQUIRED_CALLBACK devicePowerRequiredCallback = NULL; + PDEVICE_OBJECT wdmDeviceObject = NULL; + + // + // If the driver layer supplied a device-power-required callback, invoke it + // + devicePowerRequiredCallback = + DevCtx->DriverLayerPoFxCallbacks.DevicePowerRequiredCallback; + if (NULL != devicePowerRequiredCallback) { + devicePowerRequiredCallback(DevCtx->DriverLayerPoFxContext); + } + + if (PfhS0IdleNotSupported == DevCtx->DeviceInitSettings.S0IdleConfig) { + // + // S0-idle power management is not supported. This means that the device + // always remains in D0, unless there is a system sleep transition, + // resource rebalance or device removal. In other words, the device + // never leaves D0 because of S0-idle. So we can immediately report to + // the power framework that the device is powered on. + // + PoFxReportDevicePoweredOn(DevCtx->PoHandle); + + } else if (PfhS0IdleSupportedPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig) { + // + // S0-idle power management is supported and the device is not in the + // paging path. This means that we can access pageable data during the + // power transition. + // + // We need to bring the device to D0 and invoke + // PoFxReportDevicePoweredOn when the device is in D0. In order to + // achieve this we need to make a blocking call to WdfDeviceStopIdle, + // which causes KMDF to bring the device to D0. + // + // We cannot make a blocking call here because: + // * Device-power-required can be invoked at dispatch level + // * Even if device-power-required is invoked at passive level, it + // might get invoked in the context of an I/O dispatch routine of a + // power-managed queue. KMDF does not support blocking calls to + // WdfDeviceStopIdle from within the context of an I/O dispatch + // routine of a power-managed queue because it can lead to a + // deadlock in some situations. + // Therefore we queue a system work item for making the blocking call to + // WdfDeviceStopIdle. + // + // !!! IMPORTANT NOTE !!! + // + // Queuing a system work item can cause pageable data to be accessed. + // Therefore, we can do it only if we know that the device is not in the + // paging path and that we can safely access pageable data during power + // transitions. + // + + // + // Verify that the DO_POWER_PAGABLE flag is set in the device object + // + wdmDeviceObject = WdfDeviceWdmGetDeviceObject(Device); + if (0 == (wdmDeviceObject->Flags & DO_POWER_PAGABLE)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WDFDEVICE %p is in the paging path, but " + "PfhSetNotPowerPageable has not been invoked for it.", + Device); + WdfVerifierDbgBreakPoint(); + } + + // + // As described in the above comments, we queue a work item to bring the + // device to D0. + // + WdfWorkItemEnqueue(DevCtx->PowerRequiredWorkItem); + + } else { + // + // !!! IMPORTANT NOTE !!! + // + // S0-idle power management is supported and the device might be in the + // paging path. This means that we cannot access pageable data during + // the power transition. As described in the comments above, queueing a + // system work item to bring the device to D0 can cause pageable data to + // be accessed. + // + // Therefore, the current implementation of the power framework helper + // library does not automatically bring the device to D0 in response to + // a device-power-required callback, if the device is in the paging path + // and cannot access pageable data during power transitions. In such + // cases the driver layer should supply its own device-power-required + // callback. In this callback, it should queue work to its own dedicated + // worker thread (instead of a system work item) to make a blocking call + // to WdfDeviceStopIdle. When WdfDeviceStopIdle returns, the device is + // guaranteed to be in D0 so the driver can call + // PoFxReportDevicePoweredOn at that point. + // + ASSERT(PfhS0IdleSupportedNotPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig); + + DO_NOTHING(); + } + + return; +} + +VOID +DevicePowerNotRequiredForS0Idle( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ) +/*++ +Routine Description: + + In this routine we perform actions that are necessary in response to the + PO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK callback when S0-idle power + management support is enabled for the device. + +Arguments: + + Device - Handle to the KMDF device object + + DevCtx - Pointer to our context space for the device object + +Return Value: + + None + +--*/ +{ + PPO_FX_DEVICE_POWER_NOT_REQUIRED_CALLBACK + devicePowerNotRequiredCallback = NULL; + + // + // If the driver layer supplied a device-power-not-required callback, invoke + // it + // + devicePowerNotRequiredCallback = + DevCtx->DriverLayerPoFxCallbacks.DevicePowerNotRequiredCallback; + if (NULL != devicePowerNotRequiredCallback) { + devicePowerNotRequiredCallback(DevCtx->DriverLayerPoFxContext); + } + + if ((PfhS0IdleSupportedPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig) || + (PfhS0IdleSupportedNotPowerPageable == + DevCtx->DeviceInitSettings.S0IdleConfig)) { + // + // If S0-idle power management is supported, enable KMDF to power down + // the device to Dx. + // + WdfDeviceResumeIdle(Device); + } + + return; +} + +VOID +_PowerRequiredPassiveHandler( + _In_ WDFWORKITEM WorkItem + ) +/*++ +Routine Description: + + This is the callback function invoked at passive level in the context of a + system worker thread when we queue a work item in response to the device- + power-required callback. + +Arguments: + + WorkItem - Handle to the KMDF work item object + +Return Value: + + None + +--*/ +{ + NTSTATUS status; + WDFDEVICE device = NULL; + PPOFX_DEVICE_CONTEXT devCtx = NULL; + + PAGED_CODE(); + + // + // Get the handle to the KMDF device object + // + device = WdfWorkItemGetParentObject(WorkItem); + + // + // Get the device context + // + devCtx = HelperGetDeviceContext(device); + + // + // Make a blocking call to WdfDeviceStopIdle in order to get KMDF to bring + // the device to D0 and/or keep it in D0. + // + status = WdfDeviceStopIdle(device, TRUE /*WaitForD0*/); + if (FALSE == NT_SUCCESS(status)) { + // + // The call to WdfDeviceStopIdle failed. However, we need to call + // PoFxReportDevicePoweredOn regardless of success or failure because + // we need to unblock the power framework which is waiting for us to + // make this call. In case of failure, KMDF would have already declared + // the device to be in a failed state and initiated a PNP removal. + // + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceStopIdle failed for WDFDEVICE %p. " + "%!status!.", + device, + status); + + // + // Fall through to PoFxReportDevicePoweredOn call below + // || || || + // || || || + // \/ \/ \/ + // + } + + // + // Inform the power framework that the device is now powered on + // + PoFxReportDevicePoweredOn(devCtx->PoHandle); + return; +} + +#endif // PFH_S0IDLE_SUPPORTED \ No newline at end of file diff --git a/pofx/WDF/Driver/MultiComp/lib/s0idle.h b/pofx/WDF/Driver/MultiComp/lib/s0idle.h new file mode 100644 index 00000000..0ad10327 --- /dev/null +++ b/pofx/WDF/Driver/MultiComp/lib/s0idle.h @@ -0,0 +1,113 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + s0idle.h + +Abstract: + Private header file that enables the power framework library to be + conditionally compiled either with or without S0-idle power management + support, based on the value of the PFH_S0IDLE_SUPPORTED compile-time + switch. + +Environment: + + Kernel mode + +--*/ + +#if !defined(_S0IDLE_H_) +#define _S0IDLE_H_ + +EVT_WDF_WORKITEM _PowerRequiredPassiveHandler; + +#if PFH_S0IDLE_SUPPORTED + +// +// The power framework library is compiled with S0-idle power management support +// +NTSTATUS +InitializeDeviceSettingsForS0Idle( + _In_ WDFDEVICE Device, + _Inout_ PPOFX_DEVICE_CONTEXT DevCtx + ); + +NTSTATUS +StopDeviceIdleOnDeviceStart( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ); + +VOID +DevicePowerRequiredForS0Idle( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ); + +VOID +DevicePowerNotRequiredForS0Idle( + _In_ WDFDEVICE Device, + _In_ PPOFX_DEVICE_CONTEXT DevCtx + ); + +#define INITIALIZE_DEVICE_SETTINGS_FOR_S0_IDLE(_Device, _DevCtx) \ + InitializeDeviceSettingsForS0Idle(_Device, _DevCtx) + +#define STOP_DEVICE_IDLE_ON_DEVICE_START(_Status, _Device, _DevCtx, _ExitLabel)\ +{ \ + _Status = StopDeviceIdleOnDeviceStart(_Device, _DevCtx); \ + if (FALSE == NT_SUCCESS(_Status)) { \ + goto _ExitLabel; \ + } \ +} + +// +// For PDOs, if the device is restarted after being disabled, we need to +// prevent device idle until the power framework permits it. Therefore, we +// reset the StopIdleInvokedOnDeviceStart value here, so that a subsequent +// restart causes us to invoke WdfDeviceStopIdle to prevent device idling. +// +#define SELF_MANAGED_IO_FLUSH_FOR_S0_IDLE(_DevCtx) \ +{ \ + _DevCtx->StopIdleInvokedOnDeviceStart = FALSE; \ +} + +#define DEVICE_POWER_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) \ + DevicePowerRequiredForS0Idle(_Device,_DevCtx) + +#define DEVICE_POWER_NOT_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) \ + DevicePowerNotRequiredForS0Idle(_Device, _DevCtx) + +#else // PFH_S0IDLE_SUPPORTED + + +// +// The power framework library is compiled without S0-idle power management +// support +// +#define INITIALIZE_DEVICE_SETTINGS_FOR_S0_IDLE(_Device, _DevCtx) (0) +#define STOP_DEVICE_IDLE_ON_DEVICE_START(_Status, _Device, _DevCtx, _ExitLabel)\ + (0) +#define SELF_MANAGED_IO_FLUSH_FOR_S0_IDLE(_DevCtx) (0) + +// +// The current implementation does not support S0-idle. It assumes that the +// device always remains in D0 unless there is a system sleep transition, +// resource rebalance or device removal. Therefore, we call +// PoFxReportDevicePoweredOn immediately. +// +#define DEVICE_POWER_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) \ + PoFxReportDevicePoweredOn(_DevCtx->PoHandle) + +#define DEVICE_POWER_NOT_REQUIRED_FOR_S0_IDLE(_Device, _DevCtx) (0) + +#endif // PFH_S0IDLE_SUPPORTED + +#endif // _S0IDLE_H_ diff --git a/pofx/WDF/Driver/SingleComp/HwSim.c b/pofx/WDF/Driver/SingleComp/HwSim.c new file mode 100644 index 00000000..26f8faf0 --- /dev/null +++ b/pofx/WDF/Driver/SingleComp/HwSim.c @@ -0,0 +1,265 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + HwSim.c + +Abstract: + This module implements a simple hardware simulator that simulates reading of + data from the device's components. In this sample, the "data" that is read + is simply the bitwise complement of the component number. In other words, + the data for component number 'X' is simply '~X'. + + The hardware simulator also verifies that when a component is read, the + device is in D0 and the component is in F0. If not, it breaks into the + debugger. + +Environment: + + Kernel mode + +--*/ + +#include "include.h" +#include "HwSim.h" +#include "HwSim.tmh" + +NTSTATUS +HwSimInitialize( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine initializes the hardware simulator + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttributes; + PHWSIM_CONTEXT devCtx; + + // + // Allocate our context for this device + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, + HWSIM_CONTEXT); + status = WdfObjectAllocateContext((WDFOBJECT) Device, + &objectAttributes, + (PVOID*) &devCtx); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfObjectAllocateContext failed with %!status!", + status); + goto exit; + } + + devCtx->FirstD0Entry = TRUE; + + status = STATUS_SUCCESS; + +exit: + return status; +} + +VOID +HwSimD0Entry( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine simulates the device entering D0 + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + None + +--*/ +{ + PHWSIM_CONTEXT devCtx; + + devCtx = HwSimGetDeviceContext(Device); + + if (devCtx->FirstD0Entry) { + devCtx->FirstD0Entry = FALSE; + + // + // On the first D0 entry component is in F0 state + // + devCtx->ComponentFState = 0; + } + + devCtx->DevicePoweredOn = TRUE; + + return; +} + +VOID +HwSimD0Exit( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine simulates the device exiting D0 + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + None + +--*/ +{ + PHWSIM_CONTEXT devCtx; + + devCtx = HwSimGetDeviceContext(Device); + + devCtx->DevicePoweredOn = FALSE; + + return; +} + +VOID +HwSimFStateChange( + _In_ WDFDEVICE Device, + _In_ ULONG State + ) +/*++ +Routine Description: + + This routine simulates a component changing its F-state + +Arguments: + + Device - Handle to the framework device object + + State - New F-state for the component + +Return Value: + + None + +--*/ +{ + PHWSIM_CONTEXT devCtx; + + devCtx = HwSimGetDeviceContext(Device); + + // + // Verify that the device is powered on + // + if (FALSE == devCtx->DevicePoweredOn) { + // + // This means that our driver is handling an F state transition while + // device is not in D0. + // + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Expected device to be powered on, but it was not."); + + WdfVerifierDbgBreakPoint(); + } + + ASSERT(devCtx->DevicePoweredOn); + + // + // Put the component in the requested F-state + // + devCtx->ComponentFState = State; + + // + // For an actual hardware, save any hardware state on Fx transition + // and restore state on F0 transition. + // + + return; +} + +ULONG +HwSimReadComponent( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + This routine simulates the reading of data from a component + +Arguments: + + Device - Handle to the framework device object + + Component - Component from which data is being read + +Return Value: + + A ULONG value representing the data that was read from the component + +--*/ +{ + ULONG componentData; + PHWSIM_CONTEXT devCtx; + ULONG component = 0; + + devCtx = HwSimGetDeviceContext(Device); + + // + // Verify that the device is powered on + // + if (FALSE == devCtx->DevicePoweredOn) { + // + // This means that our driver is attempting to read from the component + // while the device is not powered on. + // + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Expected device to be powered on, but it was not."); + + WdfVerifierDbgBreakPoint(); + } + + // + // Verify that the component is in F0 + // + if (0 != devCtx->ComponentFState) { + // + // This means that our driver is attempting to read from the component + // while the component is not in F0. + // + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - Expected component to be in F0, but it was in F%d.", + devCtx->ComponentFState); + + WdfVerifierDbgBreakPoint(); + } + + ASSERT(devCtx->DevicePoweredOn); + ASSERT(0 == devCtx->ComponentFState); + + // + // In this sample, component data is just a bit-wise complement of the + // component number. + // + componentData = ~component; + return componentData; +} diff --git a/pofx/WDF/Driver/SingleComp/HwSim.h b/pofx/WDF/Driver/SingleComp/HwSim.h new file mode 100644 index 00000000..a43223fb --- /dev/null +++ b/pofx/WDF/Driver/SingleComp/HwSim.h @@ -0,0 +1,74 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + HwSim.h + +Abstract: + Header file for the hardware simulation module of the KMDF sample driver for + a single-component device. + +Environment: + + Kernel mode + +--*/ + +#pragma once + +NTSTATUS +HwSimInitialize( + _In_ WDFDEVICE Device + ); + +VOID +HwSimD0Entry( + _In_ WDFDEVICE Device + ); + +VOID +HwSimD0Exit( + _In_ WDFDEVICE Device + ); + +VOID +HwSimFStateChange( + _In_ WDFDEVICE Device, + _In_ ULONG State + ); + +ULONG +HwSimReadComponent( + _In_ WDFDEVICE Device + ); + +// +// This structure represents the hardware simulation module's device context +// space +// +typedef struct _HWSIM_CONTEXT { + // + // F-state of the component + // + ULONG ComponentFState; + + // + // The following member tracks whether or not the device is in D0 + // + BOOLEAN DevicePoweredOn; + + // + // The following member tracks whether or not we have previously entered the + // D0 state for this device + // + BOOLEAN FirstD0Entry; +} HWSIM_CONTEXT, *PHWSIM_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HWSIM_CONTEXT, HwSimGetDeviceContext) diff --git a/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj b/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj new file mode 100644 index 00000000..3f0b05c0 --- /dev/null +++ b/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj @@ -0,0 +1,176 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {17F25816-A09C-44DF-99A3-16D0FF5FCD66} + $(MSBuildProjectName) + 1 + Debug + Win32 + {D3CED862-FD94-4428-9E76-0D63654856A3} + + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + Driver + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + driver.h + + + $(InfArch) + true + .\$(IntDir)\SingleComponentFStateSample.inf + + + + SingleComponentFStateDriver + + + SingleComponentFStateDriver + + + SingleComponentFStateDriver + + + SingleComponentFStateDriver + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj.Filters b/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj.Filters new file mode 100644 index 00000000..a8eba88d --- /dev/null +++ b/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj.Filters @@ -0,0 +1,37 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C09CD711-AD25-4CEA-8535-7051049866B9} + + + h;hpp;hxx;hm;inl;inc;xsd + {8C669253-AA2C-4BA8-970C-BE8E4A5BEB6C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {2086162C-ED1C-4446-9A17-03967584AA37} + + + inf;inv;inx;mof;mc; + {ACF33661-8D77-4D77-ACD7-ABC80DD52BE0} + + + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + \ No newline at end of file diff --git a/pofx/WDF/Driver/SingleComp/SingleComponentFStateSample.inx b/pofx/WDF/Driver/SingleComp/SingleComponentFStateSample.inx new file mode 100644 index 00000000..1c555f11 --- /dev/null +++ b/pofx/WDF/Driver/SingleComp/SingleComponentFStateSample.inx @@ -0,0 +1,105 @@ +;/*++ +; +;Copyright (c) 1990-2000 Microsoft Corporation +; +;Module Name: +; SingleComponentFStateSample.INF +; +;Abstract: +; INF file for installing SingleComponentFStateSample Driver +; +;Installation Notes: +; Using Devcon: Type "devcon install SingleComponentFStateSample.inf root\SingleComponentFStateDevice" to install +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFT% +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +SingleComponentFStateDriver.sys = 1,, + +;***************************************** +; SingleComponentFStateDevice Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +; Decorated model section take precedence over undecorated +; ones on XP and later. +[Standard.NT$ARCH$] +%SingleComponentFStateDevice.DeviceDesc%=SingleComponentFStateDevice, root\SingleComponentFStateDevice + +[SingleComponentFStateDevice.NT] +CopyFiles=Drivers_Dir + +[Drivers_Dir] +SingleComponentFStateDriver.sys + + +;-------------- Service installation +[SingleComponentFStateDevice.NT.Services] +AddService = SingleComponentFStateSvc,%SPSVCINST_ASSOCSERVICE%, SingleComponentFStateSvc_Service_Inst + +; -------------- SingleComponentFStateDevice driver install sections +[SingleComponentFStateSvc_Service_Inst] +DisplayName = %SingleComponentFStateSvc.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\SingleComponentFStateDriver.sys + +; +;--- SingleComponentFStateDevice Coinstaller installation ------ +; + +[DestinationDirs] +SingleComponentFStateDevice_CoInstaller_CopyFiles = 11 + +[SingleComponentFStateDevice.NT.CoInstallers] +AddReg=SingleComponentFStateDevice_CoInstaller_AddReg +CopyFiles=SingleComponentFStateDevice_CoInstaller_CopyFiles + +[SingleComponentFStateDevice_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[SingleComponentFStateDevice_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames + +[SingleComponentFStateDevice.NT.Wdf] +KmdfService = SingleComponentFStateSvc, SingleComponentFStateDevice_wdfsect +[SingleComponentFStateDevice_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +MSFT = "Microsoft" +StdMfg = "(Standard system devices)" +DiskId1 = "WDF Sample SingleComponentFStateDevice Installation Disk #1" +SingleComponentFStateDevice.DeviceDesc = "Single comp F State Device" +SingleComponentFStateSvc.SVCDESC = "Single comp F State Service" +ClassName = "Sample Device" diff --git a/pofx/WDF/Driver/SingleComp/driver.c b/pofx/WDF/Driver/SingleComp/driver.c new file mode 100644 index 00000000..cd7b6dd1 --- /dev/null +++ b/pofx/WDF/Driver/SingleComp/driver.c @@ -0,0 +1,1243 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Driver.c + +Abstract: + This module implements a KMDF sample driver for a single-component device. + The driver uses the power framework to manage the power state of the + component that represents device. + + The device used in this sample is a root-enumerated device whose components + are simulated entirely in software. The simulation of the components is + implemented in HwSim.h and HwSim.c. + + This driver works only on Win8 and above. + +Environment: + + Kernel mode + +--*/ + +#include "include.h" +#include "hwsim.h" + +#include +#include "AppInterface.h" + +#include "driver.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, SingleCompEvtDriverCleanup) +#pragma alloc_text (PAGE, SingleCompEvtDeviceAdd) +#pragma alloc_text (PAGE, SingleCompEvtDeviceD0Exit) +#pragma alloc_text (PAGE, AssignS0IdleSettings) +#pragma alloc_text (PAGE, AssignPowerFrameworkSettings) +#pragma alloc_text (PAGE, SingleCompWdmEvtDeviceWdmPostPoFxRegisterDevice) +#pragma alloc_text (PAGE, SingleCompWdmEvtDeviceWdmPrePoFxUnregisterDevice) +#endif + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Driver initialization entry point. This entry point is called directly by + the I/O system. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path to the + driver-specific key in the registry. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG config; + WDF_OBJECT_ATTRIBUTES attributes; + + WPP_INIT_TRACING(DriverObject, RegistryPath); + + // + // Initiialize driver config to control the attributes that are global to + // the driver. Note that framework by default provides a driver unload + // routine. If DriverEntry creates any resources that require clean-up in + // driver unload, you can manually override the default by supplying a + // pointer to the EvtDriverUnload callback in the config structure. In + // general xxx_CONFIG_INIT macros are provided to initialize most commonly + // used members. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.EvtCleanupCallback = SingleCompEvtDriverCleanup; + WDF_DRIVER_CONFIG_INIT( + &config, + SingleCompEvtDeviceAdd + ); + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + &attributes, // Driver Attributes + &config, // Driver Config Info + WDF_NO_HANDLE + ); + + if (FALSE == NT_SUCCESS(status)) { + KdPrint( ("WdfDriverCreate failed with status 0x%x\n", status)); + WPP_CLEANUP(DriverObject); + } + + return status; +} + +VOID +SingleCompEvtDriverCleanup( + _In_ WDFOBJECT Driver + ) +{ + PAGED_CODE(); + + WPP_CLEANUP(WdfDriverWdmGetDriverObject((WDFDRIVER) Driver)); +} + +NTSTATUS +SingleCompEvtDeviceAdd( + _In_ WDFDRIVER Driver, + _Inout_ PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + EvtDeviceAdd is called by the KMDF in response to AddDevice call from + the PnP manager. + +Arguments: + + Driver - Handle to the KMDF driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + WDFDEVICE device; + WDF_IO_QUEUE_CONFIG queueConfig; + FDO_DATA *fdoContext = NULL; + ULONG queueIndex = 0; + WDF_OBJECT_ATTRIBUTES objectAttributes; + WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(Driver); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttributes, FDO_DATA); + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks); + pnpCallbacks.EvtDeviceD0Entry = SingleCompEvtDeviceD0Entry; + pnpCallbacks.EvtDeviceD0Exit = SingleCompEvtDeviceD0Exit; + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpCallbacks); + + status = WdfDeviceCreate(&DeviceInit, &objectAttributes, &device); + if (!NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceCreate failed with %!status!.", + status); + goto exit; + } + + fdoContext = FdoGetContext(device); + + // + // Our initial state is active + // + fdoContext->IsActive = TRUE; + + // + // Create three power-managed queues, one each for read, write and IOCTL + // requests. The handles to these power-managed queues are stored in an + // array in the device object context space. When the component becomes idle + // we need to stop our power-managed queues. When the component becomes + // active we need to start them. In those situations, we go through this + // array of power-managed queues and stop or start each queue as + // appropriate. Handles to non-power-managed queues should not be stored in + // this array. + // + WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, + WdfIoQueueDispatchParallel); + queueConfig.EvtIoDeviceControl = SingleCompEvtIoDeviceControl; + + // + // By default, Static Driver Verifier (SDV) displays a warning if it + // doesn't find the EvtIoStop callback on a power-managed queue. + // The 'assume' below causes SDV to suppress this warning. If the driver + // has not explicitly set PowerManaged to WdfFalse, the framework creates + // power-managed queues when the device is not a filter driver. Normally + // the EvtIoStop is required for power-managed queues, but for this driver + // it is not needed b/c the driver doesn't hold on to the requests or + // forward them to other drivers. This driver completes the requests + // directly in the queue's handlers. If the EvtIoStop callback is not + // implemented, the framework waits for all driver-owned requests to be + // done before moving in the Dx/sleep states or before removing the + // device, which is the correct behavior for this type of driver. + // If the requests were taking an indeterminate amount of time to complete, + // or if the driver forwarded the requests to a lower driver/another stack, + // the queue should have an EvtIoStop/EvtIoResume. + // + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(device, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &(fdoContext->Queues[queueIndex])); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if (FALSE == NT_SUCCESS (status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfIoQueueCreate for IoDeviceControl failed with %!status!.", + status); + goto exit; + } + + status = WdfDeviceConfigureRequestDispatching(device, + fdoContext->Queues[queueIndex], + WdfRequestTypeDeviceControl); + if (FALSE == NT_SUCCESS (status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceConfigureRequestDispatching for " + "WdfRequestTypeDeviceControl failed with %!status!.", + status); + goto exit; + } + + ++queueIndex; + + WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, + WdfIoQueueDispatchParallel); + queueConfig.EvtIoRead = SingleCompEvtIoRead; + + // + // By default, Static Driver Verifier (SDV) displays a warning if it + // doesn't find the EvtIoStop callback on a power-managed queue. + // The 'assume' below causes SDV to suppress this warning. If the driver + // has not explicitly set PowerManaged to WdfFalse, the framework creates + // power-managed queues when the device is not a filter driver. Normally + // the EvtIoStop is required for power-managed queues, but for this driver + // it is not needed b/c the driver doesn't hold on to the requests or + // forward them to other drivers. This driver completes the requests + // directly in the queue's handlers. If the EvtIoStop callback is not + // implemented, the framework waits for all driver-owned requests to be + // done before moving in the Dx/sleep states or before removing the + // device, which is the correct behavior for this type of driver. + // If the requests were taking an indeterminate amount of time to complete, + // or if the driver forwarded the requests to a lower driver/another stack, + // the queue should have an EvtIoStop/EvtIoResume. + // + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(device, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &(fdoContext->Queues[queueIndex])); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if (FALSE == NT_SUCCESS (status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfIoQueueCreate for IoRead failed with %!status!.", + status); + goto exit; + } + + status = WdfDeviceConfigureRequestDispatching(device, + fdoContext->Queues[queueIndex], + WdfRequestTypeRead); + if (FALSE == NT_SUCCESS (status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceConfigureRequestDispatching for " + "WdfRequestTypeRead failed with %!status!.", + status); + goto exit; + } + + ++queueIndex; + + WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, + WdfIoQueueDispatchParallel); + queueConfig.EvtIoWrite = SingleCompEvtIoWrite; + + // + // By default, Static Driver Verifier (SDV) displays a warning if it + // doesn't find the EvtIoStop callback on a power-managed queue. + // The 'assume' below causes SDV to suppress this warning. If the driver + // has not explicitly set PowerManaged to WdfFalse, the framework creates + // power-managed queues when the device is not a filter driver. Normally + // the EvtIoStop is required for power-managed queues, but for this driver + // it is not needed b/c the driver doesn't hold on to the requests or + // forward them to other drivers. This driver completes the requests + // directly in the queue's handlers. If the EvtIoStop callback is not + // implemented, the framework waits for all driver-owned requests to be + // done before moving in the Dx/sleep states or before removing the + // device, which is the correct behavior for this type of driver. + // If the requests were taking an indeterminate amount of time to complete, + // or if the driver forwarded the requests to a lower driver/another stack, + // the queue should have an EvtIoStop/EvtIoResume. + // + __analysis_assume(queueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(device, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &(fdoContext->Queues[queueIndex])); + __analysis_assume(queueConfig.EvtIoStop == 0); + + if (FALSE == NT_SUCCESS (status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfIoQueueCreate for IoWrite failed with %!status!.", + status); + goto exit; + } + + status = WdfDeviceConfigureRequestDispatching(device, + fdoContext->Queues[queueIndex], + WdfRequestTypeWrite); + if (FALSE == NT_SUCCESS (status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceConfigureRequestDispatching for " + "WdfRequestTypeWrite failed with %!status!.", + status); + goto exit; + } + + ++queueIndex; + + ASSERT(queueIndex == QUEUE_COUNT); + + status = AssignS0IdleSettings(device); + if (!NT_SUCCESS(status)) { + goto exit; + } + + // + // If you need to talk to hardware to figure out what F-states are + // applicable this can be done in EvtSelfManagedIoInit + // (but no later than that). EvtSelfManagedIoInit gets invoked after + // EvtPrepareHardware so you'd have chance to initialize your hardware. + // + status = AssignPowerFrameworkSettings(device); + if (!NT_SUCCESS(status)) { + goto exit; + } + + // + // Create a device interface so that applications can open a handle to this + // device. + // + status = WdfDeviceCreateDeviceInterface(device, + &GUID_DEVINTERFACE_POWERFX, + NULL /* ReferenceString */); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceCreateDeviceInterface failed with %!status!.", + status); + goto exit; + } + + // + // Initialize the hardware simulator + // + status = HwSimInitialize(device); + if (FALSE == NT_SUCCESS(status)) { + goto exit; + } +exit: + return status; +} + +NTSTATUS +SingleCompEvtDeviceD0Entry( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousState + ) +/*++ +Routine Description: + + KMDF calls this routine when the device has entered D0. + +Arguments: + + Device - Handle to the framework device object + + PreviousState - Previous device power state + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + UNREFERENCED_PARAMETER(PreviousState); + + HwSimD0Entry(Device); + + return STATUS_SUCCESS; +} + +NTSTATUS +SingleCompEvtDeviceD0Exit( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) +/*++ +Routine Description: + + KMDF calls this routine when the device is about to leave D0. + +Arguments: + + Device - Handle to the framework device object + + TargetState - Device power state that the device is about to enter + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER(TargetState); + + HwSimD0Exit(Device); + + return STATUS_SUCCESS; +} + +NTSTATUS +SingleCompWdmEvtDeviceWdmPostPoFxRegisterDevice( + _In_ WDFDEVICE Device, + _In_ POHANDLE PoHandle + ) +/*++ +Routine Description: + + KMDF calls this routine after it has registered with the Power Framework + and supplies the registration handle that driver can use directly. + +Arguments: + + Device - Handle to the framework device object + + PoHandle - Handle of registration with Power Framework. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + FDO_DATA *fdoContext = NULL; + + PAGED_CODE(); + + // + // Get the device context + // + fdoContext = FdoGetContext(Device); + + // + // Save the POHANDLE + // + fdoContext->PoHandle = PoHandle; + + // + // Set latency and residency hints so that the power framework chooses lower + // powered F-states when we are idle. + // The values used here are for illustration purposes only. The driver + // should use values that are appropriate for its device. + // + PoFxSetComponentLatency( + PoHandle, + 0, // Component + (WDF_ABS_TIMEOUT_IN_MS(DEEPEST_FSTATE_LATENCY_IN_MS) + 1) + ); + PoFxSetComponentResidency( + PoHandle, + 0, // Component + (WDF_ABS_TIMEOUT_IN_SEC(DEEPEST_FSTATE_RESIDENCY_IN_SEC) + 1) + ); + + return STATUS_SUCCESS; +} + +VOID +SingleCompWdmEvtDeviceWdmPrePoFxUnregisterDevice( + _In_ WDFDEVICE Device, + _In_ POHANDLE PoHandle + ) +/*++ +Routine Description: + + KMDF calls this routine when it is about to unregister with the Power + Framework. After returning from this routine driver must not use the + supplied registration handle anymore. + +Arguments: + + Device - Handle to the framework device object + + PoHandle - Handle of registration with Power Framework. + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + FDO_DATA *fdoContext = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(PoHandle); + + // + // Get the device context + // + fdoContext = FdoGetContext(Device); + + // + // Reset the POHANDLE + // + fdoContext->PoHandle = NULL; + + return; +} + +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +AssignS0IdleSettings( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + Helper function to assign S0 idle settings for the device + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS powerPolicy; + + PAGED_CODE(); + + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&powerPolicy, + IdleCannotWakeFromS0); + powerPolicy.IdleTimeoutType = SystemManagedIdleTimeout; + + status = WdfDeviceAssignS0IdleSettings(Device, &powerPolicy); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceAssignS0IdleSettings failed with %!status!.", + status); + } + return status; +} + +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +AssignPowerFrameworkSettings( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + Helper function to assign Power Framework related settings for the device + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + An NTSTATUS value representing success or failure of the function. + +--*/ +{ + NTSTATUS status; + WDF_POWER_FRAMEWORK_SETTINGS poFxSettings; + PO_FX_COMPONENT component; + PO_FX_COMPONENT_IDLE_STATE idleStates[FSTATE_COUNT]; + + // + // Note that we initialize the 'idleStates' array below based on the + // assumption that MAX_FSTATE_COUNT is 4. + // If we increase the value of MAX_FSTATE_COUNT, we need to initialize those + // additional F-states below. If we decrease the value of MAX_FSTATE_COUNT, + // we need to remove the corresponding initializations below. + // + C_ASSERT(FSTATE_COUNT == 4); + + PAGED_CODE(); + + // + // Initialization + // + RtlZeroMemory(&component, sizeof(component)); + RtlZeroMemory(idleStates, sizeof(idleStates)); + + // + // The transition latency and residency requirement values used here are for + // illustration purposes only. The driver should use values that are + // appropriate for its device. + // + + // + // F0 + // + idleStates[0].TransitionLatency = 0; + idleStates[0].ResidencyRequirement = 0; + idleStates[0].NominalPower = 0; + + // + // F1 + // + idleStates[1].TransitionLatency = WDF_ABS_TIMEOUT_IN_MS(200); + idleStates[1].ResidencyRequirement = WDF_ABS_TIMEOUT_IN_SEC(3); + idleStates[1].NominalPower = 0; + + // + // F2 + // + idleStates[2].TransitionLatency = WDF_ABS_TIMEOUT_IN_MS(400); + idleStates[2].ResidencyRequirement = WDF_ABS_TIMEOUT_IN_SEC(6); + idleStates[2].NominalPower = 0; + + // + // F3 + // + idleStates[3].TransitionLatency = + WDF_ABS_TIMEOUT_IN_MS(DEEPEST_FSTATE_LATENCY_IN_MS); + idleStates[3].ResidencyRequirement = + WDF_ABS_TIMEOUT_IN_SEC(DEEPEST_FSTATE_RESIDENCY_IN_SEC); + idleStates[3].NominalPower = 0; + + // + // Component 0 (the only component) + // + component.IdleStateCount = FSTATE_COUNT; + component.IdleStates = idleStates; + + WDF_POWER_FRAMEWORK_SETTINGS_INIT(&poFxSettings); + + poFxSettings.EvtDeviceWdmPostPoFxRegisterDevice = + SingleCompWdmEvtDeviceWdmPostPoFxRegisterDevice; + poFxSettings.EvtDeviceWdmPrePoFxUnregisterDevice = + SingleCompWdmEvtDeviceWdmPrePoFxUnregisterDevice; + + poFxSettings.Component = &component; + poFxSettings.ComponentActiveConditionCallback = + SingleCompWdmActiveConditionCallback; + poFxSettings.ComponentIdleConditionCallback = + SingleCompWdmIdleConditionCallback; + poFxSettings.ComponentIdleStateCallback = + SingleCompWdmIdleStateCallback; + poFxSettings.PoFxDeviceContext = (PVOID) Device; + + status = WdfDeviceWdmAssignPowerFrameworkSettings(Device, &poFxSettings); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfDeviceWdmAssignPowerFrameworkSettings failed with " + "%!status!.", + status); + } + return status; +} + +_IRQL_requires_same_ +_IRQL_requires_max_(DISPATCH_LEVEL) +BOOLEAN +F0Entry( + _In_ WDFDEVICE Device + ) +/*++ +Routine Description: + + Helper function invoked when component (representing the whole device) is + requested to enter F0. + +Arguments: + + Device - Handle to the framework device object + +Return Value: + + BOOLEAN indicating whether F0 transition has completed. + +--*/ +{ + // + // Change the F-state of the component + // This includes hardware-specific operations such as: + // * enabling DMA capabilities associated with the component + // * enabling interrupts associated with the component + // * restoring component state + // + // + HwSimFStateChange(Device, 0); + return TRUE; +} + +_IRQL_requires_same_ +_IRQL_requires_max_(DISPATCH_LEVEL) +BOOLEAN +F0Exit( + _In_ WDFDEVICE Device, + _In_ ULONG State + ) +/*++ +Routine Description: + + Helper function invoked when component (representing the whole device) is + requested to exit F0. + +Arguments: + + Device - Handle to the framework device object + State - The new F-state to be entered + +Return Value: + + BOOLEAN indicating whether Fx transition has completed. + +--*/ +{ + // + // Change the F-state of the component + // This includes hardware-specific operations such as: + // * disabling DMA capabilities associated with the component + // * disabling interrupts associated with the component + // * saving component state + // + HwSimFStateChange(Device, State); + return TRUE; +} + +VOID +SingleCompWdmIdleStateCallback( + _In_ PVOID Context, + _In_ ULONG Component, + _In_ ULONG State + ) +/*++ +Routine Description: + + This callback is invoked by Power Framework to notify driver about any + F-state transition. + +Arguments: + + Context - Context supplied to Power Framework. KMDF supplies WDFDEVICE as + the context while registering with Power Framework. Hence Context + contains the KMDF device object. + + Component - Component for which F state transition is requested. Since we + have only one component this value is always 0. + +Return Value: + + BOOLEAN indicating whether Fx transition has completed. + +--*/ +{ + WDFDEVICE device = NULL; + FDO_DATA *fdoContext = NULL; + BOOLEAN transitionComplete = TRUE; + + // + // We have only component 0 + // + if (0 != Component) { + Trace(TRACE_LEVEL_ERROR,"%!FUNC! - Unexpected component %d",Component); + ASSERT(FALSE); + } + + // + // Get the device + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + fdoContext = FdoGetContext(device); + + // + // Note the new F-state + // + switch (State) { + case 0: { + transitionComplete = F0Entry(device); + } + break; + + // + // PEP may make us go to any of the F-states directly, hence we execute + // F0Exit code for all of the Fx states. + // + // Transition to any Fx state happens from F0 (and not another + // Fx state) + // + default: { + ASSERT(State < FSTATE_COUNT); + + transitionComplete = F0Exit(device, State); + } + break; + } + + if (transitionComplete) { + PoFxCompleteIdleState(fdoContext->PoHandle, 0 /* Component */); + } +} + +VOID +SingleCompEvtIoDeviceControl( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +/*++ +Routine Description: + + Callback invoked by WDFQUEUE for a Device Io Control request. + +Arguments: + + Queue - Device I/O control queue + + Request - Device I/O control request + + OutputBufferLength - Output buffer length for the I/O control + + InputBufferLength - Input buffer length for the I/O control + + IoControlCode - I/O control code + +--*/ +{ + NTSTATUS status; + PPOWERFX_READ_COMPONENT_INPUT inputBuffer = NULL; + PPOWERFX_READ_COMPONENT_OUTPUT outputBuffer = NULL; + WDFDEVICE device = NULL; + ULONG componentData; + ULONG_PTR information = 0; + FDO_DATA *fdoContext = NULL; + + // + // When we complete the request, make sure we don't get the I/O manager to + // copy any more data to the client address space than what we write to the + // output buffer. The only data that we write to the output buffer is the + // component data and the C_ASSERT below ensures that the output buffer does + // not have room to contain anything other than that. + // + C_ASSERT(sizeof(componentData) == sizeof(*outputBuffer)); + + // + // This is a power-managed queue. So our queue stop/start logic should have + // ensured that we are in the active condition when a request is dispatched + // from this queue. + // + device = WdfIoQueueGetDevice(Queue); + fdoContext = FdoGetContext(device); + if (FALSE == fdoContext->IsActive) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - IOCTL %d was dispatched from WDFQUEUE %p when the " + "component was not in an active condition.", + IOCTL_POWERFX_READ_COMPONENT, + Queue); + WdfVerifierDbgBreakPoint(); + } + + // + // Validate Ioctl code + // + if (IOCTL_POWERFX_READ_COMPONENT != IoControlCode) { + status = STATUS_NOT_SUPPORTED; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! -Unsupported IoControlCode. Expected: %d. Actual: %d." + " %!status!.", + IOCTL_POWERFX_READ_COMPONENT, + IoControlCode, + status); + goto exit; + } + + // + // Validate input buffer length + // + if (InputBufferLength != sizeof(*inputBuffer)) { + status = STATUS_INVALID_PARAMETER; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! -Invalid output buffer size. Expected: %d. Actual: %I64u." + " %!status!.", + sizeof(*inputBuffer), + InputBufferLength, + status); + goto exit; + } + + // + // Validate output buffer length + // + if (OutputBufferLength != sizeof(*outputBuffer)) { + status = STATUS_INVALID_PARAMETER; + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! -Invalid input buffer size. Expected: %d. Actual: %I64u." + " %!status!.", + sizeof(*outputBuffer), + OutputBufferLength, + status); + goto exit; + } + + // + // Get the output buffer + // + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof(*outputBuffer), + (PVOID*) &outputBuffer, + NULL // Length + ); + if (FALSE == NT_SUCCESS(status)) { + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! - WdfRequestRetrieveOutputBuffer failed with %!status!.", + status); + goto exit; + } + + // + // Read the data from the component + // + componentData = HwSimReadComponent(device); + outputBuffer->ComponentData = componentData; + information = sizeof(*outputBuffer); + + status = STATUS_SUCCESS; + +exit: + // + // Complete the request + // + WdfRequestCompleteWithInformation(Request, status, information); + return; +} + +// +// Read and write queues are only for illustration purposes - on how to stop +// multiple queues. Currently app doesn't send Read/Write to the driver. +// +VOID +SingleCompEvtIoRead( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength + ) +/*++ +Routine Description: + + Callback invoked by WDFQUEUE for a read request. + +Arguments: + + Queue - Read queue + + Request - Read request + + OutputBufferLength - Length of read + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(OutputBufferLength); + + status = STATUS_NOT_SUPPORTED; + + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! -Reads are currently not supported: %!status!.", + status); + + WdfRequestComplete(Request, status); +} + +VOID +SingleCompEvtIoWrite( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t InputBufferLength + ) +/*++ +Routine Description: + + Callback invoked by WDFQUEUE for a write request. + +Arguments: + + Queue - Write queue + + Request - Write request + + InputBufferLength - Length of write + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(InputBufferLength); + + status = STATUS_NOT_SUPPORTED; + + Trace(TRACE_LEVEL_ERROR, + "%!FUNC! -Writes are currently not supported: %!status!.", + status); + + WdfRequestComplete(Request, status); +} + +VOID +SingleCompWdmActiveConditionCallback( + _In_ PVOID Context, + _In_ ULONG Component + ) +/*++ +Routine Description: + + This callback is invoked by Power Framework to notify driver that one of its + components has become active. + +Arguments: + + Context - Context that we supplied when calling + WdfDeviceWdmAssignPowerFrameworkSettings. + + Component - Component that have become active. Since we have only one + component this value is always 0. + +Return Value: + + None + +--*/ +{ + WDFDEVICE device; + FDO_DATA *fdoContext = NULL; + UCHAR i = 0; + + // + // We have only component 0 + // + if (0 != Component) { + Trace(TRACE_LEVEL_ERROR,"%!FUNC! - Unexpected component %d",Component); + ASSERT(FALSE); + } + + // + // Get the device + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + fdoContext = FdoGetContext(device); + + // + // Mark ourselves as active + // + fdoContext->IsActive = TRUE; + + // + // Start power-managed queues + // + for (i = 0; i < QUEUE_COUNT; i++) { + WdfIoQueueStart(fdoContext->Queues[i]); + } + + return; +} + +VOID +SingleCompWdmIdleConditionCallback( + _In_ PVOID Context, + _In_ ULONG Component + ) +/*++ +Routine Description: + This callback is invoked by Power Framework to notify driver that one of its + components has become idle. + +Arguments: + + Context - Context that we supplied when calling + WdfDeviceWdmAssignPowerFrameworkSettings. + + Component - Component that have become idle. Since we have only one + component this value is always 0. + +Return Value: + + None + +--*/ +{ + WDFDEVICE device; + FDO_DATA *fdoContext = NULL; + UCHAR i = 0; + + // + // We have only component 0 + // + if (0 != Component) { + Trace(TRACE_LEVEL_ERROR,"%!FUNC! - Unexpected component %d",Component); + ASSERT(FALSE); + } + + // + // Get the device + // + device = (WDFDEVICE) Context; + + // + // Get the device context + // + fdoContext = FdoGetContext(device); + + // + // Initialize the count of queues to be stopped. + // + // Until we complete this idle transition there cannot be other idle + // transitions, which is only where we use this count. Thus nothing races + // with it. + // + // If you have other code that stops/starts queues, it may be simpler + // to queue a work-item from this callback which stops all the queues + // synchronously. Synchronous queue stop cannot be done from here since + // this callback may be called at DISPATCH_LEVEL. + // + fdoContext->QueueStopCount = QUEUE_COUNT; + + // + // Stop power-managed queues + // + for (i = 0; i < QUEUE_COUNT; i++) { +#pragma warning(suppress: 6387) // passing NULL Context parameter is safe in this instance + WdfIoQueueStop(fdoContext->Queues[i], + SingleCompEvtQueueStopComplete, + NULL /* Context */); + } + + // + // The idle transition will complete asynchronously. We'll call + // PoFxCompleteIdleCondition to complete it when all the queues have been + // stopped. + // + // IMPORTANT NOTE + // ============== + // Given that the idle transition does not complete until all the power- + // managed queues have been stopped, it is extremely important for the + // driver to ensure that stopping of power-managed queues is reasonably + // quick. If the driver fails to ensure this, the power framework can remain + // stuck in the idle transition for a long time, which could hamper its + // ability to put the component in a low-power F-state. This could + // negatively impact any power savings that can be gained via component + // power management. + // + // In order to ensure that idle transitions can complete quickly, the driver + // should quickly process any requests that are dispatched to it via a + // power-managed queue. If the driver forwards a request (that was received + // via a power-managed queue) to an I/O target that might keep the request + // pending for a long time, then the driver should cancel the request when + // the component idle condition callback is invoked. This is because the + // power-managed queue cannot be stopped while the request is pending in the + // I/O target. + // + return; +} + +VOID +SingleCompEvtQueueStopComplete( + _In_ WDFQUEUE Queue, + _In_ WDFCONTEXT Context + ) +/*++ +Routine Description: + + Callback invoked by KMDF when stop transiton of the supplied queue has + completed. + +Arguments: + + Queue - Queue whose stop transition has completed + + Context - The context we supply while calling WdfIoQueueStop. + +Return value: + None +--*/ +{ + PFDO_DATA fdoContext; + WDFDEVICE device; + + UNREFERENCED_PARAMETER(Context); + + device = WdfIoQueueGetDevice(Queue); + + fdoContext = WdfObjectGetTypedContext(device, FDO_DATA); + + if (0 == InterlockedDecrement(&(fdoContext->QueueStopCount))) { + // + // All the queues have been stopped. Mark ourselves as idle and complete + // the idle transition. + // + fdoContext->IsActive = FALSE; + PoFxCompleteIdleCondition(fdoContext->PoHandle, 0 /* Component */); + } +} diff --git a/pofx/WDF/Driver/SingleComp/driver.h b/pofx/WDF/Driver/SingleComp/driver.h new file mode 100644 index 00000000..850657c0 --- /dev/null +++ b/pofx/WDF/Driver/SingleComp/driver.h @@ -0,0 +1,150 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + Driver.h + +Abstract: + Header file for the KMDF sample driver for a single-component device. + +Environment: + + Kernel mode + +--*/ + +#define QUEUE_COUNT 3 +#define FSTATE_COUNT 4 +#define DEEPEST_FSTATE_LATENCY_IN_MS 800 +#define DEEPEST_FSTATE_RESIDENCY_IN_SEC 12 + +// +// This structure represents the driver's device context space +// +typedef struct _FDO_DATA +{ + // + // Handle of registration with Power Framework + // + POHANDLE PoHandle; + + // + // Power-managed queues used for the device. + // We track them here so that we can stop/start them on active/idle + // transitions. + // + WDFQUEUE Queues[QUEUE_COUNT]; + + // + // Count of queues to be stopped. + // + // It is initialized to QUEUE_COUNT at the beginning of a transition to an + // idle condition. It is decremented everytime a queue completes stop + // transition. + // + // When this count reaches 0 we can transition device into an idle + // condition. + // + LONG QueueStopCount; + + // + // Tracks the active/idle state of the component + // + BOOLEAN IsActive; +} FDO_DATA, *PFDO_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DATA, FdoGetContext) + +// +// Driver's KMDF callbacks +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD SingleCompEvtDeviceAdd; +EVT_WDF_OBJECT_CONTEXT_CLEANUP SingleCompEvtDriverCleanup; + +EVT_WDF_DEVICE_D0_ENTRY SingleCompEvtDeviceD0Entry; +EVT_WDF_DEVICE_D0_EXIT SingleCompEvtDeviceD0Exit; + + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL SingleCompEvtIoDeviceControl; +EVT_WDF_IO_QUEUE_IO_READ SingleCompEvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE SingleCompEvtIoWrite; +EVT_WDF_IO_QUEUE_STATE SingleCompEvtQueueStopComplete; + +EVT_WDFDEVICE_WDM_POST_PO_FX_REGISTER_DEVICE SingleCompWdmEvtDeviceWdmPostPoFxRegisterDevice; +EVT_WDFDEVICE_WDM_PRE_PO_FX_UNREGISTER_DEVICE SingleCompWdmEvtDeviceWdmPrePoFxUnregisterDevice; + +// +// Driver's power framework callbacks +// +PO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK SingleCompWdmActiveConditionCallback; +PO_FX_COMPONENT_IDLE_CONDITION_CALLBACK SingleCompWdmIdleConditionCallback; +PO_FX_COMPONENT_IDLE_STATE_CALLBACK SingleCompWdmIdleStateCallback; + +// +// Helper functions +// + +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +AssignS0IdleSettings( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +AssignPowerFrameworkSettings( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_same_ +_IRQL_requires_max_(DISPATCH_LEVEL) +BOOLEAN +F0Entry( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_same_ +_IRQL_requires_max_(DISPATCH_LEVEL) +BOOLEAN +F0Exit( + _In_ WDFDEVICE Device, + _In_ ULONG State + ); + +// +// Define the tracing flags. +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + MyDriverTraceControl, (f9eb5c3a,c292,4c69,8b52,ccf043c25ab0), \ + \ + WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ + ) + +#define WPP_FLAG_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAG_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); +// end_wpp +// diff --git a/pofx/WDF/Driver/SingleComp/include.h b/pofx/WDF/Driver/SingleComp/include.h new file mode 100644 index 00000000..d80fa458 --- /dev/null +++ b/pofx/WDF/Driver/SingleComp/include.h @@ -0,0 +1,3 @@ +#include +#include +#include "driver.h" \ No newline at end of file diff --git a/pofx/WDF/ReadMe.md b/pofx/WDF/ReadMe.md new file mode 100644 index 00000000..41a5a620 --- /dev/null +++ b/pofx/WDF/ReadMe.md @@ -0,0 +1,114 @@ +KMDF Power Framework (PoFx) Sample +================================== + +This solution consists of two samples that demonstrate how a KMDF driver can implement F-state-based power management. The SingleComp sample demonstrates how a KMDF driver can implement F-state-based power management for a device that has only a single component. The MultiComp sample demonstrates how a KMDF driver can implement F-state-based power management for a device that has an arbitrary number of components that can be individually power-managed. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Related technologies +-------------------- +[Supporting Functional Power States](http://msdn.microsoft.com/en-us/library/windows/hardware/hh451017) + +SingleComp Overview +------------------- + +This sample demonstrates how a KMDF driver can implement F-state-based power management for a device that has only a single component. + +The sample illustrates the use of the [**WdfDeviceWdmAssignPowerFrameworkSettings**](http://msdn.microsoft.com/en-us/library/windows/hardware/hh451097) method to specify power framework settings for the single component that represents the entire device. The power framework settings that can be specified include the F-states for the component and the power framework callbacks that are invoked when the component's active/idle condition or its F-state changes. + +The sample also illustrates the use of the [**WdfDeviceAssignS0IdleSettings**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff545903) method to instruct KMDF to begin power-management of the device (and the component that represents the entire device). + +Installation +------------ + +The driver can be installed on a root-enumerated device using the devcon.exe tool. + +1. Obtain the devcon.exe tool from the WDK +2. Copy the driver binary, INF file and the KMDF coinstaller to a directory on your test machine. + + **Note**   + + You can obtain redistributable framework updates by downloading the Wdfcoinstaller.msi package from [WDK 8 Redistributable Components](http://go.microsoft.com/fwlink/p/?LinkID=226396). This package performs a silent install into the directory of your Windows Driver Kit (WDK) installation. You will see no confirmation that the installation has completed. You can verify that the redistributables have been installed on top of the WDK by ensuring there is a redist\\wdf directory under the root directory of the WDK, %ProgramFiles(x86)%\\Windows Kits\\8.0. + +3. Run the command "devcon.exe install SingleComponentFStateSample.inf root\\SingleComponentFStateDevice". + +Use the PowerFxApp.exe application to send I/O requests to the driver. Running the command "PowerFxApp.exe /?" displays detailed usage information. + +For detailed information about implementing F-state-based power management for a single component device, see [Supporting Multiple Functional Power States for Single-Component Devices](http://msdn.microsoft.com/en-us/library/windows/hardware/hh451032). + +MultiComp Overview +------------------ + +This sample demonstrates how a KMDF driver can implement F-state-based power management for a device that has an arbitrary number of components that can be individually power-managed. + +The sample driver statically links to a helper library (WdfPoFx.lib) that encapsulates all of the generic code to interact with the power framework. The device-specific code is implemented in the driver itself, outside of the helper library. The idea behind this organizing the code in this manner is make the helper library reusable by other drivers. The directory structure for the sample is as follows: + +- The helper library is implemented in the 'lib' subdirectory. +- The interface between the helper library and the rest of the driver code is defined in the 'inc' subdirectory. +- The driver is implemented in the 'driver' subdirectory. + +Installation +------------ + +The driver can be installed on a root-enumerated device using the devcon.exe tool. + +1. Obtain the devcon.exe tool from the WDK +2. Copy the driver binary, INF file and the KMDF coinstaller to a directory on your test machine. + **Note**  Starting in Windows 8.1, the WDK no longer contains the co-installers by default. You can obtain the co-installers by downloading theWdfcoinstaller.msi package from [WDK 8 Redistributable Components](http://go.microsoft.com/fwlink/p/?LinkID=226396). +3. Run the command "devcon.exe install WdfMultiComp.inf WDF\\WdfMultiComp". + +Testing +------- + +Use the PowerFxApp.exe application to send I/O requests to the driver. Running the command "PowerFxApp.exe /?" displays detailed usage information. + +Design overview +--------------- + +The driver controls a device that has more than one component. It needs to access one of those components for processing each I/O request that it receives. The specific component that it needs to access depends on the I/O request that it receives. + +In order to support this, the driver creates one top-level, power-managed queue to receive all its requests. It also creates one secondary, power-managed queue for each of its components. These secondary queues are called component queues. This is shown in the diagram below. + +![Queue Implementation for MultiComp Sample](images/multicompqueues.png) + +When the driver's dispatch routine for the top-level queue is invoked, it examines the request to determine which component it needs to access in order to process the request. Then, it forwards the request to the component queue for the component that it needs to access for that request. When the driver's dispatch routine for the component queue is invoked, it accesses the component hardware to process the request. + +The driver's top-level queue and component queues are all power-managed so KMDF ensures that the device is in D0 while the queues are in a dispatching state. The key point to note is that the driver is designed to maintain a component queue in a dispatching state only when the component is active. In order to achieve this, the driver stops the component queue when the component becomes idle and starts the component queue when the component becomes active. (To be precise, this mechanism of stopping and starting queues is encapsulated in the power framework helper library used by the driver). Thus, the driver is able to ensure that when the component queue is in a dispatching state, not only is the device in D0 but the component corresponding to that queue is also active. Thus it is safe to access the component hardware when the component queue's dispatch routine is invoked. + +Implementation notes +-------------------- + +The driver uses the power framework helper library to manage most of its interactions with the power framework. In order to achieve this, during device initialization, the driver performs the following tasks in its [*EvtDriverDeviceAdd*](http://msdn.microsoft.com/en-us/library/windows/hardware/ff541693) callback. + +- Enables the helper library to register its own KMDF callbacks for PNP and power-management of the device. +- Provides the helper library with power-framework-related information about the device. +- Provides the helper library with information about the component queues. + +During I/O request processing, the driver uses routines provided by helper library to forward requests to component queues and also to complete requests. + +The main tasks performed by the power framework helper library on behalf of the driver are: + +- Registration and unregistration with the power framework. +- Stopping component queues when the corresponding components become idle and starting them when the corresponding components become active. +- Notifying the power framework when the device returns to its working state (D0) in response to the system returning from a low-power state to the working state (S0). + +The power framework helper library does not have any hardware-specific information, so any tasks that are specific to the device's hardware are performed by the driver. In this sample, the device hardware is represented by a very simple simulation. The notable hardware-specific tasks in this sample are: + +- Accessing component hardware to process I/O requests. +- Accessing component hardware to change the component's F-state. + +As mentioned earlier, the hardware access shown is this sample is entirely simulated in software. This sample does not work with a real device – it installs on a root-enumerated software device. + +S0-idle power management support +-------------------------------- + +The power framework helper library implements support for S0-idle power management for the device. Note that this is different from the component power management support that is enabled by the power framework. Component power management enables individual components of the device to be power-managed by putting them in different F-states while the device is in a working state (D0). S0-idle power management for the device enables the device as a whole to be power-managed by putting it into different D-states while the system is in a working state (S0). + +The code to implement S0-idle power management support for the device is conditionally compiled based on the value of the PFH\_S0IDLE\_SUPPORTED compiler switch. If the switch is set to a nonzero value, the code to implement S0-idle power management support is included. If set to zero, the code is omitted thereby resulting in a smaller binary size. Thus, a driver that requires S0-idle power management for the device can use the power framework helper library's support for it but a driver that does not require it can reduce its binary size by omitting the code that is specific to S0-idle power management. + +Additional Information +---------------------- + +For detailed information about implementing F-state-based power management for a multiple component device, see [Supporting Multiple Functional Power States for Multiple-Component Devices](http://msdn.microsoft.com/en-us/library/windows/hardware/hh451028). + diff --git a/pofx/WDF/inc/AppInterface.h b/pofx/WDF/inc/AppInterface.h new file mode 100644 index 00000000..60173369 --- /dev/null +++ b/pofx/WDF/inc/AppInterface.h @@ -0,0 +1,69 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + AppInterface.h + +Abstract: + + This module contains the common declarations shared by the driver and the + user application for the PowerFx sample. + +Environment: + + user and kernel + +--*/ + +#if !defined(_APPINTERFACE_H_) +#define _APPINTERFACE_H_ + +#include + +// +// Component count for multi-component device +// +#define COMPONENT_COUNT 4 + +// +// Interface GUID for the PowerFx driver +// {D21FD4DB-5FB6-4df2-B73B-5E7F8632B390} +// +DEFINE_GUID( + GUID_DEVINTERFACE_POWERFX, + 0xd21fd4db, 0x5fb6, 0x4df2, 0xb7, 0x3b, 0x5e, 0x7f, 0x86, 0x32, 0xb3, 0x90 + ); + +// +// IOCTLs that the application sends to the PowerFx driver +// +#define IOCTL_INDEX_POWERFX 0x900 +#define FILE_DEVICE_POWERFX 0x9000 + +// +// This IOCTL is used to read a data from a component. The app initializes the +// input buffer with the component number to read. The driver writes the data +// from that component to the output buffer. +// In this sample, the "data" for a given component is just the bit-wise +// complement of the component number. In other words, the data for component +// number 'X' is simply '~X'. +// +#define IOCTL_POWERFX_READ_COMPONENT CTL_CODE(FILE_DEVICE_POWERFX, \ + IOCTL_INDEX_POWERFX, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + +// +// Input and output buffers for IOCTL_POWERFX_READ_COMPONENT +// +typedef struct _POWERFX_READ_COMPONENT_INPUT { + ULONG ComponentNumber; +} POWERFX_READ_COMPONENT_INPUT, *PPOWERFX_READ_COMPONENT_INPUT; + +typedef struct _POWERFX_READ_COMPONENT_OUTPUT { + ULONG ComponentData; +} POWERFX_READ_COMPONENT_OUTPUT, *PPOWERFX_READ_COMPONENT_OUTPUT; + +#endif // _APPINTERFACE_H_ \ No newline at end of file diff --git a/pofx/WDF/pofx.sln b/pofx/WDF/pofx.sln new file mode 100644 index 00000000..16edf7d3 --- /dev/null +++ b/pofx/WDF/pofx.sln @@ -0,0 +1,83 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{F0B333C5-FFAD-425B-8064-EB950E9718B8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{24B27C7D-03C0-4286-8250-57110C794853}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MultiComp", "MultiComp", "{36BE1DCF-A58E-45E4-B458-3640AA1FBCC1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{2F266524-ED06-4654-BCFB-DBB9FE440BD7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{8CA441A3-D099-4853-9D9D-3AE4DCBEC7E8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SingleComp", "SingleComp", "{18E8D511-CACD-410E-8791-41511E36F2D5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerFxApp", "App\PowerFxApp.vcxproj", "{3E233D0A-F988-4C9D-B15A-49B54025E11F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WdfPoFx", "Driver\MultiComp\lib\WdfPoFx.vcxproj", "{21F832DD-6A4C-4E5F-941E-15BF98CE4137}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WdfMultiComp", "Driver\MultiComp\driver\WdfMultiComp.vcxproj", "{45DBDC5F-2109-432B-8149-29D3693D57E1}" + ProjectSection(ProjectDependencies) = postProject + {21F832DD-6A4C-4E5F-941E-15BF98CE4137} = {21F832DD-6A4C-4E5F-941E-15BF98CE4137} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SingleComponentFStateDriver", "Driver\SingleComp\SingleComponentFStateDriver.vcxproj", "{17F25816-A09C-44DF-99A3-16D0FF5FCD66}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3E233D0A-F988-4C9D-B15A-49B54025E11F}.Debug|Win32.ActiveCfg = Debug|Win32 + {3E233D0A-F988-4C9D-B15A-49B54025E11F}.Debug|Win32.Build.0 = Debug|Win32 + {3E233D0A-F988-4C9D-B15A-49B54025E11F}.Release|Win32.ActiveCfg = Release|Win32 + {3E233D0A-F988-4C9D-B15A-49B54025E11F}.Release|Win32.Build.0 = Release|Win32 + {3E233D0A-F988-4C9D-B15A-49B54025E11F}.Debug|x64.ActiveCfg = Debug|x64 + {3E233D0A-F988-4C9D-B15A-49B54025E11F}.Debug|x64.Build.0 = Debug|x64 + {3E233D0A-F988-4C9D-B15A-49B54025E11F}.Release|x64.ActiveCfg = Release|x64 + {3E233D0A-F988-4C9D-B15A-49B54025E11F}.Release|x64.Build.0 = Release|x64 + {21F832DD-6A4C-4E5F-941E-15BF98CE4137}.Debug|Win32.ActiveCfg = Debug|Win32 + {21F832DD-6A4C-4E5F-941E-15BF98CE4137}.Debug|Win32.Build.0 = Debug|Win32 + {21F832DD-6A4C-4E5F-941E-15BF98CE4137}.Release|Win32.ActiveCfg = Release|Win32 + {21F832DD-6A4C-4E5F-941E-15BF98CE4137}.Release|Win32.Build.0 = Release|Win32 + {21F832DD-6A4C-4E5F-941E-15BF98CE4137}.Debug|x64.ActiveCfg = Debug|x64 + {21F832DD-6A4C-4E5F-941E-15BF98CE4137}.Debug|x64.Build.0 = Debug|x64 + {21F832DD-6A4C-4E5F-941E-15BF98CE4137}.Release|x64.ActiveCfg = Release|x64 + {21F832DD-6A4C-4E5F-941E-15BF98CE4137}.Release|x64.Build.0 = Release|x64 + {45DBDC5F-2109-432B-8149-29D3693D57E1}.Debug|Win32.ActiveCfg = Debug|Win32 + {45DBDC5F-2109-432B-8149-29D3693D57E1}.Debug|Win32.Build.0 = Debug|Win32 + {45DBDC5F-2109-432B-8149-29D3693D57E1}.Release|Win32.ActiveCfg = Release|Win32 + {45DBDC5F-2109-432B-8149-29D3693D57E1}.Release|Win32.Build.0 = Release|Win32 + {45DBDC5F-2109-432B-8149-29D3693D57E1}.Debug|x64.ActiveCfg = Debug|x64 + {45DBDC5F-2109-432B-8149-29D3693D57E1}.Debug|x64.Build.0 = Debug|x64 + {45DBDC5F-2109-432B-8149-29D3693D57E1}.Release|x64.ActiveCfg = Release|x64 + {45DBDC5F-2109-432B-8149-29D3693D57E1}.Release|x64.Build.0 = Release|x64 + {17F25816-A09C-44DF-99A3-16D0FF5FCD66}.Debug|Win32.ActiveCfg = Debug|Win32 + {17F25816-A09C-44DF-99A3-16D0FF5FCD66}.Debug|Win32.Build.0 = Debug|Win32 + {17F25816-A09C-44DF-99A3-16D0FF5FCD66}.Release|Win32.ActiveCfg = Release|Win32 + {17F25816-A09C-44DF-99A3-16D0FF5FCD66}.Release|Win32.Build.0 = Release|Win32 + {17F25816-A09C-44DF-99A3-16D0FF5FCD66}.Debug|x64.ActiveCfg = Debug|x64 + {17F25816-A09C-44DF-99A3-16D0FF5FCD66}.Debug|x64.Build.0 = Debug|x64 + {17F25816-A09C-44DF-99A3-16D0FF5FCD66}.Release|x64.ActiveCfg = Release|x64 + {17F25816-A09C-44DF-99A3-16D0FF5FCD66}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3E233D0A-F988-4C9D-B15A-49B54025E11F} = {F0B333C5-FFAD-425B-8064-EB950E9718B8} + {21F832DD-6A4C-4E5F-941E-15BF98CE4137} = {24B27C7D-03C0-4286-8250-57110C794853} + {45DBDC5F-2109-432B-8149-29D3693D57E1} = {8CA441A3-D099-4853-9D9D-3AE4DCBEC7E8} + {17F25816-A09C-44DF-99A3-16D0FF5FCD66} = {18E8D511-CACD-410E-8791-41511E36F2D5} + {24B27C7D-03C0-4286-8250-57110C794853} = {36BE1DCF-A58E-45E4-B458-3640AA1FBCC1} + {36BE1DCF-A58E-45E4-B458-3640AA1FBCC1} = {2F266524-ED06-4654-BCFB-DBB9FE440BD7} + {8CA441A3-D099-4853-9D9D-3AE4DCBEC7E8} = {36BE1DCF-A58E-45E4-B458-3640AA1FBCC1} + {18E8D511-CACD-410E-8791-41511E36F2D5} = {2F266524-ED06-4654-BCFB-DBB9FE440BD7} + EndGlobalSection +EndGlobal diff --git a/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj b/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj new file mode 100644 index 00000000..544fe0e2 --- /dev/null +++ b/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj @@ -0,0 +1,141 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {8DD661EC-C827-40E9-8894-C81041AC4324} + $(MSBuildProjectName) + false + Debug + Win32 + {AF94EE33-40D5-4CD7-9F09-5A1CC16F2EF0} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + BlankProject + + + BlankProject + + + BlankProject + + + BlankProject + + + + + + + + true + + + + + + + + + true + + + + + + + + + true + + + + + + + + + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj.Filters b/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj.Filters new file mode 100644 index 00000000..fee230a4 --- /dev/null +++ b/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj.Filters @@ -0,0 +1,17 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {B2CC4F75-22F6-4939-B93F-DAFE36BFD9D6} + + + h;hpp;hxx;hm;inl;inc;xsd + {0A89D084-FD85-4B63-B752-7C0852CD9001} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {74429B5A-7FD5-4707-BEED-E41681C9E5B0} + + + \ No newline at end of file diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M1.1a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M1.1a.oxps new file mode 100644 index 00000000..7d57c6fa Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M1.1a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M1.2a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M1.2a.oxps new file mode 100644 index 00000000..8da67915 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M1.2a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.14a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.14a.oxps new file mode 100644 index 00000000..a68e8299 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.14a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.17a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.17a.oxps new file mode 100644 index 00000000..5787ab9e Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.17a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.20a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.20a.oxps new file mode 100644 index 00000000..272f75ff Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.20a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.24a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.24a.oxps new file mode 100644 index 00000000..3cb95fbd Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.24a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.27a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.27a.oxps new file mode 100644 index 00000000..b6dd42fc Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.27a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.27b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.27b.oxps new file mode 100644 index 00000000..2d5a4a59 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.27b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.30a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.30a.oxps new file mode 100644 index 00000000..0063c896 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.30a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.31a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.31a.oxps new file mode 100644 index 00000000..82a3cec8 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.31a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.32a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.32a.oxps new file mode 100644 index 00000000..821826a6 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.32a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.33a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.33a.oxps new file mode 100644 index 00000000..1a709467 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.33a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.35a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.35a.oxps new file mode 100644 index 00000000..c35fb749 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.35a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.36a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.36a.oxps new file mode 100644 index 00000000..d9176b43 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.36a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.36b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.36b.oxps new file mode 100644 index 00000000..ca568053 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.36b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.41a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.41a.oxps new file mode 100644 index 00000000..092826a0 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.41a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.41b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.41b.oxps new file mode 100644 index 00000000..1c48a335 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.41b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.42a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.42a.oxps new file mode 100644 index 00000000..b57510e9 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.42a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.42b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.42b.oxps new file mode 100644 index 00000000..7877aadf Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.42b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.43a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.43a.oxps new file mode 100644 index 00000000..91f3c64a Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.43a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.43b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.43b.oxps new file mode 100644 index 00000000..661578e4 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.43b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.44a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.44a.oxps new file mode 100644 index 00000000..a5a26d00 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.44a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.48a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.48a.oxps new file mode 100644 index 00000000..15313e86 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.48a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.49a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.49a.oxps new file mode 100644 index 00000000..7e238cda Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.49a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.49b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.49b.oxps new file mode 100644 index 00000000..3a1c4ae3 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.49b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.50a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.50a.oxps new file mode 100644 index 00000000..31045006 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.50a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.52a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.52a.oxps new file mode 100644 index 00000000..d2c0d68f Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.52a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.55a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.55a.oxps new file mode 100644 index 00000000..d9a21ca3 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.55a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.56a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.56a.oxps new file mode 100644 index 00000000..9c82a8c6 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/MarkupCompatibility/M2.56a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M1.2a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M1.2a.oxps new file mode 100644 index 00000000..12c0d526 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M1.2a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.23a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.23a.oxps new file mode 100644 index 00000000..e4183e92 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.23a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.6a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.6a.oxps new file mode 100644 index 00000000..18955f95 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.6a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.6b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.6b.oxps new file mode 100644 index 00000000..e2d0085f Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M10.6b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.2a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.2a.oxps new file mode 100644 index 00000000..b080ba72 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.2a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.3a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.3a.oxps new file mode 100644 index 00000000..0a83e2d0 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.3a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.3b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.3b.oxps new file mode 100644 index 00000000..6c0bda7a Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.3b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5a.oxps new file mode 100644 index 00000000..cc2330fa Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5b.oxps new file mode 100644 index 00000000..b02d1522 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5c.oxps new file mode 100644 index 00000000..dd8abe81 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.5c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.7a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.7a.oxps new file mode 100644 index 00000000..f65645da Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M12.7a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.10a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.10a.oxps new file mode 100644 index 00000000..4db2144d Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.10a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.10b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.10b.oxps new file mode 100644 index 00000000..ccbf170e Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.10b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.13a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.13a.oxps new file mode 100644 index 00000000..356a7c0a Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.13a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.13b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.13b.oxps new file mode 100644 index 00000000..81819935 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.13b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.14a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.14a.oxps new file mode 100644 index 00000000..8b629d76 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.14a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.14b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.14b.oxps new file mode 100644 index 00000000..2e23e20b Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.14b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.17a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.17a.oxps new file mode 100644 index 00000000..9aa2db0b Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.17a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.18a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.18a.oxps new file mode 100644 index 00000000..6c4dc4da Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.18a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.25a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.25a.oxps new file mode 100644 index 00000000..d3764cb1 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.25a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2a.oxps new file mode 100644 index 00000000..2e587765 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2b.oxps new file mode 100644 index 00000000..a766c766 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2c.oxps new file mode 100644 index 00000000..f94c3782 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.2c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.35a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.35a.oxps new file mode 100644 index 00000000..5cac28ee Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.35a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.36a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.36a.oxps new file mode 100644 index 00000000..615bcd03 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.36a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.37a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.37a.oxps new file mode 100644 index 00000000..26d9cc97 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.37a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39a.oxps new file mode 100644 index 00000000..4856a8f4 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39b.oxps new file mode 100644 index 00000000..976a3ffb Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39c.oxps new file mode 100644 index 00000000..163a06a0 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.39c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3a.oxps new file mode 100644 index 00000000..fe72bc82 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3b.oxps new file mode 100644 index 00000000..bd84ede6 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3c.oxps new file mode 100644 index 00000000..48f5513e Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.3c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.4a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.4a.oxps new file mode 100644 index 00000000..635ba653 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.4a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.4b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.4b.oxps new file mode 100644 index 00000000..53f585b7 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.4b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.54a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.54a.oxps new file mode 100644 index 00000000..02e067b0 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.54a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59a.oxps new file mode 100644 index 00000000..749fc46c Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59b.oxps new file mode 100644 index 00000000..1732ba01 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59c.oxps new file mode 100644 index 00000000..d4530964 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.59c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.5a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.5a.oxps new file mode 100644 index 00000000..eab5315b Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.5a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.5b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.5b.oxps new file mode 100644 index 00000000..003785dc Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.5b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6a.oxps new file mode 100644 index 00000000..7d49183d Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6b.oxps new file mode 100644 index 00000000..344a2a4c Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6c.oxps new file mode 100644 index 00000000..8e6a3fee Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.6c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.70a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.70a.oxps new file mode 100644 index 00000000..dd8207dd Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.70a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71a.oxps new file mode 100644 index 00000000..a4768f4e Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71b.oxps new file mode 100644 index 00000000..3875027e Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71c.oxps new file mode 100644 index 00000000..6f0cb60b Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.71c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72a.oxps new file mode 100644 index 00000000..034178df Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72b.oxps new file mode 100644 index 00000000..24fa8200 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72c.oxps new file mode 100644 index 00000000..cf695786 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.72c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.73a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.73a.oxps new file mode 100644 index 00000000..a19a1d76 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.73a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.73b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.73b.oxps new file mode 100644 index 00000000..c4c02c89 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.73b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74a.oxps new file mode 100644 index 00000000..34754853 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74b.oxps new file mode 100644 index 00000000..7ec5e802 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74c.oxps new file mode 100644 index 00000000..70df17e7 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.74c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75a.oxps new file mode 100644 index 00000000..3d6c80c6 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75b.oxps new file mode 100644 index 00000000..f9453775 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75c.oxps new file mode 100644 index 00000000..a6af0c05 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75d.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75d.oxps new file mode 100644 index 00000000..860d6ef7 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.75d.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.76a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.76a.oxps new file mode 100644 index 00000000..04009798 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.76a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.77a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.77a.oxps new file mode 100644 index 00000000..b09370b0 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.77a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.77b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.77b.oxps new file mode 100644 index 00000000..d4c716aa Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.77b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7a.oxps new file mode 100644 index 00000000..43ae8dd4 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7b.oxps new file mode 100644 index 00000000..cd438ae8 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7c.oxps new file mode 100644 index 00000000..5769291e Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.7c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.80.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.80.oxps new file mode 100644 index 00000000..54be15f5 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.80.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.81.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.81.oxps new file mode 100644 index 00000000..3ee96f65 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.81.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.82.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.82.oxps new file mode 100644 index 00000000..c4f7bc34 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.82.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8a.oxps new file mode 100644 index 00000000..6d7ac332 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8b.oxps new file mode 100644 index 00000000..c89d5178 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8c.oxps new file mode 100644 index 00000000..9189599c Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M2.8c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.11a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.11a.oxps new file mode 100644 index 00000000..6a245834 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.11a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.11b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.11b.oxps new file mode 100644 index 00000000..67b452f2 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.11b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.12a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.12a.oxps new file mode 100644 index 00000000..51dd9a58 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.12a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.12b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.12b.oxps new file mode 100644 index 00000000..1436e752 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.12b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.13a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.13a.oxps new file mode 100644 index 00000000..86d736fd Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.13a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.14a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.14a.oxps new file mode 100644 index 00000000..61153510 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.14a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.20a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.20a.oxps new file mode 100644 index 00000000..54991b08 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.20a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.20b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.20b.oxps new file mode 100644 index 00000000..48170f5d Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.20b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.2a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.2a.oxps new file mode 100644 index 00000000..4249ffcf Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.2a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.2b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.2b.oxps new file mode 100644 index 00000000..3fb18d33 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.2b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.3a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.3a.oxps new file mode 100644 index 00000000..65627a53 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.3a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.5a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.5a.oxps new file mode 100644 index 00000000..d692b79e Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.5a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.5b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.5b.oxps new file mode 100644 index 00000000..9c778641 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.5b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.6a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.6a.oxps new file mode 100644 index 00000000..19bf2016 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.6a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.6b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.6b.oxps new file mode 100644 index 00000000..072980cc Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M3.6b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1a.oxps new file mode 100644 index 00000000..27b1345f Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1b.oxps new file mode 100644 index 00000000..a8ba179c Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1c.oxps new file mode 100644 index 00000000..e0e0dfdd Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.1c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.2a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.2a.oxps new file mode 100644 index 00000000..f7e297c5 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.2a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.2b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.2b.oxps new file mode 100644 index 00000000..ec497e43 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.2b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.3a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.3a.oxps new file mode 100644 index 00000000..fe050dcc Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M4.3a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.15a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.15a.oxps new file mode 100644 index 00000000..75944cfa Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.15a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2a.oxps new file mode 100644 index 00000000..3b83175d Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2b.oxps new file mode 100644 index 00000000..95a4fdfd Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2c.oxps new file mode 100644 index 00000000..3b334728 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.2c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3a.oxps new file mode 100644 index 00000000..9cd37caa Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3b.oxps new file mode 100644 index 00000000..fac36836 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3c.oxps new file mode 100644 index 00000000..47b8e96f Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.3c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.4a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.4a.oxps new file mode 100644 index 00000000..56874b52 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.4a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.4b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.4b.oxps new file mode 100644 index 00000000..d02e3e5b Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.4b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.7a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.7a.oxps new file mode 100644 index 00000000..67ea2d89 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M5.7a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.1a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.1a.oxps new file mode 100644 index 00000000..10082c32 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.1a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.1b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.1b.oxps new file mode 100644 index 00000000..a495ea08 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.1b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.2a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.2a.oxps new file mode 100644 index 00000000..47cb778b Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.2a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.2b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.2b.oxps new file mode 100644 index 00000000..d3f3abc8 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.2b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.3a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.3a.oxps new file mode 100644 index 00000000..f4366daa Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.3a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.3b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.3b.oxps new file mode 100644 index 00000000..42c68be9 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.3b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.4a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.4a.oxps new file mode 100644 index 00000000..ca2a1fd8 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.4a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.4b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.4b.oxps new file mode 100644 index 00000000..a5011cc2 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.4b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.5a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.5a.oxps new file mode 100644 index 00000000..31c389e5 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.5a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.5b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.5b.oxps new file mode 100644 index 00000000..2153d0c1 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.5b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.6a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.6a.oxps new file mode 100644 index 00000000..8879e4c7 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.6a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.6b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.6b.oxps new file mode 100644 index 00000000..c5c2f609 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M6.6b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.11a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.11a.oxps new file mode 100644 index 00000000..73a45e80 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.11a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.11b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.11b.oxps new file mode 100644 index 00000000..b86db281 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.11b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.2a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.2a.oxps new file mode 100644 index 00000000..e82f6990 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.2a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.2b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.2b.oxps new file mode 100644 index 00000000..69ba04cf Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.2b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.3a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.3a.oxps new file mode 100644 index 00000000..c6d8e028 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.3a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.5a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.5a.oxps new file mode 100644 index 00000000..14bed3c0 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.5a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.6a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.6a.oxps new file mode 100644 index 00000000..1e6f679e Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M7.6a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M8.14a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M8.14a.oxps new file mode 100644 index 00000000..6bd6cba4 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M8.14a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M9.10a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M9.10a.oxps new file mode 100644 index 00000000..b933f9bb Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M9.10a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M9.10b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M9.10b.oxps new file mode 100644 index 00000000..0691b803 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OXPS/M9.10b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.10a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.10a.oxps new file mode 100644 index 00000000..86bb6c6d Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.10a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.10b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.10b.oxps new file mode 100644 index 00000000..3e2820be Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.10b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.11a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.11a.oxps new file mode 100644 index 00000000..f2a269d9 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.11a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.12a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.12a.oxps new file mode 100644 index 00000000..f0f51a6a Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.12a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.13a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.13a.oxps new file mode 100644 index 00000000..29fd37e8 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.13a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.13b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.13b.oxps new file mode 100644 index 00000000..4a1aa928 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.13b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14a.oxps new file mode 100644 index 00000000..a5b1c2fd Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14b.oxps new file mode 100644 index 00000000..bda7b613 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14c.oxps new file mode 100644 index 00000000..b16535f1 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.14c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.15a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.15a.oxps new file mode 100644 index 00000000..e62a2c04 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.15a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.17a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.17a.oxps new file mode 100644 index 00000000..b46a75a8 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.17a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.17b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.17b.oxps new file mode 100644 index 00000000..abc66d3a Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.17b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.18a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.18a.oxps new file mode 100644 index 00000000..8c4db483 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.18a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.18b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.18b.oxps new file mode 100644 index 00000000..61f134ad Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.18b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.1a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.1a.oxps new file mode 100644 index 00000000..9fb579e9 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.1a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.20a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.20a.oxps new file mode 100644 index 00000000..dbf7e7b5 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.20a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.20b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.20b.oxps new file mode 100644 index 00000000..5618a606 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.20b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.21a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.21a.oxps new file mode 100644 index 00000000..7161dda4 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.21a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.21b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.21b.oxps new file mode 100644 index 00000000..600251fa Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.21b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.22a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.22a.oxps new file mode 100644 index 00000000..f115a574 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.22a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.25a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.25a.oxps new file mode 100644 index 00000000..92acd6dc Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.25a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.26a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.26a.oxps new file mode 100644 index 00000000..a1105597 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.26a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.26b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.26b.oxps new file mode 100644 index 00000000..7f97b408 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.26b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.27a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.27a.oxps new file mode 100644 index 00000000..66210956 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.27a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.27b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.27b.oxps new file mode 100644 index 00000000..38d0a4c3 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.27b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.28a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.28a.oxps new file mode 100644 index 00000000..c4cd0cff Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.28a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.28b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.28b.oxps new file mode 100644 index 00000000..0b31ee13 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.28b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2a.oxps new file mode 100644 index 00000000..c28b2d74 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2b.oxps new file mode 100644 index 00000000..a41da49d Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2c.oxps new file mode 100644 index 00000000..e57161b6 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.2c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.30a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.30a.oxps new file mode 100644 index 00000000..389055ab Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.30a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.30b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.30b.oxps new file mode 100644 index 00000000..34a11721 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.30b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.3a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.3a.oxps new file mode 100644 index 00000000..43458263 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.3a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.5a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.5a.oxps new file mode 100644 index 00000000..527dacf3 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.5a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6a.oxps new file mode 100644 index 00000000..3380472a Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6b.oxps new file mode 100644 index 00000000..53b246e8 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6c.oxps new file mode 100644 index 00000000..1b412c9f Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6d.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6d.oxps new file mode 100644 index 00000000..c0656f7a Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.6d.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.7a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.7a.oxps new file mode 100644 index 00000000..c7312c0c Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.7a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.7b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.7b.oxps new file mode 100644 index 00000000..f30e067c Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.7b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8a.oxps new file mode 100644 index 00000000..edfee4f6 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8b.oxps new file mode 100644 index 00000000..56be3417 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8c.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8c.oxps new file mode 100644 index 00000000..447aa316 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.8c.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.9a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.9a.oxps new file mode 100644 index 00000000..39a2bab7 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M1.9a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.10a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.10a.oxps new file mode 100644 index 00000000..d59d1efb Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.10a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.18a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.18a.oxps new file mode 100644 index 00000000..b913fb02 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.18a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.4a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.4a.oxps new file mode 100644 index 00000000..2ff018f6 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.4a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.5a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.5a.oxps new file mode 100644 index 00000000..d3377da4 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.5a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.5b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.5b.oxps new file mode 100644 index 00000000..c720fb68 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.5b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.6a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.6a.oxps new file mode 100644 index 00000000..d91da44f Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.6a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.6b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.6b.oxps new file mode 100644 index 00000000..b6e8e3a2 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.6b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.7a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.7a.oxps new file mode 100644 index 00000000..2cb5018c Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.7a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.7b.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.7b.oxps new file mode 100644 index 00000000..6f4ee6d0 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M2.7b.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.10a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.10a.oxps new file mode 100644 index 00000000..1cbac9f4 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.10a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.14a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.14a.oxps new file mode 100644 index 00000000..a2579585 Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.14a.oxps differ diff --git a/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.9a.oxps b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.9a.oxps new file mode 100644 index 00000000..6b5fef0b Binary files /dev/null and b/print/SampleOpenXPS/ConformanceViolations_oxps/OpenPackagingConventions/M3.9a.oxps differ diff --git a/print/SampleOpenXPS/OpenXPSTestFiles_EndUserLicenseAgreement.txt b/print/SampleOpenXPS/OpenXPSTestFiles_EndUserLicenseAgreement.txt new file mode 100644 index 00000000..bb9b187b --- /dev/null +++ b/print/SampleOpenXPS/OpenXPSTestFiles_EndUserLicenseAgreement.txt @@ -0,0 +1,7 @@ +END-USER LICENSE AGREEMENT +IMPORTANT—READ CAREFULLY: This Microsoft End-User License Agreement (“EULA”) is a legal agreement between you (either an individual or a single entity) and Microsoft Corporation (“Microsoft”) for Test Files owned by Microsoft and identified below. By installing, copying, or otherwise using the Test Files, you agree to be bound by the terms of this EULA. If you do not agree to the terms of this EULA, you may not install or use the Test Files. + +Microsoft owns a library of sample Open XML Paper Specification (“OpenXPS”) files, which Microsoft created for testing features of the OpenXPS format (the “Test Files”). The Test Files are protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. This is a license, not a sale, of the Test Files. +1. GRANT OF LICENSE. Microsoft grants to you as an individual, a personal, nonexclusive, and limited license to make and use copies of the Test Files on any number of computers residing on your premises for the sole purpose of developing and testing your software and/or hardware products. These license rights are conditioned on your compliance with the license restrictions and requirements described below. +2. TERMINATION. Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with the terms and conditions of this EULA. +3. DISCLAIMER OF WARRANTIES. To the maximum extent permitted by applicable law, Microsoft provides the Test Files AS IS AND WITH ALL FAULTS, and hereby disclaim all warranties and conditions, either express, implied or statutory, including, but not limited to, any (if any) implied warranties or conditions of merchantability, of fitness for a particular purpose, of lack of viruses, of accuracy or completeness of responses, of results, and of lack of negligence or lack of workmanlike effort, all with regard to the Test Files. ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT, WITH REGARD TO THE TEST FILES. THE ENTIRE RISK AS TO THE QUALITY OF OR ARISING OUT OF USE OR PERFORMANCE OF THE SAMPLE CODE, IF ANY, REMAINS WITH YOU. diff --git a/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb01.oxps b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb01.oxps new file mode 100644 index 00000000..3e80f38a Binary files /dev/null and b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb01.oxps differ diff --git a/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb02.oxps b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb02.oxps new file mode 100644 index 00000000..ff9cfa89 Binary files /dev/null and b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb02.oxps differ diff --git a/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb03.oxps b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb03.oxps new file mode 100644 index 00000000..b92f8aa0 Binary files /dev/null and b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb03.oxps differ diff --git a/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb04.oxps b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb04.oxps new file mode 100644 index 00000000..4db94294 Binary files /dev/null and b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb04.oxps differ diff --git a/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb05.oxps b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb05.oxps new file mode 100644 index 00000000..a3352a3c Binary files /dev/null and b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb05.oxps differ diff --git a/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb06.oxps b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb06.oxps new file mode 100644 index 00000000..99e89c53 Binary files /dev/null and b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb06.oxps differ diff --git a/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb07.oxps b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb07.oxps new file mode 100644 index 00000000..2fa2eb7e Binary files /dev/null and b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb07.oxps differ diff --git a/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb08.oxps b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb08.oxps new file mode 100644 index 00000000..f40d61cc Binary files /dev/null and b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb08.oxps differ diff --git a/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb09.oxps b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb09.oxps new file mode 100644 index 00000000..eca31524 Binary files /dev/null and b/print/SampleOpenXPS/QualityLogicMinBar_oxps/mb09.oxps differ diff --git a/print/SampleOpenXPS/ReadMe.md b/print/SampleOpenXPS/ReadMe.md new file mode 100644 index 00000000..a50148e4 --- /dev/null +++ b/print/SampleOpenXPS/ReadMe.md @@ -0,0 +1,7 @@ +OpenXPS Documents Print Sample +============================== + +This sample contains a set of documents that were generated from a variety of sources, including those generated from the Windows Presentation Foundation in the .NET Framework, from Office 2007, and from the Microsoft XPS Document Writer (MXDW). A few of the docuemts were either hand-built from scratch or hand-modified from another source. They have been included to provide you with a few documents that exercise a variety of features of the XML Paper Specification. + +In addition, a few high-quality documents have been provided in the Showcase directory to highlight some of the XPS advantages in terms of screen-to-print fidelity. We've also created some documents that are intended to fail, by violating at least one conformance rule. These are in the ConformanceViolations directory. For information about OpenXPS in Windows, see [Improvements in XPSDrv](http://msdn.microsoft.com/en-us/library/windows/hardware/jj218730(v=vs.85).aspx) and [OpenXPS Support in Windows](http://msdn.microsoft.com/en-us/library/windows/hardware/br259130.aspx). + diff --git a/print/SampleOpenXPS/SampleOpenXPS.sln b/print/SampleOpenXPS/SampleOpenXPS.sln new file mode 100644 index 00000000..19ed775e --- /dev/null +++ b/print/SampleOpenXPS/SampleOpenXPS.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlankProject", "BlankProjSrc\BlankProject.vcxproj", "{8DD661EC-C827-40E9-8894-C81041AC4324}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8DD661EC-C827-40E9-8894-C81041AC4324}.Debug|Win32.ActiveCfg = Debug|Win32 + {8DD661EC-C827-40E9-8894-C81041AC4324}.Debug|Win32.Build.0 = Debug|Win32 + {8DD661EC-C827-40E9-8894-C81041AC4324}.Release|Win32.ActiveCfg = Release|Win32 + {8DD661EC-C827-40E9-8894-C81041AC4324}.Release|Win32.Build.0 = Release|Win32 + {8DD661EC-C827-40E9-8894-C81041AC4324}.Debug|x64.ActiveCfg = Debug|x64 + {8DD661EC-C827-40E9-8894-C81041AC4324}.Debug|x64.Build.0 = Debug|x64 + {8DD661EC-C827-40E9-8894-C81041AC4324}.Release|x64.ActiveCfg = Release|x64 + {8DD661EC-C827-40E9-8894-C81041AC4324}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/print/SampleOpenXPS/readme.txt b/print/SampleOpenXPS/readme.txt new file mode 100644 index 00000000..d294a25b --- /dev/null +++ b/print/SampleOpenXPS/readme.txt @@ -0,0 +1,147 @@ +File Name: SampleOXPS.zip +Version: 1.0 +License: These sample documents are covered by the terms of the OpenXPSTestFiles_EndUserLicenseAgreement.doc file included in this ZIP archive. +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +The sample documents in this ZIP archive were generated from a variety of sources, including those generated from +the Windows Presentation Foundation in WinFX, from Office 2007, from the Microsoft XPS Document Writer (MXDW), and a few +that were either hand-built from scratch or hand-modified from another source. They have been included to provide +you with a few documents that exercise a variety of features of the XML Paper Specification. + +In addition, we have included a few high-quality documents in the Showcase directory to highlight some of the XPS advantages in terms of screen-to-print fidelity. We've also created some documents that are intended to fail, violating at least one conformance rule. These are in the ConformanceViolations directory. + +Comments on various files is denoted following the file beginning with '--'. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +These sample Test Files include: + + +QualityLogicMinBar/ -- Minimum bar test documents provided by Quality Logic + mb01.oxps + mb02.oxps + mb03.oxps + mb04.oxps + mb05.oxps + mb06.oxps + mb07.oxps + mb08.oxps + mb09.oxps + + +ConformanceViolations/ -- All these documents violate one conformance rule. They should fail or generate an error. + MarkupCompatibility/ + M1.1a.oxps -- Unrecognized element from a non-ignorable namespace + M1.2a.oxps -- Unrecognized element from Markup Compatibility namespace + M2.14a.oxps -- ProcessContent referencing an element whose namespace is not also listed as Ignorable + M2.17a.oxps -- Xml:lang attribute on a ProcessContent element + M2.20a.oxps -- PreserveElements attribute with no matching Ignorable attribute + M2.24a.oxps -- PreserveAttributes attribute with no matching Ignorable attribute + M2.27a.oxps -- Non-understood and ignorable namespace also identified in the MustUnderstand attribute value + M2.27b.oxps -- Non-understood namespace identified in the MustUnderstand attribute value + M2.30a.oxps -- AlternateContent element with no Choice child elements + M2.31a.oxps -- AlternateContent element with multiple Fallback child elements + M2.32a.oxps -- AlternateContent element with Fallback child element occurring before Choice child element + M2.33a.oxps -- Nested AlternateContent elements + M2.35a.oxps -- AlternateContent element with unprefixed Ignorable attribute. Namespace identified in Ignorable attribute is used. + M2.35b.oxps -- AlternateContent element with unprefixed Ignorable attribute. Namespace identified in Ignorable attribute is not used. + M2.36a.oxps -- AlternateContent having unrecognized child element that is not ignored + M2.41a.oxps -- Choice element having unrecognized and non-ignored attribute + M2.42a.oxps -- Choice element having MustUnderstand attribute referencing bad namespace + M2.43a.oxps -- Xml:lang attribute on AlternateContent element + M2.44a.oxps -- Choice element missing Requires attribute + M2.48a.oxps -- Choice element having unprefixed attribute other than Requires + M2.49a.oxps -- Xml:lang attribute on Choice element + M2.50a.oxps -- Requires attribute referencing non-understood namespace + M2.52a.oxps -- Prefixed Requires attribute applied to Choice element + M2.55a.oxps -- Unprefixed attribute applied to a Fallback element + M2.56a.oxps -- Xml:lang attribute on Fallback element + + OpenPackagingConvetions/ + M1.1a.oxps -- InvalidZip - Simple Text with empty reference to fdseq and empty fdseq name in zip + M1.2a.oxps -- Simple Text without FPAGE Content Type + M1.2b.oxps -- Simple Text without JPEG Content Type + M1.3a.oxps -- Simple Text with empty segment in PARTNAME in fdseq + M1.5a.oxps -- Simple Text PARTNAME with forward slash as last char + M1.6a.oxps -- Simple Text PARTNAME with nonpchar in fdseq with orig doc name + M1.6b.oxps -- Simple Text PARTNAME with nonpchar in fdseq + M1.7a.oxps -- Simple Text core prop contains fwd slash pct encode and reference also contains pct encode + M1.7b.oxps -- Simple Text PARTNAME contains '%/' + M1.8a.oxps -- Simple Text PARTNAME contains '%c' + M1.10a.oxps -- Simple Text PARTNAME only dot + M1.11a.oxps -- Simple Text Fixed Doc Seq Part contains content type part name + M1.17a.oxps -- Invalid comma token in content type + M1.18a.oxps -- Content Types Leading Whitespace + M1.18b.oxps -- Content Types LWS between type and subtype + M1.20a.oxps -- Simple Text Doc with Content Type including param + M1.22a.oxps -- Simple Text Content Types with ISO encoding + M1.26a.oxps -- Relationship Missing ID + M1.26b.oxps -- Relationship with invalid XML Identifier + M1.26c.oxps -- Relationships with duplicate IDs + M1.27a.oxps -- Relationship with missing Type + M1.28a.oxps -- Relationship with missing Target + M2.5a.oxps -- Content Type with duplicate default elements + M2.5b.oxps -- Content Type with duplicate override elements + M2.6a.oxps -- Content Type with Content Type but no Extension attrib + M2.6b.oxps -- Content Type with Extension but no Content Type attrib + M2.7a.oxps -- Content Type with Override with no ContentType attrib + M2.7b.oxps -- Content Type with Override with no PartName attrib + M2.18a.oxps -- Simple Text with Non-Interleaved as two consecutive parts + M2.24a.oxps -- Simple Text with fdseq converted to single dot char + M3.9a.oxps -- Simple Text with dig sig with Object containing PCDATA + M3.10a.oxps -- Simple Text with dig sig with fragment ident after Content Type + M3.14a.oxps -- Simple Text with dig sig Relationships XForm without Canon XForm + + OXPS/ + M1.2a.oxps -- Package is not a zip archive, it’s plain text instead + M2.3a.oxps -- Missing FixedDocumentSequence part + M2.3b.oxps -- Missing fixedrepresentation relationship + M2.3c.oxps -- Two fixedrepresentation relationships + M2.4a.oxps -- Missing FixedDocument part + M2.4b.oxps -- No to a FixedDocument + M2.5a.oxps -- Missing FixedPage part + M2.5b.oxps -- No to FixedPage + M2.6a.oxps -- Missing Font Part + M2.6b.oxps -- Missing FontURI attribute + M2.10a.oxps -- Missing required resource relationship for Font in FixedPage + M2.13a.oxps -- Missing fixedrepresentation relationship + M2.13b.oxps -- Two fixedrepresentation relationships + M2.14a.oxps -- Fixedrepresentation relationship pointing to FixedDocucment + M2.18a.oxps -- Corrupt PNG image + M2.25a.oxps -- Corrupt TIF image + M2.36a.oxps -- FixedPage with 2 thumbnails + M2.37a.oxps -- FixedPage with TIF thumbnail + M2.59a.oxps -- Two PrintTickets related to FixedDocumentSequence + M2.71a.oxps -- DTD in Relationship markup + M2.71b.oxps -- DTD in Content_Types markup + M2.71c.oxps -- DTD in FixedPage markup + M2.73a.oxps -- Xml:id attribute in FixedPage markup + M2.73b.oxps -- Xsi:SchemaLocation attribute on element + M2.74a.oxps -- having Fill attrib and child element + M2.75a.oxps -- Xml:space attribute on element + M2.75b.oxps -- Xml:space attribute on element + M2.76a.oxps -- Invalid value for xml:lang attribute on element + M2.80.oxps -- uses absolute URIs + M2.81.oxps -- uses absolute URIs + M2.82.oxps -- FixedPage part uses absolute URIs + M3.2a.oxps -- Source attribute pointing to Fixed Page instead of FixedDocucment + M3.3a.oxps -- Multiple elements pointing to same FixedDocument + M3.5a.oxps -- Source attrib pointing to FixedDocucment instead of FixedPage + M3.6a.oxps -- Multiple elements pointing to the same FixedPage from difference FixedDocuments + M3.6b.oxps -- Multiple elements pointing to same FixedPage (from the same FixedDocument) + M4.3a.oxps -- element with Figures attribute and child element + M5.2a.oxps -- with empty UnicodeString attribute and no Indices attribute + M5.2b.oxps -- with UnicodeString attribute that contains “{}” and no Indices attribute + M5.4a.oxps -- with an Indices attribute that contains an invalid Glyph Index for the specified font + M5.4b.oxps -- that has more entries in the indices attribute than the UnicodeString attribute + M5.7a.oxps -- that has a UnicodeString attribute that starts with '{' + M5.15a.oxps -- that has both BidiLevel and isSideways attributes + M6.2a.oxps -- with x:Key attribute not in a + M6.3a.oxps -- that references a font part + M7.5a.oxps -- Remote resource dictionary that references another remote resource dictionary + M12.2a.oxps -- 2 references to the same FixedDocument + M12.3b.oxps -- FixedPage that is referenced twice from the same FixedDocuments + M12.5a.oxps -- FixedDocumentSequence with two PrintTickets + M12.7a.oxps -- ContentType attribute containing a Parameter (ContentType=”image/jpeg;q=0”) + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= \ No newline at end of file diff --git a/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj b/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj new file mode 100644 index 00000000..b7b96fa8 --- /dev/null +++ b/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj @@ -0,0 +1,141 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D2B639CE-AC86-49E5-82A5-0754C41CE7C2} + $(MSBuildProjectName) + false + Debug + Win32 + {940BB686-F68A-4FA7-B9AC-441E0AA3E7D6} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + BlankProject + + + BlankProject + + + BlankProject + + + BlankProject + + + + + + + + true + + + + + + + + + true + + + + + + + + + true + + + + + + + + + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj.Filters b/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj.Filters new file mode 100644 index 00000000..fd3edb1a --- /dev/null +++ b/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj.Filters @@ -0,0 +1,17 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {4B942DCE-1A35-4E9F-A667-4A78801DCCB2} + + + h;hpp;hxx;hm;inl;inc;xsd + {F50DDDBE-9690-4E91-B1BC-32FA17D99E70} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9F0418C7-D852-4109-8654-6A18AD8318D1} + + + \ No newline at end of file diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M1.1a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M1.1a.xps new file mode 100644 index 00000000..c32c6403 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M1.1a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M1.2a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M1.2a.xps new file mode 100644 index 00000000..48bd6490 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M1.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.13a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.13a.xps new file mode 100644 index 00000000..f5f1ac2f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.13a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.14a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.14a.xps new file mode 100644 index 00000000..538f9e59 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.14a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.17a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.17a.xps new file mode 100644 index 00000000..023790ec Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.17a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.20a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.20a.xps new file mode 100644 index 00000000..dd55104c Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.20a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.24a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.24a.xps new file mode 100644 index 00000000..0ba82cb8 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.24a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.27a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.27a.xps new file mode 100644 index 00000000..3bc52c0c Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.27a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.27b.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.27b.xps new file mode 100644 index 00000000..87fa16b3 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.27b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.30a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.30a.xps new file mode 100644 index 00000000..2828247f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.30a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.31a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.31a.xps new file mode 100644 index 00000000..f046b027 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.31a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.32a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.32a.xps new file mode 100644 index 00000000..fd60c9c3 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.32a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.33a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.33a.xps new file mode 100644 index 00000000..76ecf75e Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.33a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.35a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.35a.xps new file mode 100644 index 00000000..8c8483dc Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.35a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.35b.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.35b.xps new file mode 100644 index 00000000..5d98e244 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.35b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.36a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.36a.xps new file mode 100644 index 00000000..6f77126d Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.36a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.41a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.41a.xps new file mode 100644 index 00000000..34ee4f7d Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.41a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.42a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.42a.xps new file mode 100644 index 00000000..3cb183e0 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.42a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.43a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.43a.xps new file mode 100644 index 00000000..69ecfb7a Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.43a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.44a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.44a.xps new file mode 100644 index 00000000..d0875741 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.44a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.48a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.48a.xps new file mode 100644 index 00000000..d0478fe8 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.48a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.49a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.49a.xps new file mode 100644 index 00000000..11a06bb0 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.49a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.50a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.50a.xps new file mode 100644 index 00000000..58edc94f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.50a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.52a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.52a.xps new file mode 100644 index 00000000..91ae6a0f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.52a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.55a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.55a.xps new file mode 100644 index 00000000..b89e6f3e Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.55a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.56a.xps b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.56a.xps new file mode 100644 index 00000000..96723169 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/MarkupCompatibility/M2.56a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.10a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.10a.xps new file mode 100644 index 00000000..413b8ce8 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.10a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.11a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.11a.xps new file mode 100644 index 00000000..aa8f754c Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.11a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.17a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.17a.xps new file mode 100644 index 00000000..ae7640fb Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.17a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.18a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.18a.xps new file mode 100644 index 00000000..8b682539 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.18a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.18b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.18b.xps new file mode 100644 index 00000000..b1291f17 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.18b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.1a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.1a.xps new file mode 100644 index 00000000..cf92a350 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.1a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.1b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.1b.xps new file mode 100644 index 00000000..568f188e Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.1b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.20a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.20a.xps new file mode 100644 index 00000000..84c5263b Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.20a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26a.xps new file mode 100644 index 00000000..0f2ca598 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26b.xps new file mode 100644 index 00000000..0bd649d5 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26c.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26c.xps new file mode 100644 index 00000000..17c7bcc5 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.26c.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.27a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.27a.xps new file mode 100644 index 00000000..bb352cce Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.27a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.28a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.28a.xps new file mode 100644 index 00000000..606b7913 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.28a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.2a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.2a.xps new file mode 100644 index 00000000..44c799ab Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.2b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.2b.xps new file mode 100644 index 00000000..1a27e88a Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.2b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.34a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.34a.xps new file mode 100644 index 00000000..b054bbe9 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.34a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.3a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.3a.xps new file mode 100644 index 00000000..597de9ed Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.3a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.5a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.5a.xps new file mode 100644 index 00000000..30da6c1f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.5a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.6a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.6a.xps new file mode 100644 index 00000000..1dcebd8a Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.6a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.6b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.6b.xps new file mode 100644 index 00000000..b52b7acc Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.6b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.7a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.7a.xps new file mode 100644 index 00000000..f6746cd0 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.7a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.7c.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.7c.xps new file mode 100644 index 00000000..6bd60336 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.7c.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.8a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.8a.xps new file mode 100644 index 00000000..e3f75acf Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M1.8a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.18a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.18a.xps new file mode 100644 index 00000000..6670b2f0 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.18a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.24a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.24a.xps new file mode 100644 index 00000000..4198d1f8 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.24a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.5a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.5a.xps new file mode 100644 index 00000000..32d09c18 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.5a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.5b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.5b.xps new file mode 100644 index 00000000..53ae6333 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.5b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.6a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.6a.xps new file mode 100644 index 00000000..e48d3250 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.6a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.6b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.6b.xps new file mode 100644 index 00000000..50e0193f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.6b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.7a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.7a.xps new file mode 100644 index 00000000..5ceb15a3 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.7a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.7b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.7b.xps new file mode 100644 index 00000000..f8278ecb Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M2.7b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10a.xps new file mode 100644 index 00000000..477c0183 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10b.xps new file mode 100644 index 00000000..31ac45f5 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10c.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10c.xps new file mode 100644 index 00000000..b4eecdfd Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.10c.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.11a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.11a.xps new file mode 100644 index 00000000..995040b6 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.11a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.11b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.11b.xps new file mode 100644 index 00000000..64a8af21 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.11b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.12a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.12a.xps new file mode 100644 index 00000000..498fe5cd Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.12a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.12b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.12b.xps new file mode 100644 index 00000000..ebca085e Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.12b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.13a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.13a.xps new file mode 100644 index 00000000..5ec3479f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.13a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.14a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.14a.xps new file mode 100644 index 00000000..ab717318 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.14a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.15a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.15a.xps new file mode 100644 index 00000000..35dea693 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.15a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.16a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.16a.xps new file mode 100644 index 00000000..4d8ac00d Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.16a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.17a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.17a.xps new file mode 100644 index 00000000..7cbf5580 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.17a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.17b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.17b.xps new file mode 100644 index 00000000..97876410 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.17b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.18a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.18a.xps new file mode 100644 index 00000000..f81fe6f2 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.18a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.1a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.1a.xps new file mode 100644 index 00000000..a425eff9 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.1a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.1b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.1b.xps new file mode 100644 index 00000000..052c570b Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.1b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.20a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.20a.xps new file mode 100644 index 00000000..5ec3479f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.20a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.21a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.21a.xps new file mode 100644 index 00000000..7bb049e6 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.21a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.21b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.21b.xps new file mode 100644 index 00000000..c1d6f3b4 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.21b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.25a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.25a.xps new file mode 100644 index 00000000..08c33e3d Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.25a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.26a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.26a.xps new file mode 100644 index 00000000..8a05d7b7 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.26a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.28a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.28a.xps new file mode 100644 index 00000000..18ccf0d9 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.28a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.29a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.29a.xps new file mode 100644 index 00000000..9f4dede8 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.29a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.2a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.2a.xps new file mode 100644 index 00000000..43d3896e Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.31a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.31a.xps new file mode 100644 index 00000000..63134db2 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.31a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.31b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.31b.xps new file mode 100644 index 00000000..d4b716da Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.31b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.32a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.32a.xps new file mode 100644 index 00000000..d1adc199 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.32a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.32b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.32b.xps new file mode 100644 index 00000000..5bbf1e0e Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.32b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.4a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.4a.xps new file mode 100644 index 00000000..ec57c709 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.4a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.4b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.4b.xps new file mode 100644 index 00000000..6eb0d1ce Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.4b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.5a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.5a.xps new file mode 100644 index 00000000..6593e647 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.5a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.6a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.6a.xps new file mode 100644 index 00000000..ed36762e Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.6a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.7a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.7a.xps new file mode 100644 index 00000000..c7af760c Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.7a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.8a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.8a.xps new file mode 100644 index 00000000..342b5ced Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.8a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9a.xps new file mode 100644 index 00000000..ef42bb32 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9b.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9b.xps new file mode 100644 index 00000000..6dbd62c6 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9c.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9c.xps new file mode 100644 index 00000000..d791407a Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M3.9c.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.1a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.1a.xps new file mode 100644 index 00000000..abac7f24 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.1a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.2a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.2a.xps new file mode 100644 index 00000000..e3f75acf Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.3a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.3a.xps new file mode 100644 index 00000000..60e7330f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M4.3a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.1a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.1a.xps new file mode 100644 index 00000000..05e660a0 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.1a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.2a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.2a.xps new file mode 100644 index 00000000..544d2978 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.4a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.4a.xps new file mode 100644 index 00000000..942f4003 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.4a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.6a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.6a.xps new file mode 100644 index 00000000..42cf8e0e Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.6a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.7a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.7a.xps new file mode 100644 index 00000000..e71811e5 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.7a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.8a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.8a.xps new file mode 100644 index 00000000..07219ea1 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M6.8a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M7.2a.xps b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M7.2a.xps new file mode 100644 index 00000000..7d0b1036 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/OpenPackagingConventions/M7.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M1.2a.xps b/print/SampleXPS/ConformanceViolations/XPS/M1.2a.xps new file mode 100644 index 00000000..aa0aab99 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M1.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M12.2a.xps b/print/SampleXPS/ConformanceViolations/XPS/M12.2a.xps new file mode 100644 index 00000000..37b7f4a9 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M12.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M12.3b.xps b/print/SampleXPS/ConformanceViolations/XPS/M12.3b.xps new file mode 100644 index 00000000..dd3cd57e Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M12.3b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M12.5a.xps b/print/SampleXPS/ConformanceViolations/XPS/M12.5a.xps new file mode 100644 index 00000000..a559be55 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M12.5a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M12.7a.xps b/print/SampleXPS/ConformanceViolations/XPS/M12.7a.xps new file mode 100644 index 00000000..af87d904 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M12.7a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.10a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.10a.xps new file mode 100644 index 00000000..5a589bb1 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.10a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.13a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.13a.xps new file mode 100644 index 00000000..08dd879f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.13a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.13b.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.13b.xps new file mode 100644 index 00000000..cb9c951e Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.13b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.14a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.14a.xps new file mode 100644 index 00000000..5b2d29ef Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.14a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.18a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.18a.xps new file mode 100644 index 00000000..918a5b84 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.18a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.25a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.25a.xps new file mode 100644 index 00000000..64cb6df8 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.25a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.36a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.36a.xps new file mode 100644 index 00000000..cc56b711 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.36a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.37a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.37a.xps new file mode 100644 index 00000000..6832246f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.37a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.3a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.3a.xps new file mode 100644 index 00000000..36faa225 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.3a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.3b.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.3b.xps new file mode 100644 index 00000000..a04712b7 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.3b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.3c.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.3c.xps new file mode 100644 index 00000000..61417822 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.3c.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.4a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.4a.xps new file mode 100644 index 00000000..a2b7b46b Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.4a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.4b.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.4b.xps new file mode 100644 index 00000000..6ba3ba3d Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.4b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.59a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.59a.xps new file mode 100644 index 00000000..4ed69dc3 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.59a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.5a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.5a.xps new file mode 100644 index 00000000..284cdf99 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.5a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.5b.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.5b.xps new file mode 100644 index 00000000..c2779265 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.5b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.6a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.6a.xps new file mode 100644 index 00000000..2b311786 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.6a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.6b.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.6b.xps new file mode 100644 index 00000000..a7af6881 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.6b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.71a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.71a.xps new file mode 100644 index 00000000..d2fd6911 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.71a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.71b.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.71b.xps new file mode 100644 index 00000000..8e129faf Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.71b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.71c.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.71c.xps new file mode 100644 index 00000000..6fcf2746 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.71c.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.73a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.73a.xps new file mode 100644 index 00000000..3724f3f2 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.73a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.73b.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.73b.xps new file mode 100644 index 00000000..cb492cde Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.73b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.74a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.74a.xps new file mode 100644 index 00000000..754a1dac Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.74a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.75a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.75a.xps new file mode 100644 index 00000000..e60d1999 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.75a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.75b.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.75b.xps new file mode 100644 index 00000000..afb8579f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.75b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M2.76a.xps b/print/SampleXPS/ConformanceViolations/XPS/M2.76a.xps new file mode 100644 index 00000000..70dcad6a Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M2.76a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M3.2a.xps b/print/SampleXPS/ConformanceViolations/XPS/M3.2a.xps new file mode 100644 index 00000000..974c52c8 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M3.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M3.3a.xps b/print/SampleXPS/ConformanceViolations/XPS/M3.3a.xps new file mode 100644 index 00000000..2d5f0f4c Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M3.3a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M3.5a.xps b/print/SampleXPS/ConformanceViolations/XPS/M3.5a.xps new file mode 100644 index 00000000..7471ee04 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M3.5a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M3.6a.xps b/print/SampleXPS/ConformanceViolations/XPS/M3.6a.xps new file mode 100644 index 00000000..0a2ec5ec Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M3.6a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M3.6b.xps b/print/SampleXPS/ConformanceViolations/XPS/M3.6b.xps new file mode 100644 index 00000000..3c353b55 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M3.6b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M4.3a.xps b/print/SampleXPS/ConformanceViolations/XPS/M4.3a.xps new file mode 100644 index 00000000..dbceda8b Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M4.3a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M5.15a.xps b/print/SampleXPS/ConformanceViolations/XPS/M5.15a.xps new file mode 100644 index 00000000..1cea3661 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M5.15a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M5.2a.xps b/print/SampleXPS/ConformanceViolations/XPS/M5.2a.xps new file mode 100644 index 00000000..abf74ae2 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M5.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M5.2b.xps b/print/SampleXPS/ConformanceViolations/XPS/M5.2b.xps new file mode 100644 index 00000000..b3f2559d Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M5.2b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M5.4a.xps b/print/SampleXPS/ConformanceViolations/XPS/M5.4a.xps new file mode 100644 index 00000000..44f206c0 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M5.4a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M5.4b.xps b/print/SampleXPS/ConformanceViolations/XPS/M5.4b.xps new file mode 100644 index 00000000..77596f04 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M5.4b.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M5.7a.xps b/print/SampleXPS/ConformanceViolations/XPS/M5.7a.xps new file mode 100644 index 00000000..72860767 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M5.7a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M6.2a.xps b/print/SampleXPS/ConformanceViolations/XPS/M6.2a.xps new file mode 100644 index 00000000..7c64d19f Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M6.2a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M6.3a.xps b/print/SampleXPS/ConformanceViolations/XPS/M6.3a.xps new file mode 100644 index 00000000..b62a394a Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M6.3a.xps differ diff --git a/print/SampleXPS/ConformanceViolations/XPS/M7.5a.xps b/print/SampleXPS/ConformanceViolations/XPS/M7.5a.xps new file mode 100644 index 00000000..93b14d89 Binary files /dev/null and b/print/SampleXPS/ConformanceViolations/XPS/M7.5a.xps differ diff --git a/print/SampleXPS/QualityLogicMinBar/mb01.xps b/print/SampleXPS/QualityLogicMinBar/mb01.xps new file mode 100644 index 00000000..138c8594 Binary files /dev/null and b/print/SampleXPS/QualityLogicMinBar/mb01.xps differ diff --git a/print/SampleXPS/QualityLogicMinBar/mb02.xps b/print/SampleXPS/QualityLogicMinBar/mb02.xps new file mode 100644 index 00000000..ed09e1ef Binary files /dev/null and b/print/SampleXPS/QualityLogicMinBar/mb02.xps differ diff --git a/print/SampleXPS/QualityLogicMinBar/mb03.xps b/print/SampleXPS/QualityLogicMinBar/mb03.xps new file mode 100644 index 00000000..2739c036 Binary files /dev/null and b/print/SampleXPS/QualityLogicMinBar/mb03.xps differ diff --git a/print/SampleXPS/QualityLogicMinBar/mb04.xps b/print/SampleXPS/QualityLogicMinBar/mb04.xps new file mode 100644 index 00000000..0287b040 Binary files /dev/null and b/print/SampleXPS/QualityLogicMinBar/mb04.xps differ diff --git a/print/SampleXPS/QualityLogicMinBar/mb05.xps b/print/SampleXPS/QualityLogicMinBar/mb05.xps new file mode 100644 index 00000000..225eed4e Binary files /dev/null and b/print/SampleXPS/QualityLogicMinBar/mb05.xps differ diff --git a/print/SampleXPS/QualityLogicMinBar/mb06.xps b/print/SampleXPS/QualityLogicMinBar/mb06.xps new file mode 100644 index 00000000..39b38a71 Binary files /dev/null and b/print/SampleXPS/QualityLogicMinBar/mb06.xps differ diff --git a/print/SampleXPS/QualityLogicMinBar/mb07.xps b/print/SampleXPS/QualityLogicMinBar/mb07.xps new file mode 100644 index 00000000..c844d85f Binary files /dev/null and b/print/SampleXPS/QualityLogicMinBar/mb07.xps differ diff --git a/print/SampleXPS/QualityLogicMinBar/mb08.xps b/print/SampleXPS/QualityLogicMinBar/mb08.xps new file mode 100644 index 00000000..2b2146f1 Binary files /dev/null and b/print/SampleXPS/QualityLogicMinBar/mb08.xps differ diff --git a/print/SampleXPS/QualityLogicMinBar/mb09.xps b/print/SampleXPS/QualityLogicMinBar/mb09.xps new file mode 100644 index 00000000..12470ceb Binary files /dev/null and b/print/SampleXPS/QualityLogicMinBar/mb09.xps differ diff --git a/print/SampleXPS/ReadMe.md b/print/SampleXPS/ReadMe.md new file mode 100644 index 00000000..18a242bb --- /dev/null +++ b/print/SampleXPS/ReadMe.md @@ -0,0 +1,7 @@ +XPS Documents Print Sample +========================== + +This sample is a set of documents that were generated from a variety of sources, including those generated from the Windows Presentation Foundation in the .NET Framework, from Office 2007, and from the Microsoft XPS Document Writer (MXDW). The set of documents also includes documents that were either hand-built from scratch or hand-modified from another source. They have been included to provide you with a few documents that exercise a variety of features of the XML Paper Specification. + +In addition, a few high-quality documents have been provided in the Showcase directory to highlight some of the XPS advantages in terms of screen-to-print fidelity. There are also some documents that are intended to fail by violating at least one conformance rule. These are in the ConformanceViolations directory. For information about XPS in Windows, see [XPS Printing Features](http://msdn.microsoft.com/en-us/library/windows/hardware/ff564299(v=vs.85).aspx). + diff --git a/print/SampleXPS/SampleXPS.sln b/print/SampleXPS/SampleXPS.sln new file mode 100644 index 00000000..7653b4c4 --- /dev/null +++ b/print/SampleXPS/SampleXPS.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlankProject", "BlankProjSrc\BlankProject.vcxproj", "{D2B639CE-AC86-49E5-82A5-0754C41CE7C2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D2B639CE-AC86-49E5-82A5-0754C41CE7C2}.Debug|Win32.ActiveCfg = Debug|Win32 + {D2B639CE-AC86-49E5-82A5-0754C41CE7C2}.Debug|Win32.Build.0 = Debug|Win32 + {D2B639CE-AC86-49E5-82A5-0754C41CE7C2}.Release|Win32.ActiveCfg = Release|Win32 + {D2B639CE-AC86-49E5-82A5-0754C41CE7C2}.Release|Win32.Build.0 = Release|Win32 + {D2B639CE-AC86-49E5-82A5-0754C41CE7C2}.Debug|x64.ActiveCfg = Debug|x64 + {D2B639CE-AC86-49E5-82A5-0754C41CE7C2}.Debug|x64.Build.0 = Debug|x64 + {D2B639CE-AC86-49E5-82A5-0754C41CE7C2}.Release|x64.ActiveCfg = Release|x64 + {D2B639CE-AC86-49E5-82A5-0754C41CE7C2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/print/SampleXPS/XPSTestFiles_EndUserLicenseAgreement.txt b/print/SampleXPS/XPSTestFiles_EndUserLicenseAgreement.txt new file mode 100644 index 00000000..2e90e5d7 --- /dev/null +++ b/print/SampleXPS/XPSTestFiles_EndUserLicenseAgreement.txt @@ -0,0 +1,7 @@ +END-USER LICENSE AGREEMENT +IMPORTANT—READ CAREFULLY: This Microsoft End-User License Agreement (“EULA”) is a legal agreement between you (either an individual or a single entity) and Microsoft Corporation (“Microsoft”) for Test Files owned by Microsoft and identified below. By installing, copying, or otherwise using the Test Files, you agree to be bound by the terms of this EULA. If you do not agree to the terms of this EULA, you may not install or use the Test Files. + +Microsoft owns a library of sample XML Paper Specification (“XPS”) files, which Microsoft created for testing features of the XPS format (the “Test Files”). The Test Files are protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. This is a license, not a sale, of the Test Files. +1. GRANT OF LICENSE. Microsoft grants to you as an individual, a personal, nonexclusive, and limited license to make and use copies of the Test Files on any number of computers residing on your premises for the sole purpose of developing and testing your software and/or hardware products. These license rights are conditioned on your compliance with the license restrictions and requirements described below. +2. TERMINATION. Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with the terms and conditions of this EULA. +3. DISCLAIMER OF WARRANTIES. To the maximum extent permitted by applicable law, Microsoft provides the Test Files AS IS AND WITH ALL FAULTS, and hereby disclaim all warranties and conditions, either express, implied or statutory, including, but not limited to, any (if any) implied warranties or conditions of merchantability, of fitness for a particular purpose, of lack of viruses, of accuracy or completeness of responses, of results, and of lack of negligence or lack of workmanlike effort, all with regard to the Test Files. ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT, WITH REGARD TO THE TEST FILES. THE ENTIRE RISK AS TO THE QUALITY OF OR ARISING OUT OF USE OR PERFORMANCE OF THE SAMPLE CODE, IF ANY, REMAINS WITH YOU. diff --git a/print/SampleXPS/readme.txt b/print/SampleXPS/readme.txt new file mode 100644 index 00000000..0a5ebab3 --- /dev/null +++ b/print/SampleXPS/readme.txt @@ -0,0 +1,190 @@ +File Name: SampleXps.zip +Version: 1.0 +License: These sample documents are covered by the terms of the XPSTestFiles_EndUserLicenseAgreement.doc file included in this ZIP archive. +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +The sample documents in this ZIP archive were generated from a variety of sources, including those generated from +the Windows Presentation Foundation in WinFX, from Office 2007, from the Microsoft XPS Document Writer (MXDW), and a few +that were either hand-built from scratch or hand-modified from another source. They have been included to provide +you with a few documents that exercise a variety of features of the XML Paper Specification. + +In addition, we have included a few high-quality documents in the Showcase directory to highlight some of the XPS advantages in terms of screen-to-print fidelity. We've also created some documents that are intended to fail, violating at least one conformance rule. These are in the ConformanceViolations directory. + +Comments on various files is denoted following the file beginning with '--'. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +These sample Test Files include: + + +QualityLogicMinBar/ -- Minimum bar test documents provided by Quality Logic + mb01.xps + mb02.xps + mb03.xps + mb04.xps + mb05.xps + mb06.xps + mb07.xps + mb08.xps + mb09.xps + + +ConformanceViolations/ -- All these documents violate one conformance rule. They should fail or generate an error. + MarkupCompatibility/ + M1.1a.xps -- Unrecognized element from a non-ignorable namespace + M1.2a.xps -- Unrecognized element from Markup Compatibility namespace + M2.13a.xps -- ProcessContent attribute on an element without an Ignorable attribute + M2.14a.xps -- ProcessContent referencing an element whose namespace is not also listed as Ignorable + M2.17a.xps -- Xml:lang attribute on a ProcessContent element + M2.20a.xps -- PreserveElements attribute with no matching Ignorable attribute + M2.24a.xps -- PreserveAttributes attribute with no matching Ignorable attribute + M2.27a.xps -- Non-understood and ignorable namespace also identified in the MustUnderstand attribute value + M2.27b.xps -- Non-understood namespace identified in the MustUnderstand attribute value + M2.30a.xps -- AlternateContent element with no Choice child elements + M2.31a.xps -- AlternateContent element with multiple Fallback child elements + M2.32a.xps -- AlternateContent element with Fallback child element occurring before Choice child element + M2.33a.xps -- Nested AlternateContent elements + M2.35a.xps -- AlternateContent element with unprefixed Ignorable attribute. Namespace identified in Ignorable attribute is used. + M2.35b.xps -- AlternateContent element with unprefixed Ignorable attribute. Namespace identified in Ignorable attribute is not used. + M2.36a.xps -- AlternateContent having unrecognized child element that is not ignored + M2.41a.xps -- Choice element having unrecognized and non-ignored attribute + M2.42a.xps -- Choice element having MustUnderstand attribute referencing bad namespace + M2.43a.xps -- Xml:lang attribute on AlternateContent element + M2.44a.xps -- Choice element missing Requires attribute + M2.48a.xps -- Choice element having unprefixed attribute other than Requires + M2.49a.xps -- Xml:lang attribute on Choice element + M2.50a.xps -- Requires attribute referencing non-understood namespace + M2.52a.xps -- Prefixed Requires attribute applied to Choice element + M2.55a.xps -- Unprefixed attribute applied to a Fallback element + M2.56a.xps -- Xml:lang attribute on Fallback element + + OpenPackagingConvetions/ + M1.1a.xps -- InvalidZip - Simple Text with empty reference to fdseq and empty fdseq name in zip + M1.1b.xps -- Simple Text without PARTNAME in fdseq + M1.2a.xps -- Simple Text without FPAGE Content Type + M1.2b.xps -- Simple Text without JPEG Content Type + M1.3a.xps -- Simple Text with empty segment in PARTNAME in fdseq + M1.5a.xps -- Simple Text PARTNAME with forward slash as last char + M1.6a.xps -- Simple Text PARTNAME with nonpchar in fdseq with orig doc name + M1.6b.xps -- Simple Text PARTNAME with nonpchar in fdseq + M1.7a.xps -- Simple Text core prop contains fwd slash pct encode and reference also contains pct encode + M1.7c.xps -- Simple Text PARTNAME contains '%/' + M1.8a.xps -- Simple Text PARTNAME contains '%c' + M1.10a.xps -- Simple Text PARTNAME only dot + M1.11a.xps -- Simple Text Fixed Doc Seq Part contains content type part name + M1.17a.xps -- Invalid comma token in content type + M1.18a.xps -- Content Types Leading Whitespace + M1.18b.xps -- Content Types LWS between type and subtype + M1.20a.xps -- Simple Text Doc with Content Type including param + M1.22a.xps -- Simple Text Content Types with ISO encoding + M1.26a.xps -- Relationship Missing ID + M1.26b.xps -- Relationship with invalid XML Identifier + M1.26c.xps -- Relationships with duplicate IDs + M1.27a.xps -- Relationship with missing Type + M1.28a.xps -- Relationship with missing Target + M2.5a.xps -- Content Type with duplicate default elements + M2.5b.xps -- Content Type with duplicate override elements + M2.6a.xps -- Content Type with Content Type but no Extension attrib + M2.6b.xps -- Content Type with Extension but no Content Type attrib + M2.7a.xps -- Content Type with Override with no ContentType attrib + M2.7b.xps -- Content Type with Override with no PartName attrib + M2.18a.xps -- Simple Text with Non-Interleaved as two consecutive parts + M2.24a.xps -- Simple Text with fdseq converted to single dot char + M3.1a.xps -- Simple Text Doc with core props relationship pointing to thumbnail + M3.1b.xps -- Simple Text Doc with two core props relationships + M3.2a.xps -- Simple Text Core Prop Part with Markup Compat + M3.4a.xps -- Simple Text with dig sig containing 2 dig sig origin parts + M3.4b.xps -- Simple Text with Dig Sig missing relationship to Dig Sig Origin + M3.5a.xps -- Simple Text with Dig Sig with no rel from dig sig orig to dig sig part + M3.6a.xps -- Simple Text with dig sig with no relationship to cert + M3.7a.xps -- Simple Text with dig sig ref with bad fragment identifier + M3.8a.xps -- Simple Text with dig sig with 2 Object Elements + M3.9a.xps -- Simple Text with dig sig with Object containing PCDATA + M3.9b.xps -- Simple Text with dig sig with Object containing Signature + M3.9c.xps -- Simple Text with dig sig with Sig Value in Object Element + M3.10a.xps -- Simple Text with dig sig with fragment ident after Content Type + M3.10b.xps -- Simple Text with dig sig with fragment ident right after URI + M3.10c.xps -- Simple Text with dig sig with reference to external part + M3.11a.xps -- Simple Text with Dig sig with ref query content type with bad content + M3.11b.xps -- Simple Text with Dig Sig with Ref query with missing Content Type + M3.12a.xps -- Simple Text with Dig Sig with Content Type not case matching + M3.12b.xps -- Simple Text with dig sig with mismatched Reference Content Type + M3.13a.xps -- Simple Text with dig sig Reference with unknown XForm + M3.14a.xps -- Simple Text with dig sig Relationships XForm without Canon XForm + M3.15a.xps -- Simple Text with dig sig with Signature Prop with no Sig Time + M3.16a.xps -- Simple Text with dig sig with no Objects + M3.17a.xps -- Simple Text with dig sig Signed Info missing Ref + M3.17b.xps -- Simple Text with dig sig signedinfo with ref to incorrect object + M3.18a.xps -- Simple Text with dig sig with bad algorithm + M3.20a.xps -- Simple Text with dig sig Reference with unknown XForm + M3.21a.xps -- Simple Text with dig sig with Manifest Object with no DigestMethod + M3.21b.xps -- Simple Text with dig sig with no DigestMethod in Package object + M3.25a.xps -- Simple Text with dig sig with Time format not compat with w3c + M3.26a.xps -- Simple Text with dig sig with value element not matching format + M3.28a.xps -- Simple Text with dig sig relation xform after canon xform + M3.29a.xps -- Simple Text with dig sig with Relation xform having SourceType with case sensitive mismatch + M3.31a.xps -- Simple Text with dig sig with DigestValue in Object Ref Descendant mismatch + M3.31b.xps -- Simple Text with dig sig with DigestValue in Object Ref Descendant too long. + M3.32a.xps -- Simple Text with dig sig with Bad DigestValue in Signed Info - Too long + M3.32b.xps -- Simple Text with dig sig with Bad DigestValue in Signed Info – Wrong + M4.1a.xps -- Simple Text with bad URI reference + M4.2a.xps -- Simple Text PARTNAME contains '%c' + M4.3a.xps -- Simple Text with percent encoding + M6.1a.xps -- Simple Text with mismatch filename length + M6.2a.xps -- Simple Text with invalid file name length + M6.4a.xps -- Simple Text with unsupported compression method + M6.6a.xps -- Simple Text with general purpose bit flag specifying files are encrypted + M6.7a.xps -- Simple Text with offset of central directory equal to unsigned int max + M6.8a.xps -- Simple Text with Total number of entries in central directory more than maxint + M7.2a.xps -- Simple Text DC with xml:lang attrib + + XPS/ + M1.2a.xps -- Package is not a zip archive, it’s plain text instead + M2.3a.xps -- Missing FixedDocumentSequence part + M2.3b.xps -- Missing fixedrepresentation relationship + M2.3c.xps -- Two fixedrepresentation relationships + M2.4a.xps -- Missing FixedDocument part + M2.4b.xps -- No to a FixedDocument + M2.5a.xps -- Missing FixedPage part + M2.5b.xps -- No to FixedPage + M2.6a.xps -- Missing Font Part + M2.6b.xps -- Missing FontURI attribute + M2.10a.xps -- Missing required resource relationship for Font in FixedPage + M2.13a.xps -- Missing fixedrepresentation relationship + M2.13b.xps -- Two fixedrepresentation relationships + M2.14a.xps -- Fixedrepresentation relationship pointing to FixedDocucment + M2.18a.xps -- Corrupt PNG image + M2.25a.xps -- Corrupt TIF image + M2.36a.xps -- FixedPage with 2 thumbnails + M2.37a.xps -- FixedPage with TIF thumbnail + M2.59a.xps -- Two PrintTickets related to FixedDocumentSequence + M2.71a.xps -- DTD in Relationship markup + M2.71b.xps -- DTD in Content_Types markup + M2.71c.xps -- DTD in FixedPage markup + M2.73a.xps -- Xml:id attribute in FixedPage markup + M2.73b.xps -- Xsi:SchemaLocation attribute on element + M2.74a.xps -- having Fill attrib and child element + M2.75a.xps -- Xml:space attribute on element + M2.75b.xps -- Xml:space attribute on element + M2.76a.xps -- Invalid value for xml:lang attribute on element + M3.2a.xps -- Source attribute pointing to Fixed Page instead of FixedDocucment + M3.3a.xps -- Multiple elements pointing to same FixedDocument + M3.5a.xps -- Source attrib pointing to FixedDocucment instead of FixedPage + M3.6a.xps -- Multiple elements pointing to the same FixedPage from difference FixedDocuments + M3.6b.xps -- Multiple elements pointing to same FixedPage (from the same FixedDocument) + M4.3a.xps -- element with Figures attribute and child element + M5.2a.xps -- with empty UnicodeString attribute and no Indices attribute + M5.2b.xps -- with UnicodeString attribute that contains “{}” and no Indices attribute + M5.4a.xps -- with an Indices attribute that contains an invalid Glyph Index for the specified font + M5.4b.xps -- that has more entries in the indices attribute than the UnicodeString attribute + M5.7a.xps -- that has a UnicodeString attribute that starts with '{' + M5.15a.xps -- that has both BidiLevel and isSideways attributes + M6.2a.xps -- with x:Key attribute not in a + M6.3a.xps -- that references a font part + M7.5a.xps -- Remote resource dictionary that references another remote resource dictionary + M12.2a.xps -- 2 references to the same FixedDocument + M12.3b.xps -- FixedPage that is referenced twice from the same FixedDocuments + M12.5a.xps -- FixedDocumentSequence with two PrintTickets + M12.7a.xps -- ContentType attribute containing a Parameter (ContentType=”image/jpeg;q=0”) + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= \ No newline at end of file diff --git a/print/SimplePipelineFilter/FilterSample.def b/print/SimplePipelineFilter/FilterSample.def new file mode 100644 index 00000000..f6a38ef5 --- /dev/null +++ b/print/SimplePipelineFilter/FilterSample.def @@ -0,0 +1,8 @@ +LIBRARY "FilterSample.DLL" + +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + + + diff --git a/print/SimplePipelineFilter/FilterSample.rc b/print/SimplePipelineFilter/FilterSample.rc new file mode 100644 index 00000000..caf78af0 --- /dev/null +++ b/print/SimplePipelineFilter/FilterSample.rc @@ -0,0 +1,10 @@ +#include +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Print Pipeline WDK Filter Sample" +#define VER_INTERNALNAME_STR "WdkPipelineFilter.dll" +#define VER_ORIGINALFILENAME_STR "WdkPipelineFilter.dll" + +#include "common.ver" diff --git a/print/SimplePipelineFilter/ReadMe.md b/print/SimplePipelineFilter/ReadMe.md new file mode 100644 index 00000000..06f17b61 --- /dev/null +++ b/print/SimplePipelineFilter/ReadMe.md @@ -0,0 +1,9 @@ +Print Pipeline Simple Filter +============================ + +The printing system supports a print filter pipeline. The pipeline is run when a print job is consumed by the print spooler and sent to the device. + +This sample shows how to use the print pipeline's filter interfaces. + +The filters in the print pipeline consume a certain data type and produce a certain data type. This information is specified in the pipeline configuration file on a per printer driver basis. The WDK print filter sample contains two filter samples: one that consumes and produces XPS data type, and the other one consumes and produces opaque byte stream. For more information, see the [XpsDrv](http://msdn.microsoft.com/en-us/windows/hardware/gg463364) whitepaper. + diff --git a/print/SimplePipelineFilter/SimplePipelineFilter.sln b/print/SimplePipelineFilter/SimplePipelineFilter.sln new file mode 100644 index 00000000..c7863beb --- /dev/null +++ b/print/SimplePipelineFilter/SimplePipelineFilter.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WdkPipelineFilter", "WdkPipelineFilter.vcxproj", "{D3CE825A-F33C-4DF0-AA6D-F7BC0463164D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D3CE825A-F33C-4DF0-AA6D-F7BC0463164D}.Debug|Win32.ActiveCfg = Debug|Win32 + {D3CE825A-F33C-4DF0-AA6D-F7BC0463164D}.Debug|Win32.Build.0 = Debug|Win32 + {D3CE825A-F33C-4DF0-AA6D-F7BC0463164D}.Release|Win32.ActiveCfg = Release|Win32 + {D3CE825A-F33C-4DF0-AA6D-F7BC0463164D}.Release|Win32.Build.0 = Release|Win32 + {D3CE825A-F33C-4DF0-AA6D-F7BC0463164D}.Debug|x64.ActiveCfg = Debug|x64 + {D3CE825A-F33C-4DF0-AA6D-F7BC0463164D}.Debug|x64.Build.0 = Debug|x64 + {D3CE825A-F33C-4DF0-AA6D-F7BC0463164D}.Release|x64.ActiveCfg = Release|x64 + {D3CE825A-F33C-4DF0-AA6D-F7BC0463164D}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/print/SimplePipelineFilter/StreamFilter.cxx b/print/SimplePipelineFilter/StreamFilter.cxx new file mode 100644 index 00000000..a03aafe0 --- /dev/null +++ b/print/SimplePipelineFilter/StreamFilter.cxx @@ -0,0 +1,263 @@ +//+-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This source code is intended only as a supplement to Microsoft +// Development Tools and/or on-line documentation. See these other +// materials for detailed information regarding Microsoft code samples. +// +// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Abstract: +// WDK print filter sample. +// This is the C file for the stream filter sample. +// +//---------------------------------------------------------------------------- + +#include "precomp.hxx" +#include "main.hxx" +#include "StreamFilter.tmh" +#include "StreamFilter.hxx" + +#include "winddiui.h" +#include "compstui.h" +#include "printoem.h" + +#include "initguid.h" +#include "prcomoem.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +// +// 5f5460d2-b313-44ca-82e4-37f83d793999 - generate a guid, do not use this one in your code +// +const GUID StreamFilterGuid = {0x5f5460d2, 0xb313, 0x44ca, {0x82, 0xe4, 0x37, 0xf8, 0x3d, 0x79, 0x39, 0x99}}; + +const GUID& +StreamFilter:: +FilterClsid( + void + ) +{ + return StreamFilterGuid; +} + +StreamFilter:: +StreamFilter() : + m_bShutdown(false), + m_cRef(1) +{ +} + +// +// IUnknown methods +// +__override +STDMETHODIMP +StreamFilter:: +QueryInterface( + _In_ REFIID riid, + _Out_ void **ppv + ) +{ + HRESULT hRes = E_POINTER; + + if (ppv) + { + hRes = E_NOINTERFACE; + + *ppv = NULL; + + if (riid == IID_IPrintPipelineFilter) + { + *ppv = static_cast(this); + } + else if (riid == IID_IUnknown) + { + *ppv = static_cast(this); + } + + if (*ppv) + { + AddRef(); + + hRes = S_OK; + } + } + + return hRes; +} + +__override +STDMETHODIMP_(ULONG) +StreamFilter:: +AddRef( + void + ) +{ + return InterlockedIncrement(&m_cRef); +} + +__override +STDMETHODIMP_(ULONG) +StreamFilter:: +Release( + void + ) +{ + ULONG cRefCount = InterlockedDecrement(&m_cRef); + + if (cRefCount) + { + return cRefCount; + } + + delete this; + + return 0; +} + +// +// IPrintPipelineFilter +// +__override +STDMETHODIMP +StreamFilter:: +ShutdownOperation( + void + ) +{ + m_bShutdown = true; + + return S_OK; +} + +__override +STDMETHODIMP +StreamFilter:: +InitializeFilter( + _In_ IInterFilterCommunicator *pIFilterCommunicator, + _In_ IPrintPipelinePropertyBag *pIPropertyBag, + _In_ IPrintPipelineManagerControl *pIPipelineControl + ) +{ + HRESULT hr = S_OK; + VARIANT varHelper; + PCSTR *pFeatures = NULL; + DWORD dwFeatures = 0; + Tools::SmartPtr pHelper; + + VariantInit(&varHelper); + + // + // StreamAccessSequential, IID_IPrintReadStream + // + hr = pIFilterCommunicator->RequestReader(reinterpret_cast(&m_pIRead)); + + if (SUCCEEDED(hr)) + { + // + // StreamAccessSequential, StreamModify, IID_IPrintWriteStream + // + hr = pIFilterCommunicator->RequestWriter(reinterpret_cast(&m_pIWrite)); + } + + if (SUCCEEDED(hr)) + { + m_pIPipelineControl = pIPipelineControl; + } + + // + // This shows how to use the helper interface to read information + // from a UnidrvUI based configuration module. + // + + if (SUCCEEDED(hr)) + { + hr = pIPropertyBag->GetProperty(L"IPrintCoreHelper", &varHelper); + } + + if (SUCCEEDED(hr) && V_UNKNOWN(&varHelper)) + { + hr = (V_UNKNOWN(&varHelper))->QueryInterface(IID_IPrintCoreHelper, (VOID**)(&pHelper)); + } + + // + // No need to release buffers provided by the helper. Their + // lifetime matches that of the helper. + // + if (SUCCEEDED(hr) && pHelper) + { + hr = pHelper->EnumFeatures(&pFeatures, &dwFeatures); + } + + if (SUCCEEDED(hr) && dwFeatures > 0) + { + for (DWORD i = 0; i < dwFeatures; i++) + { + DoTraceMessage(WS_TRACE, L"StreamFilter::InitializeFilter: Feature found: %s", pFeatures[i]); + } + } + + VariantClear(&varHelper); + + return hr; +} + +__override +STDMETHODIMP +StreamFilter:: +StartOperation( + void + ) +{ + HRESULT hr = S_OK; + Tools::SmartPtr pIErrorInfo; + DWORD cbRead; + BYTE *pReadBuf; + BOOL bEof = FALSE; + + pReadBuf = new BYTE[kBufferSize]; + + if (!pReadBuf) + { + hr = E_OUTOFMEMORY; + } + + while (SUCCEEDED(hr) && !bEof && !m_bShutdown) + { + hr = m_pIRead->ReadBytes(pReadBuf, kBufferSize, &cbRead, &bEof); + + if (SUCCEEDED(hr) && cbRead) + { + ULONG cbWritten; + + hr = m_pIWrite->WriteBytes(pReadBuf, cbRead, &cbWritten); + + if (SUCCEEDED(hr)) + { + DoTraceMessage(WS_TRACE, "StreamFilter::StartOperation read and wrote %u bytes", cbRead); + } + } + } + + m_pIWrite->Close(); + + if (FAILED(hr)) + { + m_pIPipelineControl->RequestShutdown(hr, pIErrorInfo); + } + + if (m_pIPipelineControl) + { + m_pIPipelineControl->FilterFinished(); + } + + delete [] pReadBuf; + + return hr; +} + + diff --git a/print/SimplePipelineFilter/StreamFilter.hxx b/print/SimplePipelineFilter/StreamFilter.hxx new file mode 100644 index 00000000..c23f5fb2 --- /dev/null +++ b/print/SimplePipelineFilter/StreamFilter.hxx @@ -0,0 +1,99 @@ +//+-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This source code is intended only as a supplement to Microsoft +// Development Tools and/or on-line documentation. See these other +// materials for detailed information regarding Microsoft code samples. +// +// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Abstract: +// WDK print filter sample. +// This is the header file for the stream filter sample. +// +//---------------------------------------------------------------------------- + +#ifndef _STREAM_FILTER_SAMPLE_HXX_ +#define _STREAM_FILTER_SAMPLE_HXX_ + +class StreamFilter : + public IPrintPipelineFilter, + private DllLockManager +{ +public: + + StreamFilter(); + + // + // IUnknown methods + // + __override + STDMETHODIMP + QueryInterface( + _In_ REFIID riid, + _Out_ void **ppv + ); + + __override + STDMETHODIMP_(ULONG) + AddRef( + void + ); + + __override + STDMETHODIMP_(ULONG) + Release( + void + ); + + // + // IPrintPipelineFilter + // + __override + STDMETHODIMP + ShutdownOperation( + void + ); + + __override + STDMETHODIMP + InitializeFilter( + _In_ IInterFilterCommunicator *pINegotiation, + _In_ IPrintPipelinePropertyBag *pIPropertyBag, + _In_ IPrintPipelineManagerControl *pIPipelineControl + ); + + __override + STDMETHODIMP + StartOperation( + void + ); + + // + // Other methods + // + static + const GUID& + FilterClsid( + void + ); + +private: + + enum + { + kBufferSize = 0x10000 + }; + + Tools::SmartPtr m_pIRead; + Tools::SmartPtr m_pIWrite; + Tools::SmartPtr m_pIPipelineControl; + Tools::SmartPtr m_pProgressReport; + bool m_bShutdown; + LONG m_cRef; +}; + +#endif // _STREAM_FILTER_SAMPLE_HXX_ diff --git a/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj b/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj new file mode 100644 index 00000000..3af320ac --- /dev/null +++ b/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj @@ -0,0 +1,294 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D3CE825A-F33C-4DF0-AA6D-F7BC0463164D} + $(MSBuildProjectName) + Debug + Win32 + {5BC5D9DA-A9ED-4BE2-8DD9-33853EFC148A} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + true + ;%(AdditionalIncludeDirectories) + precomp.hxx + Use + $(IntDir)\precomp.hxx.pch + + + true + ;%(AdditionalIncludeDirectories) + precomp.hxx + Use + $(IntDir)\precomp.hxx.pch + + + true + ;%(AdditionalIncludeDirectories) + precomp.hxx + Use + $(IntDir)\precomp.hxx.pch + + + true + + + + WdkPipelineFilter + + + WdkPipelineFilter + + + WdkPipelineFilter + + + WdkPipelineFilter + + + + %(AdditionalDependencies);user32.lib;kernel32.lib;oleaut32.lib;ole32.lib;uuid.lib;advapi32.lib + + + + + %(AdditionalDependencies);user32.lib;kernel32.lib;oleaut32.lib;ole32.lib;uuid.lib;advapi32.lib + + + + + %(AdditionalDependencies);user32.lib;kernel32.lib;oleaut32.lib;ole32.lib;uuid.lib;advapi32.lib + + + + + %(AdditionalDependencies);user32.lib;kernel32.lib;oleaut32.lib;ole32.lib;uuid.lib;advapi32.lib + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + MultiThreaded + MultiThreadedDebug + true + Level4 + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + MultiThreaded + MultiThreadedDebug + true + Level4 + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + MultiThreaded + MultiThreadedDebug + true + Level4 + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + MultiThreaded + MultiThreadedDebug + true + Level4 + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalOptions) /ignore:4070 + FilterSample.def + + + + + + + + + %(AdditionalOptions) /ignore:4070 + FilterSample.def + + + + + + + + + %(AdditionalOptions) /ignore:4070 + FilterSample.def + + + + + + + + + %(AdditionalOptions) /ignore:4070 + FilterSample.def + + + + + + + + + ;%(AdditionalIncludeDirectories) + precomp.hxx + Create + $(IntDir)\precomp.hxx.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj.Filters b/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj.Filters new file mode 100644 index 00000000..65c4c551 --- /dev/null +++ b/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj.Filters @@ -0,0 +1,39 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {9E97BFB7-9C77-4223-A684-83194A1A1C59} + + + h;hpp;hxx;hm;inl;inc;xsd + {ED009CED-C036-41C0-96C7-B993C7F42ECD} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {F44E5BC1-1970-4C43-A39A-30A1F28A6E42} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/print/SimplePipelineFilter/XpsFilter.cxx b/print/SimplePipelineFilter/XpsFilter.cxx new file mode 100644 index 00000000..fe4945d0 --- /dev/null +++ b/print/SimplePipelineFilter/XpsFilter.cxx @@ -0,0 +1,785 @@ +//+-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This source code is intended only as a supplement to Microsoft +// Development Tools and/or on-line documentation. See these other +// materials for detailed information regarding Microsoft code samples. +// +// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Abstract: +// WDK print filter sample. +// This is the C file for the stream filter sample. +// +//---------------------------------------------------------------------------- + +#include "precomp.hxx" +#include "main.hxx" +#include "XpsFilter.tmh" +#include "XpsFilter.hxx" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +// +// 8267d291-6ddd-4972-a94e-3ce88149a1fa - generate a guid, do not use this one in your code +// +const GUID ClsidXpsFilter = {0x8267d291, 0x6ddd, 0x4972, {0xa9, 0x4e, 0x3c, 0xe8, 0x81, 0x49, 0xa1, 0xfa}}; + +const GUID& +XpsFilter:: +FilterClsid( + void + ) +{ + return ClsidXpsFilter; +} + +#define COUNTOF(x) (sizeof(x)/sizeof(*x)) + +HRESULT +GetLastErrorAsHResult( + void + ) +{ + DWORD dwError = GetLastError(); + + return HRESULT_FROM_WIN32(dwError); +} + +HRESULT +ProcessTicket( + _In_ IPartPrintTicket *pIPrintTicket + ) +{ + Tools::SmartBSTR bstr; + + HRESULT hRes = pIPrintTicket->GetUri(&bstr); + + if (SUCCEEDED(hRes)) + { + Tools::SmartPtr pIStream; + + hRes = pIPrintTicket->GetStream(&pIStream); + + if (SUCCEEDED(hRes)) + { + ULONG cbRead = 0; + BOOL bEof = FALSE; + + DoTraceMessage(WS_TRACE, "ProcessTicket print ticket follows."); + + do + { + BYTE buf[200] = {0}; + + // + // Save one byte to ensure null termination + // + if (SUCCEEDED(hRes = pIStream->ReadBytes(buf, COUNTOF(buf) - 1, &cbRead, &bEof)) && cbRead) + { + // + // use PT data + // + DoTraceMessage(WS_TRACE, "%s", reinterpret_cast(buf)); + } + } + while (!bEof && cbRead && SUCCEEDED(hRes)); + } + } + + return hRes; +} + +HRESULT +AddFontToPage( + _In_ IPartFont *pNewFont, + _In_ IPrintWriteStream *pFontStream, + _In_ IFixedPage *pNewPage + ) +{ + WCHAR szFont[MAX_PATH]; + HRESULT hRes = S_OK; + + if (!GetWindowsDirectory(szFont, MAX_PATH)) + { + hRes = GetLastErrorAsHResult(); + } + + if (SUCCEEDED(hRes)) + { + hRes = StringCchCat(szFont, MAX_PATH, L"\\fonts\\verdana.TTF"); + } + + if (SUCCEEDED(hRes)) + { + HANDLE hFile = CreateFile(szFont, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + BYTE buf[400]; + ULONG cbBuf = 400; + ULONG cbRead, cbWritten; + + do + { + if (ReadFile(hFile, buf, cbBuf, &cbRead, NULL)) + { + hRes = pFontStream->WriteBytes(buf, cbRead, &cbWritten); + } + else + { + hRes = GetLastErrorAsHResult(); + } + } + while (SUCCEEDED(hRes) && cbRead); + + CloseHandle(hFile); + } + else + { + hRes = GetLastErrorAsHResult(); + } + + pFontStream->Close(); + } + + if (SUCCEEDED(hRes)) + { + hRes = pNewPage->SetPagePart(pNewFont); + } + + return hRes; +} + +HRESULT +AddTextToPage( + _In_ PCWSTR pszReferencedFont, + _In_ IPrintWriteStream *pPageMarkupStream + ) +{ + HRESULT hRes; + ULONG cb; + CHAR buf[500]; + + // + // Mark up for page generated on the fly: "hello world" + // + CHAR sz[] = "" + "" + ""; + + // + // Insert the URI of the font we added to the document + // + hRes = StringCchPrintfA(buf, COUNTOF(buf), sz, pszReferencedFont); + + // + // Write markup for page + // + if (SUCCEEDED(hRes) && + SUCCEEDED(hRes = pPageMarkupStream->WriteBytes(buf, static_cast(strlen(buf)), &cb))) + { + pPageMarkupStream->Close(); + } + + return hRes; +} + +// +// This function reads the page content and writes it back. Can be extended +// to do useful work +// +HRESULT +ModifyContent( + _In_ IPrintReadStream *pRead, + _In_ IPrintWriteStream *pWrite + ) +{ + CHAR buf[100] = {0}; + HRESULT hRes; + ULONG cbr, cbw; + BOOL bEof = FALSE; + + do + { + hRes = pRead->ReadBytes(buf, sizeof(buf), &cbr, &bEof); + + if (SUCCEEDED(hRes) && cbr) + { + // + // do something with buffer + // + cbw = cbr; + + hRes = pWrite->WriteBytes(buf, cbr, &cbw); + } + } + while (!bEof && cbr && SUCCEEDED(hRes)); + + return hRes; +} + +HRESULT +XpsFilter:: +ProcessImagePart( + _In_ IPartImage *pIPartImage + ) +{ + Tools::SmartBSTR bstr; + + HRESULT hRes = pIPartImage->GetUri(&bstr); + + DoTraceMessage(WS_TRACE, "ProcessImagePart uri %ws", static_cast(bstr)); + + if (SUCCEEDED(hRes)) + { + Tools::SmartPtr pRead; + + if (SUCCEEDED(hRes = pIPartImage->GetStream(&pRead))) + { + // + // do something with image + // + } + } + + return hRes; +} + +// +// Non-static members +// +XpsFilter:: +XpsFilter() : + m_bShutdown(false), + m_cRef(1), + m_cIdCount(0) +{ +} + +HRESULT +XpsFilter:: +ProcessFixedPage( + _In_ void *pVoid + ) +{ + Tools::SmartPtr pIFixedPage; + + // + // XpsFilter::ProcessPart already incremented the reference count for the object. This + // function is taking ownership of the object, there is no need for another AddRef call + // (hence the Attach) + // + pIFixedPage.Attach(static_cast(pVoid)); + + HRESULT hRes; + Tools::SmartBSTR bstr; + + if (SUCCEEDED(pIFixedPage->GetUri(&bstr))) + { + DoTraceMessage(WS_TRACE, "ProcessFixedPage uri %ws", static_cast(bstr)); + } + + Tools::SmartPtr pIPrintTicket; + + hRes = pIFixedPage->GetPrintTicket(&pIPrintTicket); + + if (SUCCEEDED(hRes)) + { + hRes = ProcessTicket(pIPrintTicket); + } + else if (hRes == E_ELEMENT_NOT_FOUND) + { + // + // No ticket. Benign. + // + hRes = S_OK; + + DoTraceMessage(WS_WARNING, "ProcessFixedPage uri %ws. Page print ticket not present.", static_cast(bstr)); + } + + pIFixedPage->SetPartCompression(Compression_Small); + + // + // modify content for page, optional + // + Tools::SmartPtr pRead; + Tools::SmartPtr pWrite; + + if (SUCCEEDED(hRes = pIFixedPage->GetStream(&pRead)) && + SUCCEEDED(hRes = pIFixedPage->GetWriteStream(&pWrite))) + { + hRes = ModifyContent(pRead, pWrite); + + pWrite->Close(); + } + + // + // Send page to next filter + // + if (SUCCEEDED(hRes)) + { + hRes = m_pReachConsumer->SendFixedPage(pIFixedPage); + } + + // + // Add a new page + // + if (SUCCEEDED(hRes)) + { + Tools::SmartPtr pNewPage; + Tools::SmartPtr pNewPageMarkupStream; + Tools::SmartPtr pNewFont; + Tools::SmartPtr pNewFontStream; + + WCHAR szName[MAX_PATH]; + + // + // Generate unique page name + // + if (SUCCEEDED(hRes = StringCchPrintf(szName, MAX_PATH, L"/pages/newaddedpage%u.xaml", m_cIdCount++))) + { + // + // Create new fixed page + // + hRes = m_pReachConsumer->GetNewEmptyPart(szName, + IID_IFixedPage, + reinterpret_cast(&pNewPage), + &pNewPageMarkupStream); + + DoTraceMessage(WS_TRACE, "ProcessFixedPage new page %ws", szName); + } + + // + // Generate unique font name, this can, of course, be optimized so we don't include + // the same font multiple times. But this code is supposed to just to show how to + // add a font + // + if (SUCCEEDED(hRes = StringCchPrintf(szName, MAX_PATH, L"/font_%u.ttf", m_cIdCount++))) + { + // + // Create new font resource + // + hRes = m_pReachConsumer->GetNewEmptyPart(szName, + IID_IPartFont, + reinterpret_cast(&pNewFont), + &pNewFontStream); + + DoTraceMessage(WS_TRACE, "ProcessFixedPage new font %ws", szName); + } + + if (SUCCEEDED(hRes) && + SUCCEEDED(hRes = AddFontToPage(pNewFont, pNewFontStream, pNewPage)) && + SUCCEEDED(hRes = AddTextToPage(szName, pNewPageMarkupStream))) + { + // + // Send page to next filter + // + hRes = m_pReachConsumer->SendFixedPage(pNewPage); + } + } + + return hRes; +} + + + +struct Pair_t +{ + typedef HRESULT (XpsFilter::*PFN)(void *); + + Pair_t(REFIID d, PFN n) : iid(d), pfn(n) {} + + REFIID iid; + PFN pfn; + +private: + + Pair_t& operator=(const Pair_t&); +}; + + +HRESULT +XpsFilter:: +ProcessPart( + _In_ IUnknown *pUnk + ) +{ + HRESULT hRes = S_OK; + + Pair_t pairs[] = + { + Pair_t(IID_IXpsDocument, &XpsFilter::ProcessXpsDoc), + Pair_t(IID_IFixedDocumentSequence, &XpsFilter::ProcessFixedDocSequence), + Pair_t(IID_IFixedDocument, &XpsFilter::ProcessFixedDoc), + Pair_t(IID_IFixedPage, &XpsFilter::ProcessFixedPage), + }; + + // + // Detect what part we got + // + for (ULONG i = 0; i < sizeof(pairs)/sizeof(*pairs); i++) + { + void *pvoid = NULL; + + if (SUCCEEDED(hRes = pUnk->QueryInterface(pairs[i].iid, &pvoid))) + { + // + // QueryInterface increments the reference count for the object it returns + // + hRes = (this->*pairs[i].pfn)(pvoid); + + break; + } + else if (hRes != E_NOINTERFACE) + { + break; + } + } + + // + // IPartDiscardControl objects may float through the pipeline. The code above does not check for IPartDiscardControl. + // So, for maximum compatibility, the filter forwards parts that it doesn't understand. + // + if (hRes == E_NOINTERFACE) + { + hRes = m_pReachConsumer->SendXpsUnknown(pUnk); + } + + return hRes; +} + +HRESULT +XpsFilter:: +ProcessXpsDoc( + _In_ void *pVoid + ) +{ + Tools::SmartPtr pIXpsDoc; + + pIXpsDoc.Attach(static_cast(pVoid)); + + DoTraceMessage(WS_TRACE, L"ProcessXpsDoc"); + + return m_pReachConsumer->SendXpsDocument(pIXpsDoc); +} + +HRESULT +XpsFilter:: +ProcessFixedDocSequence( + _In_ void *pVoid + ) +{ + Tools::SmartPtr pIFixedDocumentSequence; + + // + // XpsFilter::ProcessPart already incremented the reference count for the object. This + // function is taking ownership of the object, there is no need for another AddRef call + // (hence the Attach) + // + pIFixedDocumentSequence.Attach(static_cast(pVoid)); + + Tools::SmartBSTR bstr; + + if (SUCCEEDED(pIFixedDocumentSequence->GetUri(&bstr))) + { + DoTraceMessage(WS_TRACE, "ProcessFixedDocSequence uri %ws", static_cast(bstr)); + } + + Tools::SmartPtr pIPrintTicket; + + HRESULT hRes = pIFixedDocumentSequence->GetPrintTicket(&pIPrintTicket); + + if (SUCCEEDED(hRes)) + { + hRes = ProcessTicket(pIPrintTicket); + } + else if (hRes == E_ELEMENT_NOT_FOUND) + { + // + // No ticket. Benign. + // + hRes = S_OK; + + DoTraceMessage(WS_WARNING, "ProcessFixedDocSequence uri %ws. Page print ticket not present.", static_cast(bstr)); + } + + return m_pReachConsumer->SendFixedDocumentSequence(pIFixedDocumentSequence); +} + +HRESULT +XpsFilter:: +ProcessFixedDoc( + _In_ void *pVoid + ) +{ + Tools::SmartPtr pIFixedDocument; + + // + // XpsFilter::ProcessPart already incremented the reference count for the object. This + // function is taking ownership of the object, there is not need for another AddRef call + // (hence the Attach) + // + pIFixedDocument.Attach(static_cast(pVoid)); + + Tools::SmartBSTR bstr; + + if (SUCCEEDED(pIFixedDocument->GetUri(&bstr))) + { + DoTraceMessage(WS_TRACE, "ProcessFixedDoc uri %ws", static_cast(bstr)); + } + + Tools::SmartPtr pIPrintTicket; + + HRESULT hRes = pIFixedDocument->GetPrintTicket(&pIPrintTicket); + + if (SUCCEEDED(hRes)) + { + hRes = ProcessTicket(pIPrintTicket); + } + else if (hRes == E_ELEMENT_NOT_FOUND) + { + // + // No ticket. Benign. + // + hRes = S_OK; + + DoTraceMessage(WS_WARNING, "ProcessFixedDoc uri %ws. Page print ticket not present.", static_cast(bstr)); + } + + return m_pReachConsumer->SendFixedDocument(pIFixedDocument); +} + +// +// IPrintPipelineFilter +// +__override +STDMETHODIMP +XpsFilter:: +ShutdownOperation( + void + ) +{ + m_bShutdown = true; + + return S_OK; +} + +__override +STDMETHODIMP +XpsFilter:: +InitializeFilter( + _In_ IInterFilterCommunicator *pIfc, + _In_ IPrintPipelinePropertyBag *pIPropertyBag, + _In_ IPrintPipelineManagerControl *pIPipelineControl + ) +{ + HRESULT hRes = S_OK; + + // + // IID_IReachPackageProvider must be specified in the config file + // + hRes = pIfc->RequestReader(reinterpret_cast(&m_pReachProvider)); + + if (SUCCEEDED(hRes)) + { + // + // IID_IReachPackageConsumer must be specified in the config file + // + hRes = pIfc->RequestWriter(reinterpret_cast(&m_pReachConsumer)); + + m_pIPipelineControl = pIPipelineControl; + } + + VARIANT var, var2, var3, var4; + + VariantInit(&var); + VariantInit(&var2); + VariantInit(&var3); + VariantInit(&var4); + + if (SUCCEEDED(hRes) && + SUCCEEDED(hRes = pIPropertyBag->GetProperty(XPS_FP_PRINTER_NAME, &var)) && + SUCCEEDED(hRes = pIPropertyBag->GetProperty(XPS_FP_PRINTER_HANDLE, &var2)) && + SUCCEEDED(hRes = pIPropertyBag->GetProperty(XPS_FP_JOB_ID, &var3)) && + SUCCEEDED(hRes = pIPropertyBag->GetProperty(XPS_FP_USER_TOKEN, &var4))) + { + PWSTR pszName = var.bstrVal; + HANDLE hPrinter = var2.byref; + ULONG jobId = var3.ulVal; + HANDLE hUserSecurityToken = var4.byref; + + // + // Filter can use devmode, hPrinter and pszName. byref value should not be freed/deleted + // in any way. bstrVal must be deleted by clearing the variant. + // + + // + // Do not free any byref resource. Ex: do not call CloseHandle on the security token + // + + // + // Remove these if you use the variables. This is in order to build clean with /W4 + // + UNREFERENCED_PARAMETER(jobId); + UNREFERENCED_PARAMETER(hPrinter); + UNREFERENCED_PARAMETER(pszName); + + // + // Sample code to show how to impersonate in order to access resources on behalf of the + // user who submitted the job + // + if (SetThreadToken(NULL, hUserSecurityToken)) + { + // + // now revert back to the original security context + // + if (!SetThreadToken(NULL, NULL)) + { + hRes = GetLastErrorAsHResult(); + } + } + else + { + hRes = GetLastErrorAsHResult(); + } + } + + VariantClear(&var); + VariantClear(&var2); + VariantClear(&var3); + VariantClear(&var4); + + // + // Example of how to add and delete a property from the bag + // + var.vt = VT_I4; + var.lVal = 5; + + if (SUCCEEDED(hRes) && + SUCCEEDED(hRes = pIPropertyBag->AddProperty(L"TestProperty", &var))) + { + hRes = pIPropertyBag->DeleteProperty(L"TestProperty") ? S_OK : E_FAIL; + } + + + return hRes; +} + +__override +STDMETHODIMP +XpsFilter:: +StartOperation( + void + ) +{ + HRESULT hRes = S_OK; + + while (SUCCEEDED(hRes) && !m_bShutdown) + { + Tools::SmartPtr pUnk; + + hRes = m_pReachProvider->GetXpsPart(&pUnk); + + if (SUCCEEDED(hRes)) + { + if (!pUnk) + { + // + // End of objects + // + break; + } + + hRes = ProcessPart(pUnk); + } + } + + m_pReachConsumer->CloseSender(); + + if (FAILED(hRes)) + { +#pragma prefast(suppress:__WARNING_INVALID_PARAM_VALUE_1, "MSDN requires that pReason be NULL.") + m_pIPipelineControl->RequestShutdown(hRes, NULL); + } + + m_pIPipelineControl->FilterFinished(); + + return hRes; +} + +// +// IUnknown +// +STDMETHODIMP_(ULONG) +XpsFilter:: +AddRef( + VOID + ) +{ + return InterlockedIncrement(&m_cRef); +} + +STDMETHODIMP_(ULONG) +XpsFilter:: +Release( + VOID + ) +{ + ULONG cRefCount = InterlockedDecrement(&m_cRef); + + if (cRefCount) + { + return cRefCount; + } + + delete this; + + return 0; +} + +STDMETHODIMP +XpsFilter:: +QueryInterface( + _In_ REFIID riid, + _Out_ VOID **ppv + ) +{ + HRESULT hRes = E_POINTER; + + if (ppv) + { + hRes = E_NOINTERFACE; + + *ppv = NULL; + + if (riid == IID_IPrintPipelineFilter) + { + *ppv = static_cast(this); + } + else if (riid == IID_IUnknown) + { + *ppv = static_cast(this); + } + + if (*ppv) + { + AddRef(); + + hRes = S_OK; + } + } + + return hRes; +} + diff --git a/print/SimplePipelineFilter/XpsFilter.hxx b/print/SimplePipelineFilter/XpsFilter.hxx new file mode 100644 index 00000000..003650a6 --- /dev/null +++ b/print/SimplePipelineFilter/XpsFilter.hxx @@ -0,0 +1,126 @@ +//+-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This source code is intended only as a supplement to Microsoft +// Development Tools and/or on-line documentation. See these other +// materials for detailed information regarding Microsoft code samples. +// +// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Abstract: +// WDK print filter sample. +// This is the header file for the XPS sample. +// +//---------------------------------------------------------------------------- + +#ifndef _XPS_FILTER_SAMPLE_HXX_ +#define _XPS_FILTER_SAMPLE_HXX_ + +class XpsFilter : + public IPrintPipelineFilter, + private DllLockManager +{ +public: + + XpsFilter(); + + // + // IUnknown methods + // + __override + STDMETHODIMP + QueryInterface( + _In_ REFIID riid, + _Out_ void **ppv + ); + + __override + STDMETHODIMP_(ULONG) + AddRef( + void + ); + + __override + STDMETHODIMP_(ULONG) + Release( + void + ); + + // + // IPrintPipelineFilter + // + __override + STDMETHODIMP + ShutdownOperation( + void + ); + + __override + STDMETHODIMP + InitializeFilter( + _In_ IInterFilterCommunicator *pINegotiation, + _In_ IPrintPipelinePropertyBag *pIPropertyBag, + _In_ IPrintPipelineManagerControl *pIPipelineControl + ); + + __override + STDMETHODIMP + StartOperation( + void + ); + + // + // Other methods + // + static + const GUID& + FilterClsid( + void + ); + +private: + + HRESULT + ProcessFixedPage( + _In_ void *pVoid + ); + + HRESULT + ProcessXpsDoc( + _In_ void *pVoid + ); + + HRESULT + ProcessFixedDocSequence( + _In_ void *pVoid + ); + + HRESULT + ProcessFixedDoc( + _In_ void *pVoid + ); + + HRESULT + ProcessPart( + _In_ IUnknown *pUnk + ); + + static + HRESULT + ProcessImagePart( + _In_ IPartImage *pIPartImage + ); + + Tools::SmartPtr m_pIPipelineControl; + Tools::SmartPtr m_pReachProvider; + Tools::SmartPtr m_pReachConsumer; + bool m_bShutdown; + LONG m_cRef; + LONG m_cIdCount; +}; + +#endif // _XPS_FILTER_SAMPLE_HXX_ + diff --git a/print/SimplePipelineFilter/main.cxx b/print/SimplePipelineFilter/main.cxx new file mode 100644 index 00000000..180fb4c6 --- /dev/null +++ b/print/SimplePipelineFilter/main.cxx @@ -0,0 +1,172 @@ +//+-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This source code is intended only as a supplement to Microsoft +// Development Tools and/or on-line documentation. See these other +// materials for detailed information regarding Microsoft code samples. +// +// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Abstract: +// WDK print filter sample. This file contains the implementation for +// DllGetClassObject and DllMain +// +//---------------------------------------------------------------------------- + +#include "precomp.hxx" +#include "main.hxx" +#include "XpsFilter.hxx" +#include "StreamFilter.hxx" +#include "main.tmh" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +LONG DllLockManager::m_cGlobalRef = 0; + +HRESULT +WINAPI +DllCanUnloadNow(void) throw() +{ + return 0 == DllLockManager::GetGlobalRef() ? S_OK : S_FALSE; +} + +HRESULT +WINAPI +DllGetClassObject( + _In_ REFCLSID rclsid, + _In_ REFIID riid, + _Outptr_ PVOID *ppv + ) throw() +{ + HRESULT hRetval = E_POINTER; + + if (ppv) + { + *ppv = NULL; + + if (rclsid == XpsFilter::FilterClsid()) + { + Tools::SmartPtr pFilter; + + pFilter.Attach(new XpsFilter()); + + if (pFilter) + { + hRetval = pFilter->QueryInterface(riid, ppv); + } + else + { + hRetval = E_OUTOFMEMORY; + } + } + else if (rclsid == StreamFilter::FilterClsid()) + { + Tools::SmartPtr pFilter; + + pFilter.Attach(new StreamFilter()); + + if (pFilter) + { + hRetval = pFilter->QueryInterface(riid, ppv); + } + else + { + hRetval = E_OUTOFMEMORY; + } + } + else + { + hRetval = CLASS_E_CLASSNOTAVAILABLE; + } + } + + return hRetval; +} + +BOOL WINAPI +DllMain( + _In_ HINSTANCE hInst, + _In_ DWORD dwReason, + _In_opt_ PVOID pReserved + ) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hInst); + + WPP_INIT_TRACING(L"WdkPipelineFilter"); + } + else if (dwReason == DLL_PROCESS_DETACH) + { + WPP_CLEANUP(); + } + + UNREFERENCED_PARAMETER(dwReason); + UNREFERENCED_PARAMETER(pReserved); + + return TRUE; +} + +void +WppTraceDebugOut( + _In_z_ const WCHAR *pszFmt, + ... + ) +{ + va_list pArgs; + + va_start(pArgs, pszFmt); + + WCHAR buf[MAX_PATH]; + + if (SUCCEEDED(StringCchVPrintf(buf, + MAX_PATH, + pszFmt, + pArgs)) && + SUCCEEDED(StringCchCat(buf, + MAX_PATH, + L"\n"))) + { + OutputDebugString(buf); + } + + va_end(pArgs); +} + +void +WppTraceDebugOut( + _In_z_ const CHAR *pszFmt, + ... + ) +{ + va_list pArgs; + + va_start(pArgs, pszFmt); + + CHAR buf[MAX_PATH]; + + if (SUCCEEDED(StringCchVPrintfA(buf, + MAX_PATH, + pszFmt, + pArgs)) && + SUCCEEDED(StringCchCatA(buf, + MAX_PATH, + "\n"))) + { + OutputDebugStringA(buf); + } + + va_end(pArgs); +} + + + + + + + + + diff --git a/print/SimplePipelineFilter/main.hxx b/print/SimplePipelineFilter/main.hxx new file mode 100644 index 00000000..88d8c052 --- /dev/null +++ b/print/SimplePipelineFilter/main.hxx @@ -0,0 +1,51 @@ +//+-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This source code is intended only as a supplement to Microsoft +// Development Tools and/or on-line documentation. See these other +// materials for detailed information regarding Microsoft code samples. +// +// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Abstract: +// WDK print filter sample. +// The filters need to derive from DllLockManager. Thus the filter constructor and +// destructor call the DllLockManager constructor and destructor, respectively. +// +//---------------------------------------------------------------------------- + +#ifndef _FILTER_MAIN_HXX_ +#define _FILTER_MAIN_HXX_ + +class DllLockManager +{ +public: + + DllLockManager() + { + InterlockedIncrement(&m_cGlobalRef); + } + + ~DllLockManager() + { + InterlockedDecrement(&m_cGlobalRef); + } + + static + LONG + GetGlobalRef() + { + return InterlockedCompareExchange(&m_cGlobalRef, 0, 0); + } + +private: + + static LONG m_cGlobalRef; +}; + +#endif + + diff --git a/print/SimplePipelineFilter/precomp.hxx b/print/SimplePipelineFilter/precomp.hxx new file mode 100644 index 00000000..4ca7012d --- /dev/null +++ b/print/SimplePipelineFilter/precomp.hxx @@ -0,0 +1,26 @@ +//+-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This source code is intended only as a supplement to Microsoft +// Development Tools and/or on-line documentation. See these other +// materials for detailed information regarding Microsoft code samples. +// +// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Abstract: +// WDK print filter sample. +// This is the precompiled header file. +// +//---------------------------------------------------------------------------- + +#include +#include +#include +#include +#include "smartptr.hxx" +#include "trace.hxx" + +_Analysis_mode_(_Analysis_code_type_user_driver_) diff --git a/print/SimplePipelineFilter/precompsrc.cpp b/print/SimplePipelineFilter/precompsrc.cpp new file mode 100644 index 00000000..5d84a122 --- /dev/null +++ b/print/SimplePipelineFilter/precompsrc.cpp @@ -0,0 +1 @@ +#include "precomp.hxx" \ No newline at end of file diff --git a/print/SimplePipelineFilter/smartptr.hxx b/print/SimplePipelineFilter/smartptr.hxx new file mode 100644 index 00000000..7f20c530 --- /dev/null +++ b/print/SimplePipelineFilter/smartptr.hxx @@ -0,0 +1,233 @@ +//+-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This source code is intended only as a supplement to Microsoft +// Development Tools and/or on-line documentation. See these other +// materials for detailed information regarding Microsoft code samples. +// +// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Abstract: +// WDK print filter sample. +// This file contains a smart pointer implementation. +// +//---------------------------------------------------------------------------- + +#ifndef __SAMPLE_SMART_PTR_HXX__ +#define __SAMPLE_SMART_PTR_HXX__ + +namespace Tools +{ + +template +inline +void +SmartAddRef( + T *pInterface + ) throw() +{ + if (pInterface) + { + pInterface->AddRef(); + } +} + +template +inline +void +SmartRelease( + T **ppInterface + ) throw() +{ + if (*ppInterface) + { + (*ppInterface)->Release(); + *ppInterface = NULL; + } +} + +template +class SmartPtr +{ +public: + + inline + SmartPtr( + void + ) throw() : m_Pointer(NULL) + { + } + + inline + SmartPtr( + _In_opt_ T *pointer + ) throw() + : m_Pointer(pointer) + { + SmartAddRef(m_Pointer); + } + + inline + SmartPtr( + _In_ const SmartPtr © + ) throw() + { + m_Pointer = copy.m_Pointer; + SmartAddRef(m_Pointer); + } + + inline + ~SmartPtr( + void + ) throw() + { + SmartRelease(&m_Pointer); + } + + inline + SmartPtr & + operator=( + _In_opt_ T *pointer + ) throw() + { + T *temp = m_Pointer; + + m_Pointer = pointer; + SmartAddRef(m_Pointer); + SmartRelease(&temp); + + return *this; + } + + inline + SmartPtr & + operator=( + _In_ const SmartPtr © + ) throw() + { + T *temp = m_Pointer; + + m_Pointer = copy.m_Pointer; + SmartAddRef(m_Pointer); + SmartRelease(&temp); + + return *this; + } + + inline + operator T *() throw() + { + return m_Pointer; + } + + inline + operator const T *() const throw() + { + return m_Pointer; + } + + inline + T ** + operator&() throw() + { + return &m_Pointer; + } + + inline + const T ** + operator&() const throw() + { + return &m_Pointer; + } + + inline + T * + operator->() throw() + { + return m_Pointer; + } + + inline + const T * + operator->( + ) const throw() + { + return m_Pointer; + } + + inline + void + Attach( + _In_opt_ T *pointer + ) throw() + { + T *temp = m_Pointer; + m_Pointer = pointer; + + SmartRelease(&temp); + } + + inline + T * + Detach( + void + ) throw() + { + T *pTemp = m_Pointer; + m_Pointer = NULL; + + return pTemp; + } + + inline + void + Clear( + void + ) throw() + { + SmartRelease(&m_Pointer); + } + +private: + + T *m_Pointer; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class SmartBSTR +{ +public: + + SmartBSTR() throw() + : m_bstr (NULL) + { + } + + ~SmartBSTR() throw() + { + ::SysFreeString(m_bstr); + } + + operator BSTR() const throw() + { + return m_bstr; + } + + BSTR* + operator&() throw() + { + return &m_bstr; + } + +private: + + BSTR m_bstr; +}; + +}; // namespace tools + +#endif diff --git a/print/SimplePipelineFilter/trace.hxx b/print/SimplePipelineFilter/trace.hxx new file mode 100644 index 00000000..8b905c49 --- /dev/null +++ b/print/SimplePipelineFilter/trace.hxx @@ -0,0 +1,51 @@ +//+-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This source code is intended only as a supplement to Microsoft +// Development Tools and/or on-line documentation. See these other +// materials for detailed information regarding Microsoft code samples. +// +// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY +// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +// +// Abstract: +// WDK print filter sample. +// Declares what is needed to Event Tracing. +// +//---------------------------------------------------------------------------- + +#ifndef __WDK_SAMPLE_TRACE_HXX__ +#define __WDK_SAMPLE_TRACE_HXX__ + +// +// Generate your own guid. Feel free to add more flags, as needed. +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(WdkSample, (79ad6dce, adad, 47ae, b49e, 41bcdccf9ed8), \ + WPP_DEFINE_BIT(WS_ERROR) \ + WPP_DEFINE_BIT(WS_WARNING) \ + WPP_DEFINE_BIT(WS_TRACE) \ + ) + +void +WppTraceDebugOut( + _In_z_ const WCHAR *pszFmt, + ... + ); + +void +WppTraceDebugOut( + _In_z_ const CHAR *pszFmt, + ... + ); + +// +// For checked builds, send output to debugger rather than to file +// +#ifdef DBG +#define WPP_DEBUG(x) WppTraceDebugOut x +#endif + +#endif diff --git a/print/XpsRasFilter/ReadMe.md b/print/XpsRasFilter/ReadMe.md new file mode 100644 index 00000000..2dc4fe52 --- /dev/null +++ b/print/XpsRasFilter/ReadMe.md @@ -0,0 +1,38 @@ +XPS Rasterization Filter Service Sample +======================================= + +This sample implements an XPSDrv filter that rasterizes fixed pages in an XPS document. Hardware vendors can modify this sample to build an XPSDrv filter that produces bitmap images for their printers or other display devices. The sample uses the XPS Rasterization Service in Windows 7. The sample does not run in versions of Windows before Windows 7. + +This document describes the contents and use of the XPS Rasterization Service Filter sample included in the Windows 7 WDK. This document will serve as a point-of-reference to gather and draft the information required for the MSDN entries to accompany the sample prior to flowing this text and information into the appropriate MSDN format. + +The XPS Rasterization Service creates rasterizer objects for use by XPSDrv filters. A rasterizer object takes an XPS Object Model (XPS OM) page object and creates a bitmap of a specified region of the page. The sample implements an XPSDrv filter (xpsrasfilter.dll) that can be inserted into the XPS Filter Pipeline. For each fixed page in an XPS document, the sample filter does the following: + +- Uses the XPS rasterization service to create a rasterizer object for the fixed page. +- Partitions the fixed page into several horizontal bands. +- Uses the rasterizer object to render each horizontal band as a bitmap image. + +The Print Filter Pipeline is part of the XPS Print Path [Windows Print Path Overview](print.windows_print_path_overview). Fixed pages are sent as an XPS data stream from the XPS Spooler to the print filter pipeline. The print filter pipeline manager takes the XPS fixed page, calls each filter in the order defined in the pipeline configuration file, and then sends either Fixed Page OM objects or a data stream to each filter as required. The filters process the data and return either Fixed Page OM objects or a data stream back to the print filter pipeline manager. (See MSDN entry for Filter Pipeline Interfaces items IXpsDocumentProvider, IXpsDocumentConsumer, IPrintWriteStream, and IPrintReadStream.) + +As a print filter pipeline service, the XPS Rasterization Service can be loaded into the filter pipeline when the pipeline is initialized by adding a filter service provider tag to the configuration XML file (for example, \). The service is then available to be called by the filters when they are initialized and called by the print filter pipeline manager. + +The XPS Rasterization Service operates as follows: + +- The calling filter initializes an instance of the rasterizer by passing in the XPS OM for the fixed page. +- The calling filter calls the RasterizeRect method of the rasterizer to render a specified rectangle area of the fixed page. +- RasterizeRect writes the WIC (Windows Imaging Component) bitmap data to memory. (The address is specified as a parameter to RasterizeRect.) + +The default parameters in this sample are as follows: + +- Letter-sized physical page (can override in print ticket). +- 0.25-inch margins (creating an 8-inch by 10.5-inch imageable area). +- Scaling is set to FitApplicationBleedSizeToImageableSize. +- •Destination resolution set to 96 dpi (can override in print ticket). + +**Note**   + +To build this sample, you can use Microsoft Visual Studio 2013 (Professional, or Ultimate) and Windows Driver Kit (WDK) 8.1 Update. This sample will not build with Microsoft Visual Studio Express 2013 for Windows Desktop, because the sample uses Active Template Library (ATL). You can get Visual Studio 2013 and WDK 8.1 Update [here](http://go.microsoft.com/fwlink/p/?LInkID=239721). + +You can also build this sample with Visual Studio 2013 (Professional or Ultimate) and [Windows Driver Kit (WDK) 8.1](http://go.microsoft.com/fwlink/p/?LInkID=391348). + +For Windows Driver Kit (WDK) 8 samples, download the [WDK 8 samples pack](%20http://go.microsoft.com/fwlink/?LinkId=317090). The samples in the WDK 8 samples pack will build only with Microsoft Visual Studio Professional 2012 (Professional or Ultimate) and WDK 8. + diff --git a/print/XpsRasFilter/XpsRasFilter.sln b/print/XpsRasFilter/XpsRasFilter.sln new file mode 100644 index 00000000..4adc144d --- /dev/null +++ b/print/XpsRasFilter/XpsRasFilter.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xpsrasfilter", "src\xpsrasfilter.vcxproj", "{41965435-F4B0-495B-B669-2291F27F56B5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {41965435-F4B0-495B-B669-2291F27F56B5}.Debug|Win32.ActiveCfg = Debug|Win32 + {41965435-F4B0-495B-B669-2291F27F56B5}.Debug|Win32.Build.0 = Debug|Win32 + {41965435-F4B0-495B-B669-2291F27F56B5}.Release|Win32.ActiveCfg = Release|Win32 + {41965435-F4B0-495B-B669-2291F27F56B5}.Release|Win32.Build.0 = Release|Win32 + {41965435-F4B0-495B-B669-2291F27F56B5}.Debug|x64.ActiveCfg = Debug|x64 + {41965435-F4B0-495B-B669-2291F27F56B5}.Debug|x64.Build.0 = Debug|x64 + {41965435-F4B0-495B-B669-2291F27F56B5}.Release|x64.ActiveCfg = Release|x64 + {41965435-F4B0-495B-B669-2291F27F56B5}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/print/XpsRasFilter/install/xpsrassmpl-PipelineConfig.xml b/print/XpsRasFilter/install/xpsrassmpl-PipelineConfig.xml new file mode 100644 index 00000000..c391b545 --- /dev/null +++ b/print/XpsRasFilter/install/xpsrassmpl-PipelineConfig.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/print/XpsRasFilter/install/xpsrassmpl.gpd b/print/XpsRasFilter/install/xpsrassmpl.gpd new file mode 100644 index 00000000..0c300802 --- /dev/null +++ b/print/XpsRasFilter/install/xpsrassmpl.gpd @@ -0,0 +1,342 @@ +*% +*% Copyright (c) 2005 Microsoft Corporation +*% +*% All rights reserved. +*% +*% THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +*% ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +*% THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +*% PARTICULAR PURPOSE. +*% +*% File Name: +*% +*% xpsrassmpl.gpd +*% +*% Abstract: +*% +*% Xps Rasterization Service WDK sample driver install file GPD file. +*% + +*GPDFileVersion: "1.0" +*GPDSpecVersion: "1.0" +*GPDFileName: "xpsrassmpl.gpd" +*Include: "msxpsinc.gpd" +*Include: "StdNames.gpd" +*ModelName: "XPSRas WDK Sample Driver" +*MasterUnits: PAIR(1200, 1200) +*ResourceDLL: "unires.dll" +*PrinterType: PAGE +*MaxCopies: 1 + +*%****************************************************************************** +*% PageMediaSize - Paper Size +*%****************************************************************************** +*Feature: PaperSize +{ + *rcNameID: =PAPER_SIZE_DISPLAY + *DefaultOption: LETTER + + *Option: A3 + { + *rcNameID: =RCID_DMPAPER_SYSTEM_NAME + *PageProtectMem: 9667 + *switch: Orientation + { + *case: PORTRAIT + { + *PrintableArea: PAIR(13460, 19440) + *PrintableOrigin: PAIR(284, 200) + *CursorOrigin: PAIR(284, 200) + } + *case: LANDSCAPE_CC270 + { + *PrintableArea: PAIR(13628, 19368) + *PrintableOrigin: PAIR(200, 236) + *CursorOrigin: PAIR(200, 19604) + } + } + } + + *Option: A4 + { + *rcNameID: =RCID_DMPAPER_SYSTEM_NAME + *PageProtectMem: 4249 + *switch: Orientation + { + *case: PORTRAIT + { + *PrintableArea: PAIR(9352, 13628) + *PrintableOrigin: PAIR(284, 200) + *CursorOrigin: PAIR(284, 200) + } + *case: LANDSCAPE_CC270 + { + *PrintableArea: PAIR(9520, 13556) + *PrintableOrigin: PAIR(200, 236) + *CursorOrigin: PAIR(200, 13792) + } + } + } + + *Option: B4 + { + *rcNameID: =RCID_DMPAPER_SYSTEM_NAME + *PageProtectMem: 6391 + *switch: Orientation + { + *case: PORTRAIT + { + *PrintableArea: PAIR(11572, 16796) + *PrintableOrigin: PAIR(284, 200) + *CursorOrigin: PAIR(284, 200) + } + *case: LANDSCAPE_CC270 + { + *PrintableArea: PAIR(11740, 16724) + *PrintableOrigin: PAIR(200, 236) + *CursorOrigin: PAIR(200, 16960) + } + } + } + + *Option: B5 + { + *rcNameID: =RCID_DMPAPER_SYSTEM_NAME + *PageProtectMem: 3198 + *switch: Orientation + { + *case: PORTRAIT + { + *PrintableArea: PAIR(7900, 11140) + *PrintableOrigin: PAIR(352, 300) + *CursorOrigin: PAIR(300, 100) + } + *case: LANDSCAPE_CC270 + { + *PrintableArea: PAIR(7760, 11140) + *PrintableOrigin: PAIR(300, 400) + *CursorOrigin: PAIR(100, 11940) + } + } + } + + *Option: EXECUTIVE + { + *rcNameID: =RCID_DMPAPER_SYSTEM_NAME + *PageProtectMem: 4109 + *switch: Orientation + { + *case: PORTRAIT + { + *PrintableArea: PAIR(8100, 11500) + *PrintableOrigin: PAIR(300, 300) + *CursorOrigin: PAIR(300, 200) + } + *case: LANDSCAPE_CC270 + { + *PrintableArea: PAIR(8300, 11500) + *PrintableOrigin: PAIR(200, 300) + *CursorOrigin: PAIR(200, 12300) + } + } + } + + *Option: ENV_10 + { + *rcNameID: =RCID_DMPAPER_SYSTEM_NAME + *PageProtectMem: 4109 + *switch: Orientation + { + *case: PORTRAIT + { + *PrintableArea: PAIR(4348, 11000) + *PrintableOrigin: PAIR(300, 200) + *CursorOrigin: PAIR(300, 200) + } + *case: LANDSCAPE_CC270 + { + *PrintableArea: PAIR(4548, 10920) + *PrintableOrigin: PAIR(200, 240) + *CursorOrigin: PAIR(200, 11160) + } + } + } + + *Option: LEGAL + { + *rcNameID: =RCID_DMPAPER_SYSTEM_NAME + *PageProtectMem: 1692 + *switch: Orientation + { + *case: PORTRAIT + { + *PrintableArea: PAIR(9000, 15500) + *PrintableOrigin: PAIR(400, 600) + *CursorOrigin: PAIR(180, 300) + } + *case: LANDSCAPE_CC270 + { + *PrintableArea: PAIR(9000, 15500) + *PrintableOrigin: PAIR(400, 900) + *CursorOrigin: PAIR(180, 16500) + } + } + } + + *Option: LETTER + { + *rcNameID: =RCID_DMPAPER_SYSTEM_NAME + *PageProtectMem: 1028 + *switch: Orientation + { + *case: PORTRAIT + { + *PrintableArea: PAIR(9500, 12500) + *PrintableOrigin: PAIR(400, 400) + *CursorOrigin: PAIR(300, 300) + } + *case: LANDSCAPE_CC270 + { + *PrintableArea: PAIR(9500, 12200) + *PrintableOrigin: PAIR(450, 300) + *CursorOrigin: PAIR(200, 12900) + } + } + } + + *Option: ENV_MONARCH + { + *rcNameID: =RCID_DMPAPER_SYSTEM_NAME + *PageProtectMem: 4109 + *switch: Orientation + { + *case: PORTRAIT + { + *PrintableArea: PAIR(4048, 8600) + *PrintableOrigin: PAIR(300, 200) + *CursorOrigin: PAIR(300, 200) + } + *case: LANDSCAPE_CC270 + { + *PrintableArea: PAIR(4248, 8520) + *PrintableOrigin: PAIR(200, 240) + *CursorOrigin: PAIR(200, 8760) + } + } + } + + *Option: TABLOID + { + *rcNameID: =RCID_DMPAPER_SYSTEM_NAME + *PageProtectMem: 4109 + *switch: Orientation + { + *case: PORTRAIT + { + *PrintableArea: PAIR(12000, 19200) + *PrintableOrigin: PAIR(400, 400) + *CursorOrigin: PAIR(400, 400) + } + *case: LANDSCAPE_CC270 + { + *PrintableArea: PAIR(12000, 19200) + *PrintableOrigin: PAIR(400, 600) + *CursorOrigin: PAIR(400, 18000) + } + } + } +} + +*%****************************************************************************** +*% PageOrientation +*%****************************************************************************** +*Feature: Orientation +{ + *rcNameID: =ORIENTATION_DISPLAY + *DefaultOption: PORTRAIT + + *Option: PORTRAIT + { + *rcNameID: =PORTRAIT_DISPLAY + } + + *Option: LANDSCAPE_CC270 + { + *rcNameID: =LANDSCAPE_DISPLAY + } +} + +*%****************************************************************************** +*% Resolution +*%****************************************************************************** +*Feature: Resolution +{ + *rcNameID: =RESOLUTION_DISPLAY + *DefaultOption: DPI600 + + *Option: DPI600 + { + *Name: "600 x 600 " =DOTS_PER_INCH + *DPI: PAIR(600, 600) + *TextDPI: PAIR(600, 600) + *SpotDiameter: 100 + *Command: CmdBeginRaster { *Cmd : "<1B>*v7S<1B>*r1A" } + *Command: CmdEndRaster { *Cmd : "<1B>*rC" } + *Command: CmdSendBlockData { *Cmd : "<1B>*b" %d{NumOfDataBytes}"W" } + } + + *Option: DPI1200 + { + *Name: "1200 x 1200 " =DOTS_PER_INCH + *DPI: PAIR(1200, 1200) + *TextDPI: PAIR(1200, 1200) + *SpotDiameter: 100 + *Command: CmdBeginRaster { *Cmd : "<1B>*v7S<1B>*r1A" } + *Command: CmdEndRaster { *Cmd : "<1B>*rC" } + *Command: CmdSendBlockData { *Cmd : "<1B>*b" %d{NumOfDataBytes}"W" } + } +} + +*%****************************************************************************** +*% Input Bin +*%****************************************************************************** +*Feature: InputBin +{ + *rcNameID: =PAPER_SOURCE_DISPLAY + *DefaultOption: UPPER + *ConcealFromUI?: TRUE + + *Option: UPPER + { + *rcNameID: =UPPER_TRAY_DISPLAY + } +} + + +*%****************************************************************************** +*% Printer Memory +*%****************************************************************************** +*Feature: Memory +{ + *rcNameID: =PRINTER_MEMORY_DISPLAY + *DefaultOption: 32768KB + *Option: 16384KB + { + *Name: "16MB" + *MemoryConfigKB: PAIR(16384, 13950) + } + *Option: 32768KB + { + *Name: "32MB" + *MemoryConfigKB: PAIR(32768, 28350) + } +} + +*%****************************************************************************** +*% Cursor Commands +*%****************************************************************************** +*Command: CmdCR { *Cmd : "<0D>" } +*Command: CmdLF { *Cmd : "<0A>" } +*Command: CmdFF { *Cmd : "<0C>" } + + diff --git a/print/XpsRasFilter/install/xpsrassmpl.inf b/print/XpsRasFilter/install/xpsrassmpl.inf new file mode 100644 index 00000000..0683802a --- /dev/null +++ b/print/XpsRasFilter/install/xpsrassmpl.inf @@ -0,0 +1,87 @@ +; +; Copyright (c) 2005 Microsoft Corporation +; +; All rights reserved. +; +; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +; ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +; THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +; PARTICULAR PURPOSE. +; +; File Name: +; +; xpsrassmpl.inf +; +; Abstract: +; +; Xps Rasterization Service WDK sample driver install file +; +[Version] +Signature="$Windows NT$" +Provider=%MS% +ClassGUID={4D36E979-E325-11CE-BFC1-08002BE10318} +Class=Printer +DriverVer=10/17/2008,6.1.6930.0 + +[Manufacturer] +%Microsoft%=Microsoft,NTx86.6.1,NTia64.6.1,NTamd64.6.1 + + +[Microsoft.NTx86.6.1] +"XPSRas WDK Sample Driver" = INSTALL_FILTER + +[Microsoft.NTia64.6.1] +"XPSRas WDK Sample Driver" = INSTALL_FILTER + +[Microsoft.NTamd64.6.1] +"XPSRas WDK Sample Driver" = INSTALL_FILTER + +[INSTALL_FILTER] +CopyFiles=XPSRasSample +DriverFile=mxdwdrv.dll +ConfigFile=UniDrvUI.dll +HelpFile=UniDrv.HLP +DataFile=xpsrassmpl.GPD +CoreDriverSections="{D20EA372-DD35-4950-9ED8-A6335AFE79F0},UNIDRV.OEM", "{D20EA372-DD35-4950-9ED8-A6335AFE79F5},XPSDRV.OEM,XPSGPD.OEM" + +[PrinterPackageInstallation.x86] +PackageAware=TRUE +CoreDriverDependencies={D20EA372-DD35-4950-9ED8-A6335AFE79F0}, {D20EA372-DD35-4950-9ED8-A6335AFE79F5} + +[PrinterPackageInstallation.amd64] +PackageAware=TRUE +CoreDriverDependencies={D20EA372-DD35-4950-9ED8-A6335AFE79F0}, {D20EA372-DD35-4950-9ED8-A6335AFE79F5} + +[PrinterPackageInstallation.ia64] +PackageAware=TRUE +CoreDriverDependencies={D20EA372-DD35-4950-9ED8-A6335AFE79F0}, {D20EA372-DD35-4950-9ED8-A6335AFE79F5} + +[DestinationDirs] +DefaultDestDir=66000 + +[XPSRasSample] +xpsrassmpl.gpd +xpsrassmpl-pipelineconfig.xml +xpsrasfilter.dll + +[SourceDisksNames.x86] +1 = %Location%,, +2 = %Location%,,,x86 + +[SourceDisksNames.ia64] +1 = %Location%,, +2 = %Location%,,,ia64 + +[SourceDisksNames.amd64] +1 = %Location%,, +2 = %Location%,,,amd64 + +[SourceDisksFiles] +xpsrassmpl.gpd = 1 +xpsrassmpl-pipelineconfig.xml = 1 +xpsrasfilter.dll = 2 + +[Strings] +Location="XPSRas WDK Sample Driver" +MS="Microsoft" +Microsoft="Microsoft" \ No newline at end of file diff --git a/print/XpsRasFilter/src/BitmapHandler.cpp b/print/XpsRasFilter/src/BitmapHandler.cpp new file mode 100644 index 00000000..d887ddab --- /dev/null +++ b/print/XpsRasFilter/src/BitmapHandler.cpp @@ -0,0 +1,361 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// BitmapHandler.cpp +// +// Abstract: +// +// Abstract class that encapsulates the processing done to each +// individual band bitmap, as well as the concrete implemention +// that streams the bands as Tiffs to the output stream. +// + +#include "precomp.h" +#include "WppTrace.h" +#include "Exception.h" +#include "filtertypes.h" +#include "UnknownBase.h" +#include "xpsrasfilter.h" +#include "OMConvertor.h" +#include "rasinterface.h" +#include "BitmapHandler.h" + +#include "BitmapHandler.tmh" + +namespace xpsrasfilter +{ + +// +//Routine Name: +// +// TiffStreamBitmapHandler::CreateTiffStreamBitmapHandler +// +//Routine Description: +// +// Static factory method that creates an instance of +// TiffStreamBitmapHandler. +// +//Arguments: +// +// pStream - Filter output stream (IPrintWriteStream) +// +//Return Value: +// +// TiffStreamBitmapHandler_t (smart ptr) +// The new TiffStreamBitmapHandler. +// +TiffStreamBitmapHandler_t +TiffStreamBitmapHandler::CreateTiffStreamBitmapHandler( + const IPrintWriteStream_t &pStream + ) +{ + IWICImagingFactory_t pWICFactory; + + // + // Create an instance of a WIC Imaging Factory + // + THROW_ON_FAILED_HRESULT( + ::CoCreateInstance( + CLSID_WICImagingFactory, + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory), + reinterpret_cast(&pWICFactory) + ) + ); + + // + // Construct the TiffStreamBitmapHandler and return it + // + TiffStreamBitmapHandler_t toReturn( + new TiffStreamBitmapHandler( + pWICFactory, + pStream + ) + ); + + return toReturn; +} + +// +//Routine Name: +// +// TiffStreamBitmapHandler::TiffStreamBitmapHandler +// +//Routine Description: +// +// Construct the bitmap handler with the WIC factory +// and filter output stream. +// +//Arguments: +// +// pWICFactory - Windows Imaging Components object factory +// pStream - Output stream +// pHG - Encoder cache HGLOBAL +// +TiffStreamBitmapHandler::TiffStreamBitmapHandler( + const IWICImagingFactory_t &pWICFactory, + const IPrintWriteStream_t &pStream + ) : m_pWICFactory(pWICFactory), + m_pWriter(pStream), + m_nextTiffStart(0), + m_numTiffs(0), + m_tiffStarts(0) +{ +} + +// +//Routine Name: +// +// TiffStreamBitmapHandler::ProcessBitmap +// +//Routine Description: +// +// Encode the bitmap as a TIFF and stream out of the filter. +// +//Arguments: +// +// bitmap - bitmap of a single band, to stream +// +void +TiffStreamBitmapHandler::ProcessBitmap( + const IWICBitmap_t &bitmap + ) +{ + + // + // Create an empty HGLOBAL to hold the encode cache + // + SafeHGlobal_t pHG( + new SafeHGlobal(GMEM_SHARE | GMEM_MOVEABLE, 0) + ); + + // + // Create a stream to the encode buffer so that WIC can + // encode the TIFF in-memory + // + IStream_t pIStream; + + THROW_ON_FAILED_HRESULT( + ::CreateStreamOnHGlobal( + *pHG, + FALSE, // Do NOT Free the HGLOBAL on Release of the stream + &pIStream + ) + ); + + // + // Create a WIC TIFF Encoder on the stream + // + IWICBitmapEncoder_t pWICEncoder; + THROW_ON_FAILED_HRESULT( + m_pWICFactory->CreateEncoder(GUID_ContainerFormatTiff, NULL, &pWICEncoder) + ); + THROW_ON_FAILED_HRESULT( + pWICEncoder->Initialize(pIStream, WICBitmapEncoderNoCache) + ); + + // + // Create a new frame for the band and configure it + // + IWICBitmapFrameEncode_t pWICFrame; + IPropertyBag2_t pFramePropertyBag; + + THROW_ON_FAILED_HRESULT( + pWICEncoder->CreateNewFrame(&pWICFrame, &pFramePropertyBag) + ); + + { + // + // Write the compression method to the frame's property bag + // + PROPBAG2 option = { 0 }; + option.pstrName = L"TiffCompressionMethod"; + + VARIANT varValue; + VariantInit(&varValue); + varValue.vt = VT_UI1; + varValue.bVal = WICTiffCompressionLZW; + + THROW_ON_FAILED_HRESULT( + pFramePropertyBag->Write( + 1, // number of properties being set + &option, + &varValue + ) + ); + } + + THROW_ON_FAILED_HRESULT( + pWICFrame->Initialize(pFramePropertyBag) + ); + + // + // Set the frame's size + // + UINT bitmapWidth, bitmapHeight; + THROW_ON_FAILED_HRESULT( + bitmap->GetSize(&bitmapWidth, &bitmapHeight) + ); + THROW_ON_FAILED_HRESULT( + pWICFrame->SetSize(bitmapWidth, bitmapHeight) + ); + + // + // Set the frame's resolution + // + DOUBLE xDPI, yDPI; + THROW_ON_FAILED_HRESULT( + bitmap->GetResolution(&xDPI, &yDPI) + ); + THROW_ON_FAILED_HRESULT( + pWICFrame->SetResolution(xDPI, yDPI) + ); + + // + // Set the frame's pixel format + // + WICPixelFormatGUID format; + THROW_ON_FAILED_HRESULT( + bitmap->GetPixelFormat(&format) + ); + THROW_ON_FAILED_HRESULT( + pWICFrame->SetPixelFormat(&format) + ); + + // + // Write the bitmap data to the frame + // + WICRect rect = {0, 0, 0, 0}; + rect.Width = bitmapWidth; + rect.Height = bitmapHeight; + THROW_ON_FAILED_HRESULT( + pWICFrame->WriteSource(bitmap, &rect) + ); + + // + // Commit the frame and encoder + // + THROW_ON_FAILED_HRESULT( + pWICFrame->Commit() + ); + THROW_ON_FAILED_HRESULT( + pWICEncoder->Commit() + ); + + // + // Get the size of the TIFF from the stream position + // + ULARGE_INTEGER tiffSize; + LARGE_INTEGER zero; + zero.QuadPart = 0; + + THROW_ON_FAILED_HRESULT( + pIStream->Seek(zero, SEEK_CUR, &tiffSize) + ); + + ULONG cb; + + THROW_ON_FAILED_HRESULT( + ::ULongLongToULong(tiffSize.QuadPart, &cb) + ); + + // + // Update the list of Tiff locations so that it can be written to + // the end of the Tiff stream. + // + m_tiffStarts.push_back(m_nextTiffStart); + m_nextTiffStart += tiffSize.QuadPart; + m_numTiffs++; + + { + // + // Get a pointer to the HGLOBAL memory + // + HGlobalLock_t lock = pHG->Lock(); + BYTE *pCache = lock->GetAddress(); + + // + // Write the encoded Tiff to the output stream + // + ULONG written; + + THROW_ON_FAILED_HRESULT( + m_pWriter->WriteBytes(pCache, cb, &written) + ); + } +} + +// +//Routine Name: +// +// TiffStreamBitmapHandler::WriteFooter +// +//Routine Description: +// +// Write the footer to the stream, making it easier +// to decode the individual TIFFs later. The footer +// looks like this: +// +// |<--8 bytes--->| +// +// +--------------+ +// | Tiff 1 start | +// +--------------+ +// | Tiff 2 start | +// +--------------+ +// | ... | +// +--------------+ +// | Tiff N start | +// +--------------+ +// | N | +// +--------------+ +// +void +TiffStreamBitmapHandler::WriteFooter() +{ + ULONG written; + + // + // Write the vector of Tiff starts to the stream, if any Tiffs + // have been written to the stream. + // + if (m_numTiffs > 0) + { + ULONG toWrite; + + THROW_ON_FAILED_HRESULT( + SizeTToULong( + sizeof(ULONGLONG) * m_tiffStarts.size(), + &toWrite + ) + ); + + THROW_ON_FAILED_HRESULT( + m_pWriter->WriteBytes( + reinterpret_cast(&m_tiffStarts[0]), + toWrite, + &written + ) + ); + } + + // + // Write the number of Tiffs to the stream + // + THROW_ON_FAILED_HRESULT( + m_pWriter->WriteBytes( + reinterpret_cast(&m_numTiffs), + sizeof(m_numTiffs), + &written + ) + ); +} + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/BitmapHandler.h b/print/XpsRasFilter/src/BitmapHandler.h new file mode 100644 index 00000000..3f78dc30 --- /dev/null +++ b/print/XpsRasFilter/src/BitmapHandler.h @@ -0,0 +1,64 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// BitmapHandler.h +// +// Abstract: +// +// Abstract class that encapsulates the processing done to each +// individual band bitmap, as well as the concrete implemention +// that streams the bands as Tiffs to the output stream. +// + +#pragma once + +namespace xpsrasfilter +{ + +class TiffStreamBitmapHandler +{ +public: + + static + TiffStreamBitmapHandler_t + CreateTiffStreamBitmapHandler( + const IPrintWriteStream_t &pStream + ); + + void + ProcessBitmap( + const IWICBitmap_t &bitmap + ); + + void + WriteFooter(); + +private: + IWICImagingFactory_t m_pWICFactory; + IPrintWriteStream_t m_pWriter; // output stream + + // + // Members to keep track of where each Tiff + // starts in the output stream + // + ULONGLONG m_nextTiffStart; + ULONGLONG m_numTiffs; + std::vector m_tiffStarts; + + // + // Constructor is private; use CreateTiffStreamBitmapHandler + // to create instances + // + TiffStreamBitmapHandler( + const IWICImagingFactory_t &pWICFactory, + const IPrintWriteStream_t &pStream + ); +}; + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/Exception.cpp b/print/XpsRasFilter/src/Exception.cpp new file mode 100644 index 00000000..141c1ac3 --- /dev/null +++ b/print/XpsRasFilter/src/Exception.cpp @@ -0,0 +1,43 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// Exception.cpp +// +// Abstract: +// +// Exception routine definitions. +// + +#include "precomp.h" +#include "WppTrace.h" +#include "Exception.h" + +#include "Exception.tmh" + +namespace xpsrasfilter +{ + +void ThrowHRException( + HRESULT hr, + char const *fileName, + int lineNum + ) +{ + DoTraceMessage( + XPSRASFILTER_TRACE_ERROR, + L"Throwing HRESULT Exception from %s:%d (HRESULT=%!HRESULT!)", + fileName, + lineNum, + hr + ); + + throw hr_error(hr); +} + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/Exception.h b/print/XpsRasFilter/src/Exception.h new file mode 100644 index 00000000..887bfbc4 --- /dev/null +++ b/print/XpsRasFilter/src/Exception.h @@ -0,0 +1,86 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// Exception.h +// +// Abstract: +// +// Exception macro and class declarations. +// + +#pragma once + +// +// Macro to convert HRESULT into an exception throw +// +#ifndef THROW_ON_FAILED_HRESULT +#define THROW_ON_FAILED_HRESULT(func_) \ +{ \ + HRESULT hr_ = func_; \ + if (FAILED(hr_)) { xpsrasfilter::ThrowHRException(hr_, __FILE__, __LINE__); } \ +} +#endif // THROW_ON_FAILED_HRESULT + +#ifndef THROW_LAST_ERROR +#define THROW_LAST_ERROR() \ +{ \ + HRESULT errhr_ = HRESULT_FROM_WIN32(::GetLastError()); \ + THROW_ON_FAILED_HRESULT(errhr_); \ +} +#endif // THROW_LAST_ERROR + +// +// Macro to catch various exceptions, including +// HRESULT-turned-exceptions. +// +// Because we have defined USE_NATIVE_EH=1, the +// catch(...) block will not catch structural +// exceptions. +// +#ifndef CATCH_VARIOUS +#define CATCH_VARIOUS(hr_) \ + catch(std::bad_alloc const& ) \ + { \ + hr_ = E_OUTOFMEMORY; \ + } \ + catch(xpsrasfilter::hr_error const& e) \ + { \ + hr_ = e.hr; \ + } \ + catch(std::exception const& ) \ + { \ + hr_ = E_FAIL; \ + } \ + catch(...) \ + { \ + hr_ = E_UNEXPECTED; \ + } +#endif // CATCH_VARIOUS + +namespace xpsrasfilter +{ + +// +// HRESULT exception +// +struct hr_error +{ + HRESULT hr; + + hr_error(HRESULT hr_in) : hr(hr_in) + { } +}; + +void ThrowHRException( + HRESULT hr, + char const *fileName, + int lineNum + ); + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/OMConvertor.cpp b/print/XpsRasFilter/src/OMConvertor.cpp new file mode 100644 index 00000000..2cd478c2 --- /dev/null +++ b/print/XpsRasFilter/src/OMConvertor.cpp @@ -0,0 +1,774 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// omconvertor.cpp +// +// Abstract: +// +// Object model conversion routines. This class provides routines +// to convert from filter pipeline objects into Xps Object Model +// objects. +// + +#include "precomp.h" +#include "WppTrace.h" +#include "Exception.h" +#include "filtertypes.h" +#include "UnknownBase.h" +#include "OMConvertor.h" + +#include "OMConvertor.tmh" + +namespace xpsrasfilter +{ + +// +//Routine Name: +// +// CreateXpsOMPageFromIFixedPage +// +//Routine Description: +// +// This is the main method called by the filter. It +// proceeds to call the remaining Create* methods +// to convert from the print pipeline Object Model +// to the Xps Object Model. +// +// Takes an IFixedPage (print pipeline Object Model) +// and converts it to an IXpsOMPage (Xps Object Model). +// +//Arguments: +// +// pPageIn - IFixedPage to convert +// pOMFactory - Xps Object Model Object Factory +// pOpcFactory - Opc Object Factory +// +//Return Value: +// +// IXpsOMPage_t (smart pointer) +// Result IXpsOMPage +// +IXpsOMPage_t +CreateXpsOMPageFromIFixedPage( + const IFixedPage_t &pPageIn, + const IXpsOMObjectFactory_t &pOMFactory, + const IOpcFactory_t &pOpcFactory + ) +{ + // + // Get additional page parameters (uri and stream) + // + IOpcPartUri_t pPartUri; + IStream_t pPartStream; + + pPartStream = GetStreamFromPart( + static_cast(pPageIn) + ); + pPartUri = CreateOpcPartUriFromPart( + static_cast(pPageIn), + pOpcFactory + ); + + // + // Call Xps Object Model to create the page resource + // + IXpsOMPage_t pPageOut; + + THROW_ON_FAILED_HRESULT( + pOMFactory->CreatePageFromStream( + pPartStream, + pPartUri, + CollectPageResources(pPageIn, pOMFactory, pOpcFactory), + FALSE, // Do not reuse objects + &pPageOut + ) + ); + + return pPageOut; +} + +// +//Routine Name: +// +// CreateImageFromIPartImage +// +//Routine Description: +// +// Takes an IPartImage (print pipeline Object Model) +// and converts it to an IXpsOMImageResource (Xps Object Model). +// +//Arguments: +// +// pImageIn - IPartImage to convert +// pOMFactory - Xps Object Model Object Factory +// pOpcFactory - Opc Object Factory +// +//Return Value: +// +// IXpsOMImageResource_t (smart pointer) +// Result IXpsOMImageResource +// +IXpsOMImageResource_t +CreateImageFromIPartImage( + const IPartImage_t &pImageIn, + const IXpsOMObjectFactory_t &pOMFactory, + const IOpcFactory_t &pOpcFactory + ) +{ + // + // Get IPartBase parameters (stream and uri) + // + IStream_t pPartStream; + IOpcPartUri_t pPartUri; + + pPartStream = GetStreamFromPart( + static_cast(pImageIn) + ); + pPartUri = CreateOpcPartUriFromPart( + static_cast(pImageIn), + pOpcFactory + ); + + // + // Get the image type and convert it to the corresponding enum + // + BSTR_t strContentType; + XPS_IMAGE_TYPE type = XPS_IMAGE_TYPE_WDP; + + THROW_ON_FAILED_HRESULT( + pImageIn->GetImageProperties(&strContentType) + ); + + if (0 == _wcsicmp(strContentType, L"image/jpeg")) + { + type = XPS_IMAGE_TYPE_JPEG; + } + else if (0 == _wcsicmp(strContentType, L"image/png")) + { + type = XPS_IMAGE_TYPE_PNG; + } + else if (0 == _wcsicmp(strContentType, L"image/tiff")) + { + type = XPS_IMAGE_TYPE_TIFF; + } + else if (0 == _wcsicmp(strContentType, L"image/vnd.ms-photo")) + { + type = XPS_IMAGE_TYPE_WDP; + } + else + { + // + // unknown content type + // + THROW_ON_FAILED_HRESULT(E_INVALIDARG); + } + + // + // Call Xps Object Model to create the image resource + // + IXpsOMImageResource_t pImageOut; + + THROW_ON_FAILED_HRESULT( + pOMFactory->CreateImageResource( + pPartStream, + type, + pPartUri, + &pImageOut + ) + ); + + return pImageOut; +} + +// +//Routine Name: +// +// CreateProfileFromIPartColorProfile +// +//Routine Description: +// +// Takes an IPartColorProfile (print pipeline Object Model) +// and converts it to an IXpsOMColorProfileResource (Xps Object Model). +// +//Arguments: +// +// pProfileIn - IPartColorProfile to convert +// pOMFactory - Xps Object Model Object Factory +// pOpcFactory - Opc Object Factory +// +//Return Value: +// +// IXpsOMColorProfileResource_t (smart pointer) +// Result IXpsOMColorProfileResource +// +IXpsOMColorProfileResource_t +CreateProfileFromIPartColorProfile( + const IPartColorProfile_t &pProfileIn, + const IXpsOMObjectFactory_t &pOMFactory, + const IOpcFactory_t &pOpcFactory + ) +{ + // + // Get IPartBase parameters (stream and uri) + // + IStream_t pPartStream; + IOpcPartUri_t pPartUri; + + pPartStream = GetStreamFromPart( + static_cast(pProfileIn) + ); + pPartUri = CreateOpcPartUriFromPart( + static_cast(pProfileIn), + pOpcFactory + ); + + // + // Call Xps Object Model to create the color profile resource + // + IXpsOMColorProfileResource_t pProfileOut; + + THROW_ON_FAILED_HRESULT( + pOMFactory->CreateColorProfileResource( + pPartStream, + pPartUri, + &pProfileOut + ) + ); + + return pProfileOut; +} + +// +//Routine Name: +// +// CreateDictionaryFromIPartResourceDictionary +// +//Routine Description: +// +// Takes an IPartResourceDictionary (print pipeline Object Model) +// and converts it to an IXpsOMRemoteDictionaryResource (Xps Object Model). +// +//Arguments: +// +// pDictionaryIn - IPartResourceDictionary to convert +// pOMFactory - Xps Object Model Object Factory +// pOpcFactory - Opc Object Factory +// pResources - The resources of the fixed page +// +//Return Value: +// +// IXpsOMRemoteDictionaryResource_t (smart pointer) +// Result IXpsOMRemoteDictionaryResource +// +IXpsOMRemoteDictionaryResource_t +CreateDictionaryFromIPartResourceDictionary( + const IPartResourceDictionary_t &pDictionaryIn, + const IXpsOMObjectFactory_t &pOMFactory, + const IOpcFactory_t &pOpcFactory, + const IXpsOMPartResources_t &pResources + ) +{ + // + // Get IPartBase parameters (stream and uri) + // + IStream_t pPartStream; + IOpcPartUri_t pPartUri; + + pPartStream = GetStreamFromPart( + static_cast(pDictionaryIn) + ); + pPartUri = CreateOpcPartUriFromPart( + static_cast(pDictionaryIn), + pOpcFactory + ); + + // + // Call Xps Object Model to create the remote dictionary resource + // + IXpsOMRemoteDictionaryResource_t pDictionaryOut; + + THROW_ON_FAILED_HRESULT( + pOMFactory->CreateRemoteDictionaryResourceFromStream( + pPartStream, + pPartUri, + pResources, + &pDictionaryOut) + ); + + return pDictionaryOut; +} + +// +//Routine Name: +// +// CreateFontFromIPartFont +// +//Routine Description: +// +// Takes an IPartFont (print pipeline Object Model) +// and converts it to an IXpsOMFontResource (Xps Object Model). +// +//Arguments: +// +// pFontIn - IPartFont to convert +// pFactory - Xps Object Model Object Factory +// pOpcFactory - Opc Object Factory +// +//Return Value: +// +// IXpsOMFontResource_t (smart pointer) +// Result IXpsOMFontResource +// +IXpsOMFontResource_t +CreateFontFromIPartFont( + const IPartFont_t &pFontIn, + const IXpsOMObjectFactory_t &pOMFactory, + const IOpcFactory_t &pOpcFactory + ) +{ + // + // Get IPartBase parameters (stream and uri) + // + IStream_t pPartStream; + IOpcPartUri_t pPartUri; + + pPartStream = GetStreamFromPart( + static_cast(pFontIn) + ); + pPartUri = CreateOpcPartUriFromPart( + static_cast(pFontIn), + pOpcFactory + ); + + // + // Get the font restriction + // + EXpsFontRestriction eFontRestriction = Xps_Restricted_Font_Installable; + + { + IPartFont2_t pFont2In; + + if (SUCCEEDED(pFontIn->QueryInterface(__uuidof(IPartFont2), reinterpret_cast(&pFont2In)))) + { + pFont2In->GetFontRestriction(&eFontRestriction); + } + } + + // + // Get the font obfuscation + // + EXpsFontOptions eFontOptions; + + { + BSTR_t contentType; + + THROW_ON_FAILED_HRESULT( + pFontIn->GetFontProperties(&contentType, &eFontOptions) + ); + } + + // + // It is necessary to combine the obfuscation and restriction + // attributes from the print pipeline into the one parameter that + // the Xps Object Model consumes. + // + + XPS_FONT_EMBEDDING omEmbedding; + + if (eFontOptions == Font_Normal) + { + omEmbedding = XPS_FONT_EMBEDDING_NORMAL; + } + else if (eFontOptions == Font_Obfusticate && + (eFontRestriction & + (Xps_Restricted_Font_PreviewPrint | + Xps_Restricted_Font_NoEmbedding))) + { + // + // If the font is obfuscated, and either the PreviewPrint or + // NoEmbedding restriction flags are set, then create a + // Restricted font + // + omEmbedding = XPS_FONT_EMBEDDING_RESTRICTED; + } + else + { + omEmbedding = XPS_FONT_EMBEDDING_OBFUSCATED; + } + + // + // Call Xps Object Model to create the font resource + // + IXpsOMFontResource_t pFontOut; + + THROW_ON_FAILED_HRESULT( + pOMFactory->CreateFontResource( + pPartStream, + omEmbedding, + pPartUri, + FALSE, // fonts received from the pipeline are already de-obfuscated + &pFontOut + ) + ); + + return pFontOut; +} + +// +//Routine Name: +// +// CollectPageResources +// +//Routine Description: +// +// Iterates over all of the resources related to +// a fixed page and adds them to a resource +// collection. +// +//Arguments: +// +// pPage - The page to query for resources +// pOMFactory - Xps Object Model Object Factory +// pOpcFactory - Opc Object Factory +// +//Return Value: +// +// IXpsOMPartResources_t (smart pointer) +// The resource collection of all of the resources of the page +// +IXpsOMPartResources_t +CollectPageResources( + const IFixedPage_t &pPage, + const IXpsOMObjectFactory_t &pOMFactory, + const IOpcFactory_t &pOpcFactory + ) +{ + IXpsOMPartResources_t pResources; + + IXpsOMFontResourceCollection_t pFonts; + IXpsOMImageResourceCollection_t pImages; + IXpsOMColorProfileResourceCollection_t pProfiles; + IXpsOMRemoteDictionaryResourceCollection_t pDictionaries; + + // + // collection of resource dictionaries saved for later processing. + // + ResourceDictionaryList_t dictionaryList; + + // + // Create the resource collection and get all of the + // resource-specific sub-collections. + // + THROW_ON_FAILED_HRESULT( + pOMFactory->CreatePartResources(&pResources) + ); + THROW_ON_FAILED_HRESULT( + pResources->GetFontResources(&pFonts) + ); + THROW_ON_FAILED_HRESULT( + pResources->GetImageResources(&pImages) + ); + THROW_ON_FAILED_HRESULT( + pResources->GetColorProfileResources(&pProfiles) + ); + THROW_ON_FAILED_HRESULT( + pResources->GetRemoteDictionaryResources(&pDictionaries) + ); + // + // Get the XpsPartIterator and iterate through all of the parts + // related to this fixed page. + // + IXpsPartIterator_t itPart; + + THROW_ON_FAILED_HRESULT( + pPage->GetXpsPartIterator(&itPart) + ); + + for (; !itPart->IsDone(); itPart->Next()) + { + BSTR_t uri; + IUnknown_t pUnkPart; + + THROW_ON_FAILED_HRESULT( + itPart->Current(&uri, &pUnkPart) + ); + + IPartFont_t pFontPart; + IPartImage_t pImagePart; + IPartColorProfile_t pProfilePart; + IPartResourceDictionary_t pDictionaryPart; + + if (SUCCEEDED(pUnkPart.QueryInterface(&pFontPart))) + { + // + // Convert the font part to Xps Object Model and add it to the + // font resource collection + // + THROW_ON_FAILED_HRESULT( + pFonts->Append( + CreateFontFromIPartFont( + pFontPart, + pOMFactory, + pOpcFactory + ) + ) + ); + } + else if (SUCCEEDED(pUnkPart.QueryInterface(&pImagePart))) + { + // + // Convert the image part to Xps Object Model and add it to the + // image resource collection + // + THROW_ON_FAILED_HRESULT( + pImages->Append( + CreateImageFromIPartImage( + pImagePart, + pOMFactory, + pOpcFactory + ) + ) + ); + } + else if (SUCCEEDED(pUnkPart.QueryInterface(&pProfilePart))) + { + // + // Convert the color profile part to Xps Object Model and add it + // to the color profile resource collection + // + THROW_ON_FAILED_HRESULT( + pProfiles->Append( + CreateProfileFromIPartColorProfile( + pProfilePart, + pOMFactory, + pOpcFactory + ) + ) + ); + } + else if (SUCCEEDED(pUnkPart.QueryInterface(&pDictionaryPart))) + { + // + // In order to process the remote resource dictionary, all of + // its linked resources must be present in pResources. To ensure + // this, we delay the conversion of the remote resource + // dictionaries until all of the other resources have been converted. + // + dictionaryList.push_back(pDictionaryPart); + } + else + { + // + // Any other page resources are ignored + // + } + } + + for (ResourceDictionaryList_t::const_iterator it = dictionaryList.begin(); + it != dictionaryList.end(); + ++it) + { + // + // Convert the remote dictionary to Xps Object Model and add it + // to the remote dictionary collection + // + THROW_ON_FAILED_HRESULT( + pDictionaries->Append( + CreateDictionaryFromIPartResourceDictionary( + *it, + pOMFactory, + pOpcFactory, + pResources + ) + ) + ); + } + + return pResources; +} + +// +//Routine Name: +// +// GetStreamFromPart +// +//Routine Description: +// +// Gets the IStream from this part. +// +//Arguments: +// +// pPart - An Xps Part +// +//Return Value: +// +// IStream_t (smart pointer) +// The stream of the part's content +// +IStream_t +GetStreamFromPart( + const IPartBase_t &pPart + ) +{ + // + // Get the IPrintReadStream for the part from the pipeline Object Model + // + IPrintReadStream_t pStream; + THROW_ON_FAILED_HRESULT( + pPart->GetStream(&pStream) + ); + + return CreateIStreamFromIPrintReadStream(pStream); +} + +// +//Routine Name: +// +// CreateIStreamFromIPrintReadStream +// +//Routine Description: +// +// Creates an IStream from an IPrintReadStream. +// +//Arguments: +// +// pReadStream - A Print Pipeline IPrintReadStream +// +//Return Value: +// +// IStream_t (smart pointer) +// A stream with the same content as the argument stream. +// +IStream_t +CreateIStreamFromIPrintReadStream( + const IPrintReadStream_t &pReadStream + ) +{ + // + // Get the size of the stream + // + ULONGLONG tmpPos; + size_t partSize; + + THROW_ON_FAILED_HRESULT( + pReadStream->Seek(0, SEEK_END, &tmpPos) + ); + + // + // GlobalAlloc can only allocate size_t bytes, so + // throw if the part is larger than that + // + THROW_ON_FAILED_HRESULT( + ULongLongToSizeT(tmpPos, &partSize) + ); + + THROW_ON_FAILED_HRESULT( + pReadStream->Seek(0, SEEK_SET, &tmpPos) + ); + + // + // Allocate an HGLOBAL for the part cache + // + SafeHGlobal_t pHBuf( + new SafeHGlobal(GMEM_FIXED, partSize) + ); + + // + // Read the part into the cache + // + { + // + // Lock the HGLOBAL and get the address of the buffer + // from the RAII lock object + // + HGlobalLock_t lock = pHBuf->Lock(); + BYTE *pBuffer = lock->GetAddress(); + + // + // Allow the number of bytes to read to be clipped to max ULONG + // and then spin on fEOF until the stream is exhausted + // + ULONG numToRead; + + if (FAILED(SizeTToULong(partSize, &numToRead))) + { + numToRead = MAXUINT; + } + + BOOL fEOF; + ULONG numRead; + size_t pos = 0; + + // + // Iterate until all bytes from the stream + // have been read into the buffer + // + do + { + THROW_ON_FAILED_HRESULT( + pReadStream->ReadBytes(pBuffer + pos, numToRead, &numRead, &fEOF) + ); + + pos += numRead; + } while (!fEOF && numRead); + } + + // + // Create an IStream from the part cache + // + IStream_t pIStream = pHBuf->ConvertToIStream(); + + LARGE_INTEGER zero = {0}; + THROW_ON_FAILED_HRESULT( + pIStream->Seek(zero, SEEK_SET, NULL) + ); + + return pIStream; +} + +// +//Routine Name: +// +// CreateOpcPartUriFromPart +// +//Routine Description: +// +// Gets the Opc Uri from the Xps Part. +// +//Arguments: +// +// pPart - An Xps Part +// pFactory - Opc Factory +// +//Return Value: +// +// IOpcPartUri_t (smart pointer) +// The Uri of the part +// +IOpcPartUri_t +CreateOpcPartUriFromPart( + const IPartBase_t &pPart, + const IOpcFactory_t &pFactory + ) +{ + BSTR_t strPartUri; + + THROW_ON_FAILED_HRESULT( + pPart->GetUri(&strPartUri) + ); + + IOpcPartUri_t pPartUri; + THROW_ON_FAILED_HRESULT( + pFactory->CreatePartUri(strPartUri, &pPartUri) + ); + return pPartUri; +} + +} // namespace xpsrasfilter + diff --git a/print/XpsRasFilter/src/OMConvertor.h b/print/XpsRasFilter/src/OMConvertor.h new file mode 100644 index 00000000..7257da3b --- /dev/null +++ b/print/XpsRasFilter/src/OMConvertor.h @@ -0,0 +1,90 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// omconvertor.h +// +// Abstract: +// +// Object model conversion routines. +// + +#pragma once + +namespace xpsrasfilter +{ + +// +// Top-level Create Page routine +// +IXpsOMPage_t +CreateXpsOMPageFromIFixedPage( + const IFixedPage_t &pPageIn, + const IXpsOMObjectFactory_t &pFactory, + const IOpcFactory_t &pOpcFactory + ); + +// +// Individual Part conversion routines +// +IXpsOMImageResource_t +CreateImageFromIPartImage( + const IPartImage_t &pImageIn, + const IXpsOMObjectFactory_t &pFactory, + const IOpcFactory_t &pOpcFactory + ); + +IXpsOMColorProfileResource_t +CreateProfileFromIPartColorProfile( + const IPartColorProfile_t &pProfileIn, + const IXpsOMObjectFactory_t &pFactory, + const IOpcFactory_t &pOpcFactory + ); + +IXpsOMRemoteDictionaryResource_t +CreateDictionaryFromIPartResourceDictionary( + const IPartResourceDictionary_t &pDictionaryIn, + const IXpsOMObjectFactory_t &pFactory, + const IOpcFactory_t &pOpcFactory, + const IXpsOMPartResources_t &pResources + ); + +IXpsOMFontResource_t +CreateFontFromIPartFont( + const IPartFont_t &pFontIn, + const IXpsOMObjectFactory_t &pFactory, + const IOpcFactory_t &pOpcFactory + ); + +// +// Utility Routines +// +IStream_t +GetStreamFromPart( + const IPartBase_t &pPart + ); + +IOpcPartUri_t +CreateOpcPartUriFromPart( + const IPartBase_t &pPart, + const IOpcFactory_t &pOpcFactory + ); + +IXpsOMPartResources_t +CollectPageResources( + const IFixedPage_t &pPage, + const IXpsOMObjectFactory_t &pFactory, + const IOpcFactory_t &pOpcFactory + ); + +IStream_t +CreateIStreamFromIPrintReadStream( + const IPrintReadStream_t &pReadStream + ); + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/PThandler.cpp b/print/XpsRasFilter/src/PThandler.cpp new file mode 100644 index 00000000..6b158032 --- /dev/null +++ b/print/XpsRasFilter/src/PThandler.cpp @@ -0,0 +1,781 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// PThandler.cpp +// +// Abstract: +// +// Print Ticket Handler class definition. +// + +#include "precomp.h" +#include "WppTrace.h" +#include "Exception.h" +#include "filtertypes.h" +#include "UnknownBase.h" +#include "OMConvertor.h" +#include "rasinterface.h" +#include "xpsrasfilter.h" +#include "PTHandler.h" + +#include "PThandler.tmh" + +namespace xpsrasfilter +{ + +// +// This is an arbitrary page margin that is used to simulate +// an imageable area for scaling calculations. +// +const FLOAT g_pageMargin = 0.25f; + +// +//Routine Name: +// +// PrintTicketHandler::CreatePrintTicketHandler +// +//Routine Description: +// +// Static factory method that creates an instance of +// PrintTicketHandler. +// +//Arguments: +// +// pPropertyBag - Property Bag +// +//Return Value: +// +// PrintTicketHandler_t (smart ptr) +// The new PrintTicketHandler. +// +PrintTicketHandler_t +PrintTicketHandler::CreatePrintTicketHandler( + const IPrintPipelinePropertyBag_t &pPropertyBag + ) +{ + // + // Create MSXML DOM document + // + IXMLDOMDocument2_t pDOMDoc; + + THROW_ON_FAILED_HRESULT( + ::CoCreateInstance( + __uuidof(DOMDocument60), + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IXMLDOMDocument2), + reinterpret_cast(&pDOMDoc) + ) + ); + + // + // Get the default user Print Ticket Stream Factory + // + Variant_t varUserPrintTicket; + THROW_ON_FAILED_HRESULT( + pPropertyBag->GetProperty( + XPS_FP_USER_PRINT_TICKET, + &varUserPrintTicket + ) + ); + IUnknown_t pUnk = varUserPrintTicket.punkVal; + + IPrintReadStreamFactory_t pStreamFactory; + + THROW_ON_FAILED_HRESULT( + pUnk.QueryInterface(&pStreamFactory) + ); + + // + // Get the default user Print Ticket stream + // and wrap it in an IStream + // + + IPrintReadStream_t pUserPrintTicketStream; + + THROW_ON_FAILED_HRESULT( + pStreamFactory->GetStream(&pUserPrintTicketStream) + ); + + IStream_t pUserPrintTicket = + CreateIStreamFromIPrintReadStream(pUserPrintTicketStream); + + // + // Get the Printer Name + // + Variant_t varPrinterName; + THROW_ON_FAILED_HRESULT( + pPropertyBag->GetProperty( + XPS_FP_PRINTER_NAME, + &varPrinterName + ) + ); + + BSTR_t pPrinterName(varPrinterName.bstrVal); + + // + // Get the User Security Token + // Avoid CComVariant if getting the XPS_FP_USER_TOKEN property. + // Please refer to http://go.microsoft.com/fwlink/?LinkID=255534 for detailed information. + // + SafeVariant varUserSecurityToken; + THROW_ON_FAILED_HRESULT( + pPropertyBag->GetProperty( + XPS_FP_USER_TOKEN, + &varUserSecurityToken + ) + ); + + // + // Open the Print Ticket Provider + // + SafeHPTProvider_t pHProvider( + new SafeHPTProvider( + pPrinterName, + varUserSecurityToken.byref + ) + ); + + PrintTicketHandler_t toReturn( + new PrintTicketHandler( + pDOMDoc, + pHProvider, + pUserPrintTicket + ) + ); + + return toReturn; +} + +// +//Routine Name: +// +// PrintTicketHandler::PrintTicketHandler +// +//Routine Description: +// +// Constructor for the Print Ticket Handler. +// +//Arguments: +// +// pDoc - initialized MSXML DOM document +// pHProvider - handle to the Print Ticket Provider +// +PrintTicketHandler::PrintTicketHandler( + const IXMLDOMDocument2_t &pDoc, + SafeHPTProvider_t pHProvider, + const IStream_t &pUserPrintTicket + ) : m_pDOMDoc(pDoc), + m_pHProvider(pHProvider), + m_pDefaultUserPrintTicket(pUserPrintTicket) +{ +} + +// +//Routine Name: +// +// PrintTicketHandler::ProcessPrintTicket +// +//Routine Description: +// +// Gets the print ticket from the part, merges with +// the base ticket, and returns the result. +// +//Arguments: +// +// pBasePrintTicket - Base Print Ticket +// pDeltaPrintTicketPart - Delta Print Pipeline Print Ticket Part +// scope - Scope of the merged Print Ticket +// +//Return Value: +// +// IStream_t (smart pointer) +// Merged stream +// +IStream_t +PrintTicketHandler::ProcessPrintTicket( + const IStream_t &pBasePrintTicket, + const IPartPrintTicket_t &pDeltaPrintTicketPart, + EPrintTicketScope scope + ) +{ + IStream_t pMergedPrintTicket; + IStream_t pDeltaPrintTicket; + + pDeltaPrintTicket = GetStreamFromPart( + static_cast(pDeltaPrintTicketPart) + ); + + // + // Before calling PTMergeAndValidatePrintTicket, both input + // Print Ticket streams MUST be at position 0. The temp Print + // Ticket stream is already at position 0, but the Base Print + // Ticket may not be. Seek it to 0 to be sure. + // + LARGE_INTEGER zero; + zero.QuadPart = 0; + THROW_ON_FAILED_HRESULT( + pBasePrintTicket->Seek(zero, SEEK_SET, NULL) + ); + + // + // Merge the delta Print Ticket with the + // base Print Ticket + // + THROW_ON_FAILED_HRESULT( + ::CreateStreamOnHGlobal( + NULL, + TRUE, // delete on release + &pMergedPrintTicket + ) + ); + + m_pHProvider->PTMergeAndValidatePrintTicket( + pBasePrintTicket, + pDeltaPrintTicket, + scope, + pMergedPrintTicket + ); + + return pMergedPrintTicket; +} + +// +//Routine Name: +// +// PrintTicketHandler::ProcessPart +// +//Routine Description: +// +// Merges the Fixed Document Sequence Print Ticket with +// the default user Print Ticket and caches the result. +// +//Arguments: +// +// pFDS - Fixed Document Sequence part +// +void +PrintTicketHandler::ProcessPart( + const IFixedDocumentSequence_t &pFDS + ) +{ + IPartPrintTicket_t pPrintTicket; + + HRESULT hr = pFDS->GetPrintTicket(&pPrintTicket); + + // + // E_ELEMENT_NOT_FOUND means that this Fixed Document Sequence + // does not have a Print Ticket. Propagate the Default User + // Print Ticket. + // All other failed HRESULTs should be thrown. + // + if (hr == E_ELEMENT_NOT_FOUND) + { + m_pJobPrintTicket = m_pDefaultUserPrintTicket; + return; + } + + THROW_ON_FAILED_HRESULT(hr); + + m_pDocumentPrintTicket = NULL; + m_pPagePrintTicket = NULL; + + m_pJobPrintTicket = ProcessPrintTicket( + m_pDefaultUserPrintTicket, + pPrintTicket, + kPTJobScope + ); +} + +// +//Routine Name: +// +// PrintTicketHandler::ProcessPart +// +//Routine Description: +// +// Merges the Fixed Document Print Ticket with the +// Fixed Document Sequence Print Ticket and caches +// the result. The tickets are merged at the Document +// scope, so the result contains no job-level features. +// +//Arguments: +// +// pFD - Fixed Document part +// +void +PrintTicketHandler::ProcessPart( + const IFixedDocument_t &pFD + ) +{ + IPartPrintTicket_t pPrintTicket; + + HRESULT hr = pFD->GetPrintTicket(&pPrintTicket); + + // + // E_ELEMENT_NOT_FOUND means that this Fixed Document + // does not have a Print Ticket. Propagate the Job + // Print Ticket. + // All other failed HRESULTs should be thrown. + // + if (hr == E_ELEMENT_NOT_FOUND) + { + m_pDocumentPrintTicket = m_pJobPrintTicket; + return; + } + + THROW_ON_FAILED_HRESULT(hr); + + m_pPagePrintTicket = NULL; + + m_pDocumentPrintTicket = ProcessPrintTicket( + m_pJobPrintTicket, + pPrintTicket, + kPTDocumentScope + ); +} + +// +//Routine Name: +// +// PrintTicketHandler::ProcessPart +// +//Routine Description: +// +// Merges the Fixed Page Print Ticket with the +// Fixed Document Print Ticket and caches the result. +// The tickets are merged at the Page scope, so the +// result contains no job-level or document-level features. +// +//Arguments: +// +// pFP - Fixed Page part +// +void +PrintTicketHandler::ProcessPart( + const IFixedPage_t &pFP + ) +{ + IPartPrintTicket_t pPrintTicket; + + HRESULT hr = pFP->GetPrintTicket(&pPrintTicket); + + // + // E_ELEMENT_NOT_FOUND means that this Fixed Page + // does not have a Print Ticket. Propagate the Document + // Print Ticket. + // All other failed HRESULTs should be thrown. + // + if (hr == E_ELEMENT_NOT_FOUND) + { + m_pPagePrintTicket = m_pDocumentPrintTicket; + return; + } + + THROW_ON_FAILED_HRESULT(hr); + + m_pPagePrintTicket = ProcessPrintTicket( + m_pDocumentPrintTicket, + pPrintTicket, + kPTPageScope + ); +} + +// +//Routine Name: +// +// PrintTicketHandler::GetMergedPrintTicketParams +// +//Routine Description: +// +// Queries a set of parameters from the merged print ticket. +// +// NOTE: Relies on successful calls to all three PrintTicket +// processing methods. +// +//Return Value: +// +// ParametersFromPrintTicket +// The set of parameters queried from the merged +// Print Ticket. +// +ParametersFromPrintTicket +PrintTicketHandler::GetMergedPrintTicketParams() +{ + DoTraceMessage(XPSRASFILTER_TRACE_VERBOSE, L"Getting Print Ticket parameters"); + + if (!m_pPagePrintTicket) + { + DoTraceMessage(XPSRASFILTER_TRACE_ERROR, L"GetMergedPrintTicketParams called before ProcessPagePrintTicket"); + THROW_ON_FAILED_HRESULT(E_FAIL); + } + + ParametersFromPrintTicket params; + + // + // Seek the Page-level Print Ticket to 0 + // + LARGE_INTEGER zero; + zero.QuadPart = 0; + THROW_ON_FAILED_HRESULT( + m_pPagePrintTicket->Seek(zero, SEEK_SET, NULL) + ); + + // + // Load the print ticket stream into a DOM document + // + // NOTE: We are only looking for features at the Page scope for this sample, + // so we only extract features from the effective page-level PrintTicket. + // + Variant_t varStream(m_pPagePrintTicket); + VARIANT_BOOL success; + + THROW_ON_FAILED_HRESULT( + m_pDOMDoc->load(varStream, &success) + ); + if (!success) + { + WPP_LOG_ON_FAILED_HRESULT(E_FAIL); + THROW_ON_FAILED_HRESULT(E_FAIL); + } + + // + // Set the DOM Selection namespace to simplify queries + // + BSTR_t ns(L"xmlns:psf='http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework'"); + Variant_t nsProp(ns); + THROW_ON_FAILED_HRESULT( + m_pDOMDoc->setProperty(L"SelectionNamespaces", nsProp) + ); + + // + // Query the print ticket for parameters of interest + // + params.destDPI = QueryDPI(); + params.scaling = QueryScaling(); + params.physicalPageSize = QueryPhysicalPageSize(); + + // + // We simulate imageable area by assuming a constant margin of + // around the entire page + // + params.imageableArea.x = g_pageMargin * xpsDPI; + params.imageableArea.y = g_pageMargin * xpsDPI; + params.imageableArea.height = params.physicalPageSize.height + - 2 * g_pageMargin * xpsDPI; + params.imageableArea.width = params.physicalPageSize.width + - 2 * g_pageMargin * xpsDPI; + + return params; +} + +// +//Routine Name: +// +// PrintTicketHandler::QueryDPI +// +//Routine Description: +// +// Queries the DPI from the Print Ticket using an XPath query. +// +//Return Value: +// +// FLOAT +// DPI from the Print Ticket. +// +FLOAT +PrintTicketHandler::QueryDPI() +{ + // + // Default DPI: 96 DPI + // + FLOAT dpi = 96.0; + + // + // Perform the page resolution query on the print ticket + // + IXMLDOMNodeList_t pNodes; + + // + // The following XPath query is fairly straightforward, except for the + // predicate attached to Feature. This is necessary to match both the + // keyword AND the namespace of the "name" of the Feature as in: + // + // + // + // In order to match the keyword, we match the substring after the colon: + // + // [substring-after(@name,':')='PageResolution'] + // + // We also need to ensure that the namespace refers to the correct + // printschemakeywords namespace. Thus we get: + // + // [name(namespace::*[.=PRINTSCHEMAKEYWORDNS])=substring-before(@name,':')] + // + // where PRINTSCHEMAKEYWORDNS is: + // + // http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords + // + BSTR_t query(L"psf:PrintTicket/psf:Feature[substring-after(@name,':')='PageResolution']" + L"[name(namespace::*[.='http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords'])=substring-before(@name,':')]" + L"/*/*/psf:Value"); + + THROW_ON_FAILED_HRESULT( + m_pDOMDoc->selectNodes(query, &pNodes) + ); + + if (pNodes) + { + // + // The Print Ticket may have both X and Y resolutions defined, but + // the Xps Rasterization Service only accepts a single resolution. + // We query for both X and Y resolutions and take the larger of the + // two as the destination DPI. The resultant raster data could then + // be scaled down in the other dimension to achieve the non-square + // pixels. + // + + LONG numResolutions; + + THROW_ON_FAILED_HRESULT( + pNodes->get_length(&numResolutions) + ); + + if (numResolutions != 0 && + numResolutions != 1 && + numResolutions != 2) + { + // + // We expect 0, 1, or 2 resolutions to be set in the Print Ticket. + // Throw if this is not the case. + // + THROW_ON_FAILED_HRESULT(E_UNEXPECTED); + } + + LONG maxResolution = 0; + + for (INT i = 0; i < numResolutions; i++) + { + IXMLDOMNode_t pCurrentNode; + + THROW_ON_FAILED_HRESULT( + pNodes->get_item(i, &pCurrentNode) + ); + + BSTR_t strResolution; + + THROW_ON_FAILED_HRESULT( + pCurrentNode->get_text(&strResolution) + ); + + LONG resolution; + + THROW_ON_FAILED_HRESULT( + ::VarI4FromStr( + strResolution, + LOCALE_USER_DEFAULT, + 0, // no custom flags + &resolution + ) + ); + + if (resolution > maxResolution) + { + maxResolution = resolution; + } + } + + dpi = static_cast(maxResolution); + } + + DoTraceMessage(XPSRASFILTER_TRACE_VERBOSE, L"Got DPI: %f", dpi); + + return dpi; +} + +// +//Routine Name: +// +// PrintTicketHandler::QueryPhysicalPageSize +// +//Routine Description: +// +// Queries the physical page size from the +// Print Ticket using an XPath query. +// +//Return Value: +// +// XPS_SIZE +// Physical page size from the Print Ticket (in XPS units). +// +XPS_SIZE +PrintTicketHandler::QueryPhysicalPageSize() +{ + // + // Default page size: 8.5" x 11" at Xps DPI + // + XPS_SIZE pageSize = {11.0f * xpsDPI, + 8.5f * xpsDPI}; + + { + // + // Perform the page width query on the print ticket + // + IXMLDOMNode_t pNode; + + // + // See the comment in PrintTicketHandler::QueryDPI() for details + // about this XPath query. + // + BSTR_t query(L"psf:PrintTicket/psf:Feature[substring-after(@name,':')='PageMediaSize']" + L"[name(namespace::*[.='http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords'])=substring-before(@name,':')]" + L"/*/psf:ScoredProperty[substring-after(@name,':')='MediaSizeWidth']" + L"[name(namespace::*[.='http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords'])=substring-before(@name,':')]" + L"/psf:Value"); + + THROW_ON_FAILED_HRESULT( + m_pDOMDoc->selectSingleNode(query, &pNode) + ); + + if (pNode) + { + BSTR_t strWidth; + THROW_ON_FAILED_HRESULT( + pNode->get_text(&strWidth) + ); + + LONG width; + THROW_ON_FAILED_HRESULT( + ::VarI4FromStr( + strWidth, + LOCALE_USER_DEFAULT, + 0, // no custom flags + &width + ) + ); + + // + // the page dimensions are in microns; convert to Xps units + // + pageSize.width = MicronsToXpsUnits(width); + } + } + + { + // + // Perform the page height query on the print ticket + // + IXMLDOMNode_t pNode; + + // + // See the comment in PrintTicketHandler::QueryDPI() for details + // about this XPath query. + // + BSTR_t query(L"psf:PrintTicket/psf:Feature[substring-after(@name,':')='PageMediaSize']" + L"[name(namespace::*[.='http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords'])=substring-before(@name,':')]" + L"/*/psf:ScoredProperty[substring-after(@name,':')='MediaSizeHeight']" + L"[name(namespace::*[.='http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords'])=substring-before(@name,':')]" + L"/psf:Value"); + + THROW_ON_FAILED_HRESULT( + m_pDOMDoc->selectSingleNode(query, &pNode) + ); + + if (pNode) + { + BSTR_t strHeight; + THROW_ON_FAILED_HRESULT( + pNode->get_text(&strHeight) + ); + + LONG height; + THROW_ON_FAILED_HRESULT( + ::VarI4FromStr( + strHeight, + LOCALE_USER_DEFAULT, + 0, // no custom flags + &height + ) + ); + + // + // the page dimensions are in microns; convert to Xps unit + // + pageSize.height = MicronsToXpsUnits(height); + } + } + + { + // + // Perform the landscape query on the print ticket + // + IXMLDOMNode_t pNode; + + // + // See the comment in PrintTicketHandler::QueryDPI() for details + // about this XPath query. + // + BSTR_t query(L"psf:PrintTicket/psf:Feature[substring-after(@name,':')='PageOrientation']" + L"[name(namespace::*[.='http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords'])=substring-before(@name,':')]" + L"/psf:Option[substring-after(@name,':')='Landscape']" + L"[name(namespace::*[.='http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords'])=substring-before(@name,':')]"); + + THROW_ON_FAILED_HRESULT( + m_pDOMDoc->selectSingleNode(query, &pNode) + ); + + if (pNode) + { + // + // landscape. swap height and width. + // + FLOAT tmp; + + tmp = pageSize.height; + pageSize.height = pageSize.width; + pageSize.width = tmp; + } + + DoTraceMessage(XPSRASFILTER_TRACE_VERBOSE, L"Physical Page Size: %f x %f", pageSize.width, pageSize.height); + } + + return pageSize; +} + +// +//Routine Name: +// +// PrintTicketHandler::QueryScaling +// +//Routine Description: +// +// Queries the desired type of scaling from the +// Print Ticket using an XPath query. +// +//Return Value: +// +// PrintTicketScaling +// Scaling type from the Print Ticket. +// +PrintTicketScaling +PrintTicketHandler::QueryScaling() +{ + // + // Default Scaling: FitApplicationBleedSizeToPageImageableSize + // + PrintTicketScaling scaling = SCALE_BLEEDTOIMAGEABLE; + + // + // We do not query for media scaling. Rather, we always return + // the equivalent of FitApplicationBleedSizeToPageImageableSize + // + return scaling; +} + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/PThandler.h b/print/XpsRasFilter/src/PThandler.h new file mode 100644 index 00000000..103e5d78 --- /dev/null +++ b/print/XpsRasFilter/src/PThandler.h @@ -0,0 +1,112 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// PThandler.h +// +// Abstract: +// +// Print Ticket Handler class declaration. +// + +#pragma once + +namespace xpsrasfilter +{ + +class PrintTicketHandler +{ +public: + static + PrintTicketHandler_t + CreatePrintTicketHandler( + const IPrintPipelinePropertyBag_t &pPropertyBag + ); + + void + ProcessPart( + const IFixedDocumentSequence_t &pFDS + ); + + void + ProcessPart( + const IFixedDocument_t &pFD + ); + + void + ProcessPart( + const IFixedPage_t &pFP + ); + + ParametersFromPrintTicket + GetMergedPrintTicketParams(); + +private: + // + // Constructor is private; use CreatePrintTicketHandler + // to create instances. + // + PrintTicketHandler( + const IXMLDOMDocument2_t &pDoc, + SafeHPTProvider_t pHProvider, + const IStream_t &pUserPrintTicket + ); + + IStream_t + ProcessPrintTicket( + const IStream_t &pBasePrintTicket, + const IPartPrintTicket_t &pDeltaPrintTicketPart, + EPrintTicketScope scope + ); + + // + // Inline function to convert from microns to Xps Units + // + inline + FLOAT + MicronsToXpsUnits( + long dimensionInMicrons + ) + { + const FLOAT micronsPerInch = 25400.0f; // 2.54 cm/in --> 25400 um/in + + return ((static_cast(dimensionInMicrons) / micronsPerInch) * xpsDPI); + } + + // + // Routines to query parameters from the DOM document + // + FLOAT + QueryDPI(); + + XPS_SIZE + QueryPhysicalPageSize(); + + PrintTicketScaling + QueryScaling(); + + // + // MSXML DOM document + // + IXMLDOMDocument2_t m_pDOMDoc; + + // + // Handle to the Print Ticket Provider + // + SafeHPTProvider_t m_pHProvider; + + // + // Cached Print Tickets + // + IStream_t m_pDefaultUserPrintTicket; + IStream_t m_pJobPrintTicket; + IStream_t m_pDocumentPrintTicket; + IStream_t m_pPagePrintTicket; +}; + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/UnknownBase.h b/print/XpsRasFilter/src/UnknownBase.h new file mode 100644 index 00000000..5ecd2487 --- /dev/null +++ b/print/XpsRasFilter/src/UnknownBase.h @@ -0,0 +1,158 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// UnknownBase.h +// +// Abstract: +// +// IUnknown implementation common to filter components derived from +// IUnknown. +// + +#pragma once + +namespace xpsrasfilter +{ + +template +class UnknownBase : public Interface +{ +public: + UnknownBase() : m_cRef(1) { } + virtual ~UnknownBase() { }; + + // + //Routine Name: + // + // UnknownBase::QueryInterface + // + //Routine Description: + // + // Implements IUnknown QueryInterface. + // + //Arguments: + // + // riid - id of the interface + // ppv - void pointer to the requested interface + // + //Return Value: + // + // HRESULT + // S_OK - On success + // E_NOINTERFACE - Invalid interface + // + _Must_inspect_result_ + HRESULT STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID riid, + _Outptr_ PVOID *ppv + ) + { + HRESULT hr = S_OK; + + if (ppv == NULL) + { + WPP_LOG_ON_FAILED_HRESULT(E_POINTER); + + return E_POINTER; + } + + if (riid == IID_IUnknown) + { + *ppv = static_cast(this); + } + else if (riid == __uuidof(Interface)) + { + *ppv = static_cast(this); + } + else + { + *ppv = NULL; + WPP_LOG_ON_FAILED_HRESULT( + hr = E_NOINTERFACE + ); + } + + if (SUCCEEDED(hr)) + { + AddRef(); + } + + return hr; + } + + // + //Routine Name: + // + // UnknownBase::AddRef + // + //Routine Description: + // + // Implements IUnknown reference count increment + // on the current interface. + // + //Arguments: + // + // None + // + //Return Value: + // + // ULONG + // New reference count + // + ULONG STDMETHODCALLTYPE + AddRef() + { + return ::InterlockedIncrement(&m_cRef); + } + + // + //Routine Name: + // + // UnknownBase::Release + // + //Routine Description: + // + // Implements IUnknown reference count decrement + // on the current interface. + // + //Arguments: + // + // None + // + //Return Value: + // + // ULONG + // New reference count + // + //Note: + // + // The drv_at annotation tells Prefast to consider this object's memory + // freed after Release has been called. + // + _At_(this, __drv_freesMem(object)) + ULONG STDMETHODCALLTYPE + Release() + { + ULONG cRef = ::InterlockedDecrement(&m_cRef); + + if (0 == cRef) + { + delete this; + } + + return cRef; + } + +private: + volatile ULONG m_cRef; // interface reference count +}; + +} // namespace xpsrasfilter + diff --git a/print/XpsRasFilter/src/WppTrace.cpp b/print/XpsRasFilter/src/WppTrace.cpp new file mode 100644 index 00000000..b788c461 --- /dev/null +++ b/print/XpsRasFilter/src/WppTrace.cpp @@ -0,0 +1,41 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// WppTrace.h +// +// Abstract: +// +// WPP tracing definitions. +// + +#include "precomp.h" +#include "WppTrace.h" + +#include "WppTrace.tmh" + +namespace xpsrasfilter +{ + +void TraceFailedHRESULT( + HRESULT hr, + char const *fileName, + int lineNum, + wchar_t const *extraText + ) +{ + DoTraceMessage(XPSRASFILTER_TRACE_ERROR, + "Failed HRESULT (%!HRESULT!) at %s:%d (%S)", + hr, + fileName, + lineNum, + extraText + ); +} + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/WppTrace.h b/print/XpsRasFilter/src/WppTrace.h new file mode 100644 index 00000000..4f33db3b --- /dev/null +++ b/print/XpsRasFilter/src/WppTrace.h @@ -0,0 +1,58 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// WppTrace.h +// +// Abstract: +// +// WPP tracing definitions. +// + +#pragma once + +#define WPP_CONTROL_GUIDS WPP_DEFINE_CONTROL_GUID( \ + XpsRasFilter, \ + (EB4C6075, 0B67, 4a79, A0A3, 7CD9DF881194), \ + WPP_DEFINE_BIT(XPSRASFILTER_TRACE_ERROR) \ + WPP_DEFINE_BIT(XPSRASFILTER_TRACE_WARNING) \ + WPP_DEFINE_BIT(XPSRASFILTER_TRACE_INFO) \ + WPP_DEFINE_BIT(XPSRASFILTER_TRACE_VERBOSE) \ + ) + +#define WPP_LOG_ON_FAILED_HRESULT_WITH_TEXT(func_,text_) \ + { \ + HRESULT hr_ = func_; \ + if (FAILED(hr_)) \ + { \ + xpsrasfilter::TraceFailedHRESULT( \ + hr_, \ + __FILE__, \ + __LINE__, \ + text_ \ + ); \ + } \ + } + +#define WPP_LOG_ON_FAILED_HRESULT(func_) \ + { \ + WPP_LOG_ON_FAILED_HRESULT_WITH_TEXT(func_, L"") \ + } + +namespace xpsrasfilter +{ + +void +TraceFailedHRESULT( + HRESULT hr, + char const *fileName, + int lineNum, + wchar_t const *extraText + ); + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/dllentry.cpp b/print/XpsRasFilter/src/dllentry.cpp new file mode 100644 index 00000000..8e19ae16 --- /dev/null +++ b/print/XpsRasFilter/src/dllentry.cpp @@ -0,0 +1,326 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// dllentry.cpp +// +// Abstract: +// +// Xps Rasterization Service filter DLL entry points. +// + +#include "precomp.h" +#include "WppTrace.h" +#include "Exception.h" +#include "filtertypes.h" +#include "UnknownBase.h" +#include "xpsrasfilter.h" + +#include "dllentry.tmh" + +namespace xpsrasfilter +{ + +// +// Class Factory returned by DllGetClassObject() +// +class __declspec( uuid("CFF7BE69-E62D-403b-BE4F-48EC73F5DA1A") ) XPSRasFilterFactory : public UnknownBase +{ +public: + XPSRasFilterFactory() : + m_serverLocks(0) + { + ::InterlockedIncrement(&XPSRasFilter::ms_numObjects); + } + + ~XPSRasFilterFactory() + { + ::InterlockedDecrement(&XPSRasFilter::ms_numObjects); + } + + // + //Routine Name: + // + // XPSRasFilterFactory::CreateInstance + // + //Routine Description: + // + // Returns an instance of XPSRasFilter. + // + //Arguments: + // + // pUnkOuter - Outer class (must be NULL) + // riid - Requested interface (IPrintPipelineFilter) + // ppvObject - Pointer to the requested interface + // + //Return Value: + // + // HRESULT + // S_OK - On success + // Otherwise - Failure + // + HRESULT + STDMETHODCALLTYPE + CreateInstance( + IUnknown *pUnkOuter, + REFIID riid, + void **ppvObject + ) + { + HRESULT hr = S_OK; + + if (pUnkOuter != NULL) + { + WPP_LOG_ON_FAILED_HRESULT(CLASS_E_NOAGGREGATION); + + return CLASS_E_NOAGGREGATION; + } + + if (ppvObject == NULL) + { + WPP_LOG_ON_FAILED_HRESULT(E_POINTER); + + return E_POINTER; + } + + *ppvObject = NULL; + + xpsrasfilter::XPSRasFilter *pFilter = NULL; + + // + // XpsRasFilter::XpsRasFilter() can throw, as can new + // + try + { + DoTraceMessage(XPSRASFILTER_TRACE_INFO, L"Instantiating filter"); + pFilter = new xpsrasfilter::XPSRasFilter(); + } + CATCH_VARIOUS(hr) + + if (SUCCEEDED(hr)) + { + WPP_LOG_ON_FAILED_HRESULT( + hr = pFilter->QueryInterface(riid, ppvObject) + ); + + pFilter->Release(); + } + + return hr; + } + + // + //Routine Name: + // + // XPSRasFilterFactory::LockServer + // + //Routine Description: + // + // Allows clients to lock the filter factory in + // memory. + // + //Arguments: + // + // fLock - TRUE - lock; FALSE - unlock + // + //Return Value: + // + // HRESULT + // S_OK - On success + // + HRESULT + STDMETHODCALLTYPE + LockServer( + BOOL fLock + ) + { + LONG result; + + if (fLock) // lock + { + result = ::InterlockedIncrement(&m_serverLocks); + + if (result == 1) + { + // + // This was the first 'lock' call; increment the + // global numObjects + // + ::InterlockedIncrement(&XPSRasFilter::ms_numObjects); + } + } + else // unlock + { + result = ::InterlockedDecrement(&m_serverLocks); + + if (result == 0) + { + // + // All locks have been unlocked; decrement the + // global numObjects + // + ::InterlockedDecrement(&XPSRasFilter::ms_numObjects); + } + } + + return S_OK; + } + +private: + volatile LONG m_serverLocks; +}; + +} // namespace xpsrasfilter + +// +//Routine Name: +// +// DllGetClassObject +// +//Routine Description: +// +// Returns an instance of XPSRasFilterFactory. +// +//Arguments: +// +// rclsid - Requested class (XPSRasFilter) +// riid - Requested interface (IClassFactory) +// ppv - Pointer to the requested interface +// +//Return Value: +// +// HRESULT +// S_OK - On success +// Otherwise - Failure +// +STDAPI +DllGetClassObject( + _In_ REFCLSID rclsid, + _In_ REFIID riid, + _Outptr_ LPVOID *ppv + ) +{ + HRESULT hr = S_OK; + + if (ppv == NULL) + { + WPP_LOG_ON_FAILED_HRESULT(E_POINTER); + + return E_POINTER; + } + + *ppv = NULL; + + if (rclsid != __uuidof(xpsrasfilter::XPSRasFilterFactory)) + { + WPP_LOG_ON_FAILED_HRESULT(CLASS_E_CLASSNOTAVAILABLE); + + return CLASS_E_CLASSNOTAVAILABLE; + } + + DoTraceMessage(XPSRASFILTER_TRACE_INFO, L"Instantiating class factory"); + + xpsrasfilter::XPSRasFilterFactory *pFactory = NULL; + pFactory = new(std::nothrow) xpsrasfilter::XPSRasFilterFactory(); + + if (pFactory == NULL) + { + WPP_LOG_ON_FAILED_HRESULT(E_OUTOFMEMORY); + + hr = E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr)) + { + WPP_LOG_ON_FAILED_HRESULT( + hr = pFactory->QueryInterface(riid, ppv) + ); + + pFactory->Release(); + } + + return hr; +} + +// +//Routine Name: +// +// DllCanUnloadNow +// +//Routine Description: +// +// Checks whether the DLL can be unloaded. That is, +// whether there are any instances of XPSRasFilter. +// +//Arguments: +// +// None +// +//Return Value: +// +// HRESULT +// S_OK - Can unload. +// Otherwise - Cannot unload. +// +STDAPI +DllCanUnloadNow() +{ + return (0 == xpsrasfilter::XPSRasFilter::ms_numObjects) ? S_OK : S_FALSE; +} + +// +//Routine Name: +// +// DllMain +// +//Routine Description: +// +// Initializes WPP tracing when the DLL is loaded +// and cleans up WPP tracing when the DLL is unloaded. +// +//Arguments: +// +// None +// +//Return Value: +// +// HRESULT +// S_OK - On success. +// Otherwise - Otherwise. +// +extern "C" +BOOL +WINAPI +DllMain( + _In_ HINSTANCE hinstDLL, + _In_ DWORD fdwReason, + _In_opt_ LPVOID /*lpvReserved*/ + ) +{ + switch(fdwReason) + { + case DLL_PROCESS_ATTACH: + + ::DisableThreadLibraryCalls(hinstDLL); + + WPP_INIT_TRACING(L"XpsRasFilter"); + DoTraceMessage(XPSRASFILTER_TRACE_INFO, L"DLL_PROCESS_ATTACH"); + + break; + + case DLL_PROCESS_DETACH: + + DoTraceMessage(XPSRASFILTER_TRACE_INFO, L"DLL_PROCESS_DETACH"); + WPP_CLEANUP(); + + break; + } + + return TRUE; +} + diff --git a/print/XpsRasFilter/src/filtertypes.h b/print/XpsRasFilter/src/filtertypes.h new file mode 100644 index 00000000..6b5c35e6 --- /dev/null +++ b/print/XpsRasFilter/src/filtertypes.h @@ -0,0 +1,497 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// filtertypes.h +// +// Abstract: +// +// Smart pointer types. Shared structures and enums. +// + +#pragma once + +// +// XpsDrv Print Pipeline types +// +typedef CComPtr IXpsDocument_t; +typedef CComPtr IFixedDocumentSequence_t; +typedef CComPtr IFixedDocument_t; +typedef CComPtr IFixedPage_t; +typedef CComPtr IPrintReadStreamFactory_t; +typedef CComPtr IPrintReadStream_t; +typedef CComPtr IPrintWriteStream_t; +typedef CComPtr IInterFilterCommunicator_t; +typedef CComPtr IPrintPipelinePropertyBag_t; +typedef CComPtr IPrintPipelineManagerControl_t; +typedef CComPtr IXpsDocumentProvider_t; +typedef CComPtr IXpsDocumentConsumer_t; +typedef CComPtr IXpsPartIterator_t; +typedef CComPtr IPartBase_t; +typedef CComPtr IPartFont2_t; +typedef CComPtr IPartFont_t; +typedef CComPtr IPartImage_t; +typedef CComPtr IPartColorProfile_t; +typedef CComPtr IPartResourceDictionary_t; +typedef std::vector> ResourceDictionaryList_t; +typedef CComPtr IPartPrintTicket_t; + +// +// Xps Object Model types +// +typedef CComPtr IXpsOMObjectFactory_t; +typedef CComPtr IXpsOMPartResources_t; +typedef CComPtr IXpsOMFontResourceCollection_t; +typedef CComPtr IXpsOMImageResourceCollection_t; +typedef CComPtr IXpsOMColorProfileResourceCollection_t; +typedef CComPtr IXpsOMRemoteDictionaryResourceCollection_t; +typedef CComPtr IXpsOMFontResource_t; +typedef CComPtr IXpsOMImageResource_t; +typedef CComPtr IXpsOMColorProfileResource_t; +typedef CComPtr IXpsOMRemoteDictionaryResource_t; +typedef CComPtr IXpsOMPage_t; + +// +// Opc Types +// +typedef CComPtr IOpcFactory_t; +typedef CComPtr IOpcPartUri_t; + +// +// Common types +// +typedef CComPtr IStream_t; +typedef CComPtr IUnknown_t; +typedef CComBSTR BSTR_t; +typedef CComVariant Variant_t; +typedef CComPtr IPropertyBag2_t; + +// +// WIC types +// +typedef CComPtr IWICImagingFactory_t; +typedef CComPtr IWICBitmap_t; +typedef CComPtr IWICStream_t; +typedef CComPtr IWICBitmapEncoder_t; +typedef CComPtr IWICBitmapFrameEncode_t; + +// +// Xps Rasterization Service types +// +typedef CComPtr IXpsRasterizationFactory_t; +typedef CComPtr IXpsRasterizer_t; + +// +// MSXML DOM types +// +typedef CComPtr IXMLDOMDocument2_t; +typedef CComPtr IXMLDOMNode_t; +typedef CComPtr IXMLDOMNodeList_t; + +namespace xpsrasfilter +{ + +// +// Supported types of print ticket scaling +// +enum PrintTicketScaling +{ + SCALE_NONE, + SCALE_BLEEDTOIMAGEABLE, + SCALE_CONTENTTOIMAGEABLE, + SCALE_MEDIASIZETOIMAGEABLE, + SCALE_MEDIASIZETOMEDIASIZE +}; + +// +// Parameters that can be read from the Print Ticket +// to feed into rasterization calculations. +// +struct ParametersFromPrintTicket +{ + XPS_SIZE physicalPageSize; // in XPS units + PrintTicketScaling scaling; // scaling type + FLOAT destDPI; // target rasterization dpi + XPS_RECT imageableArea; // in XPS units +}; + +// +// Parameters read from the FixedPage +// to feed into rasterization calculations. +// +struct ParametersFromFixedPage +{ + XPS_SIZE fixedPageSize; // in XPS units + XPS_RECT bleedBoxRect; // in XPS units + XPS_RECT contentBoxRect; // in XPS units +}; + +// +// Forward Declarations +// +class RasterizationInterface; +class PrintTicketHandler; +class TiffStreamBitmapHandler; +class FilterLiveness; + +} // namespace xpsrasfilter + +// +// This class handles setting and clearing the security +// context, based on a token. This token is retrieved from +// the filter pipeline, and we do not want to free it. +// +class ScopeImpersonation +{ +public: + ScopeImpersonation( + HANDLE token + ) + { + if ( + !SetThreadToken( + NULL, // set the current thread's token + token + ) + ) + { + THROW_LAST_ERROR(); + } + } + + ~ScopeImpersonation() + { + if ( + !SetThreadToken( + NULL, // set the current thread's token + NULL // revert to default security context + ) + ) + { + // + // We couldn't revert the security context. The filter pipeline + // manager will clean up the thread when operation is complete, + // when it is determined that the security context was not + // reverted. Since there are no security implications with + // running this filter in an elevated context, we can log the + // error and continue to run. + // + DWORD error = ::GetLastError(); + + WPP_LOG_ON_FAILED_HRESULT_WITH_TEXT( + HRESULT_FROM_WIN32(error), + L"Failed to revert thread security context." + ); + } + } + +private: + ScopeImpersonation(ScopeImpersonation const&); + ScopeImpersonation& operator=(ScopeImpersonation const&); +}; + +// +// RAII object to make HGLOBAL locks exception-safe. This requires +// unlocking during unwind. +// +class HGlobalLock +{ +public: + HGlobalLock( + HGLOBAL hG + ) : + m_hGlobal(hG) + { + m_pAddress = static_cast( + ::GlobalLock(m_hGlobal) + ); + + if (!m_pAddress) + { + THROW_LAST_ERROR(); + } + } + + ~HGlobalLock() + { + if (!::GlobalUnlock(m_hGlobal)) + { + WPP_LOG_ON_FAILED_HRESULT( + HRESULT_FROM_WIN32(::GetLastError()) + ); + } + } + + BYTE* + GetAddress() + { + return m_pAddress; + } +private: + HGLOBAL m_hGlobal; + BYTE *m_pAddress; + + HGlobalLock(HGlobalLock const&); + HGlobalLock& operator=(HGlobalLock const&); + +}; + +typedef std::auto_ptr HGlobalLock_t; + +// +// Safe handle to make HGLOBAL exception-safe. This requires both +// freeing and unlocking during unwind. +// +class SafeHGlobal +{ +public: + + SafeHGlobal( + UINT flags, + SIZE_T size + ) + { + m_hGlobal = ::GlobalAlloc(flags, size); + + if (!m_hGlobal) + { + THROW_ON_FAILED_HRESULT(E_OUTOFMEMORY); + } + } + + virtual + ~SafeHGlobal() + { + if (m_hGlobal) + { + // + // Free the HGLOBAL + // + ::GlobalFree(m_hGlobal); + } + } + + operator HGLOBAL() + { + return m_hGlobal; + } + + // + // Passes ownership of the HGLOBAL from this safe handle + // to a new IStream. + // + IStream_t + ConvertToIStream() + { + IStream_t pStream; + + THROW_ON_FAILED_HRESULT( + ::CreateStreamOnHGlobal( + m_hGlobal, + TRUE, // Free the HGLOBAL on Release of the stream + &pStream + ) + ); + + m_hGlobal = NULL; + + return pStream; + } + + HGlobalLock_t + Lock() + { + HGlobalLock_t toReturn( + new HGlobalLock(m_hGlobal) + ); + return toReturn; + } + +private: + + HGLOBAL m_hGlobal; + + SafeHGlobal(SafeHGlobal const&); + SafeHGlobal& operator=(SafeHGlobal const&); + +}; + +// +// Safe handle to make HPTPROVIDER exception safe. This +// requires closing the provider during unwind. +// +class SafeHPTProvider +{ +public: + SafeHPTProvider( + const wchar_t *printerName, + HANDLE userSecurityToken + ) : + m_token(userSecurityToken) + { + // + // We impersonate the user while we call PTQuerySchemaVersionSupport + // and PTOpenProviderEx. + // + ScopeImpersonation impersonate(m_token); + + DWORD maxVersion, + tempVersion; + + THROW_ON_FAILED_HRESULT( + ::PTQuerySchemaVersionSupport( + printerName, + &maxVersion + ) + ); + + THROW_ON_FAILED_HRESULT( + ::PTOpenProviderEx( + printerName, + maxVersion, // maximum version + maxVersion, // preferred version + &m_hProvider, + &tempVersion // version used by the provider + ) + ); + } + + virtual + ~SafeHPTProvider() + { + WPP_LOG_ON_FAILED_HRESULT( + ::PTCloseProvider(m_hProvider) + ); + } + + void + PTMergeAndValidatePrintTicket( + const IStream_t &pBasePrintTicket, + const IStream_t &pDeltaPrintTicket, + EPrintTicketScope scope, + _Inout_ IStream_t &pMergedPrintTicket + ) + { + // + // We impersonate the user while we call PTMergeAndValidatePrintTicket. + // + ScopeImpersonation impersonate(m_token); + + BSTR_t error; + + HRESULT hr = ::PTMergeAndValidatePrintTicket( + m_hProvider, + pBasePrintTicket, + pDeltaPrintTicket, + scope, + pMergedPrintTicket, + &error + ); + + WPP_LOG_ON_FAILED_HRESULT_WITH_TEXT( + hr, + error + ); + + THROW_ON_FAILED_HRESULT(hr); + } + +private: + + HPTPROVIDER m_hProvider; + HANDLE m_token; + + SafeHPTProvider(SafeHPTProvider const&); + SafeHPTProvider& operator=(SafeHPTProvider const&); +}; + +// +// CoInitialize/CoUninitialize RAII object +// +// This object ensures that COM is initialized for the duration +// of the XpsRasFilter's lifetime, and then uninitialized after +// all of the COM objects, regardless of how the filter exits +// +class SafeCoInit +{ +public: + SafeCoInit() : + m_doCoUninitialize(FALSE) + { + // + // Initialize COM + // + HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); + + if (FAILED(hr) && + hr != RPC_E_CHANGED_MODE) + { + // + // RPC_E_CHANGED_MODE indicates that we attempted to change the + // threading model. It is safe to ignore since we do not *require* + // multi-threading. Throw on any other errors. + // + + THROW_ON_FAILED_HRESULT(hr); + } + else if (SUCCEEDED(hr)) + { + // + // It is important that we only call CoUninitialize() if we + // succeeded in setting the threading model. + // + + m_doCoUninitialize = TRUE; + } + } + + ~SafeCoInit() + { + if (m_doCoUninitialize) + { + ::CoUninitialize(); + } + } +private: + SafeCoInit(SafeCoInit const&); + SafeCoInit& operator=(SafeCoInit const&); + + BOOL m_doCoUninitialize; +}; + +// +// RAII object to make VARIANT exception-safe. This requires +// VariantClear during unwind. +// +class SafeVariant : public VARIANT +{ +public: + SafeVariant() + { + ::VariantInit(this); + } + + ~SafeVariant() + { + ::VariantClear(this); + } +private: + SafeVariant(SafeVariant const&); + SafeVariant& operator=(SafeVariant const&); +}; + +// +// Internal Types +// +typedef std::auto_ptr RasterizationInterface_t; +typedef std::auto_ptr PrintTicketHandler_t; +typedef std::auto_ptr TiffStreamBitmapHandler_t; +typedef std::auto_ptr SafeHGlobal_t; +typedef std::auto_ptr SafeHPTProvider_t; +typedef CComPtr FilterLiveness_t; + diff --git a/print/XpsRasFilter/src/precomp.h b/print/XpsRasFilter/src/precomp.h new file mode 100644 index 00000000..97e8f4ee --- /dev/null +++ b/print/XpsRasFilter/src/precomp.h @@ -0,0 +1,86 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// precomp.h +// +// Abstract: +// +// Precompiled header for the Xps Rasterization Service sample filter. +// + +#pragma once + +// +// Define this as a usermode driver for analysis purposes +// +#include +__user_driver + +// +// Standard Annotation Language include +// +#include + +// +// Windows includes +// +#include + +// Standard includes +#include +#include +#include + +// STL +#include + +// +// COM includes +// +#include +#include + +// +// Filter pipeline includes +// +#include +#include +#include +#include + +// +// ATL +// +#include + +// +// WIC +// +#include + +// +// MSXML +// +#include + +// +// OPC Layer +// +#include + +// +// Xps Object Model +// +#include + +// +// Xps Rasterization Service +// +#include + diff --git a/print/XpsRasFilter/src/precompsrc.cpp b/print/XpsRasFilter/src/precompsrc.cpp new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/print/XpsRasFilter/src/precompsrc.cpp @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/print/XpsRasFilter/src/rasinterface.cpp b/print/XpsRasFilter/src/rasinterface.cpp new file mode 100644 index 00000000..adbf73e4 --- /dev/null +++ b/print/XpsRasFilter/src/rasinterface.cpp @@ -0,0 +1,412 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// rasinterface.cpp +// +// Abstract: +// +// Class to wrap rasterization-related calculations, interactions with +// the Xps Rasterization Service, Xps Rasterization Service Callback, +// and eventual raster output (i.e. TIFF encoding) +// + +#include "precomp.h" +#include "WppTrace.h" +#include "Exception.h" +#include "filtertypes.h" +#include "UnknownBase.h" +#include "xpsrasfilter.h" +#include "OMConvertor.h" +#include "rasinterface.h" +#include "BitmapHandler.h" + +#include "rasinterface.tmh" + +namespace xpsrasfilter +{ + +// +//Routine Name: +// +// RasterizationInterface::CreateRasterizationInterface +// +//Routine Description: +// +// Static factory method that creates an instance of +// RasterizationInterface. +// +//Arguments: +// +// pPropertyBag - Property Bag +// pStream - Filter output stream (IPrintWriteStream) +// +//Return Value: +// +// RasterizationInterface_t (smart ptr) +// The new RasterizationInterface. +// +RasterizationInterface_t +RasterizationInterface::CreateRasterizationInterface( + const IPrintPipelinePropertyBag_t &pPropertyBag, + const IPrintWriteStream_t &pStream + ) +{ + Variant_t varRasFactory; + IXpsRasterizationFactory_t pXPSRasFactory; + + // + // Get the Xps Rasterization Service factory. Since XpsRasterService.dll + // is specified as an OptionalFilterServiceProvider in the filter pipeline + // configuration file, the factory may not be available in the property bag + // (e.g. when running on Windows Vista, Windows XP, etc). In this case, + // this call will fail and the filter could fail, as we do here, or could + // default to some other behavior. + // + THROW_ON_FAILED_HRESULT( + pPropertyBag->GetProperty( + L"MS_IXpsRasterizationFactory", + &varRasFactory + ) + ); + + IUnknown_t pUnk(varRasFactory.punkVal); + + THROW_ON_FAILED_HRESULT( + pUnk.QueryInterface(&pXPSRasFactory) + ); + + // + // Prepare the rasterization/encode/stream interface + // + TiffStreamBitmapHandler_t pBitmapHandler( + TiffStreamBitmapHandler::CreateTiffStreamBitmapHandler(pStream) + ); + + RasterizationInterface_t pReturnInterface( + new RasterizationInterface( + pXPSRasFactory, + pBitmapHandler + ) + ); + + return pReturnInterface; +} + +// +//Routine Name: +// +// RasterizationInterface::RasterizationInterface +// +//Routine Description: +// +// Construct the Rasterization Interface with the +// IXpsRasterizationFactory interface and bitmap +// handler. +// +//Arguments: +// +// pRasFactory - Xps Rasterization Service object factory +// pBitmapHandler - Class to handle band bitmaps +// +RasterizationInterface::RasterizationInterface( + const IXpsRasterizationFactory_t &pRasFactory, + TiffStreamBitmapHandler_t pBitmapHandler + ) : m_pXPSRasFactory(pRasFactory), + m_pBitmapHandler(pBitmapHandler) +{ +} + +// +//Routine Name: +// +// RasterizationInterface::FinishRasterization +// +//Routine Description: +// +// Tell the Rasterization Interface that the last +// page has been rasterized. +// +//Arguments: +// +// None +// +void +RasterizationInterface::FinishRasterization() +{ + m_pBitmapHandler->WriteFooter(); +} + + +// +//Routine Name: +// +// RasterizationInterface::RasterizeAndStreamPage +// +//Routine Description: +// +// Given an IXpsOMPage and a set of Print Ticket +// parameters, this method invokes the Xps Rasterization +// Service for each band of the page, and outputs the +// resultant raster data. +// +//Arguments: +// +// pPage - page to rasterize +// printTicketparams - raw parameters from the print ticket(s) +// +void +RasterizationInterface::RasterizePage( + const IXpsOMPage_t &pPage, + const ParametersFromPrintTicket &printTicketParams, + const FilterLiveness_t &pLiveness + ) +{ + // + // Calculate rasterization parameters + // + ParametersFromFixedPage fixedPageParams; + THROW_ON_FAILED_HRESULT( + pPage->GetPageDimensions(&fixedPageParams.fixedPageSize) + ); + THROW_ON_FAILED_HRESULT( + pPage->GetBleedBox(&fixedPageParams.bleedBoxRect) + ); + THROW_ON_FAILED_HRESULT( + pPage->GetContentBox(&fixedPageParams.contentBoxRect) + ); + + RasterizationParameters rastParams( + printTicketParams, + fixedPageParams + ); + + // + // Create the Rasterizer + // + IXpsRasterizer_t rasterizer; + THROW_ON_FAILED_HRESULT( + m_pXPSRasFactory->CreateRasterizer( + pPage, + rastParams.rasterizationDPI, + XPSRAS_RENDERING_MODE_ANTIALIASED, + XPSRAS_RENDERING_MODE_ANTIALIASED, + &rasterizer + ) + ); + + // + // Set the minimal line width to 1 pixel + // + THROW_ON_FAILED_HRESULT( + rasterizer->SetMinimalLineWidth(1) + ); + + // + // Loop over bands + // + INT bandOriginY = 0; + + while (pLiveness->IsAlive() && + bandOriginY < rastParams.rasterHeight) + { + DoTraceMessage(XPSRASFILTER_TRACE_VERBOSE, L"Rasterizing Band"); + + IWICBitmap_t bitmap; + + // + // Calculate the height of this band + // + INT bandHeight = rastParams.bandHeight; + if (bandOriginY + rastParams.bandHeight >= rastParams.rasterHeight) + { + bandHeight = rastParams.rasterHeight - bandOriginY; + } + + // + // Rasterize this band + // + { + HRESULT hr = rasterizer->RasterizeRect( + rastParams.originX, + bandOriginY + rastParams.originY, + rastParams.rasterWidth, + bandHeight, + static_cast(pLiveness), + &bitmap + ); + + // + // Do not throw if we have cancelled rasterization + // + if (hr == HRESULT_FROM_WIN32(ERROR_PRINT_CANCELLED)) + { + DoTraceMessage(XPSRASFILTER_TRACE_VERBOSE, L"Rasterization Cancelled"); + return; + } + + THROW_ON_FAILED_HRESULT(hr); + } + + bandOriginY += bandHeight; + + // + // The resolution of the bitmap defaults to the + // rasterization DPI, which includes the scaling factor. + // We want the output bitmap to reflect the destination DPI. + // + THROW_ON_FAILED_HRESULT( + bitmap->SetResolution( + printTicketParams.destDPI, + printTicketParams.destDPI + ) + ); + + // + // Encode the raster data as TIFF and stream out + // + m_pBitmapHandler->ProcessBitmap(bitmap); + } +} + +// +//Routine Name: +// +// RasterizationParameters::RasterizationParameters +// +//Routine Description: +// +// Given a set of fixed page parameters and a set of Print Ticket +// parameters, this constructor calculates the parameters necessary to +// invoke the Xps Rasterization Service. +// +//Arguments: +// +// printTicketparams - raw parameters from the print ticket(s) +// fixedPageParams - raw parameters from the fixed page +// +RasterizationParameters::RasterizationParameters( + const ParametersFromPrintTicket &printTicketParams, + const ParametersFromFixedPage &fixedPageParams + ) +{ + // + // Rasterize the entire physical page at the desired resolution. The fixed page + // is scaled and traslated to place it correctly witin the physical page. + // + rasterHeight = static_cast( + printTicketParams.physicalPageSize.height + * (printTicketParams.destDPI / xpsDPI) + + 0.5 + ); + rasterWidth = static_cast( + printTicketParams.physicalPageSize.width + * (printTicketParams.destDPI / xpsDPI) + + 0.5 + ); + + // + // Determine the source rectangle for the scale operation + // + XPS_RECT srcRect = {0,0,0,0}; + + switch(printTicketParams.scaling) + { + case SCALE_BLEEDTOIMAGEABLE: + + srcRect = fixedPageParams.bleedBoxRect; + + break; + default: + DoTraceMessage(XPSRASFILTER_TRACE_ERROR, L"Unknown scaling type"); + THROW_ON_FAILED_HRESULT(E_UNEXPECTED); + break; + } + + // + // Determine the destination rectangle for the scale operation + // + XPS_RECT destRect = {0,0,0,0}; + + switch(printTicketParams.scaling) + { + case SCALE_BLEEDTOIMAGEABLE: + + destRect = printTicketParams.imageableArea; + + break; + default: + DoTraceMessage(XPSRASFILTER_TRACE_ERROR, L"Unknown scaling type"); + THROW_ON_FAILED_HRESULT(E_UNEXPECTED); + break; + } + + + // + // Because we want to fit the fixed page into a physical page of + // potentially different aspect ratio, it is necessary to calculate + // the scaling factor assuming either the height or width constrains + // the operation; the rasterization dpi is then the smaller of the two. + // + // + // The basic calculation for scaling factor is: + // + // Scaling Factor = (Destination Dimension / Source Dimension) + // + // Multiplying by desired Destination DPI we get the DPI at which to + // rasterize the source content in order to get the desired size + // + // Rasterization DPI = Scaling Factor * Desired Destination DPI + // + { + FLOAT heightScalingFactor = destRect.height / srcRect.height; + FLOAT widthScalingFactor = destRect.width / srcRect.width; + + FLOAT heightRastDPI = heightScalingFactor * printTicketParams.destDPI; + FLOAT widthRastDPI = widthScalingFactor * printTicketParams.destDPI; + + rasterizationDPI = min(heightRastDPI, widthRastDPI); + } + + // + // To determine the rasterization origin, subtract the translation + // due to the destination rectangle (e.g. imageable area) from the + // translation due to the choice of scale (e.g. bleed box origin) + // + { + FLOAT srcOffsetX = srcRect.x * rasterizationDPI / xpsDPI; + FLOAT srcOffsetY = srcRect.y * rasterizationDPI / xpsDPI; + + FLOAT destOffsetX = destRect.x * printTicketParams.destDPI / xpsDPI; + FLOAT destOffsetY = destRect.y * printTicketParams.destDPI / xpsDPI; + + originX = static_cast(srcOffsetX - destOffsetX - 0.5); + originY = static_cast(srcOffsetY - destOffsetY - 0.5); + } + + // + // The height of each band is determined by the maximum band size (in bytes) + // divided by 4 bytes per pixel to get the total pixels, and then divided by + // the width of the raster data. This results in a band bitmap that is close + // to the target band size. + // + bandHeight = (RasterizationInterface::ms_targetBandSize / 4) / rasterWidth; + + if (bandHeight == 0) + { + // + // The physical page is too wide to rasterize at this + // maximum band size and dpi. Throw. + // + THROW_ON_FAILED_HRESULT( + E_OUTOFMEMORY + ); + } +} + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/rasinterface.h b/print/XpsRasFilter/src/rasinterface.h new file mode 100644 index 00000000..3fae74cf --- /dev/null +++ b/print/XpsRasFilter/src/rasinterface.h @@ -0,0 +1,107 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// rasinterface.h +// +// Abstract: +// +// Class to wrap rasterization-related calculations, interactions with +// the Xps Rasterization Service, Xps Rasterization Service Callback, +// and eventual raster output (i.e. TIFF encoding) +// + +#pragma once + +namespace xpsrasfilter +{ + +class RasterizationInterface +{ +public: + static + RasterizationInterface_t + CreateRasterizationInterface( + const IPrintPipelinePropertyBag_t &pPropertyBag, + const IPrintWriteStream_t &pStream + ); + + RasterizationInterface(); + + void + RasterizePage( + const IXpsOMPage_t &pPage, + const ParametersFromPrintTicket &printTicketParams, + const FilterLiveness_t &pLiveness + ); + + void + CancelRasterization(); + + void + FinishRasterization(); + + // + // Target band size; 16MB + // + const static LONG ms_targetBandSize = 1024 * 1024 * 16; + +private: + + // + // Constructor is private; use CreateRasterizationInterface + // to create instances + // + RasterizationInterface( + const IXpsRasterizationFactory_t &pRasFactory, + TiffStreamBitmapHandler_t pBitmapHandler + ); + + // + // prevent copy semantics + // + RasterizationInterface(const RasterizationInterface&); + RasterizationInterface& operator=(const RasterizationInterface&); + + // + // Internal data members + // + + // + // Xps Rasterization Service Factory + // + IXpsRasterizationFactory_t m_pXPSRasFactory; + + // + // Bitmap Handler + // + TiffStreamBitmapHandler_t m_pBitmapHandler; +}; + +// +// Parameters that determine how a page is rasterized, in the +// units that the Rasterization Service expects +// +struct RasterizationParameters +{ + FLOAT rasterizationDPI; // dpi (scaling factor) + INT rasterHeight; // total size of raster + INT rasterWidth; + INT originX; // origin of the rasterization (translation) + INT originY; + INT bandHeight; // height of individual bands + + RasterizationParameters( + const ParametersFromPrintTicket &printTicketParams, + const ParametersFromFixedPage &fixedPageParams + ); +}; + +const FLOAT xpsDPI = 96.0f; + +} // namespace xpsrasfilter diff --git a/print/XpsRasFilter/src/xpsrasfilter.cpp b/print/XpsRasFilter/src/xpsrasfilter.cpp new file mode 100644 index 00000000..1199a540 --- /dev/null +++ b/print/XpsRasFilter/src/xpsrasfilter.cpp @@ -0,0 +1,396 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// xpsrasfilter.cpp +// +// Abstract: +// +// Xps Rasterization Service filter implementation. +// + +#include "precomp.h" +#include "WppTrace.h" +#include "Exception.h" +#include "filtertypes.h" +#include "UnknownBase.h" +#include "OMConvertor.h" +#include "rasinterface.h" +#include "BitmapHandler.h" +#include "PThandler.h" +#include "xpsrasfilter.h" + +#include "xpsrasfilter.tmh" + +namespace xpsrasfilter +{ + +long XPSRasFilter::ms_numObjects = 0; // Initialize static object count + +// +//Routine Name: +// +// XPSRasFilter::XPSRasFilter +// +//Routine Description: +// +// Xps Rasterization Service sample filter default constructor. +// +//Arguments: +// +// None +// +//Return Value: +// +// None +// +XPSRasFilter::XPSRasFilter() +{ + // + // Take ownership with no AddRef + // + m_pLiveness.Attach(new FilterLiveness()); + + ::InterlockedIncrement(&ms_numObjects); +} + +// +//Routine Name: +// +// XPSRasFilter::~XPSRasFilter +// +//Routine Description: +// +// Xps Rasterization Service sample filter destructor. +// +//Arguments: +// +// None +// +//Return Value: +// +// None +// +XPSRasFilter::~XPSRasFilter() +{ + ::InterlockedDecrement(&ms_numObjects); +} + +// +//Routine Name: +// +// XPSRasFilter::InitializeFilter +// +//Routine Description: +// +// Exception boundary wrapper for IPrintPipelineFilter initialization. +// +//Arguments: +// +// pICommunicator - interface to interfilter communicator +// pIPropertyBag - interface to pipeline property bag +// pIPipelineControl - interface to pipeline control methods +// +//Return Value: +// +// ULONG +// New reference count +// +_Must_inspect_result_ +HRESULT STDMETHODCALLTYPE +XPSRasFilter::InitializeFilter( + _In_ IInterFilterCommunicator *pICommunicator, + _In_ IPrintPipelinePropertyBag *pIPropertyBag, + _In_ IPrintPipelineManagerControl *pIPipelineControl + ) +{ + DoTraceMessage(XPSRASFILTER_TRACE_INFO, L"Initializing Filter"); + + if (pICommunicator == NULL || + pIPropertyBag == NULL || + pIPipelineControl == NULL) + { + WPP_LOG_ON_FAILED_HRESULT(E_POINTER); + + return E_POINTER; + } + + HRESULT hr = S_OK; + + try + { + InitializeFilter_throws( + pICommunicator, + pIPropertyBag + ); + } + CATCH_VARIOUS(hr); + + return hr; +} + +// +//Routine Name: +// +// XPSRasFilter::InitializeFilter_throws +// +//Routine Description: +// +// Implements IPrintPipelineFilter initialization. Gets +// all necessary communication interfaces. +// +//Arguments: +// +// pICommunicator - interface to interfilter communicator +// pIPropertyBag - interface to pipeline property bag +// +VOID +XPSRasFilter::InitializeFilter_throws( + const IInterFilterCommunicator_t &pICommunicator, + const IPrintPipelinePropertyBag_t &pIPropertyBag + ) +{ + // + // Get the pipeline communication interfaces + // + THROW_ON_FAILED_HRESULT( + pICommunicator->RequestReader(reinterpret_cast(&m_pReader)) + ); + THROW_ON_FAILED_HRESULT( + pICommunicator->RequestWriter(reinterpret_cast(&m_pWriter)) + ); + + { + // + // Check to ensure that the provided interfaces are as expected. + // That is, that the GUIDs were correctly listed in the + // pipeline configuration file + // + IXpsDocumentProvider_t pReaderCheck; + IPrintWriteStream_t pWriterCheck; + + THROW_ON_FAILED_HRESULT( + m_pReader.QueryInterface(&pReaderCheck) + ); + THROW_ON_FAILED_HRESULT( + m_pWriter.QueryInterface(&pWriterCheck) + ); + } + + // + // Save a pointer to the Property Bag for further + // initialization, later. + // + m_pIPropertyBag = pIPropertyBag; +} + +// +//Routine Name: +// +// XPSRasFilter::ShutdownOperation +// +//Routine Description: +// +// Called asynchronously by the pipeline manager +// to shutdown filter operation. +// +//Arguments: +// +// None +// +//Return Value: +// +// HRESULT +// S_OK - On success +// +_Must_inspect_result_ +HRESULT +XPSRasFilter::ShutdownOperation() +{ + DoTraceMessage(XPSRASFILTER_TRACE_INFO, L"Shutting Down Operation"); + + m_pLiveness->Cancel(); + + return S_OK; +} + +// +//Routine Name: +// +// XPSRasFilter::StartOperation +// +//Routine Description: +// +// Called by the pipeline manager to start processing +// a document. Exception boundary for page processing. +// +//Arguments: +// +// None +// +//Return Value: +// +// HRESULT +// S_OK - On success +// Otherwise - Failure +// +_Must_inspect_result_ +HRESULT +XPSRasFilter::StartOperation() +{ + HRESULT hr = S_OK; + + DoTraceMessage(XPSRASFILTER_TRACE_INFO, L"Starting Operation"); + + // + // Process the Xps Package + // + try { + StartOperation_throws(); + } + CATCH_VARIOUS(hr); + + m_pWriter->Close(); + + return hr; +} + +// +//Routine Name: +// +// XPSRasFilter::StartOperation_throws +// +//Routine Description: +// +// Iterates over the 'trunk' parts of the document +// and calls appropriate processing methods. +// +//Arguments: +// +// None +// +void +XPSRasFilter::StartOperation_throws() +{ + // + // CoInitialize/CoUninitialize RAII object. + // COM is inititalized for the lifetime of this method. + // + SafeCoInit coInit; + + IXpsOMObjectFactory_t pOMFactory; + + // + // Create Xps Object Model Object Factory instance + // + THROW_ON_FAILED_HRESULT( + ::CoCreateInstance( + __uuidof(XpsOMObjectFactory), + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IXpsOMObjectFactory), + reinterpret_cast(&pOMFactory) + ) + ); + + IOpcFactory_t pOpcFactory; + + // + // Create Opc Object Factory instance + // + THROW_ON_FAILED_HRESULT( + ::CoCreateInstance( + __uuidof(OpcFactory), + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IOpcFactory), + reinterpret_cast(&pOpcFactory) + ) + ); + + // + // Create the rasterization interface + // + RasterizationInterface_t pRasInterface = + RasterizationInterface::CreateRasterizationInterface( + m_pIPropertyBag, + m_pWriter + ); + + // + // Create the Print Ticket Handler + // + PrintTicketHandler_t pPrintTicketHandler = + PrintTicketHandler::CreatePrintTicketHandler( + m_pIPropertyBag + ); + + IUnknown_t pUnk; + + // + // Get first part + // + THROW_ON_FAILED_HRESULT(m_pReader->GetXpsPart(&pUnk)); + + while (m_pLiveness->IsAlive() && + pUnk != NULL) + { + IXpsDocument_t pDoc; + IFixedDocumentSequence_t pFDS; + IFixedDocument_t pFD; + IFixedPage_t pFP; + + if (SUCCEEDED(pUnk.QueryInterface(&pFP))) + { + DoTraceMessage(XPSRASFILTER_TRACE_VERBOSE, L"Handling a Page"); + + pPrintTicketHandler->ProcessPart(pFP); + + ParametersFromPrintTicket printTicketParams = + pPrintTicketHandler->GetMergedPrintTicketParams(); + + pRasInterface->RasterizePage( + CreateXpsOMPageFromIFixedPage(pFP, pOMFactory, pOpcFactory), + printTicketParams, + m_pLiveness + ); + } + else if (SUCCEEDED(pUnk.QueryInterface(&pFD))) + { + pPrintTicketHandler->ProcessPart(pFD); + } + else if (SUCCEEDED(pUnk.QueryInterface(&pFDS))) + { + pPrintTicketHandler->ProcessPart(pFDS); + } + else if (SUCCEEDED(pUnk.QueryInterface(&pDoc))) + { + // + // Do nothing with the XML Document part + // + } + else + { + // + // Any other document 'trunk' parts are ignored. + // + } + + pUnk.Release(); + + // + // Get Next Part + // + THROW_ON_FAILED_HRESULT(m_pReader->GetXpsPart(&pUnk)); + } + + pRasInterface->FinishRasterization(); +} + +} // namespace xpsrasfilter + diff --git a/print/XpsRasFilter/src/xpsrasfilter.def b/print/XpsRasFilter/src/xpsrasfilter.def new file mode 100644 index 00000000..a3dac1cc --- /dev/null +++ b/print/XpsRasFilter/src/xpsrasfilter.def @@ -0,0 +1,22 @@ +; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +; ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +; THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +; PARTICULAR PURPOSE. +; +; Copyright (c) Microsoft Corporation. All rights reserved +; +; File Name: +; +; xpsrasfilter.def +; +; Abstract: +; +; Defines DLL exports for xpsrasfilter.dll +; + +LIBRARY "xpsrasfilter.dll" + +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + diff --git a/print/XpsRasFilter/src/xpsrasfilter.h b/print/XpsRasFilter/src/xpsrasfilter.h new file mode 100644 index 00000000..2ad3a4f9 --- /dev/null +++ b/print/XpsRasFilter/src/xpsrasfilter.h @@ -0,0 +1,141 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// xpsrasfilter.h +// +// Abstract: +// +// Xps Rasterization Service sample filter definition. The +// XPSRasFilter provides the interface to the filter pipeline manager. +// + +#pragma once + +namespace xpsrasfilter +{ + +// +// Class to maintain the shared state of operation between multiple components +// of the filter. Also provides the callback for the Xps Rasterization Service. +// +class FilterLiveness : public UnknownBase +{ +public: + + FilterLiveness() : + m_isAlive(TRUE) + {} + + virtual + ~FilterLiveness() {} + + BOOL + IsAlive() + { + return m_isAlive; + } + + void + Cancel() + { + // + // May be called from a different thread + // + m_isAlive = FALSE; + } + + // + // IXpsRasterizerNotificationCallback Method + // + virtual _Must_inspect_result_ + HRESULT STDMETHODCALLTYPE + Continue() + { + return (m_isAlive) ? (S_OK) : (HRESULT_FROM_WIN32(ERROR_PRINT_CANCELLED)); + } + +private: + volatile BOOL m_isAlive; + + // + // prevent copy semantics + // + FilterLiveness(const FilterLiveness&); + FilterLiveness& operator=(const FilterLiveness&); +}; + +class XPSRasFilter : public UnknownBase +{ + +public: + + static LONG ms_numObjects; // Number of instances of XPSRasFilter + + XPSRasFilter(); + + virtual + ~XPSRasFilter(); + + // + // IPrintPipelineFilter Methods + // + virtual _Must_inspect_result_ + HRESULT STDMETHODCALLTYPE + InitializeFilter( + _In_ IInterFilterCommunicator *pICommunicator, + _In_ IPrintPipelinePropertyBag *pIPropertyBag, + _In_ IPrintPipelineManagerControl *pIPipelineControl + ); + + virtual _Must_inspect_result_ + HRESULT STDMETHODCALLTYPE + ShutdownOperation(); + + virtual _Must_inspect_result_ + HRESULT STDMETHODCALLTYPE + StartOperation(); + +private: + // + // prevent copy semantics + // + XPSRasFilter(const XPSRasFilter&); + XPSRasFilter& operator=(const XPSRasFilter&); + + // + // Xps package part reader + // + IXpsDocumentProvider_t m_pReader; + IPrintWriteStream_t m_pWriter; + + // + // Pipeline Property Bag + // + IPrintPipelinePropertyBag_t m_pIPropertyBag; + + // + // IPrintPipelineFilter Methods (throwing) + // + VOID + StartOperation_throws(); + + VOID + InitializeFilter_throws( + const IInterFilterCommunicator_t &pICommunicator, + const IPrintPipelinePropertyBag_t &pIPropertyBag + ); + + // + // Keeps track of whether the operation has been cancelled + // + FilterLiveness_t m_pLiveness; +}; + +} // namespace xpsrasfilter + diff --git a/print/XpsRasFilter/src/xpsrasfilter.rc b/print/XpsRasFilter/src/xpsrasfilter.rc new file mode 100644 index 00000000..d7f2fc40 --- /dev/null +++ b/print/XpsRasFilter/src/xpsrasfilter.rc @@ -0,0 +1,28 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// File Name: +// +// xpsrasfilter.rc +// +// Abstract: +// +// Xps Rasterization Service filter resource file. +// + +#include +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "XPS Rasterization Service WDK Sample Filter" +#define VER_INTERNALNAME_STR "xpsrasfilter.dll" +#define VER_ORIGINALFILENAME_STR "xpsrasfilter.dll" +#define VER_FILEVERSION 0, 3, VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE + +#include "common.ver" + diff --git a/print/XpsRasFilter/src/xpsrasfilter.vcxproj b/print/XpsRasFilter/src/xpsrasfilter.vcxproj new file mode 100644 index 00000000..48f9b603 --- /dev/null +++ b/print/XpsRasFilter/src/xpsrasfilter.vcxproj @@ -0,0 +1,349 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {41965435-F4B0-495B-B669-2291F27F56B5} + $(MSBuildProjectName) + Debug + Win32 + {47DD7292-F334-4FEC-B539-B5D84A74585B} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + true + .cpp.cxx.h.hxx.inl + .h.hxx.inl + XpsRasFilter + true + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + true + .cpp.cxx.h.hxx.inl + .h.hxx.inl + XpsRasFilter + true + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + true + .cpp.cxx.h.hxx.inl + .h.hxx.inl + XpsRasFilter + true + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + true + .cpp.cxx.h.hxx.inl + .h.hxx.inl + XpsRasFilter + true + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + true + .cpp.cxx.h.hxx.inl + .h.hxx.inl + XpsRasFilter + true + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + true + .cpp.cxx.h.hxx.inl + .h.hxx.inl + XpsRasFilter + true + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + true + .cpp.cxx.h.hxx.inl + .h.hxx.inl + XpsRasFilter + true + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + true + .cpp.cxx.h.hxx.inl + .h.hxx.inl + XpsRasFilter + true + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + true + .cpp.cxx.h.hxx.inl + .h.hxx.inl + XpsRasFilter + true + + + + xpsrasfilter + + + xpsrasfilter + + + xpsrasfilter + + + xpsrasfilter + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);uuid.lib;prntvpt.lib;Kernel32.lib;winspool.lib;ole32.lib;oleaut32.lib;Advapi32.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);uuid.lib;prntvpt.lib;Kernel32.lib;winspool.lib;ole32.lib;oleaut32.lib;Advapi32.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);uuid.lib;prntvpt.lib;Kernel32.lib;winspool.lib;ole32.lib;oleaut32.lib;Advapi32.lib + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);uuid.lib;prntvpt.lib;Kernel32.lib;winspool.lib;ole32.lib;oleaut32.lib;Advapi32.lib + + + + Dynamic + + + Dynamic + + + Dynamic + + + Dynamic + + + + Sync + true + Level4 + + + + + Sync + true + Level4 + + + + + Sync + true + Level4 + + + + + Sync + true + Level4 + + + + + xpsrasfilter.def + + + + + xpsrasfilter.def + + + + + xpsrasfilter.def + + + + + xpsrasfilter.def + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/print/XpsRasFilter/src/xpsrasfilter.vcxproj.Filters b/print/XpsRasFilter/src/xpsrasfilter.vcxproj.Filters new file mode 100644 index 00000000..fdfc8758 --- /dev/null +++ b/print/XpsRasFilter/src/xpsrasfilter.vcxproj.Filters @@ -0,0 +1,54 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {10F00C97-F803-43B8-994A-022AE3AA52E4} + + + h;hpp;hxx;hm;inl;inc;xsd + {0422AF40-0146-47B2-BF48-C80A11C4F7F2} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {1F07CF73-346E-4FF4-80B6-BC92C1A72B62} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/print/cpsuisam/CPSUIDAT.C b/print/cpsuisam/CPSUIDAT.C new file mode 100644 index 00000000..c3854001 --- /dev/null +++ b/print/cpsuisam/CPSUIDAT.C @@ -0,0 +1,1331 @@ +/*++ + +Copyright (c) 1990-2003 Microsoft Corporation +All Rights Reserved + + +Module Name: + + cpsuidat.c + + +Abstract: + + This module contains all the predefined data + + +--*/ + + +#include "precomp.h" +#pragma hdrstop + +#define DBG_CPSUIFILENAME DbgTVPage + + + +#define DBG_TVTESTCB 0x00000001 +#define DBG_PUSHDLGPROC 0x00000002 + +DEFINE_DBGVAR(0); + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + + +HINSTANCE hInstApp = NULL; + +TCHAR TitleName[] = TEXT("Common Property Sheet UI Sample"); +TCHAR ClassName[] = TEXT("CPSUISampleClass"); +TCHAR MenuName[] = TEXT("CPSUISampleMenu"); +TCHAR OptItemName[] = TEXT("CPSUI TreeView Sample"); +TCHAR szWinSpool[] = TEXT("WinSpool.Drv"); +CHAR szDocPropSheets[] = "DocumentPropertySheets"; + +BOOL UpdatePermission = TRUE; +BOOL UseStdAbout = TRUE; + + +INT_PTR +CALLBACK +PushButtonProc( + HWND hDlg, + UINT Msg, + WPARAM wParam, + LPARAM lParam + ); + +CPSUICALLBACK +TVTestCallBack( + _In_ PCPSUICBPARAM pCPSUICBParam + ); + + +OPTPARAM NoYesOP[] = { + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + (LPTSTR)IDS_CPSUI_NO, // pData + IDI_CPSUI_NO, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + (LPTSTR)IDS_CPSUI_YES, // pData + IDI_CPSUI_YES, // IconID + 1 // lParam + } +}; + +EXTCHKBOX ECB_EP_ECB = { + + sizeof(EXTCHKBOX), + 0, + TEXT("Include Icon"), + (LPTSTR)IDS_CPSUI_SLASH_SEP, + TEXT("Icon"), + IDI_CPSUI_GRAPHIC }; + + +OPTPARAM ECB_EP_OP[] = { + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("None"), // pData + IDI_CPSUI_EMPTY, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("Extended CheckBox"), // pData + IDI_CPSUI_EMPTY, // IconID + 1 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("Extended Push"), // pData + IDI_CPSUI_EMPTY, // IconID + 0 // lParam + } +}; + + +OPTTYPE ECB_EP_OT = { + + sizeof(OPTTYPE), // cbSize + TVOT_3STATES, // Type + 0, // Flags OPTTF_xxxx + 3, // Count + 0, // BegCtrlID + ECB_EP_OP, // pOptParam + 0 // Style, OTS_xxxx + }; + +OPTTYPE NoYesOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_2STATES, // Type + 0, // Flags OPTTF_xxxx + 2, // Count + 0, // BegCtrlID + NoYesOP, // pOptParam + 0 // Style, OTS_xxxx + }; + +OPTPARAM TVOT3StatesOP[] = { + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("States 1"), // pData + IDI_CPSUI_PORTRAIT, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("States 2"), // pData + IDI_CPSUI_LANDSCAPE, // IconID + 1 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("States 3"), // pData + IDI_CPSUI_ROT_LAND, // IconID + 0 // lParam + } +}; + +OPTTYPE TVOT2StatesOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_2STATES, // Type + 0, // Flags OPTTF_xxxx + 2, // Count + 0, // BegCtrlID + TVOT3StatesOP, // pOptParam + 0 // Style, OTS_xxxx + }; + +OPTTYPE TVOT3StatesOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_3STATES, // Type + 0, // Flags OPTTF_xxxx + 3, // Count + 0, // BegCtrlID + TVOT3StatesOP, // pOptParam + 0 // Style, OTS_xxxx + }; + +OPTPARAM MinRangeOP[] = { + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT(""), // pData (postfix) + IDI_CPSUI_EMPTY, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + NULL, // pData (help line) + (DWORD)MIN_INT, // IconID + MAX_INT // lParam + } +}; + +OPTTYPE MinRangeOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_UDARROW, // Type + 0, // Flags OPTTF_xxxx + 2, // Count + 0, // BegCtrlID + MinRangeOP, // pOptParam + 0 // Style, OTS_xxxx + }; + +OPTPARAM MaxRangeOP[] = { + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT(""), // pData (postfix) + IDI_CPSUI_EMPTY, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + NULL, // pData (help line) + (DWORD)MIN_INT, // IconID + MAX_INT // lParam + } +}; + +OPTTYPE MaxRangeOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_UDARROW, // Type + 0, // Flags OPTTF_xxxx + 2, // Count + 0, // BegCtrlID + MaxRangeOP, // pOptParam + 0 // Style, OTS_xxxx + }; + + +OPTPARAM MinMaxRangeOP[] = { + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("%"), // pData (postfix) + IDI_CPSUI_SCALING, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + NULL, // pData (help line) + (DWORD)MIN_INT, // IconID + MAX_INT // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + NULL, // pData (help line) + 2, // IconID + 50 // lParam + } +}; + +OPTTYPE TVOTUDArrowOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_UDARROW, // Type + 0, // Flags OPTTF_xxxx + 2, // Count + 0, // BegCtrlID + MinMaxRangeOP, // pOptParam + 0 // Style, OTS_xxxx + }; + +OPTTYPE TVOTTrackBarOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_TRACKBAR, // Type + 0, // Flags OPTTF_xxxx + 3, // Count + 0, // BegCtrlID + MinMaxRangeOP, // pOptParam + 0 // Style, OTS_xxxx + }; + +OPTTYPE TVOTScrollBarOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_SCROLLBAR, // Type + 0, // Flags OPTTF_xxxx + 3, // Count + 0, // BegCtrlID + MinMaxRangeOP, // pOptParam + 0 // Style, OTS_xxxx + }; + +OPTPARAM TVOTLBCBOP[] = { + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + (LPTSTR)IDS_CPSUI_NOTINSTALLED, // pData + IDI_CPSUI_SEL_NONE, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT(" 1 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT(" 2 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT(" 4 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT(" 6 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT(" 8 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("10 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("12 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("14 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("16 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("18 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("20 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("24 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("32 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("48 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("64 MB"), // pData + IDI_CPSUI_MEM, // IconID + 0 // lParam + }, +}; + + +OPTTYPE TVOTListBoxOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_LISTBOX, // Type + 0, // Flags OPTTF_xxxx + COUNT_ARRAY(TVOTLBCBOP), // Count + 0, // BegCtrlID + TVOTLBCBOP, // pOptParam + 0 // Style, OTS_xxxx + }; + + +OPTTYPE TVOTComboBoxOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_COMBOBOX, // Type + 0, // Flags OPTTF_xxxx + COUNT_ARRAY(TVOTLBCBOP), // Count + 0, // BegCtrlID + TVOTLBCBOP, // pOptParam + 0 // Style, OTS_xxxx + }; + +TCHAR TVOTEditBoxBuf[128] = TEXT("Confidential"); + +OPTPARAM TVOTEditBoxOP[] = { + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("*Mark*"), // pData + IDI_CPSUI_WATERMARK, // IconID + 0 // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("Type in WaterMark text"), // pData + COUNT_ARRAY(TVOTEditBoxBuf), // IconID + 0 // lParam + } +}; + + +OPTTYPE TVOTEditBoxOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_EDITBOX, // Type + 0, // Flags OPTTF_xxxx + 2, // Count + 0, // BegCtrlID + TVOTEditBoxOP, // pOptParam + 0 // Style, OTS_xxxx + }; + +OPTPARAM TVOTChkBoxOP[] = { + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + CHKBOXS_FALSE_TRUE, // style + TEXT("Let's do it!"), // pData + IDI_CPSUI_TELEPHONE, // IconID + 0 // lParam + } +}; + + + +OPTTYPE TVOTChkBoxOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_CHKBOX, // Type + 0, // Flags OPTTF_xxxx + 1, // Count + 0, // BegCtrlID + TVOTChkBoxOP, // pOptParam + 0 // Style, OTS_xxxx + }; + + +OPTPARAM ChkBoxTypeOP[] = { + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("CHKBOXS_FALSE_TRUE"), // pData + IDI_CPSUI_EMPTY, // IconID + CHKBOXS_FALSE_TRUE // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("CHKBOXS_NO_YES"), // pData + IDI_CPSUI_EMPTY, // IconID + CHKBOXS_NO_YES // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("CHKBOXS_OFF_ON"), // pData + IDI_CPSUI_EMPTY, // IconID + CHKBOXS_OFF_ON // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("CHKBOXS_FALSE_PDATA"), // pData + IDI_CPSUI_EMPTY, // IconID + CHKBOXS_FALSE_PDATA // lParam + }, + + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("CHKBOXS_NO_PDATA"), // pData + IDI_CPSUI_EMPTY, // IconID + CHKBOXS_NO_PDATA // lParam + }, + + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("CHKBOXS_OFF_PDATA"), // pData + IDI_CPSUI_EMPTY, // IconID + CHKBOXS_OFF_PDATA // lParam + }, + + { + sizeof(OPTPARAM), // cbSize + 0, // OPTPF_xxx + 0, // style + TEXT("CHKBOXS_NONE_PDATA"), // pData + IDI_CPSUI_EMPTY, // IconID + CHKBOXS_NONE_PDATA // lParam + } +}; + + +OPTTYPE ChkBoxTypeOT = { + + sizeof(OPTTYPE), // cbSize + TVOT_LISTBOX, // Type + 0, // Flags OPTTF_xxxx + COUNT_ARRAY(ChkBoxTypeOP), // Count + 0, // BegCtrlID + ChkBoxTypeOP, // pOptParam + 0 // Style, OTS_xxxx + }; + +EXTCHKBOX TV_ECB = { + + sizeof(EXTCHKBOX), + ECBF_OVERLAY_WARNING_ICON, + TEXT("Extended CheckBox Test"), + (LPTSTR)IDS_CPSUI_SLASH_SEP, + TEXT("Got Checked!!!"), + IDI_APPLE }; + +EXTPUSH TV_EP = { + + sizeof(EXTPUSH), + 0, + TEXT("Extended Push Test"), + NULL, //DLGPROC + IDI_APPLE, + 0 }; + + + +OPTITEM TVTestOptItems[] = { + + { sizeof(OPTITEM), LEVEL_0, 0, OPTIF_NONE, 0, + (LPTSTR)TEXT("TreeView Test"), IDI_CPSUI_QUESTION, NULL, + NULL, 0, DMPUB_TVTEST }, + + { sizeof(OPTITEM), LEVEL_1, 0, OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("CPSUI Options"), 0, NULL, + NULL, 0, DMPUB_TVOPT }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Extended Type"), 0, &ECB_EP_ECB, + &ECB_EP_OT, 0, DMPUB_TVOPT_ECB_EP }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Disabled Extended"), 0, NULL, + &NoYesOT, 0, DMPUB_EXT_DISABLED }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Overlay 'Warning' Icon"), 0, NULL, + &NoYesOT, 0, DMPUB_EXT_OVERLAY_WARNING }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Overlay 'Stop' Icon"), 0, NULL, + &NoYesOT, 0, DMPUB_EXT_OVERLAY_STOP }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Overlay 'No' Icon"), 0, NULL, + &NoYesOT, 0, DMPUB_EXT_OVERLAY_NO }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("OptItem/OptType"), 0, NULL, + NULL, 0, DMPUB_TVOPT }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Disabled OptType"), 0, NULL, + &NoYesOT, 0, DMPUB_TVOPT_DISABLED }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Overlay 'Warning' Icon"), 0, NULL, + &NoYesOT, 0, DMPUB_TVOPT_OVERLAY_WARNING }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Overlay 'Stop' Icon"), 0, NULL, + &NoYesOT, 0, DMPUB_TVOPT_OVERLAY_STOP }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Overlay 'No' Icon"), 0, NULL, + &NoYesOT, 0, DMPUB_TVOPT_OVERLAY_NO }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("ScrollBar/TrackBar Ranges"), 0, NULL, + NULL, 0, DMPUB_TVOPT }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + TEXT("Min Range"), MIN_INT, NULL, + &MinRangeOT, 0, DMPUB_MINRANGE }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + TEXT("Max. Range"), MAX_INT, NULL, + &MaxRangeOT, 0, DMPUB_MAXRANGE }, + + { sizeof(OPTITEM), LEVEL_1, 0, OPTIF_NONE, 0, + TEXT("TVOT Test"), IDI_CPSUI_QUESTION, NULL, + NULL, 0, DMPUB_TVOPT_TEST }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_CALLBACK, 0, + (LPTSTR)TEXT("TVOT_2STATES"), 0, NULL, + &TVOT2StatesOT, 0, DMPUB_2STATES }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_CALLBACK, 0, + TEXT("TVOT_3STATES"), 0, NULL, + &TVOT3StatesOT, 0, DMPUB_3STATES }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_CALLBACK, 0, + TEXT("TVOT_UDARROW"), 55, NULL, + &TVOTUDArrowOT, 0, DMPUB_UDARROW }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_CALLBACK, 0, + TEXT("TVOT_TRACKBAR"), 100, NULL, + &TVOTTrackBarOT, 0, DMPUB_TRACKBAR }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_CALLBACK, 0, + TEXT("TVOT_SCROLLBAR"), 210, NULL, + &TVOTScrollBarOT, 0, DMPUB_SCROLLBAR }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_CALLBACK, 0, + TEXT("TVOT_LISTBOX"), 2, NULL, + &TVOTListBoxOT, 0, DMPUB_LISTBOX }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Include 16x16 Icons"), 1, NULL, + &NoYesOT, 0, DMPUB_TVOPT_ICONS }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_CALLBACK, 0, + TEXT("TVOT_COMBOBOX"), 4, NULL, + &TVOTComboBoxOT, 0, DMPUB_COMBOBOX }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK | OPTIF_COLLAPSE, 0, + (LPTSTR)TEXT("Include 16x16 Icons"), 1, NULL, + &NoYesOT, 0, DMPUB_TVOPT_ICONS }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_CALLBACK, 0, + TEXT("TVOT_EDITBOX"), 0, NULL, // fill in TVOTEditBoxBuf later + &TVOTEditBoxOT, 0, DMPUB_EDITBOX }, + + { sizeof(OPTITEM), LEVEL_2, 0, OPTIF_CALLBACK, 0, + TEXT("TVOT_CHKBOX"), 0, NULL, + &TVOTChkBoxOT, 0, DMPUB_CHKBOX }, + + { sizeof(OPTITEM), LEVEL_3, 0, OPTIF_CALLBACK, 0, + TEXT("CheckBox Type"), 0, NULL, + &ChkBoxTypeOT, 0, DMPUB_CHKBOX_TYPE } + +}; + + + +INT_PTR +CALLBACK +PushButtonProc( + HWND hDlg, + UINT Msg, + WPARAM wParam, + LPARAM lParam + ) +{ + +#ifndef DBG + UNREFERENCED_PARAMETER(lParam); +#endif + + switch (Msg) { + + case WM_INITDIALOG: /* message: initialize dialog box */ + + CPSUIDBG(DBG_PUSHDLGPROC, + ("PushButtonProc lParam=%s", (LPSTR)lParam)); + break; + + case WM_COMMAND: + + switch(LOWORD(wParam)) { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, (BOOL)(LOWORD(wParam) == IDOK)); + return(TRUE); + } + } + + return(FALSE); +} + + + +INT_PTR +APIENTRY +MyAboutProc( + HWND hDlg, + UINT Msg, + WPARAM wParam, + LPARAM lParam + ) + +/*++ + +Routine Description: + + This is the about... callback which pop up appliation's own about + + +--*/ +{ + PCOMPROPSHEETUI pCPSUI = NULL; + TCHAR Buf[128] = {0}; + HRESULT hr = S_OK; + + switch (Msg) { + + case WM_INITDIALOG: /* message: initialize dialog box */ + + pCPSUI = (PCOMPROPSHEETUI)lParam; + + hr = pCPSUI ? S_OK : E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + hr = StringCchPrintf(Buf, ARRAY_SIZE(Buf), TEXT("About %s"), pCPSUI->pCallerName); + } + + if (SUCCEEDED(hr)) + { + hr = SetWindowText(hDlg, Buf) ? S_OK : E_FAIL; + } + + if (SUCCEEDED(hr)) + { + hr = StringCchPrintf(Buf, ARRAY_SIZE(Buf), + TEXT("%s Version %u.%u"), + pCPSUI->pCallerName, + (UINT)HIBYTE(pCPSUI->CallerVersion), + (UINT)LOBYTE(pCPSUI->CallerVersion)); + } + + if (SUCCEEDED(hr)) + { + hr = SetDlgItemText(hDlg, IDD_ABOUT1, Buf) ? S_OK : E_FAIL; + } + + if (SUCCEEDED(hr)) + { + hr = StringCchPrintf(Buf, ARRAY_SIZE(Buf), + TEXT("%s Version %u.%u"), + pCPSUI->pOptItemName, + (UINT)HIBYTE(pCPSUI->OptItemVersion), + (UINT)LOBYTE(pCPSUI->OptItemVersion)); + } + + if (SUCCEEDED(hr)) + { + hr = SetDlgItemText(hDlg, IDD_ABOUT2, Buf) ? S_OK : E_FAIL; + } + + break; + + case WM_COMMAND: + + switch(LOWORD(wParam)) { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, (BOOL)(LOWORD(wParam) == IDOK)); + return(TRUE); + } + } + + return (FALSE); +} + + + +POPTITEM +FindOptItem( + POPTITEM pOptItem, + UINT cOptItem, + BYTE DMPubID + ) +{ + while (cOptItem--) { + + if (pOptItem->DMPubID == DMPubID) { + + return(pOptItem); + } + + ++pOptItem; + } + + return(NULL); +} + + + +CPSUICALLBACK +TVTestCallBack( + _In_ PCPSUICBPARAM pCPSUICBParam + ) +{ + POPTITEM pCurItem = pCPSUICBParam->pCurItem; + POPTITEM pItem; + BYTE DMPubID; + LONG Sel; + LONG Action = CPSUICB_ACTION_NONE; + + + DMPubID = pCurItem->DMPubID; + Sel = pCurItem->Sel; + + switch (pCPSUICBParam->Reason) { + + case CPSUICB_REASON_OPTITEM_SETFOCUS: + + CPSUIDBG(DBG_TVTESTCB, ("Got OPTITEM_SETFOCUS CallBack now")); + + switch (DMPubID) + { + case DMPUB_MINRANGE: + { + pItem = FindOptItem(pCPSUICBParam->pOptItem, + pCPSUICBParam->cOptItem, + DMPUB_MAXRANGE); + + if (pItem) + { + pCurItem->pOptType->pOptParam[1].lParam = (LONG)pItem->Sel; + } + } + + break; + + case DMPUB_MAXRANGE: + { + pItem = FindOptItem(pCPSUICBParam->pOptItem, + pCPSUICBParam->cOptItem, + DMPUB_MINRANGE); + + if (pItem) + { + pCurItem->pOptType->pOptParam[1].IconID = (DWORD)pItem->Sel; + } + } + + break; + + } + + break; + + case CPSUICB_REASON_ABOUT: + + DialogBoxParam(hInstApp, + MAKEINTRESOURCE(DLGABOUT), + pCPSUICBParam->hDlg, + MyAboutProc, + (LPARAM)(pCPSUICBParam->pOldSel)); + + break; + + case CPSUICB_REASON_ECB_CHANGED: + + CPSUIDBG(DBG_TVTESTCB, ("Got ECB_CHANGED CallBack now")); + + switch (DMPubID) { + + case DMPUB_TVOPT_ECB_EP: + + if (pCurItem->Flags & OPTIF_ECB_CHECKED) { + + TV_EP.IconID = + TV_ECB.IconID = IDI_APPLE; + + } else { + + TV_EP.IconID = + TV_ECB.IconID = 0; + } + + Action = CPSUICB_ACTION_OPTIF_CHANGED; + break; + + default: + + break; + } + + break; + + case CPSUICB_REASON_SEL_CHANGED: + + CPSUIDBG(DBG_TVTESTCB, ("Got SEL_CHANGED CallBack now")); + + switch (DMPubID) + { + case DMPUB_CHKBOX_TYPE: + { + pItem = FindOptItem(pCPSUICBParam->pOptItem, + pCPSUICBParam->cOptItem, + DMPUB_CHKBOX); + + if (pItem) + { + pItem->pOptType->pOptParam[0].Style = (BYTE)pCurItem->Sel; + + pItem->Flags |= OPTIF_CHANGED; + Action = CPSUICB_ACTION_OPTIF_CHANGED; + } + } + break; + + case DMPUB_TVOPT_ICONS: + + pItem = pCurItem - 1; + + if (Sel) { + + pItem->pOptType->Style &= ~OTS_LBCB_NO_ICON16_IN_ITEM; + + } else { + + pItem->pOptType->Style |= OTS_LBCB_NO_ICON16_IN_ITEM; + } + + break; + + case DMPUB_EXT_OVERLAY_WARNING: + + if (Sel) { + + TV_EP.Flags |= EPF_OVERLAY_WARNING_ICON; + TV_ECB.Flags |= ECBF_OVERLAY_WARNING_ICON; + + } else { + + TV_EP.Flags &= ~EPF_OVERLAY_WARNING_ICON; + TV_ECB.Flags &= ~ECBF_OVERLAY_WARNING_ICON; + } + + break; + + case DMPUB_EXT_OVERLAY_STOP: + + if (Sel) { + + TV_EP.Flags |= EPF_OVERLAY_STOP_ICON; + TV_ECB.Flags |= ECBF_OVERLAY_STOP_ICON; + + } else { + + TV_EP.Flags &= ~EPF_OVERLAY_STOP_ICON; + TV_ECB.Flags &= ~ECBF_OVERLAY_STOP_ICON; + } + + break; + + case DMPUB_EXT_OVERLAY_NO: + + if (Sel) { + + TV_EP.Flags |= EPF_OVERLAY_NO_ICON; + TV_ECB.Flags |= ECBF_OVERLAY_NO_ICON; + + } else { + + TV_EP.Flags &= ~EPF_OVERLAY_NO_ICON; + TV_ECB.Flags &= ~ECBF_OVERLAY_NO_ICON; + } + + break; + + case DMPUB_MINRANGE: + + MinMaxRangeOP[1].IconID = (DWORD)Sel; + break; + + case DMPUB_MAXRANGE: + + MinMaxRangeOP[1].lParam = (LONG)Sel; + break; + + case DMPUB_TVOPT_OVERLAY_WARNING: + case DMPUB_TVOPT_OVERLAY_STOP: + case DMPUB_TVOPT_OVERLAY_NO: + + Action = CPSUICB_ACTION_REINIT_ITEMS; + + case DMPUB_TVOPT_ECB_EP: + + if (Sel) { + + pCurItem->Flags &= ~OPTIF_EXT_DISABLED; + + } else { + + pCurItem->Flags |= OPTIF_EXT_DISABLED; + } + + pCurItem->Flags |= OPTIF_CHANGED; + Action = CPSUICB_ACTION_OPTIF_CHANGED; + + // + // Fall through + // + + case DMPUB_TVOPT_DISABLED: + case DMPUB_EXT_DISABLED: + + pItem = FindOptItem(pCPSUICBParam->pOptItem, + pCPSUICBParam->cOptItem, + DMPUB_2STATES); + + if (pItem) { + + do { + + if ((pItem->DMPubID >= DMPUB_2STATES) && + (pItem->DMPubID <= DMPUB_CHKBOX)) { + + switch (DMPubID) { + + case DMPUB_TVOPT_OVERLAY_WARNING: + + if (Sel) { + + pItem->Flags |= OPTIF_OVERLAY_WARNING_ICON; + + } else { + + pItem->Flags &= ~OPTIF_OVERLAY_WARNING_ICON; + } + + break; + + case DMPUB_TVOPT_OVERLAY_STOP: + + if (Sel) { + + pItem->Flags |= OPTIF_OVERLAY_STOP_ICON; + + } else { + + pItem->Flags &= ~OPTIF_OVERLAY_STOP_ICON; + } + + break; + + case DMPUB_TVOPT_OVERLAY_NO: + + if (Sel) { + + pItem->Flags |= OPTIF_OVERLAY_NO_ICON; + + } else { + + pItem->Flags &= ~OPTIF_OVERLAY_NO_ICON; + } + + break; + + case DMPUB_TVOPT_DISABLED: + + if (Sel) { + + pItem->pOptType->Flags |= OPTTF_TYPE_DISABLED; + + } else { + + pItem->pOptType->Flags &= ~OPTTF_TYPE_DISABLED; + } + + break; + + case DMPUB_EXT_DISABLED: + + if (Sel) { + + pItem->Flags |= OPTIF_EXT_DISABLED; + + } else { + + pItem->Flags &= ~OPTIF_EXT_DISABLED; + } + + break; + + case DMPUB_TVOPT_ECB_EP: + + switch (Sel) { + + case 0: + + pItem->Flags |= OPTIF_EXT_HIDE; + break; + + case 1: + + pItem->Flags &= ~(OPTIF_EXT_HIDE | + OPTIF_EXT_IS_EXTPUSH); + pItem->pExtChkBox = &TV_ECB; + + break; + + case 2: + + pItem->Flags &= ~OPTIF_EXT_HIDE; + pItem->Flags |= OPTIF_EXT_IS_EXTPUSH; + pItem->pExtPush = &TV_EP; + break; + } + } + } + + pItem->Flags |= OPTIF_CHANGED; + + } while ((pItem++)->DMPubID != DMPUB_CHKBOX); + } + + break; + + default: + + break; + } + + break; + + case CPSUICB_REASON_PUSHBUTTON: + + CPSUIDBG(DBG_TVTESTCB, ("Got PUSH Button CallBack now")); + break; + + case CPSUICB_REASON_EXTPUSH: + + DialogBoxParam(hInstApp, + MAKEINTRESOURCE(EXTPUSH_DLG), + pCPSUICBParam->hDlg, + PushButtonProc, + (LPARAM)(pCPSUICBParam->pOldSel)); + break; + + default: + + break; + } + + return(Action); +} + + + +BOOL +SetupComPropSheetUI( + _In_ PCOMPROPSHEETUI pCPSUI + ) +{ + static BOOL UpdateEditBox = TRUE; + UINT i; + + + ZeroMemory(pCPSUI, sizeof(COMPROPSHEETUI)); + + pCPSUI->cbSize = sizeof(COMPROPSHEETUI); + pCPSUI->hInstCaller = hInstApp; + pCPSUI->pCallerName = (LPTSTR)TitleName; + pCPSUI->CallerVersion = 0x100; + pCPSUI->pOptItemName = (LPTSTR)OptItemName; + pCPSUI->OptItemVersion = 0x400; + pCPSUI->UserData = (ULONG_PTR)pCPSUI; + pCPSUI->pHelpFile = (LPTSTR)TEXT("CPSUISam.hlp"); + pCPSUI->pfnCallBack = TVTestCallBack; + pCPSUI->pOptItem = TVTestOptItems; + pCPSUI->cOptItem = COUNT_ARRAY(TVTestOptItems); + pCPSUI->Flags = 0; + pCPSUI->pDlgPage = CPSUI_PDLGPAGE_TREEVIWONLY; + pCPSUI->cDlgPage = 0; + + + if (UpdateEditBox) { + + // + // Fixup compiler warning for the LPSTR and LONG + // + + for (i = 0; i < COUNT_ARRAY(TVTestOptItems); i++) { + + if (TVTestOptItems[i].DMPubID == DMPUB_EDITBOX) { + + TVTestOptItems[i].pSel = (LPTSTR)TVOTEditBoxBuf; + UpdateEditBox = FALSE; + break; + } + } + } + + if (UpdatePermission) { + + pCPSUI->Flags |= CPSUIF_UPDATE_PERMISSION; + } + + if (!UseStdAbout) { + + pCPSUI->Flags |= CPSUIF_ABOUT_CALLBACK; + } + + return(TRUE); +} diff --git a/print/cpsuisam/CPSUIDAT.H b/print/cpsuisam/CPSUIDAT.H new file mode 100644 index 00000000..7043d9c2 --- /dev/null +++ b/print/cpsuisam/CPSUIDAT.H @@ -0,0 +1,73 @@ +/*++ + +Copyright (c) 1990-2003 Microsoft Corporation +All Rights Reserved + + +Module Name: + + cpsuidat.h + + +Abstract: + + This module + + +[Environment:] + + Windows 2000, Windows XP, Windows Server 2003 - Common Printer Driver UI DLL + + +--*/ + + +#define COUNT_ARRAY(a) (sizeof(a) / sizeof((a)[0])) + + +#define OPTIF_NONE 0 + +#define LEVEL_0 0 +#define LEVEL_1 1 +#define LEVEL_2 2 +#define LEVEL_3 3 +#define LEVEL_4 4 + + +#define DMPUB_TVTEST (DMPUB_USER + 1) +#define DMPUB_TVOPT (DMPUB_USER + 2) +#define DMPUB_TVOPT_ECB_EP (DMPUB_USER + 3) +#define DMPUB_TVOPT_ICONS (DMPUB_USER + 4) +#define DMPUB_TVOPT_DISABLED (DMPUB_USER + 5) +#define DMPUB_TVOPT_TEST (DMPUB_USER + 6) +#define DMPUB_2STATES (DMPUB_USER + 7) +#define DMPUB_3STATES (DMPUB_USER + 8) +#define DMPUB_UDARROW (DMPUB_USER + 9) +#define DMPUB_TRACKBAR (DMPUB_USER + 10) +#define DMPUB_SCROLLBAR (DMPUB_USER + 11) +#define DMPUB_LISTBOX (DMPUB_USER + 12) +#define DMPUB_COMBOBOX (DMPUB_USER + 13) +#define DMPUB_EDITBOX (DMPUB_USER + 14) +#define DMPUB_PUSHBUTTON (DMPUB_USER + 15) +#define DMPUB_CHKBOX (DMPUB_USER + 16) +#define DMPUB_CHKBOX_TYPE (DMPUB_USER + 17) +#define DMPUB_TVOPT_OVERLAY_WARNING (DMPUB_USER + 18) +#define DMPUB_TVOPT_OVERLAY_STOP (DMPUB_USER + 19) +#define DMPUB_TVOPT_OVERLAY_NO (DMPUB_USER + 20) +#define DMPUB_EXT_OVERLAY_WARNING (DMPUB_USER + 21) +#define DMPUB_EXT_OVERLAY_STOP (DMPUB_USER + 22) +#define DMPUB_EXT_OVERLAY_NO (DMPUB_USER + 23) +#define DMPUB_EXT_DISABLED (DMPUB_USER + 24) +#define DMPUB_MINRANGE (DMPUB_USER + 25) +#define DMPUB_MAXRANGE (DMPUB_USER + 26) + +#define MAX_INT 0x00007fff +#define MIN_INT 0xFFFF8000 + + + +BOOL +SetupComPropSheetUI +( + _In_ PCOMPROPSHEETUI pCPSUI +); diff --git a/print/cpsuisam/CPSUISAM.DEF b/print/cpsuisam/CPSUISAM.DEF new file mode 100644 index 00000000..f254e960 --- /dev/null +++ b/print/cpsuisam/CPSUISAM.DEF @@ -0,0 +1,6 @@ +; module-definition file for SurPtrUI -- used by LINK.EXE + +NAME CPSUI ; application's module name + +DESCRIPTION 'Microsoft Windows 2000/Windows XP/Windows Server 2003 Common Property Sheet UI Sample Program' + diff --git a/print/cpsuisam/CPSUISAM.DLG b/print/cpsuisam/CPSUISAM.DLG new file mode 100644 index 00000000..29112c5d --- /dev/null +++ b/print/cpsuisam/CPSUISAM.DLG @@ -0,0 +1,35 @@ +1 DLGINCLUDE "cpsuisam.h" + +PUSHBUTTON_DLG DIALOGEX 9, 18, 160, 100 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Push Button Dialog" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "The TVOT_PUSHBUTTON's Button is Pushed. Press OK to Continue...", + -1, 10, 21, 141, 37 + DEFPUSHBUTTON "OK", IDOK, 88, 78, 63, 14 +END + +DLGABOUT DIALOGEX 5, 15, 200, 90 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "About 1 Text", IDD_ABOUT1, 38, 12, 152, 20 + LTEXT "About 2 Text", IDD_ABOUT2, 38, 36, 152, 24 + DEFPUSHBUTTON "OK", IDOK, 126, 68, 59, 14 + ICON IDI_APPLE, IDD_ABOUTICON, 8, 20, 18, 20 +END + +EXTPUSH_DLG DIALOGEX 6, 18, 214, 78 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Extended Push Button DIALOG BOX" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "You just push the 'Extended Push Test' push button. To Continue press OK button...", + -1, 12, 10, 187, 30 + DEFPUSHBUTTON "OK", IDOK, 129, 54, 69, 14 +END diff --git a/print/cpsuisam/CPSUISAM.H b/print/cpsuisam/CPSUISAM.H new file mode 100644 index 00000000..bcddc730 --- /dev/null +++ b/print/cpsuisam/CPSUISAM.H @@ -0,0 +1,37 @@ +/*++ + +Copyright (c) 1990-2003 Microsoft Corporation +All Rights Reserved + + +Module Name: + + cpsuisam.h + + +Abstract: + + This module is the header file for the sample + + +[Environment:] + + +--*/ + + +#define IDM_PERMISSION 10 +#define IDM_USESTDABOUT 20 +#define IDM_DOCPROP 30 +#define IDM_TVTEST 40 +#define IDM_DOCPROP_TVTEST 50 + +#define IDI_CPSUISAMPLE 500 +#define IDI_APPLE 510 + +#define PUSHBUTTON_DLG 1000 +#define DLGABOUT 1010 +#define EXTPUSH_DLG 1020 +#define IDD_ABOUT1 201 +#define IDD_ABOUT2 202 +#define IDD_ABOUTICON 203 diff --git a/print/cpsuisam/DEBUG.C b/print/cpsuisam/DEBUG.C new file mode 100644 index 00000000..48853686 --- /dev/null +++ b/print/cpsuisam/DEBUG.C @@ -0,0 +1,114 @@ +/*++ + +Copyright (c) 1990-2003 Microsoft Corporation +All Rights Reserved + +Module Name: + + debug.c + +Abstract: + + This module contains all debugging routines + +[Environment:] + + NT Windows - Common Printer Driver UI DLL. + +[Notes:] + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if DBG + +BOOL DoCPSUIWarn = TRUE; + +VOID +cdecl +CPSUIDbgPrint +( + LPCSTR pszFormat, + ... +) +/*++ + +Routine Description: + + This fucntion output the debug informat to the debugger + +Arguments: + + pszFormat - format string + + ... - variable data + +Return Value: + + VOID + +--*/ +{ + va_list vaList; + static TCHAR OutBuf[768]; +#ifdef UNICODE + static WCHAR FormatBuf[256]; +#endif + // + // We assume that UNICODE flag is turn on for the compilation, bug the + // format string passed to here is ASCII version, so we need to convert + // it to LPWSTR before the wvsprintf() + // + + va_start(vaList, pszFormat); + +#ifdef UNICODE + MultiByteToWideChar(CP_ACP, 0, pszFormat, -1, FormatBuf, COUNT_ARRAY(FormatBuf)); + StringCchVPrintf(OutBuf, COUNT_ARRAY(OutBuf), FormatBuf, vaList); +#else + StringCchVPrintf(OutBuf, COUNT_ARRAY(OutBuf), pszFormat, vaList); +#endif + va_end(vaList); + + OutputDebugString((LPTSTR)OutBuf); + OutputDebugString(TEXT("\n")); +} + +VOID +CPSUIDbgType +( + INT Type +) +/*++ + +Routine Description: + + this function output the ERROR/WARNING message + +Arguments: + + Type + +Return Value: + +--*/ +{ + static TCHAR DebugDLLName[] = TEXT("SurPtrUI"); + + if (Type < 0) + { + OutputDebugString(TEXT("ERROR) ")); + } + else if (Type > 0) + { + OutputDebugString(TEXT("WARNING: ")); + } + OutputDebugString(DebugDLLName); + OutputDebugString(TEXT("!")); +} + +#endif // DBG diff --git a/print/cpsuisam/DEBUG.H b/print/cpsuisam/DEBUG.H new file mode 100644 index 00000000..5d6b88a3 --- /dev/null +++ b/print/cpsuisam/DEBUG.H @@ -0,0 +1,66 @@ +/*++ + +Copyright (c) 1990-2003 Microsoft Corporation +All Rights Reserved + + +Module Name: + + debug.h + + +Abstract: + + This module contains all debugger definitions + + +[Environment:] + + NT Windows - Common Printer Driver UI DLL. + + +--*/ + + +#if DBG + +VOID +cdecl +CPSUIDbgPrint +( + LPCSTR pszFormat, + ... +); + +VOID +CPSUIDbgType +( + INT Type +); + +extern BOOL DoCPSUIWarn; + +#define DBGP(x) (CPSUIDbgPrint x) + +#define DEFINE_DBGVAR(x) DWORD DBG_CPSUIFILENAME=(x) + +#define CPSUIDBG(x,y) if((x)&DBG_CPSUIFILENAME){CPSUIDbgType(0);DBGP(y);} + +#define CPSUIDBGBLK(x) x; +#define CPSUIWARN(x) if(DoCPSUIWarn) { CPSUIDbgType(1);DBGP(x); } +#define CPSUIERR(x) CPSUIDbgType(-1);DBGP(x) +#define CPSUIRIP(x) CPSUIERR(x); DebugBreak() +#define CPSUIASSERT(b,x,e,i) \ + if (!(e)) { _CPSUIAssert(x,#e,__FILE__,(UINT)__LINE__,(DWORD)i,b); } + +#else // DBG + +#define CPSUIDBGBLK(x) +#define DEFINE_DBGVAR(x) +#define CPSUIDBG(x,y) +#define CPSUIWARN(x) +#define CPSUIERR(x) +#define CPSUIRIP(x) +#define CPSUIASSERT(b,x,e,i) + +#endif // DBG diff --git a/print/cpsuisam/PRECOMP.H b/print/cpsuisam/PRECOMP.H new file mode 100644 index 00000000..4e63995b --- /dev/null +++ b/print/cpsuisam/PRECOMP.H @@ -0,0 +1,17 @@ +// +// Copyright (c) 1990-2003 Microsoft Corporation +// All Rights Reserved +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debug.h" +#include "cpsuisam.h" +#include "cpsuidat.h" diff --git a/print/cpsuisam/ReadMe.md b/print/cpsuisam/ReadMe.md new file mode 100644 index 00000000..a4fddfb9 --- /dev/null +++ b/print/cpsuisam/ReadMe.md @@ -0,0 +1,13 @@ +Common Property Sheet UI Sample +=============================== + +The CPSUISAM application causes the Common Property Sheet User Interface (CPSUI) to call the Windows print spooler to create property sheet pages for the system's default printer. + +Printer interface DLLs must not perform this action and this sample shows how create property sheet pages for a printer. + +The application then creates an additional property sheet page to illustrate some of the techniques that you can use when you are using CPSUI to create a new page. + +CPSUI is a user-mode DLL that enables you to create property sheet pages that have a standard appearance. + +CPSUIAM causes CPSUI to call the Windows print spooler to create property sheet pages for the system's default printer. The application then creates an additional property sheet page to illustrate some of the techniques that you can use when you are using CPSUI to create a new page. For more information, see [Common Property Sheet User Interface](http://msdn.microsoft.com/en-us/library/windows/hardware/ff546163(v=vs.85).aspx). + diff --git a/print/cpsuisam/cpsuisam.c b/print/cpsuisam/cpsuisam.c new file mode 100644 index 00000000..d63b5844 --- /dev/null +++ b/print/cpsuisam/cpsuisam.c @@ -0,0 +1,665 @@ +/*++ + +Copyright (c) 1990-2003 Microsoft Corporation +All Rights Reserved + + +Module Name: + + cpsuisam.c + + +Abstract: + + This module contrains sample prototype for the + Windows 2000/Windows XP/Windows Server 2003 common Property + Sheet user interface + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#define DBG_CPSUIFILENAME DbgCPSUISam + + + +#define DBG_WINMAINPROC 0x00000001 + +DEFINE_DBGVAR(0); + + + +#define HAS_TVTEST_PAGES 0x01 +#define HAS_DOCPROP_PAGES 0x02 + + + +extern HINSTANCE hInstApp; +extern TCHAR TitleName[]; +extern TCHAR ClassName[]; +extern TCHAR MenuName[]; +extern TCHAR szWinSpool[]; +extern CHAR szDocPropSheets[]; +extern BOOL UpdatePermission; +extern BOOL UseStdAbout; + + +HWND hWndApp = NULL; + + +typedef struct _CPSUISAMPLE +{ + HANDLE hParent; + HANDLE hCPSUI; + HANDLE hDocProp; + PFNCOMPROPSHEET pfnCPS; + COMPROPSHEETUI CPSUI; + DOCUMENTPROPERTYHEADER DPHdr; + HGLOBAL hDevMode; + HGLOBAL hDevNames; +} CPSUISAMPLE, *PCPSUISAMPLE; + +BOOL +GetDefPrinter +( + PCPSUISAMPLE pCPSUISample +) +{ + HGLOBAL hDevMode = NULL; + HGLOBAL hDevNames = NULL; + LPTSTR pszPrinterName = NULL; + DEVMODE *pDM = NULL; + DEVNAMES *pDN = NULL; + HANDLE hPrinter = NULL; + PAGESETUPDLG PSD = {0}; + BOOL Ok = FALSE; + + PSD.lStructSize = sizeof(PSD); + PSD.Flags = PSD_RETURNDEFAULT; + + if (PageSetupDlg(&PSD)) + { + hDevMode = PSD.hDevMode; + + if (NULL != hDevMode) + { + pDM = (DEVMODE *)GlobalLock(hDevMode); + if (NULL != pDM) + { + hDevNames = PSD.hDevNames; + if (hDevNames) + { + pDN = (DEVNAMES *)GlobalLock(hDevNames); + if (NULL != pDN) + { + pszPrinterName = (LPTSTR)pDN + pDN->wDeviceOffset; + if (pszPrinterName != NULL) + { + if (OpenPrinter(pszPrinterName, &hPrinter, NULL)) + { + Ok = TRUE; + } + } + } + } + } + } + + if (Ok) + { + HGLOBAL h = pCPSUISample->hDevMode; + + if (NULL != h) + { + GlobalUnlock(h); + GlobalFree(h); + } + + h = pCPSUISample->hDevNames; + + if (NULL != h) + { + GlobalUnlock(h); + GlobalFree(h); + } + + if (pCPSUISample->DPHdr.hPrinter) + { + ClosePrinter(pCPSUISample->DPHdr.hPrinter); + } + + pCPSUISample->hDevMode = PSD.hDevMode; + pCPSUISample->hDevNames = PSD.hDevNames; + + pCPSUISample->DPHdr.cbSize = sizeof(DOCUMENTPROPERTYHEADER); + pCPSUISample->DPHdr.hPrinter = hPrinter; + pCPSUISample->DPHdr.pszPrinterName = pszPrinterName; + pCPSUISample->DPHdr.pdmIn = + pCPSUISample->DPHdr.pdmOut = pDM; + pCPSUISample->DPHdr.fMode = (DM_IN_BUFFER | DM_IN_PROMPT | DM_OUT_BUFFER); + + if (!UpdatePermission) + { + pCPSUISample->DPHdr.fMode |= DM_NOPERMISSION; + } + } + else + { + hDevMode = PSD.hDevMode; + hDevNames = PSD.hDevNames; + + if (NULL != hDevMode) + { + GlobalUnlock(hDevMode); + GlobalFree(hDevMode); + } + + if (NULL != hDevNames) + { + GlobalUnlock(hDevNames); + GlobalFree(hDevNames); + } + + if (hPrinter) + { + ClosePrinter(hPrinter); + } + } + } + + return Ok; +} + + +LONG +CALLBACK +CPSUIFunc +( + _In_ PPROPSHEETUI_INFO pPSUIInfo, + LPARAM lParam +) +{ + PPROPSHEETUI_INFO_HEADER pPSUIInfoHdr; + PCPSUISAMPLE pCPSUISample; + HANDLE h; + INSERTPSUIPAGE_INFO InsPI; + + if (!pPSUIInfo) + { + return FALSE; + } + + switch (pPSUIInfo->Reason) + { + case PROPSHEETUI_REASON_INIT: + + pCPSUISample = (PCPSUISAMPLE)LocalAlloc(LPTR, sizeof(CPSUISAMPLE)); + if (NULL == pCPSUISample) + { + return(-1); + } + + pPSUIInfo->UserData = (ULONG_PTR)pCPSUISample; + pCPSUISample->hParent = pPSUIInfo->hComPropSheet; + pCPSUISample->pfnCPS = pPSUIInfo->pfnComPropSheet; + + // + // Add Document Property Sheet for current default Printer + // + + switch (pPSUIInfo->lParamInit) + { + case IDM_DOCPROP: + case IDM_DOCPROP_TVTEST: + + if (GetDefPrinter(pCPSUISample)) + { + InsPI.cbSize = sizeof(INSERTPSUIPAGE_INFO); + InsPI.Type = PSUIPAGEINSERT_DLL; + InsPI.Mode = INSPSUIPAGE_MODE_FIRST_CHILD; + InsPI.dwData1 = (ULONG_PTR)szWinSpool; + InsPI.dwData2 = (ULONG_PTR)szDocPropSheets; + InsPI.dwData3 = (ULONG_PTR)&(pCPSUISample->DPHdr); + + pCPSUISample->hDocProp = + (HANDLE)pCPSUISample->pfnCPS( pCPSUISample->hParent, + CPSFUNC_INSERT_PSUIPAGE, + (LPARAM)0, + (LPARAM)&InsPI + ); + } + break; + } + + // + // Add TreeView Page + // + + switch (pPSUIInfo->lParamInit) + { + case IDM_TVTEST: + case IDM_DOCPROP_TVTEST: + + if (SetupComPropSheetUI(&(pCPSUISample->CPSUI))) + { + InsPI.cbSize = sizeof(INSERTPSUIPAGE_INFO); + InsPI.Type = PSUIPAGEINSERT_PCOMPROPSHEETUI; + InsPI.Mode = INSPSUIPAGE_MODE_FIRST_CHILD; + InsPI.dwData1 = (ULONG_PTR)&(pCPSUISample->CPSUI); + InsPI.dwData2 = + InsPI.dwData3 = 0; + + pCPSUISample->hCPSUI = + (HANDLE)pCPSUISample->pfnCPS( pCPSUISample->hParent, + CPSFUNC_INSERT_PSUIPAGE, + (LPARAM)0, + (LPARAM)&InsPI + ); + } + break; + } + + if ( (pCPSUISample->hCPSUI) + || (pCPSUISample->hDocProp) + ) + { + return 1; + } + + break; + + case PROPSHEETUI_REASON_GET_INFO_HEADER: + + pPSUIInfoHdr = (PPROPSHEETUI_INFO_HEADER)lParam; + if (NULL != pPSUIInfoHdr) + { + pPSUIInfoHdr->pTitle = (LPTSTR)TitleName; + + switch (pPSUIInfo->lParamInit) + { + case IDM_DOCPROP: + + pPSUIInfoHdr->IconID = IDI_CPSUI_PRINTER2; + break; + + case IDM_TVTEST: + + pPSUIInfoHdr->IconID = IDI_CPSUI_OPTION2; + break; + + case IDM_DOCPROP_TVTEST: + + pPSUIInfoHdr->IconID = IDI_CPSUI_RUN_DIALOG; + break; + } + + pPSUIInfoHdr->Flags = PSUIHDRF_PROPTITLE | PSUIHDRF_NOAPPLYNOW; + pPSUIInfoHdr->hWndParent = hWndApp; + pPSUIInfoHdr->hInst = hInstApp; + + return 1; + } + + break; + + case PROPSHEETUI_REASON_SET_RESULT: + + pCPSUISample = (PCPSUISAMPLE)pPSUIInfo->UserData; + if ( (NULL != pCPSUISample) + && (pCPSUISample->hCPSUI == ((PSETRESULT_INFO)lParam)->hSetResult) + ) + { + // + // Save the result and propagate to its owner + // + pPSUIInfo->Result = ((PSETRESULT_INFO)lParam)->Result; + return 1; + } + + break; + + case PROPSHEETUI_REASON_DESTROY: + + pCPSUISample = (PCPSUISAMPLE)pPSUIInfo->UserData; + if (NULL != pCPSUISample) + { + h = pCPSUISample->hDevMode; + if (NULL != h) + { + GlobalUnlock(h); + GlobalFree(h); + } + + h = pCPSUISample->hDevNames; + if (NULL != h) + { + GlobalUnlock(h); + GlobalFree(h); + } + + if (pCPSUISample->DPHdr.hPrinter) + { + ClosePrinter(pCPSUISample->DPHdr.hPrinter); + } + + LocalFree((HLOCAL)pCPSUISample); + pPSUIInfo->UserData = 0; + } + return 1; + } + return -1; +} + +LRESULT +CALLBACK +MainWndProc +( + HWND hWnd, + UINT Msg, + WPARAM wParam, + LPARAM lParam +) +/*++ + +Routine Description: + + This is the main window procedure to the testing program + + +Arguments: + + See SDK + + +Return Value: + + See SDK + +--*/ +{ + DWORD Result; + LONG Ret; + + switch (Msg) + { + case WM_INITMENUPOPUP: + + if (!HIWORD(lParam)) + { + CheckMenuItem( (HMENU)UIntToPtr((UINT)wParam), + IDM_PERMISSION, + MF_BYCOMMAND | ((UpdatePermission) ? MF_CHECKED : MF_UNCHECKED) + ); + + CheckMenuItem( (HMENU)UIntToPtr((UINT)wParam), + IDM_USESTDABOUT, + MF_BYCOMMAND | ((UseStdAbout) ? MF_CHECKED : MF_UNCHECKED) + ); + } + break; + + case WM_COMMAND: + + switch (wParam) + { + case IDM_USESTDABOUT: + + UseStdAbout = !UseStdAbout; + break; + + case IDM_PERMISSION: + + UpdatePermission = !UpdatePermission; + break; + + case IDM_DOCPROP: + case IDM_TVTEST: + case IDM_DOCPROP_TVTEST: + + Ret = CommonPropertySheetUI( hWnd, + (PFNPROPSHEETUI)CPSUIFunc, + (LPARAM)LOWORD(wParam), + &Result + ); + + CPSUIDBG( DBG_WINMAINPROC, + ("CommonPropertySheetUI()=%ld, Result=%ld", Ret, Result) + ); + + break; + + default: + + break; + } + break; + + case WM_DESTROY: + + PostQuitMessage(0); + break; + + default: + + return (DefWindowProc(hWnd, Msg, wParam, lParam)); + } + + return 0L; +} + +BOOL +InitInstance +( + HANDLE hInstance, + INT nCmdShow +) +/*++ + +Routine Description: + + Saves instance handle and creates main window + + This function is called at initialization time for every instance of + this application. This function performs initialization tasks that + cannot be shared by multiple instances. + + In this case, we save the instance handle in a static variable and + create and display the main program window. + + +Arguments: + + hInstance - Current instance identifier + + nComShow - Param for first ShowWindow() call. + + + +Return Value: + + TRUE/FALSE + + +--*/ +{ + // + // Save the instance handle in static variable, which will be used in + // many subsequence calls from this application to Windows. + // + + hInstApp = hInstance; + + // + // Create a main window for this application instance. + // + + hWndApp = CreateWindow( ClassName, + TitleName, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + hInstance, + NULL + ); + if (NULL != hWndApp) + { + // + // Make the window visible; update its client area; + // and send WM_PAINT message + // + + ShowWindow(hWndApp, nCmdShow); + UpdateWindow(hWndApp); + } + + return ((hWndApp) ? TRUE : FALSE); +} + + +BOOL +InitApplication +( + HANDLE hInstance +) +/*++ + +Routine Description: + + Initializes window data and registers window class + + This function is called at initialization time only if no other + instances of the application are running. This function performs + initialization tasks that can be done once for any number of running + instances. + + In this case, we initialize a window class by filling out a data + structure of type WNDCLASS and calling the Windows RegisterClass() + function. Since all instances of this application use the same window + class, we only need to do this when the first instance is initialized. + + +Arguments: + + hInstance - current instance + + +Return Value: + + BOOLEAN + +--*/ +{ + WNDCLASS wc; + + // + // Fill in window class structure with parameters that describe the + // main window. + // + + wc.style = 0L; + wc.lpfnWndProc = MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_CPSUISAMPLE); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = GetStockObject(WHITE_BRUSH); + wc.lpszMenuName = MenuName; + wc.lpszClassName = ClassName; + + // + // Register the window class and return success/failure code. + // + return RegisterClass(&wc); +} + + +INT +APIENTRY +WinMain +( + _In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPSTR lpCmdLine, + _In_ INT nCmdShow +) +/*++ + +Routine Description: + + calls initialization function, processes message loop + + Windows recognizes this function by name as the initial entry point + for the program. This function calls the application initialization + routine, if no other instance of the program is running, and always + calls the instance initialization routine. It then executes a message + retrieval and dispatch loop that is the top-level control structure + for the remainder of execution. The loop is terminated when a WM_QUIT + message is received, at which time this function exits the application + instance by returning the value passed by PostQuitMessage(). + + If this function must abort before entering the message loop, it + returns the conventional value NULL. + + +Arguments: + + + +Return Value: + + Integer + + +--*/ +{ + MSG Msg; + + UNREFERENCED_PARAMETER(lpCmdLine); + + // + // Other instances of app running? + // + + if (!hPrevInstance) + { + if (!InitApplication(hInstance)) + { + // + // Initialize shared things, Exits if unable to initialize + // + return FALSE; + } + } + + // + // Perform initializations that apply to a specific instance + // + if (!InitInstance(hInstance, nCmdShow)) + { + return FALSE; + } + + // + // Acquire and dispatch messages until a WM_QUIT message is received. + // + while (GetMessage(&Msg, NULL, 0L, 0L)) + { + // + // Translates virtual key codes and Dispatches message to window + // + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } + + // + // Returns the value from PostQuitMessage + // + return((INT)Msg.wParam); +} + diff --git a/print/cpsuisam/cpsuisam.sln b/print/cpsuisam/cpsuisam.sln new file mode 100644 index 00000000..334867da --- /dev/null +++ b/print/cpsuisam/cpsuisam.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpsuisam", "cpsuisam.vcxproj", "{F7BAD220-8095-4F67-A75B-0250FCB2B670}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F7BAD220-8095-4F67-A75B-0250FCB2B670}.Debug|Win32.ActiveCfg = Debug|Win32 + {F7BAD220-8095-4F67-A75B-0250FCB2B670}.Debug|Win32.Build.0 = Debug|Win32 + {F7BAD220-8095-4F67-A75B-0250FCB2B670}.Release|Win32.ActiveCfg = Release|Win32 + {F7BAD220-8095-4F67-A75B-0250FCB2B670}.Release|Win32.Build.0 = Release|Win32 + {F7BAD220-8095-4F67-A75B-0250FCB2B670}.Debug|x64.ActiveCfg = Debug|x64 + {F7BAD220-8095-4F67-A75B-0250FCB2B670}.Debug|x64.Build.0 = Debug|x64 + {F7BAD220-8095-4F67-A75B-0250FCB2B670}.Release|x64.ActiveCfg = Release|x64 + {F7BAD220-8095-4F67-A75B-0250FCB2B670}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/print/cpsuisam/cpsuisam.vcxproj b/print/cpsuisam/cpsuisam.vcxproj new file mode 100644 index 00000000..91f302ef --- /dev/null +++ b/print/cpsuisam/cpsuisam.vcxproj @@ -0,0 +1,255 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {F7BAD220-8095-4F67-A75B-0250FCB2B670} + $(MSBuildProjectName) + Debug + Win32 + {67BF1D0E-F90D-414D-A13C-543092176A80} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + cpsuisam + + + cpsuisam + + + cpsuisam + + + cpsuisam + + + + %(AdditionalDependencies);comdlg32.lib;winspool.lib;comctl32.lib;shlwapi.lib;compstui.lib + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + + + %(AdditionalDependencies);comdlg32.lib;winspool.lib;comctl32.lib;shlwapi.lib;compstui.lib + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + + + %(AdditionalDependencies);comdlg32.lib;winspool.lib;comctl32.lib;shlwapi.lib;compstui.lib + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + + + %(AdditionalDependencies);comdlg32.lib;winspool.lib;comctl32.lib;shlwapi.lib;compstui.lib + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + %(AdditionalIncludeDirectories);. + + + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + %(PreprocessorDefinitions);WIN32;STRICT;UNICODE;_UNICODE;cpsuisam + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/print/cpsuisam/cpsuisam.vcxproj.Filters b/print/cpsuisam/cpsuisam.vcxproj.Filters new file mode 100644 index 00000000..44bbac68 --- /dev/null +++ b/print/cpsuisam/cpsuisam.vcxproj.Filters @@ -0,0 +1,36 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {50DC862B-95AC-4892-B963-876111462136} + + + h;hpp;hxx;hm;inl;inc;xsd + {5A8D8E11-1C44-4DD6-8615-57E54DDD4C6C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {44FF2C70-58D4-464D-B774-C6C44F8E380B} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/print/cpsuisam/icons/apple.ico b/print/cpsuisam/icons/apple.ico new file mode 100644 index 00000000..dd2f44f0 Binary files /dev/null and b/print/cpsuisam/icons/apple.ico differ diff --git a/print/cpsuisam/icons/cpsuisam.ico b/print/cpsuisam/icons/cpsuisam.ico new file mode 100644 index 00000000..33df6ef4 Binary files /dev/null and b/print/cpsuisam/icons/cpsuisam.ico differ diff --git a/print/cpsuisam/precompsrc.c b/print/cpsuisam/precompsrc.c new file mode 100644 index 00000000..5944cf51 --- /dev/null +++ b/print/cpsuisam/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/print/cpsuisam/res.rc b/print/cpsuisam/res.rc new file mode 100644 index 00000000..f1e52605 --- /dev/null +++ b/print/cpsuisam/res.rc @@ -0,0 +1,51 @@ +// +// Copyright (c) 1990-2003 Microsoft Corporation +// All Rights Reserved +// + + +#include "windows.h" +#include "compstui.h" +#include "cpsuisam.h" + + + + +IDI_APPLE ICON PRELOAD icons\Apple.ico +IDI_CPSUISAMPLE ICON PRELOAD icons\cpsuisam.ico + + + +CPSUISampleMenu MENU +BEGIN + POPUP "&Test Options" + BEGIN + MENUITEM "&Default Printer Properties", IDM_DOCPROP + MENUITEM "CPSUI &TreeView Test", IDM_TVTEST + + MENUITEM SEPARATOR + + MENUITEM "&CPSUI Test All", IDM_DOCPROP_TVTEST + + MENUITEM SEPARATOR + + MENUITEM "&Permission", IDM_PERMISSION + MENUITEM "&Use Standard About" IDM_USESTDABOUT + + + END +END + + +#include "cpsuisam.dlg" + + +#include + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Common Property Sheet UI Sample Applet" +#define VER_INTERNALNAME_STR "cpsuisam.exe" +#define VER_ORIGINALFILENAME_STR "cpsuisam.exe" + +#include "common.ver" diff --git a/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/App.xaml b/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/App.xaml new file mode 100644 index 00000000..f97fc473 --- /dev/null +++ b/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/App.xaml @@ -0,0 +1,7 @@ + + diff --git a/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/App.xaml.cs b/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/App.xaml.cs new file mode 100644 index 00000000..d37585c4 --- /dev/null +++ b/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/App.xaml.cs @@ -0,0 +1,146 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// +// Abstract: +// +// This file contains the entry point to the application. + +using System; +using System.Windows; +using System.Runtime; + +using Microsoft.Samples.Printing.PrinterExtension.Types; +using Microsoft.Samples.Printing.PrinterExtension.Helpers; + +using System.Windows.Interop; + +namespace Microsoft.Samples.Printing.PrinterExtension +{ + /// + /// Interaction logic for App.xaml. + /// + public partial class App : Application + { + /// + /// This is the event handler invoked on various driver events. + /// + /// + /// + private static void OnDriverEvent(object sender, PrinterExtensionEventArgs eventArgs) + { + // + // Display the print preferences window. + // + + if (eventArgs.ReasonId.Equals(PrinterExtensionReason.PrintPreferences)) + { + PrintPreferenceWindow printPreferenceWindow = new PrintPreferenceWindow(); + printPreferenceWindow.Initialize(eventArgs); + + // + // Set the caller application's window as parent/owner of the newly created printing preferences window. + // + + WindowInteropHelper wih = new WindowInteropHelper(printPreferenceWindow); + wih.Owner = eventArgs.WindowParent; + + // + // Display a modal/non-modal window based on the 'WindowModal' parameter. + // + + if (eventArgs.WindowModal) + { + printPreferenceWindow.ShowDialog(); + } + else + { + printPreferenceWindow.Show(); + + // Flash the window to draw the user's attention. This is required + // because the printer extension may be drawn behind the parent window. + // The return value of FlashWindow can be safely ignored if there is no need + // to know if the window has focus or not. + WindowHelper.FlashWindow(wih.Handle); + } + } + else if (eventArgs.ReasonId.Equals(PrinterExtensionReason.DriverEvent)) + { + // + // Handle driver events here. + // + } + } + + /// + /// Perform initialization tasks for the printer extension in this event handler. + /// + /// + /// + private void Application_Startup(object sender, StartupEventArgs e) + { + // + // It is recommended that exactly one instance of the PrinterExtensionManager be created per instance of + // the printer extension. + // + + if (manager != null) + { + return; + } + manager = new PrinterExtensionManager(); + + // + // Enable events to be received on one printer driver id. + // + // Note: The order of adding the delegate to PrinterExtensionManager.OnDriverEvent + // and invoking PrinterExtensionManager::EnableEvents is important. + // Adding the delegate should be done first. + // + + manager.OnDriverEvent += OnDriverEvent; + + // + // It is recommended that an instance of a printer extension invoke PrinterExtensionManager::EnableEvents + // for exactly one printer driver id. The printer driver id could come in from a command line argument, + // thereby allowing one application binary to dynamically invoke PrinterExtensionManager::EnableEvents against + // the appropriate printer driver id. + // + + manager.EnableEvents(Guid.Parse(PrinterDriverID)); + } + + /// + /// Perform uninitialization tasks for the printer extension in this event handler. + /// + /// + /// + private void Application_Exit(object sender, ExitEventArgs e) + { + manager.OnDriverEvent -= OnDriverEvent; + manager.DisableEvents(); + + manager = null; + } + + /// + /// This is the printer driver id, as defined in the printer driver manifest file. + /// Please replace this GUID with the printer driver id from your manifest file. + /// + /// It is recommended that you invoke PrinterExtensionManager::EnableEvents() on exactly + /// one printer driver id. The id could come in from a command line argument, thereby enabling + /// one application binary to work with multiple printer driver ids. + /// + private const string PrinterDriverID = "{E0691E8D-F7CC-456E-A7B5-D1FC19BA2279}"; + + /// + /// Instance of the PrinterExtensionManager. It is recommended that you have only instance + /// of the PrinterExtensionManager per application instance. + /// + private static PrinterExtensionManager manager = null; + } +} diff --git a/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/BidiHelper.cs b/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/BidiHelper.cs new file mode 100644 index 00000000..c975ee7c --- /dev/null +++ b/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/BidiHelper.cs @@ -0,0 +1,154 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved +// +// +// Abstract: +// +// This file contains helper methods that provide a data-binding friendly way to access and parse bidi response data. + +using System; +using System.Collections.Generic; +using System.Windows.Media; +using System.Xml; + +namespace Microsoft.Samples.Printing.PrinterExtension.Helpers +{ + /// + /// Provide a data-binding friendly way to access and parse bidi response data. + /// + public class BidiHelper + { + /// + /// Parse the bidi response. + /// + /// Bidi response XML data. + public BidiHelper(string bidiResponse) + { + BidiResponseParser parser = new BidiResponseParser(bidiResponse); + InkLevelC = parser.GetInkLevel(Colors.Cyan); + InkLevelM = parser.GetInkLevel(Colors.Magenta); + InkLevelY = parser.GetInkLevel(Colors.Yellow); + InkLevelK = parser.GetInkLevel(Colors.Black); + } + + /// + /// Get the Cyan ink level. + /// + public double InkLevelC + { + get; + private set; + } + + /// + /// Get the Magenta ink level. + /// + public double InkLevelM + { + get; + private set; + } + + /// + /// Get the Yellow ink level. + /// + public double InkLevelY + { + get; + private set; + } + + /// + /// Get the Black ink level. + /// + public double InkLevelK + { + get; + private set; + } + } + + /// + /// This class parses bidi response xml data and provides wrapper methods that operate upon the xml. + /// + internal class BidiResponseParser + { + /// + /// Parse the bidi response. + /// + /// Bidi response XML data. + internal BidiResponseParser(string bidiResponse) + { + bidiData = new XmlDocument(); + bidiData.LoadXml(bidiResponse); + + namespaceManager = new XmlNamespaceManager(bidiData.NameTable); + namespaceManager.AddNamespace("bidi", "http://schemas.microsoft.com/windows/2005/03/printing/bidi"); + } + + /// + /// Get the ink level for a given color. + /// + /// Color + /// Ink level percentage + internal double GetInkLevel(Color color) + { + XmlElement root = bidiData.DocumentElement; + XmlNode inkNode = root.SelectSingleNode(CreateInkXPathQuery(color), namespaceManager); + return double.Parse(inkNode.FirstChild.Value) / 100; + } + + /// + /// Create an XPath query that retrieves the ink level from a standard bidi response. + /// + /// + /// + private static string CreateInkXPathQuery(Color color) + { + string colorName = null; + + if (color.Equals(Colors.Black)) + { + colorName = "Black"; + } + else if (color.Equals(Colors.Red)) + { + colorName = "Red"; + } + else if (color.Equals(Colors.Green)) + { + colorName = "Green"; + } + else if (color.Equals(Colors.Blue)) + { + colorName = "Blue"; + } + else if (color.Equals(Colors.Cyan)) + { + colorName = "Cyan"; + } + else if (color.Equals(Colors.Magenta)) + { + colorName = "Magenta"; + } + else if (color.Equals(Colors.Yellow)) + { + colorName = "Yellow"; + } + else + { + throw new ArgumentException("Unsupported color"); + } + + return "/bidi:Get/Query/Schema[@name='\\Printer.Consumables." + colorName + "Ink" + ":Level']/BIDI_INT"; + } + + private XmlDocument bidiData; + private XmlNamespaceManager namespaceManager; + } + +} diff --git a/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Fabrikam_Logo.png b/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Fabrikam_Logo.png new file mode 100644 index 00000000..196cd5f6 Binary files /dev/null and b/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/Fabrikam_Logo.png differ diff --git a/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/PrintPreferenceWindow.xaml b/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/PrintPreferenceWindow.xaml new file mode 100644 index 00000000..924efc21 --- /dev/null +++ b/print/v4PrintDriverSamples/PrinterExtensionSample/ExtensionSample/PrintPreferenceWindow.xaml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +